Latest Posts (20 found)
Susam Pal 3 weeks ago

Stories From 25 Years of Computing

Last year, I completed 20 years in professional software development. I wanted to write a post to mark the occasion back then, but couldn't find the time. This post is my attempt to make up for that omission. In fact, I have been involved in software development for a little longer than 20 years. Although I had my first taste of computer programming as a child, it was only when I entered university about 25 years ago that I seriously got into software development. So I'll start my stories from there. These stories are less about software and more about people. Unlike many posts of this kind, this one offers no wisdom or lessons. It only offers a collection of stories. I hope you'll like at least a few of them. The first story takes place in 2001, shortly after I joined university. One evening, I went to the university computer laboratory to browse the Web. Out of curiosity, I typed into the address bar to see what kind of website existed there. I ended up on this home page: susam.com . I remember that the text and the banner looked much larger back then. Since display resolutions were lower, the text and banner covered almost half the screen. I knew very little about the Internet then and I was just trying to make sense of it. I remember wondering what it would take to create my own website, perhaps at . That's when an older student who had been watching me browse over my shoulder approached and asked if I had created the website. I told him I hadn't and that I had no idea how websites were made. He asked me to move aside, took my seat and clicked View > Source in Internet Explorer. He then explained how websites are made of HTML pages and how those pages are simply text instructions. Next, he opened Notepad and wrote a simple HTML page that looked something like this: He then opened the page in a web browser and showed how it rendered. After that, he demonstrated a few more features such as changing the font face and size, centring the text and altering the page's background colour. Although the tutorial lasted only about ten minutes, it made the World Wide Web feel far less mysterious and much more fascinating. That person had an ulterior motive though. After the tutorial, he never returned the seat to me. He just continued browsing the Web and waited for me to leave. I was too timid to ask for my seat back. Seats were limited, so I returned to my dorm room both disappointed that I couldn't continue browsing that day and excited about all the websites I might create with this newfound knowledge. I could never register for myself though. That domain was always used by some business selling Turkish cuisines. Eventually, I managed to get the next best thing: a domain of my own. That brief encounter in the university laboratory set me on a lifelong path of creating and maintaining personal websites. The second story also comes from my university days. I was hanging out with my mates in the computer laboratory, in front of an MS-DOS machine powered by an Intel 8086 microprocessor. I was writing a lift control program in assembly. In those days, it was considered important to deliberately practise solving made-up problems as a way of honing our programming skills. As I worked on my program, my mind drifted to a small detail about the 8086 microprocessor that we had recently learned in a lecture. Our professor had explained that, when the 8086 microprocessor is reset, execution begins with CS:IP set to FFFF:0000. So I murmured to anyone who cared to listen, 'I wonder if the system will reboot if I jump to FFFF:0000.' I then opened and jumped to that address. The machine rebooted instantly. One of my friends, who topped the class every semester, had been watching over my shoulder. As soon as the machine restarted, he exclaimed, 'How did you do that?' I explained that the reset vector is located at physical address FFFF0 and that the CS:IP value FFFF:0000 maps to that address in real mode. After that, I went back to working on my lift control program and didn't think much more about the incident. About a week later, the same friend came to my dorm room. He sat down with a grave look on his face and asked, 'How did you know to do that? How did it occur to you to jump to the reset vector?' I must have said something like, 'It just occurred to me. I remembered that detail from the lecture and wanted to try it out.' He then said, 'I want to be able to think like that. I come top of the class every year, but I don't think the way you do. I would never have thought of taking a small detail like that and testing it myself.' I replied that I was just curious to see whether what we had learnt actually worked in practice. He responded, 'And that's exactly it. It would never occur to me to try something like that. I feel disappointed that I keep coming top of the class, yet I am not curious in the same way you are. I've decided I don't want to top the class anymore. I just want to explore and experiment with what we learn, the way you do.' That was all he said before getting up and heading back to his dorm room. I didn't take it very seriously at the time. I couldn't imagine why someone would willingly give up the accomplishment of coming first every year. But he kept his word. He never topped the class again. He still ranked highly, often within the top ten, but he kept his promise of never finishing first again. To this day, I feel a mix of embarrassment and pride whenever I recall that incident. With a single jump to the processor's reset entry point, I had somehow inspired someone to step back from academic competition in order to have more fun with learning. Of course, there is no reason one cannot do both. But in the end, that was his decision, not mine. In my first job after university, I was assigned to work on the installer for a specific component of an e-banking product. The installer was written in Python and was quite fragile. During my first week on the project, I spent much of my time stabilising the installer and writing a user guide with step-by-step instructions on how to use it. The result was well received and appreciated by both my seniors and management. To my surprise, my user guide was praised more than my improvements to the installer. While the first few weeks were enjoyable, I soon realised I would not find the work fulfilling for very long. I wrote to management a few times to ask whether I could transfer to a team where I could work on something more substantial. My emails were initially met with resistance. After several rounds of discussion, however, someone who had heard about my situation reached out and suggested a team whose manager might be interested in interviewing me. The team was based in a different city. I was young and willing to relocate wherever I could find good work, so I immediately agreed to the interview. This was in 2006, when video conferencing was not yet common. On the day of the interview, the hiring manager called me on my desk phone. He began by introducing the team, which called itself Archie , short for architecture . The team developed and maintained the web framework and core architectural components on which the entire e-banking product was built. The product had existed long before open source frameworks such as Spring or Django became popular, so features such as API routing, authentication and authorisation layers, cookie management and similar capabilities were all implemented in-house by this specialised team. Because the software was used in banking environments, it also had to pass strict security testing and audits to minimise the risk of serious flaws. The interview began well. He asked several questions related to software security, such as what SQL injection is and how it can be prevented or how one might design a web framework that mitigates cross-site scripting attacks. He also asked programming questions, most of which I answered pretty well. Towards the end, however, he asked how we could prevent MITM attacks. I had never heard the term, so I admitted that I did not know what MITM meant. He then asked, 'Man in the middle?' but I still had no idea what that meant or whether it was even a software engineering concept. He replied, 'Learn everything you can about PKI and MITM. We need to build a digital signatures feature for one of our corporate banking products. That's the first thing we'll work on.' Over the next few weeks, I studied RFCs and documentation related to public key infrastructure, public key cryptography standards and related topics. At first, the material felt intimidating, but after spending time each evening reading whatever relevant literature I could find, things gradually began to make sense. Concepts that initially seemed complex and overwhelming eventually felt intuitive and elegant. I relocated to the new city a few weeks later and delivered the digital signatures feature about a month after joining the team. We used the open source Bouncy Castle library to implement digital signatures. After that project, I worked on other parts of the product too. The most rewarding part was knowing that the code I was writing became part of a mature product used by hundreds of banks and millions of users. It was especially satisfying to see the work pass security testing and audits and be considered ready for release. That was my first real engineering job. My manager also turned out to be an excellent mentor. Working with him helped me develop new skills and his encouragement gave me confidence that stayed with me for years. Nearly two decades have passed since then, yet the product is still in use. In fact, in my current phase of life I sometimes encounter it as a customer. Occasionally, I open the browser's developer tools to view the page source where I can still see traces of the HTML generated by code I wrote almost twenty years ago. Around 2007 or 2008, I began working on a proof of concept for developing widgets for an OpenTV set-top box. The work involved writing code in a heavily trimmed-down version of C. One afternoon, while making good progress on a few widgets, I noticed that they would occasionally crash at random. I tried tracking down the bugs, but I was finding it surprisingly difficult to understand my own code. I had managed to produce some truly spaghetti code full of dubious pointer operations that were almost certainly responsible for the crashes, yet I could not pinpoint where exactly things were going wrong. Ours was a small team of four people, each working on an independent proof of concept. The most senior person on the team acted as our lead and architect. Later that afternoon, I showed him my progress and explained that I was still trying to hunt down the bugs causing the widgets to crash. He asked whether he could look at the code. After going through it briefly and probably realising that it was a bit of a mess, he asked me to send him the code as a tarball, which I promptly did. He then went back to his desk to study the code. I remember thinking that there was no way he was going to find the problem anytime soon. I had been debugging it for hours and barely understood what I had written myself; it was the worst spaghetti code I had ever produced. With little hope of a quick solution, I went back to debugging on my own. Barely five minutes later, he came back to my desk and asked me to open a specific file. He then showed me exactly where the pointer bug was. It had taken him only a few minutes not only to read my tangled code but also to understand it well enough to identify the fault and point it out. As soon as I fixed that line, the crashes disappeared. I was genuinely in awe of his skill. I have always loved computing and programming, so I had assumed I was already fairly good at it. That incident, however, made me realise how much further I still had to go before I could consider myself a good software developer. I did improve significantly in the years that followed and today I am far better at managing software complexity than I was back then. In another project from that period, we worked on another set-top box platform that supported Java Micro Edition (Java ME) for widget development. One day, the same architect from the previous story asked whether I could add animations to the widgets. I told him that I believed it should be possible, though I'd need to test it to be sure. Before continuing with the story, I need to explain how the different stakeholders in the project were organised. Our small team effectively played the role of the software vendor. The final product going to market would carry the brand of a major telecom carrier, offering direct-to-home (DTH) television services, with the set-top box being one of the products sold to customers. The set top box was manufactured by another company. So the project was a partnership between three parties: our company as the software vendor, the telecom carrier and the set-top box manufacturer. The telecom carrier wanted to know whether widgets could be animated on screen with smooth slide-in and slide-out effects. That was why the architect approached me to ask whether it could be done. I began working on animating the widgets. Meanwhile, the architect and a few senior colleagues attended a business meeting with all the partners present. During the meeting, he explained that we were evaluating whether widget animations could be supported. The set-top box manufacturer immediately dismissed the idea, saying, 'That's impossible. Our set-top box does not support animation.' When the architect returned and shared this with us, I replied, 'I do not understand. If I can draw a widget, I can animate it too. All it takes is clearing the widget and redrawing it at slightly different positions repeatedly. In fact, I already have a working version.' I then showed a demo of the animated widgets running on the emulator. The following week, the architect attended another partners' meeting where he shared updates about our animated widgets. I was not personally present, so what follows is second-hand information passed on by those who were there. I learnt that the set-top box company reacted angrily. For some reason, they were unhappy that we had managed to achieve results using their set-top box and APIs that they had officially described as impossible. They demanded that we stop work on animation immediately, arguing that our work could not be allowed to contradict their official position. At that point, the telecom carrier's representative intervened and bluntly told the set-top box representative to just shut up. If the set top box guy was furious, the telecom guy was even more so, 'You guys told us animation was not possible and these people are showing that it is! You manufacture the set-top box. How can you not know what it is capable of?' Meanwhile, I continued working and completed my proof-of-concept implementation. It worked very well in the emulator, but I did not yet have access to the actual hardware. The device was still in the process of being shipped to us, so all my early proof-of-concepts ran on the emulator. The following week, the architect planned to travel to the set-top box company's office to test my widgets on the real hardware. At the time, I was quite proud of demonstrating results that even the hardware maker believed were impossible. When the architect eventually travelled to test the widgets on the actual device, a problem emerged. What looked like buttery smooth animation on the emulator appeared noticeably choppy on a real television. Over the next few weeks, I experimented with frame rates, buffering strategies and optimising the computation done in the the rendering loop. Each week, the architect travelled for testing and returned with the same report: the animation had improved somewhat, but it still remained choppy. The modest embedded hardware simply could not keep up with the required computation and rendering. In the end, the telecom carrier decided that no animation was better than poor animation and dropped the idea altogether. So in the end, the set-top box developers turned out to be correct after all. Back in 2009, after completing about a year at RSA Security, I began looking for work that felt more intellectually stimulating, especially projects involving mathematics and algorithms. I spoke with a few senior leaders about this, but nothing materialised for some time. Then one day, Dr Burt Kaliski, Chief Scientist at RSA Laboratories, asked to meet me to discuss my career aspirations. I have written about this in more detail in another post here: Good Blessings . I will summarise what followed. Dr Kaliski met me and offered a few suggestions about the kinds of teams I might approach to find more interesting work. I followed his advice and eventually joined a team that turned out to be an excellent fit. I remained with that team for the next six years. During that time, I worked on parser generators, formal language specification and implementation, as well as indexing and querying components of a petabyte-scale database. I learnt something new almost every day during those six years. It remains one of the most enjoyable periods of my career. I have especially fond memories of working on parser generators alongside remarkably skilled engineers from whom I learnt a lot. Years later, I reflected on how that brief meeting with Dr Kaliski had altered the trajectory of my career. I realised I was not sure whether I had properly expressed my gratitude to him for the role he had played in shaping my path. So I wrote to thank him and explain how much that single conversation had influenced my life. A few days later, Dr Kaliski replied, saying he was glad to know that the steps I took afterwards had worked out well. Before ending his message, he wrote this heart-warming note: This story comes from 2019. By then, I was no longer a twenty-something engineer just starting out. I was now a middle-aged staff engineer with years of experience building both low-level networking systems and database systems. Most of my work up to that point had been in C and C++. I was now entering a new phase where I would be developing microservices professionally in languages such as Go and Python. None of this was unfamiliar territory. Like many people in this profession, computing has long been one of my favourite hobbies. So although my professional work for the previous decade had focused on C and C++, I had plenty of hobby projects in other languages, including Python and Go. As a result, switching gears from systems programming to application development was a smooth transition for me. I cannot even say that I missed working in C and C++. After all, who wants to spend their days occasionally chasing memory bugs in core dumps when you could be building features and delivering real value to customers? In October 2019, during Cybersecurity Awareness Month, a Capture the Flag (CTF) event was organised at our office. The contest featured all kinds of puzzles, ranging from SQL injection challenges to insecure cryptography problems. Some challenges also involved reversing binaries and exploiting stack overflow issues. I am usually rather intimidated by such contests. The whole idea of competitive problem-solving under time pressure tends to make me nervous. But one of my colleagues persuaded me to participate in the CTF. And, somewhat to my surprise, I turned out to be rather good at it. Within about eight hours, I had solved roughly 90% of the puzzles. I finished at the top of the scoreboard. In my younger days, I was generally known to be a good problem solver. I was often consulted when thorny problems needed solving and I usually managed to deliver results. I also enjoyed solving puzzles. I had a knack for them and happily spent hours, sometimes days, working through obscure mathematical or technical puzzles and sharing detailed write-ups with friends of the nerd variety. Seen in that light, my performance at the CTF probably should not have surprised me. Still, I was very pleased. It was reassuring to know that I could still rely on my systems programming experience to solve obscure challenges. During the course of the contest, my performance became something of a talking point in the office. Colleagues occasionally stopped by my desk to appreciate my progress in the CTF. Two much younger colleagues, both engineers I admired for their skill and professionalism, were discussing the results nearby. They were speaking softly, but I could still overhear parts of their conversation. Curious, I leaned slightly and listened a bit more carefully. I wanted to know what these two people, whom I admired a lot, thought about my performance. One of them remarked on how well I was doing in the contest. The other replied, 'Of course he is doing well. He has more than ten years of experience in C.' At that moment, I realised that no matter how well I solved those puzzles, the result would naturally be credited to experience. In my younger days, when I solved tricky problems, people would sometimes call me smart. Now it was expected. Not that I particularly care for such labels anyway, but it did make me realise how things had changed. I was now simply the person with many years of experience. Solving technical puzzles that involved disassembling binaries, tracing execution paths and reconstructing program logic was expected rather than remarkable. I continue to sharpen my technical skills to this day. While my technical results may now simply be attributed to experience, I hope I can continue to make a good impression through my professionalism, ethics and kindness towards the people I work with. If those leave a lasting impression, that is good enough for me. Read on website | #technology | #programming My First Lesson in HTML The Reset Vector My First Job Sphagetti Code Animated Television Widgets Good Blessings The CTF Scoreboard

