<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>And now it’s all this</title>
	<atom:link href="https://leancrew.com/all-this/feed/" rel="self" type="application/rss+xml" />
	<link>https://leancrew.com/all-this/</link>
	<description>I just said what I said and it was wrong. Or was taken wrong.</description>
	<lastBuildDate>Wed, 25 Feb 2026 15:13:32 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.0</generator>
  <atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/>
  <atom:link rel="hub" href="http://aniat.superfeedr.com"/>

<item>
<title>Lent and Lisp</title>
<link>https://leancrew.com/all-this/2026/02/lent-and-lisp/</link>
<pubDate>Wed, 25 Feb 2026 15:13:32 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/lent-and-lisp/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<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>
<pre><code>(load "calendrical.l")
</code></pre>
<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>
<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>
<pre><code>(in-package "CC4")

(export '(
          acre
          advent
          akan-day-name
          akan-day-name-on-or-before

          [and so on for a few hundred lines]

          yom-ha-zikkaron
          yom-kippur
          zone
          ))
</code></pre>
<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>
<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>
<pre><code> 1:  #! /usr/bin/env clisp -q
 2:  
 3:  ;; The edited Calendrical Calculations code by Reingold and Dershowitz
 4:  (load "calendar.lisp")
 5:  
 6:  ;; Names
 7:  (setq
 8:    weekday-names
 9:    '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")
10:  
11:    gregorian-month-names
12:    '("January" "February" "March" "April" "May" "June"
13:      "July" "August" "September" "October" "November" "December"))
14:  
15:  ;; Date string function
16:  (defun gregorian-date-string (date)
17:    (let ((g-date (gregorian-from-fixed date))
18:          (weekday (day-of-week-from-fixed date)))
19:      (format nil "~a, ~a ~d, ~d"
20:        (nth weekday weekday-names)
21:        (nth (1- (second g-date)) gregorian-month-names)
22:        (third g-date)
23:        (first g-date))))
24:  
25:  ;; Get today's (Gregorian) date.
26:  (multiple-value-setq
27:    (t-second t-minute t-hour t-day t-month t-year t-weekday t-dstp t-tz)
28:    (get-decoded-time))
29:    
30:  ;; Loop through 500 Islamic years, from 250 years ago to 250 years in
31:  ;; the future and find each Ramadan 1 that corresponds to Ash Wednesday.
32:  ;; Print as a Gregorian date.
33:  (setq
34:    f (fixed-from-gregorian (list t-year t-month t-day))
35:    ti-year (first (islamic-from-fixed f)))
36:  (dotimes (i 500)
37:    (setq iy (+ (- ti-year 250) i)
38:          r (fixed-from-islamic (list iy 9 1))
39:          g-year (gregorian-year-from-fixed r)
40:          aw (- (easter g-year) 46))
41:    (if (equal aw r)
42:      (format t "~a~%" (gregorian-date-string r))))
</code></pre>
<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>
<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>
<ul>
<li>learning how to handle the multiple value output of <code>get-decoded-time</code> (Lines 26–28);</li>
<li>remembering how to handle more than one variable assignment in <code>setq</code>; and</li>
<li>recognizing that what the ELisp calendar library calls “absolute” dates, the Common Lisp calendar library calls “fixed” dates.</li>
</ul>
<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>
<p>The output of <code>ramadan-lent</code> was</p>
<pre><code>Wednesday, February 6, 1799
Wednesday, February 24, 1830
Wednesday, February 22, 1928
Wednesday, February 18, 2026
Wednesday, March 7, 2057
Wednesday, February 16, 2124
Wednesday, March 5, 2155
Wednesday, February 13, 2222
Wednesday, March 2, 2253
</code></pre>
<p>The most common gap between successive correspondences was 98 years, but there were occasional gaps of 31 and 67 years.</p>
<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>
<pre><code> 1:  #! /usr/bin/env clisp -q
 2:  
 3:  ;; The edited Calendrical Calculations code by Reingold and Dershowitz
 4:  (load "calendar.lisp")
 5:  
 6:  ;; Names
 7:  (setq
 8:    weekday-names
 9:    '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")
10:  
11:    gregorian-month-names
12:    '("January" "February" "March" "April" "May" "June"
13:      "July" "August" "September" "October" "November" "December"))
14:  
15:  ;; Date string function
16:  (defun gregorian-date-string (date)
17:    (let ((g-date (gregorian-from-fixed date))
18:          (weekday (day-of-week-from-fixed date)))
19:      (format nil "~a, ~a ~d, ~d"
20:        (nth weekday weekday-names)
21:        (nth (1- (second g-date)) gregorian-month-names)
22:        (third g-date)
23:        (first g-date))))
24:  
25:  ;; Get today's (Gregorian) date.
26:  (multiple-value-setq
27:    (t-second t-minute t-hour t-day t-month t-year t-weekday t-dstp t-tz)
28:    (get-decoded-time))
29:    
30:  ;; Loop through 500 Islamic years, from 250 years ago to 250 years in
31:  ;; the future and find each Ramadan 1 that corresponds to Ash Wednesday
32:  ;; and Chinese New Year.
33:  ;; Print as a Gregorian date.
34:  (setq
35:    f (fixed-from-gregorian (list t-year t-month t-day))
36:    ti-year (first (islamic-from-fixed f)))
37:  (dotimes (i 500)
38:    (setq iy (+ (- ti-year 250) i)
39:          r (fixed-from-islamic (list iy 9 1))
40:          g-year (gregorian-year-from-fixed r)
41:          aw (- (easter g-year) 46))
42:    (if (equal aw r)
43:      (let ((ny (chinese-new-year-on-or-before r)))
44:        (if (equal ny (1- r))
45:          (format t "~a~%" (gregorian-date-string r))))))
</code></pre>
<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>
<p>This script’s output was</p>
<pre><code>Wednesday, February 6, 1799
Wednesday, February 18, 2026
Wednesday, February 16, 2124
Wednesday, February 13, 2222
</code></pre>
<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>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:sbcl">
<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>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>My OmniGraffle ticks</title>
<link>https://leancrew.com/all-this/2026/02/my-omnigraffle-ticks/</link>
<pubDate>Fri, 20 Feb 2026 17:40:43 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/my-omnigraffle-ticks/</guid>
<description>
  <![CDATA[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>:]]>
