Free software developer and hobby mathematician. Author and lead developer of #PuTTY, and "Simon Tatham's Portable Puzzle Collection", a set of one-player puzzle games running on many platforms. Also various smaller or less well known things.
Public Key
npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Profile Code
nprofile1qqs9u8kzajc98afv4h7vg9pf0d54v2n5mhnpld0zsy36sfnn0vedx8spz4mhxue69uhhyetvv9ujuerfw36x7tnsw43q3htw3a
Show more details
Published at
2026-08-17T09:08:35Z Event JSON
{
"id": "5363a36a448adabb7351d38033355771f68b0e9588095ea237635ecf7d098b6f" ,
"pubkey": "5e1ec2ecb053f52cadfcc414297b69562a74dde61fb5e28123a826737b32d31e" ,
"created_at": 1786957715 ,
"kind": 0 ,
"tags": [
[
"proxy",
"https://hachyderm.io/users/simontatham",
"activitypub"
],
[
"client",
"Mostr",
"31990:6be38f8c63df7dbf84db7ec4a6e6fbbd8d19dca3b980efad18585c46f04b26f9:mostr",
"wss://relay.ditto.pub"
]
],
"content": "{\"name\":\"Simon Tatham\",\"about\":\"Free software developer and hobby mathematician.\\n\\nAuthor and lead developer of #PuTTY, and \\\"Simon Tatham's Portable Puzzle Collection\\\", a set of one-player puzzle games running on many platforms. Also various smaller or less well known things.\",\"picture\":\"https://media.hachyderm.io/accounts/avatars/111/096/602/447/828/617/original/f8b7e5b8f2520fa3.png\",\"nip05\":\"[email protected] \",\"fields\":[[\"Web site\",\"https://www.chiark.greenend.org.uk/~sgtatham/\"]]}" ,
"sig": "63c1f8d4fbc3ec0d328d0513fa7163f92da8afccd47c121f5572ec7aa34b8a94a38d76bf9ea0e641d5a86f55ae23e8bc972cc8ce7f3cf37585433820cfc80bf0"
}
Last Notes npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Well, after over a week I thought I might have got away with it this time, but no. I've just had my first email asking me to confirm that #PuTTY is unaffected by last week's #PHP vulnerability. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I often find typos cognitively fascinating. Some typos are just last-moment motor errors: "aimed for this key and hit the one next to it". Those are easy to understand. But others reveal much more interesting things about the mental process of driving your fingers on the keyboard, or even composing words. I once aimed to type 'programs', and unexpectedly 'rpgroasm' came out. All the left-hand letters appear in the correct order, and so do all the right-hand letters, but they're interleaved in a spectacularly wrong way. That suggested that one part of my brain had sent an independent set of instructions to a subthing controlling each hand, and everything worked fine except the synchronisation between the two subthings. There's another class of typo which I think of as 'brain got ahead of fingers': you're typing one word, but already thinking about a word that will come after it, and there's a crossover and you end up mixing up the two. Like 'bit bisect' in place of 'git bisect' – you're already planning the b, and it comes out of your fingers prematurely. (Appropriately, as I typed _that_ sentence, 'in place' came out as 'pl inace', wow.) Even the 'boring' motor errors have something cognitively interesting about them, just on the reading rather than writing side. We see 'adjacent letter on the keyboard' typos a lot, so our brains get good at correcting them. Sometimes _over_-correcting, so that you imagine a typo where none existed. When a sentence is a bit unexpected in context, I've been known to consider the possibility of a typo before figuring out how the sentence as originally spelled makes sense. The best ones of those are when I hadn't even _realised_ the two words were a typo apart. I misread 'auction' as 'suction' once and it was a total surprise! I wonder if there's any serious scholarly study of this kind of thing. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I had a really good afternoon at work yesterday. I had a long pair-debugging session with a junior colleague, helping to track down a crash in something they were in the middle of writing. We started from the visible crash and backtracked to find the root cause, investigating everything along the way that looked odd. We were expecting one bug, but ended up finding two. The load instruction that first put a bogus value into a register was loading from a totally wrong address, *and* there was some weird data at that address that shouldn't have been there, and those two wrong things had independent causes. Neither bug by itself would have caused the crash: it needed both. If we'd found only one of them – for example by jumping to a conclusion, speculatively fixing something, and observing that the crash had gone away – then we'd have thought the whole problem was fixed, and been wrong. But by going through methodically to the point where the first weird thing happened, we were able to find both bugs. We fixed one on the spot, and ended the session knowing what to do about the other. My colleague had asked for help with the aim of learning debugging skills (both low-level and general) as well as getting the bug fixed. I don't always have success when I do this on real-world problems, because if the problem turns out to be too hard, I can't solve it _and_ explain what I'm doing at the same time. Here, it was just about right: challenging, but not too much so. I learned things too. We had to use LLDB rather than GDB, and I'm not nearly as familiar with LLDB, and now I'm more so. (It has a proper hex-dump command!) So it was a useful learning experience on both sides, as well as a successful bug-fixing exercise. They apologised afterwards for taking up my whole afternoon, but I think it was time well spent. And to put the icing on the cake, nobody at any point suggested throwing an LLM at anything. The only thing that bothers me about it is that this kind of experience is now rare enough in my life that I feel moved to toot about it when it happens! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham That sinking feeling when a program isn't giving the right answer, and you've checked, double-checked and re-checked all the obviously hard parts of the calculation, and they all seem to be doing the right thing. You haven't found the location of the bug _yet_, but given all the places it _isn't_ ... "When you have eliminated all the difficult parts, Watson, whatever remains, however embarrassing, must be a mistake in a really easy part." npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham #Zoom 7.2 appears to have fixed this unwanted paste request issue. #nevent1q…68ay npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham What's the right way to generate a floating-point number representing a sample of a uniformly random variable in the range [0,1]? You might think: choose the exponent to be 2⁻¹ with probability 1/2, 2⁻² with probability 1/4, etc (with some fudge when the range runs out). And then, once you've chosen an exponent, choose uniformly at random from the mantissas. Wrong! The #IEEE754 ethos is to calculate the mathematically perfect result, and then round that to the nearest representable number. So you must choose each floating-point number x with probability in proportion to its rounding 'catchment area': the range of real numbers above and below x that will round to x. Well, isn't that what I just said? No, it isn't – because powers of 2, at the boundary between two exponents, have a catchment area half as big on the left as on the right. So you must choose _most_ numbers with probability 2⁻ⁿ where n varies with the exponent, except that powers of 2 are chosen with probability 2⁻ⁿ + 2⁻ⁿ⁺¹. Arrgh! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Best bug of the day: I have a tiny script that takes a string and constructs a URL that looks it up on Wikipedia. I used the script on the string "ST:TNG" (wanting to check its broadcast dates). Presumably due to missing escaping, I got a search page that had failed to find the string "TNG" on Sotho Wikipedia. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham This week I realised that #git bundles are even more awesome than I thought. And I was a fan of them already! git bundles are my favourite way to receive patches by email. They're very small; a single file can contain a whole series of commits, instead of having to cat-herd one attachment per patch (or worse, one _email_ per patch); they're so obviously binary that not even a really bad MUA is likely to "helpfully" do text processing on them, and a user isn't tempted to paste them into the message body instead of making them a proper attachment. Also, they explicitly state which existing commit the changes started from, which is helpful for handling conflicts, and for knowing what you expect the sender to have taken account of. But this week I realised there's one more advantage: if the same commit appears in more than one bundle, it will be reliably recognised as identical. This is helpful when you're the one _sending_ the bundle. Suppose I send someone 3 patches. Half an hour later I smite my forehead and realise I need to send one more. With 'git format-patch' I probably send just the extra patch, so that the recipient has to extract attachments from two emails and apply them in the right order. Or I send all 4 patches in email #2, and the recipient gets errors if they do the obvious thing with that email after they've already applied the patches from #1. But with git bundles, I can send a bundle of all 4 commits in email #2, and it will do the right thing _regardless_ of whether the recipient had already pulled from the previous bundle in email #1! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Had a dream that someone I trusted became a vampire, and imprisoned me to feed on later. Awaiting my fate, I wondered how it could possibly have happened. Then I remembered that the day before, they'd incautiously loaded a dodgy shared library, and woke up in a cold sweat. #technightmares npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Sneak preview of a not-yet-production-quality contribution to Mines. I expected this particular mode to be brain-meltingly confusing, and it is, but not for quite the reasons I'd guessed. https://media.hachyderm.io/media_attachments/files/117/146/775/073/631/364/original/c15adc1ca8e71f6b.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I mean it ought not to even be _possible_ to assign someone a task in your organisational ticket tracker if they're not in your organisation. The ticketing system should _make_ it impossible, to prevent doing it by accident, and also this kind of (semi?)deliberate abuse. Why is it even possible to assign me a ticket if I don't have an _account_ on your tracker? The idea that you can assign tickets to any old email address is just ridiculous. Being able to have an arbitrary email address on the ticket's CC list makes sense, at least sometimes. Some ticket trackers are public-facing, for external people to make support requests. You need to be able to keep the external requester up to date on progress. Fair enough. But _assigning work_ to an external person is not the same thing! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham This month's annoyance: people non-consensually assigning me tickets in their organisation's ticket tracker. As #PuTTY maintainer, I'm often asked to fill in annoying government paperwork, usually from someone else's country. "All the software our organisation uses must comply with the National Foo Directive, so please fill in this huge questionnaire to confirm that you do." Often without bothering to even mention which nation's National Foo Directive it is. Often without noticing that a lot of the questions are obviously meaningless (e.g. assuming the product is hardware, or a cloud service, or something). And _always_ without thinking through the difference between a commercial software vendor (who has a financial incentive to do this kind of paperwork, and can employ staff to do it out of the revenue stream from selling the software itself) and an unpaid free software maintainer (LOL no). In the past, this has generally involved an email from some actual employee tasked with going through the Big Spreadsheet of all the software that (they know) they're using. So you can reply to the person and explain their misunderstanding. (Generally I wait until the followup email before bothering, in case they manage to engage their own brain before then, which sometimes they do.) But a new and unwelcome development is that they're starting to do it by automated ticketing systems, so I get an email saying "[employee] has assigned you a task" and I have no doubt it's going to carry on automatically pestering without any way of making it listen to reason. Probably I have no choice but to start blocking these ticketing systems' email addresses. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…h6ty no, and I'd be surprised if it worked that way, because my usual experience is that people have wildly varying styles for how they use text editors and command lines. To be able to infer someone's thoughts just from how they were moving the cursor, I'd expect they'd have to be not just at about my skill level, but more like an actual clone of me in particular! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Sometimes I pair-program, or more usually pair-debug, as a teaching technique: solve a problem while screen sharing, narrating my thoughts and reasoning out loud to show the other person how to track down whatever kind of problem it is. But it has to be on a reasonably simple problem, because narrating my thoughts coherently _also_ requires thought and effort, and that reduces the amount of brain I have available for the actual problem. For the hardest problems I'm capable of solving at all, the solving and the explaining don't both fit in my head at once, and I have no choice but to say "sorry, I need to concentrate completely on this, and once I've found the answer, _then_ I'll tell you how I did it." This week, someone pointed out the contrast between this and the 'rubber duck' / 'cardboard programmer' effect, in which explaining out loud what's confusing you can _help_ you to figure it out yourself. I'd never noticed that contrast before. So, some problems become easier to solve if you talk through your thoughts out loud, and others become harder. That's annoying! Now you also need the proverbial "wisdom to know the difference". npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…3ra7 I'm confused – the RSS feed is a static file served from the same web server as the main PuTTY web page. Really, that particular HTTPS request takes over a minute to satisfy? But HTML pages on the website still load quickly? Is that what you're saying? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham A further announcement related to #PuTTY: we are retiring the 'putty-announce' mailing list for release announcements. Today's release, 0.85, was announced by email to that list, but it's the last. Running a mailing list always involved a lot of work dealing with bounces from defunct addresses and email from ticketing systems that people had subscribed to the list. But now we've been starting to find that we're accused of spam, because people will leave a company, forget to unsubscribe from putty-announce, and after a few months, their ex-employer will turn their old address into a spam trap. As an alternative announcement mechanism, I've set up an RSS feed. See https://www.chiark.greenend.org.uk/~sgtatham/putty/updates.html (Of course, anyone reading this toot is receiving release announcements anyway via my Mastodon account! But if you were also using the mailing list, please spread the word that it's going away.) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham We've released #PuTTY version 0.85. This release fixes five security vulnerabilities. None is _known_ to be severe, but at least one of them could be. The potentially severe ones: • Pageant could use freed memory if one of its clients deletes an encrypted private key while the user is still answering a passphrase prompt triggered by another client's attempt to use the key. If this was triggered via a malicious user with access to agent forwarding, they might also have had some control over the contents of the reused memory. • The OpenSSH encrypt-then-MAC cipher modes (but not the ones PuTTY uses by default) had a buffer overflow if a server sent the largest permitted packet size. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…3dla @nprofile…tg2e sounds on brand for AI output! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Intellectual property law is overreaching these days, but they can't turn _everything_ proprietary. For example, the answer to "what's the longest English word with no repeated letters?" is still uncopyrightable. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham There's been some discussion recently of what motivates free software authors. #PuTTY was originally just a Telnet client. It became really useful when I added an SSH backend, during my last year at university. I did that partly because I thought network security seemed like a good idea, but mostly because I had exams coming up, and it seemed like more fun than revising for them. Three years earlier, #NASM started off as a long design discussion with about 17 people on an email list. Eventually the time came to write code. I volunteered to write an initial prototype for everyone to work on improving. Another member of the mailing list – also a student – suggested that he'd write one too, and then we could decide which one to use. He was at Oxford and I was at Cambridge, so naturally I went all-out to make sure mine would be better. (He never finished his, as it turned out.) In other words, my two biggest serious free software projects were motivated significantly by Oxbridge rivalry and by revision avoidance. I'm not quite sure that's how a university education is _supposed_ to bring benefit to the world, but I'll take it! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham What's a _good_ example, for teaching recursion to a novice programmer? I think a common mistake is to use a too-easy introductory problem, which you can solve easily without recursion. Like calculating a factorial, or a Fibonacci number. The student already knows a way to do that, and the recursive implementation is more confusing than the code they already had. So they get the impression that recursion is a way to make your life unnecessarily extra difficult. To _motivate_ recursion – to convince the student that it's important and worth learning – you want an example where the problem really _needs_ recursion. (That's never _literally_ true, because you can always write things another way _somehow_; but if the other way involves an explicit stack simulating the same recursion, and a loop in which each iteration might either increment or decrement the stack pointer, the student probably won't see it as more natural or more readable!) Or, if there _is_ a solution that has nothing to do with recursion, it should have an obvious disadvantage of some kind, so that the student can easily see why you wouldn't want to do it that way. Like using a huge amount more memory. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Around 2002, at work, I was involved in implementing support for Japanese character sets in an embedded library. A colleague from our Japanese office visited, and we worked together on it for about a week. Before she left, she gave me a small present in thanks, labelled "Japanese Luck Bringer". Two years later, it fell off a high shelf on to my head. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Bah, why isn't the 🔍 emoji at U+221B? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Sometimes the only hint you need to solve a puzzle is that it can be solved without a hint. Last week a colleague and I were staring in puzzlement at something weird in a terminal window. After 30 seconds I said "Oh! I've worked out why that happens!". Five seconds later, she got it too, because _now_ she knew that it was possible to get the answer without going off to do an hour of research, and that narrowed the search space of what the answer could possibly be. And this week, I was puzzled by a compiler error message referring to a particular C++ header, so I went and looked in (one implementation of) that header to see what it did. I found it did nothing interesting, or magic, or tied in to the internals of the compiler, at all. And that was enough to make me figure out what was going on with the error message – again, because there just weren't that many options left. When it happens, it feels like one of those deliberate puzzles in which Paul says "Ah, now I know what the numbers are!" and Sophie replies "Ah, in that case, so do I!" Only somehow reality managed to create the same effect by accident. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham (Brought to you by a conversation yesterday about 'ifconfig' and 'route' being deprecated in favour of 'ip'. So not very topical. But the point about trust erosion was a thought I hadn't had before, and retrospectively realised I was feeling it myself. And I think it applies more widely than Linux network management utilities.) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham When someone redesigns, replaces or rewrites a technology that's been around forever, there are lots of reasons why people might not be enthusiastic. Some are less obvious than others. There's plain inertia, of course: "The new way of doing things isn't the way I'm used to." There's more active frustration: "I already learned how to do this once, why are you making me learn it all over again?" And capability mismatch: "Arrgh, this specific thing I used to do a lot is difficult to do in the new system." But perhaps less obvious, there's an erosion of trust. When a thing's _never_ been replaced, you learn it with reasonable expectation that the knowledge will serve you well all your life. But after it's been replaced _once_ by enthusiastic rewriters, you don't have that expectation any more. Who knows, in five years the next batch of enthusiasts might rewrite it again! So it doesn't feel like learning the new thing will pay back on the same time scale. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham If you think about it, heat is really *weird*. Instead of regarding it as a first-class concept of physics, see it for what it is – a statistical aggregate of individual particle motions. And then consider the requirements of a life form: You need raw materials of various kinds, to make more of yourself, and repair damage. You need to get rid of garbage from that construction. You need energy to power the work, and for auxiliary activities like moving around. You need an absence of various harmful substances and phenomena. All logical enough, from first principles. But you also, for some reason, need constant gentle massaging by all the particles of whatever substances surround you. Not only that, but the vigour of the massage has to remain constantly within a narrow range. Too much, *or* too little, and the organism dies. What?! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I have a Zoom meeting at 10:00, in one minute's time (as I type this). Zoom is reporting that the meeting is in 2 minutes. Outlook already says "now". When did timekeeping on computers become this difficult? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Firefox, in some unwelcome new feature: "Shake your device to summarise this page in seconds" Me: <holds device as still as possible> npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Ha. I just web-searched for the Windows API function "RegSetValueEx", and some of the search hits I got back were about regular expressions. I see how that happened, but I hadn't anticipated it at all! Probably something somewhere reckons that "RegSetValueEx" and "set regex value" are essentially the same phrase, or similar. (noai.duckduckgo.com, but I assume it has no choice but to do the modern vector-search thing regardless) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Never underestimate the value to understanding of getting your own hands dirty. Even in a purely informational context. I lent a colleague "The Soul of a New Machine", which was among my own formative reading as a teenager. She asked me a good question: when they extended the 16-bit Eclipse instruction set to support 32-bit addresses and data, without a mode bit that switched to a completely different ISA, how did they find room for all the new stuff? Did all the 32-bit instructions have to have extra-long encodings, or what? To try to answer that, I dug out the architecture manual on the web, and hand-typed the instruction encodings into a text file. My _plan_ was to process that file with Python scripting until I'd organised it into some kind of a coherent layout. It didn't take me too long – I'm a fast typist. In fact, I found that by the time I'd typed in all the encodings, I _already_ had a sense of how the general layouts worked, just from having got used to the patterns I was commonly seeing. "Oh yes, another of those instructions with a 1, two register fields, and 11 fixed bits, there are tons of those." I didn't end up finishing my Python analysis tool at all, although the half-finished form of it was useful as a cross-check for transcription errors. By the time I'd done the data entry, I already had the understanding I'd been hoping for. I've always found playing music is a better way to appreciate it in depth than listening, because your mind necessarily engages with every single note, and you notice little details that would have otherwise gone in one ear and out the other. This seems much the same. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham On my quasi-blog: "Nitpicking the shell history scene in 'Tron: Legacy'" https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/tron-legacy/ A detailed look at a still from a 16-year-old film, which I recently found to make a good educational exercise, with the bonus of being fun. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Many (but not all) programming languages syntactically distinguish "statements" and "expressions". One intuitive idea you might have of the semantic difference is that a statement takes action (at least modifying a variable, maybe also doing I/O or the like) whereas an expression computes a value. Of course, in most such languages, the semantics cross over. Some actions also naturally return a value (e.g. do some system call and tell me whether it was successful), and some computations can have side effects. So this is only a difference of emphasis, not a hard line. Has there ever been a programming language in which the distinction was rigidly enforced, so that nothing you could write in an expression could have side effects? A language in which expressions _only_ compute values, and if you want to change the program's state, you _must_ use a statement? (Not counting vacuous cases like pure functional languages.) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham We've released #PuTTY version 0.84. This release fixes three security vulnerabilities, although all are minor as far as we know. One allows a malicious server or MITM to make PuTTY crash with a double-free, although we don't know of a way this can be exploited for worse than a DoS. Another allows the same MITM to cause an assertion failure (and maybe lose your scrollback) instead of giving the right error message. The third marks text from the server as "trusted", but only when connecting via Telnet or similar through a proxy that asks for authentication. New features: support for running a command before connecting (e.g. to perform wake-on-LAN, or a port knock), and on the Unix version, support for displaying 'pre-edit text' (Unicode characters you're half way through typing via a multiple-keystroke sequence). Other bug fixes include (on Windows) fixes for spurious network errors using proxy authentication and for mishandling of cursor blinking when it's disabled in the OS, and (on Unix) better support for running on Wayland. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Every time I see a software licence agreement containing the heading "Term and Termination", I wonder if Jane Austen might have had a day job as a corporate lawyer. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham It's the annual "change my work password" day. (Yes, I know, don't tell me, tell the IT department.) For credentials I'm going to type a lot, I still prefer a short password full of strange characters to a long passphrase made of words. It's more effort to memorise, but once that's done, it's faster to enter than a long passphrase – a benefit that lasts the rest of the year. My current memorisation technique involves a recurring timer. Every N minutes, an alert goes off, and I stop whatever I'm doing, run 'su $USERNAME -c "echo ok"', type my password, and make sure it did echo "ok". I do the password change first thing in the morning, and over the course of the day, increase the period between memory checks, from 5 minutes down to 15 or 30, so that it moves from short-term to long-term memory. If I find I've forgotten it in one of these tests, I'm allowed to look it up, but in every test I must first try it from memory and _then_ find out what I got wrong. And then retype it right. I like this technique because it's simultaneously practice at remembering the password, and practice at typing it quickly and accurately. Even the "do it right now, interrupting whatever else you were doing" aspect is deliberate: it trains the skill of remembering the password _even while distracted_, which is actually necessary, if e.g. you need to 'sudo' something in a sudden emergency that's taking up most of your brain. Reinforcing the new password periodically over the course of the first day is generally enough that when I come to log in the next morning I can remember it even after a night's sleep. And then I'm over the hump. But one problem I still haven't solved is remembering, the next day, *that* I changed my password. It's still common for me to type the old one three times running before I realise what the problem is! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Sauron Has only got One Ring Three others Are on the Elven-kings Find them In darkness bind them Along with mortals, And Dwarf-lords, And things (Mordor Army marching song, Third Age, apocryphal) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham What's the most interesting meal you can serve, consisting entirely of foods and drinks mentioned in well-known theorems or constructions of #mathematics? You can make a roughly balanced meal using • the Ham Sandwich Theorem, for protein and starch • Conway's game of Sprouts, for vitamins • the Blancmange Function, for a dairy-based dessert. https://en.wikipedia.org/wiki/Ham_sandwich_theorem https://en.wikipedia.org/wiki/Sprouts_(game) https://en.wikipedia.org/wiki/Blancmange_curve But it's not too inspiring, flavour-wise. Can it be made more interesting? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham #TIL that Ubuntu has a 'distro-info-data' package containing two tiny CSV files, one for Debian and one for Ubuntu, telling you which numbered versions correspond to which cutesy codenames. I only spotted it because the name showed up in 'apt upgrade'. But it's a more convenient source of that data than anywhere else I've ever looked! Previously when I've forgotten that kind of thing I've looked it up on Wikipedia, or on the Debian website, or in extreme cases by instantiating a container and 'cat /etc/os-release'. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham A thing I've only just realised about #less: When you press '/' to search forward, or '?' to search backward, and enter a regex, two changes of state occur: 1. The regex is remembered, so that 'n' or 'N' will search for the same thing again. 2. The direction is remembered, so that 'n' searches in the same direction again, and 'N' searches in the opposite direction. I've only just noticed that #2 happens at the instant of pressing / or ?, not at the instant of pressing Return. Because it takes effect even if you change your mind and cancel the attempt to input a new search regex: • enter "?foo", which searches backward for /foo/ • press 'n', which searches backward for /foo/ again • press '/', which prompts for a forward-search string • press Backspace, which cancels that prompt • now pressing 'n' searches *forward* for /foo/ I think this must be why I sometimes get confused about which direction I'm searching in. But also, wow, it's taken me _decades_ to notice. I think it must be because when I start and then cancel a prompt like that, I don't consciously remember doing it at all – I expected so strongly that it would be a no-op that I forget it immediately! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Around 2000, when #PuTTY first started to become popular, it brought me more and more email. At some point it became hard to answer it all, and my inbox started to grow. Young and optimistic, I came up with a plan to fix this. I drew a line in my inbox (by sending myself an email with subject "---------"), and told myself that I'd keep the region below the line clear, and every few days, deal with a few messages from above the line, until eventually the line was at the top again. Of course, it didn't work at all. Soon enough there was a huge backlog even below the line, and the stuff above the line was all still there. The line became part of the problem, not the solution. It took a historian friend of mine to point out the fallacy. To answer more mail, you must spend more time answering mail. No amount of classifying, rearranging, or reorganising it makes there be less of it. You must either answer the mail or decide not to. (And once there's too much of it, only one of *those* is possible.) This is a post about management methods that respond to any problem by spending more rather than less time moving issues around the bug tracker or kanban board or what have you. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham The existence of firewalls and air gaps implies that a truly comprehensive computer security posture ought to also involve earthworks and water cannons. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Another week, another tiny utility. I was in a Zoom call last week with very poor video quality, which we figured out was because I was also running a long compile which wasn't leaving Zoom enough CPU to work with. So I paused the compile with ^Z and things improved. Three hours after the call, I found the compile job, still suspended, in a window hidden behind something else. Oops. If only I'd been able to arrange to _automatically_ resume the job when the Zoom call terminated, instead of relying on my own memory! I already have a tool 'after' which waits for a specified Unix process to terminate. But Zoom stays running even when not on a call, and there's no reason to suppose it puts the call in an easy-to-identify subprocess. But it does put it in a separate _window_. So I wrote a tool 'xafter', which waits for a specified X window to close. Completely trivial – X11 makes it easy to ask for StructureNotify events on a window whether it's your own or not. Now if I suspend a job that's interfering with my meeting, I can immediately type 'xafter;fg' and select the meeting window, and then it'll restart automatically. I have no idea if it would be easy to write analogues of this for Wayland, or Windows, or Mac. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham When to automate a repetitive task: NO-BRAINER: "This is obviously going to be faster to automate than to do it by hand _even once_. Let's automate it right now, and not do it by hand at all." FORESIGHTED: "Doing it once by hand is faster than automating it, but I'm going to have to do it a lot of times, so it still saves time to automate it first." NEED A RUN-UP: "I don't yet understand this task well enough to automate it, so I'll do it a few times by hand first to get the idea." RAN OFF THE RUNWAY: "Great, now I've done this by hand a few times, I think I can automate it reliably! Oh, oops, turned out I only had one more case of it left to do." TERRIFIED OF RUNNING OFF THE RUNWAY: "This is a one-off, so it would be a waste of time to automate it, I'll just do it manually." [next day] "Oh, oops, I made a mistake and have to do it again. But it should be fine this time." [a month later] "Even though I've had to redo it 25 times already, surely _this_ is the last time? So it would still be a waste of time to automate it." npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham A "naming things is hard" anecdote I was just reminded of by an old web page: In the 1990s, when 32-bit versions of Windows were introduced, a new executable file format was needed for native 32-bit programs. The existing 16-bit file format used by Windows 3.x was called "NE", for "New Executable". The 32-bit one was named "PE", for "Portable Executable". Windows 95 could still run 16-bit Windows 3.x programs. But Windows 3.x couldn't run the newer 32-bit ones. (Ok, there was Win32s, but it had very restricted usefulness.) In other words, the format called "New" was the older one of the two, *and* the format called "Portable" was the one that didn't work everywhere! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham My quixotic campaign this month: pushing back on people imagining that they're saving effort by doing a sloppy job of something. It's not that it _never_ saves effort. But often what it really does is push effort on to somebody else. For example, some software gives vague error reports like "Could not download file", which is so vague that you can't even tell if the network was down (couldn't _fetch_ the file), or your disk was full (got it, but couldn't save it). This style of error report is certainly less effort for the programmer. But it costs the end user the same effort, because they now have to go through a long troubleshooting checklist of all the things that _might_ be wrong, instead of getting a message that pointed them to the true one first time. And if this happens to a lot of users, it's _wasted_ more effort than it saved. Not just programming, either. Leaving out information in a report costs the reader effort going and finding out the missing parts. More subtly, putting _too much_ information in a report – including lots of irrelevant stuff – means the reader has to do the work of figuring out which parts are useless, because you didn't do it yourself. I don't mean "never do these things". Sometimes moving the effort from yourself on to somebody else _is_ the right thing to do (e.g. because it's the other person's actual job). Or it's the least bad thing under the circumstances. Or you simply have no choice. But you should know you're doing it, and call it by its true name. It's only "saving effort" if the total amount of effort by _everybody_ is less afterwards. Otherwise it's moving effort around. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Dear Lazyfedi, can anyone recommend a software tool for hand-directed graph layout? If I have a graph (say, of a finite state machine or something), I can write it in Graphviz DOT format and feed it to xdot, which will do a complete job of layout: decide where the nodes go, figure out some nice curved paths for the edges so they don't go through other nodes or cross over too often. But if it doesn't do a good enough job, I don't know of any way to slightly tweak its output. As far as I know there's no way I can overrule its decisions about where to put the vertices, but still have it do the rest of the job for me like drawing all the edges. If I'm not 100% happy with its own choices, I have to do the whole thing myself instead. Anyone know of a good interactive tool that will let me drag the vertices around, and automatically move the edges with them? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham "I came here in a taxi with the number 1729. What a dull number that is." "On the contrary, it's a very interesting number! When it appears on my clock, it means it's one minute to going-home time." npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I've recently been rewriting my substitution-tiling transducers code in #RustLang, replacing the old Sage code. One reason is that Sage is harder to run these days. It vanished from Ubuntu some time between 22.04 and 24.04 and hasn't (yet?) come back. Another reason was that reimplementing all my algorithms from scratch was a valuable exercise in making sure I understood them, when I wrote them up for my arXiv preprint. (Which I'm hoping to produce an improved edition of at some point, filling in some gaps.) But the third reason is shown in these pictures. The hexagonal one is straight from one of my blog posts, showing the centre of one of the 6-way 'singular' instances of the Spectre tiling. It takes my Sage code 2 minutes in total to generate that SVG. The second image is the result of the Rust version of the code working for the same 2 minutes. The hexagon marked in the middle shows the boundary of what the Sage code had calculated. Much faster! #TilingTuesday #RiiR https://media.hachyderm.io/media_attachments/files/116/085/101/547/910/606/original/06a509f3e1089032.png https://media.hachyderm.io/media_attachments/files/116/085/103/148/718/245/original/8e3a5cb5341350b8.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham It's very annoying the way I have to get half way through writing a long detailed explanation of why someone is wrong before I figure out why they're right. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham An important milestone in a half-written piece of software: the point where, when it disagrees with you on whether an input file is legal, you start finding more often than not that it is right and you are wrong. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…xja6 @nprofile…f38c @nprofile…dxrd ... I think I begin to see. If you have a face of degree > 3, then perturbing its vertices might leave it nonplanar. My knee-jerk answer is to instead perturb the face normals instead of the vertices. But that's only doing the same thing in duality, so you have the flip-side problem that a _vertex_ of degree > 3 might separate into two vertices. So if you had a solid in which all faces had degree 4 or more, and so did all vertices, you might manage to arrange that either kind of perturbation destroyed one or the other. Except that that doesn't work in 3 dimensions, because those constraints are incompatible with Euler's formula. But in four dimensions, Euler's formula isn't quite the same. So presumably the answer is that it _does_ permit the analogous lower bounds to coexist? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…zvpa on a similar theme, ANTHROPIC CLAUDE → DO UNETHICAL CRAP GOOGLE AI OVERVIEW → I GAVE WOO, ERGO EVIL npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham In programming, we have a nice pair of opposed acronyms: • DRY for "Don't Repeat Yourself" • WET for its opposite, "Write Everything Twice" (or "We Enjoy Typing") But there's an intermediate position. The benefit of DRY, other than brevity, is that if a thing is specified just once, the specifications can't get out of sync with each other. If you can't manage that, the next best thing is to make sure the compiler or test suite _checks_ that they're in sync. You have to do more typing than you'd like, but at least you've removed the risk of an accident, which is the _most_ important thing. (For example, in Rust, if you add a new branch to an enum and forget to update one of its match statements, the compiler complains about the one you missed.) I feel as if there ought to be a nice intermediate acronym for that state of affairs, so you can say "Weeell, it's not as DRY as I'd like, but at least it's only MOIST." Or DAMP, or HUMID or something. "Match Or Interpreter Spots Trouble"? "Disallow Almost-Matching Programs"? "Holler Unless Many Instances Dovetail"? Not sure about any of those. The last one in particular seems especially "you resorted to a thesaurus, didn't you?". npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…l4xy @nprofile…qq28 that looks familiar. I probably have seen that video before. I hadn't seen the fractals one, though! I see that the P2 kite and dart fractals are more manageable than the P3 rhomb ones, which are only vertex-connected, and would fall apart if you tried to make physical ones. (https://hachyderm.io/@simontatham/115332685876322565) I was wondering if the Spectre tiling admits any jigsaw idea of this kind. It doesn't really have a natural counterpart the way that hats have turtles (or vice versa). But the fact that the Spectre deflation is reflecting makes me think: maybe you could have each tile be one big Spectre or Mystic on one side, and a 9- or 8-tile deflation on the other? And flipping the puzzle over also reverses handedness, so the Spectres on the two sides would be in the *same* handedness. Like hats and turtles, each tile would be different, because the deflation isn't geometrically consistent (unless you lift to 4D). But it would be practically harder to print because you'd need some kind of inset coloured markings showing the individual tiles on the deflated side, and the two tiles in the Mystic. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Visiting my sister and family for Christmas, and they have a shiny new 3D printer, so naturally we've been playing with it. Here's a hats-and-turtles jigsaw puzzle. The puzzle was made by taking a hat tiling and its combinatorially identical turtle tiling, and making a puzzle piece from each pair of corresponding tiles, so that each piece (or at least each non-edge piece) has a hat-shaped face on one side and a turtle on the other. Despite this, each piece is different: to make a hat and turtle tiling correspond as closely as possible you must scale the turtles down by an irrational factor √(4/5) to make each turtle has the same area as its hat, and rotate by an irrational angle tan⁻¹(1/√15) to keep the distance between each hat and its turtle bounded. As a result of these irrationals, no two pieces have the hat and turtle in quite the same position relative to each other. Also, the rotation makes corresponding edges skew to each other, so the sides of the puzzle pieces aren't flat planes – they all have a compound curve. Some of the tiles are truncated by the circular frame, of course. When a piece of tile was just _too_ small, I made it part of the frame, which gives a starting point for placing other pieces. The four-colouring (suggested by my brother-in-law) also provides clues to the layout. I was quite scared that this puzzle would be too hard, and _might_ have gone a bit too far in the other direction making sure it was possible! Inspired by a thread last year https://hachyderm.io/@simontatham/113329520560963878. Hat tip to Oskar van Deventer, who had a similar idea, and produced a puzzle with hats on one side and the degenerate chevron tile on the other (and without the four-colouring or frame protrusions). https://media.hachyderm.io/media_attachments/files/115/776/757/177/775/345/original/fa2ffd0ffe29f36a.jpeg https://media.hachyderm.io/media_attachments/files/115/776/757/916/907/711/original/76ee08b0f0ba2aac.jpeg https://media.hachyderm.io/media_attachments/files/115/776/758/270/694/199/original/cf373f90a83b1df9.jpeg https://media.hachyderm.io/media_attachments/files/115/776/758/522/084/349/original/9019bc7d52207dbb.jpeg npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Here's the next in my series of complicated articles about finite-state transducers and tilings! This time, instead of a blog post on my website, it's a PDF on the arXiv: https://arxiv.org/abs/2512.16595 "Finite-state transducers for substitution tilings" This is _mostly_ a distillation of the good parts of the existing blog posts, leaving out the bits that were later superseded by better ideas. But there is _some_ new content not in the blog posts. I've corrected the wrong statements I made about the Ammann-Beenker tiling; I've added more detail of the actual algorithms, and done at least some work towards proving that they work (although there are a couple of points left as conjectures). I've been a while writing this, because it ended up quite a lot longer than I expected. I'd thought cutting out all the dead ends, false starts, and exposition of other people's results (like the de Bruijn pentagrids) would leave _less_ text, but it didn't quite work out that way! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…yg3j @nprofile…65we @nprofile…p9jf here's the recursive version of mine, although converting it into PNG for upload doesn't really do it justice. If only Mastodon let me keep it as SVG, you'd be able to confirm that the recursion continues for all 7 levels by zooming in! https://media.hachyderm.io/media_attachments/files/115/730/421/622/733/498/original/6d19e7fcf9bfe609.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…yg3j @nprofile…p9jf here you go! I threw together a tiny piece of Python to generate these as SVG. https://media.hachyderm.io/media_attachments/files/115/730/400/381/771/718/original/674e5ce88ca6b198.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham When you write code to remind yourself of things, make it identify itself explicitly, because you know you're expecting to have forgotten about it. This week I attempted a 'git push' at work and got a strange message: $ git push Push attempted to branch head 'refs/heads/foo' on Gerrit remote URL 'ssh://[hostname]/[repo]'! Set GERRIT_SAFETY_CATCH=EXTREME_DANGER if you really meant it. I had no idea where that message had come from. But the wording made it look like my own work, because the combination of 'safety catch' and 'extreme danger' is an obscure Hitchhikers reference which I'm fond of. So probably it was something I'd set up myself, on purpose, long enough ago to forget about it completely? After some searching, I tracked it down to a pre-push hook that I'd installed precisely to protect myself against this mistake. (In Gerrit you often want to push to a weird secondary ref name rather than a normal branch head.) Well done, Past Me, for two things: 1. Installing the safety catch at all, which did prevent me making a mistake. 2. Putting a distinctive joke in it which at least allowed me to recognise it as probably my own work, rather than some new feature in git itself, or a message forwarded from the Gerrit server. But here's what I _should_ have done as well: 3. Instead of the joke (or as well), write "This message is from Simon's personal safety-catch script". I only identified it as my own work by the luck of recognising my own sense of humour. I could have made it _deliberately_ identifiable. 4. Mention the _pathname_ of the script in the error message! Then I wouldn't have had to _search_ for it – I could have gone straight there. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Things I love about Mastodon: I post a silly pun, and in response, people tell me interesting things I didn't already know about both Unix and nuclear physics. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Every time I see anyone refer to "prompt engineering" I think it properly ought to mean "finishing the project ahead of schedule". npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I started up Zoom this morning and it gave me the message "It looks like we are unable to connect. Please check your network connection and try again." But my network connection was fine. The problem was at Zoom's end, or more precisely, Cloudflare's. (And it seems OK now.) I'm beginning to be annoyed by this reflexive "please check _your_ network connection" coda in these messages. What it is, is victim blaming. And possibly gaslighting too. _Their_ network connection went wrong, and their immediate response is to tell all of us users that _we_ must have done something wrong. It makes us all do lots of pointless work checking things that don't need checking, and it probably makes half of us feel inadequate when we can't find any problem. If you're _going_ to advise users to check their own network connection, take reasonable steps first to ensure the problem really does look like being at the local end. Try pinging a few other independently run well-known sites; try some DNS lookups; if you can't do _anything_, suggest the user checks their connection, but if the rest of that stuff works and only your own server can't be reached, maybe redirect to your application's status webpage instead? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Why binary search doesn't always beat linear scan: The traditional method of Huffman decoding is to have a lookup table indexed by the next N bits of the input stream (for whatever N you decide you like), with each table entry saying "consume B bits and emit symbol S", or if there's a code longer than N bits, indirecting to a sub-table. If the code is lexically normalised in the usual way (like Deflate), an alternative method is to have a much smaller table with an entry per code _length_, containing the lexically smallest code of that length. Looking up the start of the input stream in that table tells you how many bits to consume, and what to subtract from them, and then you know that you have the Jth code of length B. Then a second table translates that into an output symbol. This is slower than the usual approach, but uses less memory. In particular, it uses _constant_ memory regardless of the Huffman tree (no optional sub-tables), so a bare-metal piece of code in something like a boot sector can do it without needing a malloc. So you have a table mapping the next N bits of the input stream to a code length (where this time N is the maximum value you might possibly need), and it's sorted. The natural instinct is to binary-search that table rather than just scanning it from the beginning. Everyone knows that's a faster way to search a sorted array. But not this time! Partly, this array is very small anyway (even in the worst case), and binary search has fewer steps but more bookkeeping per step, so on small arrays it's not necessarily a win. But even more: the whole point of a Huffman code is that the shorter code lengths occur _more commonly_ in the input – that's _why_ the compressor allocated them shorter code lengths. So a linear scan starting from the 'shortest code' end of the array wins, because by the nature of this particular kind of input, it terminates early more often than not. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…uk26 of course you're right, but I think it loses some of the impact that way, because it _sounds_ completely obvious. "Don't write data into the file that isn't supposed to be there? Well _of course_ I wouldn't do that, why would I even imagine wanting to?" says your listener, and discards the warning from their memory completely on the grounds that it's too obvious to worry about – and then makes the mistake anyway. I think it's also useful to demonstrate some ways in which the mistake can realistically arise, both as proof that the principle isn't as obvious as all that, and to give an idea of what kinds of situation you have to be careful of. Both of my example cases come from the quite reasonable desire to give your file format a _different_ good property. Filesystems (on a disk partition _or_ in a containing file) need to permit incremental updates without rewriting the entire thing from scratch; for some purposes it's _useful_ if a document format includes the undo history. (Indeed, my own puzzle collection has a save file format including the undo history, and it's very useful indeed, especially when bug reporters send me save files!) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Conversation with @nprofile…578r in the previous thread reminds me of the most cursed header field I've ever seen in a compressed file format. In the 1990s I helped a friend to reverse-engineer a compression format used by a videogame to store its graphics. (For the purpose of modding the game.) The format was designed in the assumption that the decoder would • allocate enough memory for the decompressed data • read the compressed data from the file into the _end_ of that buffer • start reading from the beginning of the compressed data, writing the output to the start of the buffer, so that the decompressed output ends up overwriting the compressed input. If you're lucky, the output pointer never quite overtakes the input pointer. But you're not always lucky. Sometimes, a byte of input you haven't read yet has already been overwritten by some output. If this happens at all, it will be at the very last moment, and probably there will be some small value N such that the output pointer never gets more than N bytes ahead of the input. So you can repair the problem by allocating N bytes of extra memory at the end of the buffer, and loading the compressed data N bytes higher. So the compressor, when writing one of these files, would simulate the entire decompression, track how far ahead of the input pointer the output pointer ever got, and write the necessary value of N into the file header. It took _ages_ to work out what that header field was for, and when we found out, we wished we hadn't! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I was thinking yesterday about file-format design in general, and the question "What do you do differently in your design if you expect the writer and the reader of the file format not to trust each other?" The obvious case is if the reader doesn't trust the writer: you want to design a file format so that it's easy to write a parser for it without security holes. For example, don't have 7 different binary encodings of integers, because that's 7 times as much potentially-buggy code to make sure you've thoroughly debugged. Pick one representation of integers and stick to it. But you also have to consider the _writer_ not trusting the _reader_. What do you do in your design to anticipate that risk? I can think of two things: 1. Don't use a file format designed for incremental update with internal space allocation, so that a _semantically_ superseded region of the file could still be physically present, just not linked from the header any more. A filesystem image can have this problem: if you delete a file inside it, or write a new version of the file and rename it over the original, the old file's data could still exist in the image, inside unallocated disk blocks. 2. Save just the semantics of a document, not its edit history. When someone sends a finished report to their boss, they'd prefer it _doesn't_ preserve the undo history containing evidence that they typed "ARRGH THIS IS A TOTAL WASTE OF TIME TO KEEP AN IDIOT HAPPY" in a fit of rage and then deleted it again. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…p9jf in the usual yin-yang symbol there's also a spot of yan in the ying and vice versa. With an order-7 version of the same thing I suppose you'd want a spot of _every_ colour in every other colour. (If nothing else, if you want to use it as a symbol of diversity, the idea that "there's always a little bit of $this_category in $that_category" seems likely to raise the blood pressure of anti-diversity people, which is surely a feature rather than a bug.) In the order-2 symbol the natural place to put each opposite-coloured spot is to make it a small circle centred in the larger circle. I don't know if there's any particularly 'nice' layout for the spots in the order-7 symbol. Your construction lines divide each region into _seven_ parts, not six. Placing the spots might have to be done by art rather than mathematics. But at least there's the extra resonance that the total number of spots would be 42. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…l4xy I wonder why it doesn't mention https://en.wikipedia.org/wiki/The_Hexagon! Didn't notice it, or disqualified it for some reason? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…w47d I'm curious about the post-quantum accelerator. Where I come from (the SSH community) people are still arguing about which PQ algorithms to use. But surely a hardware accelerator has to be specific to particular algorithms. Which ones have won the lottery? npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I wish the QWERTY keyboard were not laid out so as to make 'provably' and 'probably' easy typos for each other npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…l4xy I'm entertained by your example of "objects that have fallen from outer space" as a thing that has no literature. Surely in fact there is a significant literature on the subject of _actual_ objects that have fallen from outer space! :-) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I just recompressed a bunch of old .tar.bz2 archives as .tar.xz in case it compressed a bit better. Before: 630 MB. After: 13 MB. That's not a typo. The new files are smaller than the old ones by a factor of 50. There must be something _extremely_ compressible in there which only xz is able to take advantage of! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Just reminded that here-documents at the #bash prompt aren't completely useless. At the interactive prompt, I wouldn't normally type anything like this: cat <<'EOF' >file some text EOF because this takes fewer keystrokes: cat >file some text ^D But the usefulness of the first version is that you get to use bash command recall while typing the text that goes into the file. In particular, if I've just edited and re-run a complicated command 10 times interactively until I got it right, and now I want to save it into a script: cat <<'EOF' >file.sh [press Up to recall the final working version of the complicated command] EOF npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham I wonder how many times it's possible to be wrong in sequence about the same thing. In my last blog post about tilings and finite-state transducers, I mentioned making a mistake about the Ammann-Beenker tiling. I had a description of it in my software, and I had believed it didn't admit a deterministic transducer, because my code reported failure when trying to construct one. But I later found out that it did: my construction algorithm had an overzealous failure detector, and had given up too soon. But it turns out I was right the first time, if for the wrong reasons. It _doesn't_ admit a transducer after all, because I _also_ got the tiling description slightly wrong! The Ammann-Beenker rhombus tile is 180° symmetric, but its orientation matters nonetheless (it forms part of the proof of aperiodicity). My description of the tiling had the rhombuses wrongly oriented, in a way that – as it turns out – made the system accidentally simpler. The first picture here is the Ammann-Beenker analogue of the Penrose 'cartwheel pattern': a singular instance of the tiling, having the symmetry group of a regular octagon _except_ for the dark-coloured tiles, of which the blue ones vary their shape under rotation by some multiple of 45°, and the red ones keep the same shape but change orientation. The second picture is the same pattern, but generated using my earlier wrong description of the tiling. Because I got some of the rhombus orientations wrong, the red tiles are in much weirder places. _That_ diagram has been puzzling me for a week, until I figured out last night what mistake had caused it! Oh well, at least I got some pretty pictures out of it. #TilingTuesday #oops https://media.hachyderm.io/media_attachments/files/115/491/309/668/241/559/original/21a38de76f5394b0.png https://media.hachyderm.io/media_attachments/files/115/491/313/162/478/821/original/4d4fd0d09eeb7ad6.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…8eua the idea first occurred to me for practical purposes in programming. In some computer systems there's a timer which doesn't have enough bits to be treated as monotonic, so you expect it to wrap round every so often. (Examples: Windows GetTickCount() and the X11 event timestamp.) So you can't reliably take two time values and find which is earlier. But you often need to do something a bit like that anyway: for example, PuTTY wants to blink the cursor every half-second, or redo its cryptographic key exchange every hour, so I need to know whether it's been half a second yet, or an hour. The timer wraparound period is much longer than either of those intervals, so this should be reliable in principle, but what do you actually write in the code? It's a horrible programming mistake to absentmindedly compare the timestamps anyway: next_rekey_time = GetTickCount() + hour; ... if (GetTickCount() > next_rekey_time) start_rekey(); This is awful precisely because it _usually_ works, so you get the worst of all software failures: the one you almost certainly never catch in your pre-release testing, and then it fails in the field when someone happens to run it near the GetTickCount wraparound instant. To prevent this kind of error, you could wrap your timestamp in a struct which hides the built-in semantics it gets from being an integer, like supporting the < operator. Then you can't accidentally compare two timestamps, because you get a compile error if you try. But now your timestamp wrapper type has to implement some other kind of thing, which allows you to write a similar comparison but this time get it right. What thing? The ternary comparison. last_rekey_time = GetTickCount(); next_rekey_time = last_rekey_time + hour; ... if (ternary_order(last_rekey_time, next_rekey_time, GetTickCount())) start_rekey(); npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…8eua as well as ternary _operations_, returning an element of the same set as the inputs, ternary relations can also be useful. An example I keep coming back to is the ternary order relation. Given some kind of ground set which you regard as cyclic, either a continuous circular space like ℝ/ℤ or a discrete structure like a finite-order cyclic group with a specified generator, it's meaningless to ask whether a < b, because attempting to impose a total order on a cyclic structure _obviously_ makes it easy to construct failures of transitivity. So you can't have a binary comparison operator giving any kind of ordering. But it's still meaningful to compare _three_ elements at a time, by asking whether a,b,c appear in that order as you go round the cycle, or in a,c,b order. This ternary order relation has an analogue of binary ordering's antisymmetry: [a,b,c] implies [b,c,a] and [c,a,b], and is mutually exclusive with [a,c,b] and its rotations. And an analogue of transitivity: if [a,b,c] and [a,c,d], then [a,b,d] and [b,c,d]. Reflexivity vs antireflexivity is harder, because you get an _annoying_ number of variants akin to the < vs ≤ distinction, because now there are more possible equalities to include or exclude and you might not have the same opinion about each one! Another way to look at this operation is that a and c are defining an interval on the circle (made unambiguous by which of them you wrote first), and you're asking whether b is in it. Then you have all the usual choices about whether the interval is open, closed, or half-open. For example, you might well find it useful to have a variant that you treat as meaning b ∈ [a,c), which would have to disallow any case where b=c or a=c, but would accept a = b ≠ c. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham The advantage of using a duck-typed language is that it lets you get your program up and running very quackly. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham https://media.hachyderm.io/media_attachments/files/115/418/748/109/083/491/original/04566b2b87292db9.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…l4xy I found my copy of Great Stella! Version 1.1, from 2002. I didn't even have to move over to my real Windows machine to run it – it runs just fine in Wine. In the first picture I've got it to display your original solid. Or nearly so, anyway. GS has constructed it as a stellation of the _official_ deltoidal icositetrahedron (or "strombic" in its nomenclature), not of your slightly distorted version, so the six-pointed spikes are noticeably smaller than in yours. I haven't found out yet whether I can feed it a polyhedron description of my own, instead of just starting from its built-in list of well-known ones. Some other nice stellations of the same solid I found: picture 2 has weird wedge-shaped protrusions, with an edge at the end instead of a point. Picture 3 is chiral – and picture 4, superimposing the two reflections of picture 3, is all crinkly! https://media.hachyderm.io/media_attachments/files/115/382/596/100/049/222/original/527d602626f149df.png https://media.hachyderm.io/media_attachments/files/115/382/613/131/823/851/original/fcfbc43ed0ceec01.png https://media.hachyderm.io/media_attachments/files/115/382/630/338/301/861/original/e78661320e6b713d.png https://media.hachyderm.io/media_attachments/files/115/382/637/503/180/987/original/e58a1f1f176d8d48.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…l4xy did you ever play with a piece of software called 'Great Stella'? Proprietary (but cheap), and ran on Windows about 20–25 years ago. Took a polyhedron and let you interactively play with stellating it, by extending all the face planes as far as necessary, enumerating the regions they divided space into, throwing away the unbounded ones, and letting you select subsets of what was left (perhaps up to sensible criteria like preserving the original solid's symmetry, or not having holes in the middle). I don't remember its exact feature set, but I remember it being fun to play with. Alas, I have no idea where my copy went, or if it would even run on Windows 11 if I could find it. I wonder if it would be easy to reimplement as modern Free Software by finding existing computational geometry packages that have done most of the hard work already. This picture looks to me like the _minimal_ (non-identity) stellation, in which you've added the smallest possible region just outside each original face. So I guess people who like numbering the stellations of solids would have a hard time justifying calling it anything other than 'the first stellation of the deltoidal icositetrahedron'. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev oh yes, now I see the full version. I still think this is a stellation of the deltoidal icositetrahedron. I agree that I was hasty in identifying it as the union of those two pictures on Mathworld, but only because I assumed that one of them must be the eight 6-sided spikes and it wasn't :-) I think it's the union of the stellation with 8-sided spikes, and the stellation that is the compound of two triakis tetrahedra under central inversion. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev thanks, that is helpful, but _wow_ did it confuse my OpenSCAD display! https://media.hachyderm.io/media_attachments/files/115/368/330/223/429/506/original/50fad68d9b2b1703.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev hmmm. Apparently I can't download the actual STL without creating an account, so for the moment, I'm limited to rotating it around in that web UI, which means I have to work qualitatively with the combinatorics rather than finding exact point coordinates of the innermost region. It has six 8-sided spikes and eight 6-sided ones, with octahedral symmetry as you say. Rotating the model to look along the plane of one face of one spike, it looks as if the faces of the spikes come in coplanar sets of four, with each plane containing two faces of 8-sided spikes and two of 6-sided ones, for a total of 24 planes. That would be consistent with my guess of a hexakis octahedron (aka disdyakis (rhombic) dodecahedron), but I'm not convinced that makes sense. I actually wonder ... Taking every _other_ 6-sided spike, and considering only the planes involved with those vertices, might you get a triakis tetrahedron? Is this a compound of two triakis tetrahedra, stellated a bit so that the same planes also give rise to the 8-sided spikes? ... put another way, that would make it a stellation of the solid you get by _intersecting_ those two triakis tetrahedron, which has 24 kite-shaped faces and is a [hasty WP search] deltoidal icositetrahedron. And aha: https://mathworld.wolfram.com/DeltoidalIcositetrahedronStellations.html Your solid is not any of the pictures shown there, but I think it's the union of the first two, in symmetry-compatible orientations. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev hmm, my first _two_ guesses were wrong. It's the right general size and pointiness for a small stellated dodecahedron, but clearly isn't one, because any two adjacent spikes would meet at a single edge. I remembered seeing another thing of the same general pointiness in my trusty Cundy & Rollett. It's labelled "small stellated triacontahedron". But yours isn't that either, because _three_ adjacent spikes don't meet at a single concave vertex. Your alt text says it has octahedral symmetry, which is another argument against either of those guesses (and also any stellation of an icosahedron, which I think is what would be implied by people elsethread mentioning the Bit from Tron). But it has too many planes to be a stellation of an actual octahedron. So I think my best guess is that someone has taken one of those more complicated things like a triakis or hexakis octahedron, and found a suitable stellation of that. It would probably be much easier to tell from a rotatable 3d model than from a single view angle. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham #TIL that in #less, if you enable case-insensitive searching, it only searches insensitively if your search string is all lower case. As soon as you include a capital letter in the search string, it goes back to searching case-sensitively regardless. Weird how it's easy to miss things like that. I knew that happened in #emacs, but somehow, in 20 years I'd never noticed it in less. I presume the rationale (in both tools) is that if you went to the extra effort of typing the capital letter, you meant it. I found out that less did this by _pasting_ a search string into it, which invalidates that assumption – it would have been _more_ effort for me to deliberately downcase the string I pasted. So I guess I must have never done that before, or else, wasn't paying attention when I did! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham #ClamAV is falsely reporting that #PuTTY 0.83 is infected with malware: allegedly the "Win.Exploit.Marte-10058127-0" virus. We were notified this morning: the ClamAV database entry is brand new. But the binary it accuses is six months old. As usual, we believe this report is a falsehood. PuTTY has a long history of being insulted by virus checkers, and we've never worked out why, though we have a number of theories in our wishlist page for the phenomenon https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/false-positive-malware.html. In this case, the analysis in the screenshot shows that the identification is based on finding particular _text strings_ in the binary. Those text strings are the names of PuTTY source files, baked into the executable by something in the build process (most likely __FILE__, via assert statements). The full pathname includes a randomised build directory name created by mkstemp(), which identifies these strings as ones that would appear _only_ in the release build of PuTTY 0.83 – any other build would have chosen a different build directory. So this alleged virus signature is actually a signature of that particular PuTTY binary build. Of course, I've submitted a false-positive report. (Quite a grumpy one, since this isn't the first time.) But I do wonder how this keeps happening. I could certainly believe that putty.exe was reused unchanged by some actual malware, and then somehow the signature of putty.exe got into the virus db entry instead of the signature of anything specific to the malware. But another possibility is that someone is maliciously making these database entries. I wonder if there's any way to tell which is true. https://media.hachyderm.io/media_attachments/files/115/343/105/691/334/661/original/d30bf2997890090f.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham For #TilingTuesday, a fractal version of the Penrose P3 rhombs tiling. (This isn't a new idea in general, but I generated one of these fractals for a serious diagram which I then realised I wasn't going to need, and it seems a shame to waste the pretty pictures now I've made them.) Image #1 shows a substitution system that can be used to 'deflate' a single rhomb from a Penrose tiling into a pattern of smaller rhombs. If you apply these rules consistently to every rhomb in a whole tiling, the output tiles all fit together and deliver a smaller tiling. The substitution doesn't occupy exactly the same area as the original tile: there are protrusions and gaps. So if you iterate the substitution, the tile edges converge to fractal shapes. Image #2 shows the same substitution system, but with the tiles reshaped into those fractals. The fractals too fit together exactly and tile the plane. Image #3 shows a small sample fragment of P3 rhomb tiling, and image #4 shows the same fragment with these fractal tile shapes. https://media.hachyderm.io/media_attachments/files/115/332/660/868/484/944/original/f0a92d991d38b39f.png https://media.hachyderm.io/media_attachments/files/115/332/676/116/517/637/original/3b21edcdccfeb6c5.png https://media.hachyderm.io/media_attachments/files/115/332/678/428/830/789/original/e8f5e29449414c84.png https://media.hachyderm.io/media_attachments/files/115/332/681/522/791/023/original/a1bec0b737e1a19c.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev on what basis? I don't see any evidence of that in the code or the README. I can see that the code is trying to rule out some _obvious_ forms of non-uniqueness, like paths that touch themselves. But I see nothing more sophisticated than that. Indeed a comment in that check says "This would mean a path is touching itself, which is generally not allowed in *pseudo-unique* puzzles." (my emphasis) I can't really imagine any way that you _could_ be sure that a general (puzzle, solution) pair had no other solutions, without using a rigorously checking solver to make sure. If you abandon 'general' then I could certainly imagine that it might be possible to characterise some subclass of Numberlink puzzles which always had a unique solution, and restrict your generator to only output those. But then that would also be a low-quality puzzle generator, because knowledge of the restricted subclass would be an extra clue for those in the know, even if the subclass didn't already correlate to puzzles that were boring or easy in some way (e.g. only short paths). And if there were such a characterisation in use, it would be mathematically interesting, and surely the code comments would brag about it! I think those 'pseudo-uniqueness' checks are very sensible as a first step: any cause of non-uniqueness you can rule out cheaply up front is worth doing, so that you only run the expensive full solver on the minimum number of candidates. Other puzzles of mine use this strategy (Dominosa springs most readily to mind, ruling out AB/BA squares during generation). But those checks are an optimisation, not a substitute for the full solver. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev ah, but now I've had a chance to read the README, it turns out that it's exactly not suitable for my needs. Its solver _assumes_ the input puzzle is well-formed, in that there is a unique solution, and moreover it uses every square of the grid. So it will stop at the first solution it finds, and maybe not even find degenerate solutions that connect everything without having to use all squares. In both cases, that makes it useless for detecting which of the candidate puzzles emitted from a grid generator are good enough to be presented to the player. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev thank you, I'll have a look at it at some point! However, I'm currently a bit distracted by having just figured out one of the missing pieces of proof for the tilings stuff that I'm slowly writing up in LaTeX. (I still don't have all the proofs, but I've pushed the starting point back from "assume L is the zero-reversible regular language that it looks as if it ought to be" to "assume a much more modest property of your substitution system and then we can prove L is both regular and zero-reversible.") npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…saev that puzzle game in all its forms (I originally saw it under the name "Number Link" played on a plane square grid) is one of the most frustrating ones I know, because I would _love_ to have an implementation of it in my puzzle collection, but I've never been able to figure out how to autogenerate good-quality instances of it. Usually the problem divides into two parts: generate a filled grid containing a puzzle solution, then remove everything but the clues and run a solver to ensure that the solution is unique. (Sometimes you can do better than that, but this is the fallback structure if nothing else works.) In this case _both_ halves of the problem are difficult. 1. I have two different draft algorithms that can generate a filled Number Link grid, with pairs of numbered squares connected by paths which between them use up all the other grid squares. One algorithm generates too many short paths so the puzzle is too easy. The other generates too few paths, spiralling around each other too many times, so that the puzzle doesn't have a unique solution – there's a simpler way to connect the numbers together. If it was the same algorithm with some tuning parameters, I'd just try to tune for a happy medium in between, but the algorithms are totally different. 2. Even if I did have a source of plausible candidate grids, I _also_ haven't been able to write an automated solver. Normally I write a puzzle solver algorithm by solving instances of the puzzle by hand, introspecting my thought processes, and then implementing the same deductions in code. But for this puzzle my thought process is much more global and topological and intuitive than the 'fill in one square at a time' reasoning in other puzzles: I stare at it for a while and then suddenly say 'oh, it goes _that_ way'. And I've never managed to figure out what goes in the oh_it_goes_that_way() subroutine! I _guess_ that existing computer implementations haven't solved this either, and rely on a library of hand-designed puzzle instances. But I'd be interested to find out I was wrong! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham Seen from a web system: "Error: missing 404 exception" In other words, "Not Found" not found! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…6cfc now I think about it, your description makes more sense than mine, because if you're working in a space that looks like ℝ³ × (ℝ/ℤ), it's not quite clear what it even _means_ to ask if something wrapping round the time axis is equivalent to the Borromean rings. I'm not sure how I'd go about homotopy-ing the former into the latter! But your version, in which the juggler is standing near the edge of a large turntable revolving once per period of the pattern, keeps everything in a nicely bounded region of ℝ³ and it's easy to see that the question is meaningful. (For the purposes of topology we can ignore the effect of centrifugal force on the difficulty of the juggling. For the purposes of juggling, I strongly suspect you couldn't!) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham @nprofile…6cfc I guess you implicitly mean that you're quotienting time by the period of the pattern, so that you're turning ℝ⁴ into ℝ³ × (ℝ/ℤ) or some such, and each ball's world line returns to its starting point in that quotient of space-time after going round the sequence of six throws once? Otherwise I can't see how a space-time setup with three doubly-infinite world lines can be equivalent to a link with three closed loops at all. But that's very cool! I knew that the 3-ball cascade pattern _braids_ the world lines, but hadn't spotted the connection to Borromean rings. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham My ultra-geeky childhood programming story: When I was a small child in the 80s, we had two computers in the house. One was the usual kind of 'your child can learn to program on this!' 80s home computer, in our case a ZX Spectrum. And I did learn to program on it. But the other one was the interesting one. My dad worked for DEC, and had brought home a DEC Robin. (I think officially a VT180? But Dad always used the internal codename.) He would use it to work from home, by dialling the office on the phone, and running a VMS shell session directly over a 1980s modem. I'd sometimes sit and watch him work, and of course not understand very much. When Dad wasn't using the Robin, I was allowed to have a go on it. It ran CP/M-80, and had a BASIC interpreter called MBASIC. So I learned that dialect in parallel with Spectrum BASIC. On the Spectrum, my toy programs would often use the Spectrum's graphics, to draw pictures or weird interference patterns or whatever. But the Robin only had a text mode (or at least, if it did have graphics, I never found them). What would be fun for a child to do on that? (1/4) npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham New maths blog post! The fifth in my series about handling aperiodic tilings combinatorially with string-processing algorithms: https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/aperiodic-refine/ In this instalment I've automated the process of converting a tiling substitution system into one for which you can build a deterministic transducer, if the starting system didn't already admit one. I discuss the algorithm, its limitations, and a particular success in which it found a more economical substitution system for the hat tiling than any I already knew of. #TilingTuesday https://media.hachyderm.io/media_attachments/files/115/134/352/089/994/238/original/250b5883ce0d88b6.png https://media.hachyderm.io/media_attachments/files/115/134/356/471/844/709/original/f3b95911edfe6503.png https://media.hachyderm.io/media_attachments/files/115/134/359/287/627/156/original/28fccf99b95607f4.png https://media.hachyderm.io/media_attachments/files/115/134/362/609/697/679/original/47a17405bf68e951.png npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham There is a new short domain name for #PuTTY! https://putty.software/ At present, this is just a "landing page": a nice short name to remember, which will redirect you to the full PuTTY website at the same longer URL where it's always been. But unlike putty.org or other third-party landing pages, this one is run by us, the actual PuTTY team, and it doesn't have a weird separate agenda of its own. I intend to move the main PuTTY site over to that domain in the future, and leave just a redirector at the old location. But first I want to get the word out, so that people know which site to trust. If anyone is still linking to putty.org, here's a place to link to instead. Please spread the word! npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham If I am ever put in charge of the Software Developers' Afterlife, there will be some appropriately themed Special Hell reserved for the designers of websites where the login flow unconditionally lands you at some default location like the site's front page, instead of redirecting you back to the same subpage you'd been looking at before, where you'd clicked the "login" button in the corner. But there will be a worse hell still for the ones who take "redirect back to where you were before" too literally: • attempt to visit a thing you aren't allowed to see as an unauthenticated user • receive 403 Forbidden, with an error page suggesting "try logging in?" • after login, the site "helpfully" redirects you back to the same 403 error document! _Even_ the site's front page would be more use than that. npub1tc0v9m9s206jet0ucs2zj7mf2c48fh0xr7679qfr4qn8x7ej6v0q3tr9k0 Simon Tatham During an investigation today I ran across a quite long piece of base64 data which only used the 62 alphanumerics, and not + or /. "That's a weird coincidence," I thought, "what are the odds of that? Surely you'd expect a + or / by chance sooner or later." Turns out: not very unlikely at all. + and / translate to binary 111110 and 111111. In base64 those sequences have to occur at an even bit shift. If the encoded data is ASCII text then the top bit of every byte is 0, so neither sequence can occur at any shift that puts part of it in the top two bits. The only possibility is that it appears in the bottom 6 bits of a byte, so you get a byte value of the form 0?11111?. There are four of those, and one of them is 0x7f, which isn't printable. So the answer is: any printable ASCII document which avoids the three characters >?~ will encode to purely alphanumeric base64. And that's not very hard at all – plenty of syntaxes don't happen to use those. The encoded data in this case turned out to be JSON, in which all the strings were either alphanumeric identifiers or other pieces of base64. My intuition "surely you expect a + or / by chance" was based on mentally modelling base64 as some kind of random transformation. Not a good model, as it turned out!