0 views
Susam Pal 1 months ago

QuickQWERTY 1.2.1

QuickQWERTY 1.2.1 is now available. QuickQWERTY is a web-based touch typing tutor for QWERTY keyboards that runs directly in the web browser. This release contains a minor bug fix in Unit 4.3. Unit 4.3 is a 'Control' unit that lets you practise typing partial words as well as full words. In one place in this unit, the following sequence of partial and full words occurs: The full word was incorrectly repeated twice. This has been fixed to: To try out QuickQWERTY, go to quickqwerty.html . Read on website | #web | #programming

0 views
Susam Pal 1 months ago

Attention Media ≠ Social Media

When web-based social media started flourishing nearly two decades ago, they were genuinely social media. You would sign up for a popular service, follow people you knew or liked and read updates from them. When you posted something, your followers would receive your updates as well. Notifications were genuine. The little icons in the top bar would light up because someone had sent you a direct message or engaged with something you had posted. There was also, at the beginning of this millennium, a general sense of hope and optimism around technology, computers and the Internet. Social media platforms were one of the services that were part of what was called Web 2.0, a term used for websites built around user participation and interaction. It felt as though the information superhighway was finally reaching its potential. But sometime between 2012 and 2016, things took a turn for the worse. First came the infamous infinite scroll. I remember feeling uneasy the first time a web page no longer had a bottom. Logically, I knew very well that everything a browser displays is a virtual construct. There is no physical page. It is just pixels pretending to be one. Still, my brain had learned to treat web pages as objects with a beginning and an end. The sudden disappearance of that end disturbed my sense of ease. Then came the bogus notifications. What had once been meaningful signals turned into arbitrary prompts. Someone you followed had posted something unremarkable and the platform would surface it as a notification anyway. It didn't matter whether the notification was relevant to me. The notification system stopped serving me and started serving itself. It felt like a violation of an unspoken agreement between users and services. Despite all that, these platforms still remained social in some diluted sense. Yes, the notifications were manipulative, but they were at least about people I actually knew or had chosen to follow. That, too, would change. Over time, my timeline contained fewer and fewer posts from friends and more and more content from random strangers. Using these services began to feel like standing in front of a blaring loudspeaker, broadcasting fragments of conversations from all over the world directly in my face. That was when I gave up on these services. There was nothing social about them anymore. They had become attention media . My attention is precious to me. I cannot spend it mindlessly scrolling through videos that have neither relevance nor substance. But where one avenue disappeared, another emerged. A few years ago, I stumbled upon Mastodon and it reminded me of the early days of Twitter. Back in 2006, I followed a small number of folks of the nerd variety on Twitter and received genuinely interesting updates from them. But when I log into the ruins of those older platforms now, all I see are random videos presented to me for reasons I can neither infer nor care about. Mastodon, by contrast, still feels like social media in the original sense. I follow a small number of people I genuinely find interesting and I receive their updates and only their updates. What I see is the result of my own choices rather than a system trying to optimise my behaviour. There are no bogus notifications. The timeline feels calm and predictable. If there are no new updates from people I follow, there is nothing to see. It feels closer to how social media services used to work originally. I hope it stays that way. Read on website | #miscellaneous

