{"description": "I just said what I said and it was wrong. Or was taken wrong.", "feed_url": "https://leancrew.com/all-this/feed.json", "title": "And now it’s all this", "items": [{"title": "Scientific American and Friday the 13th", "url": "https://leancrew.com/all-this/2026/03/scientific-american-and-friday-the-13th/", "author": {"name": "Dr. Drang"}, "summary": "SciAm horns in on my territory.", "date_published": "2026-03-13T23:48:46+00:00", "id": "https://leancrew.com/all-this/2026/03/scientific-american-and-friday-the-13th/", "content_html": "<p><em>Scientific American</em> has <a href=\"https://www.scientificamerican.com/article/why-friday-the-13th-is-a-mathematical-inevitability/\">a fun little article</a> today about the frequency of Friday the 13ths. It ends with this table,</p>\n<p><img alt=\"SciAm 13th table\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260313-SciAm%2013th%20table.png\" title=\"SciAm 13th table\" width=\"100%\"/></p>\n<p>and this true but overstated conclusion:</p>\n<blockquote>\n<p>In other words, the 13th of a month will be a Friday more times than any other day of the week.</p>\n</blockquote>\n<p>Well, yes, if you live to be 400 years old, you’ll see one more Friday the 13th than Wednesday the 13ths or Sunday the 13ths. Kind of a weird thing to focus on, though. I’m guessing you’ll have other worries by then.</p>\n<p>But I shouldn’t be so snarky. A few years ago, <a href=\"https://leancrew.com/all-this/2023/10/friday-the-13th-comes-on-a-friday-this-month/\">I wrote a post</a> that calculated the same set of Fri13 counts for a 400-year Gregorian cycle. I did the calculations in Mathematica and (of course) showed the code. Today, I did the same thing in Python,</p>\n<pre><code>python:\n 1:  #!/usr/bin/env python\n 2:  \n 3:  from datetime import date\n 4:  \n 5:  f13s = [0]*7\n 6:  for y in range(1800, 2200):\n 7:    for m in range(1, 13):\n 8:      wd = date(y, m, 13).weekday()\n 9:      f13s[wd] += 1\n10:  \n11:  print(f13s)\n</code></pre>\n<p>and got a result of</p>\n<pre><code>[685, 685, 687, 684, 688, 684, 687]\n</code></pre>\n<p>for Monday through Sunday. This also matches the SciAm table.</p>\n<p>Those of us who are alive now (and have realistic longevities) won’t live through any non-leap century years. For us, the calendar has and will repeat every 28 years (1461 weeks), and over every 28-year period in our lives, there will be 48 Fri13s, the same as the number of Mon13s, Tue13s, Wed13s, and so on.</p>\n<p>Of course, few of us live exactly a multiple of 28 years. Personally, I’ve lived through 113 Fri13s so far, which is just under the number of Sun13s I’ve seen (114). So I’ve been lucky?</p>\n<p>In a Friday the 13th post from <a href=\"https://leancrew.com/all-this/2012/07/friday-the-13th-frequency/\">way back in 2012</a>, I talked about how Fri13s repeat within years because the number of days in certain month sequences is a multiple of 7. So if there’s a Fri13 in April, there will be another in July because</p>\n<pre><code>Apr + May + Jun\n30  + 31  + 30  = 91\n</code></pre>\n<p>which is 13 weeks. The last time that happened was in 2018.</p>\n<p>Similarly, if there’s a Fri13 in September, there will also be one in December because</p>\n<pre><code>Sep + Oct + Nov\n30  + 31  + 30  = 91\n</code></pre>\n<p>That pair of Fri13s last happened in 2024.</p>\n<p>There’s also an 8-month sequence that adds to a multiple of 7:</p>\n<pre><code>Mar + Apr + May + Jun + Jul + Aug + Sep + Oct\n31  + 30  + 31  + 30  + 31  + 31  + 30  + 31  = 245\n</code></pre>\n<p>So there will be another Fri13 in November of this year.</p>\n<p>The sequences above happen every year. In non-leap years only—this year, for example—a Fri13 in February will be followed by one in March. In leap years only, a Fri13 in January will be followed by one in April. That last happened in 2012.</p>\n<p>I covered all these repeated Fri13s in <a href=\"https://leancrew.com/all-this/2012/07/friday-the-13th-frequency/\">that 2012 post</a>. Today, I learned of a new repeat that spans certain year boundaries. If there’s a Fri13 in December of a non-leap year that’s followed by a leap year, there will be a Fri13 in March of that following year. That last happened in December of 2019 and March of 2020.</p>\n<p>Superstitious or not, you have to admit March of 2020 was pretty unlucky.</p>"}, {"title": "Port ability", "url": "https://leancrew.com/all-this/2026/03/port-ability/", "author": {"name": "Dr. Drang"}, "summary": "A brief thought about the MacBook Neo.", "date_published": "2026-03-11T00:53:12+00:00", "id": "https://leancrew.com/all-this/2026/03/port-ability/", "content_html": "<p>I read <a href=\"https://sixcolors.com/post/2026/03/macbook-neo-review/\">Jason Snell’s review</a> of the MacBook Neo this morning and was struck by this section on its two USB-C ports:</p>\n<blockquote>\n<p>So Apple has done the work to put two USB-C ports on the Neo—and those ports reveal a bit more of the struggles Apple had in building this computer. Both of the USB-C ports will let you charge (which is good, because there’s no MagSafe), but only the one that’s furthest back is a fully functional USB 3 port with support for driving an external display at 4K, 60 frames per second. The closer-in USB port only offers USB 2 speeds. (The good news is that Apple has built alerts into macOS that will warn you if the device you’ve plugged into the slow port would be better off plugged into the faster one, so you won’t be transferring files slowly unnecessarily.)</p>\n</blockquote>\n<p>It reminded me of <a href=\"https://mastodon.social/@film_girl/116172541746813426?kjy=spring\">Christina Warren’s angry Mastodon post</a> about the Neo’s ports:</p>\n<blockquote>\n<p>I’m just going to say that pointing out flaws in $600 and $700 laptops and having people instinctively respond “it’s not for real users, these buyers don’t know/care” is gross. Why should we make the assumption that those buyers don’t still deserve better, or at least on par with the 6 year old product it’s replacing that sold at the same price. Stop assuming people who don’t spend $1000+ on tech are imbeciles or deserve subpar options. No one in 2026 deserves a laptop with a single USB 3 port.</p>\n</blockquote>\n<p>My first thought upon reading Christina’s complaint last week was “Why should I care about the configuration of a computer I’ll never buy?” Kind of a Republican thought, I admit, and I’m not proud of it. But I have many reasons to be disappointed by Apple, and I need to conserve my outrage. Christina’s much younger than I am and has deeper outrage reserves.</p>\n<p>Jason pointed out the biggest problem with the low-speed port in his next paragraph:</p>\n<blockquote>\n<p>Honestly, I’m more disappointed by the fact that mismatched ports can lead to user frustration—no, not that port, the other one—than I am about the one slow USB port.</p>\n</blockquote>\n<p>Many Neo users will be too young to remember the joy of flipping USB-A plugs back and forth before getting the orientation right. Apple has updated that wonderful experience for the USB-C age.</p>"}, {"title": "Lent and Lisp", "url": "https://leancrew.com/all-this/2026/02/lent-and-lisp/", "author": {"name": "Dr. Drang"}, "summary": "More stuff about calendars, some of which I should have written about last week.", "date_published": "2026-02-25T15:13:32+00:00", "id": "https://leancrew.com/all-this/2026/02/lent-and-lisp/", "content_html": "<p>After writing <a href=\"https://leancrew.com/all-this/2026/02/chinese-new-year-and-ramadan/\">last week’s post</a> about the start of Ramadan and Chinese New Year, I expected to hear from people asking why I didn’t include the further coincidence of Ash Wednesday. I was surprised that the only such feedback I got was an email from <a href=\"https://rhymeswithdiploma.com/\">TJ Luoma</a>. It makes sense that Lent would be on TJ’s mind—it’s a big part of his business calendar—but I had an answer prepared, and I wrote him back with my reasons.</p>\n<p>As I typed out the reply, though, the reasons seemed weaker. Yes, it’s true that the full moon that determines this year’s <a href=\"https://aa.usno.navy.mil/faq/easter\">date of Easter</a> (and therefore Ash Wednesday) isn’t part of the same lunation that determines the start of Ramadan and Chinese New Year, so there was an astronomical reason to keep Ash Wednesday out of the post. But it’s also true that both Ramadan and Lent represent periods of self-denial, so there’s a cultural connection.</p>\n<p>Adding a new bit of Emacs Lisp code to what I’d already written to include a check for Ash Wednesday wouldn’t be hard, but another thought was buzzing in my head: switching from Emacs Lisp to Common Lisp. The ELisp calendar functions were written by Edward Reingold and Nachum Dershowitz, authors of the well-known <a href=\"https://lccn.loc.gov/2017024295\"><em>Calendrical Calculations</em></a>. That book includes <a href=\"https://www.cambridge.org/us/universitypress/subjects/computer-science/computing-general-interest/calendrical-calculations-ultimate-edition-4th-edition?format=HB#resources\">a lot of code</a> that isn’t in the Emacs implementation, code that does astronomical calculations I’d like to explore. So it seemed like a good idea to write a Ramadan/Lent/Chinese New Year script in Common Lisp and use the functions from the book.</p>\n<p>The problem with that idea was that the Common Lisp code I downloaded from the <a href=\"https://www.cambridge.org/us/universitypress/subjects/computer-science/computing-general-interest/calendrical-calculations-ultimate-edition-4th-edition?format=HB#resources\">Cambridge University Press site</a>, <code>calendar.l</code>, didn’t work. I tried it in both <a href=\"https://www.sbcl.org/\">SBCL</a> and <a href=\"https://clisp.sourceforge.io/\">CLISP</a>, and calling</p>\n<pre><code>(load \"calendrical.l\")\n</code></pre>\n<p>threw a huge number of errors. I was, it turned out, not the first to have <a href=\"https://stackoverflow.com/questions/72716454/start-working-with-large-lisp-library-calendrical-calculations\">run into this problem</a>. The workarounds suggested there on Stack Overflow didn’t help. There’s <a href=\"https://elangocheran.com/2023/12/06/calendars-and-usable-calendrical-calculations-code/\">a port to Clojure</a> that apparently works, but I was reluctant to use <a href=\"https://clojure.org/\">Clojure</a> and have to maintain both it and a Java Virtual Machine.</p>\n<p>What I found, though, was that Reingold &amp; Dershowitz’s code would load in CLISP with one simple change. After many lines of comments, the working part of <code>calendar.l</code> starts with these lines:</p>\n<pre><code>(in-package \"CC4\")\n\n(export '(\n          acre\n          advent\n          akan-day-name\n          akan-day-name-on-or-before\n\n          [and so on for a few hundred lines]\n\n          yom-ha-zikkaron\n          yom-kippur\n          zone\n          ))\n</code></pre>\n<p>Deleting these lines got me a file that would load without errors in CLISP,<sup id=\"fnref:sbcl\"><a href=\"#fn:sbcl\" rel=\"footnote\">1</a></sup> so I named the edited version <code>calendar.lisp</code> and saved it in my <code>~/lisp/</code> directory. I believe the problem with the unedited code has something to do with packages and namespaces, and if I keep using Common Lisp long enough, I may learn how to make a better fix. Until then, this will do.</p>\n<p>With a working library of calendar code, I wrote the following script, <code>ramadan-lent</code>, to get the dates for which Ramadan 1 and Ash Wednesday correspond over a 500-year period:</p>\n<pre><code> 1:  #! /usr/bin/env clisp -q\n 2:  \n 3:  ;; The edited Calendrical Calculations code by Reingold and Dershowitz\n 4:  (load \"calendar.lisp\")\n 5:  \n 6:  ;; Names\n 7:  (setq\n 8:    weekday-names\n 9:    '(\"Sunday\" \"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\")\n10:  \n11:    gregorian-month-names\n12:    '(\"January\" \"February\" \"March\" \"April\" \"May\" \"June\"\n13:      \"July\" \"August\" \"September\" \"October\" \"November\" \"December\"))\n14:  \n15:  ;; Date string function\n16:  (defun gregorian-date-string (date)\n17:    (let ((g-date (gregorian-from-fixed date))\n18:          (weekday (day-of-week-from-fixed date)))\n19:      (format nil \"~a, ~a ~d, ~d\"\n20:        (nth weekday weekday-names)\n21:        (nth (1- (second g-date)) gregorian-month-names)\n22:        (third g-date)\n23:        (first g-date))))\n24:  \n25:  ;; Get today's (Gregorian) date.\n26:  (multiple-value-setq\n27:    (t-second t-minute t-hour t-day t-month t-year t-weekday t-dstp t-tz)\n28:    (get-decoded-time))\n29:    \n30:  ;; Loop through 500 Islamic years, from 250 years ago to 250 years in\n31:  ;; the future and find each Ramadan 1 that corresponds to Ash Wednesday.\n32:  ;; Print as a Gregorian date.\n33:  (setq\n34:    f (fixed-from-gregorian (list t-year t-month t-day))\n35:    ti-year (first (islamic-from-fixed f)))\n36:  (dotimes (i 500)\n37:    (setq iy (+ (- ti-year 250) i)\n38:          r (fixed-from-islamic (list iy 9 1))\n39:          g-year (gregorian-year-from-fixed r)\n40:          aw (- (easter g-year) 46))\n41:    (if (equal aw r)\n42:      (format t \"~a~%\" (gregorian-date-string r))))\n</code></pre>\n<p>The <code>-q</code> in the shebang line tells CLISP not to put up its typical welcome banner. I had to write my own <code>gregorian-date-string</code> function (Lines 16–23) because <code>calendrical.lisp</code> doesn’t have one, but it was pretty easy.</p>\n<p>In fact, it was all pretty easy. I haven’t programmed in Lisp or Scheme in quite a while, but I quickly remembered how fun it is. The only tricky bits were:</p>\n<ul>\n<li>learning how to handle the multiple value output of <code>get-decoded-time</code> (Lines 26–28);</li>\n<li>remembering how to handle more than one variable assignment in <code>setq</code>; and</li>\n<li>recognizing that what the ELisp calendar library calls “absolute” dates, the Common Lisp calendar library calls “fixed” dates.</li>\n</ul>\n<p>R&amp;D’s library has an <code>easter</code> function for getting the date of Easter for a given (Gregorian) year; Line 40 gets the date of the associated Ash Wednesday by going back 46 days from Easter.</p>\n<p>The output of <code>ramadan-lent</code> was</p>\n<pre><code>Wednesday, February 6, 1799\nWednesday, February 24, 1830\nWednesday, February 22, 1928\nWednesday, February 18, 2026\nWednesday, March 7, 2057\nWednesday, February 16, 2124\nWednesday, March 5, 2155\nWednesday, February 13, 2222\nWednesday, March 2, 2253\n</code></pre>\n<p>The most common gap between successive correspondences was 98 years, but there were occasional gaps of 31 and 67 years.</p>\n<p>It took only a few extra lines at the end to include a check for Chinese New Year. Here’s <code>ramadan-lent-new-year</code>:</p>\n<pre><code> 1:  #! /usr/bin/env clisp -q\n 2:  \n 3:  ;; The edited Calendrical Calculations code by Reingold and Dershowitz\n 4:  (load \"calendar.lisp\")\n 5:  \n 6:  ;; Names\n 7:  (setq\n 8:    weekday-names\n 9:    '(\"Sunday\" \"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\")\n10:  \n11:    gregorian-month-names\n12:    '(\"January\" \"February\" \"March\" \"April\" \"May\" \"June\"\n13:      \"July\" \"August\" \"September\" \"October\" \"November\" \"December\"))\n14:  \n15:  ;; Date string function\n16:  (defun gregorian-date-string (date)\n17:    (let ((g-date (gregorian-from-fixed date))\n18:          (weekday (day-of-week-from-fixed date)))\n19:      (format nil \"~a, ~a ~d, ~d\"\n20:        (nth weekday weekday-names)\n21:        (nth (1- (second g-date)) gregorian-month-names)\n22:        (third g-date)\n23:        (first g-date))))\n24:  \n25:  ;; Get today's (Gregorian) date.\n26:  (multiple-value-setq\n27:    (t-second t-minute t-hour t-day t-month t-year t-weekday t-dstp t-tz)\n28:    (get-decoded-time))\n29:    \n30:  ;; Loop through 500 Islamic years, from 250 years ago to 250 years in\n31:  ;; the future and find each Ramadan 1 that corresponds to Ash Wednesday\n32:  ;; and Chinese New Year.\n33:  ;; Print as a Gregorian date.\n34:  (setq\n35:    f (fixed-from-gregorian (list t-year t-month t-day))\n36:    ti-year (first (islamic-from-fixed f)))\n37:  (dotimes (i 500)\n38:    (setq iy (+ (- ti-year 250) i)\n39:          r (fixed-from-islamic (list iy 9 1))\n40:          g-year (gregorian-year-from-fixed r)\n41:          aw (- (easter g-year) 46))\n42:    (if (equal aw r)\n43:      (let ((ny (chinese-new-year-on-or-before r)))\n44:        (if (equal ny (1- r))\n45:          (format t \"~a~%\" (gregorian-date-string r))))))\n</code></pre>\n<p>The <code>chinese-new-year-on-or-before</code> function (Line 43), which is in the library to aid in the writing of the typically more useful <code>chinese-from-fixed</code> function, turned out to be just what I needed here. It gets me the fixed date of Chinese New Year that’s on or before Ramadan 1. I then check to see if that’s exactly one day before Ramadan 1 in Line 44.</p>\n<p>This script’s output was</p>\n<pre><code>Wednesday, February 6, 1799\nWednesday, February 18, 2026\nWednesday, February 16, 2124\nWednesday, February 13, 2222\n</code></pre>\n<p>We see that last week’s triple correspondence hadn’t occurred in 227 years, and it’ll be another 98 years before the next one. Thanks to TJ for getting me to look into this rare event.</p>\n<p>I’ve had a copy of the second edition of <em>Calendrical Calculations</em> (called the <em>Millenium Edition</em> because it came out in 2001) for over twenty years. As I was fiddling with <code>ramadan-lent</code> and <code>ramadan-lent-new-year</code>, I ordered the fourth (or <em>Ultimate</em>) edition so I’d have the best reference on the functions in <code>calendar.lisp</code>. You can expect more posts on calendars and astronomical events as I dig into it.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:sbcl\">\n<p>Not in SBCL, unfortunately. As best as I can tell, SBCL wants every function to be defined in terms of previously defined functions, and R&amp;D didn’t write their code that way. CLISP is more forgiving in the order of definitions. <a href=\"#fnref:sbcl\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "My OmniGraffle ticks", "url": "https://leancrew.com/all-this/2026/02/my-omnigraffle-ticks/", "author": {"name": "Dr. Drang"}, "summary": "Dimension lines without arrows and structural stencils.", "date_published": "2026-02-20T17:40:43+00:00", "id": "https://leancrew.com/all-this/2026/02/my-omnigraffle-ticks/", "content_html": "<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href=\"https://leancrew.com/all-this/2026/02/my-omnigraffle-ticks/\">the original article</a> to see them rendered properly.]</p>\n<hr />\n<p>I thought some of you might be wondering about the dimensions on this drawing from <a href=\"https://leancrew.com/all-this/2026/02/easy-to-be-hard/\">yesterday’s post</a>:</p>\n<p><img alt=\"Radii relationship\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260219-Radii%20relationship.png\" title=\"Radii relationship\" width=\"50%\"/></p>\n<p>Don’t be offended—I trust you all to know why the horizontal components of the two diagonals are <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>0</mn></msub><mi>/</mi><msqrt><mn>2</mn></msqrt></mrow></math> and <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>1</mn></msub><mi>/</mi><msqrt><mn>2</mn></msqrt></mrow></math>. What I thought you might be questioning was my use of short diagonal ticks instead of arrowheads on the dimension lines along the bottom of the drawing.</p>\n<p>This is a style I picked up as an undergrad. I use it all the time in hand-drawn sketches, and I think I’m going to use it here from now on. The first person I saw using this kind of dimension line was <a href=\"https://modernize.cee.illinois.edu/spaces/article/47912\">John Haltiwanger</a>, who taught the second structural analysis class I took, and I adopted it in emulation of him. I mentioned Prof. Haltiwanger and his insistence on good sketches in <a href=\"https://leancrew.com/all-this/2025/11/a-sense-of-structure/\">this post</a> last year.</p>\n<p>There are two advantages to using ticks instead of arrowheads: speed and clarity. The speed advantage is obvious. Clarity comes in drawings of structural problems, where we use arrowheads to represent forces. Although it’s usually clear from context which lines are for forces and which are for dimensions, sometimes the two cross or are close enough to one another that it helps to use different ends. This is especially true when sketching on paper or a blackboard, where you can’t use line thickness to distinguish between the two.</p>\n<p>Let’s say I’m going to analyze a simply supported beam with a uniformly distributed load across its entire length and a concentrated load at its center. I’d sketch it this way in my notebook:</p>\n<p><img alt=\"Simply-supported beam sketch\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260220-Simply-supported%20beam%20sketch.jpg\" title=\"Simply-supported beam sketch\" width=\"80%\"/></p>\n<p>After drawing the beam, supports, and forces, I make the vertical leader lines, one long dimension line the full length of the beam, and then three quick diagonal ticks.</p>\n<p>If I’m going to blog about it, I’ll turn it into a nicer drawing in <a href=\"https://www.omnigroup.com/omnigraffle\">OmniGraffle</a>:</p>\n<p><img alt=\"Simply-supported beam OmniGraffle drawing\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260220-Simply-supported%20beam%20OmniGraffle%20drawing.png\" title=\"Simply-supported beam OmniGraffle drawing\" width=\"80%\"/></p>\n<p>Using blue for the leader and dimension lines—and making them thinner—is a good way to distinguish them from forces, and I’ve been doing that for years.<sup id=\"fnref:blue\"><a href=\"#fn:blue\" rel=\"footnote\">1</a></sup> I’ve usually used a different style of arrowhead for the dimensions than for the forces. OmniGraffle has a huge number of arrowhead styles:</p>\n<p><img alt=\"OmniGraffle arrows\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260220-OmniGraffle%20arrows.png\" title=\"OmniGraffle arrows\" width=\"50%\"/></p>\n<p>I’ve generally used the “Filled Arrow” for forces and the “Sharp Arrow” for dimensions. You might think I’d use the “Dimension Arrow,” but I’ve never liked it. It has a little leader line attached to it, which is, unfortunately, pretty much useless because it’s always the same length. Leader lines are supposed to draw your eye from the object to the dimension line—they have to be different lengths, and they’re usually much longer than the tiny ticks OmniGraffle provides.</p>\n<p>I could continue to use the sharp arrows, of course, but I’ve decided my drawings here should look more like the sketches I make in my notebook. And it’s a nice tribute to Prof. Haltiwanger. Although his was my second structural analysis class, it was the one in which I began to truly appreciate the topic.</p>\n<p>While I’m at it, I should mention that I made a set of stencils with objects commonly seen in structural analysis drawings.</p>\n<p><img alt=\"Structural stencils for OmniGraffle\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260220-Structural%20stencils%20for%20OmniGraffle.png\" title=\"Structural stencils for OmniGraffle\" width=\"50%\"/></p>\n<p>The top two rows show fixed, simple, and guided ends; the bottom two rows show springs. There are four variations on simple ends; the differences depend on whether there’s a roller and whether I want to show the hinge explicitly. There’s only one linear spring, but I needed four rotational springs to account for different angles of attachment. All of these can be rotated as needed once they’re placed in an OmniGraffle document.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:blue\">\n<p>As you can see, I have not been consistent in my use of blue or black for the dimensions themselves. <a href=\"#fnref:blue\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "Easy to be hard", "url": "https://leancrew.com/all-this/2026/02/easy-to-be-hard/", "author": {"name": "Dr. Drang"}, "summary": "After solving this SciAm puzzle the easy way, I redid it the hard way.", "date_published": "2026-02-20T00:25:13+00:00", "id": "https://leancrew.com/all-this/2026/02/easy-to-be-hard/", "content_html": "<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href=\"https://leancrew.com/all-this/2026/02/easy-to-be-hard/\">the original article</a> to see them rendered properly.]</p>\n<hr />\n<p>Here’s <a href=\"https://www.scientificamerican.com/game/math-puzzle-sum-circles/\">a geometry puzzle</a> from the March issue of <em>Scientific American</em>. If you see the trick, it’s easy to solve.<sup id=\"fnref:trick\"><a href=\"#fn:trick\" rel=\"footnote\">1</a></sup> After doing it the easy way, I decided to solve it again the hard way, as if I hadn’t noticed the trick.</p>\n<p>Here’s the puzzle:</p>\n<p><img alt=\"SciAm circle puzzle by Amanda Montañez\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260219-SciAm%20circle%20puzzle%20by%20Amanda%20Montanez.png\" title=\"SciAm circle puzzle by Amanda Montañez\" width=\"100%\"/></p>\n<blockquote>\n<p>A red circle is inscribed inside a blue square. The arrangement leaves gaps in the square’s four corners, two of which are filled with smaller circles that just barely touch the big red circle and the two corner sides of the blue square. This, in turn, leaves two smaller gaps in the corners, which are filled with smaller circles, and so on, with ever smaller circles ad infinitum. The entire diagram is inscribed inside of a 1 × 1 gray square. What is the total circumference of all the circles?</p>\n</blockquote>\n<p>Without the trick, we’re going to have to work out all the circumferences and add them together. We’ll start by figuring out the relationship between the radii of consecutive circles. Here’s a quarter of the largest circle, the radius of which we’ll call <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><msub><mi>r</mi><mn>0</mn></msub></math>, and the next largest circle, the radius of which we’ll call <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><msub><mi>r</mi><mn>1</mn></msub></math>:</p>\n<p><img alt=\"Radii relationship\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260219-Radii%20relationship.png\" title=\"Radii relationship\" width=\"50%\"/></p>\n<p>From this drawing, we can express the width in two ways and set them equal to one another:</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>0</mn></msub><mo>=</mo><msub><mi>r</mi><mn>1</mn></msub><mo>+</mo><mfrac><msub><mi>r</mi><mn>1</mn></msub><msqrt><mn>2</mn></msqrt></mfrac><mo>+</mo><mfrac><msub><mi>r</mi><mn>0</mn></msub><msqrt><mn>2</mn></msqrt></mfrac></mrow></math>\n<p>Multiplying through by <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><msqrt><mn>2</mn></msqrt></math> and rearranging, we get</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>1</mn></msub><mo>=</mo><mfrac><mrow><msqrt><mn>2</mn></msqrt><mo>−</mo><mn>1</mn></mrow><mrow><msqrt><mn>2</mn></msqrt><mo>+</mo><mn>1</mn></mrow></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub></mrow></math>\n<p>I want to turn this into a fraction with a 1 in the numerator, so I’ll multiply the top and bottom by an expression that will eliminate the square root in the numerator:</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>1</mn></msub><mo>=</mo><mfrac><mrow><msqrt><mn>2</mn></msqrt><mo>−</mo><mn>1</mn></mrow><mrow><msqrt><mn>2</mn></msqrt><mo>+</mo><mn>1</mn></mrow></mfrac><mspace width=\"0.167em\"></mspace><mfrac><mrow><msqrt><mn>2</mn></msqrt><mo>+</mo><mn>1</mn></mrow><mrow><msqrt><mn>2</mn></msqrt><mo>+</mo><mn>1</mn></mrow></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub><mo>=</mo><mfrac><mrow><mn>2</mn><mo>−</mo><mn>1</mn></mrow><mrow><mn>2</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo>+</mo><mn>1</mn></mrow></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub><mo>=</mo><mfrac><mn>1</mn><mrow><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt></mrow></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub></mrow></math>\n<p>This relationship also holds for any two consecutive circles,</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>=</mo><mfrac><mn>1</mn><mrow><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt></mrow></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow></math>\n<p>which means we can express the radius of the <em>i</em>th circle in terms of the radius of the largest circle:</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mi>i</mi></msub><mo>=</mo><mfrac><mn>1</mn><msup><mrow><mo form=\"prefix\" stretchy=\"true\">(</mo><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo form=\"postfix\" stretchy=\"true\">)</mo></mrow><mi>i</mi></msup></mfrac><mspace width=\"0.25em\"></mspace><msub><mi>r</mi><mn>0</mn></msub></mrow></math>\n<p>So the sum of all the circumferences is <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mn>2</mn><mi>π</mi></mrow></math> times the sum of all the radii:</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mn>2</mn><mi>π</mi><mrow><mo form=\"prefix\" stretchy=\"true\">[</mo><msub><mi>r</mi><mn>0</mn></msub><mo>+</mo><mn>2</mn><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mo accent=\"false\">∞</mo></munderover><mspace width=\"0.278em\"></mspace><mfrac><mn>1</mn><msup><mrow><mo form=\"prefix\" stretchy=\"true\">(</mo><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo form=\"postfix\" stretchy=\"true\">)</mo></mrow><mi>i</mi></msup></mfrac><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub><mo form=\"postfix\" stretchy=\"true\">]</mo></mrow><mo>=</mo><mn>2</mn><mi>π</mi><mspace width=\"0.167em\"></mspace><msub><mi>r</mi><mn>0</mn></msub><mspace width=\"0.278em\"></mspace><mrow><mo form=\"prefix\" stretchy=\"true\">[</mo><mn>1</mn><mo>+</mo><mn>2</mn><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mo accent=\"false\">∞</mo></munderover><mspace width=\"0.278em\"></mspace><mfrac><mn>1</mn><msup><mrow><mo form=\"prefix\" stretchy=\"true\">(</mo><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo form=\"postfix\" stretchy=\"true\">)</mo></mrow><mi>i</mi></msup></mfrac><mo form=\"postfix\" stretchy=\"true\">]</mo></mrow></mrow></math>\n<p>Note that there’s only one circle with radius <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><msub><mi>r</mi><mn>0</mn></msub></math>, but two circles for all the other radii.</p>\n<p>By the way, this expression is where it’s helpful to have the fraction inside the sum written with a 1 in the numerator. We know that</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mo accent=\"false\">∞</mo></munderover><mspace width=\"0.278em\"></mspace><mfrac><mn>1</mn><msup><mn>2</mn><mi>i</mi></msup></mfrac></mrow></math>\n<p>converges, so our fraction, which has a larger denominator, must also converge.</p>\n<p>We’re nearly there. Recall that the gray square (the one that’s rotated 45°) has a side length of 1. That means</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><msub><mi>r</mi><mn>0</mn></msub><mo>=</mo><mfrac><mn>1</mn><mrow><mn>2</mn><msqrt><mn>2</mn></msqrt></mrow></mfrac></mrow></math>\n<p>so the sum of the circumferences is</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mfrac><mi>π</mi><msqrt><mn>2</mn></msqrt></mfrac><mspace width=\"0.278em\"></mspace><mrow><mo form=\"prefix\" stretchy=\"true\">[</mo><mn>1</mn><mo>+</mo><mn>2</mn><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mo accent=\"false\">∞</mo></munderover><mspace width=\"0.278em\"></mspace><mfrac><mn>1</mn><msup><mrow><mo form=\"prefix\" stretchy=\"true\">(</mo><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo form=\"postfix\" stretchy=\"true\">)</mo></mrow><mi>i</mi></msup></mfrac><mo form=\"postfix\" stretchy=\"true\">]</mo></mrow></mrow></math>\n<p>Now for a confession: I have always stunk at working out infinite series. Luckily, I can now lean on a computational cane. Here’s a Mathematica expression that will return the infinite sum:</p>\n<pre><code>Pi/Sqrt[2] (1 + 2 Sum[1/(3 + 2 Sqrt[2])^i, {i, 1, Infinity}])\n</code></pre>\n<p>The answer, as we know from doing it the easy way, is <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>π</mi></math>.</p>\n<p>If I didn’t have Mathematica, I’d probably set up a finite series for the expression without <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>π</mi></math>,</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mfrac><mn>1</mn><msqrt><mn>2</mn></msqrt></mfrac><mspace width=\"0.278em\"></mspace><mrow><mo form=\"prefix\" stretchy=\"true\">[</mo><mn>1</mn><mo>+</mo><mn>2</mn><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mspace width=\"0.278em\"></mspace><mfrac><mn>1</mn><msup><mrow><mo form=\"prefix\" stretchy=\"true\">(</mo><mn>3</mn><mo>+</mo><mn>2</mn><msqrt><mn>2</mn></msqrt><mo form=\"postfix\" stretchy=\"true\">)</mo></mrow><mi>i</mi></msup></mfrac><mo form=\"postfix\" stretchy=\"true\">]</mo></mrow></mrow></math>\n<p>and run out the calculations for different values of <em>n</em> to see where it converges. We can show the results as a table,</p>\n<table>\n<thead>\n<tr>\n<th align=\"center\">n</th>\n<th align=\"center\">Sum</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\">1</td>\n<td align=\"center\">0.94974747</td>\n</tr>\n<tr>\n<td align=\"center\">2</td>\n<td align=\"center\">0.99137803</td>\n</tr>\n<tr>\n<td align=\"center\">3</td>\n<td align=\"center\">0.99852070</td>\n</tr>\n<tr>\n<td align=\"center\">4</td>\n<td align=\"center\">0.99974619</td>\n</tr>\n<tr>\n<td align=\"center\">5</td>\n<td align=\"center\">0.99995645</td>\n</tr>\n<tr>\n<td align=\"center\">6</td>\n<td align=\"center\">0.99999253</td>\n</tr>\n<tr>\n<td align=\"center\">7</td>\n<td align=\"center\">0.99999872</td>\n</tr>\n<tr>\n<td align=\"center\">8</td>\n<td align=\"center\">0.99999978</td>\n</tr>\n<tr>\n<td align=\"center\">9</td>\n<td align=\"center\">0.99999996</td>\n</tr>\n<tr>\n<td align=\"center\">10</td>\n<td align=\"center\">0.99999999</td>\n</tr>\n</tbody>\n</table>\n<p>or as a plot,</p>\n<p><img alt=\"Convergence plot\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260219-Convergence%20plot.png\" title=\"Convergence plot\" width=\"80%\"/></p>\n<p>Either way, going out ten terms is overkill—it’s obvious that the sum is converging to 1, which means the circumference sum is converging to <math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>π</mi></math>. You can, I guess, consider this numerical exercise as a check on Mathematica’s work. Or a check on the easy solution.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:trick\">\n<p>SciAm also uses the trick in its solution, which you <em>won’t</em> see if you click on the link in this paragraph. It’s one link further away. <a href=\"#fnref:trick\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "Chinese New Year and Ramadan", "url": "https://leancrew.com/all-this/2026/02/chinese-new-year-and-ramadan/", "author": {"name": "Dr. Drang"}, "summary": "An Emacs Lisp script to see when they coincide.", "date_published": "2026-02-18T16:08:33+00:00", "id": "https://leancrew.com/all-this/2026/02/chinese-new-year-and-ramadan/", "content_html": "<p>Yesterday was <a href=\"https://en.wikipedia.org/wiki/Chinese_New_Year\">Chinese New Year</a> and today is the first day of <a href=\"https://en.wikipedia.org/wiki/Ramadan\">Ramadan</a>. Both of these dates are based on <a href=\"https://www.timeanddate.com/moon/phases/?year=2026\">yesterday’s new moon</a>, so I thought it would be fun to write a little script to see how often the dates coincide.</p>\n<p>I used <a href=\"https://www.gnu.org/software/emacs/manual/html_node/eintr/\">Emacs Lisp</a>, mainly because I knew its <a href=\"https://www.gnu.org/software/emacs/manual/html_node/emacs/Calendar_002fDiary.html\">calendar module</a> had functions for converting between Chinese, Islamic, and Gregorian calendars. You may recall my <a href=\"https://leancrew.com/all-this/2024/07/happy-2460496/\"><code>date-convert</code> script</a>, which I first wrote back in 2008 and then updated a couple of years ago. Running it today, I got this output:</p>\n<pre><code>Gregorian:  Wednesday, February 18, 2026\n      ISO:  Day 3 of week 8 of 2026\n    Astro:  2461090\n   Julian:  February 5, 2026\n   Hebrew:  Adar 1, 5786\n  Islamic:  Ramadan 1, 1447\n  Chinese:  Cycle 78, year 43 (Bing-Wu), month 1 (Geng-Yin), day 2 (Gui-Hai)\n</code></pre>\n<p>My goal was to go through a few hundred years and print out (in Gregorian terms) the dates on which the first of Ramadan came one day after Chinese New Year. I’m <em>very</em> rusty in ELisp, so this probably isn’t very good code, but here it is:</p>\n<pre><code>#!/opt/homebrew/bin/emacs --script\n\n(require 'calendar)\n(require 'cal-islam)\n(require 'cal-china)\n\n;; Loop through 300 Islamic years, roughly centered on this year\n(setq iy 1300)\n(while (&lt; iy 1600)\n  ;; Get Ramadan 1 of the year as an absolute date\n  (setq a (calendar-islamic-to-absolute (list 9 1 iy)))\n  ;; Get the month and year of this date in the Chinese calendar\n  (setq cdate (calendar-chinese-from-absolute a))\n  (setq cmd (cdr (cdr cdate)))\n  ;; Print the Gregorian date if Ramadan 1 is the day after Chinese New Year\n  (if (equal cmd (list 1 2))\n    (princ (concat \n             (calendar-date-string (calendar-gregorian-from-absolute a))\n             \"\\n\")))\n  (setq iy (1+ iy)))\n</code></pre>\n<p>The ELisp <code>calendar</code> modules use the idea of an “absolute” date, which is a simple count of days in the Gregorian calendar. Day 1 in this absolute scale corresponds to January 1 in what would have been Year 1 if the Gregorian calendar had existed back then. This is called the <a href=\"https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar\">proleptic Gregorian calendar</a>. The absolute date is used as a way station when converting between calendars. You’ll see calls to functions with <code>to-absolute</code> and <code>from-absolute</code> in their names in a few places in the code.</p>\n<p>As you can see in the output from <code>date-convert</code>, we’re currently in Year 1447 of the Islamic calendar. The <code>while</code> loop increments the <code>iy</code> variable from 1300 to 1600, a 300-year period roughly centered on this year. I get the first day of Ramadan (the ninth month) in each of those years and see if it matches up with the second day of the Chinese year. If so, it prints out the corresponding Gregorian date.</p>\n<p>The format for dates in the Chinese calendar has four terms: Cycle, Year, Month, and Day. The <code>cmd</code> variable has just the month and year, which we get from the four-term date by applying the <code>cdr</code> function twice. <a href=\"https://en.wikipedia.org/wiki/CAR_and_CDR\"><code>cdr</code></a> is one of the first Lisp functions you learn about, and I enjoyed pulling it out of my mental mothballs.</p>\n<p>Here’s the script’s output:</p>\n<pre><code>Wednesday, February 3, 1897\nMonday, February 11, 1929\nFriday, January 31, 1930\nTuesday, February 6, 1962\nSaturday, January 26, 1963\nWednesday, February 1, 1995\nWednesday, February 18, 2026\nTuesday, February 3, 2060\nSaturday, January 22, 2061\nWednesday, January 28, 2093\nWednesday, February 16, 2124\nFriday, February 11, 2157\nTuesday, January 31, 2158\n</code></pre>\n<p>These coincidences typically come 30+ years apart, but sometimes they occur in two consecutive years. The yesterday/today coincidence is the fourth time it’s happened in my life. I doubt I’ll be around for the next one; if I am, I’ll have my caretakers write a post about it.</p>"}, {"title": "Another Apple icon regression", "url": "https://leancrew.com/all-this/2026/02/another-apple-icon-regression/", "author": {"name": "Dr. Drang"}, "summary": "This CarPlay icon has been bugging me for over a year.", "date_published": "2026-02-10T04:19:18+00:00", "id": "https://leancrew.com/all-this/2026/02/another-apple-icon-regression/", "content_html": "<p>Apple’s *OS 26 icons have been getting some well-deserved criticism over the past couple of months. There was <a href=\"https://blog.jim-nielsen.com/2025/icons-in-menus/\">Jim Nielsen’s complaint</a> about menu icons in macOS. Then came <a href=\"https://tonsky.me/blog/tahoe-icons/\">Nikita Prokopov’s more detailed criticism</a> of those same icons.<sup id=\"fnref:brent\"><a href=\"#fn:brent\" rel=\"footnote\">1</a></sup> And a lot of fun has been poked at Tahoe’s app icons, reaching a peak in <a href=\"https://www.threads.com/@heliographe.studio/post/DTeOwAykwQ1\">heliograph’s deadpan post</a> on Threads.</p>\n<p>My long-overdue icon complaint is about a CarPlay icon introduced in the fall of 2024 along with iOS 18. Apart from when an app is taking over the screen, there are two primary screens in CarPlay: the app icon view, which is sort of like the home screen on an iPhone,</p>\n<p><img alt=\"CarPlay icon screen example\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260209-CarPlay%20icon%20screen%20example.png\" title=\"CarPlay icon screen example\" width=\"100%\"/></p>\n<p>and the split screen view, which is sort of like the old split view in iPadOS, but with more parts,</p>\n<p><img alt=\"CarPlay split screen example\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260209-CarPlay%20split%20screen%20example.png\" title=\"CarPlay split screen example\" width=\"100%\"/></p>\n<p>You switch between the two views by tapping the button in the lower left corner of the screen. The button with the 3×3 grid of little squircles is clearly a way to get back to the app icon view. Yes, it used to be a 2×4 grid, which actually matched the icon layout on my screen, but it’s still obvious what the button does. The single hollow squircle, on the other hand, just makes no sense. It doesn’t look anything like the split view screen it takes you to.</p>\n<p>This wasn’t the case before the fall of 2024. Here’s what that button used to look like:<sup id=\"fnref:tidbits\"><a href=\"#fn:tidbits\" rel=\"footnote\">2</a></sup></p>\n<p><img alt=\"Old CarPlay split screen icon\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260209-Old%20CarPlay%20split%20screen%20icon.jpg\" title=\"Old CarPlay split screen icon\" width=\"30%\"/></p>\n<p>Kind of obvious where this button takes you, isn’t it?</p>\n<p>It’s not that I don’t know what the single hollow squircle button does—I’ve been using it for 16 months. The icon could look like <a href=\"https://scienceleadership.org/blog/the_use_of_illustration_in_kurt_vonnegut-s-breakfast_of_champions\">Kurt Vonnegut’s drawing of an asshole</a> in <em>Breakfast of Champions</em> and I’d soon work out what the button was for,<sup id=\"fnref:claude\"><a href=\"#fn:claude\" rel=\"footnote\">3</a></sup> but the purpose of an icon is to communicate, not just be a placeholder. There’s also parallelism to consider. The icon view button looks like the screen it leads to; so should the split screen view button.</p>\n<p>It’s probably impossible to tell the upper echelon of Apple that it’s <a href=\"https://www.apple.com/newsroom/2026/01/apple-reports-first-quarter-results/\">breaking revenue records</a> in spite of its software design, not because of it. I hope the next regime knows better.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:brent\">\n<p><a href=\"https://indieweb.social/@brentsimmons/115846213935605782\">Brent Simmons figured out</a> how to get rid of these abominations, a service to humanity deserving of a Nobel Prize. <a href=\"#fnref:brent\" rev=\"footnote\">↩</a></p>\n</li>\n<li id=\"fn:tidbits\">\n<p>I couldn’t find an image of this button in my Photos library, so I stole it from <a href=\"https://talk.tidbits.com/t/carplay-split-screen-icon-removed-in-ios-18/29099\">this TidBITS Talk page</a>. <a href=\"#fnref:tidbits\" rev=\"footnote\">↩</a></p>\n</li>\n<li id=\"fn:claude\">\n<p>Of course, Apple wouldn’t use an asshole icon—that’s Anthropic’s branding. <a href=\"#fnref:claude\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "Pulling values from a graph without an LLM", "url": "https://leancrew.com/all-this/2026/02/pulling-values-from-a-graph-without-an-llm/", "author": {"name": "Dr. Drang"}, "summary": "A fun exercise using OmniGraffle and AppleScript.", "date_published": "2026-02-04T15:48:11+00:00", "id": "https://leancrew.com/all-this/2026/02/pulling-values-from-a-graph-without-an-llm/", "content_html": "<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href=\"https://leancrew.com/all-this/2026/02/pulling-values-from-a-graph-without-an-llm/\">the original article</a> to see them rendered properly.]</p>\n<hr />\n<p>The inability of Claude and (especially) ChatGPT to extract data accurately from the chart discussed in <a href=\"https://leancrew.com/all-this/2026/02/plotting-via-claude/\">the last post</a> gnawed at me. It seemed like the sort of thing an LLM should be able to do pretty well, but Claude’s table of values needed some careful editing before I could use it. And ChatGPT just completely botched the job, even after several attempts to steer it right. I knew I could do a much better job in not much more time than I spent trying to get the LLMs to do it.</p>\n<p>As a reminder, here’s the graph:</p>\n<p><img alt=\"Warsh commentary Dutta plot\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260202-Warsh%20commentary%20Dutta%20plot.jpg\" title=\"Warsh commentary Dutta plot\" width=\"100%\"/></p>\n<p>What I wanted was a CSV file with a column of dates (the x-values) and a column of floating point numbers (the y-values). One row for each of the 29 points.</p>\n<p>I’ve done things like this in the past with <a href=\"https://www.omnigroup.com/omnigraffle\">OmniGraffle</a> and AppleScript, so that’s how I approached the problem.</p>\n<p>The first step was to get the x-values, which didn’t involve OmniGraffle or AppleScript. I opened the image in Preview, selected the text labels along the x-axis with <a href=\"https://textsniper.app/\">TextSniper</a>, and pasted the result into a new <a href=\"https://www.barebones.com/products/bbedit/index.html\">BBEdit</a> window. Text Sniper had no trouble reading the rotated text, but it did have the values separated by space characters instead of line feeds. No problem; I just did a find/replace in BBEdit to get this:</p>\n<pre><code>2006-10\n2007-03\n2007-06\n2008-01\n2008-03\n2008-05\n2008-09\n2008-12\n2009-04\n2009-06\n2009-09\n2009-12\n2010-02\n2010-03\n2010-06\n2010-11\n2011-02\n2011-05\n2015-06\n2017-03\n2024-03\n2024-07\n2024-11\n2025-04\n2025-05\n2025-07\n2025-10\n2025-11\n2026-01\n</code></pre>\n<div class=\"update\">\n<p><strong>Update 5 Feb 2026 12:11 AM</strong><br/>\nJoe Rosensteel pointed out <a href=\"https://duck.haus/@joesteel/116013282082445833\">on Mastodon</a> that I could have copied the text in Preview instead of TextSniper. While I knew that Preview could select text in an image, I didn’t know it could do it when the text was rotated. As long as the selection setting in the Tools menu is <em>not</em> Rectangular Selection, the pointer will change to the familiar I-beam shape when you move it over text. It looks a little funny because the I-beam is oriented as if it were selecting horizontal text, but the selection works.</p>\n<p>I still prefer TextSniper and keep it in my menu bar, ready to invoke with ⇧⌘2, because it OCRs any text—regardless of orientation—within its rectangular selection, but I bought it back before macOS had built-in OCR tools. TextSniper is better, but if I didn’t already own it, I probably wouldn’t buy it today.</p>\n</div>\n<p>These dates are just months, and I figured it would be best for the plot to assume they were near the middle of each month, so I appended “-15,” to the end of each line. That set all the dates to the 15th of the month, and the comma prepared the file for pasting in the y-values:</p>\n<pre><code>2006-10-15,\n2007-03-15,\n2007-06-15,\n2008-01-15,\n2008-03-15,\n2008-05-15,\n2008-09-15,\n2008-12-15,\n2009-04-15,\n2009-06-15,\n2009-09-15,\n2009-12-15,\n2010-02-15,\n2010-03-15,\n2010-06-15,\n2010-11-15,\n2011-02-15,\n2011-05-15,\n2015-06-15,\n2017-03-15,\n2024-03-15,\n2024-07-15,\n2024-11-15,\n2025-04-15,\n2025-05-15,\n2025-07-15,\n2025-10-15,\n2025-11-15,\n2026-01-15,\n</code></pre>\n<p>Then came the real work. I pasted the image into a new OmniGraffle document with the scale set to “1 pt = 1 pt.” I did this because I knew that OmniGraffle’s AppleScript dictionary returns all coordinates and lengths in points with the origin at the upper left corner of the document. I then drew some thin-lined rectangles to get the y-coordinate of the horizontal axis and calculate the y-scale of the image.</p>\n<p>A rectangle with its top edge along the horizontal axis told me that the y-origin of the plot was 581 points down from the top of the document. To get the scale of the plot, I drew another rectangle whose bottom edge was aligned with the negative sign in the “-4” label and whose top edge was aligned with the equivalent part of the “4” label. That rectangle was 512.6 points high, so the scale of the plot is</p>\n<math display=\"block\" xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow><mfrac><mn>512.6</mn><mn>8</mn></mfrac><mo>=</mo><mn>64.075</mn><mspace width=\"0.278em\"></mspace><mrow><mi mathvariant=\"normal\">p</mi><mi mathvariant=\"normal\">t</mi><mi mathvariant=\"normal\">s</mi><mi mathvariant=\"normal\">/</mi><mi mathvariant=\"normal\">u</mi><mi mathvariant=\"normal\">n</mi><mi mathvariant=\"normal\">i</mi><mi mathvariant=\"normal\">t</mi></mrow></mrow></math>\n<p>I then made a new layer called “Markers” and drew a diamond (OmniGraffle has a built-in diamond shape) sized to match the markers in the plot. By duplication and dragging, I put a diamond shape over every marker. Here’s what it looked like with every diamond selected:</p>\n<p><img alt=\"OmniGraffle document with graph and markers\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260204-OmniGraffle%20document%20with%20graph%20and%20markers.png\" title=\"OmniGraffle document with graph and markers\" width=\"100%\"/></p>\n<p>If you zoom in, you’ll see that the diamonds are 26.4 points tall, which means that the center of each diamond is 13.2 points below its top edge. We’ll need this value in the AppleScript because when OmniGraffle is asked for the location of an object, it returns the coordinates of the top left corner.</p>\n<p>Here’s another useful tidbit I’ve learned when using OmniGraffle’s AppleScript dictionary in the past: when you ask for <code>every shape</code> in a layer, the list of shapes is returned in the top-to-bottom order shown in the left sidebar. You may think this is obvious, but it isn’t. As you add shapes to a layer, each new shape appears in the sidebar <em>above</em> its predecessor. So the order of the items in the <code>every shape</code> list is the opposite of the creation order. The upshot of this is that to get the y-values of the diamonds in left-to-right order (to match their date order), I created them in right-to-left order.<sup id=\"fnref:reverse\"><a href=\"#fn:reverse\" rel=\"footnote\">1</a></sup></p>\n<div class=\"update\">\n<p><strong>Update 5 Feb 2026 12:11 PM</strong><br/>\nI forgot to mention here that the order of shapes in the sidebar is reverse chronological order only by coincidence. It’s actually the stacking order. The reason that usually corresponds to reverse chronological order is that new shapes stack above older shapes. You can, of course, take the shapes out of their original order by using the various Bring and Send commands in OmniGraffle’s Arrange menu.</p>\n</div>\n<p>Now it’s time to write and run the AppleScript that extracts the y-coordinates of the centers of the diamonds and scales them to match the plot. It’s pretty simple:</p>\n<pre><code>applescript:\n 1:  set yValues to {}\n 2:  set yOrigin to 581\n 3:  set yOffset to 13.2\n 4:  set yScale to 64.075\n 5:  \n 6:  tell application \"OmniGraffle\"\n 7:    tell layer \"Markers\" of canvas 1 of document 1\n 8:      set diamonds to every shape\n 9:      repeat with d in diamonds\n10:        set pt to (origin of d)\n11:        set end of yValues to (yOrigin - (item 2 of pt) - yOffset) / yScale\n12:      end repeat\n13:    end tell\n14:  end tell\n15:  \n16:  yValues\n</code></pre>\n<p>Line 1 initializes the list of y-values we’ll be building. Lines 2–4 set the values we established above. After the <code>tell</code> commands to establish that we’re focusing on the “Markers” layer, Line 8 creates the list of diamonds. Lines 9–12 loop through that list to get the location (<code>origin</code>) of each diamond and convert it to the value being plotted. The order in which the subtraction is done in Line 11 accounts for the fact that OmniGraffle’s coordinates increase as you go down but the plot’s coordinates increase as you go up.</p>\n<p>Line 16 spits out the list of y-values in the Result section of Script Editor:</p>\n<pre><code>{1.777610453442, 2.105119526221, 2.417138303752,\n-2.823376834196, -2.217843888773, 0.804769157205,\n-3.225882114947, -2.315093559097, 1.802782567774,\n2.308216918434, 3.517346715278, 3.011460781549,\n2.606423024043, 3.211060498316, 4.212510467041,\n4.018455838485, 4.315198697655, 4.018455838485,\n3.316423039057, 2.512942396277, 1.803414490836,\n1.203973154221, -0.507963600878, -0.304047453731,\n-0.80360682595, -2.016491975871, -1.812215148741,\n-2.521036825178, -2.2390063608}\n</code></pre>\n<p>That’s way more digits than is justified, but the extra digits don’t hurt anything. After turning each comma-space combination into a linefeed, I did a column paste to put the y-values after the dates. Adding a header line turned it into the CSV file I wanted for plotting:</p>\n<pre><code>Date,Hawk\n2006-10-15,1.777610453442\n2007-03-15,2.105119526221\n2007-06-15,2.417138303752\n2008-01-15,-2.823376834196\n2008-03-15,-2.217843888773\n2008-05-15,0.804769157205\n2008-09-15,-3.225882114947\n2008-12-15,-2.315093559097\n2009-04-15,1.802782567774\n2009-06-15,2.308216918434\n2009-09-15,3.517346715278\n2009-12-15,3.011460781549\n2010-02-15,2.606423024043\n2010-03-15,3.211060498316\n2010-06-15,4.212510467041\n2010-11-15,4.018455838485\n2011-02-15,4.315198697655\n2011-05-15,4.018455838485\n2015-06-15,3.316423039057\n2017-03-15,2.512942396277\n2024-03-15,1.803414490836\n2024-07-15,1.203973154221\n2024-11-15,-0.507963600878\n2025-04-15,-0.304047453731\n2025-05-15,-0.80360682595\n2025-07-15,-2.016491975871\n2025-10-15,-1.812215148741\n2025-11-15,-2.521036825178\n2026-01-15,-2.2390063608\n</code></pre>\n<p>I still have the CSV file made by Claude. Let’s compare:</p>\n<table>\n<thead>\n<tr>\n<th align=\"center\">Date</th>\n<th align=\"right\">Claude</th>\n<th align=\"right\">AppleScript</th>\n<th align=\"right\">Difference</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\">2006-10-15</td>\n<td align=\"right\">1.7</td>\n<td align=\"right\">1.78</td>\n<td align=\"right\">-0.08</td>\n</tr>\n<tr>\n<td align=\"center\">2007-03-15</td>\n<td align=\"right\">2.0</td>\n<td align=\"right\">2.11</td>\n<td align=\"right\">-0.11</td>\n</tr>\n<tr>\n<td align=\"center\">2007-06-15</td>\n<td align=\"right\">2.4</td>\n<td align=\"right\">2.42</td>\n<td align=\"right\">-0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2008-01-15</td>\n<td align=\"right\">-2.9</td>\n<td align=\"right\">-2.82</td>\n<td align=\"right\">-0.08</td>\n</tr>\n<tr>\n<td align=\"center\">2008-03-15</td>\n<td align=\"right\">-2.2</td>\n<td align=\"right\">-2.22</td>\n<td align=\"right\">0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2008-05-15</td>\n<td align=\"right\">0.8</td>\n<td align=\"right\">0.80</td>\n<td align=\"right\">-0.00</td>\n</tr>\n<tr>\n<td align=\"center\">2008-09-15</td>\n<td align=\"right\">-3.2</td>\n<td align=\"right\">-3.23</td>\n<td align=\"right\">0.03</td>\n</tr>\n<tr>\n<td align=\"center\">2008-12-15</td>\n<td align=\"right\">-2.5</td>\n<td align=\"right\">-2.32</td>\n<td align=\"right\">-0.18</td>\n</tr>\n<tr>\n<td align=\"center\">2009-04-15</td>\n<td align=\"right\">1.7</td>\n<td align=\"right\">1.80</td>\n<td align=\"right\">-0.10</td>\n</tr>\n<tr>\n<td align=\"center\">2009-06-15</td>\n<td align=\"right\">2.2</td>\n<td align=\"right\">2.31</td>\n<td align=\"right\">-0.11</td>\n</tr>\n<tr>\n<td align=\"center\">2009-09-15</td>\n<td align=\"right\">3.5</td>\n<td align=\"right\">3.52</td>\n<td align=\"right\">-0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2009-12-15</td>\n<td align=\"right\">3.0</td>\n<td align=\"right\">3.01</td>\n<td align=\"right\">-0.01</td>\n</tr>\n<tr>\n<td align=\"center\">2010-02-15</td>\n<td align=\"right\">2.6</td>\n<td align=\"right\">2.61</td>\n<td align=\"right\">-0.01</td>\n</tr>\n<tr>\n<td align=\"center\">2010-03-15</td>\n<td align=\"right\">3.1</td>\n<td align=\"right\">3.21</td>\n<td align=\"right\">-0.11</td>\n</tr>\n<tr>\n<td align=\"center\">2010-06-15</td>\n<td align=\"right\">4.2</td>\n<td align=\"right\">4.21</td>\n<td align=\"right\">-0.01</td>\n</tr>\n<tr>\n<td align=\"center\">2010-11-15</td>\n<td align=\"right\">4.0</td>\n<td align=\"right\">4.02</td>\n<td align=\"right\">-0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2011-02-15</td>\n<td align=\"right\">4.4</td>\n<td align=\"right\">4.32</td>\n<td align=\"right\">0.08</td>\n</tr>\n<tr>\n<td align=\"center\">2011-05-15</td>\n<td align=\"right\">4.1</td>\n<td align=\"right\">4.02</td>\n<td align=\"right\">0.08</td>\n</tr>\n<tr>\n<td align=\"center\">2015-06-15</td>\n<td align=\"right\">3.3</td>\n<td align=\"right\">3.32</td>\n<td align=\"right\">-0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2017-03-15</td>\n<td align=\"right\">2.5</td>\n<td align=\"right\">2.51</td>\n<td align=\"right\">-0.01</td>\n</tr>\n<tr>\n<td align=\"center\">2024-03-15</td>\n<td align=\"right\">1.7</td>\n<td align=\"right\">1.80</td>\n<td align=\"right\">-0.10</td>\n</tr>\n<tr>\n<td align=\"center\">2024-07-15</td>\n<td align=\"right\">1.2</td>\n<td align=\"right\">1.20</td>\n<td align=\"right\">-0.00</td>\n</tr>\n<tr>\n<td align=\"center\">2024-11-15</td>\n<td align=\"right\">-0.5</td>\n<td align=\"right\">-0.51</td>\n<td align=\"right\">0.01</td>\n</tr>\n<tr>\n<td align=\"center\">2025-04-15</td>\n<td align=\"right\">-0.3</td>\n<td align=\"right\">-0.30</td>\n<td align=\"right\">0.00</td>\n</tr>\n<tr>\n<td align=\"center\">2025-05-15</td>\n<td align=\"right\">-0.6</td>\n<td align=\"right\">-0.80</td>\n<td align=\"right\">0.20</td>\n</tr>\n<tr>\n<td align=\"center\">2025-07-15</td>\n<td align=\"right\">-2.1</td>\n<td align=\"right\">-2.02</td>\n<td align=\"right\">-0.08</td>\n</tr>\n<tr>\n<td align=\"center\">2025-10-15</td>\n<td align=\"right\">-2.0</td>\n<td align=\"right\">-1.81</td>\n<td align=\"right\">-0.19</td>\n</tr>\n<tr>\n<td align=\"center\">2025-11-15</td>\n<td align=\"right\">-2.5</td>\n<td align=\"right\">-2.52</td>\n<td align=\"right\">0.02</td>\n</tr>\n<tr>\n<td align=\"center\">2026-01-15</td>\n<td align=\"right\">-2.3</td>\n<td align=\"right\">-2.24</td>\n<td align=\"right\">-0.06</td>\n</tr>\n</tbody>\n</table>\n<p>Claude’s values were off by only about 0.2 at worst, but recall that it originally included a spurious date and had the last seven y-values assigned to the wrong date. The “Claude” values shown above are after I figured out those obvious errors and corrected them.</p>\n<p>An odd thing about Claude’s errors: they don’t show a consistent bias in either magnitude or direction. Like everyone who programs, I’m used to seeing output with incorrect numbers. (In my first draft of the AppleScript, I multiplied by <code>yScale</code> instead of dividing, which gave pretty wild results.) What I’m not used to is inexplicably wrong numbers—numbers that aren’t wrong in a predictable way.</p>\n<p>At the risk of lengthening this post even further, here’s the graph with the AppleScript-derived values:</p>\n<p><img alt=\"Warsh commentary via AppleScript\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260202-Warsh%20commentary%20via%20AppleScript.png\" title=\"Warsh commentary via AppleScript\" width=\"100%\"/></p>\n<p>And here’s the Python code that created it:</p>\n<pre><code>python:\n 1:  #!/usr/bin/env python3\n 2:  \n 3:  import pandas as pd\n 4:  from datetime import datetime\n 5:  import matplotlib.pyplot as plt\n 6:  from matplotlib.ticker import MultipleLocator, AutoMinorLocator\n 7:  from matplotlib.dates import DateFormatter, YearLocator, MonthLocator\n 8:  \n 9:  # Import the data\n10:  df = pd.read_csv('warsh-applescript.csv', parse_dates=[0])\n11:  x = df.Date\n12:  y = df.Hawk\n13:  \n14:  # Create the plot with a given size in inches\n15:  fig, ax = plt.subplots(figsize=(8, 5))\n16:  \n17:  # Add a line\n18:  ax.plot(x, y, 'D-', color='#60150a', lw=3, ms=5)\n19:  \n20:  # Set the limits\n21:  plt.xlim(xmin=datetime(2006,1,1), xmax=datetime(2026,12,31))\n22:  plt.ylim(ymin=-4, ymax=5)\n23:  \n24:  # Set the major and minor ticks and add a grid\n25:  ax.xaxis.set_major_locator(YearLocator(2))\n26:  ax.xaxis.set_minor_locator(YearLocator(1))\n27:  ax.xaxis.set_major_formatter(DateFormatter('%Y'))\n28:  ax.yaxis.set_major_locator(MultipleLocator(1))\n29:  ax.axhline(y=0, color='k', lw=.5)\n30:  \n31:  # Title and axis labels\n32:  plt.title('Warsh commentary')\n33:  plt.xlabel('')\n34:  plt.ylabel('Monetary hawkishness')\n35:  \n36:  # Make the border and tick marks 0.5 points wide\n37:  [ i.set_linewidth(0.5) for i in ax.spines.values() ]\n38:  ax.tick_params(which='both', width=.5)\n39:  \n40:  # Reduce the whitespace around the plot\n41:  plt.tight_layout()\n42:  \n43:  # Save as PNG\n44:  plt.savefig('20260202-Warsh commentary via AppleScript.png', format='png', dpi=150)\n</code></pre>\n<p>Importing <a href=\"https://pandas.pydata.org/\">Pandas</a> is overkill just to read a CSV file, but it’s more efficient of my time. The rest of the code is basically just my typical <a href=\"https://matplotlib.org/\">Matplotlib</a> boilerplate with a few tweaks for dealing with the limits and tick spacing.</p>\n<p>Did this take longer than having Claude return the CSV file? Of course it did, but it didn’t take much more than half an hour or so, equally divided between the OmniGraffle drawing and the AppleScript coding. And that half-hour was much more satisfying than arguing with a random number generator and then editing its work after losing the argument.</p>\n<p>Also, because I’ve now written up the process (which took significantly longer than doing it), I have a method I can use with confidence in the future.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:reverse\">\n<p>There are, of course, many ways to reverse a list, but there’s no <code>reverse</code> command in AppleScript. It’s easier to remember to create the shapes in reverse order. It takes no more time to do it that way. <a href=\"#fnref:reverse\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "Plotting via Claude", "url": "https://leancrew.com/all-this/2026/02/plotting-via-claude/", "author": {"name": "Dr. Drang"}, "summary": "Paul Krugman included an AI-generated plot in his post this morning. It stunk.", "date_published": "2026-02-03T02:46:20+00:00", "id": "https://leancrew.com/all-this/2026/02/plotting-via-claude/", "content_html": "<p>Paul Krugman included a terrible plot in <a href=\"https://paulkrugman.substack.com/p/kevin-warsh-and-weathervane-economics\">his Substack post</a> this morning. It’s meant to support his contention that Kevin Warsh, Trump’s soon-to-be nominee for chair of the Federal Reserve, is a political hack. It does, but it’s still terrible.</p>\n<p>Krugman’s not to blame—at least not entirely—for how bad the plot is. It was given to him by <a href=\"https://www.renmac.com/neil-dutta/\">Neil Dutta</a>, who apparently had <a href=\"https://claude.ai\">Claude</a> assess the monetary hawkishness of Warsh’s statements, assign a number to it, and plot those numbers against the dates on which the statements were made. Here’s the result:</p>\n<p><img alt=\"Warsh commentary Dutta plot\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260202-Warsh%20commentary%20Dutta%20plot.jpg\" title=\"Warsh commentary Dutta plot\" width=\"100%\"/></p>\n<p>If you look along the horizontal axis, you’ll see immediately why this is an awful plot. The points are uniformly spaced horizontally, even though the spacing of the dates is far from uniform. Apparently, Claude considered entries like “2006-10” as categories rather than dates. Not especially intelligent of Claude or Dutta.</p>\n<p>Krugman—who probably did’t want to go to the trouble of replotting the data—alerted his readers to the problem without being mean to Dutta:</p>\n<blockquote>\n<p>If you look carefully at that chart, you’ll see that there’s a gap in the timeline for several years after Warsh was passed over for Fed chair during Trump’s first term.</p>\n</blockquote>\n<p>Had the data been plotted correctly, you wouldn’t have to look carefully:</p>\n<p><img alt=\"Warsh commentary adjusted plot\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260202-Warsh%20commentary%20adjusted%20plot.png\" title=\"Warsh commentary adjusted plot\" width=\"100%\"/></p>\n<p>Because <em>I</em> didn’t want to go to too much trouble in making this plot, I didn’t adjust the labels on the horizontal axis the way I usually do. The labels are aligned with January 1 of each year.</p>\n<p>You may also notice that the y-values in my graph aren’t a perfect match with the y-values in Dutta’s original. Using a “sauce for the goose” approach, I told Claude to assess Dutta’s plot and generate a CSV file with all the points. There were some serious errors in the resulting CSV—30 data points instead of 29 and 8 negative points at the right end of the graph instead of 7—but they were fairly easy to fix.<sup id=\"fnref:fix\"><a href=\"#fn:fix\" rel=\"footnote\">1</a></sup> And since the relative hawkishness of Warsh’s commentary is a pretty soft number, I’m not worried about the individual values being off by a few tenths. The main thing was to get the dates plotted as they should be.</p>\n<div class=\"footnotes\">\n<hr/>\n<ol>\n<li id=\"fn:fix\">\n<p>I should point out that the fixing was done by me, not Claude. I tried to get Claude to fix its mistakes, but it just added more. I also tried to get ChatGPT to generate a CSV from Dutta’s plot, and it fucked the assignment so thoroughly—and was so smarmy in its apologies—it made me question whether I should ever use it again. <a href=\"#fnref:fix\" rev=\"footnote\">↩</a></p>\n</li>\n</ol>\n</div>"}, {"title": "On the average", "url": "https://leancrew.com/all-this/2026/01/on-the-average/", "author": {"name": "Dr. Drang"}, "summary": "Should you average times across different time zones?", "date_published": "2026-01-26T14:42:09+00:00", "id": "https://leancrew.com/all-this/2026/01/on-the-average/", "content_html": "<p>A few days ago, I was poking around in Apple’s Weather app and came across some interesting stuff hiding behind the sunrise/sunset box. First, there’s a plot that shows the movement of the sun throughout the day, followed by sunrise and sunset times:</p>\n<p><img alt=\"Sun motion plot\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260125-Sun%20motion%20plot.png\" title=\"Sun motion plot\" width=\"50%\"/></p>\n<p>The little dots under the “horizon” represent the beginnings and endings of <a href=\"https://www.timeanddate.com/astronomy/different-types-twilight.html\">civil, nautical, and astronomical twilight</a>, which is a nice addition to the plot. I assume the vertical axis has something to do with the Sun’s <a href=\"https://www.timeanddate.com/astronomy/horizontal-coordinate-system.html\">altitude angle</a>, but since the plot gives no units, it must be an example of <a href=\"https://sixcolors.com/post/2018/07/the-good-great-and-mac-of-apples-record-quarter/#:~:text=Jeff%20Bezos\">Bezos astronomy</a>.</p>\n<p>Scrolling down, we come to this odd graphic: the average sunrise and sunset times for each month of the year.</p>\n<p><img alt=\"Sunrise and sunset averages\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260125-Sunrise%20and%20sunset%20averages.png\" title=\"Sunrise and sunset averages\" width=\"50%\"/></p>\n<p>Given that I live in a state that switches between Standard Time and Daylight Saving Time, this is a troubling calculation. There’s no trouble <em>making</em> the calculation, of course: you just add up all the sunrise or sunset times and divide by the number of days in the month. And that’s clearly what Apple’s done. No, the trouble comes in March and November, the months when <a href=\"https://www.nist.gov/pml/time-and-frequency-division/popular-links/daylight-saving-time-dst\">we switch</a> from CST to CDT and back. What is the meaning of “average” when the individual values are measured differently?</p>\n<p>On the second Sunday in March, sunrise and sunset suddenly become an hour later than they were, and the same thing happens in reverse on the first Sunday in November. Here in 2026, those days will be March 8 and November 1; in 2027, they’ll be March 14 and November 7. That the number of days we’re on CDT and CST in March and November changes from year to year makes the “averages” for those months even weirder.</p>\n<p>After thinking about this for a while, I came to three conclusions:</p>\n<ol>\n<li>It’s nice to have on hand a simple display of how sunrise and sunset change over the course of a year.</li>\n<li>I may be the only person bothered by the way Apple presents this information.</li>\n<li>I’ve already shown how I’d like to see it done.</li>\n</ol>\n<p>A couple of years ago, <a href=\"https://leancrew.com/all-this/2023/11/more-general-sunrise-sunset-plots/\">I wrote a Python script</a> that used <a href=\"https://matplotlib.org/\">Matplotlib</a> and data from the <a href=\"https://aa.usno.navy.mil/data/RS_OneYear\">US Naval Observatory</a> to generate plots like this:</p>\n<p><img alt=\"Sunrise-sunset plot for Naperville 2026\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260125-Sunrise-sunset%20plot%20for%20Naperville%202026.png\" title=\"Sunrise-sunset plot for Naperville 2026\" width=\"100%\"/></p>\n<p>A graph like this wouldn’t work well on a phone because it’s wide instead of tall. But I figured it wouldn’t be too hard to redo it with the axes switched to give it a portrait format. I removed the various annotations because the location would be given in the Weather app. I also removed the “Hours of daylight” curve; without a 24-hour clock, I couldn’t cheat and treat the horizontal axis as both a time and a duration. I also got rid of the yellow and put everything in shades of gray to better match Apple’s aesthetic.</p>\n<p><img alt=\"Sunrise-sunset plot in vertical orientation\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260125-Sunrise-sunset%20plot%20in%20vertical%20orientation.png\" title=\"Sunrise-sunset plot in vertical orientation\" width=\"65%\"/></p>\n<p>Apple would never include a plot with this many gridlines, but I couldn’t bring myself to get rid of them. They really help you track how the sunrise and sunsets change over the course of the year. Apple doesn’t want to scare its customers with complexity; when it feels the need to show detail, it puts it in a popup that appears when you tap inside the plot. Apple’s way is certainly cleaner looking, but I prefer seeing all the information at once.</p>\n<p>Here’s the code that produced the plot above:</p>\n<pre><code>python:\n  1:  #!/usr/bin/env python3\n  2:  \n  3:  import sys\n  4:  import re\n  5:  from dateutil.parser import parse\n  6:  from datetime import datetime\n  7:  from datetime import timedelta\n  8:  from matplotlib import pyplot as plt\n  9:  import matplotlib.dates as mdates\n 10:  from matplotlib.ticker import MultipleLocator, FormatStrFormatter\n 11:  \n 12:  \n 13:  # Functions\n 14:  \n 15:  def headerInfo(header):\n 16:    \"Return location name, coordinates, and year from the USNO header lines.\"\n 17:  \n 18:    # Get the place name from the middle of the top line\n 19:    left = 'o  ,    o  ,'\n 20:    right = 'Astronomical Applications Dept.'\n 21:    placeName = re.search(rf'{left}(.+){right}', header[0]).group(1).strip()\n 22:  \n 23:    # If the place name ends with a comma, a space, and a pair of capitals,\n 24:    # assume it's in location, ST format and capitalize the location while\n 25:    # keeping the state as all uppercase. Otherwise, capitalize all the words.\n 26:    if re.match(r', [A-Z][A-Z]', placeName[-4:]):\n 27:      placeParts = placeName.split(', ')\n 28:      location = ', '.join(placeParts[:-1]).title()\n 29:      state = placeParts[-1]\n 30:      placeName = f'{location}, {state}'\n 31:    else:\n 32:      placeName = placeName.title()\n 33:  \n 34:    # The year is at a specific spot on the second line\n 35:    year = int(header[1][80:84])\n 36:  \n 37:    # The latitude and longitude are at specific spots on the second line\n 38:    longString = header[1][10:17]\n 39:    latString = header[1][19:25]\n 40:  \n 41:    # Reformat the latitude into d° m′ N format (could be S)\n 42:    dir = latString[0]\n 43:    degree, minute = latString[1:].split()\n 44:    lat = f'{int(degree)}° {int(minute)}′ {dir}'\n 45:  \n 46:    # Reformat the longitude into d° m′ W format\n 47:    dir = longString[0]\n 48:    degree, minute = longString[1:].split()\n 49:    long = f'{int(degree)}° {int(minute)}′ {dir}'\n 50:  \n 51:    return placeName, lat, long, year\n 52:  \n 53:  def bodyInfo(body, isLeap):\n 54:    \"Return lists of sunrise, sunset, and daylight length hours from the USNO body lines.\"\n 55:  \n 56:    # Initialize\n 57:    sunrises = []\n 58:    sunsets = []\n 59:    lengths = []\n 60:  \n 61:    # Rise and set character start positions for each month\n 62:    risePos = [ 4 + 11*i for i in range(12) ]\n 63:    setPos = [ 9 + 11*i for i in range(12) ]\n 64:  \n 65:    # Collect data from each day\n 66:    for m in range(12):\n 67:      for d in range(daysInMonth[m]):\n 68:        riseString = body[d][risePos[m]:risePos[m]+4]\n 69:        hour, minute = int(riseString[:2]), int(riseString[-2:])\n 70:        sunrise = hour + minute/60\n 71:        setString = body[d][setPos[m]:setPos[m]+4]\n 72:        hour, minute = int(setString[:2]), int(setString[-2:])\n 73:        sunset = hour + minute/60\n 74:        sunrises.append(sunrise)\n 75:        sunsets.append(sunset)\n 76:        lengths.append(sunset - sunrise)\n 77:  \n 78:    return(sunrises, sunsets, lengths)\n 79:  \n 80:  def dstBounds(year):\n 81:    \"Return the DST start and end day indices according to current US rules.\"\n 82:  \n 83:    # Start DST on second Sunday of March\n 84:    d = 8\n 85:    while datetime.weekday(dstStart := datetime(year, 3, d)) != 6:\n 86:      d += 1\n 87:    dstStart = (dstStart - datetime(year, 1, 1)).days\n 88:  \n 89:    # End DST on first Sunday of November\n 90:    d = 1\n 91:    while datetime.weekday(dstEnd := datetime(year, 11, d)) != 6:\n 92:      d += 1\n 93:    dstEnd = (dstEnd - datetime(year, 1, 1)).days\n 94:  \n 95:    return dstStart, dstEnd\n 96:  \n 97:  \n 98:  # Start processing\n 99:  \n100:  # Read the USNO data from stdin into a list of lines.\n101:  # Text should come from https://aa.usno.navy.mil/data/RS_OneYear\n102:  usno = sys.stdin.readlines()\n103:  \n104:  # Get location and year from header\n105:  placeName, lat, long, year = headerInfo(usno[:2])\n106:  \n107:  # Month information, adjusted for leap year if needed.\n108:  monthNames = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split()\n109:  isLeap = (year % 400 == 0) or ((year % 4 == 0) and not (year % 100 == 0))\n110:  if isLeap:\n111:    daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n112:  else:\n113:    daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n114:  \n115:  # Get sunrise, sunset, and sunlight length lists from body\n116:  sunrises, sunsets, lengths = bodyInfo(usno[9:], isLeap)\n117:  \n118:  # Generate list of days for the year\n119:  currentDay = datetime(year, 1, 1)\n120:  lastDay = datetime(year, 12, 31)\n121:  days = [currentDay]\n122:  while (currentDay := currentDay + timedelta(days=1)) &lt;= lastDay:\n123:    days.append(currentDay)\n124:  \n125:  # The portion of the year that uses DST\n126:  dstStart, dstEnd = dstBounds(year)\n127:  dstDays = days[dstStart:dstEnd + 1]\n128:  dstRises = [ x + 1 for x in sunrises[dstStart:dstEnd + 1] ]\n129:  dstSets = [ x + 1 for x in sunsets[dstStart:dstEnd + 1] ]\n130:  \n131:  # Plot the data\n132:  fig, ax =plt.subplots(figsize=(6,10))\n133:  \n134:  # Shaded areas\n135:  plt.fill_betweenx(days, sunrises, sunsets, facecolor='gray', alpha=.5)\n136:  plt.fill_betweenx(days, 0, sunrises, facecolor='black', alpha=.75)\n137:  plt.fill_betweenx(days, sunsets, 24, facecolor='black', alpha=.75)\n138:  plt.fill_betweenx(dstDays, sunsets[dstStart:dstEnd + 1], dstSets, facecolor='white', alpha=.5)\n139:  plt.fill_betweenx(dstDays, sunrises[dstStart:dstEnd + 1], dstRises, facecolor='black', alpha=.16)\n140:  \n141:  # Curves\n142:  plt.plot(sunrises, days, color='k')\n143:  plt.plot(sunsets, days, color='k')\n144:  plt.plot(dstRises, dstDays, color='k')\n145:  plt.plot(dstSets, dstDays, color='k')\n146:  \n147:  # Background grids\n148:  ax.grid(which='major', color='#ccc', ls='-', lw=.5)\n149:  ax.grid(which='minor', color='#ddd', ls=':', lw=.5)\n150:  \n151:  # Vertical axis grid at month boundaries\n152:  # ax.tick_params(axis='both', which='major', labelsize=12)\n153:  plt.ylim(datetime(year, 1, 1), datetime(year, 12, 31))\n154:  plt.tick_params(axis='y', length=0)\n155:  m = mdates.MonthLocator(bymonthday=1)\n156:  mfmt = mdates.DateFormatter('')\n157:  ax.yaxis.set_major_locator(m)\n158:  ax.yaxis.set_major_formatter(mfmt)\n159:  ax.yaxis.set_inverted(True)\n160:  \n161:  # Month labels inside the plot in white letters\n162:  for m in range(12):\n163:    middle = sum(daysInMonth[:m]) + daysInMonth[m]//2\n164:    ax.text(.5, days[middle], monthNames[m], fontsize=12, color='w', ha='left', va='center')\n165:  \n166:  # Horizontal axis labels formatted like h:mm\n167:  plt.xlim(0, 24)\n168:  xmajor = MultipleLocator(4)\n169:  xminor = MultipleLocator(1)\n170:  ax.xaxis.set_major_locator(xmajor)\n171:  ax.xaxis.set_minor_locator(xminor)\n172:  plt.xticks(ticks=[0, 4, 8, 12, 16, 20, 24], labels=['mid', '4:00', '8:00', 'noon', '4:00', '8:00', 'mid'])\n173:  \n174:  # Tighten up the white border and save\n175:  fig.set_tight_layout({'pad': 1.5})\n176:  plt.savefig(f'{placeName}-{year}.png', format='png', dpi=150)\n</code></pre>\n<p>It’s a modification of the script given in <a href=\"https://leancrew.com/all-this/2023/11/more-general-sunrise-sunset-plots/\">my post</a> from a couple of years ago. The main difference, apart from the color changes, is that instead of using Matplotlib’s <code>fill_between</code> function, I used the similar <a href=\"https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill_betweenx.html#matplotlib.pyplot.fill_betweenx\"><code>fill_betweenx</code> function</a> in Lines 135–139. Because the axes were switched, I needed to fill between vertical curves instead of horizontal curves.</p>\n<p>The other unusual thing I did was use Matplotlib’s <a href=\"https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.text.html\"><code>text</code> function</a> in Lines 161–164 to put the month labels inside the graph instead of along the left edge. That made the plot more compact. Because months are intervals of time, I centered the labels within their intervals. Apple (along with the rest of the world) puts labels like this at the start of each interval, but I refuse. Just because graphing software makes it easiest to do it that way doesn’t make it right.</p>\n<p>Overall, I prefer the horizontal graph with the yellow sunlight hours, but it was fun figuring out how to make an alternative.</p>"}, {"title": "Playing the percentages", "url": "https://leancrew.com/all-this/2026/01/playing-the-percentages/", "author": {"name": "Dr. Drang"}, "summary": "An interesting mathematical reversal by Donald Trump.", "date_published": "2026-01-22T21:19:27+00:00", "id": "https://leancrew.com/all-this/2026/01/playing-the-percentages/", "content_html": "<p>In his excessively long speech to the World Economic Forum yesterday in Davos, Donald Trump did some surprising backtracking. Not the stuff about <s>Iceland</s> Greenland, but on his method of calculating price reductions.</p>\n<p>For weeks—maybe months, time has been hard to judge this past year—Trump has been telling us that he’s worked out deals with pharmaceutical companies to lower their prices by several hundred percent. Commentators and comedians have pointed out that you can’t reduce prices more than 100% and pretty much left it at that, suggesting that Trump’s impossible numbers are due to ignorance.</p>\n<p>Don’t get me wrong. Trump’s ignorance is nearly limitless—but only nearly. I’ve always thought that he knew the right way to calculate a price drop; he did it the wrong way so he could quote a bigger number. And that came out in yesterday’s speech:</p>\n<iframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen=\"\" frameborder=\"0\" height=\"309\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/prdSj9caeQw?si=hEq5xeSBSNCJUjWK&amp;start=2827\" title=\"YouTube video player\" width=\"100%\"></iframe>\n<p>The embedding code is supposed to start the video just after the 47-minute mark. If it doesn’t, that’s where you should scroll to.</p>\n<p>If you can’t stand listening to him for even 15 seconds, here’s what he said:</p>\n<blockquote>\n<p>Under my most-favored nation policy for drug prices, the cost of prescription drugs is coming down by up to 90%, depending on the way you calculate. You could also say 5-, 6-, 7-, 800%. There are two ways of figuring that.</p>\n</blockquote>\n<p>Apparently, Trump or his staff decided that this particular audience wouldn’t swallow his usual percentage calculation, so he decided to do it the right way, even though he went on to defend his usual method. Trump has testified that his net worth is <a href=\"https://www.cnn.com/2021/04/01/politics/trump-statements-lawsuit-haunt/\">whatever he feels it should be</a> on a given day, so why wouldn’t there be more than one way to calculate a price drop?</p>\n<p>It’s hard to know what goes on in Donald Trump’s head, but I’m confident of two things:</p>\n<ol>\n<li>He <em>knows</em> that price increases and decreases are opposites. Therefore, if a price jump from $10 to $100 is a 900% increase, then a price drop from $100 to $10 must be a 900% decrease. It’s just logic.</li>\n<li>If you were selling him something and agreed to lower your price from $100 to $10, he would call it a 90% decrease, not a 900% decrease. If he were giving you the same discount (ha!), it would be a 900% decrease. The numbers he uses are whatever sound best to him at the time.</li>\n</ol>\n<p>Of course, the key thing about Trump’s deals with drug companies isn’t how percentages are calculated; it’s whether these deals will have <a href=\"https://www.npr.org/2026/01/16/nx-s1-5678915/trumprx-pharma-drug-price-deals-list-prices\">any real effect</a>.</p>"}, {"title": "Freezing pipes", "url": "https://leancrew.com/all-this/2026/01/freezing-pipes/", "author": {"name": "Dr. Drang"}, "summary": "Why frozen pipes burst. It might not be what you think.", "date_published": "2026-01-21T22:03:05+00:00", "id": "https://leancrew.com/all-this/2026/01/freezing-pipes/", "content_html": "<p>The Midwest is expected to have very cold temperatures this weekend, which got me thinking about burst water pipes. Water expands about 9% as it freezes, and a lot of people think it’s pressure from the outward expansion of ice that ruptures pipes, but it’s more complicated than that.</p>\n<p>My favorite reference for this phenomenon is <a href=\"https://www.ideals.illinois.edu/items/54859\">this 1996 paper</a> by Jeffrey R. Gordon of the Building Research Council at the University of Illinois. It’s entitled “An Investigation into Freezing and Bursting Water Pipes in Residential Construction,” and it goes through the testing that the BRC did on behalf of the Insurance Institute for Property Loss Reduction.</p>\n<p>Why do I have a favorite reference for burst pipes? I used to get hired by insurance companies to investigate burst pipes and the subsequent damage in high-end residences. When the pipe failed in a heated area, it was nice to have this paper to back up my explanation of how freezing in one part of a pipe can lead to failure in another. Also, the report is quite easy to read. There’s not a lot of jargon, and you don’t need much scientific or engineering background to understand it.</p>\n<p>Here’s Figure 15 from the report, which graphs the data collected during the testing of a 3/4″ copper pipe running through an unheated attic. The pipe was instrumented with thermocouples to capture its temperature at three locations and a pressure gauge to capture the water pressure inside it. The red annotations are mine. As you can see, the pressure rose to about 4,000 psi, at which point the pipe bulged—that’s the drop in pressure as the pipe increased in diameter and decreased in wall thickness—and then burst.</p>\n<p><img alt=\"Figure 15 from BRC report\" class=\"ss\" src=\"https://leancrew.com/all-this/images2026/20260121-Figure%2015%20from%20BRC%20report.png\" title=\"Figure 15 from BRC report\" width=\"100%\"/></p>\n<p>The best explanation of what happened in the test—and what commonly happens in real-world pipe bursts—comes from the report itself:</p>\n<blockquote>\n<p>This shows the central, and often least understood, fact about burst water pipes: freezing water pipes do not burst directly from physical pressure applied by growing ice, but from excessive water pressure. Before a complete ice blockage, the fact that water is freezing within a pipe does not, by itself, endanger the pipe. When a pipe is still open to the water system upstream, ice growth exerts no pressure on the pipe because the volumetric expansion caused by freezing is absorbed by the larger water system. A pipe that is open on one end cannot be pressurized, and thus will not burst.</p>\n<p>Once ice growth forms a complete blockage in a water pipe the situation changes dramatically. The downstream portion of the pipe, between the ice blockage and a closed outlet (faucet, shower, etc.) is now a confined pipe section. A pipe section that is closed on both ends can be dramatically pressurized, water being an essentially incompressible fluid. If ice continues to form in the confined pipe section, the volumetric expansion from freezing results in rapidly increasing water pressure between the blockage and the closed outlet. As figure 15 shows, the water pressure in a confined pipe section can build to thousands of pounds per square inch.</p>\n</blockquote>\n<p>Read that second paragraph again. It’s really a perfect explanation of how, for example, a pipe or joint under a bathroom sink can fail even though that part of the pipe was never exposed to freezing temperatures. It’s the ice buildup in the pipe elsewhere—typically in an unheated area on the other side of the drywall—and the continued growth of ice in the isolated zone between that blockage and the faucet at the sink that leads to a huge increase in pressure. The pipe will fail at whatever point is weakest within that zone.</p>\n<p>Which leads to some common advice given when temperatures are going to drop.</p>\n<ul>\n<li>For sinks up against an outside wall, leave the cabinet doors open so the warm air of the house gets a chance to circulate around the pipes. You want them to get as much exposure to warm air as possible so they can conduct that heat to the colder parts on the other side of the drywall.</li>\n<li>Open faucets that are against an outside wall and let them drip. I’ve never done this because I added extra insulation around and on the cold side of my pipes many years ago, but it can help. Not, as some people say, because moving water doesn’t freeze (have they never seen a river frozen over?) but because a slightly open valve prevents the buildup of pressure shown in Figure 15. As the report says, “A pipe that is open on one end cannot be pressurized, and thus will not burst.”</li>\n</ul>"}], "home_page_url": "https://leancrew.com/all-this/", "version": "https://jsonfeed.org/version/1", "icon": "https://leancrew.com/all-this/resources/snowman-200.png"}
