<?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, 11 Nov 2026 20:34:19 +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>Steve, Monty, and Julia</title>
<link>https://leancrew.com/all-this/2026/11/steve-monty-and-julia/</link>
<pubDate>Wed, 11 Nov 2026 20:34:19 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/11/steve-monty-and-julia/</guid>
<description>
  <![CDATA[I don’t make a habit of reading obituaries, but I’m glad I clicked the link to <a href="https://www.nytimes.com/2026/11/10/obituaries/steve-selvin-dead.html?unlocked_article_code=1.PlE.9nGH.gb9j1EYSKKB-&amp;smid=nytcore-ios-share">this one</a> in yesterday’s <em>New York Times</em> (that should be a gift link). Steve Selvin was a statistician who got his Ph.D. at the University of California, Berkeley, and taught there for many years. He got into the <em>Times</em> because of a probability puzzle he came up with in 1975: the <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Monty Hall Problem</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/11/steve-monty-and-julia/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>I don’t make a habit of reading obituaries, but I’m glad I clicked the link to <a href="https://www.nytimes.com/2026/11/10/obituaries/steve-selvin-dead.html?unlocked_article_code=1.PlE.9nGH.gb9j1EYSKKB-&amp;smid=nytcore-ios-share">this one</a> in yesterday’s <em>New York Times</em> (that should be a gift link). Steve Selvin was a statistician who got his Ph.D. at the University of California, Berkeley, and taught there for many years. He got into the <em>Times</em> because of a probability puzzle he came up with in 1975: the <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Monty Hall Problem</a>.</p>
<p>I won’t explain the problem here. Most of you already know it, and those who don’t can read the setup in the <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Wikipedia article</a>. I do want to praise the <em>Times</em> for the cute little interactive game they included in Selvin’s obituary. It allows you to be a <em>Let’s Make a Deal</em> contestant and run through the problem again and again (with your computer playing the Monty Hall role) until you’re convinced that the “switching my choice” strategy is distinctly better than the “sticking with my original choice” strategy. I doubt you have the patience to play it enough times to confirm that switching wins 2/3 of the time and sticking wins 1/3 of the time.</p>
<p>I was less pleased with the explanation the <em>Times</em> gave for the probabilities:</p>
<blockquote>
<p>After the first reveal, you have two doors, so you should have a 50-50 chance. Right? But you don’t. The host hasn’t randomly eliminated a door; he has deliberately revealed a goat, using his knowledge of where the car is. Your original door still has a one-in-three chance of hiding the car, while switching gives you a two-in-three chance of winning.</p>
</blockquote>
<p>It’s right, of course, but it doesn’t strike me as an actual explanation—it sounds more like a restatement of the answer. To me, the way to think of it is to consider the winning strategy:</p>
<blockquote>
<p>If you chose the door with a car behind it on your first guess—which will happen 1/3 of the time—you will win by sticking with that door. If you chose one of the doors with a goat behind it on your first guess—which will happen 2/3 of the time—you will win by switching to the door that Monty didn’t open. Therefore, switching gives you a 2/3 chance of winning.</p>
</blockquote>
<p>Everyone’s different, so maybe you don’t need those extra words and the <em>Times</em> explanation is sufficient. Not for me, though.</p>
<p>I mentioned above that although you could play the game in the obituary enough times to get a decent estimate of the probabilities, most of us don’t have that kind of patience. The better way to run lots of simulations in a short time is to use a random number generator to have the computer do all the selecting. I’ve been meaning to try out the <a href="https://julialang.org/">Julia programming language</a> for quite a while; this seemed like a good problem to start with.</p>
<p>Here’s my script, <code>switch.jl</code>, for running 10,000 simulations using the “switch” strategy:</p>
<pre><code> 1:  #!/usr/bin/env julia
 2:  
 3:  # Parameters
 4:  N = 10_000    # Number of trials
 5:  m = 0         # Number of wins
 6:  
 7:  # Loop through N trials, counting the number of wins
 8:  for i in 1:N
 9:    # Behind-the-scenes preparation
10:    carDoor = rand(1:3)
11:    goatDoors = [j for j in 1:3 if j != carDoor]
12:    
13:    # Player makes initial choice
14:    playerDoor = rand(1:3)
15:    
16:    # Monty chooses an available goat door
17:    if playerDoor == carDoor
18:      # Pick either of the two available
19:      montyDoor = goatDoors[rand(1:2)]
20:    else
21:      # Pick the one available
22:      montyDoor = [j for j in goatDoors if j != playerDoor][1]
23:    end
24:    
25:    # Player switches to the other door
26:    playerDoor = [j for j in 1:3 if (j != montyDoor) &amp;&amp; (j != playerDoor)][1]
27:    
28:    # Increment m if the player wins
29:    if playerDoor == carDoor
30:      global m += 1
31:    end
32:  end
33:  
34:  # Print the winning fraction for this strategy
35:  println(m/N)
</code></pre>
<p>Because I’m new to Julia, I put in lots of comments, so I doubt there’s much need for more explanation. I will say, though, that I suspect a real Julia programmer wouldn’t use list comprehensions as often as I did. There’s probably a function for removing items from a list that’s more elegant than what I did in Lines 11, 22, and 26. I just don’t know what that is yet.</p>
<p>My script for running 10,000 simulations of the “stick” strategy, cleverly named <code>stick.jl</code>, is basically the same:</p>
<pre><code> 1:  #!/usr/bin/env julia
 2:  
 3:  # Parameters
 4:  N = 10_000    # Number of trials
 5:  m = 0         # Number of wins
 6:  
 7:  # Loop through the N trials, counting the number of wins
 8:  for i in 1:N
 9:    # Behind-the-scenes preparation
10:    carDoor = rand(1:3)
11:    goatDoors = [j for j in 1:3 if j != carDoor]
12:    
13:    # Player makes initial choice
14:    playerDoor = rand(1:3)
15:    
16:    # Monty chooses an available goat door
17:    if playerDoor == carDoor
18:      # Pick either of the two available
19:      montyDoor = goatDoors[rand(1:2)]
20:    else
21:      # Pick the one available
22:      montyDoor = [j for j in goatDoors if j != playerDoor][1]
23:    end
24:    
25:    # Player sticks with original choice; do nothing
26:    playerDoor = playerDoor
27:    
28:    # Increment m if the player wins
29:    if playerDoor == carDoor
30:      global m += 1
31:    end
32:  end
33:  
34:  # Print the winning fraction for this strategy
35:  println(m/N)
</code></pre>
<p>The only difference is in Lines 25–26, where I’m basically doing a no-op.</p>
<p>These scripts give different answers each time they’re run because the random number generator starts with a different seed, but the answers are always close to 0.67 for <code>switch.jl</code> and 0.33 for <code>stick.jl</code>. As expected.</p>
<p>Are 10,000 simulations enough? Well, the 95% confidence interval can be calculated through this formula:</p>
<p><!-- $$ \pm 1.96\, \sqrt{\frac{p \, (1 - p)}{N}} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>±</mi><mn>1.96</mn><mspace width="0.167em"></mspace><msqrt><mfrac><mrow><mi>p</mi><mspace width="0.167em"></mspace><mo form="prefix" stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>p</mi><mo form="postfix" stretchy="false">)</mo></mrow><mi>N</mi></mfrac></msqrt></mrow></math></p>
<p>which works out to ±0.009 for both strategies. So 10,000 simulations is fine as long as we’re willing to accept the second decimal place to be off by one every now and then.</p>
  ]]>
</content:encoded>
</item>

<item>
<title>Images, the clipboard, and AppleScript</title>
<link>https://leancrew.com/all-this/2026/11/images-the-clipboard-and-applescript/</link>
<pubDate>Tue, 10 Nov 2026 00:18:11 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/11/images-the-clipboard-and-applescript/</guid>
<description>
  <![CDATA[Last week, <a href="https://leancrew.com/all-this/2025/12/tweaking-my-screenshot-macro/">my SnapClip macro</a> crapped out with an error message. In the process of fixing it, I learned something about AppleScript that probably won’t ever help me in the future.]]>