0 views
Susam Pal 1 months ago

Minimal GitHub Workflow

This is a note where I capture the various errors we receive when we create GitHub workflows that are smaller than the smallest possible workflow. I do not know why anyone would ever need this information and I doubt it will serve any purpose for me either but sometimes you just want to know things, no matter how useless they might be. This is one of the useless things I wanted to know today. For the first experiment we just create a zero byte file and push it to GitHub as follows, say, like this: Under the GitHub repo's Actions tab, we find this error: Then we update the workflow as follows: Now we get this error: Next update: Corresponding error: The experiments are preserved in the commit history of github.com/spxy/minighwf . Read on website | #technology Empty Workflow Runs On Ubuntu Latest Empty Steps Hello, World

0 views
Susam Pal 1 months ago

Circular Recursive Negating Acronyms

One of my favourite acronyms from the world of computing and technology is XINU. It stands for 'XINU Is Not Unix'. The delightful thing about this acronym is that XINU is also UNIX spelled backwards. For a given word W , a recursive acronym that both negates W and reverses it is possible when W has the form W = '?NI?' where each '?' denotes a distinct letter. Some fictitious examples make this clearer: Words of the form '?N?' also work if we are happy to contract the word 'is' in the acronym. In fact, in this case we can even obtain circular recursive acronyms: In each pair, the two acronyms negate each other, making them circular while also being reverses of one another. Such acronyms could serve as amusing names for expressing friendly banter between rival projects. Read on website | #miscellaneous LINA Is Not ANIL. TINK Is Not KNIT. ANI's Not INA. INA's Not ANI. ONE's Not ENO. ENO's Not ONE.

0 views
Susam Pal 2 months ago

My Coding Adventures in 2025

