1-- Collinearity and the triangle midsegment theorem.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.Lines8import Midpoint910namespace Euclidean11exportopen Euclidean1213def directionCross (a b c d : Point) : Real:=14 (x(b) - x(a)) * (y(d) - y(c)) -15 (y(b) - y(a)) * (x(d) - x(c))1617predicate Collinear (a b c : Set) := directionCross(a, b, a, c) =01819-- Algebraic orthogonality of directed segments, permitting zero directions.20predicate PerpendicularDirections (a b c d : Set) :=21 dot(vector(a, b), vector(c, d)) =02223-- Parallel directions of nonzero segments. Coincident supporting lines24-- are allowed; this predicate does not assert that two lines are disjoint.25predicate ParallelSegments (a b c d : Set) :=26 a ≠ b ∧ c ≠ d ∧ directionCross(a, b, c, d) =02728theorem directionCross.real (a b c d : Point)29showsReal(directionCross(a, b, c, d))30proof31unfold directionCross32typecheck3334theorem midpoint.collinear (a b m : Point)35requires middle : Midpoint(m, a, b)36shows Collinear(a, m, b)37proof38rcases middle with⟨horizontal, vertical⟩39have doubled : directionCross(a, m, a, b) + directionCross(a, m, a, b) = (0:R) + (0:R)40unfold directionCross41linear_combination -(y(b) - y(a)) * horizontal + (x(b) - x(a)) * vertical42unfold Collinear43exact realAddValue.double_cancel with realZero.real, doubled4445-- Twice the displacement between two side midpoints is the third side.46theorem midsegment.direction (a b c m n : Point)47requires first : Midpoint(m, c, a)48requires second : Midpoint(n, c, b)49shows (x(n) - x(m)) + (x(n) - x(m)) = x(b) - x(a) ∧50 (y(n) - y(m)) + (y(n) - y(m)) = y(b) - y(a)51proof52rcases first with⟨firstX, firstY⟩53rcases second with⟨secondX, secondY⟩54constructor55 . linear_combination firstX - secondX56 . linear_combination firstY - secondY5758theorem midsegment.distinct (a b c m n : Point)59requires first : Midpoint(m, c, a)60requires second : Midpoint(n, c, b)61requires base : a ≠ b62shows m ≠ n63proof64rcases first with⟨firstX, firstY⟩65rcases second with⟨secondX, secondY⟩66intro equal67rw equal at firstX68rw equal at firstY69have horizontal : x(a) = x(b) bylinear_combination firstX - secondX70have vertical : y(a) = y(b) bylinear_combination firstY - secondY71have same := point.ext with*72exact base with same7374theorem midsegment.parallel (a b c m n : Point)75requires first : Midpoint(m, c, a)76requires second : Midpoint(n, c, b)77requires base : a ≠ b78shows ParallelSegments(m, n, a, b)79proof80have direction := midsegment.direction at a, b, c, m, n with*81rcases direction with⟨horizontal, vertical⟩82have distinct := midsegment.distinct with*83have area := directionCross.real at m, n, a, b with*84have doubled : directionCross(m, n, a, b) + directionCross(m, n, a, b) = (0:R) + (0:R)85unfold directionCross86linear_combination (y(b) - y(a)) * horizontal - (x(b) - x(a)) * vertical87have parallel := realAddValue.double_cancel with area, realZero.real, doubled88unfold ParallelSegments89refine⟨distinct, base, parallel⟩9091-- The midsegment has half the length of the third side, stated in squares.92theorem midsegment.length (a b c m n : Point)93requires first : Midpoint(m, c, a)94requires second : Midpoint(n, c, b)95shows Euclidean.distanceSquared(a, b) =96 Euclidean.distanceSquared(m, n) + Euclidean.distanceSquared(m, n) +97 Euclidean.distanceSquared(m, n) + Euclidean.distanceSquared(m, n)98proof99have direction := midsegment.direction at a, b, c, m, n with*100rcases direction with⟨horizontal, vertical⟩101have base := Euclidean.distanceSquared.coordinates at a, b with*102have segment := Euclidean.distanceSquared.coordinates at m, n with*103rw [base, segment]104have squareX := realMulValue.square_of_double with horizontal105have squareY := realMulValue.square_of_double with vertical106rw [squareX, squareY]107ring108109-- Midsegments with the same directed base have the same displacement.110-- This is the geometric bridge used by Varignon's theorem.111theorem midsegment.equal_vectors (a b c d m n p q : Point)112requires firstM : Midpoint(m, c, a)113requires firstN : Midpoint(n, c, b)114requires secondP : Midpoint(p, d, a)115requires secondQ : Midpoint(q, d, b)116shows vector(m, n) = vector(p, q)117proof118have first := midsegment.direction at a, b, c, m, n with*119have second := midsegment.direction at a, b, d, p, q with*120rcases first with⟨firstX, firstY⟩121rcases second with⟨secondX, secondY⟩122have horizontal : x(n) - x(m) = x(q) - x(p)123apply realAddValue.double_cancel124 . cc125have vertical : y(n) - y(m) = y(q) - y(p)126apply realAddValue.double_cancel127 . cc128have components : x(n) - x(m) = x(q) - x(p) ∧129 y(n) - y(m) = y(q) - y(p)130refine⟨horizontal, vertical⟩131exact vector.equal_iff.backward with components132133end Euclidean134