</description>
<content:encoded>
  <![CDATA[<p>Last week, <a href="https://leancrew.com/all-this/2025/12/tweaking-my-screenshot-macro/">my SnapClip macro</a> crapped out with an error message. In the process of fixing it, I learned something about AppleScript that probably won’t ever help me in the future.</p>
<p>SnapClip is a Keyboard Maestro macro that puts a graphical user interface on a Python script, <code>process-screenshot</code>. The problem that led to the error was a particular branch of <code>process-screenshot</code>: the branch that puts the screenshot onto the clipboard. I’m not going to rehash the design of SnapClip or <code>process-screenshot</code> here—you can read the <a href="https://leancrew.com/all-this/2025/12/tweaking-my-screenshot-macro/">old post</a> about them if you want the details.</p>
<p>The branch of <code>process-screenshot</code> that failed runs the <a href="https://www.alecjacobson.com/weblog/3816.html"><code>impbcopy</code></a> command-line utility written by Alec Jacobson over a decade ago. I’ve had <code>impbcopy</code> running successfully on my Macs for many years, but last week it failed with the error message</p>
<pre><code>Bad CPU type in executable
</code></pre>
<p>I assumed the problem was that <code>impbcopy</code> had been compiled back when I was using an Intel Mac, and it had continued to work because of <a href="https://support.apple.com/en-us/102527">Rosetta</a>. I thought that because I had Rosetta installed before, it would be updated along with macOS 27, but it wasn’t. Running</p>
<pre><code>softwareupdate --install-rosetta
</code></pre>
<p>as described on the <a href="https://support.apple.com/en-us/102527">Rosetta page</a> got SnapClip working again.</p>
<p>But macOS 27 is the end of the road for Rosetta, so it seemed like a change was due. I installed the <a href="https://developer.apple.com/documentation/xcode/installing-the-command-line-tools">Xcode Command Line Tools</a> and tried to recompile <code>impbcopy</code> for Apple Silicon, but that failed with error messages that I couldn’t interpret because I’m not an Objective-C programmer. So I had four options:</p>
<ol>
<li>Learn enough about <code>gcc</code>/Objective-C/<code>clang</code> to get the compilation to work. Even if I could do this, I assumed it would be a lot of work with no future value for me.</li>
<li>See if I could rewrite the SnapClip macro to use Keyboard Maestro features to put the image on the clipboard. This was more appealing, but I really liked the way the macro was structured and preferred to leave it as a GUI wrapper.</li>
<li>Look into using Shortcuts to put the image on the clipboard and—if it can—replace the call to <code>impbcopy</code> with an equivalent call to the <a href="https://leancrew.com/all-this/man/man1/shortcuts.html"><code>shortcuts</code> utility</a>. But I <em>hate</em> Shortcuts and would rather avoid it if possible.</li>
<li>Work out an AppleScript solution and put a call to <a href="https://leancrew.com/all-this/man/man1/osascript.html"><code>osascript</code></a> into <code>process-screenshot</code>. I knew that AppleScript has commands for reading files and putting things on the clipboard, so I went with this, even though AppleScript may be more of a dead end than Objective-C.</li>
</ol>
<p>Apple’s online documentation for AppleScript sucks, but I have three old AppleScript books that collectively weigh over nine pounds:</p>
<ul>
<li><a href="https://lccn.loc.gov/2006274517"><em>AppleScript: The Definitive Guide</em></a> by Matt Neuburg.</li>
<li><a href="https://lccn.loc.gov/2009278244"><em>AppleScript 1-2-3</em></a> by Sal Soghoian and Bill Cheeseman.</li>
<li><a href="https://lccn.loc.gov/2010485146"><em>Learn AppleScript</em></a> by Hamish Sanderson and Hanaan Rosenthal.</li>
</ul>
<p>The answer (more or less) was in <em>Learn AppleScript</em> and led to just a couple of changes to <code>process-screenshot</code>. Here’s the new version:</p>
<pre><code>python:
 1:  #!/usr/bin/env python3
 2:  
 3:  import tempfile
 4:  from PIL import Image
 5:  import os
 6:  import subprocess
 7:  import shutil
 8:  from datetime import datetime
 9:  import urllib.parse
10:  import argparse
11:  
12:  # Handle the arguments
13:  desc = 'Process a screenshot saved to a file.'
14:  ep = '''If a title is given, it saves the image to a file on the
15:  Desktop with a filename of the form yyyymmdd-title.png.
16:  If no title is given, the image is put on the clipboard and the
17:  upload option is ignored.'''
18:  parser = argparse.ArgumentParser(description=desc, epilog=ep,
19:            formatter_class=argparse.RawDescriptionHelpFormatter)
20:  parser.add_argument('-b', '--background', help='add desktop background border', action='store_true')
21:  parser.add_argument('-u', '--upload', help='upload to images directory and print URL', action='store_true')
22:  parser.add_argument('-t', '--title', help='image title', type=str)
23:  parser.add_argument('ssfile', help='path to screenshot file')
24:  args = parser.parse_args()
25:  
26:  # Parameters
27:  type = "png"
28:  bgcolor = (85, 111, 137)
29:  border = 32
30:  optimizer = '/Applications/ImageOptim.app/Contents/MacOS/ImageOptim'
31:  ascript = f'set the clipboard to (read POSIX file "{args.ssfile}" as picture)'
32:  
33:  # Add a desktop background border if asked for
34:  if args.background:
35:    snap = Image.open(args.ssfile)
36:    # Make a solid-colored background bigger than the screenshot.
37:    snapsize = tuple([ x + 2*border for x in snap.size ])
38:    bg = Image.new('RGBA', snapsize, bgcolor)
39:    bg.alpha_composite(snap, dest=(border, border))
40:    bg.save(args.ssfile)
41:  
42:  # Optimize the file
43:  subprocess.run([optimizer, args.ssfile], stderr=subprocess.DEVNULL)
44:  
45:  # Save it to a Desktop file if a title was given; otherwise,
46:  # save it to the clipboard
47:  if args.title:
48:    sdate = datetime.now().strftime("%Y%m%d")
49:    desktop = os.environ['HOME'] + "/Desktop/"
50:    fname = f'{desktop}{sdate}-{args.title}.{type}'
51:    shutil.copyfile(args.ssfile, fname)
52:    bname = os.path.basename(fname)
53:  
54:    # Upload the file and print the URL if asked
55:    if args.upload:
56:      year = datetime.now().strftime("%Y")
57:      server = f'user@server.com:path/to/images{year}/'
58:      port = '123456789'
59:      subprocess.run(['scp', '-P', port, fname, server])
60:      bname = urllib.parse.quote(bname)
61:      print(f'https://leancrew.com/all-this/images{year}/{bname}')
62:  else:
63:    subprocess.run(['osascript', '-e', ascript])
64:  
65:  # Delete the temporary file
66:  os.remove(args.ssfile)
</code></pre>
<p>The changes are:</p>
<ul>
<li>Line 31, which is an added line that defines the AppleScript command that does what <code>impbcopy</code> used to do; and</li>
<li>Line 63, which runs the <code>ascript</code> command via <code>osascript</code>.</li>
</ul>
<p>The AppleScript that does the work is just one line:</p>
<pre><code>set the clipboard to (read POSIX file "&lt;name of file&gt;" as picture)
</code></pre>
<p>Very simple, and I suspect it will work for quite a while. I feel certain that Apple would like to kill AppleScript, but I don’t think it can—too many valuable customers (a group that doesn’t include me) would get very angry at having their longstanding automations die.</p>
<p>One thing that kind of surprised me was the <code>as picture</code> part of the command. <em>Learn AppleScript</em> says that you typically have to include an image type in the command: <code>as GIF picture</code> or <code>as JPEG picture</code> or <code>as TIFF picture</code>. The book suggests that a bare <code>as picture</code> is supposed to work only on <code>PICT</code> images. Never heard of a <code>PICT</code> image? That’s probably because it was the native Mac image format back in the classic days. It was one of Bill Atkinson’s contributions to the Macintosh Toolbox. Nobody saves image files as <code>PICT</code>s anymore.</p>
<p>And yet the unadorned <code>as picture</code> works. I created this test image in Acorn,</p>
<p><img alt="Test image" class="ss" src="https://leancrew.com/all-this/images2026/20261106-Test%20image.png" title="Test image" width="80%"/></p>
<p>and saved it in several formats: <code>BMP</code>, <code>GIF</code>, <code>HEIC</code>, <code>JPEG</code>, <code>JPEG 2000</code>, <code>PNG</code>, and <code>TIFF</code>. I put the files in a folder named <code>clipboard-test</code> on my Desktop,</p>
<p><img alt="Folder of images" class="ss" src="https://leancrew.com/all-this/images2026/20261106-Folder%20of%20images.png" title="Folder of images" width="100%"/></p>
<p>and ran this AppleScript for each of the different file extensions:</p>
<pre><code>applescript:
set filePath to path to desktop folder as text
set imageFile to (filePath &amp; "clipboard-test:Test image.bmp") as alias
set the clipboard to (read file imageFile as picture)
</code></pre>
<p>Each time I ran the script, the clipboard was loaded with the image, no matter which file was being read. I didn’t have to specify the format.</p>
<p>I suspect that if I were using AppleScript to write image data to a file, it wouldn’t be as forgiving. But <code>process-screenshot</code> uses Python and <a href="https://pypi.org/project/pillow/">Pillow</a> to save image files, so I don’t need to worry about AppleScript’s idiosyncrasies for that aspect of the script.</p>]]>
</content:encoded>
</item>

<item>
<title>XKCD and Fourier</title>
<link>https://leancrew.com/all-this/2026/10/xkcd-and-fourier/</link>
<pubDate>Fri, 30 Oct 2026 01:45:58 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/xkcd-and-fourier/</guid>
<description>
  <![CDATA[If you’ve done Fourier series approximations of functions, you had three reactions to <a href="https://xkcd.com/3317/">today’s XKCD comic</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/10/xkcd-and-fourier/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>If you’ve done Fourier series approximations of functions, you had three reactions to <a href="https://xkcd.com/3317/">today’s XKCD comic</a>:</p>
<p><img alt="https://imgs.xkcd.com/comics/gravitational_square_wave.png" class="ss" src="https://imgs.xkcd.com/comics/gravitational_square_wave.png" title="https://imgs.xkcd.com/comics/gravitational_square_wave.png" width="100%"/></p>
<p>First, of course, was a smile of recognition. Then came a “wait a minute” as you realized that the <a href="https://leancrew.com/all-this/2023/02/swings-and-roundabouts/">Gibbs phenomenon</a> doesn’t get smaller as you add more terms to the Fourier expansion; it just concentrates itself at the corners:</p>
<p><img alt="Gibbs phenomenon examples for square wave" class="ss" src="https://leancrew.com/all-this/images2023/20230213-Gibbs%20phenomenon%20examples%20for%20square%20wave.png" title="Gibbs phenomenon examples for square wave" width="100%"/></p>
<p>That ringing never goes away. Here, for example, is the plot for</p>
<p><!-- $$ \sum_{k = 1, 3, 5, \ldots}^n \frac{4}{k \pi} \sin (k \pi x) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><munderover><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn><mo>,</mo><mn>3</mn><mo>,</mo><mn>5</mn><mo>,</mo><mi>…</mi></mrow><mn>99</mn></munderover><mfrac><mn>4</mn><mrow><mi>k</mi><mi>π</mi></mrow></mfrac><mrow><mi mathvariant="normal">sin</mi><mo>⁡</mo></mrow><mo form="prefix" stretchy="false">(</mo><mi>k</mi><mi>π</mi><mi>x</mi><mo form="postfix" stretchy="false">)</mo></mrow></math></p>
<p><img alt="Square wave approximation" class="ss" src="https://leancrew.com/all-this/images2026/20261029-Square%20wave%20approximation.png" title="Square wave approximation" width="80%"/></p>
<p>Finally, you realized that thinking like this ruins the joke.</p>
  ]]>