In this post, I return with a retrospective on my coding adventures, where I summarise my hobby projects and recreational programming activities from the current year. I did the last such retrospective in 2023 . So I think this is a good time to do another retrospective. At the outset, I should mention that I have done less hobby computing this year than in the past few, largely because I spent a substantial portion of my leisure time studying Galois theory and algebraic graph theory. In case you are wondering where I am learning these subjects from, the books are Galois Theory , 5th ed. by Ian Stewart and Algebraic Graph Theory by Godsil and Royle. Both are absolutely fascinating subjects and the two books I mentioned are quite good as well. I highly recommend them. Now back to the coding adventures. Here they go: MathB : The year began not with the release of a new project but with the opposite: discontinuing a project I had maintained for 13 years. MathB.in, a mathematics pastebin service, was discontinued early this year. This is a project I developed in 2012 for myself and my friends. Although a rather simple project, it was close to my heart, as I have many fond memories of exchanging mathematical puzzles and solutions with my friends using this service. Over time, the project grew quite popular on IRC networks, as well as in some schools and universities, where IRC users, learners, and students used the service to share problems and solutions with one another, much as my friends and I had done in its early days. I shut it down this year because I wanted to move on from the project. Before the shutdown, a kind member of the Archive Team worked with me to archive all posts from the now-defunct website. Although shutting down this service was a bittersweet event for me, I feel relieved that I no longer have to run a live service in my spare time. While this was a good hobby ten years ago, it no longer is. See my blog post MathB.in Is Shutting Down for more details on the reasons behind this decision. The source code of this project remains open source and available at github.com/susam/mathb . QuickQWERTY : This is a touch-typing tutor that runs in a web browser. I originally developed it in 2008 for myself and my friends. While I learned touch typing on an actual typewriter as a child, those lessons did not stick with me. Much later, while I was at university, I came across a Java applet-based touch-typing tutor that finally helped me learn touch typing properly. I disliked installing Java plugins in the web browser, which is why I later developed this project in plain HTML and JavaScript. This year, I carried out a major refactoring to collapse the entire project into a single standalone HTML file with no external dependencies. The source code has been greatly simplified as well. When I was younger and more naive, inspired by the complexity and multiple layers of abstraction I saw in popular open source and professional projects, I tended to introduce similar abstractions and complexity into my personal projects. Over time, however, I began to appreciate simplicity. The new code for this project is smaller and simpler, and I am quite happy with the end result. You can take a look at the code here: quickqwerty.html . If you want to use the typing tutor, go here: QuickQWERTY . Unfortunately, it does not support keyboard layouts other than QWERTY. When I originally developed this project, my view of the computing world was rather limited. I was not even aware that other keyboard layouts existed. You are, however, very welcome to fork the project and adapt the lessons for other layouts. CFRS[] : This project was my first contribution to the quirky world of esolangs. CFRS[] is an extremely minimal drawing language consisting of only six simple commands: , , , , , and . I developed it in 2023 and have since been maintaining it with occasional bug fixes. This year, I fixed an annoying bug that caused the drawing canvas to overflow on some mobile web browsers. A new demo also arrived from the community this year and has now been added to the community demo page. See Glimmering Galaxy for the new demo. If you want to play with CFRS[] now, visit CFRS[] . FXYT : This is another esolang project of mine. This too is a minimal drawing language, though not as minimal as CFRS[]. Instead, it is a stack-based, postfix canvas colouring language with only 36 simple commands. The canvas overflow bug described in the previous entry affected this project as well. That has now been fixed. Further, by popular demand, the maximum allowed code length has been increased from 256 bytes to 1024 bytes. This means there is now more room for writing more complex FXYT programs. Additionally, the maximum code length for distributable demo links has been increased from 64 bytes to 256 bytes. This allows several more impressive demos to have their own distributable links. Visit FXYT to try it out now. See also the Community Demos to view some fascinating artwork created by the community. Nerd Quiz : This is a new project I created a couple of months ago. It is a simple HTML tool that lets you test your nerdiness through short quizzes. Each question is drawn from my everyday moments of reading, writing, thinking, learning, and exploring. The project is meant to serve as a repository of interesting facts I come across in daily life, captured in the form of quiz questions. Go here to try it out: Nerd Quiz . I hope you will enjoy these little bits of knowledge as much as I enjoyed discovering them. Mark V. Shaney Junior : Finally, I have my own Markov gibberish generator. Always wanted to have one. The project is inspired by the legendary Usenet bot named Mark V. Shaney that used to post messages to various newsgroups in the 1980s. My Markov chain program is written in about 30 lines of Python. I ran it on my 24 years of blog posts consisting of over 200 posts and about 200,000 words and it generated some pretty interesting gibberish. See my blog post Fed 24 Years of My Posts to Markov Model to see the examples. Elliptical Python Programming : If the previous item was not silly enough, this one surely is. Earlier this year, I wrote a blog post describing the fine art of Python programming using copious amounts of ellipses. I will not discuss it further here to avoid spoilers. I'll just say that any day I'm able to do something pointless, whimsical and fun with computers is a good day for me. And it was a good day when I wrote this post. Please visit the link above to read the post. I hope you find it fun. Fizz Buzz with Cosines : Another silly post in which I explain how to compute the discrete Fourier transform of the Fizz Buzz sequence and derive a closed-form expression that can be used to print the sequence. Fizz Buzz in CSS : Yet another Fizz Buzz implementation, this time using just four lines of CSS. That wraps up my coding adventures for this year. There were fewer hobby projects than usual but I enjoyed spending more time learning new things and revisiting old ones. One long-running project came to an end, another was cleaned up and a few small new ideas appeared along the way. Looking forward to what the next year brings. Read on website | #programming | #technology | #retrospective MathB : The year began not with the release of a new project but with the opposite: discontinuing a project I had maintained for 13 years. MathB.in, a mathematics pastebin service, was discontinued early this year. This is a project I developed in 2012 for myself and my friends. Although a rather simple project, it was close to my heart, as I have many fond memories of exchanging mathematical puzzles and solutions with my friends using this service. Over time, the project grew quite popular on IRC networks, as well as in some schools and universities, where IRC users, learners, and students used the service to share problems and solutions with one another, much as my friends and I had done in its early days. I shut it down this year because I wanted to move on from the project. Before the shutdown, a kind member of the Archive Team worked with me to archive all posts from the now-defunct website. Although shutting down this service was a bittersweet event for me, I feel relieved that I no longer have to run a live service in my spare time. While this was a good hobby ten years ago, it no longer is. See my blog post MathB.in Is Shutting Down for more details on the reasons behind this decision. The source code of this project remains open source and available at github.com/susam/mathb . QuickQWERTY : This is a touch-typing tutor that runs in a web browser. I originally developed it in 2008 for myself and my friends. While I learned touch typing on an actual typewriter as a child, those lessons did not stick with me. Much later, while I was at university, I came across a Java applet-based touch-typing tutor that finally helped me learn touch typing properly. I disliked installing Java plugins in the web browser, which is why I later developed this project in plain HTML and JavaScript. This year, I carried out a major refactoring to collapse the entire project into a single standalone HTML file with no external dependencies. The source code has been greatly simplified as well. When I was younger and more naive, inspired by the complexity and multiple layers of abstraction I saw in popular open source and professional projects, I tended to introduce similar abstractions and complexity into my personal projects. Over time, however, I began to appreciate simplicity. The new code for this project is smaller and simpler, and I am quite happy with the end result. You can take a look at the code here: quickqwerty.html . If you want to use the typing tutor, go here: QuickQWERTY . Unfortunately, it does not support keyboard layouts other than QWERTY. When I originally developed this project, my view of the computing world was rather limited. I was not even aware that other keyboard layouts existed. You are, however, very welcome to fork the project and adapt the lessons for other layouts. CFRS[] : This project was my first contribution to the quirky world of esolangs. CFRS[] is an extremely minimal drawing language consisting of only six simple commands: , , , , , and . I developed it in 2023 and have since been maintaining it with occasional bug fixes. This year, I fixed an annoying bug that caused the drawing canvas to overflow on some mobile web browsers. A new demo also arrived from the community this year and has now been added to the community demo page. See Glimmering Galaxy for the new demo. If you want to play with CFRS[] now, visit CFRS[] . FXYT : This is another esolang project of mine. This too is a minimal drawing language, though not as minimal as CFRS[]. Instead, it is a stack-based, postfix canvas colouring language with only 36 simple commands. The canvas overflow bug described in the previous entry affected this project as well. That has now been fixed. Further, by popular demand, the maximum allowed code length has been increased from 256 bytes to 1024 bytes. This means there is now more room for writing more complex FXYT programs. Additionally, the maximum code length for distributable demo links has been increased from 64 bytes to 256 bytes. This allows several more impressive demos to have their own distributable links. Visit FXYT to try it out now. See also the Community Demos to view some fascinating artwork created by the community. Nerd Quiz : This is a new project I created a couple of months ago. It is a simple HTML tool that lets you test your nerdiness through short quizzes. Each question is drawn from my everyday moments of reading, writing, thinking, learning, and exploring. The project is meant to serve as a repository of interesting facts I come across in daily life, captured in the form of quiz questions. Go here to try it out: Nerd Quiz . I hope you will enjoy these little bits of knowledge as much as I enjoyed discovering them. Mark V. Shaney Junior : Finally, I have my own Markov gibberish generator. Always wanted to have one. The project is inspired by the legendary Usenet bot named Mark V. Shaney that used to post messages to various newsgroups in the 1980s. My Markov chain program is written in about 30 lines of Python. I ran it on my 24 years of blog posts consisting of over 200 posts and about 200,000 words and it generated some pretty interesting gibberish. See my blog post Fed 24 Years of My Posts to Markov Model to see the examples. Elliptical Python Programming : If the previous item was not silly enough, this one surely is. Earlier this year, I wrote a blog post describing the fine art of Python programming using copious amounts of ellipses. I will not discuss it further here to avoid spoilers. I'll just say that any day I'm able to do something pointless, whimsical and fun with computers is a good day for me. And it was a good day when I wrote this post. Please visit the link above to read the post. I hope you find it fun. Fizz Buzz with Cosines : Another silly post in which I explain how to compute the discrete Fourier transform of the Fizz Buzz sequence and derive a closed-form expression that can be used to print the sequence. Fizz Buzz in CSS : Yet another Fizz Buzz implementation, this time using just four lines of CSS.

0 views
Susam Pal 2 months ago

Mark V. Shaney Junior 0.2.0

Mark V. Shaney Junior 0.2.0 is the second release of this little Markov gibberish generator. This release brings two small changes. First, it now reads the training data from standard input instead of a hardcoded file. Second, the program filename has been changed from to to reflect that it is an executable file and can be run as on most Unix and Linux systems. The source and a detailed documentation for this project are available at github.com/susam/mvs . See also this related blog post and a discussion on Hacker News about it. Read on website | #python | #programming | #technology

