It’s Advent of Code again!

Yup, for the 7th year in a row: it’s Advent of Code time!

Advent of Code is an advent calendar of programming puzzles. Every day of December until Christmas, you get a new puzzle and a piece of the yearly story in which you need to help the elves save Christmas because Santa is in trouble! In the previous years, we’ve repaired the snow machine, the clock that guides the sleigh, the printer that prints the nice and naughty list, time itself, we brought Santa back from the edge of the Solar System, and we tried to take some vacation last year but it was complicated. It seems this year we need to fetch the keys to the sleigh that got dropped in the ocean by a clumsy elf…

The format of the puzzle is a problem and an input (there’s a number of different inputs, assigned randomly (as far as I can tell) to all the users); the solution (typically a number or a short character string) is what matters to prove that you solved the problem. This means that you can solve it with any language you see fit… or even no language at all. There’s a guarantee that all problems can be solved within 15 seconds on 10-year-old hardware, but it may require some more significant work to get there.

I love Advent of Code. The puzzles are interesting and the difficulty ramp up is usually great, the story is whimsy, and it’s good fun. There’s a competitive aspect to it: there’s a leaderboard for the first 100 people to solve the puzzle, and there’s a “private leaderboard” feature on the website that allows to compete with friends or colleagues. I found it a great way to stretch my coding muscles and practice another language.

This year I decided to solve it in PHP: I’m still learning the language (which I’m now using in my daily professional life), and if previous years are to be believed, I’ll probably learn more than a few tricks – looking forward to that! I’m publishing my (ugly) solutions on GitHub as I go: Balise42/AoC2021.

The first day is easy… who’s in? πŸ™‚

Time tracking with timewarrior

I’ve been working from home for a bit more than a month – or, as we say around here, “doing home office”, which is apparently a typically German turn of phrase πŸ˜‰ Since I’m working part-time (60%), and days and hours tend to melt into each other, actually tracking the time that I spend working has proven pretty useful for me to dose the “right” amount of work: make sure I’m working the hours I’m supposed to work, make sure I’m not working significantly more than the hours I’m supposed to work. For those who know me a little, they’ll have an inkling that my issue is more the latter one than the former, with me being much more worried about the former than the latter πŸ˜‰

I had done that sort of tracking a long while ago with some software called arbtt. It was fairly neat when it came to automating that tracking (and that’s why I’m mentioning it here explicitly): it uses the mouse focus to determine on which window you are working, identifies said windows by their titles, and allows to define rules to classify which belongs to what. I ran that thing for a while when I was working as a freelancer, and that worked pretty well – if you’re not allergic to Haskell. (My window manager is in Haskell too, so I can survive :p )

This time around, I didn’t feel the need to fight with arbtt rule system to fit everything into categories that would be too fine-grained for my liking; after a bit of poking, I found timewarrior. There’s a package on my Ubuntu, so I just installed it, and I started tracking.

In timewarrior, you track by associating tags to intervals of time. For instance, when I start working on documentation, I open a terminal and type

$ timew start work doc

and it starts an interval tagged with work and doc. I have a very coarse set of tags: my current tags are doc (writing documentation), qa (helping our QA department with testing our software), meeting (daily and weekly status meetings, mostly) and social (when I’m socializing with colleagues, not necessarily being productive per se, although such discussions are very often fruitful). To that, I add a generic work tag to these all categories except social, so that I can easily tally my whole work hours. I’m not actively developing these days, but when that comes back, I’ll handle tags for that as well. I also track lunch when I go for my lunch break πŸ˜‰

Whenever I start a new interval, it automatically closes the previous one – so if I switch from timew start work doc to timew start lunch, the interval lunch starts, and the interval work/doc is closed. If I want to stop the current interval without starting a new one (typically at the end of the day), I just go

$ timew stop

and no interval is running anymore.

To display what’s been logged so far, timew summary is the way to go. I actually experimented with tracking my Sunday yesterday, and if I want to know how long I spent cooking, I can look at my cuisine tag that way:

