1-- Perfect squares and sums of two squares: squares are closed under2-- products, a square is 0 or 1 modulo 3 and modulo 4, an odd square is 13-- modulo 8, there is no square strictly between consecutive squares, and the4-- product of two sums of two squares is a sum of two squares5-- Part of Cumulus, the Grace17 mathematics library.6-- Copyright (c) 2026 Grant Passmore.7-- Released under the Apache License 2.0. See LICENSE in the repository root.8-- Authors: Grant Passmore910module Grace.Cumulus.NumberTheory.PerfectSquares11import ModularMore1213predicate IsSquare (n : Nat) :=∃ k, Nat(k) ∧ n = k * k1415theorem IsSquare.intro (k : Nat)16shows IsSquare(k * k)17proof18witness k19rfl2021theorem IsSquare.zero22shows IsSquare(0)23proof24witness025ring2627theorem IsSquare.one28shows IsSquare(1)29proof30witness131ring3233theorem IsSquare.pow_two (k : Nat)34shows IsSquare(k ^ 2)35proof36have square := pow.two at k37rw square38exact IsSquare.intro at k3940theorem IsSquare.pow_double (a k : Nat)41shows IsSquare(a ^ (2* k))42proof43have double := pow.double_exponent at a, k44rw double45exact IsSquare.pow_two at a ^ k4647theorem IsSquare.mul (m n : Nat)48requires first : IsSquare(m)49requires second : IsSquare(n)50shows IsSquare(m * n)51proof52obtain⟨a, aNat, aShape⟩:= first53obtain⟨b, bNat, bShape⟩:= second54witness a * b55rw [aShape, bShape]56ring5758theorem IsSquare.mod_four (n : Nat)59requires square : IsSquare(n)60shows n %4=0∨ n %4=161proof62obtain⟨k, kNat, shape⟩:= square63rw shape64have four : 4≠0byarith65have reduce := mod.mul at4, k, k with four66rw reduce67have alternatives := mod.four_cases at k68rcases alternatives with zero | one | two | three69 . left70rw zero71compute72 . right73rw one74compute75 . left76rw two77compute78 . right79rw three80compute8182theorem IsSquare.mod_three (n : Nat)83requires square : IsSquare(n)84shows n %3=0∨ n %3=185proof86obtain⟨k, kNat, shape⟩:= square87rw shape88have three : 3≠0byarith89have reduce := mod.mul at3, k, k with three90rw reduce91have alternatives := mod.three_cases at k92rcases alternatives with zero | one | two93 . left94rw zero95compute96 . right97rw one98compute99 . right100rw two101compute102103theorem IsSquare.mod_three_ne_two (n : Nat)104requires square : IsSquare(n)105shows n %3≠2106proof107have alternatives := IsSquare.mod_three with square108intro two109rw two at alternatives110cases alternatives with zero | one111 . arith112 . arith113114theorem IsSquare.mod_four_ne_two (n : Nat)115requires square : IsSquare(n)116shows n %4≠2117proof118have alternatives := IsSquare.mod_four with square119intro two120rw two at alternatives121cases alternatives with zero | one122 . arith123 . arith124125theorem IsSquare.mod_four_ne_three (n : Nat)126requires square : IsSquare(n)127shows n %4≠3128proof129have alternatives := IsSquare.mod_four with square130intro three131rw three at alternatives132cases alternatives with zero | one133 . arith134 . arith135136theorem Odd.square_mod_eight (k : Nat)137requires odd : Odd(k)138shows (k * k) %8=1139proof140obtain⟨j, jNat, shape⟩:= Odd.iff_two_mul_add_one.forward with odd141have evenProduct : Even(j * (j +1))142have parity := parity.exhaustive at j143cases parity with even | oddJ144 . exact Even.mul_left at j, j +1with even145 . have succEven := Even.succ_of_odd at j with oddJ146have one := add.right_one at j147rw one148exact Even.mul_right at j, succ(j) with succEven149obtain⟨m, mNat, mShape⟩:= Even.iff_two_mul.forward with evenProduct150have expand : k * k =8* m +1151rw shape152ring [mShape]153rw expand154have eight : 8≠0byarith155have reduce := mod.multiple_add at8, 1, m with eight156rw reduce157compute158159theorem IsSquare.odd_mod_eight (n : Nat)160requires square : IsSquare(n)161requires odd : Odd(n)162shows n %8=1163proof164obtain⟨k, kNat, shape⟩:= square165rw shape at odd166have both := Odd.of_mul at k, k with odd167rw shape168exact Odd.square_mod_eight with both.left169170theorem IsSquare.even_iff (n : Nat)171requires square : IsSquare(n)172shows Even(n) ↔∃ k, Nat(k) ∧ Even(k) ∧ n = k * k173proof174obtain⟨k, kNat, shape⟩:= square175constructor176 . intro even177rw shape at even178have characterization := Even.mul_self_iff at k179have kEven := characterization.forward with even180exact⟨k, kNat, kEven, shape⟩181 . intro witnessed182obtain⟨j, jNat, jEven, jShape⟩:= witnessed183rw jShape184exact Even.mul_left at j, j with jEven185186-- Strictly increasing squares: a square below another has the smaller root.187theorem IsSquare.root_lt (k m : Nat)188requires below : k * k < m * m189shows k < m190proof191by_contra notLess192have above := LessEqual.of_not_Less with notLess193have squares := LessEqual.mul at m, k, m, k with above, above194have cycle := Less.transitive_LessEqual with below, squares195exact Less.irreflexive at k * k with cycle196197theorem IsSquare.not_between (k n : Nat)198requires lower : k * k < n199requires upper : n < (k +1) * (k +1)200shows¬IsSquare(n)201proof202intro square203obtain⟨m, mNat, shape⟩:= square204rw shape at lower205rw shape at upper206have first := IsSquare.root_lt at k, m with lower207have second := IsSquare.root_lt at m, k +1with upper208arith209210theorem IsSquare.not_two211shows¬IsSquare(2)212proof213have lower : 1*1 < 2byarith214have square : (1+1) * (1+1) =2*2bycompute215have upper : 2 < (1+1) * (1+1)216rw square217arith218exact IsSquare.not_between at1, 2with lower, upper219220theorem IsSquare.not_three221shows¬IsSquare(3)222proof223have lower : 1*1 < 3byarith224have square : (1+1) * (1+1) =2*2bycompute225have upper : 3 < (1+1) * (1+1)226rw square227arith228exact IsSquare.not_between at1, 3with lower, upper229230theorem IsSquare.not_five231shows¬IsSquare(5)232proof233have lower : 2*2 < 5byarith234have square : (2+1) * (2+1) =3*3bycompute235have upper : 5 < (2+1) * (2+1)236rw square237arith238exact IsSquare.not_between at2, 5with lower, upper239240predicate SumOfTwoSquares (n : Nat) :=241∃ a, Nat(a) ∧∃ b, Nat(b) ∧ n = a * a + b * b242243theorem SumOfTwoSquares.intro (a b : Nat)244shows SumOfTwoSquares(a * a + b * b)245proof246refine⟨a, assumption, b, assumption, _⟩247rfl248249theorem SumOfTwoSquares.of_square (n : Nat)250requires square : IsSquare(n)251shows SumOfTwoSquares(n)252proof253obtain⟨k, kNat, shape⟩:= square254have zero : k * k = k * k +0*0byring255rw [shape, zero]256exact SumOfTwoSquares.intro at k, 0257258theorem SumOfTwoSquares.zero259shows SumOfTwoSquares(0)260proof261have square := IsSquare.zero262exact SumOfTwoSquares.of_square with square263264theorem SumOfTwoSquares.one265shows SumOfTwoSquares(1)266proof267have square := IsSquare.one268exact SumOfTwoSquares.of_square with square269270theorem SumOfTwoSquares.two271shows SumOfTwoSquares(2)272proof273have shape : 2=1*1+1*1bycompute274rw shape275exact SumOfTwoSquares.intro at1, 1276277-- The Brahmagupta–Fibonacci identity, with the signed cross term replaced278-- by the distance between the two cross products.279theorem SumOfTwoSquares.product_identity (a b c d : Nat)280shows281 (a * a + b * b) * (c * c + d * d) =282 (a * c + b * d) * (a * c + b * d) +283 natDist(a * d, b * c) * natDist(a * d, b * c)284proof285have total := LessEqual.total at b * c, a * d286cases total with below | above287 . obtain⟨e, eNat, split⟩:= LessEqual.exists_add with below288have characterization := natDist.eq_iff at a * d, b * c, e289have distance : natDist(a * d, b * c) = e290apply characterization.backward291right292exactsplit293rw distance294ring [split]295 . obtain⟨e, eNat, split⟩:= LessEqual.exists_add with above296have characterization := natDist.eq_iff at a * d, b * c, e297have distance : natDist(a * d, b * c) = e298apply characterization.backward299left300exactsplit301rw distance302ring [split]303304theorem SumOfTwoSquares.mul (m n : Nat)305requires first : SumOfTwoSquares(m)306requires second : SumOfTwoSquares(n)307shows SumOfTwoSquares(m * n)308proof309obtain⟨a, aNat, b, bNat, mShape⟩:= first310obtain⟨c, cNat, d, dNat, nShape⟩:= second311have identity := SumOfTwoSquares.product_identity at a, b, c, d312rw [mShape, nShape, identity]313exact SumOfTwoSquares.intro at a * c + b * d, natDist(a * d, b * c)314315theorem SumOfTwoSquares.mod_four (n : Nat)316requires sum : SumOfTwoSquares(n)317shows n %4≠3318proof319obtain⟨a, aNat, b, bNat, shape⟩:= sum320intro three321rw shape at three322have four : 4≠0byarith323have reduce := mod.add at4, a * a, b * b with four324rw reduce at three325have squareA := IsSquare.intro at a326have squareB := IsSquare.intro at b327have leftCases := IsSquare.mod_four with squareA328have rightCases := IsSquare.mod_four with squareB329cases leftCases with leftZero | leftOne330 . rw leftZero at three331cases rightCases with rightZero | rightOne332 . rw rightZero at three333have value : (0+0) %4=0bycompute334rw value at three335arith336 . rw rightOne at three337have value : (0+1) %4=1bycompute338rw value at three339arith340 . rw leftOne at three341cases rightCases with rightZero | rightOne342 . rw rightZero at three343have value : (1+0) %4=1bycompute344rw value at three345arith346 . rw rightOne at three347have value : (1+1) %4=2bycompute348rw value at three349arith350