1 views
Susam Pal 2 months ago

I Fed 24 Years of My Blog Posts to a Markov Model

Yesterday I shared a little program called Mark V. Shaney Junior at github.com/susam/mvs . It is a minimal implementation of a Markov text generator inspired by the legendary Mark V. Shaney program from the 1980s. If you don't know about Mark V. Shaney, read more about it on the Wikipedia article Mark V. Shaney . It is a very small program with only about 30 lines of Python that favours simplicity over efficiency. As a hobby, I often engage in exploratory programming where I write computer programs not to solve a specific problem but simply to explore a particular idea or topic for the sole purpose of recreation. I must have written small programs to explore Markov chains for various kinds of state spaces over a dozen times by now. Every time, I just pick my last experimental code and edit it to encode the new state space I am exploring. That's usually my general approach to such one-off programs. I have hundreds of tiny little experimental programs lying on my disk at any given time. Once in a while, I get the itch to take one of those exploratory programs, give it some finishing touches, wrap it up in a nice Git repo along with a , and the whole shebang and share it on github.com/susam and codeberg.org/susam . The Mark V. Shaney Junior program that I shared yesterday happened to be one such exercise. If you scroll down the README of this project, you'll find some nice examples of the gibberish produced by this program. The first few examples there are the result of training the model on A Christmas Carol by Charles Dickens, one of my favourite authors. It is often said that Dickens never used fewer words when more would suffice. So I thought there couldn't be a better piece of text when it comes to testing out my tiny Markov model. I'll not reproduce the generated text examples here for the sake of brevity. If you are interested to take a look, just head over to the Gibberish section of the README. Soon after sharing the project, I wondered what kind of gibberish it would produce if I fed all 24 years of my blog posts and pages into the program. Well, here's one of the results: This is the text that comes out after the program consumes over 200 posts consisting of about 200,000 words. My blog also has a comments section with over 500 comments consisting of about 40,000 words. All comments were excluded while training the model. Here is another output example: Here is a particularly incoherent but amusing one: No, I have never said anywhere that opening a Lisp source file could harm anyone's self-esteem. The text generator has picked up the 'Lisp source file' phrase from my Lisp in Vim post and the 'self-esteem' bit from the From Perl to Pi post. By default, this program looks at trigrams (all sequences of three adjacent words) and creates a map where the first two words of the trigram are inserted as the key and the third word is appended to its list value. This map is the model. In this way, the model captures each pair of adjacent words along with the words that immediately follow each pair. The text generator then chooses a key (a pair of words) at random and looks for a word which follows. If there are multiple followers, it picks one at random. That is pretty much the whole algorithm. There isn't much more to it. It is as simple as it gets. For that reason, I often describe a simple Markov model like this as the 'hello, world' for language models. Of course, in 2025, given the overwhelming popularity of large language models (LLMs), Markov models like this look unimpressive. Unlike LLMs, a simple Markov model cannot capture global structure or long-range dependencies within the text. It relies entirely on local word transition statistics. Also, these days, one hardly needs a Markov model to generate gibberish; social media provides an ample supply. Nevertheless, I think the simplicity of its design and implementation serves as a good entry point into language models. In my implementation, the number of words in the key of the map can be set via command line arguments. By default, it is 2 as described above. This value is also known as the order of the model. So by default the order is 2. If we increase it to, say, 3 or 4, the generated text becomes a little more coherent. Here is one such example: Except for a couple of abrupt and meaningless transitions, the text is mostly coherent. We need to be careful about not increasing the order too much. In fact, if we increase the order of the model to 5, the generated text becomes very dry and factual because it begins to quote large portions of the blog posts verbatim. Not much fun can be had with that. Before I end this post, let me present one final example where I ask it to generate text from an initial prompt: Apparently, this is how I would sound if I ever took up speaking gibberish! Read on website | #python | #programming | #technology

1 views
Susam Pal 2 months ago

Mark V. Shaney Junior 0.1.0

Mark V. Shaney Junior is a minimal implementation of a Markov gibberish generator inspired by the legendary Mark V. Shaney program from the 1980s. Mark V. Shaney was a synthetic Usenet user in the 1980s that posted messages to newsgroups using text generated by a Markov chain program. See the Wikipedia article Mark V. Shaney for more details. This release introduces the program mvs.py that implements a similar Markov text generator. It reads a text corpus of text from standard input, build an internal Markov model and then generate text using the model. Please visit github.com/susam/mvs for the source code and some output examples. Read on website | #python | #programming | #technology

0 views
Susam Pal 2 months ago

Fizz Buzz in CSS

How many CSS selectors and declarations do we need to produce the Fizz Buzz sequence? Of course we could do this with no CSS at all simply by placing the entire sequence as text in the HTML body. So to make the problem precise as well as to keep it interesting, we require that all text that appears in the Fizz Buzz sequence comes directly from the CSS. Placing any part of the output numbers or words outside the stylesheet is not allowed. JavaScript is not allowed either. With these constraints, I think we need at least four CSS selectors along with four declarations to solve this puzzle, as shown below: Here is a complete working example: css-fizz-buzz.html . The above solution contains four CSS rules with one selector and one declaration in each rule. For example, is one rule consisting of the selector and the declaration . Seasoned code golfers looking for a challenge can probably shrink this solution further. A common trick to solve this problem is to use an ordered list ( ) which provides the integers in the form of list markers automatically, thereby eliminating the need to maintain a counter in the stylesheet. However, this violates the requirement set out in the introduction. We want every integer to be produced directly by the stylesheet. Treating the integers in the list markers as part of the output sequence breaks this rule. Not to mention the output looks untidy because of the unnecessary dot after each marker and also because the numbers and words appear misaligned. See an example of that trick here: css-fizz-buzz-ol.html . Correcting the misaligned output calls for extra code that cancels out the number of declarations saved. In any case, the requirement that all integers of the output must come directly from the stylesheet prevents untidy solutions like this and also forces us to rely on the capabilities of CSS to solve this puzzle. The intention here was to obtain the smallest possible code in terms of the number of CSS declarations. This example is not crafted to minimise bytes, but for code golfers who enjoy reducing byte count, here is the number of bytes this solution consumes after removing all whitespace: Further reduction in byte count can be achieved by using the element instead of , nesting CSS selectors, etc. I'll leave that as an exercise for the reader. Returning to the focus of this post as well as to summarise it, the solution here uses four CSS rules consisting of four selectors and four declarations to render the Fizz Buzz sequence. Read on website | #absurd | #web | #technology | #puzzle

0 views
Susam Pal 2 months ago

CSS Fizz Buzz with Ordered List

A version of my CSS Fizz Buzz that uses ordered list ( ) to reduce code. However, I don't quite like how misaligned the numbers and the words look. Correcting that would call for extra code that would cancel out the bytes saved. Read on website | #web

0 views
Susam Pal 2 months ago

Triangle-Free Cayley Graph

