1-- The standard metric on the Dedekind reals2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.Real.Metric8import AbsoluteValue9import Natural1011-- The field-theoretic half of a real. Its laws come from the generic12-- ordered-field development rather than the Dedekind-cut construction.13def half (A : Real) : Real:=14 fieldHalf(realAdd, realMul, realOne, realInv, A)1516theorem half.real (A : Real)17showsReal(half(A))18proof19unfold half20unfoldReal21have realA : A ∈ ℝ bysimp [Real]22exact IsOrderedField.half_closed with reals.ordered_field, realA2324theorem half.positive (A : Real)25requires positive : 0 < A26shows0 < half(A)27proof28unfold half29exact IsOrderedField.half_positive with reals.ordered_field, positive3031theorem half.add_self (A : Real)32shows half(A) + half(A) = A33proof34have realA : A ∈ ℝ bysimp [Real]35have generic := IsOrderedField.half_add_self with reals.ordered_field, realA36unfold half37exact generic3839theorem half.lt_self (A : Real)40requires positive : 0 < A41shows half(A) < A42proof43have halfReal := half.real at A44have halfPositive := half.positive with positive45have step := realLess.add_left with realZero.real, halfReal, halfReal, halfPositive46have zero := realAddValue.zero_right with halfReal47have doubled := half.add_self at A48 rwa [zero, doubled] at step4950def dist (A B : Real) : Real:= abs(A - B)5152theorem dist.real (A B : Real)53showsReal(dist(A, B))54proof55unfold dist56have differenceReal := realSubValue.real at A, B with*57exact abs.real with differenceReal5859theorem dist.nonnegative (A B : Real)60shows0≤ dist(A, B)61proof62unfold dist63have differenceReal := realSubValue.real at A, B with*64exact abs.nonnegative with differenceReal6566theorem dist.self (A : Real)67shows dist(A, A) =068proof69unfold dist70have selfDifference := realSubValue.self at A71rw selfDifference72exact abs.zero7374theorem realSubValue.eq_zero_iff (A B : Real)75shows A - B =0↔ A = B76proof77constructor78 . intro differenceZero79have cancelled := realSubValue.add_right_cancel at A, B with80*{2}81have zeroLeft := realAddValue.zero_left at B82rw [differenceZero, zeroLeft] at cancelled83rw cancelled84rfl85 . intro equal86rw equal87exact realSubValue.self withassumption8889theorem dist.eq_zero_iff (A B : Real)90shows dist(A, B) =0↔ A = B91proof92constructor93 . intro zeroDistance94unfold dist at zeroDistance95have zeroDifference := abs.eq_zero_iff.forward with zeroDistance96exact realSubValue.eq_zero_iff.forward with zeroDifference97 . intro equal98unfold dist99rw equal100have selfDifference := realSubValue.self at B101rw selfDifference102exact abs.zero103104theorem dist.positive_of_ne (A B : Real)105requires unequal : A ≠ B106shows0 < dist(A, B)107proof108unfold dist109have differenceReal := realSubValue.real at A, B with110*{2}111apply abs.positive_of_ne with differenceReal112intro differenceZero113apply unequal114exact realSubValue.eq_zero_iff.forward with differenceZero115116theorem dist.symmetric (A B : Real)117shows dist(A, B) = dist(B, A)118proof119have differenceReal := realSubValue.real at A, B with*120have swapped := realSubValue.neg_swap at A, B with*121have absoluteNeg := abs.neg with differenceReal122simp [dist]123124theorem dist.triangle (A B C : Real)125shows dist(A, C) ≤ dist(A, B) + dist(B, C)126proof127have firstDifferenceReal := realSubValue.real at A, B with128*{2}129have secondDifferenceReal := realSubValue.real at B, C with130*{2}131have path : A - C = (A - B) + (B - C)132unfold realSubValue133have negBReal := realNegValue.real at B134have negCReal := realNegValue.real at C135have secondReal := realAddValue.real at B, -C withassumption, negCReal136have outerAssoc := realAddValue.associative at A, -B, B + (-C) with137assumption, negBReal, secondReal138have innerAssoc := realAddValue.associative at -B, B, -C with139 negBReal, assumption, negCReal140have inverse := realAddValue.neg_left at B141have identity := realAddValue.zero_left with negCReal142cc143have triangle := abs.triangle with firstDifferenceReal, secondDifferenceReal144unfold dist145 rwa path146147def openBall (C E : Real) : Set:=148 { X ∈ ℝ | dist(X, C) < E }149150theorem openBall.center (C E : Real)151requires positive : 0 < E152shows C ∈ openBall(C, E)153proof154have typedC : Real(C) bytypecheck155have realC : C ∈ ℝ bysimp [Real]156have selfDistance := dist.self with typedC157apply openBall.membership.backward158constructor159 . exact realC160 . rw selfDistance161exact positive162163theorem openBall.symmetric (X C E : Real)164shows X ∈ openBall(C, E) ↔ C ∈ openBall(X, E)165proof166have symmetry := dist.symmetric at X, C with*167have realX : X ∈ ℝ bysimp [Real]168have realC : C ∈ ℝ bysimp [Real]169constructor170 . intro member171have data := openBall.membership.forward with member172rcases data with⟨realX, within⟩173apply openBall.membership.backward174constructor175 . exact realC176 . rw← symmetry177exact within178 . intro member179have data := openBall.membership.forward with member180rcases data with⟨realC, within⟩181apply openBall.membership.backward182constructor183 . exact realX184 . rw symmetry185exact within186187theorem openBall.mono (X C E D : Real)188requires member : X ∈ openBall(C, E)189requires radii : E ≤ D190shows X ∈ openBall(C, D)191proof192obtain⟨realX, within⟩:= openBall.membership.forward with member193have typedX : Real(X) byexact realX194have distanceReal := dist.real at X, C with typedX, assumption195apply openBall.membership.backward196constructor197 . exact realX198 . exact realLess.transitive_le at dist(X, C), E, D with199 distanceReal, *{2}, within, radii200201-- Metric balls compose additively: an E-step followed by a D-step remains202-- inside the ball of radius E + D.203theorem openBall.triangle (X Y Z E D : Real)204requires xy : X ∈ openBall(Y, E)205requires yz : Y ∈ openBall(Z, D)206shows X ∈ openBall(Z, E + D)207proof208have xyData := openBall.membership.forward with xy209have yzData := openBall.membership.forward with yz210rcases xyData with⟨realX, xyLess⟩211rcases yzData with⟨realY, yzLess⟩212have typedX : Real(X) byexact realX213have typedY : Real(Y) byexact realY214have xyReal := dist.real with typedX, typedY215have yzReal := dist.real at Y, Z with typedY, assumption216have xzReal := dist.real at X, Z with typedX, assumption217have sumDistancesReal := realAddValue.real with xyReal, yzReal218have radiusReal := realAddValue.real at E, D with*219have firstLess := realLess.add_right at220 dist(X, Y), E, dist(Y, Z) with221 xyReal, assumption, yzReal, xyLess222have secondLess := realLess.add_left at223 dist(Y, Z), D, E with224 yzReal, *{2}, yzLess225have sumLess := realLess.transitive with firstLess, secondLess226have triangle := dist.triangle at X, Y, Z with typedX, typedY, assumption227apply openBall.membership.backward228constructor229 . exact realX230 . exact realLe.transitive_less at231 dist(X, Z), dist(X, Y) + dist(Y, Z), E + D with232 xzReal, sumDistancesReal,233 radiusReal, triangle, sumLess234235-- Two balls whose common radius is half the distance between their centers236-- cannot share a point unless the centers coincide.237theorem openBall.half_radius_unique (X A B : Real)238requires nearA : X ∈ openBall(A, half(dist(A, B)))239requires nearB : X ∈ openBall(B, half(dist(A, B)))240shows A = B241proof242have distanceReal := dist.real at A, B with*243have radiusReal := half.real with distanceReal244have aNearX := openBall.symmetric.forward at245 X, A, half(dist(A, B)) with246*{2}, radiusReal, nearA247have path := openBall.triangle at248 A, X, B, half(dist(A, B)), half(dist(A, B)) with249*{3}, radiusReal, radiusReal,250 aNearX, nearB251have doubled := half.add_self with distanceReal252rw doubled at path253have pathData := openBall.membership.forward at254 B, dist(A, B), A with path255rcases pathData with⟨realA, impossible⟩256exfalso257exact IsOrderedField.irreflexive with reals.ordered_field, impossible258259theorem dist.add_right (A B C : Real)260shows dist(A + C, B + C) = dist(A, B)261proof262have identity : (A + C) - (B + C) = A - B263unfold realSubValue264have negSum := realNegValue.add at B, C with*265rw negSum266have negBReal := realNegValue.real at B267have negCReal := realNegValue.real at C268have shuffle := realAddValue.four_shuffle at A, C, -B, -C with269*{2}, negBReal, negCReal270have inverse := realAddValue.neg_right at C271have differenceReal := realAddValue.real at A, -B with272assumption, negBReal273have zero := realAddValue.zero_right with differenceReal274cc275simp [dist]276277theorem dist.add_left (A B C : Real)278shows dist(C + A, C + B) = dist(A, B)279proof280have leftCommutative := realAddValue.commutative at C, A with281*{2}282have rightCommutative := realAddValue.commutative at C, B with283*{2}284rw [leftCommutative, rightCommutative]285exact dist.add_right with*286287theorem dist.neg (A B : Real)288shows dist(-A, -B) = dist(A, B)289proof290have differenceReal := realSubValue.real at A, B with*291have identity : (-A) - (-B) = -(A - B)292unfold realSubValue293have negBReal := realNegValue.real at B294have leftInvolutive := realNegValue.involutive at B295have rightDistribution := realNegValue.add at A, -B with296assumption, negBReal297cc298unfold dist299rw identity300exact abs.neg with differenceReal301302-- Pointwise addition is Lipschitz in both arguments.303theorem dist.add_add (A B C D : Real)304shows dist(A + B, C + D) ≤ dist(A, C) + dist(B, D)305proof306have firstDifferenceReal := realSubValue.real at A, C with307*{2}308have secondDifferenceReal := realSubValue.real at B, D with309*{2}310have identity : (A + B) - (C + D) = (A - C) + (B - D)311unfold realSubValue312have negSum := realNegValue.add at C, D with*313have negCReal := realNegValue.real at C314have negDReal := realNegValue.real at D315rw negSum316exact realAddValue.four_shuffle with*{2}, negCReal, negDReal317have triangle := abs.triangle with firstDifferenceReal, secondDifferenceReal318unfold dist319 rwa identity320321-- Adding points in two balls adds their radii.322theorem openBall.add (A B C D E F : Real)323requires nearA : A ∈ openBall(C, E)324requires nearB : B ∈ openBall(D, F)325shows A + B ∈ openBall(C + D, E + F)326proof327have dataA := openBall.membership.forward with nearA328have dataB := openBall.membership.forward with nearB329rcases dataA with⟨realA, closeA⟩330rcases dataB with⟨realB, closeB⟩331have distanceACReal := dist.real at A, C with*332have distanceBDReal := dist.real at B, D with*333have sumDistancesReal := realAddValue.real with distanceACReal, distanceBDReal334have radiusReal := realAddValue.real at E, F with*335have sumReal := realAddValue.real at A, B with*336have centerReal := realAddValue.real at C, D with*337have totalDistanceReal := dist.real with sumReal, centerReal338have distanceBound := dist.add_add at A, B, C, D with339*{4}340have radiiBound := realLess.add at341 dist(A, C), E, dist(B, D), F with342 distanceACReal, assumption, distanceBDReal, assumption,343 closeA, closeB344have within := realLe.transitive_less with345 totalDistanceReal, sumDistancesReal, radiusReal, distanceBound, radiiBound346apply openBall.membership.backward347constructor348exact sumReal349 . exact within350351-- A point near B has absolute value at most its distance from B plus |B|.352-- This is the standard local boundedness estimate used in limit proofs.353theorem abs.le_dist_add_abs (A B : Real)354shows abs(A) ≤ dist(A, B) + abs(B)355proof356have differenceReal := realSubValue.real at A, B with*357have cancellation := realSubValue.add_right_cancel at A, B with358*{2}359have triangle := abs.triangle at A - B, B with differenceReal, assumption360unfold dist361 rwa cancellation at triangle362363-- The familiar reciprocal-difference identity, stated in the orientation364-- used by epsilon estimates.365theorem realSubValue.inv_inv (A B : Real)366requires nonzeroA : A ≠0367requires nonzeroB : B ≠0368shows realInvValue(A) - realInvValue(B) =369 (B - A) * (realInvValue(A) * realInvValue(B))370proof371have inverseAReal := realInvValue.real at A372have inverseBReal := realInvValue.real at B373have negAReal := realNegValue.real at A374have inverseProductReal := realMulValue.real with inverseAReal, inverseBReal375have inverseA := realInvValue.mul_right with nonzeroA376have inverseB := realInvValue.mul_right with nonzeroB377have distribute := realMulValue.distributive_right at378 B, -A, realInvValue(A) * realInvValue(B) with379assumption, negAReal, inverseProductReal380have negProduct := realMulValue.neg_left at381 A, realInvValue(A) * realInvValue(B) with382assumption, inverseProductReal383have commuteBInvA := realMulValue.commutative at B, realInvValue(A) with384assumption, inverseAReal385have assocB := realMulValue.associative at386 B, realInvValue(A), realInvValue(B) with387assumption, inverseAReal, inverseBReal388have assocInvAB := realMulValue.associative at389 realInvValue(A), B, realInvValue(B) with390 inverseAReal, assumption, inverseBReal391have assocA := realMulValue.associative at392 A, realInvValue(A), realInvValue(B) with393assumption, inverseAReal, inverseBReal394have oneLeftInvA := realMulValue.one_left with inverseAReal395have oneLeftInvB := realMulValue.one_left with inverseBReal396have oneRightInvA := realMulValue.one_right with inverseAReal397unfold realSubValue398calc399 realInvValue(A) + -realInvValue(B) =400 realInvValue(A) *1+ -(1* realInvValue(B)) :=bycc401 _ = realInvValue(A) * (B * realInvValue(B)) +402 -((A * realInvValue(A)) * realInvValue(B)) :=bycc403 _ = B * (realInvValue(A) * realInvValue(B)) +404 -(A * (realInvValue(A) * realInvValue(B))) :=bycc405 _ = (B + -A) * (realInvValue(A) * realInvValue(B)) :=bycc406407theorem dist.inv_inv (A B : Real)408requires nonzeroA : A ≠0409requires nonzeroB : B ≠0410shows dist(realInvValue(A), realInvValue(B)) =411 dist(A, B) * (abs(realInvValue(A)) * abs(realInvValue(B)))412proof413have inverseAReal := realInvValue.real at A414have inverseBReal := realInvValue.real at B415have difference := realSubValue.inv_inv with nonzeroA, nonzeroB416have baReal := realSubValue.real at B, A with*417have inverseProductReal := realMulValue.real with inverseAReal, inverseBReal418have outerAbs := abs.mul with baReal, inverseProductReal419have innerAbs := abs.mul with inverseAReal, inverseBReal420have symmetric := dist.symmetric at B, A with*421unfold dist422unfold dist at symmetric423rw [difference, outerAbs, innerAbs]424have absSymmetric : abs(B - A) = abs(A - B) byexact symmetric425calc426 abs(B - A) * (abs(realInvValue(A)) * abs(realInvValue(B))) =427 abs(A - B) * (abs(realInvValue(A)) * abs(realInvValue(B))) :=428bycc429430-- If A lies within half of |B| from a nonzero B, then A remains uniformly431-- away from zero. This is the standard local lower bound for reciprocals.432theorem abs.half_lt_of_dist_lt_half (A B : Real)433requires nonzeroB : B ≠0434requires close : dist(A, B) < half(abs(B))435shows half(abs(B)) < abs(A)436proof437have absAReal := abs.real at A438have absBReal := abs.real at B439have halfAbsBReal := half.real with absBReal440have distReal := dist.real at A, B with*441have absBPositive := abs.positive_of_ne with nonzeroB442have halfPositive := half.positive with absBPositive443have cutHalf :=Real.characterization.forward with halfAbsBReal444have cutAbsA :=Real.characterization.forward with absAReal445have notAbsBelow : ¬(abs(A) ⊆ half(abs(B)))446intro absABelow447have absALeHalf := realLe.of_subset with absABelow448have first := realLess.add_right with distReal, halfAbsBReal, absAReal, close449have second := realLe.add_left with absAReal, halfAbsBReal, halfAbsBReal, absALeHalf450have sumBelow := realLess.transitive_le with first, second451have doubled := half.add_self with absBReal452rw doubled at sumBelow453have triangle := abs.le_dist_add_abs at B, A with454*{2}455have symmetric := dist.symmetric at B, A with*456rw symmetric at triangle457have cycle := realLe.transitive_less with triangle, sumBelow458have irreflexive := realLess.irreflexive_on459exact irreflexive with abs(B), cycle460have comparable := DedekindCut.comparable with cutHalf, cutAbsA461cases comparable with halfBelow | absABelow462 . apply realLess.characterization.backward at half(abs(B)), abs(A)463unfold RealLess464refine⟨halfAbsBReal, absAReal, _⟩465constructor466 . exact halfBelow467 . intro equal468apply notAbsBelow469rw← equal470exact Subset.reflexive471 . exfalso472exact notAbsBelow with absABelow473474-- The difference of two products, arranged in the form most useful for475-- analysis: AB - CD = A(B - D) + D(A - C).476theorem realSubValue.mul_mul (A B C D : Real)477shows A * B - C * D = A * (B - D) + D * (A - C)478proof479have negDReal := realNegValue.real at D480have negCReal := realNegValue.real at C481have abReal := realMulValue.real at A, B with*482have adReal := realMulValue.real at A, D with*483have cdReal := realMulValue.real at C, D with*484have negADReal := realNegValue.real with adReal485have negCDReal := realNegValue.real with cdReal486have adPlusNegCDReal := realAddValue.real with adReal, negCDReal487have distributeA := realMulValue.distributive at A, B, -D with488*{2}, negDReal489have distributeD := realMulValue.distributive at D, A, -C with490*{2}, negCReal491have negateAD := realMulValue.neg_right at A, D with*492have negateDC := realMulValue.neg_right at D, C with*493have commuteDA := realMulValue.commutative at D, A with*494have commuteDC := realMulValue.commutative at D, C with*495have cancelAD := realAddValue.neg_left with adReal496have zeroNegCD := realAddValue.zero_left with negCDReal497have middleAssoc := realAddValue.associative with negADReal, adReal, negCDReal498have outerAssoc := realAddValue.associative with abReal, negADReal, adPlusNegCDReal499unfold realSubValue500calc501 A * B + -(C * D) = A * B + (0+ -(C * D)) :=bycc502 _ = A * B + ((-(A * D) + A * D) + -(C * D)) :=bycc503 _ = A * B + (-(A * D) + (A * D + -(C * D))) :=bycc504 _ = A * (B + -D) + D * (A + -C) :=bycc505506-- Multiplication is locally Lipschitz in the familiar two-term form.507theorem dist.mul_mul (A B C D : Real)508shows dist(A * B, C * D) ≤509 abs(A) * dist(B, D) + abs(D) * dist(A, C)510proof511have bdReal := realSubValue.real at B, D with*512have acReal := realSubValue.real at A, C with*513have firstReal := realMulValue.real at A, B - D withassumption, bdReal514have secondReal := realMulValue.real at D, A - C withassumption, acReal515have decomposition := realSubValue.mul_mul at A, B, C, D with516*{4}517have triangle := abs.triangle with firstReal, secondReal518have firstAbsolute := abs.mul at A, B - D withassumption, bdReal519have secondAbsolute := abs.mul at D, A - C withassumption, acReal520unfold dist521rw decomposition522 rwa [firstAbsolute, secondAbsolute] at triangle523524-- Cancel doubling without introducing a numeral-dependent arithmetic rule.525theorem realAddValue.double_cancel (A B : Real)526requires doubled : A + A = B + B527shows A = B528proof529have halfDouble := half.add_self with realOne.real530linear_combination half(realOne) * doubled - (A - B) * halfDouble531532-- Keep the small scalar calculation reusable in vector and geometry proofs.533theorem realMulValue.square_of_double (A B : Real)534requires doubled : A + A = B535shows B * B = A * A + A * A + A * A + A * A536proof537rw← doubled538ring539540theorem realAddValue.two_nonzero541shows (2:R) ≠0542proof543intro zero544have onePositive := realZero.lt_one545linarith546547-- Recover a value from its doubled equation without expanding its expression.548theorem realDiv.of_double_eq (A B : Real)549requires doubled : A + A = B550shows A = B / (2:R)551proof552have two : Real((2:R)) bytypecheck553have quotient := realDiv.real at B, (2:R) with*554have cancel := realDiv.mul_left at B, (2:R) with555assumption, two, realAddValue.two_nonzero556apply realAddValue.double_cancel withassumption, quotient557linear_combination doubled - cancel558559-- Halves as quotients by two.560theorem realHalf.add_self561shows (1:R) / 2+1 / 2=1562proof563have twoNonzero : (2:R) ≠0564intro zero565linarith566have oneReal : (1:R) ∈ ℝ bytypecheck567have twoReal : (2:R) ∈ ℝ bytypecheck568have halfReal := realDiv.real at (1:R), (2:R) with*569have half := realDiv.mul_right at (1:R), (2:R) with twoNonzero570have twice : (1:R) / 2+1 / 2=1 / 2*2byring571rw [twice, half]572rfl573574theorem realDiv.lt_half (A B : Real)575requires positive : 0 < B576requires below : 2* A < B577shows A / B < 1 / 2578proof579have oneReal : (1:R) ∈ ℝ bytypecheck580have twoReal : (2:R) ∈ ℝ bytypecheck581have halfReal := realDiv.real at (1:R), (2:R) with*582have twice := realHalf.add_self583havesplit : 1 / 2* B +1 / 2* B = (1 / 2+1 / 2) * B byring584rw twice atsplit585have unit : (1:R) * B = B byring586have halfBelow : A < 1 / 2* B bylinarith587exact realDiv.lt_of_lt_mul with positive, halfBelow588589-- Division by three and cancellation of tripled real values.590theorem realAddValue.three_nonzero591shows (3:R) ≠0592proof593intro zero594have onePositive := realZero.lt_one595linarith596597def third (A : Real) : Real:= A / (3:R)598599theorem third.real (A : Real)600showsReal(third(A))601proof602unfold third603typecheck604605theorem third.add_self (A : Real)606shows third(A) + third(A) + third(A) = A607proof608have cancel := realDiv.mul_left with realAddValue.three_nonzero609unfold third610linear_combination cancel611612theorem realAddValue.triple_cancel (A B : Real)613requires tripled : A + A + A = B + B + B614shows A = B615proof616have thirdTriple := third.add_self at realOne with*617linear_combination third(realOne) * tripled - (A - B) * thirdTriple618