</description>
<content:encoded>
  <![CDATA[<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>
  <hr />
  <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>
<p><img alt="Radii relationship" class="ss" src="https://leancrew.com/all-this/images2026/20260219-Radii%20relationship.png" title="Radii relationship" width="50%"/></p>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<p><img alt="OmniGraffle arrows" class="ss" src="https://leancrew.com/all-this/images2026/20260220-OmniGraffle%20arrows.png" title="OmniGraffle arrows" width="50%"/></p>
<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>
<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>
<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>
<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>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:blue">
<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>
</li>
</ol>
</div>
  ]]>
</content:encoded>
</item>

<item>
<title>Easy to be hard</title>
<link>https://leancrew.com/all-this/2026/02/easy-to-be-hard/</link>
<pubDate>Fri, 20 Feb 2026 00:25:13 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/easy-to-be-hard/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
  <hr />
  <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>
<p>Here’s the puzzle:</p>
<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>
<blockquote>
<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>
</blockquote>
<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>
<p><img alt="Radii relationship" class="ss" src="https://leancrew.com/all-this/images2026/20260219-Radii%20relationship.png" title="Radii relationship" width="50%"/></p>
<p>From this drawing, we can express the width in two ways and set them equal to one another:</p>
<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>
<p>Multiplying through by <math xmlns="http://www.w3.org/1998/Math/MathML"><msqrt><mn>2</mn></msqrt></math> and rearranging, we get</p>
<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>
<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>
<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>
<p>This relationship also holds for any two consecutive circles,</p>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<p>converges, so our fraction, which has a larger denominator, must also converge.</p>
<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>
<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>
<p>so the sum of the circumferences is</p>
<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>
<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>
<pre><code>Pi/Sqrt[2] (1 + 2 Sum[1/(3 + 2 Sqrt[2])^i, {i, 1, Infinity}])
</code></pre>
<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>
<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>
<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>
<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>
<table>
<thead>
<tr>
<th align="center">n</th>
<th align="center">Sum</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">1</td>
<td align="center">0.94974747</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center">0.99137803</td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">0.99852070</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">0.99974619</td>
</tr>
<tr>
<td align="center">5</td>
<td align="center">0.99995645</td>
</tr>
<tr>
<td align="center">6</td>
<td align="center">0.99999253</td>
</tr>
<tr>
<td align="center">7</td>
<td align="center">0.99999872</td>
</tr>
<tr>
<td align="center">8</td>
<td align="center">0.99999978</td>
</tr>
<tr>
<td align="center">9</td>
<td align="center">0.99999996</td>
</tr>
<tr>
<td align="center">10</td>
<td align="center">0.99999999</td>
</tr>
</tbody>
</table>
<p>or as a plot,</p>
<p><img alt="Convergence plot" class="ss" src="https://leancrew.com/all-this/images2026/20260219-Convergence%20plot.png" title="Convergence plot" width="80%"/></p>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:trick">
<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>
</li>
</ol>
</div>
  ]]>
</content:encoded>
</item>

<item>
<title>Chinese New Year and Ramadan</title>
<link>https://leancrew.com/all-this/2026/02/chinese-new-year-and-ramadan/</link>
<pubDate>Wed, 18 Feb 2026 16:08:33 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/chinese-new-year-and-ramadan/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<pre><code>Gregorian:  Wednesday, February 18, 2026
      ISO:  Day 3 of week 8 of 2026
    Astro:  2461090
   Julian:  February 5, 2026
   Hebrew:  Adar 1, 5786
  Islamic:  Ramadan 1, 1447
  Chinese:  Cycle 78, year 43 (Bing-Wu), month 1 (Geng-Yin), day 2 (Gui-Hai)
</code></pre>
<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>
<pre><code>#!/opt/homebrew/bin/emacs --script

