1-- Pythagorean triples: the parametrization (m² − n², 2mn, m² + n²) gives2-- every primitive triple with an even second leg, and every triple is a3-- multiple of a primitive one4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.NumberTheory.PythagoreanTriples10import CoprimeSquares1112-- Positive naturals a, b, c with a² + b² = c².13predicate Pythagorean (a b c : Nat) :=0 < a ∧0 < b ∧ a ^ 2+ b ^ 2= c ^ 21415-- A Pythagorean triple whose legs are coprime.16predicate PrimitivePythagorean (a b c : Nat) :=17 Pythagorean(a, b, c) ∧ gcd(a, b) =11819-- Squaring is injective on the naturals.20theorem IsSquare.root_unique (x y : Nat)21requires squares : x * x = y * y22shows x = y23proof24have order := Less.trichotomy at x, y with*25rcases order with below | equal | above26 . exfalso27have strict := Less.mul at x, y, x, y with below, below28rw squares at strict29exact Less.irreflexive at y * y with strict30 . exact equal31 . exfalso32have strict := Less.mul at y, x, y, x with above, above33rw squares at strict34exact Less.irreflexive at y * y with strict3536theorem Odd.square_mod_four (k : Nat)37requires odd : Odd(k)38shows (k * k) %4=139proof40obtain⟨j, jNat, shape⟩:= Odd.iff_two_mul_add_one.forward with odd41have expand : k * k =4* (j * j + j) +142rw shape43ring44rw expand45have four : 4≠0byarith46have reduce := mod.multiple_add at4, 1, j * j + j with four47rw reduce48compute4950-- An odd divisor of 2t divides t.51theorem divides.of_double_odd (d t : Nat)52requires odd : Odd(d)53requires double : d ∣ 2* t54shows d ∣ t55proof56obtain⟨k, kNat, product⟩:= double57have evenK : Even(k)58have evenProduct : Even(d * k)59rw product60exact Even.two_mul at t61havesplit:= Even.of_mul at d, k with evenProduct62casessplitwith evenD | evenK63 . exfalso64exact Odd.not_even with odd, evenD65 . exact evenK66obtain⟨j, jNat, kShape⟩:= Even.iff_two_mul.forward with evenK67witness j68have scaled : 2* (d * j) =2* t69rw [← product, kShape]70ring71have two : 2≠0byarith72exact mul.left_cancel_positive at d * j, t, 2with two, scaled7374-- With m² = n² + d, the difference d has the parity of m + n.75theorem Even.root_sum_iff (m n d : Nat)76requires shape : m * m = n * n + d77shows Even(m + n) ↔ Even(d)78proof79have squareM := Even.mul_self_iff at m80have squareN := Even.mul_self_iff at n81have sumMN := Even.add_iff at m, n82have sumND := Even.add_iff at n * n, d83rw shape at squareM84tauto8586-- The parametrization: for m > n > 0 the triple87-- (m² − n², 2mn, m² + n²) is Pythagorean.88theorem pythagorean.of_parameters (m n : Nat)89requires positive : 0 < n90requires below : n < m91shows Pythagorean(m ^ 2 - n ^ 2, 2* m * n, m ^ 2+ n ^ 2)92proof93have squares := Less.mul at n, m, n, m with below, below94have squaresLe : n * n ≤ m * m byarith95obtain⟨d, dNat, shape⟩:= LessEqual.exists_add at n * n, m * m96with squaresLe97have difference : m ^ 2 - n ^ 2= d98rw [pow.two, pow.two, shape]99exact sub.add_left at d, n * n100have dPositive : 0 < d byarith101have productPositive : 0 < 2* m * n102have mNonzero : m ≠0byarith103have nNonzero : n ≠0byarith104have two : 2≠0byarith105have twoM := mul.nonzero at2, m with two, mNonzero106have all := mul.nonzero at2* m, n with twoM, nNonzero107exact Less.positive_of_ne_zero at2* m * n with all108unfold Pythagorean109rw difference110refine⟨dPositive, productPositive, _⟩111simp [pow.two]112ring [shape]113114-- With m, n coprime and of opposite parity, the parametrized triple is115-- primitive: a common divisor e of m² − n² and 2mn is odd, hence divides116-- mn, and is coprime to m and to n, hence to mn.117theorem pythagorean.primitive_of_parameters (m n : Nat)118requires positive : 0 < n119requires below : n < m120requires coprime : gcd(m, n) =1121requires parity : Odd(m + n)122shows PrimitivePythagorean(m ^ 2 - n ^ 2, 2* m * n, m ^ 2+ n ^ 2)123proof124constructor125 . exact pythagorean.of_parameters at m, n with positive, below126 . have squares := Less.mul at n, m, n, m with below, below127have squaresLe : n * n ≤ m * m byarith128obtain⟨d, dNat, shape⟩:= LessEqual.exists_add at n * n, m * m129with squaresLe130have difference : m ^ 2 - n ^ 2= d131rw [pow.two, pow.two, shape]132exact sub.add_left at d, n * n133rw difference134have oddD : Odd(d)135have parities := Even.root_sum_iff at m, n, d with shape136have notEvenSum := Odd.not_even at m + n with parity137have oddIff := Odd.iff_not_even at d138apply oddIff.backward139tauto140have every := gcd.eq_one_iff at d, 2* m * n141apply every.backward142intros e eNat ed eProduct143have oddE : Odd(e)144obtain⟨k, kNat, product⟩:= ed145rw← product at oddD146have both := Odd.of_mul at e, k with oddD147exact both.left148have regroup : 2* m * n =2* (m * n) byring149rw regroup at eProduct150have eDividesMN := divides.of_double_odd at e, m * n with oddE, eProduct151have coprimeEM : gcd(e, m) =1152have everyM := gcd.eq_one_iff at e, m153apply everyM.backward154intros f fNat fe fm155have fd := divides.transitive at f, e, d with fe, ed156have fmm := divides.mul_self at f, m with fm157have fnn : f ∣ n * n158have cancel := sub.add_right at n * n, d159rw [← cancel, ← shape]160exact divides.sub at f, m * m, d with fmm, fd161have coprimeFN := coprime.of_divides_left at m, n, f with coprime, fm162have coprimeFNN := coprime.pow_right at f, n, 2with coprimeFN163rw pow.two at coprimeFNN164have fSelf := divides.reflexive at f165exact coprime.divisor_one at f, n * n, f with coprimeFNN, fSelf, fnn166have coprimeEN : gcd(e, n) =1167have everyN := gcd.eq_one_iff at e, n168apply everyN.backward169intros f fNat fe fn170have fd := divides.transitive at f, e, d with fe, ed171have fnn := divides.mul_self at f, n with fn172have fmm : f ∣ m * m173rw shape174exact divides.add at f, n * n, d with fnn, fd175have coprimeNM := coprime.symmetric at m, n with coprime176have coprimeFM := coprime.of_divides_left at n, m, f with coprimeNM, fn177have coprimeFMM := coprime.pow_right at f, m, 2with coprimeFM178rw pow.two at coprimeFMM179have fSelf := divides.reflexive at f180exact coprime.divisor_one at f, m * m, f with coprimeFMM, fSelf, fmm181have coprimeME := coprime.symmetric at e, m with coprimeEM182have coprimeNE := coprime.symmetric at e, n with coprimeEN183have coprimeMNE := coprime.mul_of_coprime at m, n, e184with coprimeME, coprimeNE185have coprimeEMN := coprime.symmetric at m * n, e with coprimeMNE186have eSelf := divides.reflexive at e187exact coprime.divisor_one at e, m * n, e with coprimeEMN, eSelf, eDividesMN188189-- In a primitive triple exactly one leg is even: two even legs share the190-- factor 2, and two odd legs give c² ≡ 2 modulo 4.191theorem pythagorean.one_even (a b c : Nat)192requires primitive : PrimitivePythagorean(a, b, c)193shows Odd(a + b)194proof195obtain⟨triple, coprime⟩:= primitive196obtain⟨positiveA, positiveB, equation⟩:= triple197simp [pow.two] at equation198have notBothEven : ¬(Even(a) ∧ Even(b))199intro both200have twoIffA := divides.two_iff_even at a201have twoIffB := divides.two_iff_even at b202have twoA := twoIffA.backward with both.left203have twoB := twoIffB.backward with both.right204have two := coprime.divisor_one at a, b, 2with coprime, twoA, twoB205arith206have notBothOdd : ¬(Odd(a) ∧ Odd(b))207intro both208have modA := Odd.square_mod_four at a with both.left209have modB := Odd.square_mod_four at b with both.right210have four : 4≠0byarith211have reduce := mod.add at4, a * a, b * b with four212rw [modA, modB, equation] at reduce213have value : (1+1) %4=2bycompute214rw value at reduce215have square := IsSquare.intro at c216have notTwo := IsSquare.mod_four_ne_two at c * c with square217exact notTwo with reduce218have evenIffA := Even.iff_not_odd at a219have evenIffB := Even.iff_not_odd at b220have sum := Even.add_iff at a, b221have oddIff := Odd.iff_not_even at a + b222apply oddIff.backward223tauto224225-- Every primitive triple with b even comes from the parametrization: with226-- c = a + 2u and b = 2w, the equation gives w² = u(a + u) with u and a + u227-- coprime, so u = n² and a + u = m².228theorem pythagorean.primitive_form (a b c : Nat)229requires primitive : PrimitivePythagorean(a, b, c)230requires even : Even(b)231shows∃ m, Nat(m) ∧∃ n, Nat(n) ∧2320 < n ∧ n < m ∧ gcd(m, n) =1∧ Odd(m + n) ∧233 a = m ^ 2 - n ^ 2∧ b =2* m * n ∧ c = m ^ 2+ n ^ 2234proof235have parity := pythagorean.one_even at a, b, c with primitive236obtain⟨triple, coprime⟩:= primitive237obtain⟨positiveA, positiveB, equation⟩:= triple238simp [pow.two] at equation239have oddA : Odd(a)240have swapped : Odd(b + a)241have commute : b + a = a + b byring242rw commute243exact parity244exact Odd.of_add_odd_even at b, a with swapped, even245have oddC : Odd(c)246have squareA := Odd.mul_self_iff at a247have squareB := Even.mul_self_iff at b248have oddSquareA := squareA.backward with oddA249have evenSquareB := squareB.backward with even250have oddSum := Odd.add_even at a * a, b * b with oddSquareA, evenSquareB251rw equation at oddSum252have squareC := Odd.mul_self_iff at c253exact squareC.forward with oddSum254have aBelowC : a < c255have bNonzero : b ≠0byarith256have bbNonzero := mul.nonzero at b, b with bNonzero, bNonzero257have bbPositive := Less.positive_of_ne_zero at b * b with bbNonzero258have squares : a * a < c * c byarith259exact IsSquare.root_lt at a, c with squares260have aLeC : a ≤ c byarith261obtain⟨t, tNat, cShape⟩:= LessEqual.exists_add at a, c with aLeC262have evenT : Even(t)263rw cShape at oddC264exact Even.of_add_odd_odd at a, t with oddC, oddA265obtain⟨u, uNat, tShape⟩:= Even.iff_two_mul.forward with evenT266obtain⟨w, wNat, bShape⟩:= Even.iff_two_mul.forward with even267have key : u * (a + u) = w * w268have expanded : 4* (u * (a + u)) =4* (w * w)269rw [bShape, cShape, tShape] at equation270have leftSide : a * a +2* w * (2* w) = a * a +4* (w * w) byring271have rightSide : (a +2* u) * (a +2* u) = a * a +4* (u * (a + u))272ring273rw [leftSide, rightSide] at equation274have cancel := add.left_cancel at4* (w * w), 4* (u * (a + u)), a * a275have swapped := cancel with equation276 symm277exact swapped278have four : 4≠0byarith279exact mul.left_cancel_positive at u * (a + u), w * w, 4with four, expanded280have uPositive : 0 < u byarith281have coprimeUV : gcd(u, a + u) =1282have every := gcd.eq_one_iff at u, a + u283apply every.backward284intros d dNat du dv285have da := divides.sub at d, a + u, u with dv, du286have cancel := sub.add_right at a, u287rw cancel at da288have dSquares := divides.mul_mono at d, u, d, a + u with du, dv289rw key at dSquares290have dw := divides.of_mul_self at d, w with dSquares291have db : d ∣ b292rw bShape293exact divides.multiply_left at d, w, 2with*294exact coprime.divisor_one at a, b, d with coprime, da, db295obtain⟨squareU, squareV⟩:= IsSquare.of_coprime_mul at u, a + u, w296with coprimeUV, key297obtain⟨n, nNat, uShape⟩:= squareU298obtain⟨m, mNat, vShape⟩:= squareV299have nPositive : 0 < n300have nNonzero : n ≠0301intro nZero302rw nZero at uShape303have zero : 0*0=0bycompute304rw zero at uShape305arith306exact Less.positive_of_ne_zero at n with nNonzero307have nBelowM : n < m308have squares : n * n < m * m309rw [← uShape, ← vShape]310arith311exact IsSquare.root_lt at n, m with squares312have coprimeMN : gcd(m, n) =1313have coprimeVU := coprime.symmetric at u, a + u with coprimeUV314rw [vShape, uShape] at coprimeVU315have coprimeMNN := coprime.of_mul_left at m, m, n * n with coprimeVU316have coprimeNNM := coprime.symmetric at m, n * n with coprimeMNN317have coprimeNM := coprime.of_mul_left at n, n, m with coprimeNNM318exact coprime.symmetric at n, m with coprimeNM319have parityMN : Odd(m + n)320have shape : m * m = n * n + a321rw [← vShape, ← uShape]322ring323have parities := Even.root_sum_iff at m, n, a with shape324have notEvenA := Odd.not_even at a with oddA325have oddIff := Odd.iff_not_even at m + n326apply oddIff.backward327tauto328have aForm : a = m ^ 2 - n ^ 2329rw [pow.two, pow.two, ← vShape, ← uShape]330 symm331exact sub.add_right at a, u332have wForm : w = m * n333have squares : w * w = m * n * (m * n)334rw [← key, vShape, uShape]335ring336exact IsSquare.root_unique at w, m * n with squares337have bForm : b =2* m * n338rw [bShape, wForm]339ring340have cForm : c = m ^ 2+ n ^ 2341rw [pow.two, pow.two, ← vShape, ← uShape, cShape, tShape]342ring343exact⟨m, mNat, n, nNat, nPositive, nBelowM, coprimeMN, parityMN, aForm,344 bForm, cForm⟩345346-- Every Pythagorean triple is gcd(a, b) times a primitive one.347theorem pythagorean.form (a b c : Nat)348requires triple : Pythagorean(a, b, c)349shows∃ x, Nat(x) ∧∃ y, Nat(y) ∧∃ z, Nat(z) ∧350 PrimitivePythagorean(x, y, z) ∧351 a = gcd(a, b) * x ∧ b = gcd(a, b) * y ∧ c = gcd(a, b) * z352proof353obtain⟨positiveA, positiveB, equation⟩:= triple354simp [pow.two] at equation355have aNonzero : a ≠0byarith356have gcdNonzero := gcd.nonzero at a, b with aNonzero357obtain⟨x, xNat, aShape⟩:= gcd.divides_left at a, b358obtain⟨y, yNat, bShape⟩:= gcd.divides_right at a, b359have cofactors := gcd.cofactor_coprime at a, b, x, y360with gcdNonzero, aShape, bShape361have squareDividesC : gcd(a, b) * gcd(a, b) ∣ c * c362have dividesA := gcd.divides_left at a, b363have dividesB := gcd.divides_right at a, b364haveleft:= divides.mul_mono at gcd(a, b), a, gcd(a, b), a365with dividesA, dividesA366haveright:= divides.mul_mono at gcd(a, b), b, gcd(a, b), b367with dividesB, dividesB368rw← equation369exact divides.add at gcd(a, b) * gcd(a, b), a * a, b * b withleft, right370have dividesC := divides.of_mul_self at gcd(a, b), c with squareDividesC371obtain⟨z, zNat, cShape⟩:= dividesC372have reduced : x * x + y * y = z * z373have scaled : gcd(a, b) * gcd(a, b) * (x * x + y * y) =374 gcd(a, b) * gcd(a, b) * (z * z)375have leftSide : gcd(a, b) * gcd(a, b) * (x * x + y * y) = a * a + b * b376ring [aShape, bShape]377have rightSide : gcd(a, b) * gcd(a, b) * (z * z) = c * c byring [cShape]378rw [leftSide, rightSide]379exact equation380have squareNonzero := mul.nonzero at gcd(a, b), gcd(a, b)381with gcdNonzero, gcdNonzero382exact mul.left_cancel_positive at x * x + y * y, z * z,383 gcd(a, b) * gcd(a, b) with squareNonzero, scaled384have positiveX : 0 < x385have nonzero : x ≠0386intro zero387rw zero at aShape388have value : gcd(a, b) *0=0byring389rw value at aShape390arith391exact Less.positive_of_ne_zero at x with nonzero392have positiveY : 0 < y393have nonzero : y ≠0394intro zero395rw zero at bShape396have value : gcd(a, b) *0=0byring397rw value at bShape398arith399exact Less.positive_of_ne_zero at y with nonzero400have primitive : PrimitivePythagorean(x, y, z)401constructor402 . refine⟨positiveX, positiveY, _⟩403rw [pow.two, pow.two, pow.two]404exact reduced405 . exact cofactors406refine⟨x, xNat, y, yNat, z, zNat, primitive, _, _, _⟩407 . symm408exact aShape409 . symm410exact bShape411 . symm412exact cShape413