In this note I elaborate the proof of a claim regarding Cayley graphs of symmetric groups with transpositions as generators that I found in the book Algebraic Graph Theory by Chris Godsil and Gordon Royle. This claim appears as commentary in Section 3.10 about Transpositions . Here I present it in the form of a theorem along with a complete proof. Theorem. If \( \mathcal{T} \) is a set of transpositions, then the Cayley graph \( X(\operatorname{Sym}(n), \mathcal{T}) \) has no triangles. Proof. Suppose the vertices \( a, b, c \in \operatorname{Sym}(n) \) form a triangle in the Cayley graph \( X(\operatorname{Sym}(n), \mathcal{T}). \) Since multiplication by \( a^{-1} \) is an automorphism of the Cayley graph (by the proof of Theorem 3.1.2 that comes earlier), the vertices \( e, ba^{-1}, ca^{-1} \) form a triangle too. Let us label them as \( e, b', c' \) respectively. Now by the definition of a Cayley graph, for any two vertices \( a, b \in \operatorname{Sym}(n), \) we have \begin{align*} a \sim b & \iff ba^{-1} \in \mathcal{T} \\ & \iff ba^{-1} = g \\ & \iff b = ga \end{align*} for some \( g \in \mathcal{T}. \) Therefore \begin{align*} e \sim b' & \iff b' = ge = g, \\ e \sim c' & \iff c' = he = h, \\ b' \sim c' & \iff c' = lb' \end{align*} for some \( g, h, l \in \mathcal{T}. \) Note that the last equality gives \[ l =c'b'^{-1} = hg^{-1} = hg \in \mathcal{T} \] Therefore \( g, h, hg \in \mathcal{T}. \) However, this is impossible since the product of two transpositions is \( e, \) a \( 3 \)-cycle or a product of two disjoint transpositions. For example, \( (12)(12) = e, \) \( (12)(13) = (123) \) and \( (12)(24) = (12)(24). \) Therefore \( hg \) cannot be a transposition, i.e. \( hg \notin \mathcal{T}. \) This is a contradiction. Therefore the vertices \( a, b, c \) cannot form a triangle. We conclude that the Cayley graph \( X(\operatorname{Sym}(n), \mathcal{T}) \) has no triangles. Read on website | #mathematics

0 views
Susam Pal 2 months ago

Emacs Info Expressions

On IRC or Matrix channels, we often share references to the built-in Emacs documentation as Elisp expressions that look like this: Here is another example: This is a common practice in the Emacs community even though all of the Emacs manual is available on the World Wide Web too. For example, the section referred to in the above expression eis available here: GNU Emacs Manual: Word Search . The reason for sharing Elisp expressions like this is likely partly tradition and partly convenience. Many Emacs users are logged into IRC networks via Emacs itself, so once the recipient sees an Elisp expression like the above one in their chat buffer, visiting the corresponding manual page is a simple matter of placing the cursor right after the closing parenthesis and typing . But isn't it clumsy for the sender to type Elisp expressions like this merely to share a pointer to a section of a manual with others? Turns out, it is not. This is Emacs! So of course there are key-bindings to do this. Say, while helping another Emacs user we type and land on the section "Branches" and realise that this is the section that the person we are trying to help should read. Now when we are on this section, we can simply type and Emacs will copy the name of the current Info node to the kill ring. The name looks like this: While this is handy in some situations, it isn't the expression we want. It's just the Info node name. To copy the complete expression with the node name, we need to use the zero prefix argument with the key. So when we are on the section "Branches", if we type , the following expression is copied to the kill ring: The person who receives this expression can visit the corresponding section of the manual simply by evaluating it. For example, after copying the expression in Emacs, they could type to paste the expression into a buffer and evaluate it immediately. Alternatively, they might want to type to bring the minibuffer, paste the expression there and evaluate it. Read on website | #emacs | #technology

0 views
Susam Pal 3 months ago

Fizz Buzz with Cosines