(require 'calendar)
(require 'cal-islam)
(require 'cal-china)

;; Loop through 300 Islamic years, roughly centered on this year
(setq iy 1300)
(while (&lt; iy 1600)
  ;; Get Ramadan 1 of the year as an absolute date
  (setq a (calendar-islamic-to-absolute (list 9 1 iy)))
  ;; Get the month and year of this date in the Chinese calendar
  (setq cdate (calendar-chinese-from-absolute a))
  (setq cmd (cdr (cdr cdate)))
  ;; Print the Gregorian date if Ramadan 1 is the day after Chinese New Year
  (if (equal cmd (list 1 2))
    (princ (concat 
             (calendar-date-string (calendar-gregorian-from-absolute a))
             "\n")))
  (setq iy (1+ iy)))
</code></pre>
<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>
<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>
<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>
<p>Here’s the script’s output:</p>
<pre><code>Wednesday, February 3, 1897
Monday, February 11, 1929
Friday, January 31, 1930
Tuesday, February 6, 1962
Saturday, January 26, 1963
Wednesday, February 1, 1995
Wednesday, February 18, 2026
Tuesday, February 3, 2060
Saturday, January 22, 2061
Wednesday, January 28, 2093
Wednesday, February 16, 2124
Friday, February 11, 2157
Tuesday, January 31, 2158
</code></pre>
<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>]]>
</content:encoded>
</item>

<item>
<title>Another Apple icon regression</title>
<link>https://leancrew.com/all-this/2026/02/another-apple-icon-regression/</link>
<pubDate>Tue, 10 Feb 2026 04:19:18 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/another-apple-icon-regression/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<p>and the split screen view, which is sort of like the old split view in iPadOS, but with more parts,</p>
<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>
<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>
<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>
<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>
<p>Kind of obvious where this button takes you, isn’t it?</p>
<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>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:brent">
<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>
</li>
<li id="fn:tidbits">
<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>
</li>
<li id="fn:claude">
<p>Of course, Apple wouldn’t use an asshole icon—that’s Anthropic’s branding. <a href="#fnref:claude" rev="footnote">↩</a></p>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>Pulling values from a graph without an LLM</title>
<link>https://leancrew.com/all-this/2026/02/pulling-values-from-a-graph-without-an-llm/</link>
<pubDate>Wed, 04 Feb 2026 15:48:11 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/pulling-values-from-a-graph-without-an-llm/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
  <hr />
  <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>
<p>As a reminder, here’s the graph:</p>
<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>
<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>
<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>
<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>
<pre><code>2006-10
2007-03
2007-06
2008-01
2008-03
2008-05
2008-09
2008-12
2009-04
2009-06
2009-09
2009-12
2010-02
2010-03
2010-06
2010-11
2011-02
2011-05
2015-06
2017-03
2024-03
2024-07
2024-11
2025-04
2025-05
2025-07
2025-10
2025-11
2026-01
</code></pre>
<div class="update">
<p><strong>Update 5 Feb 2026 12:11 AM</strong><br/>
Joe 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>
<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>
</div>
<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>
<pre><code>2006-10-15,
2007-03-15,
2007-06-15,
2008-01-15,
2008-03-15,
2008-05-15,
2008-09-15,
2008-12-15,
2009-04-15,
2009-06-15,
2009-09-15,
2009-12-15,
2010-02-15,
2010-03-15,
2010-06-15,
2010-11-15,
2011-02-15,
2011-05-15,
2015-06-15,
2017-03-15,
2024-03-15,
2024-07-15,
2024-11-15,
2025-04-15,
2025-05-15,
2025-07-15,
2025-10-15,
2025-11-15,
2026-01-15,
</code></pre>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<div class="update">
<p><strong>Update 5 Feb 2026 12:11 PM</strong><br/>
I 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>
</div>
<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>
<pre><code>applescript:
 1:  set yValues to {}
 2:  set yOrigin to 581
 3:  set yOffset to 13.2
 4:  set yScale to 64.075
 5:  
 6:  tell application "OmniGraffle"
 7:    tell layer "Markers" of canvas 1 of document 1
 8:      set diamonds to every shape
 9:      repeat with d in diamonds
10:        set pt to (origin of d)
11:        set end of yValues to (yOrigin - (item 2 of pt) - yOffset) / yScale
12:      end repeat
13:    end tell
14:  end tell
15:  
16:  yValues
</code></pre>
<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>
<p>Line 16 spits out the list of y-values in the Result section of Script Editor:</p>
<pre><code>{1.777610453442, 2.105119526221, 2.417138303752,
-2.823376834196, -2.217843888773, 0.804769157205,
-3.225882114947, -2.315093559097, 1.802782567774,
2.308216918434, 3.517346715278, 3.011460781549,
2.606423024043, 3.211060498316, 4.212510467041,
4.018455838485, 4.315198697655, 4.018455838485,
3.316423039057, 2.512942396277, 1.803414490836,
1.203973154221, -0.507963600878, -0.304047453731,
-0.80360682595, -2.016491975871, -1.812215148741,
-2.521036825178, -2.2390063608}
</code></pre>
<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>
<pre><code>Date,Hawk
2006-10-15,1.777610453442
2007-03-15,2.105119526221
2007-06-15,2.417138303752
2008-01-15,-2.823376834196
2008-03-15,-2.217843888773
2008-05-15,0.804769157205
2008-09-15,-3.225882114947
2008-12-15,-2.315093559097
2009-04-15,1.802782567774
2009-06-15,2.308216918434
2009-09-15,3.517346715278
2009-12-15,3.011460781549
2010-02-15,2.606423024043
2010-03-15,3.211060498316
2010-06-15,4.212510467041
2010-11-15,4.018455838485
2011-02-15,4.315198697655
2011-05-15,4.018455838485
2015-06-15,3.316423039057
2017-03-15,2.512942396277
2024-03-15,1.803414490836
2024-07-15,1.203973154221
2024-11-15,-0.507963600878
2025-04-15,-0.304047453731
2025-05-15,-0.80360682595
2025-07-15,-2.016491975871
2025-10-15,-1.812215148741
2025-11-15,-2.521036825178
2026-01-15,-2.2390063608
</code></pre>
<p>I still have the CSV file made by Claude. Let’s compare:</p>
<table>
<thead>
<tr>
<th align="center">Date</th>
<th align="right">Claude</th>
<th align="right">AppleScript</th>
<th align="right">Difference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">2006-10-15</td>
<td align="right">1.7</td>
<td align="right">1.78</td>
<td align="right">-0.08</td>
</tr>
<tr>
<td align="center">2007-03-15</td>
<td align="right">2.0</td>
<td align="right">2.11</td>
<td align="right">-0.11</td>
</tr>
<tr>
<td align="center">2007-06-15</td>
<td align="right">2.4</td>
<td align="right">2.42</td>
<td align="right">-0.02</td>
</tr>
<tr>
<td align="center">2008-01-15</td>
<td align="right">-2.9</td>
<td align="right">-2.82</td>
<td align="right">-0.08</td>
</tr>
<tr>
<td align="center">2008-03-15</td>
<td align="right">-2.2</td>
<td align="right">-2.22</td>
<td align="right">0.02</td>
</tr>
<tr>
<td align="center">2008-05-15</td>
<td align="right">0.8</td>
<td align="right">0.80</td>
<td align="right">-0.00</td>
</tr>
<tr>
<td align="center">2008-09-15</td>
<td align="right">-3.2</td>
<td align="right">-3.23</td>
<td align="right">0.03</td>
</tr>
<tr>
<td align="center">2008-12-15</td>
<td align="right">-2.5</td>
<td align="right">-2.32</td>
<td align="right">-0.18</td>
</tr>
<tr>
<td align="center">2009-04-15</td>
<td align="right">1.7</td>
<td align="right">1.80</td>
<td align="right">-0.10</td>
</tr>
<tr>
<td align="center">2009-06-15</td>
<td align="right">2.2</td>
<td align="right">2.31</td>
<td align="right">-0.11</td>
</tr>
<tr>
<td align="center">2009-09-15</td>
<td align="right">3.5</td>
<td align="right">3.52</td>
<td align="right">-0.02</td>
</tr>
<tr>
<td align="center">2009-12-15</td>
<td align="right">3.0</td>
<td align="right">3.01</td>
<td align="right">-0.01</td>
</tr>
<tr>
<td align="center">2010-02-15</td>
<td align="right">2.6</td>
<td align="right">2.61</td>
<td align="right">-0.01</td>
</tr>
<tr>
<td align="center">2010-03-15</td>
<td align="right">3.1</td>
<td align="right">3.21</td>
<td align="right">-0.11</td>
</tr>
<tr>
<td align="center">2010-06-15</td>
<td align="right">4.2</td>
<td align="right">4.21</td>
<td align="right">-0.01</td>
</tr>
<tr>
<td align="center">2010-11-15</td>
<td align="right">4.0</td>
<td align="right">4.02</td>
<td align="right">-0.02</td>
</tr>
<tr>
<td align="center">2011-02-15</td>
<td align="right">4.4</td>
<td align="right">4.32</td>
<td align="right">0.08</td>
</tr>
<tr>
<td align="center">2011-05-15</td>
<td align="right">4.1</td>
<td align="right">4.02</td>
<td align="right">0.08</td>
</tr>
<tr>
<td align="center">2015-06-15</td>
<td align="right">3.3</td>
<td align="right">3.32</td>
<td align="right">-0.02</td>
</tr>
<tr>
<td align="center">2017-03-15</td>
<td align="right">2.5</td>
<td align="right">2.51</td>
<td align="right">-0.01</td>
</tr>
<tr>
<td align="center">2024-03-15</td>
<td align="right">1.7</td>
<td align="right">1.80</td>
<td align="right">-0.10</td>
</tr>
<tr>
<td align="center">2024-07-15</td>
<td align="right">1.2</td>
<td align="right">1.20</td>
<td align="right">-0.00</td>
</tr>
<tr>
<td align="center">2024-11-15</td>
<td align="right">-0.5</td>
<td align="right">-0.51</td>
<td align="right">0.01</td>
</tr>
<tr>
<td align="center">2025-04-15</td>
<td align="right">-0.3</td>
<td align="right">-0.30</td>
<td align="right">0.00</td>
</tr>
<tr>
<td align="center">2025-05-15</td>
<td align="right">-0.6</td>
<td align="right">-0.80</td>
<td align="right">0.20</td>
</tr>
<tr>
<td align="center">2025-07-15</td>
<td align="right">-2.1</td>
<td align="right">-2.02</td>
<td align="right">-0.08</td>
</tr>
<tr>
<td align="center">2025-10-15</td>
<td align="right">-2.0</td>
<td align="right">-1.81</td>
<td align="right">-0.19</td>
</tr>
<tr>
<td align="center">2025-11-15</td>
<td align="right">-2.5</td>
<td align="right">-2.52</td>
<td align="right">0.02</td>
</tr>
<tr>
<td align="center">2026-01-15</td>
<td align="right">-2.3</td>
<td align="right">-2.24</td>
<td align="right">-0.06</td>
</tr>
</tbody>
</table>
<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>
<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>
<p>At the risk of lengthening this post even further, here’s the graph with the AppleScript-derived values:</p>
<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>
<p>And here’s the Python code that created it:</p>
<pre><code>python:
 1:  #!/usr/bin/env python3
 2:  
 3:  import pandas as pd
 4:  from datetime import datetime
 5:  import matplotlib.pyplot as plt
 6:  from matplotlib.ticker import MultipleLocator, AutoMinorLocator
 7:  from matplotlib.dates import DateFormatter, YearLocator, MonthLocator
 8:  
 9:  # Import the data
10:  df = pd.read_csv('warsh-applescript.csv', parse_dates=[0])
11:  x = df.Date
12:  y = df.Hawk
13:  
14:  # Create the plot with a given size in inches
15:  fig, ax = plt.subplots(figsize=(8, 5))
16:  
17:  # Add a line
18:  ax.plot(x, y, 'D-', color='#60150a', lw=3, ms=5)
19:  
20:  # Set the limits
21:  plt.xlim(xmin=datetime(2006,1,1), xmax=datetime(2026,12,31))
22:  plt.ylim(ymin=-4, ymax=5)
23:  
24:  # Set the major and minor ticks and add a grid
25:  ax.xaxis.set_major_locator(YearLocator(2))
26:  ax.xaxis.set_minor_locator(YearLocator(1))
27:  ax.xaxis.set_major_formatter(DateFormatter('%Y'))
28:  ax.yaxis.set_major_locator(MultipleLocator(1))
29:  ax.axhline(y=0, color='k', lw=.5)
30:  
31:  # Title and axis labels
32:  plt.title('Warsh commentary')
33:  plt.xlabel('')
34:  plt.ylabel('Monetary hawkishness')
35:  
36:  # Make the border and tick marks 0.5 points wide
37:  [ i.set_linewidth(0.5) for i in ax.spines.values() ]
38:  ax.tick_params(which='both', width=.5)
39:  
40:  # Reduce the whitespace around the plot
41:  plt.tight_layout()
42:  
43:  # Save as PNG
44:  plt.savefig('20260202-Warsh commentary via AppleScript.png', format='png', dpi=150)
</code></pre>
<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>
<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>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:reverse">
<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>
</li>
</ol>
</div>
  ]]>
