1-- Euclidean distance in the plane and the triangle inequality.2-- 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.Geometry.Distance8import Plane9import Real.Sqrt1011namespace Euclidean12exportopen Euclidean1314-- Section: Cauchy-Schwarz in the plane1516-- The difference of the two sides is the square of a cross term.17theorem angleDot.cauchy_schwarz (a b c : Point)18shows angleDot(a, b, c) * angleDot(a, b, c) ≤ Euclidean.distanceSquared(b, a) * Euclidean.distanceSquared(b, c)19proof20have dotRule := angleDot.coordinates at a, b, c with*21have firstRule := Euclidean.distanceSquared.coordinates at b, a with*22have secondRule := Euclidean.distanceSquared.coordinates at b, c with*23have crossNonnegative := realMulValue.square_nonnegative at (x(a) - x(b)) * (y(c) - y(b)) - (y(a) - y(b)) * (x(c) - x(b)) with*24have difference : Euclidean.distanceSquared(b, a) * Euclidean.distanceSquared(b, c) - angleDot(a, b, c) * angleDot(a, b, c) =25 ((x(a) - x(b)) * (y(c) - y(b)) - (y(a) - y(b)) * (x(c) - x(b))) * ((x(a) - x(b)) * (y(c) - y(b)) - (y(a) - y(b)) * (x(c) - x(b)))26rw [dotRule, firstRule, secondRule]27ring28have nonnegativeDifference : 0≤ Euclidean.distanceSquared(b, a) * Euclidean.distanceSquared(b, c) - angleDot(a, b, c) * angleDot(a, b, c)29 rwa difference30exact realLe.of_nonnegative_difference with*3132-- Section: Distance3334theorem Euclidean.distanceSquared.nonnegative_real (a b : Point)35shows NonnegativeReal(Euclidean.distanceSquared(a, b))36proof37have le := Euclidean.distanceSquared.nonnegative at a, b with*38exact NonnegativeReal.of_le with*3940def distance (a b : Point) : Real:= sqrt(Euclidean.distanceSquared(a, b))4142theorem distance.real (a b : Point)43showsReal(distance(a, b))44proof45typecheck4647theorem distance.nonnegative (a b : Point)48shows0≤ distance(a, b)49proof50have nonnegative := Euclidean.distanceSquared.nonnegative_real at a, b with*51have root := sqrt.nonnegative with nonnegative52unfold distance53exact root5455theorem distance.square (a b : Point)56shows distance(a, b) * distance(a, b) = Euclidean.distanceSquared(a, b)57proof58have nonnegative := Euclidean.distanceSquared.nonnegative_real at a, b with*59have square := sqrt.square with nonnegative60unfold distance61cc6263theorem distance.symmetric (a b : Point)64shows distance(a, b) = distance(b, a)65proof66have swap := Euclidean.distanceSquared.symmetric at a, b with*67unfold distance68cc6970theorem distance.eq_zero_iff (a b : Point)71shows distance(a, b) =0↔ a = b72proof73have squared := Euclidean.distanceSquared.zero_iff at a, b with*74have square := distance.square at a, b with*75constructor76 . intro zero77have squareZero : Euclidean.distanceSquared(a, b) =078rw [← square, zero]79exact realMulValue.zero_left with realZero.real80exact squared.forward with squareZero81 . intro equal82have squareZero := squared.backward with equal83have root : distance(a, b) * distance(a, b) =0by rwa square84exact realMulValue.square_eq_zero_iff.forward with root8586theorem distance.positive_of_ne (a b : Point)87requires distinct : a ≠ b88shows0 < distance(a, b)89proof90have nonnegative := Euclidean.distanceSquared.nonnegative_real at a, b with*91have positiveSquare := Euclidean.distanceSquared.positive with*92have nonzero : Euclidean.distanceSquared(a, b) ≠093intro zero94rw zero at positiveSquare95exact realLess.not_self with realZero.real, positiveSquare96have root := sqrt.positive with nonnegative, nonzero97unfold distance98exact root99100-- Section: Triangle inequality101102-- Cauchy-Schwarz as a lower bound on the dot product of two rays.103theorem angleDot.lower_bound (a b c : Point)104shows0≤ angleDot(a, b, c) + distance(b, a) * distance(b, c)105proof106have dotReal := angleDot.real at a, b, c with*107have firstNonnegative := distance.nonnegative at b, a with*108have secondNonnegative := distance.nonnegative at b, c with*109have productNonnegative := realMulValue.nonnegative at distance(b, a), distance(b, c) with*110have firstSquare := distance.square at b, a with*111have secondSquare := distance.square at b, c with*112have productSquare : (distance(b, a) * distance(b, c)) * (distance(b, a) * distance(b, c)) = Euclidean.distanceSquared(b, a) * Euclidean.distanceSquared(b, c)113rw [← firstSquare, ← secondSquare]114ring115have schwarz := angleDot.cauchy_schwarz at a, b, c with*116rw← productSquare at schwarz117have sign :=Real.sign_cases with dotReal118cases sign with nonpositive | nonnegative119 . have negReal := realNegValue.real at angleDot(a, b, c) with dotReal120have negNonnegative := realNegValue.nonnegative_of_nonpositive with nonpositive121have negLe := realLe.of_subset with realZero.real, negNonnegative122have negSquare := realMulValue.neg_both at angleDot(a, b, c), angleDot(a, b, c) with*123have negSchwarz : (-angleDot(a, b, c)) * (-angleDot(a, b, c)) ≤ (distance(b, a) * distance(b, c)) * (distance(b, a) * distance(b, c))124 rwa negSquare125have negLeProduct := realMulValue.le_of_square_le at -angleDot(a, b, c), distance(b, a) * distance(b, c) with*126have shifted := realLe.add_right at -angleDot(a, b, c), distance(b, a) * distance(b, c), angleDot(a, b, c) with*127have cancel : -angleDot(a, b, c) + angleDot(a, b, c) =0byring128rw cancel at shifted129have swap : distance(b, a) * distance(b, c) + angleDot(a, b, c) = angleDot(a, b, c) + distance(b, a) * distance(b, c)130ring131 rwa swap at shifted132 . have dotLe := realLe.of_subset at realZero, angleDot(a, b, c) with realZero.real, nonnegative133have sum := realLe.add at realZero, angleDot(a, b, c), realZero, distance(b, a) * distance(b, c) with*134have zeroZero := realAddValue.zero_right with realZero.real135 rwa zeroZero at sum136137theorem distance.triangle (a b c : Point)138shows distance(a, c) ≤ distance(a, b) + distance(b, c)139proof140have acNonnegative := distance.nonnegative at a, c with*141have abNonnegative := distance.nonnegative at a, b with*142have bcNonnegative := distance.nonnegative at b, c with*143have sumNonnegative : 0≤ distance(a, b) + distance(b, c)144have step := realLe.add at realZero, distance(a, b), realZero, distance(b, c) with*145have zeroZero := realAddValue.zero_right with realZero.real146 rwa zeroZero at step147have acSquare := distance.square at a, c with*148have abSquare := distance.square at a, b with*149have bcSquare := distance.square at b, c with*150have symmetric := distance.symmetric at b, a with*151have bound := angleDot.lower_bound at a, b, c with*152rw symmetric at bound153have identity := cosine_identity at a, b, c with*154have doubled : 0≤ (angleDot(a, b, c) + distance(a, b) * distance(b, c)) + (angleDot(a, b, c) + distance(a, b) * distance(b, c))155have step := realLe.add at realZero, angleDot(a, b, c) + distance(a, b) * distance(b, c), realZero, angleDot(a, b, c) + distance(a, b) * distance(b, c) with*156have zeroZero := realAddValue.zero_right with realZero.real157 rwa zeroZero at step158have difference : (distance(a, b) + distance(b, c)) * (distance(a, b) + distance(b, c)) - distance(a, c) * distance(a, c) =159 (angleDot(a, b, c) + distance(a, b) * distance(b, c)) + (angleDot(a, b, c) + distance(a, b) * distance(b, c))160linear_combination abSquare + bcSquare - acSquare - identity161have nonnegativeDifference : 0≤ (distance(a, b) + distance(b, c)) * (distance(a, b) + distance(b, c)) - distance(a, c) * distance(a, c)162 rwa difference163have squares := realLe.of_nonnegative_difference with*164exact realMulValue.le_of_square_le with*165166end Euclidean167