[isa@wayfarer ~]$ timew summary :yesterday cuisine

Wk  Date       Day Tags       Start      End    Time   Total
W17 2020-04-26 Sun cuisine 11:28:00 11:59:00 0:31:00
                   cuisine 18:04:59 18:28:09 0:23:10
                   cuisine 18:44:23 19:05:10 0:20:47 1:14:57

It gives me all the instances and a sum of the time I spent overall during the day. This is particularly useful for my work usage: I tag everything I do for work with work, and that allows me to keep track of the amount of work I’ve put in the day.

If I forget to start or stop an activity, it’s trivial to do a posteriori if I’m in an “open” interval, slightly more tricky but still feasible if I need to re-insert things in the middle of other things. Generally speaking, I haven’t found something that I wanted to do that I didn’t find how to yet. The interface can be somewhat clunky, but it’s surprisingly input-flexible (the easy example is that I can do “timew start work doc” or “timew work doc start” or even “timew doc start work” – and it’ll start tracking an activity with tags doc and work in both cases).

The documentation is also very good, allowing both discoverability of features and reference. There’s also ways of getting pretty charts, to export data as JSON, and to develop extensions.

Obviously, if you’re looking for something that synchronizes between several computers, or that you can use on your phone, or or or…. this may not be the software for you. (Although running it somewhere on the internet and tracking via SSH would probably be a viable option). Me, I just want to open a term, type timew start mystuff, close my term, and be done with it – and I don’t care about tracking different things on different computers, because the context of what I’m tracking is different anyway. If your needs align with mine, I can only recommend you have a look at timewarrior πŸ™‚

Advent of Code 2019

2019 was the fifth year of Advent of Code – and I consequently spent December waking up at 6AM and spending a lot of brain cycles solving puzzles to bring back Santa from the other side of the solar system, where he was stranded.

Let me quote myself to describe the whole thing to the people who are not familiar with it.  Advent of Code is an advent calendar with puzzles that can mostly be solved by programming: the input is a problem description and a user-specific input (as far as I know, there’s a set of a β€œfew” pre-validated/pre-tested inputs), and you have to compute a number or a short string, and provide that as a result. If you have the correct result, you unlock a star and the second part of the puzzle, which allows to unlock a second star. Over the course of the 25 first days of December, you consequently get to collect 50 stars.

When I wrote my Advent of Code 2018 blog post last year, it was December 26th, and I had solved everything – this year it took me until yesterday (so, December 31th) before I got the 50th star. I don’t know if the problems were harder or if I got worse at solving them (maybe a mix of both?), but I still made it before the end of 2019, so I’ll count that as a win πŸ™‚

This year, I worked in Kotlin, a JVM-based language designed by JetBrains, and that I enjoy quite a lot – it is fully compatible with Java, and allows for a much terser syntax, and requires to do things explicitly when it comes to mutability of variables and collections. I like it. My solution repository is on GitHub – beware, here be dragons… and spoilers!

And, like last year, let me give a few impressions of the different puzzles for this year. I WILL spoil the problems and at least hint at their solutions – if you want to start solving the problems with no preconception at all, you may want to stop reading here πŸ™‚

Continue reading “Advent of Code 2019”

New monitor, new setup!

I just replaced my 24″ monitor with a new shiny 27″ – 3 more inches in the diagonal, but 4 times more pixels – which required a bit of tinkering – so here’s a bit of a write-up, so that a/ I can share what worked for me and b/ I have something to refer to myself whenever I do that again (I’ll probably have to tinker is some similar ways for the laptop…)

General setup

I have a fairly… personal setup – it DOES work for me, but it’s definitely in the “less common” category, which makes searching for information somewhat more challenging.

  • First things first, obviously: I run Linux, specifically right now Ubuntu 19.04 Disco Dingo – that’s probably the most standard element of my setup.
  • I run it with xmonad/xmobar/dmenu as my user interface.
  • My main screen is now a 27″, 3840Γ—2160 (very pixels, wow)
  • I have a second screen, left of my main one, as a secondary screen, which I mostly use for “browsing documentation when I’m doing something on the main screen” or “chat window when I’m playing in full screen on the main screen”. It’s a 19″, 1280Γ—1024. So, yes, that means multiple-DPI setup – I will admit that the thought had not crossed my mind when I bought the new one. Eh.