</content:encoded>
</item>

<item>
<title>Squaring the triangle with help</title>
<link>https://leancrew.com/all-this/2026/10/squaring-the-triangle-with-help/</link>
<pubDate>Mon, 26 Oct 2026 21:24:29 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/squaring-the-triangle-with-help/</guid>
<description>
  <![CDATA[Yesterday, Numberphile released <a href="https://www.youtube.com/watch?v=EgnAN0ZUl08&amp;t=609s">a delightful video</a> with James Grime (redundant, I know) in which he walks us through a geometry problem first posed by Henry Dudeney in 1902. It’s known as the “<a href="https://www.gutenberg.org/cache/epub/27635/pg27635-images.html#s26">Haberdasher’s Puzzle</a>” and is reproduced in Dudeney’s 1907 book <em>The Canterbury Puzzles</em>. The problem was solved by C.W. McElroy after Dudeney published it in a periodical called the <em>Weekly Dispatch</em>.]]>
</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/10/squaring-the-triangle-with-help/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>Yesterday, Numberphile released <a href="https://www.youtube.com/watch?v=EgnAN0ZUl08&amp;t=609s">a delightful video</a> with James Grime (redundant, I know) in which he walks us through a geometry problem first posed by Henry Dudeney in 1902. It’s known as the “<a href="https://www.gutenberg.org/cache/epub/27635/pg27635-images.html#s26">Haberdasher’s Puzzle</a>” and is reproduced in Dudeney’s 1907 book <em>The Canterbury Puzzles</em>. The problem was solved by C.W. McElroy after Dudeney published it in a periodical called the <em>Weekly Dispatch</em>.</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/EgnAN0ZUl08?si=aLz6IkQ67JsbCwUR" title="YouTube video player" width="560"></iframe>
<p>The problem is to cut up an equilateral triangle into pieces that can be translated and rotated (not flipped) to fit together into a square. The minimum number of pieces that allow you to do this is four, and here’s the arrangement shown in the video:</p>
<p><img alt="Equilateral triangle pieces in video" class="ss" src="https://leancrew.com/all-this/images2026/20261025-Equilateral%20triangle%20pieces%20in%20video.jpg" title="Equilateral triangle pieces in video" width="80%"/></p>
<p>What James doesn’t do in the video is explain how you position the cuts. Dudeney shows a <a href="https://www.gutenberg.org/cache/epub/27635/pg27635-images.html#s26">straightedge-and-compass construction</a> in his book, but I wanted an algebraic solution. This turned out to be more complicated than I anticipated, but I had Mathematica to keep track of the messy algebra.</p>
<p>Let’s start with the equilateral triangle and its component pieces. The points that define the vertices of the triangle and cut lines are given the labels <em>A</em> through <em>J</em> (to avoid confusion, <em>I</em> is omitted).</p>
<p><img alt="Equilateral triangle with cuts" class="ss" src="https://leancrew.com/all-this/images2026/20261026-Equilateral%20triangle%20with%20cuts.png" title="Equilateral triangle with cuts" width="80%"/></p>
<p>The lines <em>GJ</em> and <em>DH</em> are perpendicular to Line <em>EF</em>.</p>
<p>To make the explanation simpler, let’s jump to the end and show how to make a square out of these components. The fun way to do it is in a series of rotations:</p>
<ol>
<li>Rotate the green piece 180° about Point <em>G</em>.</li>
<li>Rotate the green and orange pieces together 180° about Point <em>E</em>.</li>
<li>Rotate the green, orange, and blue pieces together 180° about Point <em>D</em>.</li>
</ol>
<p>Here’s an illustration of the steps:</p>
<p><img alt="Rotations from triangle to square" class="ss" src="https://leancrew.com/all-this/images2026/20261026-Rotations%20from%20triangle%20to%20square.png" title="Rotations from triangle to square" width="80%"/></p>
<p>In this illustration, the red piece stays put through all the steps.</p>
<p>We can rotate the square to make its sides horizontal and vertical and label the various points to show where they ended up.</p>
<p><img alt="Aligned square" class="ss" src="https://leancrew.com/all-this/images2026/20261026-Aligned%20square.png" title="Aligned square" width="80%"/></p>
<p>The vertices of the equilateral triangle have coalesced into a single point in the interior of the square. The right angles at <em>H</em> and <em>J</em> have split up to form the corners of the square.</p>
<p>Before we get into the algebra of the situation, let’s think about the various dimensions. The side lengths of the square, <em>s</em>, must be equal, so</p>
<p><!-- $$ s = 2 \bar{GJ} = 2 \bar{DH} = \bar{EJ} + \bar{EH} = \bar{FH} + \bar{FJ} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>s</mi><mo>=</mo><mn>2</mn><mspace width=".1em"></mspace><mover><mrow><mi>G</mi><mi>J</mi></mrow><mo accent="false" stretchy="true">——</mo></mover><mo>=</mo><mn>2</mn><mspace width=".1em"></mspace><mover><mrow><mi>D</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>=</mo><mover><mrow><mi>E</mi><mi>J</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>E</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>=</mo><mover><mrow><mi>F</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>F</mi><mi>J</mi></mrow><mo accent="true" stretchy="true">——</mo></mover></mrow></math></p>
<p>A bit of algebra tells us that</p>
<p><!-- $$ \begin{align*}
2\,s &= \bar{EJ} + \bar{EH} + \bar{FH} + \bar{FJ}\\
 &= \left( \bar{EJ} + \bar{FJ} \right) + \left( \bar{FH} + \bar{EH} \right)
 \end{align*} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mn>2</mn><mspace width="0.167em"></mspace><mi>s</mi></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mover><mrow><mi>E</mi><mi>J</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>E</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>F</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>F</mi><mi>J</mi></mrow><mo accent="true" stretchy="true">——</mo></mover></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mrow><mo form="prefix" stretchy="true">(</mo><mover><mrow><mi>E</mi><mi>J</mi></mrow><mo accent="true">——</mo></mover><mo>+</mo><mover><mrow><mi>F</mi><mi>J</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo form="postfix" stretchy="true">)</mo></mrow><mo>+</mo><mrow><mo form="prefix" stretchy="true">(</mo><mover><mrow><mi>F</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo>+</mo><mover><mrow><mi>E</mi><mi>H</mi></mrow><mo accent="true" stretchy="true">——</mo></mover><mo form="postfix" stretchy="true">)</mo></mrow></mtd></mtr></mtable></math></p>
<p>Looking back at the drawing of the equilateral triangle, we see that the terms in the parentheses make up Line <em>EF</em>. Therefore, the length of <em>EF</em> is the side length of the square, <em>s</em>.</p>
<p>Now it’s time for some analytic geometry. We’ll put the origin of our <em>xy</em> coordinate system at Point <em>A</em>, and we’ll assume each side of the equilateral triangle has unit length. The coordinates are:</p>
<p><!-- $$ \begin{align*}
A:\quad &(0, 0) \\
B:\quad &(1, 0)\\
C:\quad &\left(\frac{1}{2}, \frac{\sqrt{3}}{2} \right)
\end{align*} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>A</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo form="prefix" stretchy="false">(</mo><mn>0</mn><mo>,</mo><mn>0</mn><mo form="postfix" stretchy="false">)</mo></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>B</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo form="prefix" stretchy="false">(</mo><mn>1</mn><mo>,</mo><mn>0</mn><mo form="postfix" stretchy="false">)</mo></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>C</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><mn>1</mn><mn>2</mn></mfrac><mo>,</mo><mfrac><msqrt><mn>3</mn></msqrt><mn>2</mn></mfrac><mo form="postfix" stretchy="true">)</mo></mrow></mtd></mtr></mtable></math></p>
<p>The area of the triangle is</p>
<p><!-- $$ \frac{1}{2}\, (1)\, \left( \frac{\sqrt{3}}{2} \right) = \frac{\sqrt{3}}{4} $$ -->
<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>1</mn><mo form="postfix" stretchy="false">)</mo><mspace width="0.167em"></mspace><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><msqrt><mn>3</mn></msqrt><mn>2</mn></mfrac><mo form="postfix" stretchy="true">)</mo></mrow><mo>=</mo><mfrac><msqrt><mn>3</mn></msqrt><mn>4</mn></mfrac></mrow></math></p>
<p>This is the same as the area of the square, so</p>
<p><!-- $$ s = \frac{3^{1/4}}{2} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>s</mi><mo>=</mo><mfrac><msup><mn>3</mn><mrow><mn>1</mn><mi>/</mi><mn>4</mn></mrow></msup><mn>2</mn></mfrac></mrow></math></p>
<p>This will be the length of <!-- $\bar{EF}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mover><mrow><mi>E</mi><mi>F</mi></mrow><mo accent="true">——</mo></mover></math>.</p>
<p>Points <em>D</em> and <em>E</em> are the midpoints of <em>AC</em> and <em>BC</em>:</p>
<p><!-- $$ \begin{align*}
D:\quad & \left( \frac{1}{4}, \, \frac{\sqrt{3}}{4} \right) \\
E:\quad & \left( \frac{3}{4}, \, \frac{\sqrt{3}}{4} \right)
\end{align*} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>D</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><mn>1</mn><mn>4</mn></mfrac><mo>,</mo><mspace width="0.167em"></mspace><mfrac><msqrt><mn>3</mn></msqrt><mn>4</mn></mfrac><mo form="postfix" stretchy="true">)</mo></mrow></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>E</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><mn>3</mn><mn>4</mn></mfrac><mo>,</mo><mspace width="0.167em"></mspace><mfrac><msqrt><mn>3</mn></msqrt><mn>4</mn></mfrac><mo form="postfix" stretchy="true">)</mo></mrow></mtd></mtr></mtable></math></p>
<p>Since we know the length of Line <em>EF</em> and the position of Point <em>E</em>, we can calculate the position of Point <em>F</em> along Line <em>AB</em>:</p>
<p><!-- $$ F:\quad \left( \frac{1}{4} \, \left[ 3-\sqrt{4
   \sqrt{3}-3} \right], \, 0 \right) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>F</mi><mo>:</mo><mspace width="1.0em"></mspace><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><mn>1</mn><mn>4</mn></mfrac><mspace width="0.167em"></mspace><mrow><mo form="prefix" stretchy="true">[</mo><mn>3</mn><mo>−</mo><msqrt><mrow><mn>4</mn><msqrt><mn>3</mn></msqrt><mo>−</mo><mn>3</mn></mrow></msqrt><mo form="postfix" stretchy="true">]</mo></mrow><mo>,</mo><mspace width="0.167em"></mspace><mn>0</mn><mo form="postfix" stretchy="true">)</mo></mrow></mrow></math></p>