Fizz Buzz is a counting game that has become oddly popular in the world of computer programming as a simple test of basic programming skills. The rules of the game are straightforward. Players say the numbers aloud in order beginning with one. Whenever a number is divisible by 3, they say 'Fizz' instead. If it is divisible by 5, they say 'Buzz'. If it is divisible by both 3 and 5, the player says both 'Fizz' and 'Buzz'. Here is a typical Python program that prints this sequence: Here is the output: fizz-buzz.txt . Can we make the program more complicated? The words 'Fizz', 'Buzz' and 'FizzBuzz' repeat in a periodic manner throughout the sequence. What else is periodic? Trigonometric functions! Perhaps we can use trigonometric functions to encode all four rules of the sequence in a single closed-form expression. That is what we are going to explore in this article, for fun and no profit. By the end, we will obtain a discrete Fourier series that can take any integer \( n \) and select the corresponding text to be printed. In fact, we will derive it using two different methods. First, we will follow a long-winded but hopefully enjoyable approach that relies on a basic understanding of complex exponentiation, geometric series and trigonometric functions. Then, we will obtain the same result through a direct application of the discrete Fourier transform. Before going any further, we establish a precise mathematical definition for the Fizz Buzz sequence. We begin by introducing a few functions that will help us define the Fizz Buzz sequence later. We define a set of four functions \( \{ s_0, s_1, s_2, s_3 \} \) for integers \( n \) by: \begin{align*} s_0(n) &= n, \\ s_1(n) &= \mathtt{Fizz}, \\ s_2(n) &= \mathtt{Buzz}, \\ s_3(n) &= \mathtt{FizzBuzz}. \end{align*} We call these the symbol functions because they produce every term that appears in the Fizz Buzz sequence. The symbol function \( s_0 \) returns \( n \) itself. The functions \( s_1, \) \( s_2 \) and \( s_3 \) are constant functions that always return the literal words \( \mathtt{Fizz}, \) \( \mathtt{Buzz} \) and \( \mathtt{FizzBuzz} \) respectively, no matter what the value of \( n \) is. We define a function \( f(n) \) for integer \( n \) by \[ f(n) = \begin{cases} 1 & \text{if } 3 \mid n \text{ and } 5 \nmid n, \\ 2 & \text{if } 3 \nmid n \text{ and } 5 \mid n, \\ 3 & \text{if } 3 \mid n \text{ and } 5 \mid n, \\ 0 & \text{otherwise}. \end{cases} \] The notation \( m \mid n \) means that the integer \( m \) divides the integer \( n, \) i.e. \( n \) is a multiple of \( m. \) Equivalently, there exists an integer \( c \) such that \( n = cm . \) Similarly, \( m \nmid n \) means that \( m \) does not divide \( n, \) i.e. \( n \) is not a multiple of \( m. \) This function covers all four conditions involved in choosing the \( n \)th item of the Fizz Buzz sequence. As we will soon see, this function tells us which of the four symbol functions produces the \( n \)th item of the Fizz Buzz sequence. For this reason, we call \( f(n) \) the index function. We now define the Fizz Buzz sequence as the sequence \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] We can expand the first few terms of the sequence explicitly as follows: \begin{align*} (s_{f(n)}(n))_{n = 1}^{\infty} &= (s_{f(1)}(1), \; s_{f(2)}(2), \; s_{f(3)}(3), \; s_{f(4)}(4), \; s_{f(5)}(5), \; s_{f(6)}(6), \; s_{f(7)}(7), \; \dots) \\ &= (s_0(1), \; s_0(2), \; s_1(3), \; s_0(4), s_2(5), \; s_1(6), \; s_0(7), \; \dots) \\ &= (1, \; 2, \; \mathtt{Fizz}, \; 4, \; \mathtt{Buzz}, \; \mathtt{Fizz}, \; 7, \; \dots). \end{align*} Note how the function \( f(n) \) produces an index \( i \) which we then use to select the symbol function \( s_i(n) \) to produce the \( n \)th term of the sequence. This is precisely why we decided to call \( f(n) \) the index function while defining it in the previous section. Here we discuss the first method of deriving our closed form expression, starting with indicator functions and rewriting them using complex exponentials and cosines. Here is the index function \( f(n) \) from the previous section with its cases and conditions rearranged to make it easier to spot interesting patterns: \[ f(n) = \begin{cases} 0 & \text{if } 5 \nmid n \text{ and } 3 \nmid n, \\ 1 & \text{if } 5 \nmid n \text{ and } 3 \mid n, \\ 2 & \text{if } 5 \mid n \text{ and } 3 \nmid n, \\ 3 & \text{if } 5 \mid n \text{ and } 3 \mid n. \end{cases} \] This function helps us select another function \( s_{f(n)}(n) \) which in turn determines the \( n \)th term of the Fizz Buzz sequence. Our goal now is to replace this piecewise formula with a single closed-form expression. To do so, we first define indicator functions \( I_m(n) \) as follows: \[ I_m(n) = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] The formula for \( f(n) \) can now be written as: \[ f(n) = \begin{cases} 0 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 0, \\ 1 & \text{if } I_5(n) = 0 \text{ and } I_3(n) = 1, \\ 2 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 0, \\ 3 & \text{if } I_5(n) = 1 \text{ and } I_3(n) = 1. \end{cases} \] Do you see a pattern? Here is the same function written as a table: Do you see it now? If we treat the values in the first two columns as binary digits and the values in the third column as decimal numbers, then in each row the first two columns give the binary representation of the number in the third column. For example, \( 3_{10} = 11_2 \) and indeed in the last row of the table, we see the bits \( 1 \) and \( 1 \) in the first two columns and the number \( 3 \) in the last column. In other words, writing the binary digits \( I_5(n) \) and \( I_3(n) \) side by side gives us the binary representation of \( f(n). \) Therefore \[ f(n) = 2 \, I_5(n) + I_3(n). \] We can now write a small program to demonstrate this formula: We can make it even shorter at the cost of some clarity: What we have obtained so far is pretty good. While there is no universal definition of a closed-form expression, I think most people would agree that the indicator functions as defined above are simple enough to be permitted in a closed-form expression. In the previous section, we obtained the formula \[ f(n) = I_3(n) + 2 \, I_5(n) \] which we then used as an index to look up the text to be printed. We also argued that this is a pretty good closed-form expression already. However, in the interest of making things more complicated, we must ask ourselves: What if we are not allowed to use the indicator functions? What if we must adhere to the commonly accepted meaning of a closed-form expression which allows only finite combinations of basic operations such as addition, subtraction, multiplication, division, integer exponents and roots with integer index as well as functions such as exponentials, logarithms and trigonometric functions. It turns out that the above formula can be rewritten using only addition, multiplication, division and the cosine function. Let us begin the translation. Consider the sum \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m}, \] where \( i \) is the imaginary unit and \( n \) and \( m \) are integers. This is a geometric series in the complex plane with ratio \( r = e^{i 2 \pi n / m}. \) If \( n \) is a multiple of \( m , \) then \( n = cm \) for some integer \( c \) and we get \[ r = e^{i 2 \pi n / m} = e^{i 2 \pi c} = 1. \] Therefore, when \( n \) is a multiple of \( m, \) we get \[ S_m(n) = \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m} = \sum_{k = 0}^{m - 1} 1^k = m. \] If \( n \) is not a multiple of \( m, \) then \( r \ne 1 \) and the geometric series becomes \[ S_m(n) = \frac{r^m - 1}{r - 1} = \frac{e^{i 2 \pi n} - 1}{e^{i 2 \pi n / m} - 1} = 0. \] Therefore, \[ S_m(n) = \begin{cases} m & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] Dividing both sides by \( m, \) we get \[ \frac{S_m(n)}{m} = \begin{cases} 1 & \text{if } m \mid n, \\ 0 & \text{if } m \nmid n. \end{cases} \] But the right-hand side is \( I_m(n). \) Therefore \[ I_m(n) = \frac{S_m(n)}{m} = \frac{1}{m} \sum_{k = 0}^{m - 1} e^{i 2 \pi k n / m}. \] We begin with Euler's formula \[ e^{i x} = \cos x + i \sin x \] where \( x \) is a real number. From this formula, we get \[ e^{i x} + e^{-i x} = 2 \cos x. \] Therefore \begin{align*} I_3(n) &= \frac{1}{3} \sum_{k = 0}^2 e^{i 2 \pi k n / 3} \\ &= \frac{1}{3} \left( 1 + e^{i 2 \pi n / 3} + e^{i 4 \pi n / 3} \right) \\ &= \frac{1}{3} \left( 1 + e^{i 2 \pi n / 3} + e^{-i 2 \pi n / 3} \right) \\ &= \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right). \end{align*} The third equality above follows from the fact that \( e^{i 4 \pi n / 3} = e^{i 6 \pi n / 3} e^{-i 2 \pi n / 3} = e^{i 2 \pi n} e^{-i 2 \pi n/3} = e^{-i 2 \pi n / 3} \) when \( n \) is an integer. The function above is defined for integer values of \( n \) but we can extend its formula to real \( x \) and plot it to observe its shape between integers. As expected, the function takes the value \( 1 \) whenever \( x \) is an integer multiple of \( 3 \) and \( 0 \) whenever \( x \) is an integer not divisible by \( 3. \) Similarly, \begin{align*} I_5(n) &= \frac{1}{5} \sum_{k = 0}^4 e^{i 2 \pi k n / 5} \\ &= \frac{1}{5} \left( 1 + e^{i 2 \pi n / 5} + e^{i 4 \pi n / 5} + e^{i 6 \pi n / 5} + e^{i 8 \pi n / 5} \right) \\ &= \frac{1}{5} \left( 1 + e^{i 2 \pi n / 5} + e^{i 4 \pi n / 5} + e^{-i 4 \pi n / 5} + e^{-i 2 \pi n / 5} \right) \\ &= \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right). \end{align*} Extending this expression to real values of \( x \) allows us to plot its shape as well. Once again, the function takes the value \( 1 \) at integer multiples of \( 5 \) and \( 0 \) at integers not divisible by \( 5. \) Recall that we expressed \( f(n) \) as \[ f(n) = I_3(n) + 2 \, I_5(n). \] Substituting these trigonometric expressions yields \[ f(n) = \frac{1}{3} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + 2 \cdot \left( \frac{1}{5} + \frac{2}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{2}{5} \cos \left( \frac{4 \pi n}{5} \right) \right). \] A straightforward simplification gives \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right). \] We can extend this expression to real \( x \) and plot it as well. The resulting curve takes the values \( 0, 1, 2 \) and \( 3 \) at integer points, as desired. Now we can write our Python program as follows: The keen-eyed might notice that the expression we obtained for \( f(n) \) is a discrete Fourier series. This is not surprising, since the output of a Fizz Buzz program depends only on \( n \bmod 15. \) Any function on a finite cyclic group can be written exactly as a finite Fourier expansion. In this section, we obtain \( f(n) \) using the discrete Fourier transform. It is worth mentioning that the calculations presented here are quite tedious to do by hand. Nevertheless, this section offers a glimpse of how such calculations are performed. By the end, we will arrive at exactly the same \( f(n) \) as before. There is nothing new to discover here. We simply obtain the same result by a more direct but more laborious method. If this doesn't sound interesting, you may safely skip the subsections that follow. We know that \( f(n) \) is a periodic function with period \( 15. \) To apply the discrete Fourier transform, we look at one complete period of the function using the values \( n = 0, 1, \dots, 14. \) Over this period, we have: \begin{array}{c|ccccccccccccccc} n & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 \\ \hline f(n) & 3 & 0 & 0 & 1 & 0 & 2 & 1 & 0 & 0 & 1 & 2 & 0 & 1 & 0 & 0 \end{array} The discrete Fourier series of \( f(n) \) is \[ f(n) = \sum_{k = 0}^{14} c_k \, e^{i 2 \pi k n / 15} \] where the Fourier coefficients \( c_k \) are given by the discrete Fourier transform \[ c_k = \frac{1}{15} \sum_{n = 0}^{14} f(n) e^{-i 2 \pi k n / 15}. \] for \( k = 0, 1, \dots, 14. \) The formula for \( c_k \) is called the discrete Fourier transform (DFT). The formula for \( f(n) \) is called the inverse discrete Fourier transform (IDFT). Let \( \omega = e^{-i 2 \pi / 15}. \) Then using the values of \( f(n) \) from the table above, the DFT becomes: \[ c_k = \frac{3 + \omega^{3k} + 2 \omega^{5k} + \omega^{6k} + \omega^{9k} + 2 \omega^{10k} + \omega^{12k}}{15}. \] Substituting \( k = 0, 1, 2, \dots, 14 \) into the above equation gives us the following Fourier coefficients: \begin{align*} c_{0} &= \frac{11}{15}, \\ c_{3} &= c_{6} = c_{9} = c_{12} = \frac{2}{5}, \\ c_{5} &= c_{10} = \frac{1}{3}, \\ c_{1} &= c_{2} = c_{4} = c_{7} = c_{8} = c_{11} = c_{13} = c_{14} = 0. \end{align*} Calculating these Fourier coefficients by hand can be rather tedious. In practice they are almost always calculated using numerical software, computer algebra systems or even simple code such as the example here: fizz-buzz-fourier.py . Once the coefficients are known, we can substitute them into the inverse transform introduced earlier to obtain \begin{align*} f(n) &= \sum_{k = 0}^{14} c_k \, e^{i 2 \pi k n / 15} \\[1.5em] &= \frac{11}{15} + \frac{2}{5} \left( e^{i 2 \pi \cdot 3n / 15} + e^{i 2 \pi \cdot 6n / 15} + e^{i 2 \pi \cdot 9n / 15} + e^{i 2 \pi \cdot 12n / 15} \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( e^{i 2 \pi \cdot 5n / 15} + e^{i 2 \pi \cdot 10n / 15} \right) \\[1em] &= \frac{11}{15} + \frac{2}{5} \left( e^{i 2 \pi \cdot 3n / 15} + e^{i 2 \pi \cdot 6n / 15} + e^{-i 2 \pi \cdot 6n / 15} + e^{-i 2 \pi \cdot 3n / 15} \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( e^{i 2 \pi \cdot 5n / 15} + e^{-i 2 \pi \cdot 5n / 15} \right) \\[1em] &= \frac{11}{15} + \frac{2}{5} \left( 2 \cos \left( \frac{2 \pi n}{5} \right) + 2 \cos \left( \frac{4 \pi n}{5} \right) \right) \\ & \phantom{=\frac{11}{15}} + \frac{1}{3} \left( 2 \cos \left( \frac{2 \pi n}{3} \right) \right) \\[1em] &= \frac{11}{15} + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right) + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right). \end{align*} This is exactly the same expression for \( f(n) \) we obtained in the previous section. We see that the Fizz Buzz index function \( f(n) \) can be expressed precisely using the machinery of Fourier analysis. To summarise, we have defined the Fizz Buzz sequence as \[ (s_{f(n)}(n))_{n = 1}^{\infty} \] where \[ f(n) = \frac{11}{15} + \frac{2}{3} \cos \left( \frac{2 \pi n}{3} \right) + \frac{4}{5} \cos \left( \frac{2 \pi n}{5} \right) + \frac{4}{5} \cos \left( \frac{4 \pi n}{5} \right). \] and \( s_0(n) = n, \) \( s_1(n) = \mathtt{Fizz}, \) \( s_2(n) = \mathtt{Buzz} \) and \( s_3(n) = \mathtt{FizzBuzz}. \) A Python program to print the Fizz Buzz sequence based on this definition was presented earlier. That program can be written more succinctly as follows: We can also wrap this up nicely in a shell one-liner, in case you want to share it with your friends and family and surprise them: We have taken a simple counting game and turned it into a trigonometric construction consisting of a discrete Fourier series with three cosine terms and four coefficients. None of this makes Fizz Buzz any easier. Quite the contrary. But it does show that every \( \mathtt{Fizz} \) and \( \mathtt{Buzz} \) now owes its existence to a particular set of Fourier coefficients. We began with the modest goal of making this simple problem more complicated. I think it is safe to say that we did not fall short. Read on website | #absurd | #python | #programming | #technology | #mathematics | #puzzle Definitions Symbol Functions Index Function Fizz Buzz Sequence From Indicator Functions to Cosines Indicator Functions Complex Exponentials Discrete Fourier Transform One Period of Fizz Buzz Fourier Coefficients Inverse Transform