So, essentially, this looks like this:

Display configuration

I have a fair amount of things running in the browser, so the first thing I experimented with was changing the scale of the pages of what I was browsing. That went okay on the large screen, but then the scale was completely off for my secondary screen – and switching scale levels depending on the screen would have been a major pain. There is some automated and experimental stuff there; since my setup is already atypical, I decided to not dig much more into why it didn’t work. (I tried. Vaguely.)

I ran into a reasonable solution almost by chance (I didn’t know exactly what I was looking for) here: Configuring mixed DPI monitors with xrandr. I’m now scaling the secondary monitor by a factor of 1.5, which makes it still “good enough for my use” when it comes to the look of the screen (it’s wobbly on my terminal. I can live with that.) and which is “compatible enough with my large screen” when it comes to displaying stuff that’s configured for my large screen.

I’ve been running a “screen setup” script for a long while (which I basically run every time I boot my computer – both on the laptop and on the desktop), so it was a matter of editing the xrandr line to the following:

xrandr --output DVI-D-0 --scale 1.5x1.5 --output DP-2 --pos 1920x0 \
    --mode 3840x2160 --primary 

So from there, I know that whatever I do on the large screen is going to be “good enough” on the secondary screen.


Chrome has been a bit of a pain. My first attempt was playing with the default scaling of the rendered web pages and fonts, but the tabs and the UI were still (as expected) super small. I finally found the right flag, specifically --force-device-scale-factor=1.5. As far as I can tell, there’s no way to make that configuration persistent at Chrome level (or, at least, I didn’t find it). And since I’m not starting my browser from an icon or a shortcut or anything like that, I couldn’t set it up there either. I ended up creating a google-chrome launch script in my personal bin directory (which was already setup in front of my PATH, thankfully, otherwise I would have required additional yaks) to pass the flag.

Moreover, the flag worked to scale the UI, but it also re-scaled the web page rendering, so I had to roll back my earlier config attempts. But now everything is fine and I can use Chrome without squinting too much, yay.

The “Save” dialog is still tiny, but I really don’t want to try to fix that now, so it’ll wait.

xmonad / xmobar / dmenu / trayer

My top bar with xmobar and the trayer were feeling a bit cramped, and so did my dmenu (the text-based launcher I use to start everything else). Some adjustments were required there:

  • I changed the configuration of the xmobar position in .xmobarrc to read position = Static { xpos = 1920, ypos = 0, width = 3456, height = 30 } ; the xpos parameter is set to 1920 because I don’t want that bar to be present on my secondary screen (and it’s setup to have a width of 1920); the width is 90% of 3840 so that I have 10% of the width for my trayer.
  • I wouldn’t have needed to touch my xmonad config if not for the fact that it’s launching dmenu, and that dmenu’s config is in the command line; I just modified the font of dmenu so that I have ((modMask, xK_p), spawn "dmenu_run -fn xft:terminus:style=medium:pixelsize=22") to start dmenu on Mod-P.
  • Finally, I only changed the height of the trayer (since the width is expressed as a percentage of the total width) so that it now reads trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --width 10 --transparent true --tint 0x191970 --height 30 --monitor 1 &. This part is in my “setup screen” script, so the modification here was minor.


I use gnome-terminal as my standard terminal: it’s the only thing I ever managed to configure exactly as I wanted it (set of colors, unicode handling, non-blinking cursor, and that sort of things.)

The font was on the small side on the highres display, so I switched to Terminus Regular size 22. I’m not convinced yet by that choice, because it feels bolder than I think it should be, so I may have to play with alternate choices at some point or another. For now, it’s good enough. And I don’t care about the UI scaling in general because I don’t use “anything else than writing on the terminal” often enough for it to be a problem.


