
    
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
            
{"version":"https:\/\/jsonfeed.org\/version\/1","title":"mathspp.com feed","home_page_url":"https:\/\/mathspp.com\/blog\/tags\/slice-of-life","feed_url":"https:\/\/mathspp.com\/blog\/tags\/slice-of-life.json","description":"Stay up-to-date with the articles on mathematics and programming that get published to mathspp.com.","author":{"name":"Rodrigo Gir\u00e3o Serr\u00e3o"},"items":[{"title":"Ask the LLM to write code for it","date_published":"2026-03-24T14:16:00+01:00","id":"https:\/\/mathspp.com\/blog\/ask-the-llm-to-write-code-for-it","url":"https:\/\/mathspp.com\/blog\/ask-the-llm-to-write-code-for-it","content_html":"<p>This article covers a useful LLM pattern where you ask the LLM to write code to solve a problem instead of asking it to solve the problem directly.<\/p>\n\n<h2 id=\"the-problem-of-merging-two-transcripts\">The problem of merging two transcripts<a href=\"#the-problem-of-merging-two-transcripts\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>I had two files that contained two halves of the transcript of an audio recording and I wanted to use an LLM to merge the two halves.\nThere were three reasons that stopped me from simply copying part 2 and pasting it after part 1:<\/p>\n<ol>\n<li>the two transcripts overlapped (the end of part 1 was after the start of part 2);<\/li>\n<li>the timestamps for part 2 started from 0, so they were missing an offset; and<\/li>\n<li>speaker identification was not consistent.<\/li>\n<\/ol>\n<p>I uploaded the two halves into ChatGPT and asked it to merge the two transcripts, fix the timestamps and the speaker identification, but to not change the text.<\/p>\n<p>The result I got back was a ridiculous attempt at providing the full transcript, with two sections that supposedly represented parts of either transcript I could just copy and paste confidently, and a couple of other ridiculous blunders.<\/p>\n<p>Instead of fighting ChatGPT, I decided to use a very useful pattern I learned about last year.<\/p>\n<h2 id=\"ask-the-llm-to-write-code-for-it\">Ask the LLM to write code for it<a href=\"#ask-the-llm-to-write-code-for-it\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Instead of asking ChatGPT to merge the transcripts, I could ask it to analyse them, find the solutions to the three problems listed above, and then write code that would merge the transcripts.<\/p>\n<p>Since I was confident that ChatGPT could<\/p>\n<ol>\n<li>identify the overlap between the two files;<\/li>\n<li>use the overlap information to compute the timestamp offset required for part 2; and<\/li>\n<li>figure out you had to swap the two speakers in part 2,<\/li>\n<\/ol>\n<p>I knew ChatGPT would be able to write a Python script that could read from both files and apply a couple of string operations to the second part.<\/p>\n<p>This yielded much better results in two ways.\nChatGPT was able to find the solutions for the three problems above and write a script that fixed them automatically.\nThat was the goal.<\/p>\n<p>On top of that, since ChatGPT had a very clear implicit goal \u2014 get the final merged transcript \u2014 and since running Python code is something that ChatGPT can do, ChatGPT even ran the script for me and produced two artifacts at the end:<\/p>\n<ol>\n<li>the full Python script I could run against the two halves if I wanted; and<\/li>\n<li>the final, fixed transcript.<\/li>\n<\/ol>\n<p>This is an example application of a really useful LLM pattern:<\/p>\n<blockquote>\n<p>Don't ask the LLM to solve a problem. Instead, ask it to write code that solves the problem.<\/p>\n<\/blockquote>\n<p>As another visual example, it's much easier to ask an LLM to write a Python script that draws a path that solves a maze (that's just a couple hundred of lines of code) than it is to upload an image and ask the LLM to draw a <em>valid path<\/em> on the picture of a maze.\nTry it yourself!<\/p>","summary":"This article covers a useful LLM pattern where you ask the LLM to write code to solve a problem instead of asking it to solve the problem directly.","date_modified":"2026-07-06T16:16:52+02:00","tags":["python","programming","llms","slice of life"],"image":"\/user\/pages\/02.blog\/ask-the-llm-to-write-code-for-it\/thumbnail.webp"},{"title":"Regex in real life","date_published":"2025-06-08T17:34:00+02:00","id":"https:\/\/mathspp.com\/blog\/regex-in-real-life","url":"https:\/\/mathspp.com\/blog\/regex-in-real-life","content_html":"<p>This article shows how I have used regex in real life for all sorts of tasks.<\/p>\n\n<p>I love keeping track of real-life examples of the usage of certain features and regex is one of those things, so in this article you will find real-life examples of how\/where I used regular expressions to do all sorts of tasks.<\/p>\n<p>As I use regex in new and interesting ways, I will update this article to include those examples.<\/p>\n<h2 id=\"mass-reformatting-hints-to-some-exercises\">Mass-reformatting hints to some exercises<a href=\"#mass-reformatting-hints-to-some-exercises\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>I was working on some exercises for <a href=\"\/courses\/intermediate-python-course\">my intermediate Python course<\/a> and they all had this format:<\/p>\n<pre><code class=\"language-markdown\">#### Exercise&nbsp;?\nProblem statement goes here.\n\n*Hint: exercise hint goes here.*<\/code><\/pre>\n<p>I wanted to reformat this so that the hints weren't shown by default, so I wanted to have this:<\/p>\n<pre><code class=\"language-markdown\">#### Exercise&nbsp;?\nProblem statement goes here.\n\n&lt;details&gt;\n&lt;summary&gt;Hint:&lt;\/summary&gt;\n\nExercise hint goes here.\n\n&lt;\/details&gt;<\/code><\/pre>\n<p>I could easily search for <code>\\*Hint: (.*)\\*<\/code>, giving me all the lines with hints, and just as easily I could replace that with the <code>&lt;details&gt;<\/code>\/<code>&lt;summary&gt;<\/code> tags...\nBut then I would still have hundreds of lines to fix, because the first line of the problem hint was in lowercase in the original text and I wanted it uppercase now, since the hint is now a standalone sentence...<\/p>\n<p>Thankfully, <a href=\"\/blog\/til\/change-casing-in-search-and-replace\">VS Code allows you to do search &amp; replace operations that modify the casing of the matches<\/a>, so that's what I ended up doing.\nI didn't even need to bring out Python to do this. &#128517;<\/p>\n<h2 id=\"compiling-a-numbered-list-of-tip-titles\">Compiling a numbered list of tip titles<a href=\"#compiling-a-numbered-list-of-tip-titles\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>For <a href=\"\/drops\">my newsletter Python drops &#128013;&#128167;<\/a>, I keep a list of all previous tips titles so that people can see what they're missing out on.\nTo compile this list, I run a short script that traverses the directory where the tips are and finds their number and title.<\/p>\n<p>This is the rough directory structure:<\/p>\n<pre><code>|- 0001-zip_strict_true\n   |- tip.md\n|- 0002-string_casefold_case-insensitive_comparison\n   |- tip.md\n|- 0003_type_unions_with_pipe\n   |- tip.md\n...<\/code><\/pre>\n<p>Each directory contains some other files, but I only care about the files <code>**\/tip.md<\/code> for the purposes of this specific task.\nI want to check each file <code>tip.md<\/code> and extract the tip number and title.<\/p>\n<p>As an example, this is what the file <code>0003_type_unions_with_pipe\/tip.md<\/code> looks like:<\/p>\n<pre><code class=\"language-txt\">---\nthemes:\n    - \"typing\/type hints\"\n    - \"`isinstance`\"\n    - \"vertical bar `|`\"\n    - \"type unions\"\n---\n\n## 3 &ndash; Type unions with the vertical bar in `isinstance`\n\n...<\/code><\/pre>\n<p>Assuming all files <code>tip.md<\/code> have a title in the same format, I can use regex to extract them:<\/p>\n<pre><code class=\"language-py\">TITLE_PATTERN = re.compile(r\"(?m)^## (?P&lt;nr&gt;\\d{1,4}) &ndash; (?P&lt;title&gt;.*)$\")<\/code><\/pre>\n<p>This neat little pattern uses a couple of nice regex features.<\/p>\n<p>By using the inline flag <code>(?m)<\/code>, I make sure that the special characters <code>^<\/code> and <code>$<\/code> match the beginning and end of every new line, respectively, instead of only matching the beginning and end of the string.<\/p>\n<p>I also use named groups with the syntax <code>(?P&lt;group_name&gt;...)<\/code>, which allows me to refer to the parts of the match that I care about by name, instead of having to figure...<\/p>","summary":"This article shows how I have used regex in real life for all sorts of tasks.","date_modified":"2025-09-03T02:19:00+02:00","tags":["regex","python","programming","slice of life"],"image":"\/user\/pages\/02.blog\/regex-in-real-life\/thumbnail.webp"},{"title":"Beating LinkedIn \u201cQueens\u201d with Python","date_published":"2025-02-25T18:40:00+01:00","id":"https:\/\/mathspp.com\/blog\/beating-linkedin-queens-with-python","url":"https:\/\/mathspp.com\/blog\/beating-linkedin-queens-with-python","content_html":"<p>This is a short account of how I wrote a program that solves all LQueens puzzles from LinkedIn automatically with Python.<\/p>\n\n<p>About a year ago LinkedIn started publishing a daily logic puzzle called &ldquo;Queens&rdquo;.\nThis puzzle is a crossover between the <a href=\"\/blog\/problems\/8-queens\">queen-placing puzzle from chess<\/a> and Sudoku, and takes place in a square grid with a number of heterogeneous coloured regions, as the image below demonstrates:<\/p>\n<figure class=\"image-caption\"><img title=\"Queens puzzle #179.\" alt='A screenshot showing the text \"Puzzle 179 Appeared on 26 October 2024\" and a coloured grid that is 8 by 8 with 8 coloured regions of heterogeneous shapes.' src=\"\/user\/pages\/02.blog\/beating-linkedin-queens-with-python\/_puzzle.webp\"><figcaption class=\"\">Queens puzzle #179.<\/figcaption><\/figure><h2 id=\"puzzle-rules\">Puzzle rules<a href=\"#puzzle-rules\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The rules for the puzzle are quite simple.\nYou have to place one queen on each coloured region while also making sure that:<\/p>\n<ul><li>there is one and only one queen per row and column; and<\/li>\n<li>queens cannot touch each other, not even diagonally.<\/li>\n<\/ul><p>See if you can solve the puzzle above, for example by visiting <a href=\"https:\/\/www.archivedqueens.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">this unofficial archive<\/a>.\n(Make sure to select the same puzzle as I'm showing above, number 179.)<\/p>\n<h2 id=\"solving-the-puzzle\">Solving the puzzle<a href=\"#solving-the-puzzle\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>I write algorithms for things like this all the time &ndash; which is a bit of a weird thing to be experienced on, but oh well... &ndash;, so solving the puzzle in Python was actually quite straightforward.<\/p>\n<p>I decided to represent the board as a list of sets, where each set contains the coordinates of the cells of a coloured region.\nFor example, puzzle 179 shown above would be represented as the following list of sets:<\/p>\n<pre><code class=\"language-py\">[\n    {(0, 1), (4, 0), (0, 0), (7, 0), (2, 0), (3, 0), ...},  # yellow\n    {(7, 4), (7, 1), (2, 1), (7, 7), (3, 1), (6, 1), ...},  # orange\n    {(2, 4), (0, 4), (0, 3), (1, 4), (0, 6), (0, 2), ...},  # blue\n    {(2, 3), (3, 2), (1, 2), (2, 2)},                       # green\n    {(6, 2), (4, 3), (4, 2), (3, 3), (5, 2)},               # gray\n    {(2, 5), (3, 4), (3, 5), (1, 5)},                       # red\n    {(4, 4), (5, 5), (6, 5), (5, 4), (4, 5)},               # purple\n    {(0, 7), (2, 7), (3, 7), (4, 6), (5, 7), (1, 7), ...},  # brown\n]<\/code><\/pre>\n<p>The good stuff lies in the function <code>solve<\/code>.\nThe function <code>solve<\/code> is a recursive function that accepts the list with coloured groups that don't have a queen yet and returns the list of positions where queens must go.<\/p>\n<p>How does the function know it's done?\nIf it receives an empty list of coloured groups, then that means all groups received a queen, and we can start producing the list of queen positions:<\/p>\n<pre><code class=\"language-py\">def solve(group_sets):\n    if not group_sets:\n        return []\n\n    # ...<\/code><\/pre>\n<p>That's the best possible scenario, because that's when the function is already done.\nBut usually, the function has work to do.<\/p>\n<p>If the list of coloured groups is not empty, then the function looks at the very first coloured group, which should be <code>group_sets[0]<\/code>, and traverses that set of positions, one position at a time:<\/p>\n<pre><code class=\"language-py\">    for tx, ty in group_sets[0]:<\/code><\/pre>\n<p>The <code>tx<\/code> and <code>ty<\/code> and the x and y coordinates for the <em>tentative<\/em> position of the queen of the current coloured group.\nBut let us assume, for a second,...<\/p>","summary":"This is a short account of how I wrote a program that solves all Queens puzzles from LinkedIn automatically with Python.","date_modified":"2025-07-23T16:49:02+02:00","tags":["algorithms","image processing","python","programming","recursion","slice of life"],"image":"\/user\/pages\/02.blog\/beating-linkedin-queens-with-python\/thumbnail.webp"},{"title":"Automatic site updates with cog and pre-commit","date_published":"2024-08-09T19:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/automatic-site-updates-with-cog-and-pre-commit","url":"https:\/\/mathspp.com\/blog\/automatic-site-updates-with-cog-and-pre-commit","content_html":"<p>This article outlines how I use a pre-commit hook and cog to keep my blog stats updated automatically.<\/p>\n\n<p>Some months ago I introduced some <a href=\"\/blog\">basic stats in my blog<\/a> that show how many articles I've published, along with how many words and lines of code those articles contain.\nI <a href=\"\/blog\/adding-stats-to-my-blog\">wrote an article explaining how I wrote a script that gathers these blog stats for me<\/a> but that's a script I still need to run and then manually update the stats.<\/p>\n<p>I wanted to automate this process but I couldn't bring myself to do it.\nAnd then, I came to the silly realisation that I could use ChatGPT to help me with the set up that I was dreading to do.<\/p>\n<h2 id=\"rewriting-the-stats-with-cog\">Rewriting the stats with cog<a href=\"#rewriting-the-stats-with-cog\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p><a href=\"https:\/\/nedbatchelder.com\/code\/cog\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">Cog<\/a> is &ldquo;a file generation tool&rdquo; that lets you use snippets of Python code to fill in parts of other files.\nFor this particular project, I wanted it to rewrite <a href=\"https:\/\/github.com\/mathspp\/mathspp\/blob\/876da9f7b38708f928e0940247cfa1e10ba8c68a\/pages\/02.blog\/blog.md?plain=1#L16-L18\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">the Markdown table that lists the blog stats<\/a>, that looks like this:<\/p>\n<pre><code class=\"language-markdown\">...\n\n| 364 | 401,577 | 33,251 |\n| :-: | :-: | :-: |\n| articles | words | lines of code |\n\n...<\/code><\/pre>\n<p>I already have a script <code>stats.py<\/code> that computes the stats, so I started by changing it to produce the output in this exact format.\nThen, I tweaked the markdown file to include a cog tag that runs the script and inserts its output in the markdown file.\nThe markdown file now looks like this:<\/p>\n<pre><code class=\"language-markdown\">...\n\n&lt;!--\n[[[cog\nimport cog, subprocess\nresult = subprocess.run([\"python\", \"pages\/02.blog\/stats.py\"], text=True, capture_output=True)\ncog.outl(result.stdout)\n]]]--&gt;\n| 364 | 401,577 | 33,251 |\n| :-: | :-: | :-: |\n| articles | words | lines of code |\n&lt;!--[[[end]]]---&gt;\n\n...<\/code><\/pre>\n<p>The <code>[[[cog<\/code> part tells cog where my Python code starts and the <code>]]]<\/code> tells it where it ends.\nIn my case, I'm just using the module <code>subprocess<\/code> to run the script I already have, I'm capturing its output, and I'm reemitting the output through cog.<\/p>\n<p>The output that I produce with <code>cog.outl<\/code> is then inserted between the <code>]]]<\/code> and the <code>[[[end]]]<\/code>, which is where the table ends up being.\nIn case you're wondering, for every run cog will start by clearing the previous table and then will write the new one.<\/p>\n<p>Finally, note that the cog tags are surrounded by HTML multi-line comments, so that the cog tags themselves do not show up in the final page.\nAt least, not visibly.\nIf you inspect the source code of <a href=\"\/blog\">my blog page<\/a> you will see the HTML comments with the cog tags there.<\/p>\n<h2 id=\"triggering-cog-automatically-with-pre-commit\">Triggering cog automatically with pre-commit<a href=\"#triggering-cog-automatically-with-pre-commit\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The thing with cog is that I still have to run <code>cog -r blog.md<\/code> to update the table, so my next step is to automate the step that calls cog.\nFor that, I asked ChatGPT to help me set up <a href=\"https:\/\/pre-commit.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">a pre-commit hook<\/a> that is triggered when my blog pages change.<\/p>\n<p>After a couple of exchanges with ChatGPT, I ended up with <a href=\"https:\/\/github.com\/mathspp\/mathspp\/blob\/876da9f7b38708f928e0940247cfa1e10ba8c68a\/.pre-commit-config.yaml\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">the following <code>.pre-commit-config.yaml<\/code> file<\/a>...<\/p>","summary":"This article outlines how I use a pre-commit hook and cog to keep my blog stats updated automatically.","date_modified":"2026-07-06T16:16:52+02:00","tags":["git","llms","productivity","programming","python","scripting","slice of life"],"image":"\/user\/pages\/02.blog\/automatic-site-updates-with-cog-and-pre-commit\/thumbnail.webp"},{"title":"Dynamic string replacements with regex","date_published":"2024-01-09T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/dynamic-string-replacements-with-regex","url":"https:\/\/mathspp.com\/blog\/dynamic-string-replacements-with-regex","content_html":"<p>Learn how to find text patterns and replace them with dynamic content using regex.<\/p>\n\n<p>The other day I was working on <a href=\"https:\/\/mathspp.gumroad.com\/l\/puzzles-riddles-problems\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">my problems book<\/a> and I needed to update a markdown file to number the headings.\nIn short, I had a markdown file like this:<\/p>\n<pre><code class=\"language-md\"># Problems\n\n## Dancing triangle\n\n...\n\n## Bag full of numbers\n\n...\n\n## Quarrel in the Shire\n\n...<\/code><\/pre>\n<p>I wanted to find all H2 headings and number them, so I'd end up with this file:<\/p>\n<pre><code class=\"language-md\"># Problems\n\n## 01 &ndash; Dancing triangle\n\n...\n\n## 02 &ndash; Bag full of numbers\n\n...\n\n## 03 &ndash; Quarrel in the Shire\n\n...<\/code><\/pre>\n<p>I thought about doing this manually but I'd need to do this to 2 documents, each with thousands of lines and 64 headings, so I decided to use a bit of Python and regex to do it.<\/p>\n<p>The module <code>re<\/code>, from the Python standard library, lets you do string replacements with <a href=\"https:\/\/docs.python.org\/3\/library\/re.html#re.sub\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">the function <code>re.sub<\/code><\/a>, which needs 3 parameters:<\/p>\n<ol><li>the pattern we're looking for;<\/li>\n<li>the replacement for the pattern; and<\/li>\n<li>the string we're searching &amp; replacing in.<\/li>\n<\/ol><p><code>re.sub<\/code> is like the string method <code>str.replace<\/code> on steroids!<\/p>\n<p>In my case, the pattern I was looking for was this:<\/p>\n<pre><code>^## (.*)$<\/code><\/pre>\n<p>This looks for a <code>##<\/code> at the beginning of the line, followed by a space, and then the <code>(.*)$<\/code> matches everything else until the end of the line.<\/p>\n<p>We can see this in action if we use <code>re.findall<\/code> and a dummy string:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; import re\n&gt;&gt;&gt; string = '# Problems\\n\\n## Dancing triangle\\n\\n...\\n\\n## Bag full of numbers\\n\\n...\\n\\n## Quarrel in the Shire\\n\\n...'\n&gt;&gt;&gt; re.findall(\"^## (.*)$\", string, flags=re.MULTILINE)\n[\n    'Dancing triangle',\n    'Bag full of numbers',\n    'Quarrel in the Shire'\n]<\/code><\/pre>\n<p>The flag <code>re.MULTILINE<\/code> was used so that the anchors <code>^<\/code> and <code>$<\/code> matche the beginning and end of each line, respectively, instead of the beginning and end of the string.<\/p>\n<p>(Go to <a href=\"https:\/\/regex101.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">regex101<\/a> (an online regex playground), paste the regular expression <code>^## (.*)$<\/code> in the top, middle bar, and copy and paste the first version of the <code># Problems<\/code> markdown in the big, central text area.)<\/p>\n<p>The next thing I wanted was to be able to replace each title with a number followed by <code>&ndash;<\/code> and then itself!\nBy using group references, adding the <code>&ndash;<\/code> before the title is easy:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; print(\n...     re.sub(\"^## (.*)$\", r\"## xx &ndash; \\1\", string, flags=re.MULTILINE)\n... )\n# Problems\n\n## xx &ndash; Dancing triangle\n\n...\n\n## xx &ndash; Bag full of numbers\n\n...\n\n## xx &ndash; Quarrel in the Shire\n\n...<\/code><\/pre>\n<p>The &ldquo;difficult&rdquo; part is adding the number that must be incremented each time we add it.\nThankfully, the function <code>re.sub<\/code> has a trick up its sleeve!<\/p>\n<p>The second parameter of the function <code>re.sub<\/code>, which is the replacement, can be a <em>function<\/em>.\nThis function must have a single parameter, which is the match object, and must return a string, which is the replacement for the given match.\nThus, by using a counter variable, I can add these increasing IDs:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; counter...<\/code><\/pre>","summary":"Learn how to find text patterns and replace them with dynamic content using regex.","date_modified":"2025-11-22T09:33:35+01:00","tags":["programming","python","regex","slice of life"],"image":"\/user\/pages\/02.blog\/dynamic-string-replacements-with-regex\/thumbnail.webp"},{"title":"Secret Santa draw with Python","date_published":"2023-11-22T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/secret-santa-draw-with-python","url":"https:\/\/mathspp.com\/blog\/secret-santa-draw-with-python","content_html":"<p>This article shows how I made a secret Santa draw with Python.<\/p>\n\n<p>Today I needed to make a Secret Santa draw for my whole family.\nThere are lots of services online that do this for you, but they require giving those services the emails of my family members who will then be mercilessly spammed by said services...<\/p>\n<p>So I decided to make my own draw with a bit of Python programming.\nAll I needed was:<\/p>\n<ul><li>to be able to input the names of my family members; and<\/li>\n<li>to be able to set exclusion groups so that family members don't draw the names of others in their nuclear family (for example, I don't want to be able to draw the name of my own brother).<\/li>\n<\/ul><p>This article is my account of how I did it, bit by bit, on the REPL.\nI'll try to describe briefly what each objective was at each point in time.\nYou'll see that the code isn't particularly well structured but it shows an accurate depiction of real code I wrote to solve a real-life need I had.<\/p>\n<h2 id=\"specifying-the-names\">Specifying the names<a href=\"#specifying-the-names\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The first thing I did was specify the names of everyone that was participating in the draw.\nWithout giving it too much thought, I figured this looked like a reasonable way of doing it:<\/p>\n<pre><code class=\"language-py\">names = [\n    [\"Alfred\", \"Ana\", \"Arthur\"],\n    [\"Ben\", \"Beatrice\", \"Bianca\"],\n    [\"Charles\", \"Catherine\"],\n    [\"David\"],\n    [\"Edward\", \"Elaine\", \"Eve\"],\n]<\/code><\/pre>\n<p>The idea is that each sublist represents a nuclear family, so we wouldn't want Alfred to draw Ana's or Arthur's name, but he could draw any other name starting with B, C, D, or E.<\/p>\n<h2 id=\"a-flat-list-with-all-participants\">A flat list with all participants<a href=\"#a-flat-list-with-all-participants\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The next step, I figured, was to get a flat list with <em>all<\/em> of the participants, so that I could easily shuffle them around.<\/p>\n<p>Flattening a list of lists is easy if you know what to do:<\/p>\n<pre><code class=\"language-py\">from itertools import chain\nflat_names = list(chain.from_iterable(names))\nprint(flat_names)  # ['Alfred', 'Ana', 'Arthur', 'Ben', ...]<\/code><\/pre>\n<h2 id=\"creating-a-more-manageable-data-structure-for-exclusions\">Creating a more manageable data structure for exclusions<a href=\"#creating-a-more-manageable-data-structure-for-exclusions\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>As I was getting ready to shuffle things around, I figured I'd need a quick and easy way to take a pair of names and figure out if they were in the same exclusion group or not.<\/p>\n<p>To do this, I created a dictionary that maps each single name into its exclusion group.\nI used a dictionary comprehension, which is aking to a <a href=\"\/blog\/pydonts\/list-comprehensions-101\">list comprehension<\/a>, but for dictionaries.<\/p>\n<p>This dictionary adds some redundancy, but it's easier to work with:<\/p>\n<pre><code class=\"language-py\">exclusions = {name: group for group in names for name in group}\nprint(exclusions)\n\"\"\"\n{\n    'Alfred': ['Alfred', 'Ana', 'Arthur'],\n    'Ana': ['Alfred', 'Ana', 'Arthur'],\n    'Arthur': ['Alfred', 'Ana', 'Arthur'],\n    'Ben': ['Ben', 'Beatrice', 'Bianca'],\n    # ...\n}\n\"\"\"<\/code><\/pre>\n<h2 id=\"drawing-names\">Drawing names<a href=\"#drawing-names\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The next step was generating the actual draw.\nI didn't feel like thinking too much about it, so I thought I'd go with brute-force, randomising the draw and then repeating the draw until all exclusion requirements were met by coincidence.<\/p>\n<p>To achieve this, I wrote this <code>for<\/code> loop:<\/p>\n<pre><code class=\"language-py\">import...<\/code><\/pre>","summary":"This article shows how I made a secret Santa draw with Python.","date_modified":"2025-07-23T16:49:02+02:00","tags":["programming","python","scripting","slice of life"],"image":"\/user\/pages\/02.blog\/secret-santa-draw-with-python\/thumbnail.webp"},{"title":"Adding stats to my blog","date_published":"2023-10-03T07:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/adding-stats-to-my-blog","url":"https:\/\/mathspp.com\/blog\/adding-stats-to-my-blog","content_html":"<p>This explains how I added stats to my blog page.<\/p>\n\n<figure class=\"image-caption\"><img title=\"Screenshot of the blog page.\" alt=\"Screenshot of my blog page where it shows three stats &ndash; total number of articles, lines of code written, and words.\" src=\"\/images\/9\/b\/5\/a\/6\/9b5a64afaf29472c9e64bd227aebf8f3b97498ea-stats.png\"><figcaption class=\"\">Screenshot of the blog page.<\/figcaption><\/figure><p>I recently added some basic statistics to my blog, as the image above shows.\nThese statistics include the total number of articles published, the number of lines of code written, and the number of words written.<\/p>\n<p>I wrote a Python script to do this for me, naturally, and I wanted to go over it with you.<\/p>\n<h2 id=\"find-blog-articles\">Find blog articles<a href=\"#find-blog-articles\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The first thing I did was write a function that finds all blog posts.\nThis function expects a folder location and it will find all Markdown files that look like blog posts.\nIn my blog, articles are files called <code>item.md<\/code> or <code>item.en.md<\/code> inside their respective folder, which may be nested arbitrarily, inside a folder where all blog articles reside.<\/p>\n<p>Recursively finding files whose name matches a simple pattern can be done by the <a href=\"\/blog\/module-pathlib-overview\">module <code>pathlib<\/code><\/a> and <a href=\"\/blog\/module-pathlib-overview#searching-for-files\">the method <code>rglob<\/code><\/a> that path objects have.\nThe final function looks like this:<\/p>\n<pre><code class=\"language-py\">from pathlib import Path\n\ndef find_files(folder):\n    yield from Path(folder).rglob(\"item*.md\")<\/code><\/pre>\n<p>Thanks to <code>pathlib<\/code>, this is short enough!\nI then opened a REPL inside <a href=\"https:\/\/github.com\/mathspp\/mathspp\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">my blog directory structure<\/a> and tested this function:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; from stats import find_files\n&gt;&gt;&gt; next(find_files(\".\"))\nPosixPath('smoosh-all-the-things\/item.md')<\/code><\/pre>\n<p>The function <code>find_files<\/code> can also be used to count how many articles I have:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; len(list(find_files(\".\")))\n293<\/code><\/pre>\n<h2 id=\"remove-optional-frontmatter\">Remove optional frontmatter<a href=\"#remove-optional-frontmatter\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The next thing I want to do is to get rid of metadata that blog articles might have.\nSome of my articles will have an initial section that is delimited by three dashes <code>---<\/code>, and that section contains metadata about the article (for example, the title or the date it was published on) and I don't want to take that into account when computing stats for each article.<\/p>\n<p>For example, an article might look like this:<\/p>\n<pre><code>---\ndate: 01-01-2023\ntitle: Happy New Year\n---\n\nHey there, in this article I wish you a happy new year!<\/code><\/pre>\n<p>So, my next step was to create a function that takes in the path to a Markdown article and returns its useful content, possibly stripping it from its frontmatter.\nThis is the function I wrote:<\/p>\n<pre><code class=\"language-py\"># ...\n\ndef get_file_contents(filepath):\n    contents = Path(filepath).read_text()\n    if contents.startswith(\"---\"):\n        contents = contents.removeprefix(\"---\")\n        contents = contents[3 + contents.find(\"---\") :]\n    return contents<\/code><\/pre>\n<p>This function will slice out the frontmatter, in case its present, and it will return the useful contents of the article.<\/p>\n<h2 id=\"split-code-from-prose\">Split code from prose<a href=\"#split-code-from-prose\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The next processing step is taking in the article and splitting the prose from the code snippets.\nCode snippets are surrounded by triple backticks ```, so I can use a regular expression to look for those blocks.<\/p>\n<p>The module <code>re<\/code> contains a function <code>sub<\/code> that lets me substitute text.\nSo, what I did was to find all the blocks that are surrounded by triple backticks and substitute them with the empty string <code>\"\"<\/code>.\nHowever, I also want to <em>extract<\/em> the blocks, not just remove them from the prose.<\/p>\n<p>In order to <em>extract<\/em> the blocks, I used the fact that the...<\/p>","summary":"This explains how I added stats to my blog page.","date_modified":"2025-07-23T16:49:02+02:00","tags":["programming","python","slice of life"],"image":"\/user\/pages\/02.blog\/adding-stats-to-my-blog\/_stats.png"},{"title":"Converting RAW images into JPEG","date_published":"2023-03-04T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/converting-raw-images-into-jpeg","url":"https:\/\/mathspp.com\/blog\/converting-raw-images-into-jpeg","content_html":"<p>Learn how I used Python in my daily life to convert 5,000 RAW images into the JPEG format.<\/p>\n\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>I have a folder with 4,902 RAW images and I want to convert them into the format JPEG to make it easier to share them with friends and family.<\/p>\n<p>I wasn't sure how I would convert almost 5,000 RAW images into another format but Python had my back.\nA quick Google search brought up <a href=\"https:\/\/stackoverflow.com\/q\/59054975\/2828287\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">a similar Stack Overflow question<\/a> from someone trying to do something similar.<\/p>\n<p>I installed the modules <code>rawpy<\/code> and <code>imageio<\/code> with <code>python -m pip install rawpy imageio<\/code> and it took me a couple of lines of code to convert my first image.<\/p>\n<p>I added a loop to go over all the images, I added a <a href=\"https:\/\/rich.readthedocs.io\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">Rich<\/a> progress bar to see my code working, and I was done.<\/p>\n<p>Here is a screenshot of the code showing the progress bar, so I know it is still doing work:<\/p>\n<figure class=\"image-caption\"><img title=\"The progress bar during the execution of the script.\" alt=\"Screenshot of a Python script that used a rich progress bar while converting RAW images into JPEGs.\" src=\"\/user\/pages\/02.blog\/converting-raw-images-into-jpeg\/_working.webp\"><figcaption class=\"\">The progress bar during the execution of the script.<\/figcaption><\/figure>\n<p>The full code for this script can be found below.\nNow, I wonder...\nHow much time would I have spent if I did not know Python?!<\/p>\n<pre><code class=\"language-py\">\"\"\"\nConvert NEF images into JPG.\n\nCredit to https:\/\/stackoverflow.com\/q\/59054975\/2828287 and https:\/\/stackoverflow.com\/a\/66048939\/2828287.\n\"\"\"\n\nimport pathlib\n\nimport imageio\nimport rawpy\nfrom rich.progress import track\n\nFROM = pathlib.Path(...)  # Folder to read from.\nTO = pathlib.Path(...)  # Folder to save images into.\n\nimages = list(FROM.glob(\"*\"))\n\nfor img in track(images):\n    with rawpy.imread(str(img)) as raw:\n        rgb = raw.postprocess(rawpy.Params(use_camera_wb=True))\n    new_location = (TO \/ img.name).with_suffix(\".jpg\")\n    imageio.imsave(new_location, rgb)<\/code><\/pre>","summary":"Learn how I used Python in my daily life to convert 5,000 RAW images into the JPEG format.","date_modified":"2025-07-23T16:49:02+02:00","tags":["programming","python","scripting","slice of life"],"image":"\/user\/pages\/02.blog\/converting-raw-images-into-jpeg\/thumbnail.webp"},{"title":"Finding similar photos","date_published":"2023-02-01T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/finding-similar-photos","url":"https:\/\/mathspp.com\/blog\/finding-similar-photos","content_html":"<p>Go through the complete code for a Python project that consists in finding similar photographs in a folder. This tutorial will make use of classical techniques and is suitable for beginners.<\/p>\n\n<p><img alt=\"\" src=\"\/images\/a\/2\/1\/1\/9\/a21191c1e958cd25ee5aea62e3e43592c1bb5842-thumbnail.webp\"><\/p>\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The photo gallery on my phone is consistently packed with photos that all look (pretty much) the same.\nGoing through batches of photos and looking for near-duplicates is boring, so I thought I would try to use some maths and some Python to help me find images that are very close to one another.<\/p>\n<p>For example, I would like my program to determine that the first two photos in the collage below are pretty much the same, while also saying that the third one is different from the other two.<\/p>\n<figure class=\"image-caption\"><img title=\"Three photos of the same seagull.\" alt=\"A collage of three photos of the same seagull. This collag was used in this Python project to see if we can find similar images (or near duplicates) with Python.\" src=\"\/user\/pages\/02.blog\/finding-similar-photos\/_seagulls.webp\"><figcaption class=\"\">Three photos of the same seagull.<\/figcaption><\/figure><p>This short tutorial will go through the (surprisingly little) code I wrote as an experiment.<\/p>\n<h2 id=\"tooling\">Tooling<a href=\"#tooling\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>This short project makes use of the <a href=\"https:\/\/pillow.readthedocs.io\/en\/stable\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">pillow<\/a> imaging library for Python and <strong>does not<\/strong> use any machine learning.\nTo determine our near-duplicate photos, we will be using rudimentary image processing techniques.<\/p>\n<h2 id=\"github-repository\">GitHub repository<a href=\"#github-repository\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<h3 id=\"code\">Code<a href=\"#code\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>The code for this project can be found in the <a href=\"https:\/\/github.com\/mathspp\/Finding-similar-photos\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">&ldquo;Finding similar images&rdquo; GitHub repo<\/a>.\nFeel free to star the repository and fork it to play around with the code.<\/p>\n<p>To get started using the code,<\/p>\n<ol><li>clone the repository;<\/li>\n<li>run <code>poetry install<\/code> to install the <a href=\"https:\/\/pillow.readthedocs.io\/en\/stable\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">pillow<\/a> dependency (or install it by hand, if you must); and<\/li>\n<li>run <code>python src\/similarity.py<\/code>.<\/li>\n<\/ol><h3 id=\"images\">Images<a href=\"#images\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>The repository also includes two folders <code>seagulls<\/code> and <code>dublin<\/code> with some <em>real<\/em> photographs I took,\nrespectively of a seagull and from Dublin.\nWe can use these photographs to test our code.<\/p>\n<h2 id=\"algorithm-to-determine-similarity\">Algorithm to determine similarity<a href=\"#algorithm-to-determine-similarity\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>In order to determine if two images are near duplicates of each other, we need to come up with a simple algorithm to do so.\nIn this project, we will follow this approach:<\/p>\n<ol><li>resize the two images to 16 by 16;<\/li>\n<li>determine the average pixel difference of the two 16 by 16 images; and<\/li>\n<li>check if that average difference is below a threshold value.<\/li>\n<\/ol><p>This is just a high-level description of what we will be doing together.\nAs we go through the tutorial, I will give you more details regarding what we need to do.<\/p>\n<p>Ready?\nLet's get started.<\/p>\n<h2 id=\"setting-up-everything\">Setting up everything<a href=\"#setting-up-everything\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>To go through this tutorial, you can <a href=\"https:\/\/github.com\/mathspp\/Finding-similar-photos\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">clone the repository<\/a> and use poetry to install the dependencies with <code>poetry install<\/code>.\nIf you can't be bothered with that, you'll need at least the folders <code>seagulls<\/code> and <code>dublin<\/code> from that repository.\nThen, go ahead and create your file <code>src\/similarity.py<\/code>:<\/p>\n<pre><code class=\"language-py\">## src\/similarity.py\nfrom PIL import Image\n\nseagull = Image.open(\"seagulls\/seagull_1.jpeg\")\nseagull.show()<\/code><\/pre>\n<p>If you run this code, it should open the image for you.\nIf it didn't, make sure you have pillow installed (you can follow the instructions in the <a href=\"https:\/\/pillow.readthedocs.io\/en\/stable\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">pillow docs<\/a>) and make sure the path to the <code>seagulls<\/code> directory is correct.<\/p>\n<div class=\"notices blue\">\n<p>If you cloned the repository or if you have a folder structure like mine:<\/p>\n<pre><code>project_folder\n| - seagulls\n  | - seagull_1.jpeg...<\/code><\/pre><\/div>","summary":"Go through the complete code for a Python project that consists in finding similar photographs in a folder. This tutorial will make use of classical techniques and is suitable for beginners.","date_modified":"2025-07-23T16:49:02+02:00","tags":["image processing","mathematics","programming","python","slice of life"],"image":"\/user\/pages\/02.blog\/finding-similar-photos\/thumbnail.webp"},{"title":"What all promotions actually mean","date_published":"2021-01-30T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/what-all-promotions-actually-mean","url":"https:\/\/mathspp.com\/blog\/what-all-promotions-actually-mean","content_html":"<p>Nowadays stores come up with all sorts of funky promotions to catch your eye...\nBut how much money do you actually save with each type of promotion?<\/p>\n\n<p><img alt='A picture of a big \"SALE\" sign, photo by Justin Lim on Unsplash.' src=\"\/user\/pages\/02.blog\/what-all-promotions-actually-mean\/thumbnail.png\"><\/p>\n<p>I will do the maths needed to understand how much money each type of promotion actually\nsaves you, and I will try to cover all the types of promotions I can think of.\nIf you have seen a type of promotion that is not covered here, then go ahead and ask\nme to include it by commenting down below.<\/p>\n<div class=\"notices yellow\">\n<p>For the sake of simplicity, I'll use the euro (&euro;) as the currency during this post,\nbut if you use some other currency, you can mentally replace all &euro; symbols with\nthe symbol for your currency and the statements will still be true, there is <strong>no<\/strong>\nneed for conversions.<\/p>\n<\/div>\n<p>I will start with a brief note on the classical promotion that says &ldquo;X% off&rdquo;.\nEveryone is used to seeing those and those are more or less well understood.\nThen, I'll provide a short description of a method to convert any other promotion\nto this &ldquo;X% off&rdquo; type of promotion.\nFinally, I will go over many different types of promotions, listed below, and I will\nconvert them to the &ldquo;X% off&rdquo; promotion for some specific values.<\/p>\n<ul><li>&ldquo;X for the price of Y&rdquo;<\/li>\n<li>&ldquo;Buy X, we offer the cheapest Y&rdquo;<\/li>\n<li>&ldquo;X% off in the second item&rdquo;<\/li>\n<li>&ldquo;Plus X% for free\" \/ &ldquo;Plus X for free&rdquo;<\/li>\n<li>&ldquo;Get X% cash back in a card\/for later\/when you come back&rdquo;<\/li>\n<li>&ldquo;Get X&euro; for a purchase above Y&euro;&rdquo;<\/li>\n<li>Loyalty cards \/ stamp cards \/ point systems<\/li>\n<\/ul><h2 id=\"x-off\">&ldquo;X% off&rdquo;<a href=\"#x-off\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The first type of promotion that I will cover is the one that is easier to grasp, in my opinion.\nIt is the &ldquo;X% off&rdquo; type of promotions that we are all accustomed to:<\/p>\n<p><img alt='A picture of a \"50% off\" tag, cropped from an original by Artem Beliaikin on Unsplash.' src=\"\/user\/pages\/02.blog\/what-all-promotions-actually-mean\/_fifty_percent_off.png\"><\/p>\n<p>When a tag\/sign reads &ldquo;X% off&rdquo;, it means that for every <span class=\"mathjax mathjax--inline\">\\(100\\)<\/span>&euro; you are going to spend, you save X&euro;.<\/p>\n<p>For example:<\/p>\n<ul><li>A &ldquo;50% off&rdquo; tag means that, for every <span class=\"mathjax mathjax--inline\">\\(100\\)<\/span>&euro; you were about to spend, you get to save <span class=\"mathjax mathjax--inline\">\\(50\\)<\/span>.<\/li>\n<li>A &ldquo;70% off&rdquo; tag means that, for every <span class=\"mathjax mathjax--inline\">\\(100\\)<\/span>&euro; you were about to spend, you get to save <span class=\"mathjax mathjax--inline\">\\(70\\)<\/span>.<\/li>\n<\/ul><h2 id=\"how-to-calculate-how-much-i-save-with-a-promotion\">How To Calculate How Much I Save With A Promotion<a href=\"#how-to-calculate-how-much-i-save-with-a-promotion\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>There is a simple train of thought that allows you to understand how much money each\npromotion actually saves you, and that is the train of thought I will be using throughout\nthe remainder of this blog post.<\/p>\n<p>To see how much a promotion actually saves, you need to do a couple of simple steps:<\/p>\n<ol><li>\n<p>First, calculate what is the amount of money you need to spend in order\nto trigger the promotion, which means accounting for the things you have to pay and the\nthings you will get for free.<\/p>\n<\/li>\n<li>\n<p>Calculate what is the amount of money you can actually save.<\/p>\n<\/li>\n<li>\n<p>Lastly, divide the amount of step 2 by the amount of step 1, multiply by <span class=\"mathjax mathjax--inline\">\\(100\\)<\/span> and that\ngives the percentage that...<\/p><\/li><\/ol>","summary":"In this blog post I go over all types of promotions and discounts that shops usually have and compute how much money each one actually saves you.","date_modified":"2025-07-23T16:49:02+02:00","tags":["mathematics","slice of life"],"image":"\/user\/pages\/02.blog\/what-all-promotions-actually-mean\/thumbnail.png"}]}