<p>As I said, it’s kind of messy, but that’s what Mathematica is for. To six decimal places, the <em>x</em> coordinate is 0.254508. This is almost directly under Point <em>D</em>, but not quite.</p>
<p>Now we need the position of Point <em>G</em>. As it happens, it’s exactly <!-- $\frac{1}{2}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math> unit to the right of Point <em>F</em>:</p>
<p><!-- $$ G:\quad \left( \frac{1}{4} \, \left[ 5 - \sqrt{4
   \sqrt{3}-3} \right], \, 0 \right) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>G</mi><mo>:</mo><mspace width="1.0em"></mspace><mrow><mo form="prefix" stretchy="true">(</mo><mfrac><mn>1</mn><mn>4</mn></mfrac><mspace width="0.167em"></mspace><mrow><mo form="prefix" stretchy="true">[</mo><mn>5</mn><mo>−</mo><msqrt><mrow><mn>4</mn><msqrt><mn>3</mn></msqrt><mo>−</mo><mn>3</mn></mrow></msqrt><mo form="postfix" stretchy="true">]</mo></mrow><mo>,</mo><mspace width="0.167em"></mspace><mn>0</mn><mo form="postfix" stretchy="true">)</mo></mrow></mrow></math></p>
<p>To six decimal places, this is 0.754508.</p>
<p>How do we know this? Consider this drawing:</p>
<p><img alt="Second green triangle" class="ss" src="https://leancrew.com/all-this/images2026/20261026-Second%20green%20triangle.png" title="Second green triangle" width="80%"/></p>
<p>The light green triangle, <em>DEH</em>, is congruent to the dark green triangle, <em>GFJ</em>. We know this because:</p>
<ol>
<li>The right angle at <em>H</em> matches the right angle at <em>J</em>.</li>
<li>The length of <em>DH</em> is equal to the length of <em>GJ</em> (see the discussion above).</li>
<li>The angle at <em>D</em> matches the angle at <em>G</em> because <em>DE</em> is parallel to <em>FG</em> and <em>DH</em> is parallel to <em>GJ</em>.</li>
</ol>
<p>We therefore have an angle-side-angle congruence. The light green triangle is a duplicate of the dark green triangle, rotated 180° and moved up so its horizontal side is aligned with <em>DE</em>. The distance between <em>D</em> and <em>E</em> is <!-- $\frac{1}{2}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math>, so that must also be the distance between <em>F</em> and <em>G</em>.</p>
<p>With the locations of <em>F</em> and <em>G</em> known, we can find <em>H</em> and <em>J</em> by running lines perpendicular to <em>EF</em> that intersect <em>F</em> and <em>G</em>. The full equations for the positions of <em>H</em> and <em>J</em> are even messier than those of <em>F</em> and <em>G</em>, so I’ll just give the six-place decimal approximations:</p>
<p><!-- $$ \begin{align*}
H:\quad &(0.466506, 0.185267) \\
J:\quad &(0.538001, 0.247746)
\end{align*} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>H</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo form="prefix" stretchy="false">(</mo><mn>0.466506</mn><mo>,</mo><mn>0.185267</mn><mo form="postfix" stretchy="false">)</mo></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>J</mi><mo>:</mo><mspace width="1.0em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo form="prefix" stretchy="false">(</mo><mn>0.538001</mn><mo>,</mo><mn>0.247746</mn><mo form="postfix" stretchy="false">)</mo></mtd></mtr></mtable></math></p>
<p>If you want to go through the Mathematica that generated these results and the graphics, here it is:</p>
<iframe height="800" src="https://www.wolframcloud.com/obj/mark80/Published/dudeney.nb?_embed=iframe" width="100%"></iframe>
  ]]>
</content:encoded>
</item>

<item>
<title>HomeView</title>
<link>https://leancrew.com/all-this/2026/10/homeview/</link>
<pubDate>Tue, 20 Oct 2026 16:48:47 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/homeview/</guid>
<description>
  <![CDATA[Given that I typically take a wait-and-see attitude when Apple introduces a new product, I was surprised to find myself ordering a <a href="https://www.apple.com/apple-homeview/">HomeView</a> within a day of its announcement. I was even more surprised when lots of otherwise pro-Apple people—<a href="https://daringfireball.net/2026/10/homeview_thoughts_and_observations">this guy</a>, for example—were skeptical about its value.]]>
</description>
<content:encoded>
  <![CDATA[<p>Given that I typically take a wait-and-see attitude when Apple introduces a new product, I was surprised to find myself ordering a <a href="https://www.apple.com/apple-homeview/">HomeView</a> within a day of its announcement. I was even more surprised when lots of otherwise pro-Apple people—<a href="https://daringfireball.net/2026/10/homeview_thoughts_and_observations">this guy</a>, for example—were skeptical about its value.</p>
<p><img alt="HomeView" class="ss" src="https://leancrew.com/all-this/images2026/20261020-HomeView.png" title="HomeView" width="80%"/></p>
<p>Why is this? Well, one reason could be that I don’t already have a HomePod in my kitchen, which makes me behind the times. I’ve been thinking I should get one (or two), but Siri’s deficiencies have kept me back. Also, I like the idea of a status board but didn’t want to dedicate an iPad to that. The recently improved Siri and the HomeView’s touchscreen kill those two birds with one $300 stone.</p>
<p>And I think I have a good place for it in my kitchen. Not on the center island. The two likely spots for it on the island are corners that are already taken:</p>
<ul>
<li>The corner that currently has a shallow wooden bowl that my dad turned and that I finished after his death. It’s where I put fruit and vegetables that don’t need refrigeration and is currently holding an avocado that should be ripe tomorrow.</li>
<li>The corner that has a vase I keep filled with flowers from Trader Joe’s. That’s where Janet put flowers, and it isn’t going to change.</li>
</ul>
<p>Another obvious place would be the kitchen desk. Many years ago, we had a laptop computer on the desk, so it has a history of similar use. But the desk isn’t in a great spot for the HomeView—a little too far away from the island to look at it while I’m preparing a meal, and maybe a little too far away from the kitchen table to use as a status board.</p>
<p>But there is a perfect spot at the end of one of the counters. It’s near both the kitchen table and the island. I’ll need to rotate it, depending on where I am, but <a href="https://www.apple.com/105/media/us/apple-homeview/2026/f7ce47a8-10f2-455c-b99f-bf9b85a35cb6/films/product/apple-homeview-product-tpl-us-2026_16x9.m3u8">the annoying family in the video</a> showed that that was no problem. And there’s an outlet right there, so the power cord can be stored neatly.</p>
<p>The counter isn’t a good spot for a FaceTime call, but it’ll be easy to move the HomeView over to the kitchen table when I use it for that.</p>
<p>So by being behind the times for several years, I’m now in the perfect position to be on the cutting edge. With a product <a href="https://gizmodo.com/apples-homeviews-reportedly-collected-dust-in-warehouses-for-the-better-part-of-a-year-2000827427">that’s been sitting on the shelf</a> for a year or so.</p>]]>
</content:encoded>
</item>

<item>
<title>TextEdit vs. Pages</title>
<link>https://leancrew.com/all-this/2026/10/textedit-vs-pages/</link>
<pubDate>Sat, 17 Oct 2026 02:42:51 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/textedit-vs-pages/</guid>
<description>
  <![CDATA[A few days ago, Allison Sheridan wrote <a href="https://www.podfeet.com/blog/2026/10/textedit/">a very persuasive article</a> about using TextEdit, the “unsung hero of macOS,” as a simple word processor. I’ve been trying it out and… I’m going to stick with Pages. Let me explain why.]]>