0 views
Susam Pal 3 months ago

Nerd Quiz #2

Nerd Quiz #2 is the second release of Nerd Quiz, a single-page HTML application that invites you to gauge your nerd level through short, focused quiz sets. Each question is carefully crafted from everyday moments of reading, writing, thinking, learning and exploring. This release introduces five new questions drawn from a range of topics such as chess, graph theory and linguistics. To try the quiz, visit nq.html . Read on website | #web | #miscellaneous | #game

0 views
Susam Pal 4 months ago

Nerd Quiz

A nerdy, handcrafted quiz built from everyday moments of reading, writing, thinking, learning and exploring. Read on website | #web | #technology

0 views
Susam Pal 4 months ago

Nerd Quiz #1

Nerd Quiz #1 is the first release of Nerd Quiz, a single-page HTML application that lets you test your nerd level through short, focused quiz sets. Each question in the quiz is handcrafted from everyday moments of reading, writing, thinking, learning and exploring. To try the quiz now, visit nq.html . The source code of Nerd Quiz is available at github.com/susam/nq under the terms of the MIT licence. Read on website | #web | #miscellaneous | #game

0 views
Susam Pal 4 months ago

QuickQWERTY 1.2.0

QuickQWERTY 1.2.0 is now available. QuickQWERTY is a web-based touch typing tutor for QWERTY keyboards that runs directly in the web browser. This release brings small improvements to make the typing tutor smoother and the lessons more accurate. When you return to the application, it no longer needlessly redirects you back to Unit 1.1 if that was the last lesson you practised. Several lessons have received minor corrections. Units 7.2 and 7.3 no longer include the word "tyre", avoiding a preference for either British or American spelling. Units 9.2 and 9.3 now spell "orwellian" correctly. A factual error in Unit 17.4 has been fixed too. It previously said "89 is one more than 99", which now correctly reads "89 is one more than 88". Finally, switching between the 5-6 and 6-7 split layouts is now more reliable. In the previous release, pressing Esc to cancel the confirmation dialogue still caused the switch to go through. Now pressing Esc properly cancels the switch as expected. To try out the new release of QuickQWERTY, go to quickqwerty.html . Read on website | #web | #programming

0 views
Susam Pal 5 months ago

Reinstated My Guestbook After 20 Years

I have reinstated the guestbook on this website after 20 years! As I have written in the About page, this website began its life as an intranet portal in my university network back in 2001–2005. That portal first ran on Microsoft Personal Web Server (PWS) and later on Microsoft Internet Information Services (IIS), with the guestbook data stored in a Microsoft Access database. At the same time, I also maintained a small public website on the Internet, hosted on a subdomain provided by a free hosting service, where I published a subset of the articles from the intranet portal. Both the intranet portal and the public website had guestbooks. The guestbook on the public website was powered by a CGI script provided by the hosting service, which inserted each comment directly into the guestbook HTML page. As some of you might guess, yes, it was vulnerable to cross-site scripting, but that's beside the point. After leaving the university, I registered my own domain name and set up my current website on the World Wide Web and included some of the content from both the original intranet portal and the old public website. For some reason, though, I never thought to include a guestbook. And so this website went on without a guestbook for the next 20 years. Finally, in June 2025, I decided to bring the guestbook back. I already had a commenting system implemented for this website using Common Lisp and Hunchentoot, so I simply reused it for the guestbook. The server program accepts POST requests to receive comments and writes them to text files on the web server for manual review. The comments are then rendered as static HTML pages by my static site generator. In a way, it is less a comment system and more a static comment pages generator. Unfortunately, I could not restore all the comments from the original guestbooks. The ASP source code of my intranet portal as well as the guestbook database are now lost to time. Note that what I refer to as ASP here is now known as Classic ASP, to avoid confusion with ASP.NET. A CD-ROM backup eventually succumbed to disc rot and with it vanished the source code and the database. But a handful of comments had survived because they had found their way into other files and correspondence and I have now included them here. I had better luck with the guestbook of the old public website, from which I was able to recover a greater number of comments. It is still only a small fraction of what once existed, but it feels good to have even those fragments preserved. The new guestbook is now available here: Guestbook . I wasn't expecting anybody to notice the new guestbook. It is only mentioned in the About and Links pages, neither of which see much traffic. Here are a few examples: I love that your website has a guestbook! :D Love the guestbook! We need more of these. Makes you feel invited. It's a small addition to my website, but after two decades, it feels good to see the guestbook alive again and even better to see visitors enjoying it! Read on website | #web | #technology

0 views