I just zoomed to 150% in the default preferences in Accessibility. The menus and whatnot are still tiny, but I actually don’t care (because I don’t use them much). Good enough for now.


Darktable the software I use to do most of my photo post-processing. The picture area is much nicer on the new screen (ahem. It may also have something to do with “the picture area is much nicer on a clean screen.), but the interface was also very tiny. Two things there:

  • in .config/darktable/darktablerc, set screen_dpi_overwrite=150 – I didn’t feel the need to experiment more with other values, this works for me
  • in the UI settings (available from the interface), set the “width of the side panels in pixels” to 400.

It is necessary to restart Darktable after this modification.


Since I also started to learn how to use GIMP, it did cross my mind to set it up during my initial setup. Two things:

  • I defined the icon size to “Large” in Preferences > Interface > Icon Theme
  • I also defined the font name to “sans 16” in my theme file, /usr/share/gimp/2.0/themes/gtkrc (defined in Preferences > Interface > Theme).

And this can be reloaded without restarting Gimp πŸ™‚

IntelliJ / CLion

I’m using IntelliJ at work, and the rest of the JetBrains IDEs at home. These days, I’m using CLion to develop on Marzipan (my fractal generator). IntelliJ scales its interface depending on the UI font size; so in Settings > Appearance & Behavior > Appearance, I modified the custom font for a size of 20.

This doesn’t modify the editor font size, though, which needs to be defined in Settings > Editor > Font.

Also, I know have a stronger incentive to continue working on my fractal generator: now that I have a higher res screen, I’m tempted to generate high res images, so I need to optimize that πŸ˜‰ And also probably to decouple the size of the image from the size of my UI πŸ˜›

World of Warcraft

I’ll admit, WoW is one of the first things I tested after plugging in everything and checking that basic function was there. I’m running WoW on WINE – and this was actually the least painful experience: it started without problem, the UI scaled properly immediately, and everything was just as I left it. The only difference is that the cursor is smaller (and that I probably need to modify my mouse sensitivity). But all in all, flawless. And it still runs 100fps, which is cool. (I did feel the need to triple check that I was indeed running in that large resolution. It seems I am.)

Slay the Spire

Other game I play a lot, Slay the Spire – there, I had to adjust resolution manually, but once that was done, nothing to see here, move along.


This may seem like quite a lot of work, and some snark along the lines of “if you ran Windows/MacOS/GNOME/KDE you wouldn’t have to configure things in such a gazillion places” may be warranted – but to me the “having things exactly as I want them” is definitely worth a bit of extra work – as well as knowing that once the configuration is stable, it doesn’t break at every update πŸ˜‰

I’ll probably find a couple more things to fix in the near future, and I’ll update this post with my findings.

Marzipan update – now with context menu!

It may seem trivial, but I unlocked an achievement on Marzipan: I added a menu. I’ve been wanting to have a few “quality of life” improvements for a while now, but so far I had been hiding under the duvet of “I really don’t want to touch the UI/Qt code more than I strictly have to”.

My experience with graphical toolkits in general has never been great. It’s really out of my comfort zone; I tend to find that the tutorials on the Internet don’t have anything between an equivalent of “Hello, World!” and an equivalent of “here’s some advanced quantum mechanics” (I do suck at physics in general as well); I kind of have the impression that my use cases are dead simple and should just Be Available As Is and that For Sure I Don’t Need To Read All That Documentation. (Yeah, yeah, I may be somewhat guilty here.) And I get upset and impatient, and generally speaking it’s not a good experience – neither for myself nor for anyone else in the room. (My apologies to my husband!)