</description>
<content:encoded>
  <![CDATA[<p>A few days ago, Allison Sheridan wrote <a href="https://www.podfeet.com/blog/2026/10/textedit/">a very persuasive article</a> about using TextEdit, the “unsung hero of macOS,” as a simple word processor. I’ve been trying it out and… I’m going to stick with Pages. Let me explain why.</p>
<p>First, you should know that I almost never use a word processor. The overwhelming majority of my writing over the past 30 years has been done in a text editor, and its plain text is then fed to some sort of formatting software—Markdown or LaTeX, typically—to set the styles for all the parts. But with one-off documents, the styles I use for blog posts and technical reports don’t make sense. A note for someone who’s coming over to water my plants while I’m out of town shouldn’t look like an analysis of a mobile crane tipover accident. That’s where a word processor comes in handy. It’s much faster to change fonts, sizes, and margins in a word processor than in a LaTeX preamble.</p>
<p>Allison got on my good side early in the article when she complained about the ribbon in Microsoft Word. I think toolbars are way overused in apps nowadays, and Microsoft Office apps are probably the worst offenders. Their ribbons are overloaded with functions that should be in a menu, and they just take up too much space. Fundamentally, I see Microsoft’s ribbons as a user interface cop-out—it’s easier to load up a ribbon with buttons than it is to think carefully about how to make features available when needed without adding visual clutter.</p>
<p>Unfortunately, TextEdit has a toolbar that can’t be hidden. It’s a compact toolbar, to be sure, but almost everything in it could be done through menu commands—as <a href="https://en.wikipedia.org/wiki/MacWrite">MacWrite</a> and <a href="https://en.wikipedia.org/wiki/WriteNow">WriteNow</a> managed to do 40 years ago—without taking up any space in the document window. Honestly, who needs buttons for bold and italic? Or left, center, and right justification? This is no better than Word’s buttons for cut, copy, and paste. I really thought TextEdit would have a setting for hiding the toolbar, but I haven’t found one. Control-clicking the toolbar doesn’t let you customize it, either.</p>
<p><img alt="TextEdit example" class="ss" src="https://leancrew.com/all-this/images2026/20261016-TextEdit%20example.png" title="TextEdit example" width="63%"/></p>
<p>Now it’s true that Pages has a toolbar, but it’s tucked up in the title bar where it doesn’t take away space from the document. And you can customize it to include only the buttons you find useful.</p>
<p><img alt="Pages example" class="ss" src="https://leancrew.com/all-this/images2026/20261016-Pages%20example.png" title="Pages example" width="80%"/></p>
<p>Allison is right that the Format sidebar in Pages could use a rethink. <span class="menu">Style</span>, <span class="menu">Layout</span>, and <span class="menu">More</span> are pretty useless names for the settings they expose—calling them <span class="menu">One</span>, <span class="menu">Two</span>, and <span class="menu">Three</span> would be just about as informative. But at least the sidebar can be hidden until you need it.</p>
<p><img alt="Pages example with formatting sidebar" class="ss" src="https://leancrew.com/all-this/images2026/20261016-Pages%20example%20with%20formatting%20sidebar.png" title="Pages example with formatting sidebar" width="80%"/></p>
<p>Another TextEdit deficiency that bothers me is the lack of a sensible zoom control. There are <span class="menu">Zoom In</span> and <span class="menu">Zoom Out</span> commands in the <span class="menu">View</span> menu, but they double and halve the displayed size of the document, which isn’t fine enough control. Would it be so bad to have 25% increments for your users with decaying eyesight?</p>
<div class="update">
<p><strong>Update 18 Oct 2026 4:19 PM</strong><br/>
Andrew Kemp pointed out <a href="https://mastodon.social/@ajwk/117456364364874807">on Mastodon</a> that you <em>can</em> zoom in TextEdit using pinch and reverse pinch gestures on your trackpad. I should’ve tested that before writing the previous paragraph. Pages still has a zoom advantage because you can set a starting zoom level for every new document, but it’s not as big of an advantage as I thought. Thanks, Andrew!</p>
</div>
<p>I mentioned earlier that it’s nice to be able to quickly change styles within a document. It’s also nice to have some standard styles to start with. Pages uses templates, a longstanding Mac feature that may have come over from NeXT, for creating documents with standard styles. It comes with lots of templates that I would never use, but it also lets users create their own. I have a simple one called Big Note, which uses 16-point Helvetica Neue for body text, centered 18-point bold Helvetica Neue for headings, and a 0.75″ indentation for bulleted lists. This works without changes for most of my printed notes to myself and others. While you can create new styles in TextEdit, you can’t save templates.</p>
<p>An advantage of TextEdit that Allison points out and which I concede: TextEdit uses much less memory than Pages. I wish that weren’t the case, but I have a 48 GB MacBook Pro, so I can afford to be profligate with RAM.</p>
<p>Price is not an advantage of TextEdit for me, as I’m using the free version of Pages. If Apple ever forces Pages users to sign up for Creator Studio—which I doubt will happen—I’ll be revisiting TextEdit.</p>]]>
</content:encoded>
</item>

<item>
<title>Randomizing array indices</title>
<link>https://leancrew.com/all-this/2026/10/randomizing-array-indices/</link>
<pubDate>Tue, 13 Oct 2026 02:15:04 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/randomizing-array-indices/</guid>
<description>
  <![CDATA[I mentioned in <a href="https://leancrew.com/all-this/2026/10/mean-vs-mean/">my last post</a> that I’m working my way through <a href="https://learning-oreilly-com.ezproxy.naperville-lib.org/library/view/practical-deep-learning/9798341655119/"><em>Practical Deep Learning</em></a> by Ronald Kneusel. The math so far has been straightforward, and the code is written in Python, which I find easy to follow.]]>
</description>
<content:encoded>
  <![CDATA[<p>I mentioned in <a href="https://leancrew.com/all-this/2026/10/mean-vs-mean/">my last post</a> that I’m working my way through <a href="https://learning-oreilly-com.ezproxy.naperville-lib.org/library/view/practical-deep-learning/9798341655119/"><em>Practical Deep Learning</em></a> by Ronald Kneusel. The math so far has been straightforward, and the code is written in Python, which I find easy to follow.</p>
<p>“Easy to follow” does not mean “correct” or “to my liking.” Some of the code in the book simply doesn’t work, and I’ve had to make changes here and there to get it to run. It’s possible that some of the APIs the book relies on have changed their calling conventions, but I also found a pretty obvious off-by-one error. And I’m only three chapters in.</p>
<p>This post, though, is about the “not to my liking” aspect of the code. I feel a little uncomfortable doing this, but I’ve been reading and writing data analysis code in Python for 15–20 years, so I’m going to put my discomfort to the side.</p>
<p>One of the examples used in the book is the well-known <a href="https://en.wikipedia.org/wiki/Iris_flower_data_set">iris measurement dataset</a>. There are 150 records in the dataset; here are the first ten:</p>
<table style="font-size: 80%">
<thead>
<tr>
<th align="center">Index</th>
<th align="center">Sepal<br/>length</th>
<th align="center">Sepal<br/>width</th>
<th align="center">Petal<br/>length</th>
<th align="center">Petal<br/>width</th>
<th align="center">Class</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">0</td>
<td align="center">5.1</td>
<td align="center">3.5</td>
<td align="center">1.4</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">4.9</td>
<td align="center">3.0</td>
<td align="center">1.4</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">2</td>
<td align="center">4.7</td>
<td align="center">3.2</td>
<td align="center">1.3</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">3</td>
<td align="center">4.6</td>
<td align="center">3.1</td>
<td align="center">1.5</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">5.0</td>
<td align="center">3.6</td>
<td align="center">1.4</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">5</td>
<td align="center">5.4</td>
<td align="center">3.9</td>
<td align="center">1.7</td>
<td align="center">0.4</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">6</td>
<td align="center">4.6</td>
<td align="center">3.4</td>
<td align="center">1.4</td>
<td align="center">0.3</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">7</td>
<td align="center">5.0</td>
<td align="center">3.4</td>
<td align="center">1.5</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">8</td>
<td align="center">4.4</td>
<td align="center">2.9</td>
<td align="center">1.4</td>
<td align="center">0.2</td>
<td align="center">Iris-setosa</td>
</tr>
<tr>
<td align="center">9</td>
<td align="center">4.9</td>
<td align="center">3.1</td>
<td align="center">1.5</td>
<td align="center">0.1</td>
<td align="center">Iris-setosa</td>
</tr>
</tbody>
</table>
<p>There are three species of iris in the dataset: <em>setosa</em>, <em>versicolor</em>, and <em>virginica</em>. The records, which are <a href="https://archive.ics.uci.edu/dataset/53/iris">easy to find</a>, are sorted according to class, and it’s necessary to randomize the records for certain operations.</p>
<p>Let’s say the measurements are in a 2D (150×4) NumPy array called <code>x</code>, and the classes, which have been reduced to the integers 0, 1, and 2 for <em>setosa</em>, <em>versicolor</em>, and <em>virginica</em>, respectively, are in a 1D NumPy array called <code>y</code>. Here they are in an interactive Python session:</p>
<pre><code>&gt;&gt;&gt; x
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       [5. , 3.6, 1.4, 0.2],

       [140 other rows],

       [6.7, 3. , 5.2, 2.3],
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])
&gt;&gt;&gt; y
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
</code></pre>
<p>Kneusel uses NumPy’s <a href="https://numpy.org/doc/stable/user/basics.indexing.html#integer-array-indexing">integer array indexing</a> to randomize <code>y</code> and the rows of <code>x</code> using the same rearrangement in each. Here’s what he does:</p>
<pre><code>&gt;&gt;&gt; idx = np.argsort(np.random.random(x.shape[0]))
&gt;&gt;&gt; x = x[idx, :]
&gt;&gt;&gt; y = y[idx]
</code></pre>
<p>The first line starts by generating an array of 150 (<code>x.shape[0]</code> is 150) random numbers between 0 and 1 via <a href="https://numpy.org/doc/stable/reference/random/generated/numpy.random.random.html"><code>random.random</code></a>; <a href="https://numpy.org/doc/2.5/reference/generated/numpy.argsort.html"><code>argsort</code></a> then returns an array of the indices of the sorted version of that array. So <code>idx</code> is an array of the integers from 0 through 149 in random order. The two lines after that rearrange <code>x</code> and <code>y</code> in that same random order.</p>
<p>I don’t know about you, but I see this as a weird way to go about randomization. I realize you can’t just shuffle <code>x</code> and <code>y</code> individually, as that would break the connection between an iris’s measurements and its class. But why go through two steps when you can create the <code>idx</code> array directly?</p>
<pre><code>&gt;&gt;&gt; idx = np.random.permutation(x.shape[0])
</code></pre>
<p>Maybe Kneusel’s method is faster, even though it seems like it should be slower. This won’t matter for the small iris dataset, but he may be preparing his readers for huge datasets to come.</p>
<p>I tested the two methods using <a href="https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit">IPython’s <code>%timeit</code> magic command</a>. Here’s what I found, with 1,000,000 indices instead of 150:</p>
<pre><code>&gt;&gt;&gt; %timeit np.argsort(np.random.random(1_000_000))
63.8 ms ± 434 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

