<?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>John D. Cook</title>
	<atom:link href="http://www.johndcook.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.johndcook.com/blog</link>
	<description>Applied Mathematics Consulting</description>
	<lastBuildDate>Sun, 12 Jul 2026 19:26:29 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.johndcook.com/wp-content/uploads/2020/01/cropped-favicon_512-32x32.png</url>
	<title>John D. Cook</title>
	<link>https://www.johndcook.com/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Posterior variance</title>
		<link>https://www.johndcook.com/blog/2026/07/12/posterior-variance/</link>
					<comments>https://www.johndcook.com/blog/2026/07/12/posterior-variance/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 12 Jul 2026 19:26:29 +0000</pubDate>
				<category><![CDATA[Statistics]]></category>
		<category><![CDATA[Bayesian]]></category>
		<category><![CDATA[Probability and Statistics]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247336</guid>

					<description><![CDATA[<p>A few days ago I wrote a post entitled Does additional data always reduce posterior variance?. In a nutshell, the answer is no, not always. That led the previous post which looked at posterior means for three Bayesian models, showing how the posterior mean is a weighted average of the prior mean and the mean [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/12/posterior-variance/">Posterior variance</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>A few days ago I wrote a post entitled <a href="https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/">Does additional data always reduce posterior variance?</a>. In a nutshell, the answer is no, not always.</p>
<p>That led the <a href="https://www.johndcook.com/blog/2026/07/12/posterior-mean/">previous post</a> which looked at posterior means for three Bayesian models, showing how the posterior mean is a weighted average of the prior mean and the mean of the new data. The weights are <em>precisions</em>, which means something different for each model.</p>
<p>For the beta-binomial model, variance may increase when seeing unexpected data (details <a href="https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/">here</a>), but precision always increases.</p>
<p>For the normal-normal model precision is the reciprocal of variance. Every new data point makes precision go up and posterior variance go down.</p>
<p>The Poisson-gamma model may be the most interesting. As stated in the previous post, if data has a Poisson distribution with parameter λ, and λ has a gamma(α<sub>0</sub>, β<sub>0</sub>) prior distribution, then the posterior distribution on λ after observing <em>k</em> events over time <em>t</em> has a gamma(α<sub>0</sub> + <em>k</em>, β<sub>0</sub> + <em>t</em>) posterior distribution. Therefore the posterior variance is</p>
<p style="padding-left: 40px;">(α<sub>0</sub> + <em>k</em>) / (β<sub>0</sub> + <em>t</em>)².</p>
<p>Note the posterior variance is an increasing function of <em>k</em> and a decreasing function of <em>t</em>. This means that the posterior variance increases <em>every time</em> an event is observed, and it decreases quadratically between observations.</p>
<p>Here&#8217;s an illustration. I simulated data from a Poisson process with λ and used a gamma(1, 1) prior on λ. Here&#8217;s a plot of the posterior variance.<img fetchpriority="high" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/posterior_variance.png" width="480" height="360" /></p>The post <a href="https://www.johndcook.com/blog/2026/07/12/posterior-variance/">Posterior variance</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/12/posterior-variance/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Posterior mean</title>
		<link>https://www.johndcook.com/blog/2026/07/12/posterior-mean/</link>
					<comments>https://www.johndcook.com/blog/2026/07/12/posterior-mean/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 12 Jul 2026 17:48:14 +0000</pubDate>
				<category><![CDATA[Statistics]]></category>
		<category><![CDATA[Bayesian]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247322</guid>

					<description><![CDATA[<p>Common sense says that what you believe after seeing new data should be some sort of compromise between what you believed before and what the new data says. You don&#8217;t want to ignore previous information or new information. How much should new data change your prior beliefs? When prior judgment and new information are in [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/12/posterior-mean/">Posterior mean</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Common sense says that what you believe after seeing new data should be some sort of compromise between what you believed before and what the new data says. You don&#8217;t want to ignore previous information or new information.</p>
<p>How much should new data change your prior beliefs? When prior judgment and new information are in conflict, which one should be given the benefit of the doubt?</p>
<p>Bayesian data models provide a framework for making such decisions quantitative and objective. The choice of a data model is somewhat subjective—whether it&#8217;s a Bayesian model or not—but given a Bayesian model, the rules for updating the representation of your beliefs are objective. As some put it, you &#8220;turn the Bayesian crank.&#8221; A likelihood model and a prior on parameters together specify how new data changes the prior distribution into a posterior distribution.</p>
<p>We will make this more concrete with three examples.</p>
<h2>Normal-normal model</h2>
<p>Suppose that data <em>X</em> has a normal distribution with unknown mean &mu; and known variance σ², and we assume that <em>a priori</em> &mu; has a normal distribution with mean μ<sub>0</sub> and variance σ<sub>0</sub>².</p>
<p>After observing <em>x</em>, the posterior distribution on &mu; also has a normal distribution, but with a different mean and variance. Its mean is somewhere between the prior mean and <em>x</em>. We will ignore the change in the variance for this post.</p>
<p>The posterior mean of &mu; is</p>
<p><img class='aligncenter' src='https://www.johndcook.com/postmean1.svg' alt='\mu_{\text{post}} = \frac{\dfrac{\mu_0}{\sigma_0^2} + \dfrac{x}{\sigma^2}}{\dfrac{1}{\sigma_0^2} + \dfrac{1}{\sigma^2}}' style='background-color:white' height='103' width='128' /></p>
<p>This equation becomes more understandable when we introduce precisons τ = 1/σ² and τ<sub>0</sub> = 1/σ<sub>0</sub>².</p>
<p>Then we have</p>
<p><img class='aligncenter' src='https://www.johndcook.com/postmean2.svg' alt='\mu_{\text{post}} = \frac{\mu_0 \cdot \tau_0 + x \cdot \tau}{\tau_0 + \tau}' style='background-color:white' height='41' width='163' /></p>
<p>which you can read as saying the posterior mean is the weighted average of the prior mean and <em>x</em>, with the weights given by the precision. Intuitively, you take the weighted mean of your conclusions from previous data and new data, weighting the mean according to how much confidence you have in each.</p>
<h2>Beta-binomial model</h2>
<p>Now let&#8217;s switch over to a different data model. Now assume <em>X</em> is a binary random variable, with probability of success <em>p</em> and probability of failure 1 − <em>p</em>, and we assume <em>p</em> has a beta(<em>a</em>, <em>b</em>) distribution.</p>
<p>After observing <em>s</em> successes and <em>f</em> failures, the posterior mean of the distribution on <em>p</em> becomes</p>
<p><img class='aligncenter' src='https://www.johndcook.com/postmean3.svg' alt='p_{\text{post}} = \frac{a + s}{a + b + s + f}' style='background-color:white' height='42' width='164' /></p>
<p>We can rewrite this as</p>
<p><img class='aligncenter' src='https://www.johndcook.com/postmean4.svg' alt='p_{\text{post}} = \frac{(a + b) \dfrac{a}{a+b} + (s + f) \dfrac{s}{s+f}}{(a + b) + (s +f)}' style='background-color:white' height='68' width='286' /></p>
<p>This says that the posterior mean is the weighted average of the prior mean <em>a</em>/(<em>a</em> + <em>b</em>) and the mean of the data <em>s</em>/<em>n</em>. The weights are the prior effective sample size <em>a</em> + <em>b</em> and the sample size of the new data <em>n</em>. In this example (effective) sample size is playing the role that precision played in the normal-normal model above.</p>
<h2>Gamma-Poisson model</h2>
<p>Suppose data have a Poisson distribution with parameter &lambda;, and &lambda; has a gamma(&alpha;<sub>0</sub>, &beta;<sub>0</sub>) prior distribution [1]. And suppose you observe <em>k</em> events over time <em>t</em>. Then the posterior distribution of &lambda; given the data has a gamma(&alpha;<sub>0</sub> + <em>k</em>, &beta;<sub>0</sub> + <em>t</em>) prior distribution and the mean of the posterior distribution is given by</p>
<p><img class='aligncenter' src='https://www.johndcook.com/postmean5.svg' alt='\lambda_{\text{post}} = \frac{\alpha_0 + k}{\beta_0 + t} = \frac{\beta_0 (\alpha_0 / \beta_0) + t (k / t)}{\beta_0 + t}' style='background-color:white' height='46' width='291' /></p>
<p>As before, the posterior mean is a weighted average of the prior mean and new data, and the weights are interpretable as some sort of measure of confidence, namely time. The variable <em>t</em> is directly time and the parameter &beta;<sub>0</sub> is sort of an effective time, just as <em>a</em> + <em>b</em> is an effective sample size for the beta distribution.</p>
<h2>Common thread</h2>
<p>In each example the posterior mean is the weighted average of the prior mean and the mean of the data, with the weights given by a precision. However, precision means something different in each example. In the normal-normal model, precision is the reciprocal of variance, but in the beta-binomial model precision is sample size and in the Poisson-gamma model precision is time.</p>
<p>What all three examples have in common is that they are conjugate models using distributions from the &#8220;exponential family&#8221; of probability distributions. In technical terms, precision is the multiplicative factor on the sufficient statistic in the exponent of the posterior kernel.</p>
<h2>Related posts</h2>
<ul>
<li class='link'><a href='https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/'>Does additional data always reduce posterior variance?</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/distribution_chart/'>Diagram of probability distribution relationships</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/bayesian-consulting/'>Bayesian statistics consulting</a></li>
</ul>
<p>[1] There are multiple conventions for parameterizing the gamma distribution. Here we&#8217;re using the shape-rate parameterization, where the mean is &alpha;/&beta;.</p>The post <a href="https://www.johndcook.com/blog/2026/07/12/posterior-mean/">Posterior mean</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/12/posterior-mean/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Progress on Gilbreath&#8217;s conjecture</title>
		<link>https://www.johndcook.com/blog/2026/07/11/progress-on-gilbreaths-conjecture/</link>
					<comments>https://www.johndcook.com/blog/2026/07/11/progress-on-gilbreaths-conjecture/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 11 Jul 2026 21:30:11 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247319</guid>

					<description><![CDATA[<p>Years ago I wrote about Gilbreath&#8217;s conjecture. It&#8217;s a simple conjecture; you could explain it to anyone who understands what prime numbers are. See the linked post for a description of the problem. Gilbreath&#8217;s conjecture is simple, but it&#8217;s also kinda weird. As I wrote before, Paul Erdős speculated that Gilbreath’s conjecture is true but [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/11/progress-on-gilbreaths-conjecture/">Progress on Gilbreath’s conjecture</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Years ago I wrote about <a href="https://www.johndcook.com/blog/2009/09/09/gilbreath-conjecture/">Gilbreath&#8217;s conjecture</a>. It&#8217;s a simple conjecture; you could explain it to anyone who understands what prime numbers are. See the linked post for a description of the problem.</p>
<p>Gilbreath&#8217;s conjecture is simple, but it&#8217;s also kinda weird. As I wrote before,</p>
<blockquote><p>Paul Erdős speculated that Gilbreath’s conjecture is true but it would be 200 years before anyone could prove it. I find Erdős’s conjecture more interesting than Gilbreath’s conjecture.</p></blockquote>
<p>The conjecture is hard in a way that, say, solving a nasty-looking differential equation is not. Over the last three centuries, mathematics has developed quite a toolbox for solving differential equations. But Gilbreath&#8217;s conjecture is just odd enough that it&#8217;s not at all clear what kind of tool might be useful in approaching it.</p>
<p>Terence Tao has a <a href="https://terrytao.wordpress.com/2026/07/11/gilbreaths-conjecture-a-cramer-random-model-and-a-deterministic-analysis/">new blog post</a> announcing a <a href="https://arxiv.org/abs/2607.08712">paper</a> that he and two coauthors wrote on a random model intended to mimic Gilbreath&#8217;s calculation on primes. This random model is more sophisticated than the little game Gilbreath was playing, but it&#8217;s also much more amenable to analysis by established techniques. Tao&#8217;s post gives a heuristic explanation for why Gilbreath&#8217;s conjecture is plausible, but then adds</p>
<blockquote><p>However, it seems well beyond current technology to try to make these heuristics rigorous; even the first step … is far out of reach.</p></blockquote>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2025/07/29/moessners-magic/">Moessner’s Magic</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2024/04/25/closed-form-pde/">Closed form solutions to PDEs</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/07/11/progress-on-gilbreaths-conjecture/">Progress on Gilbreath’s conjecture</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/11/progress-on-gilbreaths-conjecture/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Reproducing a geometry theorem diagram</title>
		<link>https://www.johndcook.com/blog/2026/07/06/arc-hypotenuse/</link>
					<comments>https://www.johndcook.com/blog/2026/07/06/arc-hypotenuse/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 06 Jul 2026 14:22:35 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Geometry]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247315</guid>

					<description><![CDATA[<p>I ran across a geometry theorem with the following diagram. The theorem corresponding to the diagram is interesting, but I found reproducing the diagram more interesting. The segment AB is a diameter and the line CD is perpendicular to the diameter. Assume the outer circle is a unit circle. I guessed C = (cos(1), sin(1)) and made the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/06/arc-hypotenuse/">Reproducing a geometry theorem diagram</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I ran across a geometry theorem with the following diagram.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/three_circles1.png" width="407" height="360" /></p>
<p>The theorem corresponding to the diagram is interesting, but I found reproducing the diagram more interesting.</p>
<p>The segment <em>AB</em> is a diameter and the line <em>CD</em> is perpendicular to the diameter.</p>
<p>Assume the outer circle is a unit circle. I guessed <em>C</em> = (cos(1), sin(1)) and made the following diagram.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/three_circles2.png" width="360" height="360" /></p>
<p>I guessed the value of <em>C</em> by eyeballing it, but in retrospect this would have been a convenient value for the creator of the original diagram to have chosen.</p>
<p>Drawing the blue circle inscribed in the triangle was easy using the equations for the center and radius from <a href="https://www.johndcook.com/blog/2026/06/25/incircle-excircle/">this post</a>. Drawing the other two circles, the green and orange circles, was harder. They are also inscribed circles, but not inscribed in a triangle. They&#8217;re inscribed in a three-sided figure with two perpendicular sides and a circular arc.</p>
<p>The radius <em>r</em> of the green circle is the distance from the center of the circle to each of its tangent lines. Also, the distance from the origin to the center of the circle must be 1 − <em>r</em>. This is enough information to set up a quadratic equation for <em>r</em>. The same reasoning applies to the orange circle.</p>
<p>The original diagram comes from [1] and the theorem it illustrates says the diameter of the blue circle equals the sum of the radii of the green and orange circles.</p>
<h2>Python code</h2>
<p>In case you&#8217;re interested, here&#8217;s the code that created the diagram.</p>
<pre>
#!/usr/bin/env -S uv run --script

# /// script
# dependencies = ["numpy", "matplotlib"]
# ///

import numpy as np
import matplotlib.pyplot as plt

def connect(A, B, color='gray'):
    plt.plot([A[0], B[0]], [A[1], B[1]], color=color, linewidth=2)

def circle(c, r, color='gray'):
    t = np.linspace(0, 2*np.pi)
    plt.plot(c[0] + r*np.cos(t), c[1] + r*np.sin(t), color=color, linewidth=2)

def quadratic(a, b, c):
    det = b**2 - 4*a*c
    return ((-b - det**0.5)/(2*a), (-b + det**0.5)/(2*a))

A = np.array([-1, 0])
B = np.array([ 1, 0])
C = np.array([np.cos(1), np.sin(1)])
a = np.linalg.norm(B - C)
b = np.linalg.norm(A - C)
c = np.linalg.norm(B - A)
s = (a + b + c)/2

circle([0,0], 1)
connect(A, B,)
connect(A, C)
connect(C, B)
connect(C, C*np.array([1, -1]))

center = (a*A + b*B + c*C)/(2*s)
radius = 0.5*a*b/s
circle(center, radius, 'C0')

Ex = C[0]
roots = quadratic(1, 2 + 2*Ex, Ex**2 - 1)
r = roots[1] # Smaller root is negaive
print(roots)
center = (r + Ex, -r)
circle(center, r, 'C1')

roots = quadratic(1, 2 - 2*Ex, Ex**2 - 1)
r = roots[1] # Smaller root is negaive
center = (Ex - r, -r)
circle(center, r, 'C2')

plt.gca().set_aspect("equal")
plt.axis("off")
plt.show()
</pre>
<p>[1] Leon Bankoff. A Geometrical Coincidence. Mathematics Magazine, Vol. 37, No. 5 (Nov., 1964), p. 324.</p>The post <a href="https://www.johndcook.com/blog/2026/07/06/arc-hypotenuse/">Reproducing a geometry theorem diagram</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/06/arc-hypotenuse/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>e approximation</title>
		<link>https://www.johndcook.com/blog/2026/07/06/e-approximation/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 06 Jul 2026 12:22:29 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247311</guid>

					<description><![CDATA[<p>I ran across the approximation e ≈ 2721/1001 recently. What makes this remarkable is its accuracy relative to the size of the denominator. You can create a trivial approximation just by truncating a decimal expansion e ≈ 2718/1000 but this is only good to four significant figures, but 2721/1001 is good to seven, almost eight, [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/06/e-approximation/">e approximation</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I ran across the approximation</p>
<p style="padding-left: 40px;"><em>e</em> ≈ 2721/1001</p>
<p>recently. What makes this remarkable is its accuracy relative to the size of the denominator.</p>
<p>You can create a trivial approximation just by truncating a decimal expansion</p>
<p style="padding-left: 40px;"><em>e</em> ≈ 2718/1000</p>
<p>but this is only good to four significant figures, but 2721/1001 is good to seven, almost eight, significant figures.</p>
<pre>e         = 2.71828182… 
2721/1001 = 2.71828171…
</pre>
<p>The comparison is more impressive in binary.</p>
<pre>$ bc -l
&gt;&gt;&gt; obase=2
&gt;&gt;&gt; 2721/1001
10.10110111111000010100…
&gt;&gt;&gt; e(1)
10.10110111111000010101…
</pre>
<p>The denominator is a 10-bit number but the approximation is accurate to 21 bits.</p>The post <a href="https://www.johndcook.com/blog/2026/07/06/e-approximation/">e approximation</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Does additional data always reduce posterior variance?</title>
		<link>https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/</link>
					<comments>https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 04 Jul 2026 02:50:36 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Bayesian]]></category>
		<category><![CDATA[Formal methods]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247301</guid>

					<description><![CDATA[<p>A discussion over lunch today brought up the fact that additional data does not always decrease the size of a confidence interval. This post will look at this from a Bayesian perspective. In general, new information reduces your uncertainty regarding whatever you&#8217;re estimating. The posterior distribution becomes more concentrated as more data are collected. That&#8217;s [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/">Does additional data always reduce posterior variance?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>A discussion over lunch today brought up the fact that additional data does not always decrease the size of a confidence interval. This post will look at this from a Bayesian perspective.</p>
<p>In general, new information reduces your uncertainty regarding whatever you&#8217;re estimating. The posterior distribution becomes more concentrated as more data are collected.</p>
<p>That&#8217;s what happens &#8220;in general&#8221; but does it necessarily happen every time you get new data? Conceivably if you get surprising data, data that is very unlikely given your current prior, posterior uncertainty might increase.</p>
<h2>Beta-binomial model</h2>
<p>To show that this is the case, suppose the probability of success in some binary trial has parameter θ and that θ has a beta prior. You could imagine this prior to be the posterior after having made some number of previous observations. Can a new observation increase the posterior variance in θ? If so, under what conditions?</p>
<p>The variance of a beta(<em>a</em>, <em>b</em>) random variable is</p>
<p style="padding-left: 40px;"><em>ab</em> / (<em>a</em> + <em>b</em>)²(<em>a</em> + <em>b</em> + 1).</p>
<p>After observing a successful trial, the posterior distribution on θ is beta(<em>a</em> + 1, <em>b</em>). We can calculate the ratio of the posterior variance to the prior variance and ask under what circumstances, if any, the ratio is greater than 1.</p>
<p>If 2<em>a</em> ≥ <em>b</em> the posterior variance will be strictly less than the prior variance. This says if the prior mean odds against a success are no more than 2 : 1, observing a success will reduce the variance. (So will observing a failure.) But for any value of <em>b</em>, you can find a small enough value of <em>a</em> that observing a success will increase the variance.</p>
<h2>Normal-normal model</h2>
<p>Whether an observation can increase the posterior variance depends on the data model. If your data have a normal likelihood function with known variance and a normal prior on the mean θ, the posterior variance is always less than the prior observation, and it reduces by the same amount, independent of the observation <em>x</em>. If <em>x</em> is very unlikely <em>a priori</em> then it will pull the posterior mean toward itself more than an observation that is more concordant with the prior would have, but the change in the posterior variance is the same.</p>
<h2>Proof of beta theorem</h2>
<p>Here is a proof in Lean 4 of the statement above that if 2<em>a</em> ≥ <em>b</em> the posterior variance will be strictly less than the prior variance.</p>
<pre>import Mathlib

set_option linter.style.header false

noncomputable def f (a b : ℝ) : ℝ := a * b / ((a + b) ^ 2 * (a + b + 1))

theorem f_ratio_lt_one' (a b : ℝ) (ha : 0 &lt; a) (hb : 0 &lt; b) (hab : b ≤ 2 * a) :
    f (a + 1) b / f a b &lt; 1 := by
  have hs : 0 &lt; a + b := by linarith
  have h2ab : 0 ≤ 2 * a - b := by linarith
  have hprod : 0 ≤ (a + b) * (2 * a - b) := mul_nonneg hs.le h2ab
  -- key polynomial inequality (∗)
  have key : (a + 1) * (a + b) ^ 2 &lt; a * ((a + b + 1) * (a + b + 2)) := by
    nlinarith [hprod, ha]
  -- nonzero facts needed to clear denominators
  have ha' : a ≠ 0 := ne_of_gt ha
  have hb' : b ≠ 0 := ne_of_gt hb
  have hs' : a + b ≠ 0 := ne_of_gt hs
  have hs1' : a + b + 1 ≠ 0 := by positivity
  have hs2' : a + b + 2 ≠ 0 := by positivity
  have ha1' : a + 1 ≠ 0 := by positivity
  -- express the ratio as a single closed-form fraction
  have hratio : f (a + 1) b / f a b
      = ((a + 1) * (a + b) ^ 2) / (a * ((a + b + 1) * (a + b + 2))) := by
    unfold f
    have e : a + 1 + b = a + b + 1 := by ring
    rw [e]
    field_simp
    ring
  rw [hratio, div_lt_one (by positivity)]
  exact key
</pre>The post <a href="https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/">Does additional data always reduce posterior variance?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/03/does-additional-data-always-reduce-posterior-variance/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>DNA Sequence Alignment and Kings</title>
		<link>https://www.johndcook.com/blog/2026/06/30/dna-sequence-alignment-and-kings/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 01 Jul 2026 00:21:21 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<category><![CDATA[Genetics]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247287</guid>

					<description><![CDATA[<p>This morning I wrote a post that included the central Delannoy numbers. The nth central Delannoy number Dn counts the number of ways a king can move from one corner of a chessboard to the diagonally opposite corner without backtracking. The more general Delannoy numbers Dm,n are the analogy for an m × n rectangular board, not [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/30/dna-sequence-alignment-and-kings/">DNA Sequence Alignment and Kings</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p><a href="https://www.johndcook.com/blog/2026/06/30/silver-kings/">This morning</a> I wrote a post that included the central Delannoy numbers. The <em>n</em>th central Delannoy number <em>D</em><sub><em>n</em></sub> counts the number of ways a king can move from one corner of a chessboard to the diagonally opposite corner without backtracking.</p>
<p>The more general Delannoy numbers <em>D</em><sub><em>m</em>,<em>n</em></sub> are the analogy for an <em>m</em> × <em>n</em> rectangular board, not necessarily square.</p>
<p><em>D</em><sub><em>m</em>,<em>n</em></sub> is also the number of possible sequence alignments for a strand of DNA with <em>m</em> base pairs and a strand with <em>n</em> base pairs [1]. At each step in the alignment process, you can introduce a gap in the first strand, the second strand or neither, which is analogous to the king who can move N, E, or NE at each step.</p>
<p>The Delannoy numbers can be computed recursively:</p>
<pre>def D(m, n):
    if m == 0 or n == 0:
        return 1
    return D(m - 1, n) + D(m, n - 1) + D(m - 1, n - 1)
</pre>
<p>The code above can be sped up tremendously by adding the decorator</p>
<pre>@lru_cache(maxsize=None)</pre>
<p>above the function definition to turn on memoization. I did an experiment computing <em>D</em><sub>12,15</sub> with and without memoization and the times were 77.1805 seconds and 0.000062 seconds respectively, i.e. memoization made the code over a million times faster.</p>
<p>Incidentally, <em>D</em><sub>12,15</sub> = 2653649025 and so there are a <em>lot</em> of ways to align even short sequences unless you place some restriction on the permissible alignments.</p>
<p><strong>Update</strong>: Here&#8217;s a heatmap plotting log<sub>10</sub>(<em>D</em><sub><em>m</em>,<em>n</em></sub>). Obviously the function increases with <em>m</em> and <em>n</em>: bigger chessboards have more possible paths. Moreover, it&#8217;s larger along the diagonal (i.e. the <em>central</em> Delannoy numbers). If you look along northeast to southwest diagonals, the function is largest in the middle where <em>m</em> = <em>n</em>. </p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/delannoy_heatmap.png" width="500" height="375" /></p>
<p>[1] Torres, A., Cabada, A., &amp; Nieto, J. J. (2003). An exact formula for the number of alignments between two DNA sequences. <em>DNA Sequence, 14</em>(6), 427–430. https://doi.org/10.1080/10425170310001617894</p>The post <a href="https://www.johndcook.com/blog/2026/06/30/dna-sequence-alignment-and-kings/">DNA Sequence Alignment and Kings</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Distinguishing variables from parameters</title>
		<link>https://www.johndcook.com/blog/2026/06/30/variables-and-parameters/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 30 Jun 2026 18:51:36 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[notation]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247281</guid>

					<description><![CDATA[<p>Imagine the following dialog. Professor: f is a function of a real variable x that takes a real parameter k. Student: What&#8217;s a parameter? Professor: It&#8217;s a constant that can vary. Student: Then if it can vary, isn&#8217;t it a variable? Professor: Sorta, but no not really. This conversation plays out over and over, and unfortunately it often [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/30/variables-and-parameters/">Distinguishing variables from parameters</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Imagine the following dialog.</p>
<p style="padding-left: 40px;"><strong>Professor</strong>: <em>f</em> is a function of a real variable <em>x</em> that takes a real parameter <em>k</em>.</p>
<p style="padding-left: 40px;"><strong>Student</strong>: What&#8217;s a parameter?</p>
<p style="padding-left: 40px;"><strong>Professor</strong>: It&#8217;s a constant that can vary.</p>
<p style="padding-left: 40px;"><strong>Student</strong>: Then if it can vary, isn&#8217;t it a variable?</p>
<p style="padding-left: 40px;"><strong>Professor</strong>: Sorta, but no not really.</p>
<p>This conversation plays out over and over, and unfortunately it often ends as it does above, with the student confused. Here&#8217;s how I believe the conversation should continue.</p>
<p style="padding-left: 40px;"><strong>Professor</strong>: You&#8217;re absolutely right that <em>f</em> is a function of two variables, <em>x</em> and <em>k</em>. But usually <em>k</em> is fixed in the context of a specific application and <em>x</em> is not. A different application might have a different, but also fixed, value of <em>k</em>. So it is helpful to think of <em>f</em>(<em>x</em>; <em>k</em>), a function of <em>x</em> with a parameter <em>k</em>, rather than <em>f</em>(<em>x</em>, <em>k</em>), a function of two variables. The former carries more information, giving a hint as to how the numbers are used.</p>
<p>Is there really a difference between a parameter and a variable? In a reductionistic sense, no. But in a practical sense, yes, absolutely.</p>
<p>It might sound pedantic to distinguish a variable from a parameter, and it is, in the best sense of the word. Pedant literally means teacher. Usually <em>pedantic</em> carries a negative connotation, such as making a distinction without a difference. But here the pedant would be making a helpful distinction.</p>
<p>For example, we might write a probability density function as <em>f</em>(<em>x</em>; μ, σ). The function gives the probability density at a point <em>x</em>. The density depends on parameters μ and σ, and these parameters change between applications, but for a given application they have fixed values.</p>
<p>You find the probability of a random variable taking on values in an interval [<em>a</em>, <em>b</em>] by integrating <em>f</em> over that interval. When I say that, you know that I mean you&#8217;d integrate with respect to <em>x</em>, because <em>f</em> is a function of <em>x</em>. It is also, in an abstract sense, a function of μ and σ, but it&#8217;s typically not useful to think of it that way.</p>
<p>Hypergeometric functions have two sets of parameters, and so you may see two semicolons, such as <em>f</em>(<em>x</em>; <em>a</em>, <em>b</em>; <em>c</em>). This denotes a function of the variable <em>x</em>, with upper parameters <em>a</em> and <em>b</em>, and a lower parameter <em>c</em>. In some abstract sense this is a function of four variables, but it acts very differently with respect to <em>x</em> than with respect to <em>a</em>, <em>b</em>, and <em>c</em>. There&#8217;s also a difference between <em>a</em> and <em>b</em> on the one hand and <em>c</em> on the other, one worth paying attention to, though it is less of a difference than between <em>x</em> and the parameters collectively.</p>
<p>Sometimes you&#8217;ll see a vertical bar rather than a semicolon to separate variables from parameters. This works out even better for probability densities because then <em>f</em>(<em>x</em> | μ, σ) suggests the probability density of <em>x</em> <em>given</em> μ and σ since the vertical bar is also used for conditional probability. You might also see <em>f</em>(<em>x</em> | <em>a, b;</em> c) for hypergeometric functions, with the vertical bar separating variables from parameters and the semicolon separating two kinds of parameters.</p>
<p>When I first saw a semicolon separating variables from parameters, no explanation was given, and I figured I could mentally replace the semicolon with a comma. Then later I realized that the semicolon was an act of kindness by the author giving the reader additional information.</p>The post <a href="https://www.johndcook.com/blog/2026/06/30/variables-and-parameters/">Distinguishing variables from parameters</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Silver Rectangles and the Ways of Kings</title>
		<link>https://www.johndcook.com/blog/2026/06/30/silver-kings/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 30 Jun 2026 14:36:14 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Chess]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247278</guid>

					<description><![CDATA[<p>Golden rectangles The defining property of golden rectangle is that if you stick a square on its longer side, you get another golden rectangle. The smaller vertical rectangle is similar to the larger horizontal rectangle. This means φ / 1 = (1 + φ) / φ which tells us φ² = 1 + φ and [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/30/silver-kings/">Silver Rectangles and the Ways of Kings</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<h2>Golden rectangles</h2>
<p>The defining property of golden rectangle is that if you stick a square on its longer side, you get another golden rectangle.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/golden_rectangle.png" width="263" height="188" /></p>
<p>The smaller vertical rectangle is similar to the larger horizontal rectangle. This means</p>
<p style="padding-left: 40px;">φ / 1 = (1 + φ) / φ</p>
<p>which tells us φ² = 1 + φ and so the golden ratio φ equals (1 + √5)/2.</p>
<h2>Silver rectangles</h2>
<p>A silver rectangle is one that if you stick <em>two</em> squares on its longer side you get another rectangle with the same aspect ratio.</p>
<p><img loading="lazy" decoding="async" class="size-medium aligncenter" src="https://www.johndcook.com/silver_rectangle.png" width="530" height="254" /></p>
<p>This tells us</p>
<p style="padding-left: 40px;">σ / 1 = (1 + 2σ) / σ</p>
<p>and so σ² = 1 + 2σ and the silver ratio is σ = 1 + √2.</p>
<p>Just as you can define a golden ratio and a silver ratio, there&#8217;s an analogous way to define a sequence of <a href="https://www.johndcook.com/blog/2023/04/14/metallic-ratios/">metallic ratios</a>.</p>
<h2>Kings and Delannoy numbers</h2>
<p>The silver ratio has several connections to the ways of ways kings. By that I mean the number of ways a king can go from one corner of a chessboard to the diagonally opposite corner without backtracking.</p>
<p>A king can move one space in any direction. If we start with a king in the bottom left corner of the board, the no-backtracking requirement means the king can move up, right, or up and right.</p>
<p>The number of paths a king can take from one corner to the opposite corner of an <em>n</em> × <em>n</em> chessboard is the <em>n</em>th central Delannoy number <em>D</em><sub><em>n</em></sub>. more generally Delannoy numbers are defined for an <em>m</em> × <em>n</em> chessboard, but I&#8217;ll stick to the case <em>m</em> = <em>n</em> called the <em>central</em> Delannoy number, or just Delannoy numbers for short.</p>
<p>The first Delannoy number is 1 because there&#8217;s only one way for a king to get from one corner to the other: do nothing, because the opposite corner is the same corner. The second Delannoy number is 3 because the king can move up then right, or right then up, or move diagonally up and right.</p>
<p>For a 3 × 3 grid things are significantly more complicated, and <em>D</em><sub>3</sub> = 13. For an 8 × 8 grid the number of paths is 48,639.</p>
<h2>Generating function</h2>
<p>How would you estimate the number of paths on an <em>n</em> × <em>n</em> board for large values of <em>n</em> without calculating it exactly? You might start by finding a generating function for the Delannoy numbers, which works out to be</p>
<p style="padding-left: 40px;">(<em>x</em>² − 6<em>x</em> + 1)<sup>−1/2</sup></p>
<p>The radius of convergence <em>r</em> for the generating function series is the distance from 0 to the closest singularity of the generating function, which is the smaller root of</p>
<p style="padding-left: 40px;"><em>x</em>² − 6<em>x</em> + 1</p>
<p>which is</p>
<p style="padding-left: 40px;">3 − √8 = (3 + √8)<sup>−1</sup> = (1 + √2)<sup>−2</sup> = 1/σ²</p>
<p>i.e. the radius of convergence is the reciprocal of the silver ratio squared.</p>
<h2>Asymptotic estimate</h2>
<p>The radius of convergence gives us a first approximation to the asymptotic size of the series coefficients. Since we&#8217;re working with the generating function of the Delannoy numbers, these coefficients are the Delannoy numbers. That is,</p>
<p style="padding-left: 40px;"><em>D</em><sub><em>n</em></sub> ~ <em>r</em><sup>−<em>n</em></sup> = (σ<sup>2</sup>)<sup><em>n</em></sup> = σ<sup>2<em>n</em></sup>.</p>
<p>That&#8217;s as good as you can do just knowing the radius of convergence. A more careful analysis would refine this estimate by dividing by a factor proportional to √<em>n</em>.</p>
<h2>Related posts</h2>
<ul>
<li class='link'><a href='https://www.johndcook.com/blog/2009/05/20/the-silver-ratio/'>The silver ratio</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2011/04/13/a-magic-kings-tour/'>A magic king&#8217;s tour</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/30/silver-kings/">Silver Rectangles and the Ways of Kings</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Derivative equals inverse</title>
		<link>https://www.johndcook.com/blog/2026/06/29/derivative-equals-inverse/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 30 Jun 2026 01:06:12 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247274</guid>

					<description><![CDATA[<p>Here&#8217;s kind of a strange problem with an interesting solution: find a function f such that the derivative of f equals the inverse of f for all positive x. f ′(x) = f−1(x) This is a differential equation, but a very unusual one, one that cannot be solved using any of the techniques taught in a class on differential equations. [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/29/derivative-equals-inverse/">Derivative equals inverse</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Here&#8217;s kind of a strange problem with an interesting solution: find a function <em>f</em> such that the derivative of <em>f</em> equals the inverse of <em>f</em> for all positive <em>x</em>.</p>
<p style="padding-left: 40px;"><em>f</em> ′(<em>x</em>) = <em>f</em><sup>−1</sup>(<em>x</em>)</p>
<p>This is a differential equation, but a very unusual one, one that cannot be solved using any of the techniques taught in a class on differential equations.</p>
<p>The unique solution is</p>
<p style="padding-left: 40px;"><em>f</em>(<em>x</em>) = φ(<em>x</em> / φ)<sup>φ</sup></p>
<p>where φ is the golden ratio. What an unexpected appearance of the golden ratio!</p>
<p>The problem was proposed by H. L. Nelson and solved by A. C. Hindmarsh. See The American Mathematical Monthly, Vol. 76, No. 6 p. 696.</p>The post <a href="https://www.johndcook.com/blog/2026/06/29/derivative-equals-inverse/">Derivative equals inverse</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Who you gonna believe: Grok or the docs?</title>
		<link>https://www.johndcook.com/blog/2026/06/29/who-you-gonna-believe/</link>
					<comments>https://www.johndcook.com/blog/2026/06/29/who-you-gonna-believe/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 12:12:05 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247268</guid>

					<description><![CDATA[<p>The calculator utility bc has a minimal math library. For example, there&#8217;s no tangent function because you&#8217;re expected take the ratio of sine and cosine. (The Gnu version of bc does have a function for tangent, but the POSIX version does not.) And yet bc includes support for Bessel functions J(x). The bc function j [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/29/who-you-gonna-believe/">Who you gonna believe: Grok or the docs?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The calculator utility <code>bc</code> has a minimal math library. For example, there&#8217;s no tangent function because you&#8217;re expected take the ratio of sine and cosine. (The Gnu version of <code>bc</code> does have a function for tangent, but the POSIX version does not.) And yet <code>bc</code> includes support for Bessel functions <em>J</em>(<em>x</em>).</p>
<p>The <code>bc</code> function <code>j</code> takes two arguments. Is the first argument <em>n</em> or <em>x</em>? Grok said the function arguments are <code>j(n,x)</code>. I thought I should run <code>man bc</code> just to make sure, and it said</p>
<blockquote><p>j(x, n) Returns the bessel integer order n (truncated) of x.</p></blockquote>
<p>So Grok says <code>j(n,x)</code> and the documentation that ships with the software says <code>j(x,n)</code>. Which one should you believe? Neither! You should run a little test.</p>
<pre>~$ bc -l
&gt;&gt;&gt; j(1, 0)
0
&gt;&gt;&gt; j(0, 1)
.76519768655796655144
</pre>
<p>Now <em>J</em><sub>1</sub>(0) = 0, so apparently the first argument is the order <em>n</em>. Grok was right and the man page was wrong.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/who_you_gonna_believe.jpeg" alt="Groucho Marx saysing " width="480" height="349" /></p>
<p>As further confirmation, let&#8217;s see which argument is truncated.</p>
<pre>&gt;&gt;&gt; j(1.2, 3.4)
.17922585168150711099
&gt;&gt;&gt; j(1, 3.4)
.17922585168150711099
&gt;&gt;&gt; j(1.2, 3)
.33905895852593645892
</pre>
<p>The first argument is truncated to an integer value, so that&#8217;s the order <em>n</em>.</p>
<p>Turns out there&#8217;s a bug in the man page. The man page text above comes from running <code>man bc</code> on my Macbook. On my Linux box, the documentation is correct. It says</p>
<blockquote><p>j(n,x) The Bessel function of integer order n of x.</p></blockquote>
<p>The software produces the same results on both computers. It&#8217;s just a documentation bug.</p>
<p>The version running on my Macbook is the version that ships with the OS. It&#8217;s not the Gnu version, though the documentation says &#8220;This bc is compatible with both the GNU bc and the POSIX bc spec.&#8221; It has a function <code>t</code> for tangent, for example, which a POSIX version does not. But if you run <code>bc --standard -l</code> attempting to call <code>t</code> produces an error.</p>The post <a href="https://www.johndcook.com/blog/2026/06/29/who-you-gonna-believe/">Who you gonna believe: Grok or the docs?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/06/29/who-you-gonna-believe/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Brace expansion tree</title>
		<link>https://www.johndcook.com/blog/2026/06/27/brace-expansion-tree/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 28 Jun 2026 00:33:33 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247263</guid>

					<description><![CDATA[<p>Here&#8217;s a crazy bash one-liner I found via an article by Peter Krumins: echo {w,t,}h{e{n{,ce{,forth}},re{,in,fore,with{,al}}},ither,at} This prints 30 English words: when, whence, whenceforth, where, wherein, wherefore, wherewith, wherewithal, whither, what, then, thence, thenceforth, there, therein, therefore, therewith, therewithal, thither, that, hen, hence, henceforth, here, herein, herefore, herewith, herewithal, hither, hat This post will explain how [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/27/brace-expansion-tree/">Brace expansion tree</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Here&#8217;s a crazy bash one-liner I found via an <a href="https://catonmat.net/bash-one-liners-explained-part-two">article</a> by Peter Krumins:</p>
<pre>echo {w,t,}h{e{n{,ce{,forth}},re{,in,fore,with{,al}}},ither,at}</pre>
<p>This prints 30 English words:</p>
<blockquote><p>when, whence, whenceforth, where, wherein, wherefore, wherewith, wherewithal, whither, what, then, thence, thenceforth, there, therein, therefore, therewith, therewithal, thither, that, hen, hence, henceforth, here, herein, herefore, herewith, herewithal, hither, hat</p></blockquote>
<p>This post will explain how the one-liner works.</p>
<p>Bash brace expansion iterates through all possibilities listed within curly braces, with possibilities separated by a comma. Note that the comma is a <em>separator</em> and not a <em>terminator</em>. And so, for example, the expression <code>{w,t,}</code> is effectively <code>{w,t,""}</code>.</p>
<p>When bash sees two brace expressions, these expand to the cartesian product of the two expressions. For example,</p>
<pre>echo {A,B}{1,2,3}</pre>
<p>produces</p>
<pre>A1 A2 A3 B1 B2 B3</pre>
<p>In the expression above we have</p>
<pre>{w,t,}h{e…,ither,at}</pre>
<p>So the expansion will enumerate all possibilities of <code>{w,h,}</code> multiplied by all possibilities of <code>{e…,ither,at}</code> where <code>e…</code> is itself a brace expression.</p>
<p>A diagram will help a lot.</p>
<p><img loading="lazy" decoding="async" src="https://www.johndcook.com/krumins_brace.png" width="480" height="407" class="aligncenter size-medium" /></p>
<p>The brace expansion does a depth-first traversal of this tree.</p>The post <a href="https://www.johndcook.com/blog/2026/06/27/brace-expansion-tree/">Brace expansion tree</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>When will the decimals in a/b repeat?</title>
		<link>https://www.johndcook.com/blog/2026/06/27/decimal-period/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 27 Jun 2026 18:12:03 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247260</guid>

					<description><![CDATA[<p>The previous post looked at how many digits are in the reduced fraction for the nth harmonic number. I was curious about how long the cycle of digits in a harmonic number might be. I wrote about the period length for the digits of fractions almost a decade ago. This post includes code so I can [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/27/decimal-period/">When will the decimals in a/b repeat?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The previous post looked at how many digits are in the reduced fraction for the <em>n</em>th harmonic number. I was curious about how long the cycle of digits in a harmonic number might be.</p>
<p>I wrote about the period length for the digits of fractions <a href="https://www.johndcook.com/blog/2016/10/18/periods-of-fractions/">almost a decade ago</a>. This post includes code so I can apply it to harmonic denominators.</p>
<pre>
from sympy import lcm, factorint, n_order

def period(n):
    factors = factorint(n)
    exp2 = factors.get(2, 0)
    exp5 = factors.get(5, 0)
    r = max(exp2, exp5)

    d = n // (2**exp2 * 5**exp5)
    s = 1 if d == 1 else n_order(10, d)
    return (r, s)
</pre>
<p>This function returns two numbers: <em>r</em> is the number of non-repeating digits at the beginning and <em>s</em> is the length of the repeating part. </p>
<p>The following code</p>
<pre>
from functools import reduce

def lcm_range(n):
    return reduce(lcm, range(1, n + 1))

print( period( lcm_range(50) ) )
</pre>
<p>prints (5, 1275120) meaning that 1/lcm(1, 2, 3, &hellip;, 49, 50) has five non-repeating digits following by 1,275,120 digits that repeat <em>ad infinitum</em>. And so the decimals in the expansion of <em>H</em><sub>50</sub> have a cycle length of 1,275,120.</p>The post <a href="https://www.johndcook.com/blog/2026/06/27/decimal-period/">When will the decimals in a/b repeat?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Height of harmonic numbers</title>
		<link>https://www.johndcook.com/blog/2026/06/27/height-of-harmonic-numbers/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 27 Jun 2026 12:51:42 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247253</guid>

					<description><![CDATA[<p>The previous post looked at writing the harmonic numbers as reduced fractions and estimating the number of digits in the numerator and denominator based on asymptotics. This is a follow up post with plots. We&#8217;ll choose our base b to be 2. And we&#8217;ll look at the total number of bits in both the numerator and [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/27/height-of-harmonic-numbers/">Height of harmonic numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/">previous post</a> looked at writing the harmonic numbers as reduced fractions and estimating the number of digits in the numerator and denominator based on asymptotics. This is a follow up post with plots.</p>
<p>We&#8217;ll choose our base <em>b</em> to be 2. And we&#8217;ll look at the total number of bits in both the numerator and denominator, which we will use as the <a href="https://www.johndcook.com/blog/2023/09/17/rational-height-functions/">height</a> of the fractions.</p>
<p>First, let&#8217;s look at the actual and estimated heights, using the estimates from the previous post.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/harmonic_height1.png" width="480" height="360" /></p>
<p>Next let&#8217;s look at the difference between the actual and estimated heights.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/harmonic_height2.png" width="480" height="360" /></p>
<p>In the previous post I looked at <em>n</em> = 50, which was kind of a lucky choice, the error being smaller than usual. I had also looked at, but didn&#8217;t publish, <em>n</em> = 100, which would be an unlucky choice.</p>
<p>Finally, let&#8217;s look at the <em>relative</em> error in the estimates, and plot over a larger range of <em>n</em>.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/harmonic_height3.png" width="480" height="360" /></p>
<p>The error goes to zero, as predicted by the asymptotic estimates. And it goes noisily, which you&#8217;d expect since the heights are related to the distribution of primes.</p>The post <a href="https://www.johndcook.com/blog/2026/06/27/height-of-harmonic-numbers/">Height of harmonic numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Writing down harmonic numbers</title>
		<link>https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/</link>
					<comments>https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 27 Jun 2026 01:51:03 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247251</guid>

					<description><![CDATA[<p>The nth harmonic number is the sum of the reciprocals of the first n positive integers. Hn = 1 + 1/2 + 1/3 + 1/4 + … + 1/n The product of all the denominators is n!, so you could write Hn as a fraction Hn = p/q where p = n! Hn is an integer and q = n!. While [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/">Writing down harmonic numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <em>n</em>th harmonic number is the sum of the reciprocals of the first <em>n</em> positive integers.</p>
<p style="padding-left: 40px;"><em>H</em><sub><em>n</em></sub> = 1 + 1/2 + 1/3 + 1/4 + … + 1/<em>n</em></p>
<p>The product of all the denominators is <em>n</em>!, so you could write <em>H</em><sub><em>n</em></sub> as a fraction</p>
<p style="padding-left: 40px;"><em>H</em><sub><em>n</em></sub> = <em>p</em>/<em>q</em></p>
<p>where <em>p</em> = <em>n</em>! <em>H</em><sub><em>n</em></sub> is an integer and <em>q</em> = <em>n</em>!.</p>
<p>While <em>p</em>/<em>q</em> is <em>a</em> way to write <em>H</em><sub><em>n</em></sub> as a fraction, it&#8217;s not the most efficient because <em>p</em> and <em>n</em>! will have common factors.</p>
<p>If we write <em>H</em><sub><em>n</em></sub> as a reduced fraction, the denominator will be the least common multiple of the integers 1 through <em>n</em>. That number is asymptotically exp(<em>n</em>). That estimate follows from the prime number theorem.</p>
<p>So for large <em>n</em> the denominator will be roughly exp(<em>n</em>), and in base <em>b</em> it would have around</p>
<p style="padding-left: 40px;"><span style="background-color: #dee;"><em>n</em>/log(<em>b</em>)</span></p>
<p>digits.</p>
<p>The numerator will be exp(<em>n</em>) <em>H</em><sub><em>n</em></sub>, and since <em>H</em><sub><em>n</em></sub> is asymptotically log(<em>n</em>) + γ, the numerator for large <em>n</em> will be roughly</p>
<p style="padding-left: 40px;">exp(<em>n</em>) (log(<em>n</em>) + γ)</p>
<p>and will have around</p>
<p style="padding-left: 40px;"><span style="background-color: #dee;">(<em>n</em> + log log(<em>n</em>) ) / log(<em>b</em>)</span></p>
<p>digits.</p>
<p>Let&#8217;s see how well our asymptotic estimates work for <em>n</em> = 50. The 50th harmonic number is</p>
<p style="padding-left: 40px;"><em>H</em><sub>50</sub> = 13943237577224054960759 / 3099044504245996706400.</p>
<p>This fraction has 23 digits in the numerator and 22 in the denominator. We would have predicted around</p>
<p style="padding-left: 40px;">(50 + log(log(50)))/log(10) = 22.3</p>
<p>digits in the numerator and</p>
<p style="padding-left: 40px;">50/log(10) = 21.7</p>
<p>digits in the denominator.</p>
<p>Let&#8217;s try a larger example, looking at the 1000th harmonic number in binary. We&#8217;ll use the following Python code.</p>
<pre>from fractions import Fraction

def bits(n):
    H = sum(Fraction(i, i+1) for i in range(1, n+1))
    p, q = H.numerator, H.denominator
    # subtract 2 because bin returns a string starting with 0b.
    return len(bin(p)) - 2, len(bin(q)) - 2

print(bits(1000))
</pre>
<p>This returns 1448 and 1438. We would have estimated</p>
<p style="padding-left: 40px;">(1000 + log(log(1000)))/log(2) = 1445.4</p>
<p>bits in the numerator and</p>
<p style="padding-left: 40px;">1000/log(2) = 1442.7</p>
<p>bits in the denominator.</p>
<p><strong>Update</strong>: See the <a href="https://www.johndcook.com/blog/2026/06/27/height-of-harmonic-numbers/">next post</a> for plots as a function of <em>n</em>.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2017/04/18/computing-harmonic-numbers/">Computing harmonic numbers</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2021/01/23/sums-of-consecutive-reciprocals/">Sums of consecutive reciprocals</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2023/05/23/am-over-gm/">AM over GM</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/">Writing down harmonic numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/06/26/writing-down-harmonic-numbers/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Hart&#8217;s theorem</title>
		<link>https://www.johndcook.com/blog/2026/06/25/harts-theorem/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 19:20:09 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Geometry]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247249</guid>

					<description><![CDATA[<p>Hart&#8217;s theorem says If a triangle be formed by the arcs of three circles, the inscribed and the three escribed circles are all tangent to a new circle or line. Here &#8220;triangle&#8221; means a three-sided figure whose sides are portions of a circle. The inscribed circle is the largest circle that can fit inside the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/25/harts-theorem/">Hart’s theorem</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Hart&#8217;s theorem says</p>
<blockquote><p>If a triangle be formed by the arcs of three circles, the inscribed and the three escribed circles are all tangent to a new circle or line.</p></blockquote>
<p>Here &#8220;triangle&#8221; means a three-sided figure whose sides are portions of a circle. The inscribed circle is the largest circle that can fit inside the three-sided figure.</p>
<p>The &#8220;escribed&#8221; circles are analogous to the excircles in the <a href="https://www.johndcook.com/blog/2026/06/25/incircle-excircle/">previous post</a>: you extend two sides and find a circle that is tangent to the triangle side and the extended side. The difference here being that the side extensions are now circles.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/hart_circles.png" width="400" height="466" /></p>The post <a href="https://www.johndcook.com/blog/2026/06/25/harts-theorem/">Hart’s theorem</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Incircles and Excircles of Pythagorean triangles</title>
		<link>https://www.johndcook.com/blog/2026/06/25/incircle-excircle/</link>
					<comments>https://www.johndcook.com/blog/2026/06/25/incircle-excircle/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 14:35:52 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Geometry]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247245</guid>

					<description><![CDATA[<p>This post will reveal the connection between my two previous posts: one on the Star Trek lemma and one on Pythagorean triples. In the process of writing the latter, I looked at the Wikipedia article on Pythagorean triples and noticed this curious paragraph. In every Pythagorean triangle, the radius of the incircle and the radii of the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/25/incircle-excircle/">Incircles and Excircles of Pythagorean triangles</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>This post will reveal the connection between my two previous posts: one on the <a href="https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/">Star Trek lemma</a> and one on <a href="https://www.johndcook.com/blog/2026/06/25/consecutive-pythagorean/">Pythagorean triples</a>.</p>
<p>In the process of writing the latter, I looked at the <a href="https://en.wikipedia.org/wiki/Pythagorean_triple">Wikipedia article</a> on Pythagorean triples and noticed this curious paragraph.</p>
<blockquote><p>In every Pythagorean triangle, the radius of the incircle and the radii of the three excircles are positive integers. Specifically, for a primitive triple the radius of the incircle is <i>r</i> = <i>n</i>(<i>m</i> − <i>n</i>), and the radii of the excircles opposite the sides <i>m</i><sup>2</sup> − <i>n<sup>2</sup></i>, <i>2mn</i>, and the hypotenuse <i>m</i><sup>2</sup> + <i>n</i><sup>2</sup> are respectively <i>m</i>(<i>m</i> − <i>n</i>), <i>n</i>(<i>m</i> + <i>n</i>), and <em>m</em>(<i>m</i> + <i>n</i>).</p></blockquote>
<p>The citation for the paragraph above was the book by my former officemate, which led to the post on the Star Trek lemma. The passage in Arthur Baragar&#8217;s book that Wikipedia cites is Exercise 15.3.</p>
<blockquote><p>Let Δ<em>ABC</em> be a right angle triangle with sides of integer length. Prove that the inradius <em>r</em> and the exradii <em>r</em><sub><em>a</em></sub>, <em>r</em><sub><em>b</em></sub>, and <em>r</em><sub><em>c</em></sub> are all integers.</p></blockquote>
<p>I don&#8217;t know whether Arthur discovered this theorem, but I&#8217;ll call it Baragar&#8217;s theorem for this post.</p>
<h2>Incircles and excircles</h2>
<p>To unpack Baragar&#8217;s theorem, let&#8217;s start by saying what <a href="https://www.johndcook.com/blog/2022/11/26/incircle-and-excircles/">incircles and excircles</a> are. Incircles are more familiar. The <strong>incircle</strong> of a triangle is the largest circle that can be inscribed inside the triangle, and the radius of this circle is the inradius.</p>
<p>Since an incircle is an inscribed circle, you might expect an excircle to be a circumscribed circle, but that&#8217;s not it. There are three excircles, one for each side. To find the <strong>excircle</strong> for a side, extend the other two sides and find the circle tangent to the side and the two extensions. The radius of an excircle is its exradius.</p>
<h2>Proof</h2>
<p>Baragar&#8217;s theorem follows directly from Euclid&#8217;s formula for Pythagorean triples mentioned in the previous post</p>
<p><img loading="lazy" decoding="async" class="aligncenter" src="https://www.johndcook.com/osborne2.svg" alt="\begin{align*} a &amp;= m^2 - n^2 \\ b &amp;= 2mn \\ c &amp;= m^2 + n^2 \end{align*} " width="96" height="79" /></p>
<p>and formulas for the inradius <em>r</em> and the exradii <em>r</em><sub><em>a</em></sub>, <em>r</em><sub><em>b</em></sub>, and <em>r</em><sub><em>c</em></sub>.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/exradii.svg" alt="\begin{align*} r &amp;= \frac{K}{s} \\ r_a &amp;= \frac{K}{s - a} \\ r_b &amp;= \frac{K}{s - b} \\ r_c &amp;= \frac{K}{s - c} \\ \end{align*}" width="80" height="181" /></p>
<p>Here <em>K</em> is the area of the triangle, which in our case is <em>ab</em>/2, and <em>s</em> is the semiperimeter, half the perimeter.</p>
<p>Expressing the radii in terms of <em>m</em> and <em>n</em> gives the values cited by Wikipedia above.</p>
<h2>Illustrating the theorem</h2>
<p>I&#8217;d like to write a Python script to illustrate the theorem, and knowing the radii of the circles help, but we also need to know the centers of the circles.</p>
<p>The center of the incircle is the weighted average of the vertices, with weights given by the lengths of the opposite sides. That is, if the vertices are <em>A</em>, <em>B</em>, and <em>C</em>, and the sides opposite these vertices are <em>a</em>, <em>b</em>, and <em>c</em>, the the incenter is</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/incenter_formula.svg" alt="I = \frac{aA + bB + cC}{a + b + c}" width="141" height="41" /></p>
<p>The centers for the excircles have remarkably similar expressions. For the incenter of the circle opposite a vertex, flip the sign of the corresponding side.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/excenters.svg" alt="\begin{align*} I_a = \frac{-aA + bB + cC}{-a + b + c} \\ I_a = \frac{aA - bB + cC}{a - b + c} \\ I_a = \frac{aA + bB - cC}{a + b - c} \\ \end{align*}" width="160" height="137" /></p>
<h2>Python code</h2>
<p>Putting it all together, here&#8217;s an illustrate the theorem.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/baragar_circles.png" width="480" height="360" /></p>
<p>And here&#8217;s the code that produced it. Note that everything in this section works for right triangles in general, not just Pythagorean triangles.</p>
<pre>import numpy as np
import matplotlib.pyplot as plt

def connect(A, B):
    plt.plot([A[0], B[0]], [A[1], B[1]], "C0")

def draw_circle(c, r, color):
    t = np.linspace(0, 2*np.pi)
    plt.plot(r*np.cos(t) + c[0], r*np.sin(t) + c[1], color=color)

a, b, c, = 3, 4, 5

A = np.array([0, b])
B = np.array([-a, 0])
C = np.array([0, 0])

s = (a + b + c)/2
K = a*b/2
r = K/s
ra = K/(s - a)
rb = K/(s - b)
rc = K/(s - c)
I = (a*A + b*B + c*C)/(a + b + c)
Ia = (-a*A + b*B + c*C)/(-a + b + c)
Ib = (a*A - b*B + c*C)/(a - b + c)
Ic = (a*A + b*B - c*C)/(a + b - c)

draw_circle(I, r, "C1")
draw_circle(Ia, ra, "C2")
draw_circle(Ib, rb, "C3")
draw_circle(Ic, rc, "C4")

plt.plot([-2*rc, 2*rb], [0, 0], "C0")
plt.plot([0, 0], [-2*ra, 2*rc], "C0")
plt.plot([(-2*ra - b)*a/b, 2*rb], [-2*ra, 2*rb*b/a + b], "C0")

plt.gca().set_aspect("equal")
plt.show()
</pre>The post <a href="https://www.johndcook.com/blog/2026/06/25/incircle-excircle/">Incircles and Excircles of Pythagorean triangles</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/06/25/incircle-excircle/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Consecutive Pythagorean triangle sides</title>
		<link>https://www.johndcook.com/blog/2026/06/25/consecutive-pythagorean/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 12:00:14 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247232</guid>

					<description><![CDATA[<p>In this post we find all Pythagorean triples that contain consecutive numbers, all Pythagorean triples (a, b, c) such that a + 1 = b or b + 1 = c. a + 1 = b George Osborne wrote a paper [1] addressing the question of when the squares of two consecutive numbers is also a square. Geometrically this is asking [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/25/consecutive-pythagorean/">Consecutive Pythagorean triangle sides</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>In this post we find all Pythagorean triples that contain consecutive numbers, all Pythagorean triples (<em>a</em>, <em>b</em>, <em>c</em>) such that <em>a</em> + 1 = <em>b</em> or <em>b</em> + 1 = c.</p>
<h2><em>a</em> + 1 = <em>b</em></h2>
<p>George Osborne wrote a paper [1] addressing the question of when the squares of two consecutive numbers is also a square. Geometrically this is asking for primitive Pythagorean triples for which the legs are consecutive integers.</p>
<p>He proved that the sequence shorter legs satisfies the recurrence relation</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/osborne0.svg" alt="u_{n+2} = 6 u_{n+1} - u_{n+1} + 2" width="173" height="18" /><br />
with initial conditions <em>u</em><sub>0</sub> = 0 and <em>u</em><sub>1</sub> = 1. This is OEIS sequence <a href=" https://oeis.org/A001652">A001652</a>.</p>
<p>The method for solving recurrences like the one above is analogous to the method for solving linear differential equations. See a solution <a href="https://www.johndcook.com/consecutive_pythagorean.pdf">here</a>. This gives us the following formula for the terms:</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/osborne.svg" alt="u_n = \dfrac{1 + \sqrt{2}}{4} \left(3 + 2\sqrt{2}\right)^n + \dfrac{1 - \sqrt{2}}{4} \left(3 - 2\sqrt{2}\right)^n - \dfrac{1}{2}" width="367" height="44" /></p>
<h2><em>b</em> + 1 = <em>c</em></h2>
<p>It&#8217;s also possible for the longer side and hypotenuse of a Pythagorean triangle to be consecutive numbers, as in the (5, 12, 13) triangle.</p>
<p>All primitive Pythagorean triples are given by Euclid&#8217;s formula</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/osborne2.svg" alt="\begin{align*} a &amp;= m^2 - n^2 \\ b &amp;= 2mn \\ c &amp;= m^2 + n^2 \end{align*} " width="96" height="79" /></p>
<p>with integers <em>m</em> &gt; <em>n</em> &gt; 0. If <em>b</em> and <em>c</em> are consecutive numbers, then</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/osborne3.svg" alt="c - b = 1 = m^2 + n^2 - 2mn = (m -n)^2" width="310" height="22" /></p>
<p>and so <em>m</em> = <em>n</em> + 1. Therefore all possible values of <em>b</em> are given by 2<em>n</em>(<em>n</em> + 1) for <em>n</em> &gt; 1.</p>
<p>[1] Geo. A. Osborne. A Problem in Number Theory. The American Mathematical Monthly, Vol. 21, No. 5 (May, 1914), pp. 148-150</p>The post <a href="https://www.johndcook.com/blog/2026/06/25/consecutive-pythagorean/">Consecutive Pythagorean triangle sides</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The Star Trek lemma</title>
		<link>https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/</link>
					<comments>https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 25 Jun 2026 02:38:42 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Geometry]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247235</guid>

					<description><![CDATA[<p>I was reading an article this evening and saw a footnote to a book by Arthur Baragar [1]. This caught my eye because he was my officemate at UT for a year. I found his book on Archive.org and was surprised to see &#8220;The Star Trek Lemma&#8221; in the table of contents. What could this [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/">The Star Trek lemma</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I was reading an article this evening and saw a footnote to a book by Arthur Baragar [1]. This caught my eye because he was my officemate at UT for a year.</p>
<p>I found his book on <a href="https://archive.org/details/surveyofclassica0000bara">Archive.org</a> and was surprised to see &#8220;The Star Trek Lemma&#8221; in the table of contents. What could this be?</p>
<p>It&#8217;s a theorem that goes back to Euclid that applies to an angle formed by connecting a point to two other points on a circle. The theorem says &#8220;The measure of an inscribed angle is half the measure of the arc it subtends.&#8221; But why call it the Star Trek lemma? Quoting Arthur:</p>
<p>In the spirit of Euclid, we will refer to this theorem as the Star Trek lemma because of the figure associated with the statement of the theorem. … Before Star Trek, as far as I know, this theorem had no name, though some might call it Euclid III.20, which is its proposition number in Euclid&#8217;s <em>Elements</em> (Book III, Proposition 20).</p>
<p>Here is my reconstruction of the figure given in the book.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/baragar_star_trek.png" alt="Baragar's illustration of the Star Trek lemma" width="300" height="332" /></p>
<p>The lemma says that ∠BAC is half of ∠BOC.</p>
<p>[1] Baragar, Arthur (2001), <i>A Survey of Classical and Modern Geometries: With Computer Activities</i>, Prentice Hall</p>
<p>&nbsp;</p>The post <a href="https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/">The Star Trek lemma</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/06/24/star-trek-lemma/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Regular expressions that work &#8220;everywhere&#8221;</title>
		<link>https://www.johndcook.com/blog/2026/06/23/regex-everywhere/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 24 Jun 2026 00:26:11 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Regular expressions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247223</guid>

					<description><![CDATA[<p>The most frustrating aspect of regular expressions is that implementations vary. Features supported in one tool may not be supported at all in another tool, or they may be supported with slightly different syntax. I learned regular expressions in the context Perl, a maximalist regex environment. This led to frustration when features I expect to [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/23/regex-everywhere/">Regular expressions that work “everywhere”</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The most frustrating aspect of regular expressions is that implementations vary. Features supported in one tool may not be supported at all in another tool, or they may be supported with slightly different syntax.</p>
<p>I learned regular expressions in the context Perl, a maximalist regex environment. This led to frustration when features I expect to work are missing [1]. One way around this is to use <a href="https://www.johndcook.com/blog/2013/08/20/perl-as-a-better/">Perl analogs</a> of other tools, but this is very non-standard. I want to be able to send colleagues and clients code that works out of the box.</p>
<p>As I mentioned in my post on <a href="https://www.johndcook.com/blog/2019/10/09/computational-survivalist/">computational survivalism</a>, I occasionally need to work on computers that I cannot install software on. So a better approach is to identify a subset of regex features that work everywhere. The stricter your definition of &#8220;everywhere&#8221; the less this includes. The strictest subset would be</p>
<ul>
<li>literals</li>
<li>character classes <code>[…]</code></li>
<li>the special characters <code>. * ^ $</code></li>
</ul>
<p>A more relaxed definition of &#8220;everywhere&#8221; would be the tools you most care about. Currently the tools I most want to use with regular expressions are sed, awk, grep, and Emacs.</p>
<h2>Awk as lowest common denominator</h2>
<p>If you use the Gnu versions of sed, awk, and grep, and use the <code>-E</code> option with sed and grep, then the list of common features is bigger. The regular expression features of the three tools are similar, and awk&#8217;s features are supported in the other tools, with one exception: word boundaries in awk are <code>\&lt;</code> and <code>\&gt;</code> rather than <code>\b</code> and <code>\B</code>.</p>
<p>I wrote about Awk&#8217;s regex features <a href="https://www.johndcook.com/blog/awk-regular-expressions/">here</a>.</p>
<h2>Emacs as the oddball</h2>
<p>Emacs supports analogs of most of awk&#8217;s regex features. However, the characters</p>
<pre>    + ? ( ) { } |</pre>
<p>all require a backslash in front in order to act like the awk counterparts. Also, the analog of <code>\s</code> and <code>\S</code> in awk is <code>\s-</code> and <code>\S-</code> in Emacs.</p>
<p>Instead of meaning space or nonspace, <code>\s</code> and <code>\S</code> in Emacs begin a (negated) character class, and one of those classes is <code>-</code> for space. But there are many others. For example, <code>\s.</code> stands for a punctuation character and <code>\S.</code> stands for a non-punctuation character.</p>
<h2>What works everywhere</h2>
<p>So for my definition of &#8220;everywhere,&#8221; with the caveats mentioned above, the following features work everywhere. YMMV.</p>
<pre>    .
    ^, $
    […], [^…]
    *
    \w, \W, \s, \S
    \1 - \9 backreferences
    \b \B
    ? + 
    | alternation
    {n,m} for counting matches
    (...) capturing
</pre>
<p>One footnote is that gawk supports backreferences in replacement strings but not in regular expressions <em>per se</em>.</p>
<p>[1] To some extent, basic Perl features work elsewhere and advanced features do not, depending on your idea of what is basic or advanced. I think of <a href="https://www.johndcook.com/blog/2014/05/01/look-behind-regex/">look-around</a> features as advanced, and that tracks. But I think of <code>\d</code> for digits as basic, but that&#8217;s not supported in many regex flavors.</p>The post <a href="https://www.johndcook.com/blog/2026/06/23/regex-everywhere/">Regular expressions that work “everywhere”</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Lobachevsky&#8217;s integral formula</title>
		<link>https://www.johndcook.com/blog/2026/06/22/lobachevskys-integral-formula/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 22 Jun 2026 20:02:47 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Fourier analysis]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247217</guid>

					<description><![CDATA[<p>Let f be an even function with period π. Then the following remarkable theorem by Lobachevsky holds. This theorem is useful in Fourier analysis and signal processing. It&#8217;s useful to know even in the special case f(x) = 1. For a &#8220;jinc&#8221; analog, see this paper. *** Every time I see the name Lobachevsky I [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/22/lobachevskys-integral-formula/">Lobachevsky’s integral formula</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Let <em>f</em> be an even function with period π. Then the following remarkable theorem by Lobachevsky holds.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/lobachevsky.svg" alt="\int_0^\infty \frac{\sin^2 x}{x^2} f(x) \, dx = \int_0^\infty\frac{\sin x} x f(x) \, dx = \int_0^{\pi/2} f(x) \, dx" width="455" height="49" /></p>
<p>This theorem is useful in Fourier analysis and signal processing. It&#8217;s useful to know even in the special case <em>f</em>(<em>x</em>) = 1.</p>
<p>For a &#8220;jinc&#8221; analog, see <a href="https://arxiv.org/abs/2006.09575v1">this paper</a>.</p>
<p style="text-align: center;">***</p>
<p>Every time I see the name Lobachevsky I think of Tom Lehrer&#8217;s song about him. You can find the words <a href="https://tomlehrersongs.com/wp-content/uploads/2018/12/lobachevsky.pdf">here</a> and the audio <a href="https://www.youtube.com/watch?v=rlgRDdWUs7g">here</a>.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2015/12/16/sinc-and-jinc-integrals/">Sinc and jinc integrals</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2015/12/17/sinc-and-jinc-sums/">Sinc and jinc sums</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/22/lobachevskys-integral-formula/">Lobachevsky’s integral formula</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Queens on a prime order board</title>
		<link>https://www.johndcook.com/blog/2026/06/21/queens-prime/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 22 Jun 2026 00:21:44 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Chess]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247212</guid>

					<description><![CDATA[<p>The n queens problem is to place on an n × n chessboard n queens so that none attacks any other. This means there is only one queen on every horizontal, vertical, and diagonal line. When n is a prime number ≥ 5, it is sufficient to place the queens on a line that has slope 2, 3, 4, …, [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/21/queens-prime/">Queens on a prime order board</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <em>n</em> queens problem is to place on an <em>n</em> × <em>n</em> chessboard <em>n</em> queens so that none attacks any other. This means there is only one queen on every horizontal, vertical, and diagonal line.</p>
<p>When <em>n</em> is a <strong>prime number</strong> ≥ 5, it is sufficient to place the queens on a line that has slope 2, 3, 4, …, <em>n</em> − 2. (The slope cannot be 1 because that&#8217;s a diagonal. And it cannot be <em>n</em> − 1 because <i>n </i>− 1 = −1 mod <em>n</em> is also a diagonal.) [1]</p>
<p>Here we imagine opposite edges of the board being joined together. Geometrically, this makes the chessboard a torus (donut). Algebraically, the points on a line of slope <em>s</em> have the coordinates</p>
<p style="padding-left: 40px;">(<em>a</em> + <em>k</em>, <em>b</em> + <em>ks</em>)</p>
<p>where addition is carried out mod <em>n</em>.</p>
<p><strong>All</strong> solutions to the <em>n</em> queens problem have this form when <em>n</em> = 5. <strong>Some</strong> solutions will have this form for larger prime values of <em>n</em> but not all.</p>
<p>For example, when <em>n</em> = 7, here is a solution where all the queens are on a line of slope 2.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/queens71.png" width="400" height="400" /></p>
<p>But here is another solution where the queens do not all lie on a line of constant slope.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/queens72.png" width="400" height="400" /></p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2025/05/11/one-hot-sat/">Formulating eight queens as a SAT problem</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2021/08/18/queens-on-a-donut/">Queens on a donut</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/06/11/prolog-claude/">Solving a chess puzzle with Claude and Prolog</a></li>
</ul>
<p>[1] W. H. Bussey. A Note on the Problem of the Eight Queens. The American Mathematical Monthly, Vol. 29, No. 7 (August 1922), pp. 252–253</p>The post <a href="https://www.johndcook.com/blog/2026/06/21/queens-prime/">Queens on a prime order board</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>All pieces on a 6 by 5 board</title>
		<link>https://www.johndcook.com/blog/2026/06/20/z3-python-claude/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 20 Jun 2026 21:41:16 +0000</pubDate>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Chess]]></category>
		<category><![CDATA[Logic]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247206</guid>

					<description><![CDATA[<p>I&#8217;ve written a couple posts lately on getting an LLM to generate code to solve chess problems. The first used Claude to generate Prolog and the second used ChatGPT to generate Prolog. This post will use Claude to generate Z3/Python code. The puzzle is one I&#8217;ve written about before: Place all the pieces—king, queen, two [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/20/z3-python-claude/">All pieces on a 6 by 5 board</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I&#8217;ve written a couple posts lately on getting an LLM to generate code to solve chess problems. <a href="https://www.johndcook.com/blog/2026/06/11/prolog-claude/">The first</a> used Claude to generate Prolog and <a href="https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/">the second</a> used ChatGPT to generate Prolog. This post will use Claude to generate Z3/Python code.</p>
<p>The puzzle is one I&#8217;ve written about <a href="https://www.johndcook.com/blog/2025/07/13/all-pieces-on-a-small-chessboard/">before</a>:</p>
<blockquote><p>Place all the pieces—king, queen, two bishops, two knights, and two rooks—on a 6 × 5 chessboard, with the requirement that the two bishops be on opposite colored squares and no piece is attacking another.</p></blockquote>
<p>Incidentally, it&#8217;s common for &#8220;piece&#8221; to exclude pawns, as above. But then what do you call all the things on a chessboard? You might call them &#8220;chess pieces,&#8221; in which case a pawn is a &#8220;chess piece&#8221; but not a &#8220;piece.&#8221; One convention is to use &#8220;chessmen&#8221; or simply &#8220;men&#8221; to include pieces and pawns.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/claudez3.png" width="400" height="389" /></p>
<p>This was the prompt I used.</p>
<blockquote>
<p class="whitespace-pre-wrap break-words">Write Z3/Python code to find all solutions to the following chess puzzle.</p>
<p class="whitespace-pre-wrap break-words">Place all the pieces—king, queen, two bishops, two knights, and two rooks—on a 6 × 5 chessboard, with the requirement that the two bishops be on opposite colored squares and no piece is attacking another.</p>
</blockquote>
<p>The code found 192 = 8 × 24 solutions. The factor of 8 comes from 2<sup>3</sup> ways of swapping the pairs of bishops, knights, and rooks. The script reports</p>
<pre>
Total raw solutions: 192
Unique solutions (deduplicating piece-pair swaps): 24

── Solution 1 ──
  0 1 2 3 4
0 . K . . N
1 . . . . B
2 . . R . .
3 Q . . . .
4 . . . R .
5 . B . . N
  King: (0,1)
  Queen: (3,0)
  Bishop1: (5,1) [light]
  Bishop2: (1,4) [dark]
  Knight1: (5,4)
  Knight2: (0,4)
  Rook1: (4,3)
  Rook2: (2,2)

── Solution 2 ──
  0 1 2 3 4
0 N . . K .
1 B . . . .
2 . . R . .
3 . . . . Q
4 . R . . .
5 N . . B .
  King: (0,3)
  Queen: (3,4)
  Bishop1: (5,3) [light]
  Bishop2: (1,0) [dark]
  Knight1: (5,0)
  Knight2: (0,0)
  Rook1: (4,1)
  Rook2: (2,2)

...

── Solution 24 ──
  0 1 2 3 4
0 B . . B .
1 N . . . .
2 . . . . Q
3 . . R . .
4 . R . . .
5 N . . K .
  King: (5,3)
  Queen: (2,4)
  Bishop1: (0,0) [light]
  Bishop2: (0,3) [dark]
  Knight1: (5,0)
  Knight2: (1,0)
  Rook1: (3,2)
  Rook2: (4,1)
</pre>
<p>Here&#8217;s the code.</p>
<pre>
"""
Chess Puzzle Solver using Z3
Place king, queen, 2 bishops, 2 knights, 2 rooks on a 6×5 board
such that:
  - The two bishops are on opposite-colored squares
  - No piece attacks any other piece
"""

from z3 import (
    Int, Solver, Or, And, Not, Distinct, If,
    sat, ForAll, Implies
)
import itertools

ROWS = 6
COLS = 5
TOTAL_CELLS = ROWS * COLS  # 30

PIECES = ["king", "queen", "bishop1", "bishop2", "knight1", "knight2", "rook1", "rook2"]
N = len(PIECES)  # 8 pieces

def cell(r, c):
    return r * COLS + c

def pos_to_rc(pos):
    return pos // COLS, pos % COLS

def color(r, c):
    """Square color: 0=light, 1=dark"""
    return (r + c) % 2

def attacks_king(r1, c1, r2, c2):
    return max(abs(r1 - r2), abs(c1 - c2)) == 1

def attacks_queen(r1, c1, r2, c2):
    return (r1 == r2 or c1 == c2 or
            abs(r1 - r2) == abs(c1 - c2))

def attacks_bishop(r1, c1, r2, c2):
    return abs(r1 - r2) == abs(c1 - c2)

def attacks_rook(r1, c1, r2, c2):
    return r1 == r2 or c1 == c2

def attacks_knight(r1, c1, r2, c2):
    dr, dc = abs(r1 - r2), abs(c1 - c2)
    return (dr == 1 and dc == 2) or (dr == 2 and dc == 1)

# Map piece index to its attack function
ATTACK_FN = {
    0: attacks_king,
    1: attacks_queen,
    2: attacks_bishop,
    3: attacks_bishop,
    4: attacks_knight,
    5: attacks_knight,
    6: attacks_rook,
    7: attacks_rook,
}

def solve_all():
    solutions = []

    # Each piece gets an integer position in [0, TOTAL_CELLS)
    pos = [Int(f"pos_{p}") for p in PIECES]

    s = Solver()

    # All positions in valid range
    for p in pos:
        s.add(p >= 0, p < TOTAL_CELLS)

    # All pieces on distinct squares
    s.add(Distinct(*pos))

    # Bishops on opposite colors
    # bishop1 = pos[2], bishop2 = pos[3]
    # color of pos = ((pos // COLS) + (pos % COLS)) % 2
    b1_color = (pos[2] / COLS + pos[2] % COLS) % 2  # Z3 integer arithmetic
    b2_color = (pos[3] / COLS + pos[3] % COLS) % 2

    # Z3 doesn't do Python //; use integer division carefully
    # We'll encode opposite colors: sum of colors == 1
    # color(pos) = (row + col) % 2 = (pos//COLS + pos%COLS) % 2
    # For Z3 int vars, use: (pos / COLS + pos % COLS) % 2
    s.add((pos[2] / COLS + pos[2] % COLS) % 2 != (pos[3] / COLS + pos[3] % COLS) % 2)

    # No piece attacks another
    # We enumerate all (i,j) pairs and for each possible (pos_i, pos_j) assignment,
    # assert that those pieces don't attack each other.
    # Since positions are Z3 vars, we use a constraint table approach:
    # For each pair (i,j), add constraints over all concrete (r1,c1,r2,c2) combos.

    # Pre-build attack lookup tables for each piece-type pair
    # This avoids slow Z3 symbolic reasoning over large disjunctions.

    # We'll encode: for all concrete assignments, if pos[i]==cell(r1,c1) and pos[j]==cell(r2,c2),
    # then piece i must not attack piece j.
    # Equivalently: NOT (pos[i]==cell(r1,c1) AND pos[j]==cell(r2,c2)) for all attacking pairs.

    # For efficiency, we build a set of "forbidden (cell_i, cell_j)" pairs per piece pair.

    for i, j in itertools.combinations(range(N), 2):
        fn_i = ATTACK_FN[i]
        fn_j = ATTACK_FN[j]
        forbidden = []
        for r1, c1 in itertools.product(range(ROWS), range(COLS)):
            for r2, c2 in itertools.product(range(ROWS), range(COLS)):
                if (r1, c1) == (r2, c2):
                    continue
                # i attacks j or j attacks i
                if fn_i(r1, c1, r2, c2) or fn_j(r2, c2, r1, c1):
                    forbidden.append((cell(r1, c1), cell(r2, c2)))

        if forbidden:
            # Add: NOT (pos[i] == ci AND pos[j] == cj) for each forbidden pair
            s.add(And([
                Not(And(pos[i] == ci, pos[j] == cj))
                for ci, cj in forbidden
            ]))

    # Enumerate all solutions
    solution_count = 0
    while s.check() == sat:
        m = s.model()
        sol = [m[pos[k]].as_long() for k in range(N)]
        solutions.append(sol)
        solution_count += 1

        # Block this solution (and symmetric duplicates via blocking exact assignment)
        s.add(Or([pos[k] != sol[k] for k in range(N)]))

        if solution_count % 10 == 0:
            print(f"  Found {solution_count} solutions so far...")

    return solutions

def print_board(sol):
    board = [["." for _ in range(COLS)] for _ in range(ROWS)]
    symbols = ["K", "Q", "B", "B", "N", "N", "R", "R"]
    for k, p in enumerate(sol):
        r, c = pos_to_rc(p)
        board[r][c] = symbols[k]
    print("  " + " ".join(str(c) for c in range(COLS)))
    for r in range(ROWS):
        print(f"{r} " + " ".join(board[r]))

def canonicalize(sol):
    """
    Produce a canonical form to deduplicate solutions where
    bishop1/bishop2, knight1/knight2, rook1/rook2 are interchangeable.
    Returns a frozenset-based key.
    """
    king_pos   = sol[0]
    queen_pos  = sol[1]
    bishops    = tuple(sorted([sol[2], sol[3]]))
    knights    = tuple(sorted([sol[4], sol[5]]))
    rooks      = tuple(sorted([sol[6], sol[7]]))
    return (king_pos, queen_pos, bishops, knights, rooks)

def main():
    print("Solving chess puzzle on 6×5 board...")
    print("Pieces: King, Queen, 2 Bishops (opposite colors), 2 Knights, 2 Rooks")
    print("Constraint: No piece attacks another\n")

    solutions = solve_all()

    # Deduplicate by canonical form
    seen = {}
    for sol in solutions:
        key = canonicalize(sol)
        if key not in seen:
            seen[key] = sol

    unique = list(seen.values())
    print(f"\nTotal raw solutions: {len(solutions)}")
    print(f"Unique solutions (deduplicating piece-pair swaps): {len(unique)}\n")

    for idx, sol in enumerate(unique, 1):
        print(f"── Solution {idx} ──")
        print_board(sol)
        labels = ["King", "Queen", "Bishop1", "Bishop2", "Knight1", "Knight2", "Rook1", "Rook2"]
        for k, p in enumerate(sol):
            r, c = pos_to_rc(p)
            col_name = "light" if color(r, c) == 0 else "dark"
            sq = f"({r},{c})"
            extra = f" [{col_name}]" if "Bishop" in labels[k] else ""
            print(f"  {labels[k]}: {sq}{extra}")
        print()

if __name__ == "__main__":
    main()
</pre>
<p><b>Related post</b>: <a href="https://www.johndcook.com/blog/2025/03/17/lessons-learned-with-the-z3-sat-smt-solver/">Lessons Learned With the Z3 SAT/SMT Solver</a></p>The post <a href="https://www.johndcook.com/blog/2026/06/20/z3-python-claude/">All pieces on a 6 by 5 board</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Formalizing a ring theorem with Lean 4 and Claude</title>
		<link>https://www.johndcook.com/blog/2026/06/17/rings-with-lean-claude/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 17 Jun 2026 14:23:51 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Artificial intelligence]]></category>
		<category><![CDATA[Formal methods]]></category>
		<category><![CDATA[Lean]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247201</guid>

					<description><![CDATA[<p>I&#8217;ve been testing Claude&#8217;s ability to generate Lean 4 code to prove theorems. I&#8217;ve written about a couple experiments that verified calculations. I did not write about my failed attempt to get Claude to formalize a proof of the pqr theorem for seminorms. This time I asked Claude to formally prove the theorem from the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/17/rings-with-lean-claude/">Formalizing a ring theorem with Lean 4 and Claude</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I&#8217;ve been testing Claude&#8217;s ability to generate Lean 4 code to prove theorems. I&#8217;ve written about a <a href="https://www.johndcook.com/blog/2026/06/10/claude-and-lean/">couple</a> <a href="https://www.johndcook.com/blog/2026/06/15/quaternions-claude-lean/">experiments</a> that verified calculations. I did not write about my failed attempt to get Claude to formalize a proof of the <a href="https://www.johndcook.com/pqr_theorem.pdf">pqr theorem</a> for seminorms.</p>
<p>This time I asked Claude to formally prove the theorem from the previous post on <a href="https://www.johndcook.com/blog/2026/06/16/partial-fraction-decomposition/">partial fraction decomposition</a> in an abstract setting. Unlike the previous experiments, I did not give it a proof or calculation to verify; I asked it to <em>generate</em> a proof.</p>
<p>This was the prompt:</p>
<blockquote><p>Write a proof in Lean 4 for the following generalization of partial fraction decomposition.</p>
<p>If a ring R is a principle ideal domain (PID), then every element c of the field of fractions over R can be written in the form \[c = \sum_i \frac{a_i}{p_i^{r_i}}\]</p>
<p>where the p_i are nonassociate irreducible elements of R, the r_i are non-negative integers, and the elements a_i and p_i are relatively prime.</p></blockquote>
<p>This was Claude&#8217;s immediate response:</p>
<blockquote><p>I&#8217;ll work through this carefully, as it&#8217;s a substantial formalization task. Let me think through what&#8217;s available in Mathlib and what needs to be constructed.</p>
<p>This is a genuinely hard Lean 4 formalization problem. Let me think carefully about what&#8217;s in Mathlib, what the statement precisely means, and what proof strategy is realistic.</p></blockquote>
<p>It also stated its overall proof strategy:</p>
<blockquote><p><strong>Proof strategy</strong>: Write <code>c = a/b</code> in lowest terms. Factor <code>b = ∏ pᵢ^rᵢ</code>. Use the Chinese Remainder Theorem / Bezout identity to decompose <code>a/b</code> into partial fractions.</p></blockquote>
<p>If I needed to prove the theorem by hand, this suggestion alone could be very helpful.</p>
<p>The first attempt generated code that would not build. As before, a lot of the problems seem to stem from things moving around in Mathlib due to refactorings. There are also occasional hallucinations.</p>
<p>I did not offer any help other than pasting in error messages after each failure. When working on a real problem, I&#8217;d be more interactive. But as a test, I just passively pasted error messages and tried again. It took 11 iterations, but I finally got a proof. The proof had five sections marked &#8220;sorry,&#8221; which is essentially a TODO note indicating an unproven assumption.</p>
<p>I asked if it could explain or remove the sorries, and it said it could remove three of them. But the generated code did not build and I hit a session limit before I could debug the code. Here is the code with five sorry sections.</p>
<pre>
/-
  PartialFractions.lean  (v11 — final)
-/

import Mathlib

open UniqueFactorizationMonoid

variable {R : Type*} [CommRing R] [IsDomain R] [IsPrincipalIdealRing R]

structure PFDSummand (R : Type*) [CommRing R] where
  prime    : R
  exponent : ℕ
  numer    : R

noncomputable def PFDSummand.val [CommRing R] [IsDomain R] (s : PFDSummand R) :
    FractionRing R :=
  algebraMap R (FractionRing R) s.numer /
  algebraMap R (FractionRing R) (s.prime ^ s.exponent)

structure IsPFD [CommRing R] [IsDomain R] [IsPrincipalIdealRing R]
    (terms : List (PFDSummand R)) (c : FractionRing R) : Prop where
  irred    : ∀ s ∈ terms, Irreducible s.prime
  nonAssoc : ∀ s ∈ terms, ∀ t ∈ terms, s ≠ t → ¬Associated s.prime t.prime
  coprime  : ∀ s ∈ terms, IsCoprime s.numer s.prime
  sum_eq   : (terms.map PFDSummand.val).sum = c

private lemma algMap_ne_zero {a : R} (h : a ≠ 0) :
    (algebraMap R (FractionRing R)) a ≠ 0 :=
  map_ne_zero_iff _ (IsFractionRing.injective R (FractionRing R)) |>.mpr h

private lemma exists_num_denom (c : FractionRing R) :
    ∃ a b : R, b ≠ 0 ∧
      (algebraMap R (FractionRing R)) a / (algebraMap R (FractionRing R)) b = c := by
  obtain ⟨a, b, hb, hab⟩ := IsFractionRing.div_surjective (A := R) c
  exact ⟨a, b, nonZeroDivisors.ne_zero hb, hab⟩

-- ======================================================================
-- §6  Bézout splitting
-- ======================================================================

private lemma bezout_split (x y : R) (hcop : IsCoprime x y)
    (hx : x ≠ 0) (hy : y ≠ 0) (e : R) :
    ∃ s t : R, s * x + t * y = 1 ∧
      (algebraMap R (FractionRing R) e) /
        ((algebraMap R (FractionRing R) x) * (algebraMap R (FractionRing R) y)) =
      (algebraMap R (FractionRing R) (e * t)) / (algebraMap R (FractionRing R) x) +
      (algebraMap R (FractionRing R) (e * s)) / (algebraMap R (FractionRing R) y) := by
  obtain ⟨s, t, hst⟩ := hcop
  refine ⟨s, t, hst, ?_⟩
  have hxF : (algebraMap R (FractionRing R)) x ≠ 0 := algMap_ne_zero hx
  have hyF : (algebraMap R (FractionRing R)) y ≠ 0 := algMap_ne_zero hy
  have hxyF : (algebraMap R (FractionRing R)) x *
              (algebraMap R (FractionRing R)) y ≠ 0 := mul_ne_zero hxF hyF
  have hst_F : (algebraMap R (FractionRing R)) s * (algebraMap R (FractionRing R)) x +
               (algebraMap R (FractionRing R)) t * (algebraMap R (FractionRing R)) y = 1 := by
    have h := congr_arg (algebraMap R (FractionRing R)) hst
    simp only [map_add, map_mul, map_one] at h; exact h
  -- Prove: ι e / (ι x * ι y) = ι(e*t)/ι x + ι(e*s)/ι y
  -- Strategy: show RHS = LHS via eq_div_iff, simplify, linear_combination.
  symm
  rw [eq_div_iff hxyF, add_mul, div_mul_eq_mul_div, div_mul_eq_mul_div]
  -- Goal: ι(e*t)*(ι x*ι y)/ι x + ι(e*s)*(ι x*ι y)/ι y = ι e
  -- The mul_comm on the product inside:
  rw [show (algebraMap R (FractionRing R)) x * (algebraMap R (FractionRing R)) y =
        (algebraMap R (FractionRing R)) y * (algebraMap R (FractionRing R)) x from mul_comm _ _]
  -- Now: ι(e*t)*(ι y*ι x)/ι x + ι(e*s)*(ι y*ι x)/ι y = ι e
  -- Use field_simp to clear remaining denominators
  field_simp [hxF, hyF]
  -- Now a polynomial goal: close with linear_combination
  simp only [map_mul]
  linear_combination (algebraMap R (FractionRing R) e) * hst_F

-- ======================================================================
-- §7  Coprime numerator reduction
-- ======================================================================

private lemma coprime_numer_exists {p : R} (hp : Irreducible p) (a : R) (n : ℕ) :
    ∃ a' q : R, a = a' + q * p ^ n ∧ IsCoprime a' p := by
  rcases Classical.em (p ∣ a) with _h | h
  · exact ⟨0, 0, by sorry, by sorry⟩
  · exact ⟨a, 0, by ring,
      isCoprime_comm.mpr ((irreducible_iff_prime.mp hp).coprime_iff_not_dvd.mpr h)⟩

-- ======================================================================
-- §8  Main theorem
-- ======================================================================

private def emptyPFD (c : FractionRing R) (hc : c = 0) :
    ∃ terms : List (PFDSummand R), IsPFD terms c :=
  ⟨[], { irred := by simp, nonAssoc := by simp, coprime := by simp,
         sum_eq := by simp [hc] }⟩

private lemma isPFD_of_denom : ∀ (d : R) (e : R),
    ∃ terms : List (PFDSummand R),
      IsPFD terms ((algebraMap R (FractionRing R) e) /
                   (algebraMap R (FractionRing R) d)) := by
  intro d
  induction d using UniqueFactorizationMonoid.induction_on_coprime with
  | h0 =>
    intro e; simp only [map_zero, div_zero]
    exact emptyPFD 0 rfl
  | h1 =>
    intro e
    -- Unit denominator: sorry for now (§9C)
    exact ⟨[], { irred := by simp, nonAssoc := by simp,
                 coprime := by simp, sum_eq := by simp; sorry }⟩
  | hpr =>
    rename_i prime_elem exp hp_prime
    intro e
    have hp_irred : Irreducible prime_elem := irreducible_iff_prime.mpr hp_prime
    obtain ⟨e', _q, _he, he'_cop⟩ := coprime_numer_exists hp_irred e exp
    exact ⟨[⟨prime_elem, exp, e'⟩], {
      irred    := fun s hs => by
        simp only [List.mem_singleton] at hs; subst hs; exact hp_irred
      nonAssoc := fun s hs t ht hne => by
        simp only [List.mem_singleton] at hs ht; subst hs; subst ht; exact absurd rfl hne
      coprime  := fun s hs => by
        simp only [List.mem_singleton] at hs; subst hs; exact he'_cop
      sum_eq   := by
        simp only [List.map_singleton, List.sum_singleton, PFDSummand.val]; sorry }⟩
  | hcp =>
    rename_i x y hrel ih_x ih_y
    intro e
    -- Handle the case where x = 0 or y = 0 directly
    rcases eq_or_ne x 0 with rfl | hx
    · -- x = 0: denominator = 0*y = 0, so quotient = 0
      simp only [zero_mul, map_zero, div_zero]
      exact emptyPFD 0 rfl
    rcases eq_or_ne y 0 with rfl | hy
    · -- y = 0: denominator = x*0 = 0, so quotient = 0
      simp only [mul_zero, map_zero, div_zero]
      exact emptyPFD 0 rfl
    -- Both nonzero: apply Bézout splitting
    -- IsCoprime x y from IsRelPrime x y (using IsRelPrime.isCoprime)
    have hcop : IsCoprime x y := hrel.isCoprime
    obtain ⟨s, t, _hst, hsplit⟩ := bezout_split x y hcop hx hy e
    obtain ⟨terms_x, hpfd_x⟩ := ih_x (e * t)
    obtain ⟨terms_y, hpfd_y⟩ := ih_y (e * s)
    exact ⟨terms_x ++ terms_y, {
      irred    := fun s' hs' => by
        rw [List.mem_append] at hs'
        exact hs'.elim (hpfd_x.irred s') (hpfd_y.irred s')
      nonAssoc := fun s' hs' t' ht' hne => by
        rw [List.mem_append] at hs' ht'
        rcases hs' with hs' | hs' <;> rcases ht' with ht' | ht'
        · exact hpfd_x.nonAssoc s' hs' t' ht' hne
        · intro; sorry
        · intro; sorry
        · exact hpfd_y.nonAssoc s' hs' t' ht' hne
      coprime  := fun s' hs' => by
        rw [List.mem_append] at hs'
        exact hs'.elim (hpfd_x.coprime s') (hpfd_y.coprime s')
      sum_eq   := by
        rw [List.map_append, List.sum_append, hpfd_x.sum_eq, hpfd_y.sum_eq]
        -- Goal: ι(e*t)/ι x + ι(e*s)/ι y = ι e / ι(x*y)
        -- hsplit: ι e/(ι x*ι y) = ι(e*t)/ι x + ι(e*s)/ι y
        -- So: ι(e*t)/ι x + ι(e*s)/ι y = ι e/(ι x*ι y) = ι e/ι(x*y)
        rw [← hsplit, map_mul] }⟩

theorem pid_partial_fraction_decomposition (c : FractionRing R) :
    ∃ terms : List (PFDSummand R), IsPFD terms c := by
  obtain ⟨a, b, _hb, hc⟩ := exists_num_denom c
  rw [← hc]; exact isPFD_of_denom b a

section IntExample
example (c : FractionRing ℤ) :
    ∃ terms : List (PFDSummand ℤ), IsPFD terms c :=
  pid_partial_fraction_decomposition c
end IntExample
</pre>The post <a href="https://www.johndcook.com/blog/2026/06/17/rings-with-lean-claude/">Formalizing a ring theorem with Lean 4 and Claude</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Partial fraction decomposition</title>
		<link>https://www.johndcook.com/blog/2026/06/16/partial-fraction-decomposition/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 17:56:27 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Ring theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247197</guid>

					<description><![CDATA[<p>Nearly everyone who has seen partial fraction decomposition was introduced to it as a way to compute integrals. If P(x) and Q(x) are polynomials, then you can break their ratio P(x)/Q(x) into a sum of terms that can each be integrated in closed form. As with most topics in a calculus class, partial fractions go by in [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/16/partial-fraction-decomposition/">Partial fraction decomposition</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Nearly everyone who has seen partial fraction decomposition was introduced to it as a way to compute integrals. If <em>P</em>(<em>x</em>) and <em>Q</em>(<em>x</em>) are polynomials, then you can break their ratio <em>P</em>(<em>x</em>)/<em>Q</em>(<em>x</em>) into a sum of terms that can each be integrated in closed form. As with most topics in a calculus class, partial fractions go by in a blur.</p>
<p>This post will look at partial fractions more generally.</p>
<h2>Computation</h2>
<p>Every polynomial with real coefficients can be factored into a product of linear and irreducible quadratic terms. But actually calculating this factorization is difficult if the degree of the denominator is large.</p>
<p>The quadratic equation is easy to use. There are analogs for 3rd and 4th order polynomials, but they&#8217;re cumbersome. And there is no formula in general for finding roots of polynomials of degree 5 or higher.</p>
<p>You could find the roots numerically, but if you&#8217;re going to go that route, maybe you should evaluate your integral numerically.</p>
<p>Still, it is useful in proving theorems to know that a partial fraction decomposition exists, even if in practice you cannot calculate it.</p>
<h2>Complex numbers</h2>
<p>Rational polynomials over the real numbers can be factored into powers of linear terms and irreducible quadratic terms. There are no irreducible quadratics over the complex numbers thanks to the <a href="https://www.johndcook.com/blog/2020/05/27/fundamental-theorem-of-algebra/">Fundamental Theorem of Algebra</a>, and every polynomial can be factored into a product of linear terms.</p>
<p>This means every rational in <em>z</em> can be broken into a sum of a polynomial in <em>z</em> and polynomials in 1/(<em>z</em> − <em>z</em><sub><em>i</em></sub>) where the <em>z</em><sub><em>i</em></sub> are the roots of the denominator. This fact is important, for example, in contour integration.</p>
<h2>Principle ideal domains</h2>
<p>The concept of partial fraction decomposition can be generalized to the field of fractions over a ring <em>R</em> [1].</p>
<p>If the ring <em>R</em> is a principle ideal domain (PID) [2], then every element <em>c</em> of the field <em>K</em> of fractions over <em>R</em> can be written in the form</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/partial_frac1.svg" alt="c = \sum_i \frac{a_i}{p_i^{r_i}}" width="85" height="44" /></p>
<p>where the <em>p</em><sub><em>i</em></sub> are nonassociate [3] irreducible elements of <em>R</em>, the <em>r</em><sub><em>i</em></sub> are non-negative integers, and the elements <em>a</em><sub><em>i</em></sub> and <em>p</em><sub><em>i</em></sub> are relatively prime.</p>
<p>When <em>R</em> is the ring of polynomials over a field, <em>R</em> is a PID, and the field of fractions is the set of rational functions over that field. When the field is the real or complex numbers, we get the results above. But the field could be something else, such as a finite field.</p>
<h2>Integers</h2>
<p>When <em>R</em> is the ring of integers, the irreducible elements are prime numbers. The nonassociate condition means you can&#8217;t count <em>p</em> and −<em>p</em> as distinct elements, so practically this means we only look at positive primes. The field of fractions is the rational numbers. So the theorem above says that every rational number can be written as a sum of fractions where the denominators of the fractions are prime powers and the numerators are relatively prime to the denominators.</p>
<p>The way you would decompose a rational number into fractions with prime power denominators is analogous to the way you&#8217;d do partial fraction decomposition in a calculus class. For example, suppose we want to decompose 46/75. The distinct prime factors of 75 are 3 and 5, and so we&#8217;d look for fractions with denominators 3, 5, and 25, and in fact</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/partial_frac2.svg" alt="\frac{46}{75} = \frac{1}{3} + \frac{2}{5} - \frac{3}{25}" width="136" height="41" /></p>
<h2>Footnotes</h2>
<p>[1] The field of fractions over <em>R</em> is the set of formal terms <em>a</em>/<em>b</em> where <em>a</em> and <em>b</em> are in <em>R</em> and <em>b</em> ≠ 0. Operations are defined by analogy with rational numbers. If <em>R</em> is an integral domain, the field of fractions really is a field.</p>
<p>[2] A ring is a PID if every ideal can be generated by a single element.</p>
<p>[3] Two elements of an integral domain are said to be associate if they generate the same ideal.</p>The post <a href="https://www.johndcook.com/blog/2026/06/16/partial-fraction-decomposition/">Partial fraction decomposition</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Three examples suffice</title>
		<link>https://www.johndcook.com/blog/2026/06/16/three-examples-suffice/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 11:14:01 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247192</guid>

					<description><![CDATA[<p>You can&#8217;t prove a theorem by just checking a few examples. Except sometimes you can. A few weeks ago I wrote Pentagonal numbers are truncated triangular numbers. In a nutshell, if the pentagonal numbers are defined by Pn = (3n² − n)/2 and the triangular numbers by Tn = (n² + n)/2 then Pn = T2n − 1 − Tn − 1. Here&#8217;s a visualization [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/16/three-examples-suffice/">Three examples suffice</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>You can&#8217;t prove a theorem by just checking a few examples. Except sometimes you can.</p>
<p>A few weeks ago I wrote <a href="https://www.johndcook.com/blog/2026/04/01/truncated-triangular-numbers/">Pentagonal numbers are truncated triangular numbers</a>. In a nutshell, if the pentagonal numbers are defined by</p>
<p style="padding-left: 40px;"><em>P</em><sub><em>n</em></sub> = (3<em>n</em>² − <em>n</em>)/2</p>
<p>and the triangular numbers by</p>
<p style="padding-left: 40px;"><em>T</em><sub><em>n</em></sub> = (<em>n</em>² + <em>n</em>)/2</p>
<p>then</p>
<p style="padding-left: 40px;"><em>P</em><sub><em>n</em></sub> = <em>T</em><sub>2<em>n</em> − 1</sub> − <em>T</em><sub><em>n</em> − 1</sub>.</p>
<p>Here&#8217;s a visualization of the equation.</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/pent_trunk.svg" /></p>
<p>Note that the equation asserts that two quadratic polynomials are equal. If the two polynomials are equal at three points, then they&#8217;re equal everywhere. We might as well make life easy and choose <em>n</em> = 0, 1, and 2.</p>
<p>If you&#8217;d like, you could do this in code.</p>
<pre>&gt;&gt;&gt; P = lambda n: (3*n**2 - n)/2
&gt;&gt;&gt; T = lambda n: (n**2 + n)/2
&gt;&gt;&gt; for n in [0, 1, 2]: assert(P(n) == T(2*n-1) - T(n-1))
</pre>
<p>This provides a rigorous proof, not just a sanity check.</p>
<p>Sometimes checking a few points is not enough to prove an equation with certainty, but it is enough to establish an equation with high probability. More on that <a href="https://www.johndcook.com/blog/2026/05/30/schwartz-zippel/">here</a>.</p>
<h2>Related posts</h2>
<ul>
<li class='link'><a href='https://www.johndcook.com/blog/2026/06/15/testing-pentagonal-numbers/'>Testing pentagonal numbers</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2012/07/21/binomial-coefficient-trick/'>Binomial coefficient trick</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/16/three-examples-suffice/">Three examples suffice</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Testing pentagonal numbers</title>
		<link>https://www.johndcook.com/blog/2026/06/15/testing-pentagonal-numbers/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 15 Jun 2026 23:57:54 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247187</guid>

					<description><![CDATA[<p>The nth pentagonal number Pn is the number of dots in diagrams like those below with n concentric pentagons. We have the formula Pn = (3n² − n)/2 where n is a positive integer. If n is an integer but not positive, the equation above defines a generalized pentagonal number. If you&#8217;re given an n, you can easily compute Pn. [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/15/testing-pentagonal-numbers/">Testing pentagonal numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <em>n</em>th pentagonal number <em>P</em><sub><em>n</em></sub> is the number of dots in diagrams like those below with <em>n</em> concentric pentagons.</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/pentagonal_numbers.png" /></p>
<p>We have the formula</p>
<p style="padding-left: 40px;"><em>P</em><sub><em>n</em></sub> = (3<em>n</em>² − <em>n</em>)/2</p>
<p>where <em>n</em> is a positive integer. If <em>n</em> is an integer but not positive, the equation above defines a <em>generalized</em> pentagonal number.</p>
<p>If you&#8217;re given an <em>n</em>, you can easily compute <em>P</em><sub><em>n</em></sub>. But suppose you&#8217;re given a large number <em>x</em>. How would you determine if it is a pentagonal number? And if it is a pentagonal number, how would you find <em>n</em> such that <em>x</em> = <em>P</em><sub><em>n</em></sub>?</p>
<h2>Rejecting non-pentagonal numbers</h2>
<p>If</p>
<p style="padding-left: 40px;"><em>x</em> = <em>P</em><sub><em>n</em></sub> = (3<em>n</em>² − <em>n</em>)/2</p>
<p>then we can solve a quadratic equation for <em>n</em>:</p>
<p style="padding-left: 40px;"><em>n</em> = (1 ± √(24<em>x</em> + 1))/6.</p>
<p>If 24<em>x</em> + 1 is not a perfect square, <em>n</em> is not an integer and <em>x</em> is not a pentagonal number, ordinary or generalized.</p>
<h3>Small numbers</h3>
<p>For example,</p>
<p style="padding-left: 40px;">√(24 × 20260615 + 1)) = 22051.185…</p>
<p>and so 20260615 is not a pentagonal number nor a generalized pentagonal number.</p>
<h3>Big numbers</h3>
<p>Now suppose</p>
<p style="padding-left: 40px;"><em>x</em> = 170141183460469231731687303715884105727.</p>
<p>Is this a pentagonal number? You can&#8217;t just compute √(24<em>x</em> + 1) in floating point arithmetic because the result is a 20-digit number, and floating point number have 15 digits of precision, so you can&#8217;t tell whether the result is an integer.</p>
<p>However, you can compute</p>
<p style="padding-left: 40px;">⌊√(24<em>x</em> + 1)⌋</p>
<p>with only integer arithmetic using the <code>sqrt_floor</code> function from <a href="https://www.johndcook.com/blog/2024/01/01/computing-square-root-floor/">this post</a>.</p>
<pre>def sqrt_floor(n):
    a = n
    b = (n + 1) // 2
    while b &lt; a:
        a = b
        b = (a*a + n) // (2*a)
    return a
</pre>
<p>The following prints a positive number,</p>
<pre>x = 2**127 - 1
y = 24*x + 1
r = sqrt_floor(y)
print(y - r**2)
</pre>
<p>which tells us <em>y</em> is not a perfect square.</p>
<h2>Finding the index</h2>
<p>Now suppose <em>y</em> <em>is</em> a perfect square. Then the roots of</p>
<p style="padding-left: 40px;">(1 ± √(24<em>x</em> + 1))/6</p>
<p>are rational, but are they integers? In fact one, and only one, of the roots will be an integer. If</p>
<p style="padding-left: 40px;">24<em>x</em> + 1 = <em>r</em>²</p>
<p>then <em>r</em> is congruent to ±1 mod 6 because the left side is congruent to 1 mod 6. If <em>r</em> = 1 mod 6 then the smaller root is an integer, and if <em>r</em> = 5 mod 6 then the larger root is an integer.</p>
<p>So if 24<em>x</em> + 1 = <em>r</em>², then <em>x</em> is a pentagonal number if <em>r</em> = 5 mod 6 and a generalized pentagonal number otherwise.</p>
<p>The function <code>pentagonal_index</code> takes a number <em>x</em> and return n if <em>x</em> = <em>P</em><sub><em>n</em></sub> and <code>None</code> if no such <em>n</em> exists.</p>
<pre>def pentagonal_index(x):
    y = 24*x + 1
    r = sqrt_floor(y)
    if r*r != y:
        return None
    if r % 6 == 5:
        return (1 + r) // 6
    else:
        return (1 - r) // 6
</pre>
<p>We can test this with the following code.</p>
<pre>P = lambda n: (3*n**2 - n) // 2
for n in [2, 3, -4, -5, 10**200]:
    assert(pentagonal_index(P(n)) == n)
</pre>
<h2>Integer division in Python</h2>
<p>Note that <code>P(10**200)</code> is too big to fit into a float, but the code works fine. This is because we use integer division (<code>//</code>) everywhere. If we had said</p>
<pre>P = lambda n: (3*n**2 - n) / 2</pre>
<p>the test above would pass for the small values of <em>n</em> but output</p>
<pre>OverflowError: integer division result too large for a float</pre>
<p>when it came to <em>n</em> = 10<sup>200</sup>.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2021/11/10/partitions-and-pentagons/">Partitions and pentagons</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/02/21/big-certified-fibonacci/">Certified Fibonacci numbers</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/04/01/truncated-triangular-numbers/">Truncated triangular numbers</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/15/testing-pentagonal-numbers/">Testing pentagonal numbers</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Quaternion Rotations, Claude, and Lean</title>
		<link>https://www.johndcook.com/blog/2026/06/15/quaternions-claude-lean/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 15 Jun 2026 19:31:38 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Formal methods]]></category>
		<category><![CDATA[Lean]]></category>
		<category><![CDATA[Quaternions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247178</guid>

					<description><![CDATA[<p>I got an email message this afternoon reporting a typo in a blog post from about a year ago on converting between quaternions and rotation matrices [1]. The email said exactly where the typo was, but I decided to see whether Claude would find it. Specifically, I prompted Sonnet 4.6 Medium with the following. Write [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/15/quaternions-claude-lean/">Quaternion Rotations, Claude, and Lean</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I got an email message this afternoon reporting a typo in a blog post from about a year ago on <a href="https://www.johndcook.com/blog/2025/05/07/quaternions-and-rotation-matrices/">converting between quaternions and rotation matrices</a> [1]. The email said exactly where the typo was, but I decided to see whether Claude would find it. Specifically, I prompted Sonnet 4.6 Medium with the following.</p>
<p class="whitespace-pre-wrap break-words" style="padding-left: 40px;">Write Lean code to verify the two theorems at the top of this post:<br />
<a class="underline" href="https://www.johndcook.com/blog/2025/05/07/quaternions-and-rotation-matrices/" target="_blank" rel="noopener">https://www.johndcook.com/blog/2025/05/07/quaternions-and-rotation-matrices/</a></p>
<p class="whitespace-pre-wrap break-words" style="padding-left: 40px;">That is, prove that the expressions given in the two SVG files are correct.</p>
<p>The post included Python code to numerically verify the equations. However, the Python code differed from the LaTeX code for the image in one subscript [2]. Although I asked Claude to prove the expressions in the SVG file produced by the LaTeX code, it detected the conflict between the Python and LaTeX and correctly concluded that the former was correct.</p>
<p class="font-claude-response-body break-words whitespace-normal" style="padding-left: 40px;">The SVG is an image — let me rely on the Python code in the blog (which is the ground truth implementation) and the matrix entries in the alt text. &hellip; Note the alt text says row 1 col 2 is <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">2(q_1 q_3 - q_0 q_1)</code> — that&#8217;s a typo in the alt text; the Python code says <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">2*(q2*q3 - q0*q1)</code> which makes sense. I&#8217;ll use the Python code as the authoritative source.</p>
<p>The code did not run on first attempt. After four iterations of pasting in error messages and generating code again, it worked.</p>
<p>Here is the final Lean 4 code.</p>
<pre>/-
  Lean 4 / Mathlib verification of the two theorems from:
    "Converting between quaternions and rotation matrices"
    ...

  Matrix entries (0-based, matching the post's Python code):
    R00 = 2(q0²+q1²)−1    R01 = 2(q1 q2−q0 q3)   R02 = 2(q1 q3+q0 q2)
    R10 = 2(q1 q2+q0 q3)  R11 = 2(q0²+q2²)−1     R12 = 2(q2 q3−q0 q1)
    R20 = 2(q1 q3−q0 q2)  R21 = 2(q2 q3+q0 q1)   R22 = 2(q0²+q3²)−1

  THEOREM 1  (quaternion → rotation matrix)
    If q0²+q1²+q2²+q3² = 1 then R is orthogonal (Rᵀ R = I),
    proved via the 9 scalar dot-product identities.

  THEOREM 2  (rotation matrix → quaternion, Chiaverini–Siciliano)
    With rᵢⱼ as above:
        1 + R00 + R11 + R22 = 4 q0²
        1 + R00 − R11 − R22 = 4 q1²
        1 − R00 + R11 − R22 = 4 q2²
        1 − R00 − R11 + R22 = 4 q3²
-/

import Mathlib.Tactic

set_option linter.style.whitespace false

variable (q0 q1 q2 q3 : ℝ)

/-! ## Theorem 1 : Rᵀ R = I -/

-- ── Column norms = 1 ─────────────────────────────────────────────────────────

theorem col0_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) ^ 2 + (2 * (q1 * q2 + q0 * q3)) ^ 2 +
    (2 * (q1 * q3 - q0 * q2)) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 + q1 ^ 2 - q2 ^ 2 - q3 ^ 2),
             sq_nonneg (q0 * q2 + q1 * q3), sq_nonneg (q0 * q3 - q1 * q2),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

theorem col1_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q2 - q0 * q3)) ^ 2 + (2 * (q0 ^ 2 + q2 ^ 2) - 1) ^ 2 +
    (2 * (q2 * q3 + q0 * q1)) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 - q1 ^ 2 + q2 ^ 2 - q3 ^ 2),
             sq_nonneg (q0 * q1 + q2 * q3), sq_nonneg (q0 * q3 - q1 * q2),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

theorem col2_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q3 + q0 * q2)) ^ 2 + (2 * (q2 * q3 - q0 * q1)) ^ 2 +
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 - q1 ^ 2 - q2 ^ 2 + q3 ^ 2),
             sq_nonneg (q0 * q1 - q2 * q3), sq_nonneg (q0 * q2 + q1 * q3),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

-- ── Column orthogonality = 0 ─────────────────────────────────────────────────
-- These need h (the residuals shown by Lean are multiples of (q0²+q1²+q2²+q3²-1)).
-- We use linear_combination with the explicit witnesses.

theorem col0_col1_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) * (2 * (q1 * q2 - q0 * q3)) +
    (2 * (q1 * q2 + q0 * q3)) * (2 * (q0 ^ 2 + q2 ^ 2) - 1) +
    (2 * (q1 * q3 - q0 * q2)) * (2 * (q2 * q3 + q0 * q1)) = 0 := by
  linear_combination 4 * q1 * q2 * h

theorem col0_col2_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) * (2 * (q1 * q3 + q0 * q2)) +
    (2 * (q1 * q2 + q0 * q3)) * (2 * (q2 * q3 - q0 * q1)) +
    (2 * (q1 * q3 - q0 * q2)) * (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 0 := by
  linear_combination 4 * q1 * q3 * h

theorem col1_col2_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q2 - q0 * q3)) * (2 * (q1 * q3 + q0 * q2)) +
    (2 * (q0 ^ 2 + q2 ^ 2) - 1) * (2 * (q2 * q3 - q0 * q1)) +
    (2 * (q2 * q3 + q0 * q1)) * (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 0 := by
  linear_combination 4 * q2 * q3 * h

-- ── Row norms = 1 ────────────────────────────────────────────────────────────

theorem row0_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) ^ 2 + (2 * (q1 * q2 - q0 * q3)) ^ 2 +
    (2 * (q1 * q3 + q0 * q2)) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 + q1 ^ 2 - q2 ^ 2 - q3 ^ 2),
             sq_nonneg (q0 * q2 - q1 * q3), sq_nonneg (q0 * q3 + q1 * q2),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

theorem row1_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q2 + q0 * q3)) ^ 2 + (2 * (q0 ^ 2 + q2 ^ 2) - 1) ^ 2 +
    (2 * (q2 * q3 - q0 * q1)) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 - q1 ^ 2 + q2 ^ 2 - q3 ^ 2),
             sq_nonneg (q0 * q1 - q2 * q3), sq_nonneg (q0 * q3 + q1 * q2),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

theorem row2_norm (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q3 - q0 * q2)) ^ 2 + (2 * (q2 * q3 + q0 * q1)) ^ 2 +
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) ^ 2 = 1 := by
  nlinarith [sq_nonneg (q0 ^ 2 - q1 ^ 2 - q2 ^ 2 + q3 ^ 2),
             sq_nonneg (q0 * q1 + q2 * q3), sq_nonneg (q0 * q2 - q1 * q3),
             sq_nonneg q0, sq_nonneg q1, sq_nonneg q2, sq_nonneg q3]

-- ── Row orthogonality = 0 ────────────────────────────────────────────────────

theorem row0_row1_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) * (2 * (q1 * q2 + q0 * q3)) +
    (2 * (q1 * q2 - q0 * q3)) * (2 * (q0 ^ 2 + q2 ^ 2) - 1) +
    (2 * (q1 * q3 + q0 * q2)) * (2 * (q2 * q3 - q0 * q1)) = 0 := by
  linear_combination 4 * q1 * q2 * h

theorem row0_row2_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q0 ^ 2 + q1 ^ 2) - 1) * (2 * (q1 * q3 - q0 * q2)) +
    (2 * (q1 * q2 - q0 * q3)) * (2 * (q2 * q3 + q0 * q1)) +
    (2 * (q1 * q3 + q0 * q2)) * (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 0 := by
  linear_combination 4 * q1 * q3 * h

theorem row1_row2_orth (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    (2 * (q1 * q2 + q0 * q3)) * (2 * (q1 * q3 - q0 * q2)) +
    (2 * (q0 ^ 2 + q2 ^ 2) - 1) * (2 * (q2 * q3 + q0 * q1)) +
    (2 * (q2 * q3 - q0 * q1)) * (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 0 := by
  linear_combination 4 * q2 * q3 * h

/-! ## Theorem 2 : Chiaverini–Siciliano square-root arguments -/

theorem cs_arg0 (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    1 + (2 * (q0 ^ 2 + q1 ^ 2) - 1) + (2 * (q0 ^ 2 + q2 ^ 2) - 1) +
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 4 * q0 ^ 2 := by linarith

theorem cs_arg1 (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    1 + (2 * (q0 ^ 2 + q1 ^ 2) - 1) - (2 * (q0 ^ 2 + q2 ^ 2) - 1) -
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 4 * q1 ^ 2 := by linarith

theorem cs_arg2 (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    1 - (2 * (q0 ^ 2 + q1 ^ 2) - 1) + (2 * (q0 ^ 2 + q2 ^ 2) - 1) -
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 4 * q2 ^ 2 := by linarith

theorem cs_arg3 (h : q0 ^ 2 + q1 ^ 2 + q2 ^ 2 + q3 ^ 2 = 1) :
    1 - (2 * (q0 ^ 2 + q1 ^ 2) - 1) - (2 * (q0 ^ 2 + q2 ^ 2) - 1) +
    (2 * (q0 ^ 2 + q3 ^ 2) - 1) = 4 * q3 ^ 2 := by linarith

/-! ### Sign-correction identities (pure ring) -/

theorem sign_q1 : (2 * (q2 * q3 + q0 * q1)) - (2 * (q2 * q3 - q0 * q1)) = 4 * (q0 * q1) := by ring
theorem sign_q2 : (2 * (q1 * q3 + q0 * q2)) - (2 * (q1 * q3 - q0 * q2)) = 4 * (q0 * q2) := by ring
theorem sign_q3 : (2 * (q1 * q2 + q0 * q3)) - (2 * (q1 * q2 - q0 * q3)) = 4 * (q0 * q3) := by ring
</pre>
<p>[1] I very much appreciate error reports. Thank you to everyone who has helped improve this blog.</p>
<p>[2] It is awkward that math is written in LaTeX and implemented in, say, Python. Lately I&#8217;ve been generating one or the other to reduce the chance of error. When I&#8217;m using Mathematica, for example, I&#8217;ll use <code>TeXForm[]</code> to convert the Mathematica code to LaTeX.</p>
<p>&nbsp;</p>The post <a href="https://www.johndcook.com/blog/2026/06/15/quaternions-claude-lean/">Quaternion Rotations, Claude, and Lean</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Writing Prolog with ChatGPT</title>
		<link>https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/</link>
					<comments>https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 15 Jun 2026 17:22:43 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Chess]]></category>
		<category><![CDATA[Logic]]></category>
		<category><![CDATA[Prolog]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247171</guid>

					<description><![CDATA[<p>A few days ago I wrote about using Claude to solve a chess puzzle by writing Prolog code. This morning I tried a similar chess puzzle with ChatGPT. The task is to place a queen, king, rook, bishop, and knight on a 4 by 4 chessboard so no piece attacks another. Of course there&#8217;s not [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/">Writing Prolog with ChatGPT</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p><a href="https://www.johndcook.com/blog/2026/06/11/prolog-claude/">A few days ago</a> I wrote about using Claude to solve a chess puzzle by writing Prolog code. This morning I tried a <a href="https://www.johndcook.com/blog/2025/05/13/a-crowded-little-chess-puzzle/">similar chess puzzle</a> with ChatGPT. The task is to place a queen, king, rook, bishop, and knight on a 4 by 4 chessboard so no piece attacks another.</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/crowded4.png" /></p>
<p>Of course there&#8217;s not a lot of market demand for solving chess puzzles, but there is demand for solving real-world problems that amount to logic puzzles. Experiments with chess puzzles suggest writing Prolog with LLM help might be pleasant. The syntax of Prolog can be a little odd, but the LLM handles that. And because Prolog has been around so long without changing much, you&#8217;d expect LLMs to be good at generating code in it. [1]</p>
<p>I gave ChatGPT this prompt:</p>
<blockquote><p>Write a SWI Prolog program to solve the following chess puzzle. Place a queen, king, rook, bishop, and knight on a 4 by 4 chessboard so no piece attacks another. Print all solutions.</p></blockquote>
<p>It produced a file <code>chess4.pl</code> (below) that I ran with</p>
<pre>swipl -q -s chess4.pl -g solve -t halt</pre>
<p>It found all 16 solutions:</p>
<pre>Solution 1:
  queen=1-2  king=2-4  rook=3-1  bishop=4-3  knight=4-4
. K . N
. . . B
Q . . .
. . R .

Solution 2:
  queen=1-2  king=4-3  rook=2-4  bishop=3-1  knight=4-1
. R . .
. . . K
Q . . .
. . B N

...
</pre>
<p>Here&#8217;s the code.</p>
<pre>% chess4.pl
% Place queen, king, rook, bishop, and knight on a 4x4 board
% so that no piece attacks another.

:- use_module(library(lists)).

square(X-Y) :-
    between(1, 4, X),
    between(1, 4, Y).

solve :-
    findall(Board, solution(Board), Boards),
    print_solutions(Boards, 1),
    length(Boards, Count),
    format('Total solutions: ~d~n', [Count]).

solution(Board) :-
    Pieces = [queen, king, rook, bishop, knight],
    findall(S, square(S), Squares),
    place_pieces(Pieces, Squares, Board),
    safe(Board).

place_pieces([], _, []).
place_pieces([Piece | Pieces], Squares, [Piece-Square | Board]) :-
    select(Square, Squares, RemainingSquares),
    place_pieces(Pieces, RemainingSquares, Board).

safe(Board) :-
    safe(Board, Board).

safe([], _).
safe([Piece-Square | Rest], Board) :-
    forall(
        member(OtherPiece-OtherSquare, Rest),
        non_attacking(Piece-Square, OtherPiece-OtherSquare, Board)
    ),
    safe(Rest, Board).

non_attacking(P1-S1, P2-S2, Board) :-
    \+ attacks(P1, S1, S2, Board),
    \+ attacks(P2, S2, S1, Board).

% Attack rules

attacks(king, X1-Y1, X2-Y2, _) :-
    X1-Y1 \= X2-Y2,
    DX is abs(X1 - X2),
    DY is abs(Y1 - Y2),
    DX =&lt; 1,
    DY =&lt; 1. attacks(knight, X1-Y1, X2-Y2, _) :- DX is abs(X1 - X2), DY is abs(Y1 - Y2), ( DX =:= 1, DY =:= 2 ; DX =:= 2, DY =:= 1 ). attacks(rook, S1, S2, Board) :- same_rank_or_file(S1, S2), clear_line(S1, S2, Board). attacks(bishop, S1, S2, Board) :- same_diagonal(S1, S2), clear_line(S1, S2, Board). attacks(queen, S1, S2, Board) :- ( same_rank_or_file(S1, S2) ; same_diagonal(S1, S2) ), clear_line(S1, S2, Board). same_rank_or_file(X1-Y1, X2-Y2) :- ( X1 =:= X2 ; Y1 =:= Y2 ). same_diagonal(X1-Y1, X2-Y2) :- DX is abs(X1 - X2), DY is abs(Y1 - Y2), DX =:= DY. % For sliding pieces, make sure no piece blocks the path. clear_line(X1-Y1, X2-Y2, Board) :- step(X1, X2, SX), step(Y1, Y2, SY), NX is X1 + SX, NY is Y1 + SY, clear_path(NX-NY, X2-Y2, SX-SY, Board). clear_path(Target, Target, _, _) :- !. clear_path(X-Y, Target, SX-SY, Board) :- \+ occupied(X-Y, Board), NX is X + SX, NY is Y + SY, clear_path(NX-NY, Target, SX-SY, Board). step(A, B, 0) :- A =:= B. step(A, B, 1) :- B &gt; A.
step(A, B, -1) :-
    B &lt; A.

occupied(Square, Board) :-
    member(_-Square, Board).

% Printing

print_solutions([], _).
print_solutions([Board | Rest], N) :-
    format('Solution ~d:~n', [N]),
    print_coordinates(Board),
    print_board(Board),
    nl,
    N1 is N + 1,
    print_solutions(Rest, N1).

print_coordinates(Board) :-
    member(queen-Q, Board),
    member(king-K, Board),
    member(rook-R, Board),
    member(bishop-B, Board),
    member(knight-N, Board),
    format('  queen=~w  king=~w  rook=~w  bishop=~w  knight=~w~n',
           [Q, K, R, B, N]).

print_board(Board) :-
    forall(
        between(1, 4, Row0),
        (
            Y is 5 - Row0,
            forall(
                between(1, 4, X),
                (
                    piece_at(X-Y, Board, Symbol),
                    format('~w ', [Symbol])
                )
            ),
            nl
        )
    ).

piece_at(Square, Board, Symbol) :-
    member(Piece-Square, Board),
    !,
    piece_symbol(Piece, Symbol).
piece_at(_, _, '.').

piece_symbol(queen,  'Q').
piece_symbol(king,   'K').
piece_symbol(rook,   'R').
piece_symbol(bishop, 'B').
piece_symbol(knight, 'N').
</pre>
<p>[1] I&#8217;ve played around with generating Prolog and Lean this weekend, and I&#8217;ve had better results with Prolog. The problems with Lean haven&#8217;t been Lean per se but the Mathlib library. The library is frequently refactored, which makes sense for a young language, but this makes it harder to generate and debug code.</p>The post <a href="https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/">Writing Prolog with ChatGPT</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/06/15/writing-prolog-with-chatgpt/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>RSA munitions T-shirt</title>
		<link>https://www.johndcook.com/blog/2026/06/13/rsa-munitions-t-shirt/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 13 Jun 2026 20:10:20 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Cryptography]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247160</guid>

					<description><![CDATA[<p>Back when the US government classified strong encryption as &#8220;munitions,&#8221; RSA public key cryptography was illegal to export. In 1995, Adam Back protested this by creating a terse, obfuscated implementation of RSA in Perl code and used it as an email signature. The code was also printed on T-shirts. The shirt was classified as munitions [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/06/13/rsa-munitions-t-shirt/">RSA munitions T-shirt</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Back when the US government classified strong encryption as &#8220;munitions,&#8221; RSA public key cryptography was illegal to export. In 1995, Adam Back protested this by creating a terse, obfuscated implementation of RSA in Perl code and used it as an email signature.</p>
<p>The code was also printed on T-shirts. The shirt was classified as munitions because it contained source code for strong encryption. More on the shirt <a href="http://www.cypherspace.org/adam/rsa/uk-shirt.html">here</a>.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/munitions_shirt.jpg" alt="Adam Back's munitions T-shirt" width="499" height="574" /></p>
<p>This was the code:</p>
<pre>#!/bin/perl -s-- -export-a-crypto-system-sig -RSA-3-lines-PERL
$m=unpack(H.$w,$m."\0"x$w),$_=`echo "16do$w 2+4Oi0$d*-^1[d2%Sa
2/d0&lt;X+d*La1=z\U$n%0]SX$k"[$m*]\EszlXx++p|dc`,s/^.|\W//g,print
pack('H*',$_)while read(STDIN,$m,($w=2*$d-1+length$n&amp;~1)/2)
</pre>
<p>My initial intention was to unpack the code, explaining each piece in detail. I don&#8217;t have the time or patience for that, and I imagine many readers don&#8217;t either. For more of a blow-by-blow explanation, see this <a href="http://www.cypherspace.org/rsa/org-post.html">commentary</a> from 1995.</p>
<h2>dc</h2>
<p>In the middle of the code is</p>
<pre>    echo ... | dc</pre>
<p>This is the most dense and most important part of the code. Perl calls the <code>dc</code> calculator to do the arbitrary precision arithmetic that RSA encryption requires.</p>
<p>I&#8217;ve written about <code>bc</code> several times. <code>bc</code> (&#8220;basic calculator&#8221;) was a originally a more user-friendly wrapper around the reverse-Polish <code>dc</code> (&#8220;desktop calculator&#8221;). <code>dc</code> is still part of every Unix and Unix-like system, but I imagine <code>bc</code> is far more popular.</p>
<p>The important feature of <code>dc</code> for this post is that it is stack-based, meaning that users would push data and commands on to the stack and pop results off the stack. A sequence of commands that might be understandable when interactively using <code>dc</code> would look cryptic in a transcript. This is part of what makes the code so cryptic.</p>
<p>I&#8217;ll parse just a tiny bit of the <code>dc</code> code to give a flavor of what it does. The first four characters <code>16do</code> instructs <code>dc</code> to push 16 on to the stack, duplicate it, and set the output radix to 16, i.e. these four characters tell <code>dc</code> to work in hexadecimal.</p>
<p>Believe it or not, the <code>dc</code> code is computing</p>
<p style="padding-left: 40px;"><em>m</em><sup><em>k</em></sup> mod <em>n</em></p>
<p>using <a href="https://www.johndcook.com/blog/2008/12/10/fast-exponentiation/">fast exponentiation</a>, which is the key step in the RSA algorithm.</p>
<h2>Textbook RSA</h2>
<p>Note that Adam Back&#8217;s code is computing what we would now call textbook RSA, not RSA as it has been refined over the years and is <a href="https://www.johndcook.com/blog/2023/08/05/rsa-oaep/">currently implemented</a>.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2025/08/05/martin-gardners-rsa/">Martin Gardner&#8217;s RSA article</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2019/02/11/rsa-duplication-flaws/">RSA encryption flaws</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2018/09/23/eulers-theorem/">Three applications of Euler&#8217;s theorem</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/06/13/rsa-munitions-t-shirt/">RSA munitions T-shirt</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