</content:encoded>
</item>

<item>
<title>Plotting via Claude</title>
<link>https://leancrew.com/all-this/2026/02/plotting-via-claude/</link>
<pubDate>Tue, 03 Feb 2026 02:46:20 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/02/plotting-via-claude/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<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>
<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>
<blockquote>
<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>
</blockquote>
<p>Had the data been plotted correctly, you wouldn’t have to look carefully:</p>
<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>
<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>
<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>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:fix">
<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>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>On the average</title>
<link>https://leancrew.com/all-this/2026/01/on-the-average/</link>
<pubDate>Mon, 26 Jan 2026 14:42:09 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/01/on-the-average/</guid>
<description>
  <![CDATA[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:]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<p>Scrolling down, we come to this odd graphic: the average sunrise and sunset times for each month of the year.</p>
<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>
<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>
<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>
<p>After thinking about this for a while, I came to three conclusions:</p>
<ol>
<li>It’s nice to have on hand a simple display of how sunrise and sunset change over the course of a year.</li>
<li>I may be the only person bothered by the way Apple presents this information.</li>
<li>I’ve already shown how I’d like to see it done.</li>
</ol>
<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>
<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>
<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>
<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>
<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>
<p>Here’s the code that produced the plot above:</p>
<pre><code>python:
  1:  #!/usr/bin/env python3
  2:  
  3:  import sys
  4:  import re
  5:  from dateutil.parser import parse
  6:  from datetime import datetime
  7:  from datetime import timedelta
  8:  from matplotlib import pyplot as plt
  9:  import matplotlib.dates as mdates
 10:  from matplotlib.ticker import MultipleLocator, FormatStrFormatter
 11:  
 12:  
 13:  # Functions
 14:  
 15:  def headerInfo(header):
 16:    "Return location name, coordinates, and year from the USNO header lines."
 17:  
 18:    # Get the place name from the middle of the top line
 19:    left = 'o  ,    o  ,'
 20:    right = 'Astronomical Applications Dept.'
 21:    placeName = re.search(rf'{left}(.+){right}', header[0]).group(1).strip()
 22:  
 23:    # If the place name ends with a comma, a space, and a pair of capitals,
 24:    # assume it's in location, ST format and capitalize the location while
 25:    # keeping the state as all uppercase. Otherwise, capitalize all the words.
 26:    if re.match(r', [A-Z][A-Z]', placeName[-4:]):
 27:      placeParts = placeName.split(', ')
 28:      location = ', '.join(placeParts[:-1]).title()
 29:      state = placeParts[-1]
 30:      placeName = f'{location}, {state}'
 31:    else:
 32:      placeName = placeName.title()
 33:  
 34:    # The year is at a specific spot on the second line
 35:    year = int(header[1][80:84])
 36:  
 37:    # The latitude and longitude are at specific spots on the second line
 38:    longString = header[1][10:17]
 39:    latString = header[1][19:25]
 40:  
 41:    # Reformat the latitude into d° m′ N format (could be S)
 42:    dir = latString[0]
 43:    degree, minute = latString[1:].split()
 44:    lat = f'{int(degree)}° {int(minute)}′ {dir}'
 45:  
 46:    # Reformat the longitude into d° m′ W format
 47:    dir = longString[0]
 48:    degree, minute = longString[1:].split()
 49:    long = f'{int(degree)}° {int(minute)}′ {dir}'
 50:  
 51:    return placeName, lat, long, year
 52:  
 53:  def bodyInfo(body, isLeap):
 54:    "Return lists of sunrise, sunset, and daylight length hours from the USNO body lines."
 55:  
 56:    # Initialize
 57:    sunrises = []
 58:    sunsets = []
 59:    lengths = []
 60:  
 61:    # Rise and set character start positions for each month
 62:    risePos = [ 4 + 11*i for i in range(12) ]
 63:    setPos = [ 9 + 11*i for i in range(12) ]
 64:  
 65:    # Collect data from each day
 66:    for m in range(12):
 67:      for d in range(daysInMonth[m]):
 68:        riseString = body[d][risePos[m]:risePos[m]+4]
 69:        hour, minute = int(riseString[:2]), int(riseString[-2:])
 70:        sunrise = hour + minute/60
 71:        setString = body[d][setPos[m]:setPos[m]+4]
 72:        hour, minute = int(setString[:2]), int(setString[-2:])
 73:        sunset = hour + minute/60
 74:        sunrises.append(sunrise)
 75:        sunsets.append(sunset)
 76:        lengths.append(sunset - sunrise)
 77:  
 78:    return(sunrises, sunsets, lengths)
 79:  
 80:  def dstBounds(year):
 81:    "Return the DST start and end day indices according to current US rules."
 82:  
 83:    # Start DST on second Sunday of March
 84:    d = 8
 85:    while datetime.weekday(dstStart := datetime(year, 3, d)) != 6:
 86:      d += 1
 87:    dstStart = (dstStart - datetime(year, 1, 1)).days
 88:  
 89:    # End DST on first Sunday of November
 90:    d = 1
 91:    while datetime.weekday(dstEnd := datetime(year, 11, d)) != 6:
 92:      d += 1
 93:    dstEnd = (dstEnd - datetime(year, 1, 1)).days
 94:  
 95:    return dstStart, dstEnd
 96:  
 97:  
 98:  # Start processing
 99:  
100:  # Read the USNO data from stdin into a list of lines.
101:  # Text should come from https://aa.usno.navy.mil/data/RS_OneYear
102:  usno = sys.stdin.readlines()
103:  
104:  # Get location and year from header
105:  placeName, lat, long, year = headerInfo(usno[:2])
106:  
107:  # Month information, adjusted for leap year if needed.
108:  monthNames = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split()
109:  isLeap = (year % 400 == 0) or ((year % 4 == 0) and not (year % 100 == 0))
110:  if isLeap:
111:    daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
112:  else:
113:    daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
114:  
115:  # Get sunrise, sunset, and sunlight length lists from body
116:  sunrises, sunsets, lengths = bodyInfo(usno[9:], isLeap)
117:  
118:  # Generate list of days for the year
119:  currentDay = datetime(year, 1, 1)
120:  lastDay = datetime(year, 12, 31)
121:  days = [currentDay]
122:  while (currentDay := currentDay + timedelta(days=1)) &lt;= lastDay:
123:    days.append(currentDay)
124:  
125:  # The portion of the year that uses DST
126:  dstStart, dstEnd = dstBounds(year)
127:  dstDays = days[dstStart:dstEnd + 1]
128:  dstRises = [ x + 1 for x in sunrises[dstStart:dstEnd + 1] ]
129:  dstSets = [ x + 1 for x in sunsets[dstStart:dstEnd + 1] ]
130:  
131:  # Plot the data
132:  fig, ax =plt.subplots(figsize=(6,10))
133:  
134:  # Shaded areas
135:  plt.fill_betweenx(days, sunrises, sunsets, facecolor='gray', alpha=.5)
136:  plt.fill_betweenx(days, 0, sunrises, facecolor='black', alpha=.75)
137:  plt.fill_betweenx(days, sunsets, 24, facecolor='black', alpha=.75)
138:  plt.fill_betweenx(dstDays, sunsets[dstStart:dstEnd + 1], dstSets, facecolor='white', alpha=.5)
139:  plt.fill_betweenx(dstDays, sunrises[dstStart:dstEnd + 1], dstRises, facecolor='black', alpha=.16)
140:  
141:  # Curves
142:  plt.plot(sunrises, days, color='k')
143:  plt.plot(sunsets, days, color='k')
144:  plt.plot(dstRises, dstDays, color='k')
145:  plt.plot(dstSets, dstDays, color='k')
146:  
147:  # Background grids
148:  ax.grid(which='major', color='#ccc', ls='-', lw=.5)
149:  ax.grid(which='minor', color='#ddd', ls=':', lw=.5)
150:  
151:  # Vertical axis grid at month boundaries
152:  # ax.tick_params(axis='both', which='major', labelsize=12)
153:  plt.ylim(datetime(year, 1, 1), datetime(year, 12, 31))
154:  plt.tick_params(axis='y', length=0)
155:  m = mdates.MonthLocator(bymonthday=1)
156:  mfmt = mdates.DateFormatter('')
157:  ax.yaxis.set_major_locator(m)
158:  ax.yaxis.set_major_formatter(mfmt)
159:  ax.yaxis.set_inverted(True)
160:  
161:  # Month labels inside the plot in white letters
162:  for m in range(12):
163:    middle = sum(daysInMonth[:m]) + daysInMonth[m]//2
164:    ax.text(.5, days[middle], monthNames[m], fontsize=12, color='w', ha='left', va='center')
165:  
166:  # Horizontal axis labels formatted like h:mm
167:  plt.xlim(0, 24)
168:  xmajor = MultipleLocator(4)
169:  xminor = MultipleLocator(1)
170:  ax.xaxis.set_major_locator(xmajor)
171:  ax.xaxis.set_minor_locator(xminor)
172:  plt.xticks(ticks=[0, 4, 8, 12, 16, 20, 24], labels=['mid', '4:00', '8:00', 'noon', '4:00', '8:00', 'mid'])
173:  
174:  # Tighten up the white border and save
175:  fig.set_tight_layout({'pad': 1.5})
176:  plt.savefig(f'{placeName}-{year}.png', format='png', dpi=150)
</code></pre>
<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>
<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>
<p>Overall, I prefer the horizontal graph with the yellow sunlight hours, but it was fun figuring out how to make an alternative.</p>]]>
</content:encoded>
</item>