&gt;&gt;&gt; %timeit np.random.permutation(1_000_000)
5.98 ms ± 17.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
</code></pre>
<p>So the simple code is both more direct <em>and</em> about an order of magnitude faster. I’ll be looking at the code in the book with a more critical eye as I proceed—assuming I don’t bail out and look for another machine learning reference.</p>
<hr/>
<p>You may have noticed that the code above uses NumPy’s older random number system. The current approach would be to create a <a href="https://numpy.org/doc/stable/reference/random/generator.html#permutations"><code>Generator</code></a> instance and use that as the basis for later function calls. Would this have any effect on the timing results? Let’s see.</p>
<pre><code>&gt;&gt;&gt; rng = np.random.default_rng()
&gt;&gt;&gt; %timeit np.argsort(rng.random(1_000_000))
65 ms ± 188 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

&gt;&gt;&gt; %timeit rng.permutation(1_000_000)
4.91 ms ± 10.9 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
</code></pre>
<p>No, the runtimes are about the same.</p>]]>
</content:encoded>
</item>

<item>
<title>Mean vs mean</title>
<link>https://leancrew.com/all-this/2026/10/mean-vs-mean/</link>
<pubDate>Fri, 09 Oct 2026 22:55:47 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/mean-vs-mean/</guid>
<description>
  <![CDATA[I decided to start learning about machine learning a couple of weeks ago. Partly because it sounds interesting, partly because I think I have enough math to handle it, and partly because I felt it was important to have a deeper knowledge if I want to criticize AI.]]>
</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/10/mean-vs-mean/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>I decided to start learning about machine learning a couple of weeks ago. Partly because it sounds interesting, partly because I think I have enough math to handle it, and partly because I felt it was important to have a deeper knowledge if I want to criticize AI.</p>
<p>I started with a library copy of Friedman and Mendhekar’s <a href="https://lccn.loc.gov/2022045341"><em>The Little Learner</em></a> but decided to drop it after I was about halfway through. Although I was following along reasonably well, I kept struggling with the book’s notation—after 200 pages, I shouldn’t have to keep returning to the front of the book to see what things like <!-- $t|_i$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>t</mi><msub><mo form="prefix" stretchy="false">|</mo><mi>i</mi></msub></mrow></math> mean. If the book’s Scheme code were actually written in Scheme, I might have stuck with it, but its mixture of Scheme with math was too frustrating.</p>
<p>Yesterday I started Kneusel’s <a href="https://www.oreilly.com/library/view/practical-deep-learning/9798341655119/"><em>Practical Deep Learning</em></a>. My library gives me free access to O’Reilly ebooks, so it’s easy to have the book open on my iPad while I work through example code on my MacBook. And yes, I certainly feel more comfortable working in Python. But I was surprised by a result pretty early on.</p>
<p>It had to do with calculating the mean values of the columns of a 2D array. The book is explaining a way to normalize (or standardize) a set of data whose features are on vastly different scales. Here’s the code (in an interactive Python session) that does it using the book’s example data:</p>
<pre><code>python:
&gt;&gt;&gt; import numpy as np
&gt;&gt;&gt; x = np.array([
...  [6998, 0.1361, 0.3408, 0.00007350, 78596048],
...  [6580, 0.4908, 3.0150, 0.00004484, 38462706],
...  [7563, 0.9349, 4.3465, 0.00001003,  6700340],
...  [8355, 0.6529, 2.1271, 0.00002966, 51430391],
...  [2393, 0.4605, 2.7561, 0.00003395, 27284192],
...  [9498, 0.0244, 2.7887, 0.00008880, 78543394],
...  [4030, 0.6467, 4.8231, 0.00000403, 19101443],
...  [5275, 0.3560, 0.0705, 0.00000899, 96029352],
...  [8094, 0.7979, 3.9897, 0.00006691,  7307156],
...  [ 843, 0.7892, 0.9804, 0.00005798, 10179751],
...  [1221, 0.9564, 2.3944, 0.00007815, 14241835],
...  [5879, 0.0329, 2.0085, 0.00009564, 34243070],
...  [ 923, 0.4159, 1.7821, 0.00002467, 52404615],
...  [5882, 0.0002, 1.5362, 0.00005066, 18728752],
...  [1796, 0.7247, 2.3190, 0.00001332, 96703562],
... ])
...
&gt;&gt;&gt; xs = (x - x.mean(axis=0)) / x.std(axis=0)
&gt;&gt;&gt; xs
array([[ 0.69308862, -1.12598999, -1.53184967,  0.95258185,  1.18240196],
       [ 0.54647372, -0.01203876,  0.50510857, -0.01928358, -0.1141859 ],
       [ 0.89126426,  1.38267719,  1.51932212, -1.19969655, -1.14033263],
       [ 1.16906091,  0.49704356, -0.17121154, -0.53403994,  0.3047611 ],
       [-0.92213055, -0.10719726,  0.30790249, -0.38856532, -0.47533014],
       [ 1.56997199, -1.47678885,  0.33273415,  1.47140742,  1.18070086],
       [-0.34794732,  0.47757218,  1.88235192, -1.40315756, -0.73969021],
       [ 0.0887406 , -0.43538419, -1.73773921, -1.23496312,  1.7456197 ],
       [ 1.07751429,  0.95242255,  1.24754488,  0.72911384, -1.12072823],
       [-1.46579825,  0.92509981, -1.04466154,  0.42629603, -1.0279233 ],
       [-1.33321348,  1.4501989 ,  0.03239288,  1.11026413, -0.89668956],
       [ 0.30059562, -1.45009422, -0.26155005,  1.70335297, -0.25050967],
       [-1.43773798, -0.24726556, -0.43400063, -0.70325168,  0.33623535],
       [ 0.30164788, -1.55279003, -0.62130451,  0.1780736 , -0.75173073],
       [-1.1315303 ,  0.72253467, -0.02503987, -1.08813209,  1.7674014 ]])
</code></pre>
<p>The example data consists of 15 samples, each of which has 5 features. The third command rescales each feature by subtracting its mean and dividing by its standard deviation.</p>
<p>In the book, Kneusel checks the mean of the fourth column of rescaled data to confirm that it’s basically zero. He gets <!-- $-1.33 \times 10^{-16}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>−</mi><mn>1.33</mn><mo>×</mo><msup><mn>10</mn><mrow><mi>−</mi><mn>16</mn></mrow></msup></mrow></math>, which is effectively zero for 64-bit floating point values.</p>
<p>To make sure I was doing things right, I ran this:</p>
<pre><code>python:
&gt;&gt;&gt; xs.mean(axis=0)
array([ 1.48029737e-17, -8.88178420e-17,  6.77698638e-17, -1.62832710e-16,
        4.44089210e-17])
</code></pre>
<p>While the fourth mean, <!-- $-1.63 \times 10^{-16}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>−</mi><mn>1.63</mn><mo>×</mo><msup><mn>10</mn><mrow><mi>−</mi><mn>16</mn></mrow></msup></mrow></math>, is certainly as good a zero as the book’s answer, it isn’t the same. To double-check my work, I ran this:</p>
<pre><code>python:
&gt;&gt;&gt; xs[:, 3].mean()
-1.3322676295501878e-16
</code></pre>
<p>This <em>is</em> the book’s value. What’s going on?</p>
<p>You may think this is perfectly normal. Floating point numbers are inherently inexact, and as long as I get a result that’s effectively zero, there’s nothing to be bothered about. But I was bothered.</p>
<p>I’m using the same function, <a href="https://numpy.org/doc/stable/reference/generated/numpy.mean.html"><code>mean</code></a>, from the same library, NumPy, on the same set of data, and I get different answers depending on how I invoke that function. Or maybe I do get the same answers. Here’s another command and its output:</p>
<pre><code>python:
&gt;&gt;&gt; for i in range(xs.shape[1]):
...     print(f'Column {i}: {xs.mean(axis=0)[i] == xs[:, i].mean()}')
...
Column 0: True
Column 1: True
Column 2: True
Column 3: False
Column 4: False
</code></pre>
<p>For the first three columns, it doesn’t matter which way I use <code>mean</code>; for the last two, it does.</p>
<p>I should mention here that while the different results are obvious when the mean is close to zero, this behavior is present when operating on any 2D array. I’ve tried it out on several 2D arrays made from randomly generated floating point numbers.</p>
<p>During these experiments with randomly generated arrays, I got the sense that the likelihood of getting the same mean both ways got smaller as the number of rows in the array got larger. I decided to check that out with this script:</p>
<pre><code>python:
 1:  #!/usr/bin/env python3
 2:  
 3:  import numpy as np
 4:  
 5:  rng = np.random.default_rng()
 6:  
 7:  N = 5000
 8:  
 9:  for n in (10, 100, 1000, 10_000, 100_000):
10:    count = 0
11:    for i in range(N):
12:      x0 = rng.uniform(low=0, high=100, size=n)
13:      x1 = rng.uniform(low=100, high=200, size=n)
14:      x = np.column_stack((x0, x1))
15:      if x.mean(axis=0)[0] == x[:, 0].mean():
16:        count += 1
17:      if x.mean(axis=0)[1] == x[:, 1].mean():
18:        count += 1
19:    print(f'n = {n:,d}')
20:    print(f'  Same means: {count/(2*N):.2%}')
</code></pre>
<p>The results were</p>
<pre><code>n = 10
  Same means: 73.97%
