1-- Midpoints, medians, and perpendicular bisectors in the Euclidean plane.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.Midpoint8import Circle910namespace Euclidean11exportopen Euclidean1213-- The midpoint is constructed from coordinates.14def midpoint (a b : Set) : Set:=15 (half(x(a) + x(b)), half(y(a) + y(b)))1617theorem midpoint.point (a b : Point)18shows Point(midpoint(a, b))19proof20have ax := x.real at a withassumption21have ay := y.real at a withassumption22have bx := x.real at b withassumption23have byReal := y.real at b withassumption24have sumX := realAddValue.real with ax, bx25have sumY := realAddValue.real with ay, byReal26have halfX := half.real with sumX27have halfY := half.real with sumY28unfold midpoint29exact pair.point with halfX, halfY3031theorem midpoint.correct (a b : Point)32shows Midpoint(midpoint(a, b), a, b)33proof34have ax := x.real at a withassumption35have ay := y.real at a withassumption36have bx := x.real at b withassumption37have byReal := y.real at b withassumption38have sumX := realAddValue.real with ax, bx39have sumY := realAddValue.real with ay, byReal40have halfX := half.real with sumX41have halfY := half.real with sumY42have horizontal := x.pair with halfX, halfY43have vertical := y.pair with halfX, halfY44have doubleX := half.add_self with sumX45have doubleY := half.add_self with sumY46constructor47 . unfold midpoint48cc49 . unfold midpoint50cc5152theorem midpoint.unique (a b m n : Point)53requires first : Midpoint(m, a, b)54requires second : Midpoint(n, a, b)55shows m = n56proof57rcases first with⟨firstX, firstY⟩58rcases second with⟨secondX, secondY⟩59have doubledX : x(m) + x(m) = x(n) + x(n) bycc60have doubledY : y(m) + y(m) = y(n) + y(n) bycc61have horizontal := realAddValue.double_cancel with doubledX62have vertical := realAddValue.double_cancel with doubledY63exact point.ext with*6465theorem midpoint.symmetric (a b m : Point)66requires middle : Midpoint(m, a, b)67shows Midpoint(m, b, a)68proof69rcases middle with⟨horizontal, vertical⟩70have ax := x.real at a withassumption71have ay := y.real at a withassumption72have bx := x.real at b withassumption73have byReal := y.real at b withassumption74have swapX := realAddValue.commutative with ax, bx75have swapY := realAddValue.commutative with ay, byReal76constructor77 . cc78 . cc7980theorem midpoint.equidistant (a b m : Point)81requires middle : Midpoint(m, a, b)82shows Euclidean.distanceSquared(m, a) = Euclidean.distanceSquared(m, b)83proof84rcases middle with⟨horizontal, vertical⟩85have first := Euclidean.distanceSquared.coordinates at m, a with*86have second := Euclidean.distanceSquared.coordinates at m, b with*87rw [first, second]88linear_combination (x(a) - x(b)) * horizontal + (y(a) - y(b)) * vertical8990-- Opposite radius vectors have opposite dot products with every vector.91theorem midpoint.opposite_dots (a b m p : Point)92requires middle : Midpoint(m, a, b)93shows angleDot(p, m, a) + angleDot(p, m, b) =094proof95rcases middle with⟨horizontal, vertical⟩96have first := angleDot.coordinates at p, m, a with*97have second := angleDot.coordinates at p, m, b with*98rw [first, second]99linear_combination (x(p) - x(m)) * horizontal + (y(p) - y(m)) * vertical100101-- Apollonius' median theorem. The two smaller triangles share a median;102-- their cosine terms cancel because the two half-base vectors are opposite.103theorem apollonius (a b m p : Point)104requires middle : Midpoint(m, a, b)105shows Euclidean.distanceSquared(p, a) + Euclidean.distanceSquared(p, b) =106 Euclidean.distanceSquared(p, m) + Euclidean.distanceSquared(p, m) +107 Euclidean.distanceSquared(m, a) + Euclidean.distanceSquared(m, a)108proof109have first := cosine_identity at p, m, a with*110have second := cosine_identity at p, m, b with*111have opposite := midpoint.opposite_dots at a, b, m, p with*112have halves := midpoint.equidistant with*113linear_combination first + second - (2:R) * opposite - halves114115-- The locus of points equidistant from A and B is their perpendicular116-- bisector. RightAngle denotes orthogonality, including the zero vector;117-- thus the midpoint itself belongs to the locus. For a proper base, A ≠ B.118theorem perpendicular_bisector (a b m p : Point)119requires middle : Midpoint(m, a, b)120shows Euclidean.distanceSquared(p, a) = Euclidean.distanceSquared(p, b) ↔121 RightAngle(p, m, a)122proof123have median := apollonius at a, b, m, p with*124constructor125 . intro equalSides126have doubled : Euclidean.distanceSquared(p, a) + Euclidean.distanceSquared(p, a) =127 (Euclidean.distanceSquared(p, m) + Euclidean.distanceSquared(m, a)) +128 (Euclidean.distanceSquared(p, m) + Euclidean.distanceSquared(m, a))129linear_combination median + equalSides130have pythagorean := realAddValue.double_cancel with doubled131exact pythagoras_converse at p, m, a with*132 . intro perpendicular133have pythagorean := Euclidean.pythagoras at p, m, a with*134linear_combination (2:R) * pythagorean - median135136-- The median from the apex of an isosceles triangle is an altitude.137theorem isosceles_median_perpendicular (a b c m : Point)138requires middle : Midpoint(m, a, b)139requires equalLegs : Euclidean.distanceSquared(c, a) = Euclidean.distanceSquared(c, b)140shows RightAngle(c, m, a)141proof142exact perpendicular_bisector.forward at a, b, m, c with*143144-- The same diameter identity proves both Thales and its converse.145-- This is an orthogonality statement; endpoint exclusions are unnecessary.146theorem right_iff_on_diameter_circle (a b p m : Point)147requires middle : Midpoint(m, a, b)148shows RightAngle(a, p, b) ↔ OnCircle(p, m, a)149proof150have identity := diameter_identity at a, b, p, m with*151constructor152 . introright153unfold RightAngle atright154unfold OnCircle155linear_combinationright - identity156 . intro onCircle157unfold OnCircle at onCircle158unfold RightAngle159linear_combination identity + onCircle160161-- The midpoint of the hypotenuse is equidistant from all three vertices.162theorem right_triangle_circumcenter (a b p m : Point)163requires middle : Midpoint(m, a, b)164requiresright : RightAngle(a, p, b)165shows Euclidean.distanceSquared(m, p) = Euclidean.distanceSquared(m, a) ∧166 Euclidean.distanceSquared(m, p) = Euclidean.distanceSquared(m, b)167proof168have onCircle := right_iff_on_diameter_circle.forward at a, b, p, m with*169have halves := midpoint.equidistant with*170have pm := Euclidean.distanceSquared.symmetric at p, m with*171have am := Euclidean.distanceSquared.symmetric at a, m with*172unfold OnCircle at onCircle173constructor174 . cc175 . cc176177-- A circle's center lies on the perpendicular bisector of each chord.178-- When the chord is a diameter the center equals its midpoint; the179-- orthogonality formulation includes that case.180theorem circle_center_chord (a b center through m : Point)181requires endpointA : OnCircle(a, center, through)182requires endpointB : OnCircle(b, center, through)183requires middle : Midpoint(m, a, b)184shows RightAngle(center, m, a)185proof186unfold OnCircle at endpointA187unfold OnCircle at endpointB188have first := Euclidean.distanceSquared.symmetric at center, a with*189have second := Euclidean.distanceSquared.symmetric at center, b with*190have equalRadii : Euclidean.distanceSquared(center, a) = Euclidean.distanceSquared(center, b) bycc191exact perpendicular_bisector.forward at a, b, m, center with*192193end Euclidean194