<item>
<title>Playing the percentages</title>
<link>https://leancrew.com/all-this/2026/01/playing-the-percentages/</link>
<pubDate>Thu, 22 Jan 2026 21:19:27 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/01/playing-the-percentages/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<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>
<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>
<p>If you can’t stand listening to him for even 15 seconds, here’s what he said:</p>
<blockquote>
<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>
</blockquote>
<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>
<p>It’s hard to know what goes on in Donald Trump’s head, but I’m confident of two things:</p>
<ol>
<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>
<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>
</ol>
<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>]]>
</content:encoded>
</item>

<item>
<title>Freezing pipes</title>
<link>https://leancrew.com/all-this/2026/01/freezing-pipes/</link>
<pubDate>Wed, 21 Jan 2026 22:03:05 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/01/freezing-pipes/</guid>
<description>
  <![CDATA[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.]]>
</description>
<content:encoded>
  <![CDATA[<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>
<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>
<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>
<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>
<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>
<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>
<blockquote>
<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>
<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>
</blockquote>
<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>
<p>Which leads to some common advice given when temperatures are going to drop.</p>
<ul>
<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>
<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>
</ul>]]>
</content:encoded>
</item>

<item>
<title>Belaying follow-up</title>
<link>https://leancrew.com/all-this/2026/01/belaying-follow-up/</link>
<pubDate>Tue, 20 Jan 2026 22:46:18 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/01/belaying-follow-up/</guid>
<description>
  <![CDATA[At the end of last week’s <a href="https://leancrew.com/all-this/2026/01/fixing-a-physics-problem/">belaying post</a>, I said we’d look into the condition in which the climber and belayer don’t weigh the same. And we will. But first, I want to talk about stretchy ropes.]]>