n = 100
  Same means: 28.47%
n = 1,000
  Same means:  7.47%
n = 10,000
  Same means:  2.50%
n = 100,000
  Same means:  1.03%
</code></pre>
<p>So arrays with more rows <em>are</em> less likely to yield the same mean values when the <code>mean</code> function is used in these two different ways.</p>
<p>My working theory is that when I use <code>mean()</code> on a 1D array extracted from a 2D array, the computations are done differently than when I use <code>mean(axis=0)</code> on the 2D array. Mathematically the same, but computationally different. Maybe it’s as simple as the summation being done in a different order so the partial sums round off in slightly different ways. The more partial sums, the greater the likelihood of a different answer.</p>
<p>Ultimately, <!-- $-1.33 \times 10^{-16}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>−</mi><mn>1.33</mn><mo>×</mo><msup><mn>10</mn><mrow><mi>−</mi><mn>16</mn></mrow></msup></mrow></math> and <!-- $-1.63 \times 10^{-16}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>−</mi><mn>1.63</mn><mo>×</mo><msup><mn>10</mn><mrow><mi>−</mi><mn>16</mn></mrow></msup></mrow></math> are both zero, so this was mainly an exercise in satisfying my curiosity. I worry when computer results that ought to be exactly the same are only nearly the same. It usually means I’ve done something wrong, but apparently not in this case.</p>
<div class="update">
<p><strong>Update 9 Oct 2026 9:41 PM</strong><br/>
<a href="https://nathangrigg.com/">Nathan Grigg</a>, who has set me straight too many times to count, <a href="https://mastodon.social/@nathangrigg/117413516387160372">pointed out</a> that the <a href="https://numpy.org/doc/stable/reference/generated/numpy.sum.html">NumPy docs for <code>sum</code></a> explain how that function uses algorithms with different precision depending on</p>
<ol>
<li>whether the <code>axis</code> parameter is used or not; and</li>
<li>which axis is chosen if the parameter is used.</li>
</ol>
<p>There’s basically a speed/precision tradeoff at work. While it’s true that <a href="https://numpy.org/doc/stable/reference/generated/numpy.mean.html">the docs for <code>mean</code></a> don’t include this explanation, <code>sum</code> and <code>mean</code> are so alike that it must be the reason for the different answers.</p>
<p>Thanks, Nathan!</p>
</div>
  ]]>
</content:encoded>
</item>

<item>
<title>I’m pissed at the Library of Congress</title>
<link>https://leancrew.com/all-this/2026/10/im-pissed-at-the-library-of-congress/</link>
<pubDate>Fri, 09 Oct 2026 02:27:19 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/im-pissed-at-the-library-of-congress/</guid>
<description>
  <![CDATA[Today I tried to add a couple of recently purchased books to my library database using <a href="https://leancrew.com/all-this/2025/04/adding-books-to-my-library-database/">my <code>lccn2library</code> script</a>. It failed because the Library of Congress has either moved or deleted the files <code>lccn2library</code> relies on.]]>