But when you’re the only coder and the only user of a project, at some point biting the bullet gets inevitable. Consequently, in the last pull request, I did a fair amount of refactoring. When I started my coding session, everything was contained in a QWindow, in which I was painting an image directly on the QBackingStore. I then read a bunch of stuff about menus, which made me update my QWindow to a QMainWindow – for which the QBackingStore seems less trivially accessible, so I modified that. But then, the refresh was only working when resizing the window, which kind of sucked (and I’m still not entirely sure why). So I put a QWidget inside my QMainWindow as a main widget, and that allowed me to have both the refresh and the menu – yay! I needed to tinker a bit more to get back the keyboard control (move them from the QWidget to the QMainWindow), and now it’s all nice and shiny.

The undo/redo function itself is basically storing the fractal parameters in a couple of stacks and re-computing on undo/redo – nothing fancy (and my memory management is utter shit – read “nonexistent”, I really need to fix that – and I don’t handle storing the orbits properly yet, but one thing at a time.)

As a result: I do have at least half-functional Undo/Redo, and more importantly, I have a reasonable base for future UI/QoL development: I hope that the major hurdle of figuring out how things might fit together is behind me, and I’m a bit less scared of it.

I can’t say I’m happy with that session, because I still have the impression that I tried to put stuff together while having no idea what I’m doing, and while having the impression that I’m not actually learning anything in the process, but this may actually be better than I think. We’ll see πŸ™‚

Some more Marzipan bitmap fun

I did track down my zoom/scaling issue that I was mentioning in the previous post, and I improved the distance map for my bitmap orbit coloring. I’m still not 100% happy with it – I need to think a bit more about what I want to do there exactly, but it’s still enough that I spent a couple of hours yesterday playing with Marzipan as a “fractal generating software” and having fun with it, and not only as a “software project I’m working on”. Granted, the “UI” is still mostly “let me change the color palette and recompile”, but I was having too much fun making pretty images to dig into making the UI usable πŸ™‚

So – here are a few pictures from my last “creative” session πŸ™‚

Purple explosion

For “Purple explosion,” the seed bitmap is a bunch of rectangles of the same size put randomly on the canvas.

Rainbow equation

“Rainbow equation” is dedicated to Matthias, who was the one mentioning I should put the set equation in the image. And it’s rainbow because why the hell not πŸ™‚

Lace flowers

“Lace flowers” uses one of the brushes from GIMP (Manju’s Flower – Large) as its base. I quite like this one, except for the fact that my processing of the flower should be smoother so that the end result is also smoother. That’s how I learn πŸ˜‰

Tux funnel

For “Tux funnel”, I used as a base the Tux Mono drawing by gg3po, Iwan Gabovitch, GPL licensed, via Wikimedia Commons.

One of the things that amuses me is the ephemeral quality of what I do here. Choosing to save a picture (and that’s still somewhat of an ordeal) is the only way to keep a trace of what I’m doing. I cannot get back to an image and try to “keep it, but improve it”: since I don’t have my settings and I don’t remember where I zoom, any new attempt will be a new image. I even didn’t keep my seed bitmaps so far – so reproducing an image would really be hopeless. And I quite like it that way.

Marzipan – bitmap orbits (and some bugs)

I worked a bit on Marzipan this week-end, and started playing with bitmap orbits. The idea is the same as for point orbits and line orbits: we look at the iterations of the escaping points, and we look at the distance on the plane between the complex numbers represented by these iterations and a given set on the same plane.

Since my creativity was apparently all used up by trying to make the algorithm work instead of trying to find a nice bitmap to play with, I simply have the name of my project somewhere on an image, as black&white. I pre-compute a distance map to that bitmap (in a very approximate and most probably buggy way right now – something’s not quite right there, but good enough for a first approximation) and I use that distance map to display the points I’m interested in when rendering the fractal.

The very annoying thing is that I do have a bug on the zooming algorithm – things tend to flip and/or to go to weird places. I haven’t been able to track it down yet: it must be said that I’m typically hopeless at handling 2D grids and scaling factors without breaking a neuron or two, and that on top of that, as mentioned previously, I’m probably making my own life miserable by using a non-standard window manager. I’ll probably need to go to KDE or something to debug this thing properly (sigh :/)

Still, in the meantime, I’m not unhappy with the results πŸ™‚

Marzipan progress

