[00:00.000 --> 00:17.080] Hello, my name is Christophe de Dinosha, and today I'm going to talk about DB48X, which [00:17.080 --> 00:25.720] is my attempt at resurrecting Reverse Polish Lisp, or RPL, on a modern open-source platform. [00:30.840 --> 00:38.680] So, first of all, what are we going to talk about? Why revive RPL, and why should you care? [00:39.320 --> 00:48.840] I'm going to give a demo of what DB48X looks like, both on a simulator and on real hardware. [00:49.880 --> 00:53.880] The idea is to have a handy calculator that does tons of stuff for you. [00:55.640 --> 00:59.800] Then I'm going to give a brief history of RPL and of pocket calculators, [00:59.800 --> 01:06.440] notably from HP. I'm going to talk about free software on modern calculators, [01:08.040 --> 01:11.480] and I'm going, since this is really the topic of this [01:12.120 --> 01:17.560] track, to talk about the very minimalist aspects of calculators, even today. [01:19.640 --> 01:23.960] Here's a quick feature overview. I'm going to develop this a little with various examples, [01:24.600 --> 01:32.360] and I'm also going to explain how this works inside, and notably talk about a very compact [01:32.360 --> 01:41.320] C++ object model with garbage collection. So, let's start by running DB48X. I'm going to give [01:41.320 --> 01:47.320] two demos, one running on simulator, and the second one on SwissMicro's DM42 hardware. [01:47.720 --> 01:59.080] So, the demo on simulator can be formed online. You can scan this QR code to see the details of [01:59.080 --> 02:06.920] the demo. The point here is that we have a development environment that simulates at least [02:06.920 --> 02:15.720] the user-spaced portion of the application software, and will let us perform a variety of [02:16.680 --> 02:21.960] online tests automatically that are difficult to do on the physical calculator. [02:25.720 --> 02:35.480] Now, on real hardware, what you do is you have a USB interface. You simply load a DM42 program in it, [02:36.840 --> 02:43.480] and that's essentially it. When you exit, you have your new environment, and you can switch back [02:43.480 --> 02:48.120] and forth between this and, for instance, the standard DM42 software. [02:50.520 --> 02:56.840] So, RPL has a common line where you type, for instance, numbers, and you hit Enter, [02:57.640 --> 03:04.520] and you see that the numbers go on a stack that is used for operations. When you want to add numbers [03:04.520 --> 03:09.960] or subtract them, you will essentially operate on the last two items on the stack and push the [03:09.960 --> 03:17.800] result there. Operations like scientific computations operate exactly the same way. [03:18.760 --> 03:21.800] They operate on the top level number one of the stack. [03:24.040 --> 03:30.840] In terms of programming, RPL has this special signed brackets that identifies an RPL program, [03:30.840 --> 03:36.120] and I push the program on the stack. Then I'm going to give it a name, so 1 plus increments, [03:37.000 --> 03:42.680] the first level of the stack, and then to execute the program, I simply hit the soft key, [03:42.680 --> 03:48.280] and you see that every time I push it, I increment the number on the stack. So, fairly simple. [03:50.760 --> 03:57.640] There is a built-in markdown help if you hit a key and keep it held. So, here we are seeing [03:57.640 --> 04:03.640] the feature for that function, and then we can explore the hyperlinks. So, it's all markdown [04:03.640 --> 04:11.000] based, so we can reuse the same help for the GitHub repository and for the calculator itself. [04:14.120 --> 04:21.240] Now, let me give you a very brief history of RPL. So, again, RPL stands for Reverse Polish Lisp, [04:21.880 --> 04:27.880] and it's an interactive language for calculators that has been used by Hewlett-Packard from 1984 [04:27.880 --> 04:37.160] until 2015. So, RPL has an ancestry called RPL, Reverse Polish Notation, [04:38.200 --> 04:45.480] and this started with the HP35 scientific calculator that introduced this system which [04:45.480 --> 04:51.960] saves keystrokes and is very similar to the way you think about the computations you're doing. [04:52.520 --> 04:59.880] It also leads, naturally, to a step-by-step programming model where you simply record keystrokes [04:59.880 --> 05:05.000] and the calculator is going to replay these keystrokes. That was introduced with the HP65, [05:05.560 --> 05:10.600] which was really marvel for the day. The little slots you see on the side, for instance, [05:10.600 --> 05:15.400] are for card readers. So, you can actually store your software on little tapes. [05:16.120 --> 05:23.320] The last real complete RPL system was the HP41. I'm talking about a system because it could be [05:23.320 --> 05:27.800] connected to a variety of expansions. There was a bus, you could connect it to printers, [05:27.800 --> 05:39.000] to plotters, to data acquisition tools, and so on. But there were later machines that a [05:39.000 --> 05:43.640] RPL was introduced and still used RPN. So, essentially, the main difference has been [05:43.640 --> 05:52.680] a fixed size for the stack and no real type system. And the high end of that series is the HP42. [05:54.920 --> 06:03.160] Now, RPL itself was introduced with the HP28C in 1984 or five. That machine had only [06:03.160 --> 06:07.320] two kilobytes of run, so that tells you this can run on a very, very small system. [06:08.120 --> 06:14.360] It also introduced a new Hewlett-Pagard CPU called the Salon that I'm going to talk about in a moment. [06:16.600 --> 06:23.480] The series culminated, the historical series culminated with the HP48 series, [06:24.120 --> 06:30.120] and that had equations, larger graphics, was extensible. So, you had slots where you could [06:30.120 --> 06:36.120] put memory, ROM cards, etc. And then there were follow-ups like the 49, etc. that were not very [06:36.120 --> 06:45.640] different from the 48. That series was recreated with HP50G and other calculators like the 38, [06:45.640 --> 06:53.880] the 48G2, etc. that are essentially running the original software designed for the Salon CPU [06:54.520 --> 07:02.760] under emulation with an ARM CPU that emulates the Salon. And that gives you a significant boost in [07:02.760 --> 07:12.120] speed and essentially it executes exactly the same software. Now, because the ARM CPU itself [07:12.120 --> 07:17.480] is much, much faster than Salon, a number of folks started developing software for it. [07:17.480 --> 07:23.560] And these series of calculators were based on somewhat standard platforms that could be flashed. [07:24.280 --> 07:31.880] And so, people developed open-source software and free software to replace the built-in firmware. [07:31.880 --> 07:37.320] An example shown here is new RPL, which is an ARM native implementation of RPL, [07:37.880 --> 07:43.720] that is relatively complete as far as the language itself goes, but is missing a number [07:43.720 --> 07:48.280] of features from the original calculator, including graphics, equation editor, etc. [07:50.680 --> 07:57.320] Now, how does RPL work inside? It's very interesting because it's a very smart, minimalist system. [07:58.120 --> 08:03.960] So, first of all, it's optimized for the HP Salon CPU, which is a descendant from [08:03.960 --> 08:12.600] CPUs built for earlier calculators. And that's a four-bit CPU with 64-bit registers designed [08:12.600 --> 08:19.160] mostly for floating points. And so, you have four-bit nibbles that you can address individually in [08:19.160 --> 08:26.680] memory. Addresses are 20 bits, that's five nibbles. And the 64 bits in the register can be [08:26.760 --> 08:32.280] addressed in a variety of ways that correspond to a BCD representation of floating points. [08:32.280 --> 08:37.320] So, for instance, the X field is for exponent, the M for mantissa, the S for sine. [08:39.960 --> 08:47.080] So, there is a number of pieces of free software and free calculator firmware [08:47.080 --> 08:54.760] that can run either on ARM-based calculator, and then later led to platforms developed [08:54.760 --> 09:01.640] specifically to run this kind of software. In terms of available platforms, if you go beyond [09:01.640 --> 09:08.440] the HP calculators, so first of all, the ARM-based HP calculators can be flashed. So, even a lowly [09:09.320 --> 09:17.880] HP 20 something can be given new firmware and a new life. You have an example here with something [09:17.880 --> 09:27.560] called WP34S which creates a very advanced scientific calculator from a very inexpensive HP calculator. [09:30.840 --> 09:37.480] And there are also a number of free emulators for iOS, Android, etc. So, what you see here is a 42 [09:37.480 --> 09:46.760] emulator called 342. And Swiss Micros essentially started building the hardware around this software. [09:46.840 --> 09:53.560] So, they created the DM42 which runs a variant of 342 with some underlying firmware [09:54.280 --> 10:00.760] to provide operating system-level services. And so, they have this platform and that same [10:00.760 --> 10:06.360] platform just with a firmware flashing and changes in keyboard can emulate the HP 42, [10:06.360 --> 10:13.960] the HP 41, etc. Now, third-party firmware has started sporting like mushrooms but [10:13.960 --> 10:19.800] really large and advanced firmware. There are not that many variants. What you see here [10:20.360 --> 10:28.840] is the descendant of WP34S which is called 43S and has a number of really advanced features, [10:28.840 --> 10:35.960] but it's essentially still in the same spirit as the RPN calculators. In other words, it's still [10:35.960 --> 10:41.320] using the RPN logic with a fixed-size stack and not much in terms of typing. [10:42.120 --> 10:49.320] So, my first attempt to enter that space was to port a new RPL to DM42. And so, I created a simulator [10:49.320 --> 10:57.560] and you can see the results of this experiment there with a side-by-side setup where you have [10:57.560 --> 11:04.120] the DM42 on the left, the HP 50G simulator on the in the middle, and the HP Prime simulator on the [11:04.120 --> 11:12.120] right. And essentially, my work was to try to make the software more portable, support one-bit [11:12.120 --> 11:20.120] graphics on the DM42, but really take advantage of that platform. And on simulator, it worked pretty [11:20.120 --> 11:26.600] well. The problem is, as I said, this machine is really minimalist and it turns out that new [11:26.600 --> 11:32.120] RPL, as soon as I started trying to run it on the physical hardware, it just did not fit. [11:32.840 --> 11:41.240] Why? Because the platform is built around an ultra-low-power ARM Cortex M4F, which has, [11:41.240 --> 11:47.400] among other benefits, that the battery life on a battery like this is up to three years according [11:47.400 --> 11:57.320] to the vendor. Now, that machine has only 96K of RAM and only 70K free after the operating system [11:57.400 --> 12:04.600] load. How much is 64K? Well, that's essentially one Commodore 64 and a half. And the Commodore 64 [12:04.600 --> 12:12.200] is not exactly yesterday's machine. There's only two megabytes of flash available. So again, in terms [12:12.200 --> 12:19.560] of old stuff, what remains free once you have loaded standard libraries and the floating-point [12:20.520 --> 12:28.440] emulation library from Intel, et cetera, is about 700K. So that's about the same size as an original [12:28.440 --> 12:36.600] Macintosh floppy disk. So my conclusion within these numbers is that I had better restart from [12:36.600 --> 12:41.160] scratch to create a firmware that was redesigned to fit in such a small system. [12:42.920 --> 12:49.400] So how does that work? Well, first of all, I wanted to use C++ on a modern language with [12:49.480 --> 12:55.480] templates and various library utilities, et cetera. But I needed to have garbage collection [12:55.480 --> 13:01.160] for the objects, just like the original RPL, and a very, very minimal memory usage. [13:03.240 --> 13:06.600] Let's start with the features that are implemented today. And that's essentially [13:06.600 --> 13:13.400] based off the command set of the whole series from the HP48SX to the HP50G. [13:14.120 --> 13:21.320] The Intel floating-point library that ships with the platform gives me 34 decimal [13:22.280 --> 13:29.160] places for floating points. So you see E and Pi here with the number of digits that were computed [13:29.160 --> 13:34.200] by running the exponential of one and four times the octangent of one. [13:34.680 --> 13:45.400] The platform, so my application software on top of that also supports large integers like the HP50G [13:46.120 --> 13:52.520] as well as base numbers that today can be in hexadecimal, decimal, octal, or binary. [13:53.640 --> 13:59.960] And I plan to support arbitrary bases between two and 36 in a later firmware revision. [14:00.840 --> 14:07.560] You see here how these numbers are entered in the machine with the hashtag at the beginning. [14:08.200 --> 14:16.760] And then when you put this hashtag on the command line, the cursor shifts to be like [14:16.760 --> 14:24.280] binary or base. And then I can enter the numbers directly and the first row of letters changes [14:24.280 --> 14:34.040] directly to let me enter numbers more practically. So let me show you that live. So I bring up the [14:34.040 --> 14:41.480] calculator. I click on shift base. And you see that I have the hash sign here. And I can say hash [14:42.200 --> 14:56.920] one, two, A. And hash two, two, E plus. And I have my hexadecimal conversion here. [15:01.400 --> 15:09.000] So as I said, RPL has a number of data types that includes text, list, and arrays. So the lists [15:09.000 --> 15:16.920] are between braces. The arrays are between square brackets. And the text is between quotes. [15:16.920 --> 15:23.480] You see a program there on level two that takes the hello string, the world string, [15:23.480 --> 15:27.320] then does a plus. And when you evaluate that program, you get hello world. [15:29.240 --> 15:34.760] You have also programs and algebraic expressions. So I just showed what the program looks like. [15:34.760 --> 15:42.040] But you can also have algebraic expressions written the usual way. You see here, for instance, [15:42.040 --> 15:49.960] square root of x plus one. There is a plethora of scientific functions. The catalog in the [15:50.520 --> 15:58.680] HP48 series lists something like 1700 functions total. A little less on some other models, [15:58.680 --> 16:05.080] but it's the order of what you have. I also already implemented a storage mechanism for [16:05.080 --> 16:11.560] persistent values, so variables, directories, et cetera. And so what you see here is a three-level [16:12.040 --> 16:20.680] menu where when you hit the key, you evaluate what is inside the variable. When you shift, [16:21.400 --> 16:26.280] you will go to the second level in the menu and that will read the content of the variable. [16:26.840 --> 16:32.840] And if you shift twice, then you're going to the third level of the menu and you're going to [16:32.840 --> 16:38.920] store something in the variable. So again, I can show that live. I'm going to store the result I [16:38.920 --> 16:49.000] just had. So execute is for execute equation. I'm going to call that B and I do store, sorry, [16:49.960 --> 16:57.000] enter store. And then if I go to the recall menu that shows me the variable and you see my B here, [16:57.640 --> 17:04.760] and if I just evaluate B, I have the number I had. If I shift B, I record the value. [17:05.720 --> 17:15.000] And if I want to store something else in B, I will shift twice, hit that key, and now B is 12. [17:15.960 --> 17:25.800] So as you can see, the system works already at that level. So in order to be able to really [17:27.320 --> 17:34.520] have something efficient on such a small machine, I had to design a custom object model and I based [17:34.520 --> 17:42.040] it on RPL itself, the historical RPL, but I tried to make it much more compact. And for [17:42.040 --> 17:49.800] instance, I use LB128 to store all the objects in memory. So LB128 is this system used for [17:49.800 --> 17:57.240] instance in Dwarf that encodes integers by having only the last, so you have seven bits per byte, [17:57.960 --> 18:06.200] and the last one in the series has a bit clear, the other have a bit set. So the type that is the [18:06.200 --> 18:13.880] first byte or LB128 value is an index to the handler table used for evaluation. So instead [18:13.880 --> 18:22.520] of using direct addresses like in RPL, I use an index. And so that means I can have 128 [18:23.160 --> 18:36.680] one byte types or commands and 16384 fit in two bytes. And as a reminder, in RPL that was 205, [18:36.680 --> 18:46.840] 2.5 bytes, five nibbles for each type. So I'm saving a little here. So you see here the catalog [18:46.840 --> 18:57.960] on the HP 450, I think. So let me compare and contrast the storage of something like the number [18:57.960 --> 19:07.640] one. To be precise, it's the internal number one on the HP48. The HP48 has no real user integers, [19:07.720 --> 19:18.440] whereas a DB48X has. So when you type one, the most compact storage you have for, sorry, [19:18.440 --> 19:27.960] that's actually three, I got that wrong on the HP48. So the value that you see here, [19:27.960 --> 19:37.880] that's the prefix. And so the 02911 is the address of the evaluation handler for integers. [19:38.760 --> 19:46.040] And three, that should actually be one, is the payload. The storage in LB128 is 14, [19:46.040 --> 19:52.840] that's the index for integer types. And 01 is the actual value. And because the habit is not set, [19:52.840 --> 19:59.880] that stops here and we're done. If you look at ABC, how the text ABC is stored, [19:59.880 --> 20:08.520] the prefix in the HP48 is 0282C. So that's the five nibbles address. Then you have the total size, [20:08.520 --> 20:19.400] and then you have the ABC encoding itself. Whereas for DB48X, you have the type, which is two, [20:19.400 --> 20:27.720] then you have the length again encoded as a DB128. And so because it's less than 128, [20:27.720 --> 20:36.440] it uses only one byte. And then I have the data itself after that. The name ABC is exactly the [20:36.440 --> 20:42.760] same encoding, except that the prefix is not the same. And for DB48X, the type shifts from two to [20:42.840 --> 20:46.680] one C. The types themselves change with every build, by the way. [20:48.760 --> 20:52.040] So that means the evaluation loop is extremely simple. [20:54.920 --> 21:00.920] It's essentially the way this works. You can see the code here is that you're going to take for [21:00.920 --> 21:05.720] each object, you're going to compute its size, skip to the next one, and then call the handler [21:05.720 --> 21:13.240] and evaluate that handler. So it's really evaluating a program in DB48X is extremely fast. [21:15.160 --> 21:22.840] And there is a fast, simple copying garbage collector. And the picture that was supposed [21:22.840 --> 21:29.320] to illustrate that was a promptly garbage collector as well. So what is the improvement [21:29.400 --> 21:36.200] over existing ASP calculators? Well, moving from 4-bit to 32-bit CPU means that it's much, [21:36.200 --> 21:42.760] much faster on various tests like loops, et cetera, and between one, two or three orders of magnitude [21:42.760 --> 21:50.440] faster. Scientific computations are even faster. There is a high resolution monochrome in display. [21:51.400 --> 21:57.240] That means that when you switch off the calculator, it keeps a picture that you display there. [21:57.240 --> 22:04.360] And so we have these fancy off images that you can use. So let me show you some examples here. [22:06.120 --> 22:11.400] So you see this is one off image. And if I shift off, then I'm going to see another image. [22:11.400 --> 22:15.400] And again, because it's an E ink, it doesn't consume any memory. [22:18.200 --> 22:23.560] There are three rows for the softkey menu system. That's an improvement compared to [22:24.520 --> 22:32.520] the original HP calculators. Because of the high resolution display, we can display [22:32.520 --> 22:40.360] the functions associated with base function, shift, and double shift. And as I pointed out earlier, [22:40.920 --> 22:46.920] the highlighted portion in black moves as you hit the shift key. So let me show that again. [22:47.880 --> 22:53.080] So you see that if I hit the shift key once, then I get to recalling the value B. [22:53.080 --> 22:59.480] And if I hit twice, then I move there and then back to the original location. [23:03.240 --> 23:08.440] There is a common catalog and auto completion. So that's better shown than explained. [23:09.240 --> 23:17.720] So let me type. So let me go back to my demo system here. So let's say that if I hit the [23:18.840 --> 23:23.880] shift key and I hold it, I shift to alpha mode. And now I'm going to type, for instance, [23:23.880 --> 23:29.240] A. And we are going to see nothing because I was still in the recall menu. I hit plus. [23:29.960 --> 23:34.360] And you see that now I have auto completion at the bottom with the various comments that begin [23:34.360 --> 23:40.360] with A. There is a plus here. And you might wonder why the plus is here. It's because it also takes [23:40.360 --> 23:48.760] the name add. So add contains an A. And I have ABS, for instance. And now I can do ABS. And I have [23:48.760 --> 23:55.960] evaluated ABS of 12 directly. So that's pretty neat. That's a good way to quickly access a very, [23:55.960 --> 24:03.320] very large number of functions. And it's optimized for the original GM42 key layout. [24:03.320 --> 24:09.320] I paid a lot of attention to this. So for instance, I showed earlier how, for instance, [24:09.320 --> 24:19.400] when you type execute, which is execute a comment in the GM42, there is no real equivalent for the [24:21.320 --> 24:28.440] RPL model. So instead, I retranslate that as execute equation. And that does something that is [24:28.440 --> 24:38.600] very frequent in RPL, which is to have a symbolic value for something. You can see also that the [24:38.600 --> 24:44.840] cursor is moving, is changing depending on what I'm doing. So for instance, here it's A for algebraic. [24:45.400 --> 24:50.280] And it's white because I'm in alpha mode. If I leave alpha mode, it's going to turn black. But [24:50.280 --> 24:59.080] I'm still in algebraic mode. The row keys are, I have only two, the HP48 has four. So on the [24:59.080 --> 25:08.600] common line, up and down, move left and right. It's an acquired paste. There is also no real [25:08.600 --> 25:16.280] run stop for programs. So RS is instead translated as eval. So it evaluates the value that you have. [25:20.280 --> 25:24.680] And as I said, there is this markdown based online help. So you saw that in the video, [25:24.680 --> 25:31.240] but we can show it live now. So for instance, if I hit sin and I hold sin, [25:32.440 --> 25:37.880] then it's going to show the online help there. You see that there is this home button. So I can go [25:37.880 --> 25:45.560] to home and then I can go down and select, for instance, the first entry there. [25:46.520 --> 25:53.160] And I'm going to jump to help. And that explains how the help system works. [25:57.160 --> 26:03.160] So there is a lot that remains to be done. The future plans include support for complex numbers [26:03.160 --> 26:09.880] that are not implemented yet. Vector and metrics arithmetic, which is integral to the HP48 [26:10.520 --> 26:18.680] RPL variant that also exists today within 28, et cetera. That's a relatively complex set of things [26:18.680 --> 26:24.680] in particular, because I would like to do it like new RPL does, when new RPL does support [26:24.680 --> 26:29.400] matrices with symbolic values in there. So you can have a matrix with an X in there [26:29.400 --> 26:32.920] and as the determinant of that matrix, you're going to get the results. [26:33.560 --> 26:36.760] Whether I can fit that in the available space is unclear. [26:36.760 --> 26:43.320] As I said, there are about 1500 functions that remain to be implemented in some way, [26:44.600 --> 26:51.880] including variants. So for instance, the sin function for sinus, the sine cosine function, [26:51.880 --> 26:57.080] so all the trigonometrics are implemented for real numbers, but they are not implemented [26:57.080 --> 27:04.280] for complex numbers yet or for other data types. So there is some work that remains to be done [27:04.280 --> 27:12.840] also even on existing functions. Plotting and graphing is a key feature of these calculators. [27:12.840 --> 27:20.200] So I'd like to have that. The HP50G is quite advanced in that respect and getting [27:20.200 --> 27:24.280] to the point where we have feature piety is going to take a lot of time. [27:25.960 --> 27:31.320] So that's essentially what I had to show. I hope that you found this interesting [27:31.320 --> 27:39.160] and I'm really welcoming contributors if you want to take a look at how this works inside and if you [27:39.160 --> 27:47.480] want to help me add many of the new features or if it were only just to write or extend the online [27:47.480 --> 27:53.640] help, any kind of help is really welcome. And that's about it. Thanks a lot for listening. [27:53.640 --> 27:59.000] Now it's time for questions and the questions will be live and I'll have a calculator available [27:59.000 --> 28:15.880] if you want to play with it. So we should be live now. We have only 30 seconds left. [28:17.320 --> 28:22.680] So how do the funds work on these calculators? Is it possible to load custom funds for different [28:22.680 --> 28:29.800] steps? Okay, so there are two parts to this question. The first one is the funds themselves [28:29.800 --> 28:38.120] and the second one is non-letting scripts. So in terms of funds, there were multiple formats [28:38.120 --> 28:45.960] that I tried. The current model, the current firmware supports two formats that I call dense [28:45.960 --> 28:53.880] and sparse. The sparse format is more efficient for large funds that have a lot of space and the [28:53.880 --> 29:04.600] dense format is more compact for smaller funds that have something like, for instance if you have a [29:04.600 --> 29:12.200] five or eight bits of hate for very small funds, then practically all pixels inside are used [29:13.000 --> 29:19.160] and so you have a dense or format for that. So that's for the representation of funds. [29:19.160 --> 29:27.400] All the run presentations cover the 16-bit range of unicode and so they do include [29:28.120 --> 29:41.160] the most of the non-letting characters. So we do cover [29:45.080 --> 29:53.400] an arbitrary range of non-letting characters. What the system lacks at the moment is that it [29:53.400 --> 29:59.880] doesn't know how to do combining glyphs and it doesn't know how to do right to left rendering. [30:00.440 --> 30:04.920] Those are a bit complex, they are not implemented in the firmware at the moment. [30:06.760 --> 30:14.920] The fund that, and then I wrote in the GitHub repository, there is a tool that lets you convert [30:14.920 --> 30:25.640] any TTF font to use as a font in the system. The font that I used is derived from an open source [30:25.640 --> 30:31.000] font and I forgot what the name is and I changed a few glyphs inside just to make them look better [30:32.520 --> 30:44.280] on the screen. So you can look at the GitHub history and you'll see that I tried a dozen [30:44.280 --> 30:47.960] fonts until I found one that I thought would look good. [30:50.840 --> 30:57.640] Okay, thank you. Thanks for speaking at Fasten Christophe. I will catch you later, [30:57.640 --> 31:03.720] I'll move on to the next talk now. You can hang out in this room if people want to come and chat [31:03.720 --> 31:10.280] with you. This is a breakout room just for this talk. Yep, thanks a lot. Yeah, bye.