Squaring the triangle with help

Yesterday, Numberphile released a delightful video 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 “Haberdasher’s Puzzle” and is reproduced in Dudeney’s 1907 book The Canterbury Puzzles. The problem was solved by C.W. McElroy after Dudeney published it in a periodical called the Weekly Dispatch.

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:

Equilateral triangle pieces in video

What James doesn’t do in the video is explain how you position the cuts. Dudeney shows a straightedge-and-compass construction 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.

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 A through J (to avoid confusion, I is omitted).

Equilateral triangle with cuts

The lines GJ and DH are perpendicular to Line EF.

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:

  1. Rotate the green piece 180° about Point G.
  2. Rotate the green and orange pieces together 180° about Point E.
  3. Rotate the green, orange, and blue pieces together 180° about Point D.

Here’s an illustration of the steps:

Rotations from triangle to square

In this illustration, the red piece stays put through all the steps.

We can rotate the square to make its sides horizontal and vertical and label the various points to show where they ended up.

Aligned square

The vertices of the equilateral triangle have coalesced into a single point in the interior of the square. The right angles at H and J have split up to form the corners of the square.

Before we get into the algebra of the situation, let’s think about the various dimensions. The side lengths of the square, s, must be equal, so

s=2GJ——=2DH——=EJ——+EH——=FH——+FJ——

A bit of algebra tells us that

2s=EJ——+EH——+FH——+FJ——=(EJ——+FJ——)+(FH——+EH——)

Looking back at the drawing of the equilateral triangle, we see that the terms in the parentheses make up Line EF. Therefore, the length of EF is the side length of the square, s.

Now it’s time for some analytic geometry. We’ll put the origin of our xy coordinate system at Point A, and we’ll assume each side of the equilateral triangle has unit length. The coordinates are:

A:(0,0)B:(1,0)C:(12,32)

The area of the triangle is

12(1)(32)=34

This is the same as the area of the square, so

s=31/42

This will be the length of EF——.

Points D and E are the midpoints of AC and BC:

D:(14,34)E:(34,34)

Since we know the length of Line EF and the position of Point E, we can calculate the position of Point F along Line AB:

F:(14[3−43−3],0)

As I said, it’s kind of messy, but that’s what Mathematica is for. To six decimal places, the x coordinate is 0.254508. This is almost directly under Point D, but not quite.

Now we need the position of Point G. As it happens, it’s exactly 12 unit to the right of Point F:

G:(14[5−43−3],0)

To six decimal places, this is 0.754508.

How do we know this? Consider this drawing:

Second green triangle

The light green triangle, DEH, is congruent to the dark green triangle, GFJ. We know this because:

  1. The right angle at H matches the right angle at J.
  2. The length of DH is equal to the length of GJ (see the discussion above).
  3. The angle at D matches the angle at G because DE is parallel to FG and DH is parallel to GJ.

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 DE. The distance between D and E is 12, so that must also be the distance between F and G.

With the locations of F and G known, we can find H and J by running lines perpendicular to EF that intersect F and G. The full equations for the positions of H and J are even messier than those of F and G, so I’ll just give the six-place decimal approximations:

H:(0.466506,0.185267)J:(0.538001,0.247746)

If you want to go through the Mathematica that generated these results and the graphics, here it is:


HomeView

Given that I typically take a wait-and-see attitude when Apple introduces a new product, I was surprised to find myself ordering a HomeView within a day of its announcement. I was even more surprised when lots of otherwise pro-Apple people—this guy, for example—were skeptical about its value.

HomeView

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.

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:

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.

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 the annoying family in the video showed that that was no problem. And there’s an outlet right there, so the power cord can be stored neatly.

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.

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 that’s been sitting on the shelf for a year or so.


TextEdit vs. Pages

A few days ago, Allison Sheridan wrote a very persuasive article 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.

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.

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.

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 MacWrite and WriteNow 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.

TextEdit example

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.

Pages example

Allison is right that the Format sidebar in Pages could use a rethink. Style, Layout, and More are pretty useless names for the settings they expose—calling them One, Two, and Three would be just about as informative. But at least the sidebar can be hidden until you need it.

Pages example with formatting sidebar

Another TextEdit deficiency that bothers me is the lack of a sensible zoom control. There are Zoom In and Zoom Out commands in the View 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?

Update 18 Oct 2026 4:19 PM
Andrew Kemp pointed out on Mastodon that you can 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!

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.

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.

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.


Randomizing array indices

I mentioned in my last post that I’m working my way through Practical Deep Learning by Ronald Kneusel. The math so far has been straightforward, and the code is written in Python, which I find easy to follow.

“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.

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.

One of the examples used in the book is the well-known iris measurement dataset. There are 150 records in the dataset; here are the first ten:

Index Sepal
length
Sepal
width
Petal
length
Petal
width
Class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
5 5.4 3.9 1.7 0.4 Iris-setosa
6 4.6 3.4 1.4 0.3 Iris-setosa
7 5.0 3.4 1.5 0.2 Iris-setosa
8 4.4 2.9 1.4 0.2 Iris-setosa
9 4.9 3.1 1.5 0.1 Iris-setosa

There are three species of iris in the dataset: setosa, versicolor, and virginica. The records, which are easy to find, are sorted according to class, and it’s necessary to randomize the records for certain operations.

Let’s say the measurements are in a 2D (150×4) NumPy array called x, and the classes, which have been reduced to the integers 0, 1, and 2 for setosa, versicolor, and virginica, respectively, are in a 1D NumPy array called y. Here they are in an interactive Python session:

>>> 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]])
>>> 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])

Kneusel uses NumPy’s integer array indexing to randomize y and the rows of x using the same rearrangement in each. Here’s what he does:

>>> idx = np.argsort(np.random.random(x.shape[0]))
>>> x = x[idx, :]
>>> y = y[idx]

The first line starts by generating an array of 150 (x.shape[0] is 150) random numbers between 0 and 1 via random.random; argsort then returns an array of the indices of the sorted version of that array. So idx is an array of the integers from 0 through 149 in random order. The two lines after that rearrange x and y in that same random order.

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 x and y 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 idx array directly?

>>> idx = np.random.permutation(x.shape[0])

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.

I tested the two methods using IPython’s %timeit magic command. Here’s what I found, with 1,000,000 indices instead of 150:

>>> %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)

>>> %timeit np.random.permutation(1_000_000)
5.98 ms ± 17.8 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

So the simple code is both more direct and 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.


You may have noticed that the code above uses NumPy’s older random number system. The current approach would be to create a Generator instance and use that as the basis for later function calls. Would this have any effect on the timing results? Let’s see.

>>> rng = np.random.default_rng()
>>> %timeit np.argsort(rng.random(1_000_000))
65 ms ± 188 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit rng.permutation(1_000_000)
4.91 ms ± 10.9 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

No, the runtimes are about the same.