1-- The modular inverse of a unit mod a prime, chosen once: it is a unit, it2-- is an involution, and its only fixed points are 1 and p − 13-- Part of Cumulus, the Grace17 mathematics library.4-- Copyright (c) 2026 Grant Passmore.5-- Released under the Apache License 2.0. See LICENSE in the repository root.6-- Authors: Grant Passmore78module Grace.Cumulus.NumberTheory.InverseInvolution9import Euler1011defunique modInverse (p x : Nat) (b ∈ p) := mul(x, b) % p =112requires prime : Prime(p)13requires unit : gcd(x, p) =114proof15have aboveOne := Prime.greater_than_one at p with prime16obtain⟨b, bNat, bBelow, inverse⟩:= coprime.inverse at x, p with aboveOne, unit17witness b18constructor19 . exact membership.of_Less at b, p with*, *, bBelow20 . exact inverse2122uniqueness23intros u uMember first v vMember second24have nonzero := Prime.nonzero at p with prime25have uNat := natural.member at p with uMember26have vNat := natural.member at p with vMember27have uBelow := Less.of_membership at u, p28have vBelow := Less.of_membership at v, p29have same : (x * u) % p = (x * v) % p30rw [first, second]31rfl32have residues := coprime.mul_cancel_mod at x, p, u, v with nonzero, unit, same33have uSelf := mod.of_less at p, u with (uBelow with uMember)34have vSelf := mod.of_less at p, v with (vBelow with vMember)35rw [uSelf, vSelf] at residues36exact residues3738theorem modInverse.below (p x : Nat)39requires prime : Prime(p)40requires unit : gcd(x, p) =141shows modInverse(p, x) < p42proof43have selected := modInverse.selected with prime, unit44have bNat := natural.member at p with selected.left45have below := Less.of_membership at modInverse(p, x), p46exact below with selected.left4748theorem modInverse.natural (p x : Nat)49requires prime : Prime(p)50requires unit : gcd(x, p) =151showsNat(modInverse(p, x))52proof53have selected := modInverse.selected with prime, unit54exact natural.member at p with selected.left5556theorem modInverse.product (p x : Nat)57requires prime : Prime(p)58requires unit : gcd(x, p) =159shows mul(x, modInverse(p, x)) % p =160proof61have selected := modInverse.selected with prime, unit62exact selected.right6364theorem modInverse.unit (p x : Nat)65requires prime : Prime(p)66requires unit : gcd(x, p) =167shows gcd(modInverse(p, x), p) =168proof69have nonzero := Prime.nonzero with prime70have bNat := modInverse.natural with prime, unit71have product := modInverse.product with prime, unit72exact units.inverse_coprime with nonzero, product7374-- Inverses are unique among residues below p.75theorem modInverse.unique_below (p x b : Nat)76requires prime : Prime(p)77requires unit : gcd(x, p) =178requires below : b < p79requires product : (x * b) % p =180shows b = modInverse(p, x)81proof82have nonzero := Prime.nonzero with prime83have inverseNat := modInverse.natural with prime, unit84have inverseBelow := modInverse.below with prime, unit85have inverseProduct := modInverse.product with prime, unit86have same : (x * b) % p = (x * modInverse(p, x)) % p87rw [product, inverseProduct]88rfl89have residues := coprime.mul_cancel_mod with nonzero, unit, same90have bSelf := mod.of_less with below91have inverseSelf := mod.of_less with inverseBelow92 rwa [bSelf, inverseSelf] at residues9394theorem modInverse.involution (p x : Nat)95requires prime : Prime(p)96requires unit : gcd(x, p) =197requires below : x < p98shows modInverse(p, modInverse(p, x)) = x99proof100have inverseNat := modInverse.natural with prime, unit101have inverseUnit := modInverse.unit with prime, unit102have product := modInverse.product with prime, unit103have commute := mul.commutative at modInverse(p, x), x with*104rw commute at product105have flipped : mul(modInverse(p, x), x) % p =1byexact product106have same := modInverse.unique_below with prime, inverseUnit, below, flipped107 symm at same108exact same109110theorem modInverse.positive (p x : Nat)111requires prime : Prime(p)112requires unit : gcd(x, p) =1113shows0 < modInverse(p, x)114proof115have aboveOne := Prime.greater_than_one with prime116have inverseNat := modInverse.natural with prime, unit117have product := modInverse.product with prime, unit118have trichotomy := Less.trichotomy at0, modInverse(p, x)119rcases trichotomy with positive | zero | negative120 . exact positive121 . exfalso122rw← zero at product123have vanish : x *0=0byring124rw [vanish, mod.zero_left] at product125have impossible : ¬(0:N) =1byarith126exact impossible with product127 . exfalso128have impossible : ¬modInverse(p, x) < 0byarith129exact impossible with negative130131-- A residue below p is its own inverse only when it is 1 or p − 1.132theorem modInverse.fixed (p x : Nat)133requires prime : Prime(p)134requires positive : 0 < x135requires below : x < p136requires fixed : modInverse(p, x) = x137shows x =1∨ succ(x) = p138proof139have nonzero := Prime.nonzero with prime140have unit := prime.coprime_below with prime, positive, below141have product := modInverse.product with prime, unit142rw fixed at product143have shape := natural.zero_or_successor at x144cases shape with zero | successor145 . exfalso146rw zero at positive147exact Less.irreflexive with*, positive148 . obtain⟨y, yNat, form⟩:= successor149obtain⟨q, qNat, equation⟩:= mod.equation at x * x, p with nonzero150rw [product, form] at equation151have expand : succ(y) * succ(y) = y * (y +2) +1byring152rw expand at equation153have cancel := add.right_cancel at y * (y +2), p * q, 1154have reduced := cancel with equation155have dividesProduct : p ∣ y * (y +2)156witness q157rw reduced158rfl159havesplit:= Prime.divides_mul with prime, dividesProduct160casessplitwith dividesY | dividesNext161 . left162have yShape := natural.zero_or_successor at y163cases yShape with yZero | ySucc164 . rw [form, yZero]165rfl166 . exfalso167have yNonzero : y ≠0168intro zero169obtain⟨z, zNat, yForm⟩:= ySucc170rw yForm at zero171have impossible : succ(z) ≠0byarith172exact impossible with zero173have le := divides.le with dividesY, yNonzero174rw form at below175have impossible : ¬p ≤ y byarith176exact impossible with le177 . right178have nextNonzero : y +2≠0byarith179have le := divides.le with dividesNext, nextNonzero180rw form at below181have upper : y +2≤ p182haveunfold : y +2= succ(succ(y)) byring183rwunfold184exact below185have same := LessEqual.antisymmetric with upper, le186rw form187haveunfold : succ(succ(y)) = y +2byring188 rwa unfold189