Today I added a couple of features to Marzipan, my fractal generator, so I’m going to show a few images of what I worked on πŸ™‚

First, I refactored the orbit trap coloring to be able to expand it with other types of orbit traps; then I added the implementation for line orbit traps. Turns out, adding random line and point orbit traps is pretty fun, and can yield pretty results!

A mix of line and point trap orbits

The other thing that I did was to add “multi-color palettes”: instead of giving a “beginning color” and an “end-color”, I can now pass a set of colors that will get used over the value interval. Which means, I can get much more colorful (and hence incredibly more eye-hurting) images!

Yay, rainbows!

And, well, I can also combine these two approaches to get NEON RAINBOWS!


Finally, I also played a bit with the ratios of the image that I generate – the goal is to not distort the general shape of the bulbs when zooming in the image. This is still pretty unsatisfactory, I need to make that work better (and to understand exactly how the size of the Qt window interacts with my window manager, because I’m probably making my life more complicated than it strictly needs to be there).

I had a lot of fun today implementing all of that, and then losing myself into finding fun colorings and fun trap orbits and all that kind of things. Now, the issue is that if I have more colors, I have more leeway to generate REALLY UGLY IMAGES, and I may need to develop a bit of taste if I want to continue doing pretty stuff πŸ˜‰

Now what crossed my mind today:

  • I believe I have a bug, either in the multi-color palette or in the orbit trap coloring – I’ve seen suspicious things when zooming on certain parts (colors changing whereas they should not have), so I’ll need to track it and fix it. I also made performance worse when playing with multiple orbits for laziness reasons, I’ll need to fix that too.
  • I have some more things that I want to experiment with on orbit traps – I think there’s some prettiness I haven’t explored yet and I want to do that.
  • I should really start thinking about getting a better UI. I want a have better UI, I don’t want to code the better UI πŸ˜›
  • I should at least add a proper way to save an image instead of relying on getting them from the place where I dump them before displaying them – that would be helpful.
  • I should also find a way to import/export “settings” so that a given image can be reproduced – right now it’s very ephemeral. Which is not necessarily a bad thing πŸ˜‰
  • I can probably improve the performance of the “increasing the number of iterations” operation, and I should do that.
  • The “image ratio” thing is still very fuzzy and I need to think a bit more about what I want and how to do it.

Smooth coloring of Mandelbrot

I got half nerd-sniped (by my dad, as it quite often happens) after my blog post on my fractal renderer – “but… your continuous coloring, there… how does it work?”. I had implemented it 8 months ago without really asking myself the question, so after answering “eh, some magic”, I finally got curious for real, and I started digging a bit more into it.

Starting from the Wikipedia article footnotes, I ended up on a couple of articles: Renormalizing the Mandelbrot Escape and Smooth Escape Iteration Counts, as well as smooth iteration count for generalized Mandelbrot sets, that mostly allowed me to piece together some understanding of it. I will admit that my own understanding is still a bit messy/sloppy – my aim here is to try to give some understanding somewhere between “apply this function and you get smooth coloring” and “spectral analysis of eigenvalues in Hilbert spaces” (it’s APPARENTLY related; I’m pretty sure I’ve known at SOME point what this group of words meant, but right now it’s barely a fuzzy concept. Math: if you don’t use it you lose it 😦 ).

Mandelbrot set and discrete coloring

Before talking about continuous coloring, let me set a few concepts and notations so that we’re on the same page.

We iterate, from a point c = x + y\mathrm{i} (where (x,y) are coordinates on the plane), the sequence z = z^2 + c, where z is a starting constant, typically 0, and we look at the points for which this sequence diverges to infinity or does not. The points for which the sequence does not diverge to infinity (i.e., the sequence is bounded) are part of the Mandelbrot set.