</description>
<content:encoded>
  <![CDATA[<p>Today I tried to add a couple of recently purchased books to my library database using <a href="https://leancrew.com/all-this/2025/04/adding-books-to-my-library-database/">my <code>lccn2library</code> script</a>. It failed because the Library of Congress has either moved or deleted the files <code>lccn2library</code> relies on.</p>
<p>Let’s say I was trying to add <a href="https://lccn.loc.gov/26010978"><em>Applied Elasticity</em></a> by Timoshenko and Lessells.<sup id="fnref:westinghouse"><a href="#fn:westinghouse" rel="footnote">1</a></sup> If you go to the LoC’s MARC record for the book and click on the Download link, you’ll see a popup that looks like this:</p>
<p><img alt="LoC downloads popup" class="ss" src="https://leancrew.com/all-this/images2026/20261008-LoC%20downloads%20popup.png" title="LoC downloads popup" width="60%"/></p>
<p>So it looks like you can click the MODS link to get the <a href="https://www.loc.gov/standards/mods/">MODS file</a> for the book. That’s the file my script parses to get the information to add to the database. The MODS link for this book is</p>
<pre><code>https://lccn.loc.gov/26010978/mods
</code></pre>
<p>which is the URL form that <code>lccn2library</code> expects. Unfortunately, that link returns an empty file. And not just for that book, but for every book I tried. What does the little northeast-pointing arrow mean? Beats me. You’d think it was an indication that the MODS link was pointing to some external site, but that’s not the case.</p>
<p>Maybe I’ve caught the LoC in the middle of a transition to a new system for storing records in various formats. Maybe I’ll return in a month or two to find that everything is working again—perhaps with new URLs for the different formats. I hope so.</p>
<p>But until that hoped-for day, I’m pissed at the LoC because I have to add books to my database by hand using the <a href="https://www.sqlite.org/"><code>sqlite3</code></a> interactive command-line tool. Which is slow but not terrible, especially since I’m adding only a book or two at a time.</p>
<p>The command to add a new book to the database is</p>
<pre><code>INSERT INTO book(title, subtitle, volume, edition, publisher, published, lccn, loc, added)
VALUES(&lt;list of values separated by commas&gt;);
</code></pre>
<p>The book will be assigned an ID automatically. To get it, I run</p>
<pre><code>SELECT id,title FROM book WHERE added IS "2026-10-08";
</code></pre>
<p>The first field in the output will be the ID. The date must be a string with the current date in yyyy-mm-dd format.</p>
<div class="update">
<p><strong>Update 9 Oct 2026 10:12 AM</strong><br/>
On Mastodon, <a href="https://mastodon.gamedev.place/@enno/117408775161902954">Enno Rehling suggested</a> a shorter command to get the ID of the book just entered:</p>
<pre><code>SELECT last_insert_rowid();
</code></pre>
<p>After looking at the SQLite documentation for <a href="https://www.sqlite.org/lang_corefunc.html#last_insert_rowid"><code>last_insert_row</code></a>, I noticed that a similar thing could be done with the <a href="https://www.sqlite.org/lang_corefunc.html#max_scalar"><code>max</code> function</a>:</p>
<pre><code>SELECT max(id) from book;
</code></pre>
<p>This works because the <code>id</code> field is automatically incremented with each insertion. Commands like these can also be used below to get the ID of the most recently added author.</p>
<p>Thanks, Enno!</p>
</div>
<p>The new book may be written by an author already in the database. To check if that’s the case, I run</p>
<pre><code>SELECT * FROM author WHERE name LIKE "%&lt;portion of name&gt;%";
</code></pre>
<p>If the author’s already in the database, that command will return their ID. If not, it returns nothing. I then run</p>
<pre><code>INSERT INTO author(name) VALUES("&lt;full name&gt;");
</code></pre>
<p>and then the <code>SELECT</code> command above to get the author’s ID.</p>
<p>The final step is to add a record to the <code>book_author</code> table to tie the book and author together. That’s</p>
<pre><code>INSERT INTO book_author(book_id, author_id) VALUES(&lt;book ID&gt;, &lt;author ID&gt;);
</code></pre>
<p>As I said, this is slow because it’s several steps, but none of the steps are difficult.</p>
<p>What will I do if the LoC never fixes the problem of the missing MODS files? I suppose I could parse the <a href="https://www.loc.gov/marc/umb/um01to06.html">MARC records</a>, which are still accessible, but that would be a pain in the ass. Maybe I’ll build a little interactive script where I can just type in the necessary information and have the script generate and run the SQL commands. I hope it doesn’t come to that.</p>
<div class="update">
<p><strong>Update 6 Nov 2026 11:22 AM</strong><br/>
Sometime in the past month, the LoC fixed the MODS problem. I tried my <code>lccn2library</code> script on a newly purchased book, and it worked just fine. Reader Ben Trettel sent me an email last month, saying</p>
<ol>
<li>in his experience, the LoC is receptive to feedback; and</li>
<li>he had reported the bug to them.</li>
</ol>
<p>I made a note to myself to also report the bug but hadn’t gotten around to it, so I’m giving all credit to Bob for fixing this problem.</p>
</div>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:westinghouse">
<p>It’d be so cool to have this book. It was written when Timoshenko had just come to the US and was working at Westinghouse before landing a teaching gig at the University of Michigan. Sadly, this was not one of the books I was adding to my library. <a href="#fnref:westinghouse" rev="footnote">↩</a></p>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>Making a tall, skinny plot in Mathematica</title>
<link>https://leancrew.com/all-this/2026/10/making-a-tall-skinny-plot-in-mathematica/</link>
<pubDate>Wed, 07 Oct 2026 17:12:51 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/making-a-tall-skinny-plot-in-mathematica/</guid>
<description>
  <![CDATA[<a href="https://leancrew.com/all-this/2026/10/distorted-plots/">Yesterday’s post</a> included this plot:]]>
</description>
<content:encoded>
  <![CDATA[<p><a href="https://leancrew.com/all-this/2026/10/distorted-plots/">Yesterday’s post</a> included this plot:</p>
<p><img alt="Rescaled Moon and Earth orbit" class="ss" src="https://leancrew.com/all-this/images2026/20261006-Rescaled%20Moon%20and%20Earth%20orbit.svg" title="Rescaled Moon and Earth orbit" width="40%"/></p>
<p>I figured it was worth a quick post to show how I made it.</p>
<p>I had already done most of the work back in 2022 in a Mathematica notebook I made to support <a href="https://leancrew.com/all-this/2022/10/orbital-curvature/">my original orbital curvature post</a>. So I copied the pertinent calculations from that notebook, pasted them into a new one, and made the adjustments necessary to produce the plot. Here’s the new notebook:</p>
<iframe height="800" src="https://www.wolframcloud.com/obj/mark80/Published/allain-replot.nb?_embed=iframe" width="550"></iframe>
<p>The bottom half of the notebook is where the plotting commands are. The key items are:</p>
<ul>
<li>The definitions of <code>xGrid</code>, <code>xTicks</code>, <code>yGrid</code>, and <code>yTicks</code>. These produce lists of values used to lay out the grid spacing and tick mark labels. Most of this is self-explanatory, but I do want to call out the use of <a href="http://reference.wolfram.com/language/ref/NumberForm.html"><code>NumberForm</code></a> to format the tick labels with two decimal places. <code>NumberForm[x, {3, 2}]</code> is very much like Python’s <code>{x:3.2f}</code> in an f-string. Like most things in Mathematica, it’s kind of verbose, but it is readable.</li>
<li><p>The call to <a href="http://reference.wolfram.com/language/ref/ParametricPlot.html"><code>ParametricPlot</code></a> includes the option</p>
<pre><code>AspectRatio -&gt; .25/.05
</code></pre>
<p><a href="http://reference.wolfram.com/language/ref/AspectRatio.html"><code>AspectRatio</code></a> sets the ratio of height to width for the plot as a whole, so the value I set it to was based on the ratio of the values given in the <a href="http://reference.wolfram.com/language/ref/PlotRange.html"><code>PlotRange</code></a> option. The overall plot is 0.25 units high and 0.05 units wide, so to make the <em>x</em> and <em>y</em> units the same size, <code>AspectRatio</code> is set to that ratio. I entered it as a division instead of as the number 5 because I wanted the reasoning behind it to be clear. I suppose I could have been even more explicit by saying</p>
<pre><code>AspectRatio -&gt; (.25 - 0)/(1.01 - .96)
</code></pre>
<p>but there are limits to my verbosity, even when using Mathematica.</p></li>
</ul>
<p>To get the image out of the notebook, I right-clicked on it, selected <span class="menu">Save Graphic As…</span> from the popup menu, and chose SVG as the format. That’s why the plot stays smooth if you zoom in on it.</p>]]>
</content:encoded>
</item>

<item>
<title>Distorted plots</title>
<link>https://leancrew.com/all-this/2026/10/distorted-plots/</link>
<pubDate>Wed, 07 Oct 2026 02:08:33 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/10/distorted-plots/</guid>
<description>
  <![CDATA[Rhett Allain, physics professor and blogger, published <a href="https://rjallain.medium.com/why-do-textbooks-lie-about-the-moons-orbit-around-the-earth-7e29f9810aa9?sk=7cf9fbcc5f1f235461c6dc8d04367fa6">a nice article today</a> about the Moon’s orbit. It follows largely the same line of reasoning as <a href="https://leancrew.com/all-this/2022/10/orbital-curvature/">my post on the same topic</a> (and the various write-ups I linked to in that post) from a few years ago. I nodded my head as I read through it, until I got to a plot at the end of the article.]]>
</description>
<content:encoded>
  <![CDATA[<p>Rhett Allain, physics professor and blogger, published <a href="https://rjallain.medium.com/why-do-textbooks-lie-about-the-moons-orbit-around-the-earth-7e29f9810aa9?sk=7cf9fbcc5f1f235461c6dc8d04367fa6">a nice article today</a> about the Moon’s orbit. It follows largely the same line of reasoning as <a href="https://leancrew.com/all-this/2022/10/orbital-curvature/">my post on the same topic</a> (and the various write-ups I linked to in that post) from a few years ago. I nodded my head as I read through it, until I got to a plot at the end of the article.</p>
<p>For much of the article, Allain complains about how the orbit of the Moon as it and the Earth make their way around the Sun is often distorted in textbooks. Like this, which is a frame from an animated GIF in the article.</p>
<p><img alt="Distorted orbits around the Sun" class="ss" src="https://leancrew.com/all-this/images2026/20261006-Distorted%20orbits%20around%20the%20Sun.png" title="Distorted orbits around the Sun" width="80%"/></p>
<p>In this image, the Earth-Moon distance is exaggerated, and it looks like the Moon curves away from the Sun when it’s between the Earth and the Sun. To show that this is wrong, he plots portions of the Moon and Earth orbits without exaggerating the Moon-Earth distance:</p>
<p><img alt="Allain plot of Moon and Earth orbits" class="ss" src="https://leancrew.com/all-this/images2026/20261006-Allain%20plot%20of%20Moon%20and%20Earth%20orbits.png" title="Allain plot of Moon and Earth orbits" width="100%"/></p>
<p>Forgive me, but what the actual fuck? How can you spend over 1000 words complaining about graphical distortions and then produce this? Sure, it’s not the same distortion he was complaining about, but still. The whole point of the article was to show that the Moon’s path over the course of an Earth year is a sort of wobbly circle around the Sun. The arcs in this plot are nowhere near circular.</p>
<p>Here’s a properly scaled version of Allain’s plot:</p>
<p><img alt="Rescaled Moon and Earth orbit" class="ss" src="https://leancrew.com/all-this/images2026/20261006-Rescaled%20Moon%20and%20Earth%20orbit.svg" title="Rescaled Moon and Earth orbit" width="35%"/></p>
<p>Yes, the plot is really tall and skinny. It has to be to avoid distorting the paths. Note that the grids are square and represent the same distance in each direction.</p>
<p>We all know how this happened. The range of the <em>x</em> data is very small compared to the range of the <em>y</em> data, but because the plotting software defaults to a landscape-style graph, the paths come out looking like arcs of very wide ellipses. Unless the person running the software steps in to make sure <em>x</em> and <em>y</em> distances are drawn to the same scale.</p>
<p>I could write a post like this almost every day. So many people accept the defaults of their graphing software without thinking about how one or two small tweaks could make their plots so much better.</p>]]>
</content:encoded>
</item>

<item>
<title>Too dumb for AI</title>
<link>https://leancrew.com/all-this/2026/09/too-dumb-for-ai/</link>
<pubDate>Wed, 30 Sep 2026 21:13:17 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/too-dumb-for-ai/</guid>
<description>
  <![CDATA[I’ll admit that the first thing I looked into regarding <a href="https://www.reuters.com/world/us/trump-tech-executives-sign-morally-binding-ai-document-2026-09-29/">yesterday’s “Super Intelligence” meeting</a> at the White House was whether Tim Cook was there. He wasn’t, which was probably a relief to Tim, even though it’s also an admission that Apple isn’t considered a player in AI. By anyone, not even the idiots in the Trump administration.]]>
</description>
<content:encoded>
  <![CDATA[<p>I’ll admit that the first thing I looked into regarding <a href="https://www.reuters.com/world/us/trump-tech-executives-sign-morally-binding-ai-document-2026-09-29/">yesterday’s “Super Intelligence” meeting</a> at the White House was whether Tim Cook was there. He wasn’t, which was probably a relief to Tim, even though it’s also an admission that Apple isn’t considered a player in AI. By anyone, not even the idiots in the Trump administration.</p>
<p>You’ve probably seen the stupid misspelling above Trump’s signature on the “morally binding” agreement that came out of the meeting.</p>
<p><img alt="Signature page" class="ss" src="https://leancrew.com/all-this/images2026/20260930-Signature%20page.jpg" title="Signature page" width="80%"/></p>
<p>Yes, he signed it as the “President of the Unites States” and then proudly <a href="https://truthsocial.com/@realDonaldTrump/posts/117356435739432952">posted it to Truth Social</a>. I can’t imagine the agreement means anything, as you’d be hard-pressed to find a single moral shared among the people in the meeting. I will say, though, that Jensen Huang’s signature is pretty spiffy.</p>
<p>As for who was at the meeting, Trump <a href="https://truthsocial.com/@realDonaldTrump/posts/117355137125519227">also posted</a> this seating chart, which hurts to look at:</p>
<p><a href="https://leancrew.com/all-this/images2026/20260930-Seating%20chart.jpg"><img alt="Seating chart" class="ss" src="https://leancrew.com/all-this/images2026/20260930-Seating%20chart.jpg" title="Seating chart" width="80%"/></a></p>
<p>Click the image to see it at full size, if you dare. Also, don’t feel obligated to follow either of the Truth Social links; I’m including them only because I feel an obligation to give credit—or in this case blame.</p>
<p>Lots of the images Trump posts are AI-generated, but I find it hard to believe that any AI system would produce something with this many graphical errors:</p>
<ul>
<li>The chairs on the left side of the table are vertically offset from those on the right side, even though there are the same number on each side. Poor Will Scharf is nearly off the end.</li>
<li>The chairs on the left are distinctly farther from the table than the chairs on the right.</li>
<li>The vertical spaces between chairs on a given side aren’t equal. The seats near the ends are closer to one another than the seats near the center.</li>
<li>The two lines of chairs aren’t horizontally aligned. This was perhaps the smallest mistake, and you probably can’t see it unless you zoom in, but what drawing app wouldn’t automatically align the items?</li>
<li>The names and chairs aren’t aligned vertically. You’d expect the names to be centered vertically with the chairs, and many of them are at least close to that, but once you see Alex Karp and Andrew Ferguson you’ll notice several similar misalignments.</li>
<li>The names on the left side should be right-aligned but aren’t. Susie Wiles is way out of her chair, while David Sacks has nearly crashed into his.</li>
<li>Similarly, the names on the right side should be left-aligned but aren’t. See Richard Walters and Sanjay Mehrotra for the most extreme misalignments.</li>
</ul>
<p>And then there’s the best one:</p>
<ul>
<li>The chairs are facing away from the table.</li>
</ul>
<p>The meeting probably would have been better for all of us if this had been so.</p>]]>
</content:encoded>
</item>

</channel>
</rss>