</description>
<content:encoded>
  <![CDATA[<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/01/belaying-follow-up/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>At the end of last week’s <a href="https://leancrew.com/all-this/2026/01/fixing-a-physics-problem/">belaying post</a>, I said we’d look into the condition in which the climber and belayer don’t weigh the same. And we will. But first, I want to talk about stretchy ropes.</p>
<p>One of my assumptions in the last solution was that the rope connecting the climber and the belayer was inextensible. Now, no ropes are truly inextensible, but I thought it was a reasonable assumption, at least for a first-pass solution. But Rob Nee (via <a href="https://fosstodon.org/@robnee/115896237545472093">Mastodon</a>), Dirk KS (also via <a href="https://fediscience.org/@DirkK/115897675374440059">Mastodon</a>), and Kenneth Prager (via email) thought the extensibility of climbing ropes was worth discussing. Dirk KS even sent me a link to <a href="https://www.samsonrope.com/resources/arborist/climbing-line-comparison">the Samson Rope Technologies page</a> that includes this nice graph of its ropes’ compliance:</p>
<p><img alt="Samson rope compliance graph" class="ss" src="https://leancrew.com/all-this/images2026/20260120-Samson%20rope%20compliance%20graph.jpg" title="Samson rope compliance graph" width="100%"/></p>
<p>(If you’re an engineer, you’re used to seeing load-deflection curves with the load on the vertical axis and deflection on the horizontal axis. Be aware that this one is drawn the opposite way.)</p>
<p>I can certainly understand why climbers consider their ropes’ compliance to be important: it makes for a more gentle stop at the end of a fall. For our purposes, what’s important is the potential energy absorbed by the rope as it stretches. If the rope is linearly elastic, the potential energy in a rope that extends by an amount <em>x</em> from its natural length is</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>k</mi><msup><mi>x</mi><mn>2</mn></msup><mo>=</mo><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>F</mi><mi>x</mi></mrow></math>
<p>where <em>k</em> is the stiffness of the rope and <em>F</em> is the tension. The Samson graph tells us that their ropes <em>are</em> linearly elastic for loads of up to about 400 lbs, so let’s use this equation and see where it gets us.</p>
<p>You may recall that Prof. Allain <a href="https://rjallain.medium.com/real-world-physics-conservation-of-energy-in-a-belay-fall-49bd474e794d">did some motion analysis of the video</a> and came to the conclusion that the climber was about 7.2 m above the belayer when he started his fall. I took this value and some scaling of the video frames I showed in the previous post to estimate the following values:</p>
<ul>
<li>Length of rope (<em>L</em>): 24 ft.</li>
<li>Height of loop (<em>h</em>): 18 ft.</li>
<li>Initial distance of climber above loop (<em>d</em>): 6 ft.</li>
</ul>
<p>I’ll also assume the climber and belayer each weigh 150 lbs. These are rough, rounded estimates because I don’t feel greater precision is warranted.</p>
<p>When the climber and belayer come to a halt and are just hanging there, the tension in the rope is equal to each man’s weight: 150 lbs. According to the Samson chart, the more compliant ropes will stretch about 0.75% under this load, which means the rope will stretch</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mo form="prefix" stretchy="false">(</mo><mn>0.0075</mn><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>24</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mn>0.18</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow></mrow></math>
<p>So the elastic energy in the rope is</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mo form="prefix" stretchy="false">(</mo><mn>150</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">l</mi><mi mathvariant="normal">b</mi></mrow><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>0.18</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mn>13.5</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">l</mi><mi mathvariant="normal">b</mi><mo mathvariant="normal">⋅</mo><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow></mrow></math>
<p>The gravitational potential energy lost during the initial drop of twelve feet by the climber (which is when the rope first becomes taut) is</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mn>2</mn><mspace width="0.167em"></mspace><mi>m</mi><mi>g</mi><mi>d</mi><mo>=</mo><mn>2</mn><mo form="prefix" stretchy="false">(</mo><mn>150</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">l</mi><mi mathvariant="normal">b</mi></mrow><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>6</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mn>1</mn><mo>,</mo><mn>800</mn><mspace width="0.278em"></mspace><mrow><mi mathvariant="normal">l</mi><mi mathvariant="normal">b</mi><mo mathvariant="normal">⋅</mo><mi mathvariant="normal">f</mi><mi mathvariant="normal">t</mi></mrow></mrow></math>
<p>The energy that goes into stretching the rope is small potatoes compared to this, so it’s not unreasonable to ignore it in our energy accounting system.</p>
<p>Let’s move on to consider unequal weights. We’ll say the climber has a mass of <em>m</em>, as before, but now the mass of the belayer is <em>βm</em>. As before, the potential energy before the climber falls is</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><msub><mi>U</mi><mn>0</mn></msub><mo>=</mo><mi>m</mi><mi>g</mi><mo form="prefix" stretchy="false">(</mo><mi>h</mi><mo>+</mo><mi>d</mi><mo form="postfix" stretchy="false">)</mo></mrow></math>
<p>and there’s no kinetic energy: <math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><msub><mi>K</mi><mn>0</mn></msub><mo>=</mo><mn>0</mn></mrow></math></p>
<p><img alt="Initial position" class="ss" src="https://leancrew.com/all-this/images2026/20260114-Initial%20position.png" title="Initial position" width="40%"/></p>
<p>When the rope becomes taut (we’re assuming an inextensible rope again), the potential energy has dropped to</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><msub><mi>U</mi><mn>1</mn></msub><mo>=</mo><mi>m</mi><mi>g</mi><mo form="prefix" stretchy="false">(</mo><mi>h</mi><mo>−</mo><mi>d</mi><mo form="postfix" stretchy="false">)</mo></mrow></math>
<p>so the kinetic energy must be</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><msub><mi>K</mi><mn>1</mn></msub><mo>=</mo><msub><mi>U</mi><mn>0</mn></msub><mo>−</mo><msub><mi>U</mi><mn>1</mn></msub><mo>=</mo><mn>2</mn><mi>m</mi><mi>g</mi><mi>d</mi></mrow></math>
<p><img alt="When rope first gets taut" class="ss" src="https://leancrew.com/all-this/images2026/20260114-When%20rope%20first%20gets%20taut.png" title="When rope first gets taut" width="40%"/></p>
<p>From this point on, the rope keeps climber A and belayer B moving in concert, so</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><msub><mi>y</mi><mi>A</mi></msub><mo>=</mo><mi>−</mi><mi mathvariant="normal">Δ</mi><msub><mi>y</mi><mi>B</mi></msub></mrow></math>
<p>and the change in potential energy will be</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><mi>U</mi><mo>=</mo><mi>β</mi><mi>m</mi><mi>g</mi><mi mathvariant="normal">Δ</mi><msub><mi>y</mi><mi>B</mi></msub><mo>+</mo><mi>m</mi><mi>g</mi><mi mathvariant="normal">Δ</mi><msub><mi>y</mi><mi>A</mi></msub><mo>=</mo><mi>m</mi><mi>g</mi><mo form="prefix" stretchy="false">(</mo><mi>β</mi><mo>−</mo><mn>1</mn><mo form="postfix" stretchy="false">)</mo><msub><mi>y</mi><mi>B</mi></msub></mrow></math>
<p><img alt="After belayer rises" class="ss" src="https://leancrew.com/all-this/images2026/20260114-After%20belayer%20rises.png" title="After belayer rises" width="40%"/></p>
<p>Ignoring frictional losses, the change in kinetic energy will be</p>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><mi>K</mi><mo>=</mo><mi>−</mi><mi mathvariant="normal">Δ</mi><mi>U</mi><mo>=</mo><mi>m</mi><mi>g</mi><mo form="prefix" stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>β</mi><mo form="postfix" stretchy="false">)</mo><msub><mi>y</mi><mi>B</mi></msub></mrow></math>
<p>Now we have three situations to consider:</p>
<ol>
<li><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>β</mi><mo>&lt;</mo><mn>1</mn></mrow></math> means <math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><mi>K</mi><mo>&gt;</mo><mn>0</mn></mrow></math> and (without friction) the movement never stops.</li>
<li><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>β</mi><mo>=</mo><mn>1</mn></mrow></math> means <math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><mi>K</mi><mo>=</mo><mn>0</mn></mrow></math> and (without friction) the movement never stops.</li>
<li><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>β</mi><mo>&gt;</mo><mn>1</mn></mrow></math> means <math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi mathvariant="normal">Δ</mi><mi>K</mi><mo>&lt;</mo><mn>0</mn></mrow></math> and the movement might stop, at least momentarily, even without friction.</li>
</ol>
<p>The second condition is what we covered last time, so I won’t repeat that.</p>
<p>For the third condition, where the belayer weighs more, the system might stop momentarily with the belayer at his highest point and the climber at his lowest, even if there’s no friction in the system. If and where this happens depends on the specific values of <em>β</em>, <em>h</em>, and <em>d</em>. The key concern is whether the climber hits the ground before this momentary stop occurs.</p>
<p>To me, the first condition is the most interesting. It says that the system won’t stop if the belayer weighs less than the climber. In fact, if there’s no friction in the system, the climber would go down to the ground even if the two started hanging statically at the same elevation. But the fact is, lighter belayers <em>can</em> safely control heavier climbers (Kenneth Prager told me he often belays his son, who outweighs him by over 50 lbs). You can see that in this video from the American Alpine Club:</p>
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/BOIAYx-d4HE?si=PboZLh97HMMMpaoN" title="YouTube video player" width="100%"></iframe>
<p>In this video, the rope is running through several carabiners; in the subject video, it appears to be running through just one. Either way, this way of using the friction of a rope running over a stationary curved surface to control movement has been known for ages. It’s called the <em>capstan problem</em>, and it’s covered in the friction section of most elementary engineering mechanics classes.</p>
<div class="update">
<p><strong>Update 20 Jan 2026 5:34 PM</strong><br/>
Gus Mueller (developer of <a href="https://flyingmeat.com/acorn/">Acorn</a>) is a climber, and he <a href="https://leancrew.com/all-this/2026/01/fixing-a-physics-problem/">showed me</a> that the rope in the subject video passes through two carabiners, not one. I have the top one identified correctly; the lower one is about a body length below the top one. The second carabiner doesn’t affect any of the calculations, but it does give another place for friction to do its job.</p>
</div>
<p>At this point, longtime readers are expecting me to go through the capstan problem, but <em>really</em> longtime readers know <a href="https://leancrew.com/all-this/2010/04/aye-aye-capstan/">I already did that</a> back in 2010. Oddly enough, that post was inspired by my acting as a belayer at my younger son’s school while his fourth-grade gym class did a rock-climbing unit. That son will be visiting me this weekend; he’s coming into town on a business trip for client meetings in the area early next week. I’m still trying to figure out how a fourth-grader has clients.</p>
  ]]>
</content:encoded>
</item>

<item>
<title>Resting heart rate and the Apple Watch</title>
<link>https://leancrew.com/all-this/2026/01/resting-heart-rate-and-the-apple-watch/</link>
<pubDate>Fri, 16 Jan 2026 02:31:47 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/01/resting-heart-rate-and-the-apple-watch/</guid>
<description>
  <![CDATA[My apologies to those of you hoping for another pulse-pounding post on <a href="https://leancrew.com/all-this/2026/01/fixing-a-physics-problem/">the mechanics of belaying</a>, but I’m going to talk about Apple again. The belaying follow-up should come out this weekend.]]>
</description>
<content:encoded>
  <![CDATA[<p>My apologies to those of you hoping for another pulse-pounding post on <a href="https://leancrew.com/all-this/2026/01/fixing-a-physics-problem/">the mechanics of belaying</a>, but I’m going to talk about Apple again. The belaying follow-up should come out this weekend.</p>
<p>What prompted this post was being chastised by my phone this morning for getting only about an hour of sleep last night. This was unwarranted. What actually happened was that I fell asleep for maybe an hour and a half while reading last night. When I woke up, I took off my watch, put it on the charger, and got ready for bed. I slept pretty well.</p>
<p>I could take this opportunity to say that since my watch knew damned well that it was on the charger instead of my wrist overnight, it should have shared that information with my phone and told it not to scold me. But my expectations of Apple software have been lowered to the point where that wasn’t even a blip on my radar. It did remind me, though, that I’ve been meaning to write about errors the Apple Watch makes in measuring resting heart rate.</p>
<p>Note the word <em>resting</em>. I think the watch’s heart rate monitor itself does a good job. Whenever I’ve checked its reading against a manual pulse count, the two are very close—close enough that the difference could be a real minute-to-minute variation. But the <em>resting</em> heart rate is supposed to be… well, here’s what Apple says in the Health app:</p>
<blockquote>
<p>Your resting heart rate is the average heart beats per minute measured when you’ve been inactive or relaxed for several minutes. A lower resting heart rate typically indicates better heart health and cardiovascular fitness. An increase in resting heart rate at times may be normal and expected, such as during an illness or a pregnancy.</p>
<p>You may be able to lower your resting heart rate over time by staying active, managing your weight, and reducing everyday stress. <em>Resting heart rate does not include your heart rate while you’re asleep</em> and is validated for users over the age of 18.</p>
</blockquote>
<p>Emphasis mine.</p>
<p>So the resting heart rate is based not only on the watch’s heart rate monitor, which, as I just said, is quite good, but also on its assessment of whether you’re sleeping or not. Like Santa Claus, the Apple Watch sees you when you’re sleeping and knows when you’re awake. Or does it?</p>
<p>I’ve had an Apple Watch since the Series 3, and over most of that time I’ve taken the watch off when I go to bed and let it charge overnight. In July of 2024, though, I started a diet program to control my Type 2 diabetes. The diet program included an app that wanted the sleep data from my watch, so I started wearing it overnight.</p>
<p>I left the program about a year ago because my insurance changed and my new plan wouldn’t pay for it. Don’t worry, I’m still eating right and my A1C has continued to go down. And I kept wearing my watch overnight because I was in the habit of doing so.</p>
<p>Then came iOS and watchOS 26 this fall and with them came the Sleep Score, which was on by default after the upgrade. My Sleep Scores stunk because I am a gentleman of a certain age, and I get up to pee overnight—sometimes twice. This was annoying, so my first reaction was to remove the Sleep Score from the Pinned section of the Health app’s Summary. But then I thought, <em>Why am I still wearing my watch overnight?</em> Doing so means I have to find a time during the day to charge the watch, and it’s much easier to just charge it overnight and wear it all day.</p>
<p>So I started wearing my watch overnight in July of 2024 and went back to not wearing it overnight in November of 2025. Let’s take a peek at my resting heart rate for the six-month periods surrounding those months.</p>
<p><img alt="My resting heartrate decreases in July 2024" class="ss" src="https://leancrew.com/all-this/images2026/20260115-My%20resting%20heartrate%20decreases%20in%20July%202024.png" title="My resting heartrate decreases in July 2024" width="50%"/></p>
<p><img alt="My resting heartrate increases in November 2025" class="ss" src="https://leancrew.com/all-this/images2026/20260115-My%20resting%20heartrate%20increases%20in%20November%202025.png" title="My resting heartrate increases in November 2025" width="50%"/></p>
<p>Oh, look! My heart rate went from the mid-50s down to the high-40s in July 2024 and from the high-40s to the low-50s in November 2025. What a coincidence!</p>
<p>What really happened, of course, was that the watch was treating some of my sleep time as awake-but-relaxed time and was counting my heart rate during those periods as resting. The Apple Watch is not as perceptive as Santa Claus.</p>
<p>So if you’ve always worn your watch overnight because you like getting sleep statistics from it, recognize that it may be giving you lower readings for your resting heart rate. If that matters to you, you may want to sleep without your watch for a while to see if you get a different resting heart rate.</p>]]>
</content:encoded>
</item>

</channel>
</rss>