Counting to infinity is, however, a bit long. The first thing we can do is – if we know early that the sequence goes to infinity, we can stop counting: the sequence diverges. We happen to know that, for z = z^2 + c, the sequence diverges as soon as its magnitude |z| > R (or equivalently, x^2 + y^2 > R). (Note that the minimum value for this to work is R = 2.) So we can iterate on every point and, as soon as its magnitude reaches 2, conclude that the point is not in the Mandelbrot set. This process gives another information: the iteration at which this condition is met – which is an indication about how fast the sequence diverges.

That only solves a part of the problem – because by definition, the points for which the sequence is bounded will never stop the process in that model. So the second thing we do is to consider that if a sequence has not diverged after a given, fixed number of iterations, then probably it won’t.

Finally, for every possible number of iterations, we define a color (in a way that eventually makes the result pretty/interesting); we also define a color for points whose sequence does not diverge (and that are considered in the set); and we color each point according to that.

Now the thing with this approach is that the number of iterations tend to gather in “bands” – as in the following picture.

Discrete coloring bands

Smooth coloring

The bands come from the fact that two neighboring pixels “tend to” have the same number of iterations, and hence the same color. If the range of possible values is smooth, and if two neighboring points end up not being associated to the exact same value, and if the palette maps them to different colors, then the coloring should be smooth – even with a fairly reduced palette.

The idea is that we want to represent whether a sequence “barely” reached the escape radius or whether it could have done so almost on the previous iteration, so far it is from the threshold. Ideally, we represent that by a function that goes from 0 to 1, we get a nice smooth range, and that yields a nice smooth coloring.

Now many sources go from this statement to “and now, like a rabbit out of the hat, take \log(\log(z)) and we’re done”. Some of them mumble something about “deriving it from Douady-Hubbard potential“, but it’s not really satisfying either. I gave a quick look to that, and I may revisit the topic eventually, but right now the maths go wayyyy over my head 😦

Still, the main question I had was “well, if we’re only interested in mapping ‘the space between the bands’, why not just drop a linear operator on that and be done?”. My reasoning was as follows: we know that at iteration i, we’re above the radius of convergence R, and we were not in the previous iteration, that means that we can bound it. If z_{i-1} and z_i are the values of z at escape iteration i, we have, by definition of i, |z_i| > R and |z_{i-1}| \leq R. To bound z_i from the above, we write |z_i| = |z_{i-1}^2 + c| (by definition of z), which is less than |z_{i-1}|^2 + |c| (by triangular inequality), which is in turn less than R^2 + |c|.

Hence, our “escape margin” |z| - R is between 0 and r^2 + |c|, and we want to map that to something between 0 and 1, so let’s just divide those two things, define my fractional iteration count as \displaystyle i + 1 - \frac{|z| - R}{r^2 + |c|} and we’re done, right?

Hmmm. It DOES look smooth-er. It’s not… smooth. I have two guesses here. First: the triangular inequality is a bit too rough for my estimate. Second: it may be that I’m generally speaking missing something somewhere. Maybe a bit of both.

Now that’s where the “magic” happens. I’ve read enough on the topic to have some inkling that there’s actual, proper math behind that makes it work, but I’m not able to understand, let alone explain it in simple terms πŸ˜‰

The fractional iteration count that makes things work is \displaystyle i + 1 - \frac{\ln \ln |z|}{\ln 2}. The best intuition/simplified explanation I’ve seen for this comes from Renormalizing the Mandelbrot Escape, where they have the following quote:

The following simplified and very insightful explanation is provided by Earl L. Hinrichs:
“Ignoring the +C in the usual formula, the orbit point grows by Z := Z^2. So we have the progression Z, Z^2, Z^4, Z^8, Z^16, etc. Iteration counting amounts to assigning an integer to these values. Ignoring a multiplier, the log of this sequence is: 1, 2, 4, 8, 16. The log-log is 0, 1, 2, 3, 4 which matches the integer value from the iteration counting. So the appropriate generalization of the discrete iteration counting to a continuous function would be the double log.”

Smooth coloring

So, there. I’m going to leave the topic there for now – that was quite fun to dig into, but I do need more advanced math if I want to dig deeper. It might happen – who knows – but not now πŸ™‚