1-- Intersecting chords: the power of a point with respect to a circle, and2-- the equal products of the segments of two chords through one point.3-- 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.Geometry.Chords9import Circle10import Distance1112namespace Euclidean13exportopen Euclidean1415-- A point p of the line through a and b, written p = a + t(b − a).16predicate OnLine (p a b t : Set) :=17 x(p) = x(a) + (t:R) * (x(b) - x(a)) ∧ y(p) = y(a) + (t:R) * (y(b) - y(a))1819-- The power of a point: for a chord ab through p, the dot product of the20-- two segments at p is the squared distance to the center less the squared21-- radius, whatever the position of p on the line.22theorem chord.power (a b p center through : Point) (t : Real)23requires onA : OnCircle(a, center, through)24requires onB : OnCircle(b, center, through)25requires onLine : OnLine(p, a, b, t)26shows angleDot(a, p, b) =27 distanceSquared(p, center) - distanceSquared(a, center)28proof29rcases onLine with⟨horizontal, vertical⟩30unfold OnCircle at onA31unfold OnCircle at onB32have angle := angleDot.coordinates at a, p, b with*33have pointRadius := distanceSquared.coordinates at p, center with*34have firstRadius := distanceSquared.coordinates at a, center with*35have secondRadius := distanceSquared.coordinates at b, center with*36have throughRadius := distanceSquared.coordinates at through, center with*37rw [firstRadius, throughRadius] at onA38rw [secondRadius, throughRadius] at onB39rw [angle, pointRadius, firstRadius, horizontal, vertical]40linear_combination t * onA - t * onB4142-- The squared segments of a chord through p, in terms of the parameter.43theorem chord.segment_squares (a b p : Point) (t : Real)44requires onLine : OnLine(p, a, b, t)45shows distanceSquared(p, a) * distanceSquared(p, b) =46 angleDot(a, p, b) * angleDot(a, p, b)47proof48rcases onLine with⟨horizontal, vertical⟩49have first := distanceSquared.coordinates at p, a with*50have second := distanceSquared.coordinates at p, b with*51have angle := angleDot.coordinates at a, p, b with*52rw [first, second, angle, horizontal, vertical]53ring5455-- Two chords ab and cd through p: the products of the squared segments agree.56theorem chord.products_squared (a b c d p center through : Point) (t u : Real)57requires onA : OnCircle(a, center, through)58requires onB : OnCircle(b, center, through)59requires onC : OnCircle(c, center, through)60requires onD : OnCircle(d, center, through)61requires firstLine : OnLine(p, a, b, t)62requires secondLine : OnLine(p, c, d, u)63shows distanceSquared(p, a) * distanceSquared(p, b) =64 distanceSquared(p, c) * distanceSquared(p, d)65proof66have firstPower := chord.power at a, b, p, center, through, t with*67have secondPower := chord.power at c, d, p, center, through, u with*68have firstSquares := chord.segment_squares at a, b, p, t with*69have secondSquares := chord.segment_squares at c, d, p, u with*70unfold OnCircle at onA71unfold OnCircle at onC72have same : angleDot(a, p, b) = angleDot(c, p, d)73linear_combination firstPower - secondPower - onA + onC74rw [firstSquares, secondSquares, same]75rfl7677-- The classical statement: the products of the segment lengths agree.78theorem chord.products (a b c d p center through : Point) (t u : Real)79requires onA : OnCircle(a, center, through)80requires onB : OnCircle(b, center, through)81requires onC : OnCircle(c, center, through)82requires onD : OnCircle(d, center, through)83requires firstLine : OnLine(p, a, b, t)84requires secondLine : OnLine(p, c, d, u)85shows distance(p, a) * distance(p, b) = distance(p, c) * distance(p, d)86proof87have squares := chord.products_squared at a, b, c, d, p, center, through, t, u88with*89have nonnegativeA := Euclidean.distanceSquared.nonnegative_real at p, a with*90have nonnegativeB := Euclidean.distanceSquared.nonnegative_real at p, b with*91have nonnegativeC := Euclidean.distanceSquared.nonnegative_real at p, c with*92have nonnegativeD := Euclidean.distanceSquared.nonnegative_real at p, d with*93have first := sqrt.mul with nonnegativeA, nonnegativeB94have second := sqrt.mul with nonnegativeC, nonnegativeD95unfold distance96rw [← first, ← second, squares]97rfl9899end Euclidean100