Viviani’stheorem
Pick any point inside an equilateral triangle and measure straight to each of the three sides. The three distances always add up to the same length: the triangle’s height.
This page proves that fact in , a language in which a computer checks every step, and then follows it as far as it goes: to every triangle, to the whole plane, and up into higher dimensions. Every figure can be dragged.
6 Lean files, 883 lines. Source on GitHub.
Chapter 1
The theorem
Let A, B, C be three points with all three sides of equal length, |AB| = |BC| = |CA|, and let P be any point of the closed triangle, that is, of the of {A, B, C}. Write d(P, BC) for the distance from P to the side BC. Then
The right-hand side is the of the triangle. Nothing on that side mentions P: that is the whole surprise.
The same statement in Lean
theorem viviani_segment (hAB : dist A B = dist B C) (hCA : dist C A = dist B C) (hP : P ∈ convexHull ℝ {A, B, C}) : infDist P (segment ℝ B C) + infDist P (segment ℝ C A) + infDist P (segment ℝ A B) = Real.sqrt 3 / 2 * dist B CHere segment ℝ B C is the side itself, as in the textbook. A companion theorem, viviani, says the same for the full line through each side, line[ℝ, B, C], which is the form most of the proofs use. There is no assumption that the triangle has positive size: if the three points coincide, both sides of the equation are zero.
Chapter 2
Why it’s true
Cut, lay flat, stackThe classical argument, in motion
Join P to the corners and the triangle falls into three pieces, each standing on one side. Follow the pieces. Every move below keeps areas exactly: first a rigid slide and turn, then a shear, where the tip of a triangle slides parallel to its base.
- Cut. Join P to the three corners. Three triangles, each with a full side s as its base and one of the distances as its height.
- Lay flat. Slide and turn each piece, without changing its shape, until its base lies level in the frame below, one on top of the other.
- Shear. Slide each tip sideways, parallel to its base. Base and height stay the same, so the area does too.
- Double. Each piece is half of a rectangle s wide and one distance tall. The three rectangles fill a frame s wide and h tall, which is twice the big triangle.
The only place the argument uses “all sides equal” is the last step, where the same width s appears in all three rectangles. In Lean the area identity is the theorem sum_infDist_facetSpan_mul_volume, and it holds for any triangle: each distance times its own side, added up, is twice the area.
Weigh the cornersThe idea behind the Lean proof
The formal proof takes a second route, which uses no areas at all, only distances and averages. It is the idea everything else on this page grows from.
Every point of the triangle is a weighted average of the corners: P = aA + bB + cC, where the weights are never negative and add up to 1.
Drag P. The distance to BC is always the weight of A times the height: 0.357 × 1.732 = 0.619.
- Distance scales with weight. Distance to the line BC, taken with a sign, is an of the point, and it is zero at B and at C. So at P it is exactly a times its value at A: the distance from P to BC is a · hA, where hA is the height from A.infDist_facetSpan_affineCombination
- Add the three sides. The three distances add up to ahA + bhB + chC. This is true in every triangle.sum_infDist_facetSpan_affineCombination
- Equal sides, equal heights. Height times opposite side is twice the area, whichever corner you start from. So if the sides are equal, the heights are equal, to some h.equilateral_iff_height_eq
- Collect. ah + bh + ch = (a + b + c)h = h.viviani
Chapter 3
Every triangle, and only one of them
Step 2 above never asked for equal sides. In any triangle at all, the three distances from P = aA + bB + cC add up to a weighted average of the three heights:
Move the corners and see what that means. The bar on the right stacks the three distances at their true lengths; the ticks mark the three heights.
Not equilateral. The sum slides between the shortest height, 2.955, and the tallest, 3.854. The faint lines join points with equal sums.
Drag the hollow corners and the point P. First line: the three distances and their sum. Second line: weight × height for each corner. The two always agree. Thin dashed lines are the three heights.
Between the shortest and the tallest height
An average cannot leave the range of what it averages. So on the closed triangle the distance sum is at least the shortest height and at most the tallest, and it reaches both, at corners.le_sum_infDist_facetSpan, sum_infDist_facetSpan_le
The faint lines in the figure join points with the same sum. They are straight and parallel, because the sum is an affine function of P. This picture of a general triangle is due to Elias Abboud and to Burkard Polster; see related work.
The converse
If the sum is the same everywhere in a triangle, then in particular it is the same at the three corners, where it equals the three heights. Equal heights force equal sides, because height times side is always twice the area. So:
theorem viviani_iff (h : AffineIndependent ℝ ![A, B, C]) : (∃ c : ℝ, ∀ P ∈ convexHull ℝ {A, B, C}, infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] = c) ↔ dist A B = dist B C ∧ dist C A = dist B CA triangle has a constant distance sum if and only if it is equilateral. The hypothesis AffineIndependent says the three points are not on one line. It is needed: for three points on a line every distance is zero, so the sum is constant, yet the “triangle” need not be equilateral.
Chapter 4
Sides, lines, and the rest of the plane
To the side, or to its line?
The textbook says “distance to the side”. Proofs prefer the whole line through the side, which is easier to handle. For a point inside an equilateral triangle the two are the same, because the perpendicular dropped onto each line lands on the side itself, never on its extension. That needs an argument, and the Lean development gives one: the foot of the perpendicular from P is the same weighted average of the feet from A, B and C, and those three feet are the midpoint of the side and its two ends.orthogonalProjection_mem_segment, viviani_segment
In an obtuse triangle it can fail: try the “Obtuse” button in the figure above and look for the dashed extensions.
Outside the triangle
Carry P across a side and the theorem seems to break: the three distances now add up to more than the height. It is repaired by a sign. Count a distance as negative when P is on the far side of that line from the triangle, and the sum is the height again, everywhere in the plane.
P is outside: one distance is measured on the far side of a side line and counts as negative (dashed). Without signs the three would add to 3.350.
Drag P anywhere. The arrows on the right are the three signed distances laid end to end, up for positive and down for negative. They always finish level with h. Side length 2, so h = √3 ≈ 1.732.
theorem sum_signedInfDist_eq_of_height_eq {s : Simplex ℝ P n} {h : ℝ} (hh : ∀ i, s.height i = h) {p : P} (hp : p ∈ affineSpan ℝ (Set.range s.points)) : ∑ i, s.signedInfDist i p = hThis is stated for a of any dimension whose heights all equal h; an equilateral triangle is the case n = 2. The signed distance signedInfDist is Mathlib’s.
Chapter 5
Why ternary plots work
Chemists, geologists and soil scientists draw mixtures of three ingredients as a point in an equilateral triangle. The share of each ingredient is read off as the distance to one side. That only makes sense if the three readings always total 100%, and Viviani’s theorem is exactly that guarantee.
The key lemma says more: distance to a side, divided by the height, is the weight of the opposite corner. So the readings are not just three numbers that happen to add up; they are the proportions in which the corners are mixed.signedInfDist_div_height
Drag the point. The blue distance, to the side opposite A, measures the share of A; the blue grid lines mark 20%, 40%, 60% and 80% of A. The same goes for red and B, ochre and C.
Chapter 6
Up a dimension
Nothing in the weights argument is special to the plane. Replace the triangle by a tetrahedron, its three sides by four faces, and distance to a line by distance to a plane. The distance from P to a face is still the weight of the opposite corner times the height from that corner, and the four distances still add up to an average of the four heights.
For a regular tetrahedron with edge r, every height is √(2/3) · r, so:
theorem viviani_tetrahedron {s : Simplex ℝ P 3} {r : ℝ} (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {p : P} (hp : p ∈ s.closedInterior) : ∑ i, infDist p (facetSpan s i) = Real.sqrt (2 / 3) * rRegular tetrahedron, edge 2. Every height is √(2/3) × 2 ≈ 1.633, and so is the sum.
Drag P to move it; drag the empty space to turn the solid. Each perpendicular has the colour of the face it meets (the fourth face is ink). The bar shows the four distances at true length.
The converse is different in space
In the plane, a constant sum singles out the equilateral triangle. In space it does not single out the regular tetrahedron. The same reasoning as before shows the sum is constant exactly when the four heights are equal, and since height times face area is three times the volume, that happens exactly when the four faces have equal area.sum_infDist_facetSpan_const_iff_volume
A , a tetrahedron whose opposite edges are equal in pairs, has four congruent faces, so it qualifies without being regular. The Lean proof uses Heron’s formula to see that the four faces have the same area.viviani_disphenoid By a classical theorem of Bang (1897), these are in fact the only tetrahedra with four faces of equal area; that last step is not formalized here.
Every dimension
For a regular simplex of dimension n and edge r, the distances from any point of the closed simplex to its n + 1 facets add up to the height:
theorem viviani_simplex {s : Simplex ℝ P n} {r : ℝ} (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {p : P} (hp : p ∈ s.closedInterior) : ∑ i, infDist p (facetSpan s i) = Real.sqrt ((n + 1) / (2 * n)) * r| Dimension | Shape | Sum of distances |
|---|---|---|
| 1 | Segment | r |
| 2 | Equilateral triangle | √(3/4) · r = (√3 / 2) · r ≈ 0.866 r |
| 3 | Regular tetrahedron | √(2/3) · r ≈ 0.816 r |
| 4 | Regular 5-cell | √(5/8) · r ≈ 0.791 r |
| n | Regular n-simplex | √((n + 1) / (2n)) · r, tending to r / √2 |
Chapter 7
The Lean, line by line
Seven statements carry the whole development. Choose one and the figure shows what it says about the triangle. Each box quotes the statement exactly as it stands in the source.
P = 0.36·A + 0.26·B + 0.38·C
theorem exists_weights_of_mem_closedInterior {s : Simplex ℝ P n} {p : P} (hp : p ∈ s.closedInterior) : ∃ w : Fin (n + 1) → ℝ, ∑ j, w j = 1 ∧ (∀ j, 0 ≤ w j) ∧ p = univ.affineCombination ℝ s.points wEvery point of the closed shape is a weighted average of the corners, with weights that are not negative and add up to 1. The numbers in the figure are the three weights.
theorem infDist_facetSpan_affineCombination (s : Simplex ℝ P n) (i : Fin (n + 1)) {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) : infDist (univ.affineCombination ℝ s.points w) (facetSpan s i) = |w i| * s.height iThe distance from the point to the side opposite corner i is its weight at i times the height from i. In the figure: the blue distance, against the dashed height from A.
theorem sum_infDist_facetSpan_affineCombination (s : Simplex ℝ P n) {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) (h0 : ∀ j, 0 ≤ w j) : ∑ i, infDist (univ.affineCombination ℝ s.points w) (facetSpan s i) = ∑ i, w i * s.height iInside the shape, the distances to all the sides add up to the weights times the heights: an average of the heights.
theorem equilateral_iff_height_eq (t : Triangle ℝ P) : t.Equilateral ↔ ∀ i j, t.height i = t.height jA triangle has three equal sides exactly when it has three equal heights, because height times side is twice the area from every corner.
theorem viviani (hAB : dist A B = dist B C) (hCA : dist C A = dist B C) (hP : P ∈ convexHull ℝ {A, B, C}) : infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] = Real.sqrt 3 / 2 * dist B CWith equal heights the average is just the height. The three distances stack up to fill it exactly.
theorem viviani_segment (hAB : dist A B = dist B C) (hCA : dist C A = dist B C) (hP : P ∈ convexHull ℝ {A, B, C}) : infDist P (segment ℝ B C) + infDist P (segment ℝ C A) + infDist P (segment ℝ A B) = Real.sqrt 3 / 2 * dist B CEach perpendicular lands on its side, not on an extension of it. So the theorem also holds for distance to the sides as segments, the literal textbook statement.
theorem viviani_iff (h : AffineIndependent ℝ ![A, B, C]) : (∃ c : ℝ, ∀ P ∈ convexHull ℝ {A, B, C}, infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] = c) ↔ dist A B = dist B C ∧ dist C A = dist B CSkew the triangle and the heights differ, so the sum moves with the point. Constant sum happens exactly for equilateral triangles.
The files
883 lines of Lean 4 in 6 files, built on . Two facts are taken from Mathlib and deserve credit by name: Affine.Simplex.signedInfDist_affineCombination, which is the signed form of the key lemma, and Affine.Simplex.volume_eq, base times height for simplices. What is built here is what follows from them.
- The textbook statements, for three points A, B, C: Viviani with side lines and with the sides themselves, the converse, and the formula for every triangle. 271 lines.
- The general theory, in any dimension: distance to a facet is weight times height; the sum is an average of the heights; it is constant exactly when the heights, or the facet volumes, are equal; signed distances on the whole space; the area identity. 247 lines.
- Triangles: equilateral exactly when the three heights are equal; Viviani and its converse. 85 lines.
- Regular simplices in every dimension: the height is √((n + 1) / (2n)) times the edge. 123 lines.
- The regular tetrahedron, and the disphenoid. 90 lines.
- A concrete triangle, (0, 0), (2, 0), (1, √3), on which the theorem gives √3. 67 lines.
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Viviani.Triangle910/-!11# Viviani's theorem, as in the textbook1213Statements about three points `A B C` of a real inner product space `V` (for instance the plane14`EuclideanSpace ℝ (Fin 2)`), with1516* the closed triangle written `convexHull ℝ {A, B, C}`,17* the side lines written `line[ℝ, B, C]`, the sides themselves `segment ℝ B C`,18* distance from a point to a set written `Metric.infDist`.1920## Main results2122* `viviani`: if `dist A B = dist B C = dist C A` and `P` lies in the closed triangle, the23 distances from `P` to the three side lines add up to `√3 / 2 * dist B C`. There is no24 non-degeneracy hypothesis: if the three points coincide both sides are `0`.25* `viviani_segment`: the same with distances to the three sides (segments), which is the26 literal textbook wording.27* `viviani_iff`: for a genuine (affinely independent) triangle, the distance sum is constant on28 the closed triangle if and only if the triangle is equilateral.29* `sum_infDist_eq_of_affineIndependent`: in any triangle the distance sum at30 `a • A + b • B + c • C` is `a * h_A + b * h_B + c * h_C`.31* `infDist_eq_mul_of_affineIndependent`: the distance to side line `BC` is `a * h_A`.32* `infDist_segment_eq_infDist_line`: when the foot of the altitude from `A` lies on the side33 `BC`, distance to the side and distance to its line agree on the closed triangle.34-/3536public section3738open EuclideanGeometry Metric Affine Finset3940namespace Viviani4142section image4344variable {α : Type*} (A B C : α)4546/-- The vertices other than the first. -/47theorem image_compl_zero : (![A, B, C] : Fin 3 → α) '' {0}ᶜ = {B, C} := by48 ext x; simp [Fin.exists_fin_succ, eq_comm]4950/-- The vertices other than the second. -/51theorem image_compl_one : (![A, B, C] : Fin 3 → α) '' {1}ᶜ = {C, A} := by52 ext x; simp [Fin.exists_fin_succ, eq_comm, or_comm]5354/-- The vertices other than the third. -/55theorem image_compl_two : (![A, B, C] : Fin 3 → α) '' {2}ᶜ = {A, B} := by56 ext x; simp [Fin.exists_fin_succ, eq_comm]5758/-- The set of vertices. -/59theorem range_eq : Set.range (![A, B, C] : Fin 3 → α) = {A, B, C} := by60 ext x; simp [eq_comm]; tauto6162end image6364variable {V : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] {A B C P : V}6566section bridge6768variable (h : AffineIndependent ℝ ![A, B, C])6970/-- The facet opposite `A` spans the line `BC`. -/71theorem facetSpan_zero : facetSpan (⟨![A, B, C], h⟩ : Triangle ℝ V) 0 = line[ℝ, B, C] := by72 rw [facetSpan, image_compl_zero]7374/-- The facet opposite `B` spans the line `CA`. -/75theorem facetSpan_one : facetSpan (⟨![A, B, C], h⟩ : Triangle ℝ V) 1 = line[ℝ, C, A] := by76 rw [facetSpan, image_compl_one]7778/-- The facet opposite `C` spans the line `AB`. -/79theorem facetSpan_two : facetSpan (⟨![A, B, C], h⟩ : Triangle ℝ V) 2 = line[ℝ, A, B] := by80 rw [facetSpan, image_compl_two]8182/-- The closed triangle is the convex hull of the three vertices. -/83theorem closedInterior_eq_convexHull :84 (⟨![A, B, C], h⟩ : Triangle ℝ V).closedInterior = convexHull ℝ {A, B, C} := by85 rw [← Simplex.convexHull_eq_closedInterior, range_eq]8687/-- The sum over the three facets, written out. -/88theorem sum_facetSpan_eq (P : V) :89 ∑ i, infDist P (facetSpan (⟨![A, B, C], h⟩ : Triangle ℝ V) i) =90 infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] := by91 rw [Fin.sum_univ_three, facetSpan_zero, facetSpan_one, facetSpan_two]9293end bridge9495/-- Three points at equal positive distances from each other form a genuine triangle. -/96theorem affineIndependent_of_dist_eq (hAB : dist A B = dist B C) (hCA : dist C A = dist B C)97 (hne : A ≠ B) : AffineIndependent ℝ ![A, B, C] := by98 have hpos : 0 < dist A B := dist_pos.2 hne99 rw [affineIndependent_iff_not_collinear_set]100 intro hcol101 rcases hcol.wbtw_or_wbtw_or_wbtw with h | h | h <;>102 · have := h.dist_add_dist103 simp only [dist_comm] at *104 linarith105106107/-! ### Every triangle -/108109/-- **Key lemma, for three points.** The distance from `a • A + b • B + c • C` to the line `BC`110is `a` times the distance from `A` to that line. -/111theorem infDist_eq_mul_of_affineIndependent (h : AffineIndependent ℝ ![A, B, C]) {a b c : ℝ}112 (ha : 0 ≤ a) (habc : a + b + c = 1) :113 infDist (a • A + b • B + c • C) line[ℝ, B, C] = a * infDist A line[ℝ, B, C] := by114 have hw : ∑ i, ![a, b, c] i = 1 := by simpa [Fin.sum_univ_three] using habc115 have key := infDist_facetSpan_affineCombination (⟨![A, B, C], h⟩ : Triangle ℝ V) 0 hw116 have hcomb : univ.affineCombination ℝ ![A, B, C] ![a, b, c] = a • A + b • B + c • C := by117 rw [affineCombination_eq_linear_combination _ _ _ hw]118 simp [Fin.sum_univ_three]119 rw [height_eq_infDist, facetSpan_zero] at key120 rw [← hcomb]121 simpa [abs_of_nonneg ha] using key122123/-- **Viviani for every triangle.** At the point `a • A + b • B + c • C` of a closed triangle,124the distances to the three side lines add up to `a * h_A + b * h_B + c * h_C`, where `h_A` is125the distance from `A` to the line `BC` and so on: a weighted average of the three altitudes. -/126theorem sum_infDist_eq_of_affineIndependent (h : AffineIndependent ℝ ![A, B, C]) {a b c : ℝ}127 (ha : 0 ≤ a) (hb : 0 ≤ b) (hc : 0 ≤ c) (habc : a + b + c = 1) :128 infDist (a • A + b • B + c • C) line[ℝ, B, C] +129 infDist (a • A + b • B + c • C) line[ℝ, C, A] +130 infDist (a • A + b • B + c • C) line[ℝ, A, B] =131 a * infDist A line[ℝ, B, C] + b * infDist B line[ℝ, C, A] + c * infDist C line[ℝ, A, B] := by132 set t : Triangle ℝ V := ⟨![A, B, C], h⟩133 have hw : ∑ i, ![a, b, c] i = 1 := by simpa [Fin.sum_univ_three] using habc134 have h0 : ∀ i, 0 ≤ ![a, b, c] i := by135 intro i; fin_cases i <;> simpa136 have key := sum_infDist_facetSpan_affineCombination t hw h0137 have hcomb : univ.affineCombination ℝ t.points ![a, b, c] = a • A + b • B + c • C := by138 rw [affineCombination_eq_linear_combination _ _ _ hw]139 simp [t, Fin.sum_univ_three]140 rw [hcomb, sum_facetSpan_eq h] at key141 rw [key, Fin.sum_univ_three, height_eq_infDist, height_eq_infDist, height_eq_infDist,142 facetSpan_zero, facetSpan_one, facetSpan_two]143 rfl144145/-! ### The classical theorem -/146147/-- **Viviani's theorem.** For an equilateral triangle `ABC` and any point `P` of the closed148triangle, the distances from `P` to the three side lines add up to `√3 / 2` times the side,149which is the altitude of the triangle. -/150theorem viviani (hAB : dist A B = dist B C) (hCA : dist C A = dist B C)151 (hP : P ∈ convexHull ℝ {A, B, C}) :152 infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] =153 Real.sqrt 3 / 2 * dist B C := by154 by_cases hne : A = B155 · subst hne156 have hBC : A = C := by157 rw [dist_self] at hAB158 exact dist_eq_zero.1 hAB.symm159 subst hBC160 simp only [Set.insert_eq_of_mem, Set.mem_singleton_iff, convexHull_singleton] at hP161 subst hP162 simp163 · have h := affineIndependent_of_dist_eq hAB hCA hne164 set t : Triangle ℝ V := ⟨![A, B, C], h⟩165 have ht : t.Equilateral := by166 rw [Triangle.equilateral_iff_dist_01_eq_02_and_dist_01_eq_12]167 refine ⟨?_, hAB⟩168 change dist A B = dist A C169 rw [hAB, ← hCA, dist_comm]170 rw [← closedInterior_eq_convexHull h] at hP171 have key := viviani_triangle ht hP172 rw [sum_facetSpan_eq h] at key173 rw [key, ← hAB]174 rfl175176/-- **Viviani's theorem with its converse.** For a genuine triangle `ABC`, the sum of the177distances to the three side lines is the same at every point of the closed triangle if and only178if the triangle is equilateral. -/179theorem viviani_iff (h : AffineIndependent ℝ ![A, B, C]) :180 (∃ c : ℝ, ∀ P ∈ convexHull ℝ {A, B, C},181 infDist P line[ℝ, B, C] + infDist P line[ℝ, C, A] + infDist P line[ℝ, A, B] = c) ↔182 dist A B = dist B C ∧ dist C A = dist B C := by183 have key := sum_infDist_const_iff_equilateral (⟨![A, B, C], h⟩ : Triangle ℝ V)184 simp_rw [sum_facetSpan_eq h, closedInterior_eq_convexHull h] at key185 rw [key, Triangle.equilateral_iff_dist_01_eq_02_and_dist_01_eq_12]186 change dist A B = dist A C ∧ dist A B = dist B C ↔ _187 rw [dist_comm A C]188 constructor189 · rintro ⟨h1, h2⟩190 exact ⟨h2, h1 ▸ h2⟩191 · rintro ⟨h1, h2⟩192 exact ⟨h1.trans h2.symm, h1⟩193194195/-! ### Distances to the sides themselves -/196197/-- A side is contained in its line. -/198theorem segment_subset_line (B C : V) : segment ℝ B C ⊆ line[ℝ, B, C] := by199 rw [← convexHull_pair]200 exact convexHull_subset_affineSpan _201202/-- If the foot of the altitude from `A` lies on the side `BC`, then so does the foot of the203perpendicular from any point of the closed triangle. -/204theorem orthogonalProjection_mem_segment205 (hA : (orthogonalProjection line[ℝ, B, C] A : V) ∈ segment ℝ B C)206 (hP : P ∈ convexHull ℝ {A, B, C}) :207 (orthogonalProjection line[ℝ, B, C] P : V) ∈ segment ℝ B C := by208 let π : V →ᵃ[ℝ] V :=209 (line[ℝ, B, C]).subtype.comp (orthogonalProjection line[ℝ, B, C]).toAffineMap210 have hπ : π '' convexHull ℝ {A, B, C} ⊆ segment ℝ B C := by211 rw [AffineMap.image_convexHull]212 refine convexHull_min ?_ (convex_segment B C)213 rintro _ ⟨x, hx, rfl⟩214 rcases hx with rfl | rfl | rfl215 · exact hA216 · have : π x = x :=217 orthogonalProjection_eq_self_iff.2 (left_mem_affineSpan_pair ℝ x C)218 rw [this]219 exact left_mem_segment ℝ x C220 · have : π x = x :=221 orthogonalProjection_eq_self_iff.2 (right_mem_affineSpan_pair ℝ B x)222 rw [this]223 exact right_mem_segment ℝ B x224 exact hπ ⟨P, hP, rfl⟩225226/-- If the foot of the altitude from `A` lies on the side `BC`, then for points of the closed227triangle the distance to the side `BC` equals the distance to the line `BC`. -/228theorem infDist_segment_eq_infDist_line229 (hA : (orthogonalProjection line[ℝ, B, C] A : V) ∈ segment ℝ B C)230 (hP : P ∈ convexHull ℝ {A, B, C}) :231 infDist P (segment ℝ B C) = infDist P line[ℝ, B, C] := by232 refine le_antisymm ?_233 (infDist_le_infDist_of_subset (segment_subset_line B C) ⟨B, left_mem_segment ℝ B C⟩)234 rw [← dist_orthogonalProjection_eq_infDist]235 exact infDist_le_dist_of_mem (orthogonalProjection_mem_segment hA hP)236237/-- In an isosceles triangle the foot of the altitude from the apex is the midpoint of the base. -/238theorem orthogonalProjection_eq_midpoint (h : dist A B = dist A C) :239 (orthogonalProjection line[ℝ, B, C] A : V) = midpoint ℝ B C := by240 rw [coe_orthogonalProjection_eq_iff_mem]241 refine ⟨AffineMap.lineMap_mem_affineSpan_pair _ _ _, ?_⟩242 rw [direction_affineSpan, vectorSpan_pair, Submodule.mem_orthogonal_singleton_iff_inner_right,243 ← neg_vsub_eq_vsub_rev, inner_neg_left, neg_eq_zero]244 exact AffineSubspace.mem_perpBisector_iff_inner_eq_zero'.1245 (AffineSubspace.mem_perpBisector_iff_dist_eq.2 h)246247/-- In an isosceles triangle with apex `A`, distance to the base and distance to its line agree248on the closed triangle. -/249theorem infDist_segment_eq_of_dist_eq (h : dist A B = dist A C)250 (hP : P ∈ convexHull ℝ {A, B, C}) :251 infDist P (segment ℝ B C) = infDist P line[ℝ, B, C] :=252 infDist_segment_eq_infDist_line253 (by rw [orthogonalProjection_eq_midpoint h]; exact midpoint_mem_segment B C) hP254255/-- **Viviani's theorem, literally.** For an equilateral triangle `ABC` and any point `P` of256the closed triangle, the distances from `P` to the three sides (as segments) add up to `√3 / 2`257times the side. -/258theorem viviani_segment (hAB : dist A B = dist B C) (hCA : dist C A = dist B C)259 (hP : P ∈ convexHull ℝ {A, B, C}) :260 infDist P (segment ℝ B C) + infDist P (segment ℝ C A) + infDist P (segment ℝ A B) =261 Real.sqrt 3 / 2 * dist B C := by262 have hP₁ : P ∈ convexHull ℝ {B, C, A} := by263 convert hP using 2; ext x; simp; tauto264 have hP₂ : P ∈ convexHull ℝ {C, A, B} := by265 convert hP using 2; ext x; simp; tauto266 rw [infDist_segment_eq_of_dist_eq (by rw [hAB, ← hCA, dist_comm]) hP,267 infDist_segment_eq_of_dist_eq (by rw [← hAB, dist_comm]) hP₁,268 infDist_segment_eq_of_dist_eq (by rw [hCA, dist_comm]) hP₂]269 exact viviani hAB hCA hP270271end Viviani
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Mathlib910/-!11# Distances from a point to the facets of a simplex1213Everything in this project rests on one observation: the distance from a point to a facet of a14simplex is an affine function of the point, which vanishes on that facet. Hence, if `p` has15barycentric coordinates `w` with respect to a simplex `s`,1617 `dist(p, facet opposite i) = |w i| * (height of s from vertex i)`.1819Mathlib records the signed form of this as `Affine.Simplex.signedInfDist_affineCombination`;20here we draw the consequences for ordinary (unsigned) distances.2122## Main results2324For a simplex `s` in a Euclidean affine space, of any dimension `n ≥ 1`:2526* `infDist_facetSpan_affineCombination`: the displayed formula.27* `sum_infDist_facetSpan_affineCombination`: on the closed simplex the distances to the `n + 1`28 facets add up to the weighted average `∑ i, w i * s.height i` of the heights.29* `le_sum_infDist_facetSpan`, `sum_infDist_facetSpan_le`: so the sum lies between the smallest30 and the largest height.31* `sum_infDist_facetSpan_const_iff`: the sum is the same at every point of the closed simplex32 if and only if all heights are equal,33* `sum_infDist_facetSpan_const_iff_volume`: if and only if all facets have the same volume.34* `sum_signedInfDist_div_height`, `sum_signedInfDist_eq_of_height_eq`: with signed distances35 the identities hold on the whole affine span, not only inside the simplex.36* `sum_infDist_facetSpan_mul_volume`: `∑ i, dist(p, facet i) * vol(facet i) = n * vol(s)`, the37 identity behind the classical proof by areas.3839The distance to a facet always means `Metric.infDist p (facetSpan s i)`: the least distance from40`p` to the affine subspace spanned by the facet (a line for a triangle, a plane for a41tetrahedron).42-/4344public section4546open EuclideanGeometry Metric Affine Finset4748namespace Viviani4950variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P]51 [NormedAddTorsor V P] {n : ℕ} [NeZero n]5253/-- The affine span of the facet of `s` opposite vertex `i`: for a triangle, the line through the54side opposite `i`; for a tetrahedron, the plane through the face opposite `i`. -/55abbrev facetSpan (s : Simplex ℝ P n) (i : Fin (n + 1)) : AffineSubspace ℝ P :=56 affineSpan ℝ (s.points '' {i}ᶜ)5758/-- The height of a simplex from vertex `i` is the distance from that vertex to the affine59span of the opposite facet. -/60theorem height_eq_infDist (s : Simplex ℝ P n) (i : Fin (n + 1)) :61 s.height i = infDist (s.points i) (facetSpan s i) := by62 rw [← dist_orthogonalProjection_eq_infDist, Simplex.height, Simplex.altitudeFoot,63 Simplex.orthogonalProjectionSpan]64 simp_rw [Simplex.range_faceOpposite_points]6566/-- For a point of the affine span of the simplex, the absolute value of Mathlib's signed67distance to a facet is the ordinary distance to the affine span of that facet. -/68theorem abs_signedInfDist_eq_infDist (s : Simplex ℝ P n) (i : Fin (n + 1)) {p : P}69 (hp : p ∈ affineSpan ℝ (Set.range s.points)) :70 |s.signedInfDist i p| = infDist p (facetSpan s i) := by71 rw [← dist_orthogonalProjection_eq_infDist]72 refine AffineSubspace.abs_signedInfDist_eq_dist_of_mem_affineSpan_insert ?_73 rwa [affineSpan_insert_affineSpan, Set.insert_image_compl_eq_range]7475/-- The signed distance to the facet opposite `i` is the barycentric weight of `i` times the76height from `i`. (A restatement of `Affine.Simplex.signedInfDist_affineCombination` in terms of77`Affine.Simplex.height`.) -/78theorem signedInfDist_affineCombination (s : Simplex ℝ P n) (i : Fin (n + 1))79 {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) :80 s.signedInfDist i (univ.affineCombination ℝ s.points w) = w i * s.height i := by81 rw [s.signedInfDist_affineCombination i hw, Simplex.height, Simplex.altitudeFoot,82 dist_eq_norm_vsub]8384/-- **Key lemma.** The distance from a point to the facet opposite vertex `i` is the absolute85value of its barycentric weight at `i`, times the height from `i`. -/86theorem infDist_facetSpan_affineCombination (s : Simplex ℝ P n) (i : Fin (n + 1))87 {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) :88 infDist (univ.affineCombination ℝ s.points w) (facetSpan s i) = |w i| * s.height i := by89 rw [← abs_signedInfDist_eq_infDist s i (affineCombination_mem_affineSpan hw _),90 signedInfDist_affineCombination s i hw, abs_mul, abs_of_pos (s.height_pos i)]9192/-- **Viviani for every simplex.** For a point of the closed simplex, with barycentric weights93`w`, the distances to the facets add up to the weighted average `∑ i, w i * s.height i` of the94heights. -/95theorem sum_infDist_facetSpan_affineCombination (s : Simplex ℝ P n)96 {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) (h0 : ∀ j, 0 ≤ w j) :97 ∑ i, infDist (univ.affineCombination ℝ s.points w) (facetSpan s i) =98 ∑ i, w i * s.height i := by99 refine sum_congr rfl fun i _ ↦ ?_100 rw [infDist_facetSpan_affineCombination s i hw, abs_of_nonneg (h0 i)]101102omit [NeZero n] in103/-- A point of the closed simplex is an affine combination of the vertices with nonnegative104weights. -/105theorem exists_weights_of_mem_closedInterior {s : Simplex ℝ P n} {p : P}106 (hp : p ∈ s.closedInterior) :107 ∃ w : Fin (n + 1) → ℝ, ∑ j, w j = 1 ∧ (∀ j, 0 ≤ w j) ∧108 p = univ.affineCombination ℝ s.points w := by109 obtain ⟨w, hw, hw01, rfl⟩ := hp110 exact ⟨w, hw, fun j ↦ (hw01 j).1, rfl⟩111112113/-! ### The distance sum on the closed simplex -/114115/-- At a vertex, the distance sum is the height from that vertex. -/116theorem sum_infDist_facetSpan_point (s : Simplex ℝ P n) (j : Fin (n + 1)) :117 ∑ i, infDist (s.points j) (facetSpan s i) = s.height j := by118 have h := sum_infDist_facetSpan_affineCombination s (w := Pi.single j 1)119 (Fintype.sum_pi_single' _ _) (fun k ↦ by by_cases hk : k = j <;> simp [hk])120 rw [univ.affineCombination_piSingle ℝ s.points (mem_univ j)] at h121 simpa [Pi.single_apply] using h122123/-- On the closed simplex the distance sum is at least the smallest height. -/124theorem le_sum_infDist_facetSpan {s : Simplex ℝ P n} {p : P} (hp : p ∈ s.closedInterior)125 {m : ℝ} (hm : ∀ i, m ≤ s.height i) : m ≤ ∑ i, infDist p (facetSpan s i) := by126 obtain ⟨w, hw, h0, rfl⟩ := exists_weights_of_mem_closedInterior hp127 rw [sum_infDist_facetSpan_affineCombination s hw h0]128 calc m = ∑ i, w i * m := by rw [← sum_mul, hw, one_mul]129 _ ≤ ∑ i, w i * s.height i := sum_le_sum fun i _ ↦ mul_le_mul_of_nonneg_left (hm i) (h0 i)130131/-- On the closed simplex the distance sum is at most the largest height. -/132theorem sum_infDist_facetSpan_le {s : Simplex ℝ P n} {p : P} (hp : p ∈ s.closedInterior)133 {M : ℝ} (hM : ∀ i, s.height i ≤ M) : ∑ i, infDist p (facetSpan s i) ≤ M := by134 obtain ⟨w, hw, h0, rfl⟩ := exists_weights_of_mem_closedInterior hp135 rw [sum_infDist_facetSpan_affineCombination s hw h0]136 calc ∑ i, w i * s.height i ≤ ∑ i, w i * M :=137 sum_le_sum fun i _ ↦ mul_le_mul_of_nonneg_left (hM i) (h0 i)138 _ = M := by rw [← sum_mul, hw, one_mul]139140/-- If all heights of a simplex equal `h`, the distances from any point of the closed simplex to141the facets add up to `h`. -/142theorem sum_infDist_facetSpan_eq_of_height_eq {s : Simplex ℝ P n} {h : ℝ}143 (hh : ∀ i, s.height i = h) {p : P} (hp : p ∈ s.closedInterior) :144 ∑ i, infDist p (facetSpan s i) = h :=145 le_antisymm (sum_infDist_facetSpan_le hp fun i ↦ (hh i).le)146 (le_sum_infDist_facetSpan hp fun i ↦ (hh i).ge)147148/-- **Viviani and its converse, in every dimension.** The distance sum is constant on the closed149simplex if and only if all heights of the simplex are equal. -/150theorem sum_infDist_facetSpan_const_iff (s : Simplex ℝ P n) :151 (∃ c : ℝ, ∀ p ∈ s.closedInterior, ∑ i, infDist p (facetSpan s i) = c) ↔152 ∀ i j, s.height i = s.height j := by153 constructor154 · rintro ⟨c, hc⟩ i j155 rw [← sum_infDist_facetSpan_point s i, ← sum_infDist_facetSpan_point s j,156 hc _ (s.point_mem_closedInterior i), hc _ (s.point_mem_closedInterior j)]157 · intro h158 exact ⟨s.height 0, fun p hp ↦ sum_infDist_facetSpan_eq_of_height_eq (fun i ↦ h i 0) hp⟩159160/-! ### Signed distances: the identity on the whole affine span -/161162/-- **Ternary-plot reading.** Signed distance to a facet, measured in units of the corresponding163height, is the barycentric coordinate. -/164theorem signedInfDist_div_height (s : Simplex ℝ P n) (i : Fin (n + 1))165 {w : Fin (n + 1) → ℝ} (hw : ∑ j, w j = 1) :166 s.signedInfDist i (univ.affineCombination ℝ s.points w) / s.height i = w i := by167 rw [signedInfDist_affineCombination s i hw, mul_div_assoc, div_self (s.height_pos i).ne',168 mul_one]169170/-- For every point of the affine span of a simplex, the signed distances to the facets, each171measured in units of the corresponding height, add up to `1`. -/172theorem sum_signedInfDist_div_height (s : Simplex ℝ P n) {p : P}173 (hp : p ∈ affineSpan ℝ (Set.range s.points)) :174 ∑ i, s.signedInfDist i p / s.height i = 1 := by175 obtain ⟨w, hw, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype hp176 simp_rw [signedInfDist_div_height s _ hw, hw]177178/-- **Signed Viviani on the whole plane.** If all heights equal `h`, the signed distances to the179facets add up to `h` at every point of the affine span of the simplex, inside or outside. -/180theorem sum_signedInfDist_eq_of_height_eq {s : Simplex ℝ P n} {h : ℝ}181 (hh : ∀ i, s.height i = h) {p : P} (hp : p ∈ affineSpan ℝ (Set.range s.points)) :182 ∑ i, s.signedInfDist i p = h := by183 obtain ⟨w, hw, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype hp184 simp_rw [signedInfDist_affineCombination s _ hw, hh, ← sum_mul, hw, one_mul]185186/-! ### Volumes: the content of the classical area proof -/187188/-- Height times the volume of the opposite facet is `n` times the volume of the simplex. -/189theorem height_mul_volume_faceOpposite (s : Simplex ℝ P n) (i : Fin (n + 1)) :190 s.height i * (s.faceOpposite i).volume = n * s.volume := by191 have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.2 (NeZero.ne n)192 rw [s.volume_eq i]193 field_simp194195/-- **The area identity.** For a point `p` of the closed simplex,196`∑ i, dist(p, facet i) * vol(facet i) = n * vol(s)`. For a triangle this says that the three197triangles with apex `p` over the three sides have total area equal to the area of the triangle,198which is the heart of the classical proof of Viviani's theorem. -/199theorem sum_infDist_facetSpan_mul_volume {s : Simplex ℝ P n} {p : P}200 (hp : p ∈ s.closedInterior) :201 ∑ i, infDist p (facetSpan s i) * (s.faceOpposite i).volume = n * s.volume := by202 obtain ⟨w, hw, h0, rfl⟩ := exists_weights_of_mem_closedInterior hp203 have : ∀ i, infDist (univ.affineCombination ℝ s.points w) (facetSpan s i) *204 (s.faceOpposite i).volume = w i * (n * s.volume) := fun i ↦ by205 rw [infDist_facetSpan_affineCombination s i hw, abs_of_nonneg (h0 i), mul_assoc,206 height_mul_volume_faceOpposite]207 simp_rw [this, ← sum_mul, hw, one_mul]208209/-- Two heights of a simplex are equal exactly when the opposite facets have equal volume. -/210theorem height_eq_iff_volume_faceOpposite_eq (s : Simplex ℝ P n) (i j : Fin (n + 1)) :211 s.height i = s.height j ↔ (s.faceOpposite i).volume = (s.faceOpposite j).volume := by212 have h := (height_mul_volume_faceOpposite s i).trans (height_mul_volume_faceOpposite s j).symm213 have hi := s.height_pos i214 have hj := s.height_pos j215 have vi := (s.faceOpposite i).volume_pos216 have vj := (s.faceOpposite j).volume_pos217 constructor218 · intro e219 rw [e] at h220 exact mul_left_cancel₀ hj.ne' h221 · intro e222 rw [e] at h223 exact mul_right_cancel₀ vj.ne' h224225/-- The distance sum is constant on the closed simplex if and only if all facets have the same226volume. -/227theorem sum_infDist_facetSpan_const_iff_volume (s : Simplex ℝ P n) :228 (∃ c : ℝ, ∀ p ∈ s.closedInterior, ∑ i, infDist p (facetSpan s i) = c) ↔229 ∀ i j, (s.faceOpposite i).volume = (s.faceOpposite j).volume := by230 simp_rw [sum_infDist_facetSpan_const_iff, height_eq_iff_volume_faceOpposite_eq]231232/-! ### Regular simplices -/233234/-- All heights of a regular simplex (in the sense of `Affine.Simplex.Regular`) are equal. -/235theorem height_eq_of_regular {s : Simplex ℝ P n} (hs : s.Regular) (i j : Fin (n + 1)) :236 s.height i = s.height j := by237 obtain ⟨x, hx⟩ := hs (Equiv.swap i j)238 let f : P →ᵃⁱ[ℝ] P := x.toRealAffineIsometryEquiv.toAffineIsometry239 have e : s.map f.toAffineMap f.injective = s.reindex (Equiv.swap i j).symm := by240 ext k241 change x (s.points k) = s.points ((Equiv.swap i j).symm k)242 simpa using (congrFun hx k).symm243 have h := s.height_map f i244 rw [e, Simplex.height_reindex] at h245 simpa using h.symm246247end Viviani
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Viviani.Simplex910/-!11# Triangles: Viviani's theorem and its converse1213For a triangle `t` in a Euclidean affine space:1415* `equilateral_iff_height_eq`: `t` is equilateral if and only if its three heights are equal16 (because height times opposite side is twice the area).17* `viviani_triangle`: in an equilateral triangle the distances from a point of the closed18 triangle to the three side lines add up to `√3 / 2` times the side.19* `sum_infDist_const_iff_equilateral`: the distance sum is constant on the closed triangle20 **if and only if** the triangle is equilateral.2122Readers who prefer plain points `A B C` to `Affine.Triangle` should look at23`Viviani/Classical.lean`.24-/2526public section2728open EuclideanGeometry Metric Affine Finset2930namespace Viviani3132variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P]33 [NormedAddTorsor V P]3435/-- Height times the opposite side is twice the area. -/36theorem height_mul_dist (t : Triangle ℝ P) {i₁ i₂ i₃ : Fin 3} (h₁₂ : i₁ ≠ i₂) (h₁₃ : i₁ ≠ i₃)37 (h₂₃ : i₂ ≠ i₃) : t.height i₁ * dist (t.points i₂) (t.points i₃) = 2 * t.volume := by38 rw [t.volume_eq_height_mul h₁₂ h₁₃ h₂₃]39 ring4041/-- A triangle is equilateral if and only if its three heights are equal. -/42theorem equilateral_iff_height_eq (t : Triangle ℝ P) :43 t.Equilateral ↔ ∀ i j, t.height i = t.height j := by44 have h0 := height_mul_dist t (i₁ := 0) (i₂ := 1) (i₃ := 2) (by decide) (by decide) (by decide)45 have h1 := height_mul_dist t (i₁ := 1) (i₂ := 0) (i₃ := 2) (by decide) (by decide) (by decide)46 have h2 := height_mul_dist t (i₁ := 2) (i₂ := 0) (i₃ := 1) (by decide) (by decide) (by decide)47 have p0 := t.height_pos 048 rw [Triangle.equilateral_iff_dist_01_eq_02_and_dist_01_eq_12]49 constructor50 · rintro ⟨e1, e2⟩51 rw [← e2] at h052 rw [← e1] at h153 have d : 0 < dist (t.points 0) (t.points 1) :=54 dist_pos.2 (t.independent.injective.ne (by decide))55 have a : t.height 0 = t.height 1 := mul_right_cancel₀ d.ne' (h0.trans h1.symm)56 have b : t.height 0 = t.height 2 := mul_right_cancel₀ d.ne' (h0.trans h2.symm)57 intro i j58 fin_cases i <;> fin_cases j <;> simp [← a, ← b]59 · intro h60 rw [h 1 0] at h161 rw [h 2 0] at h262 exact ⟨mul_left_cancel₀ p0.ne' (h2.trans h1.symm), mul_left_cancel₀ p0.ne' (h2.trans h0.symm)⟩6364/-- Every height of an equilateral triangle is `√3 / 2` times the side. -/65theorem height_eq_of_equilateral {t : Triangle ℝ P} (ht : t.Equilateral) (i : Fin 3) :66 t.height i = Real.sqrt 3 / 2 * dist (t.points 0) (t.points 1) := by67 obtain ⟨j, k, hij, hik, hjk⟩ : ∃ j k : Fin 3, i ≠ j ∧ i ≠ k ∧ j ≠ k := by68 fin_cases i <;> decide69 rw [t.height_eq_sin_mul_dist hij hik hjk, ht.angle_eq_pi_div_three hij hik hjk,70 Real.sin_pi_div_three, ht.dist_eq hij (show (0 : Fin 3) ≠ 1 by decide)]7172/-- **Viviani's theorem** for an `Affine.Triangle`: in an equilateral triangle, the distances73from any point of the closed triangle to the three side lines add up to `√3 / 2` times the side. -/74theorem viviani_triangle {t : Triangle ℝ P} (ht : t.Equilateral) {p : P}75 (hp : p ∈ t.closedInterior) :76 ∑ i, infDist p (facetSpan t i) = Real.sqrt 3 / 2 * dist (t.points 0) (t.points 1) :=77 sum_infDist_facetSpan_eq_of_height_eq (height_eq_of_equilateral ht) hp7879/-- **Viviani's theorem with its converse.** The sum of the distances to the three side lines80is the same at every point of a closed triangle if and only if the triangle is equilateral. -/81theorem sum_infDist_const_iff_equilateral (t : Triangle ℝ P) :82 (∃ c : ℝ, ∀ p ∈ t.closedInterior, ∑ i, infDist p (facetSpan t i) = c) ↔ t.Equilateral := by83 rw [sum_infDist_facetSpan_const_iff, equilateral_iff_height_eq]8485end Viviani
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Viviani.Simplex910/-!11# Equilateral simplices in every dimension1213In a simplex of dimension `n ≥ 1` all of whose edges have length `r`, every height equals14`√((n + 1) / (2 n)) * r`: `√3 / 2 * r` for a triangle, `√(2 / 3) * r` for a tetrahedron.1516* `height_sq_of_dist_eq`, `height_of_dist_eq`: the height.17* `viviani_simplex`: the distances from a point of the closed simplex to the `n + 1` facets add18 up to `√((n + 1) / (2 n)) * r`.19* `viviani_simplex_signed`: the same for signed distances, at every point of the affine span.2021The proof of the height formula does not locate the foot of the altitude. It writes the foot22as an affine combination of the other vertices with unknown weights, and Mathlib's identity23`⟪pᵢ - pⱼ, pᵢ - foot⟫ = height²` then forces all the weights to be equal.24-/2526public section2728open EuclideanGeometry Metric Affine Finset29open scoped RealInnerProductSpace3031namespace Viviani3233variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P]34 [NormedAddTorsor V P] {n : ℕ}3536/-- Inner products of edge vectors at a vertex of a simplex whose edges all have length `r`. -/37theorem inner_vsub_vsub_of_dist_eq {s : Simplex ℝ P n} {r : ℝ}38 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {i j k : Fin (n + 1)}39 (hij : i ≠ j) (hik : i ≠ k) :40 ⟪s.points i -ᵥ s.points j, s.points i -ᵥ s.points k⟫ =41 if k = j then r ^ 2 else r ^ 2 / 2 := by42 rw [real_inner_eq_norm_mul_self_add_norm_mul_self_sub_norm_sub_mul_self_div_two,43 vsub_sub_vsub_cancel_left, ← dist_eq_norm_vsub, ← dist_eq_norm_vsub, ← dist_eq_norm_vsub,44 hr _ _ hij, hr _ _ hik]45 split_ifs with h46 · subst h47 simp48 ring49 · rw [hr _ _ h]50 ring5152variable [NeZero n]5354/-- In a simplex of dimension `n` whose edges all have length `r`, each height squared is55`(n + 1) / (2 n) * r ^ 2`. -/56theorem height_sq_of_dist_eq {s : Simplex ℝ P n} {r : ℝ}57 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) (i : Fin (n + 1)) :58 s.height i ^ 2 = (n + 1) / (2 * n) * r ^ 2 := by59 have hmem : s.altitudeFoot i ∈ affineSpan ℝ (Set.range s.points) :=60 s.altitudeFoot_mem_affineSpan i61 obtain ⟨w, hw, hwf⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype hmem62 have hwi : w i = 0 :=63 (Simplex.affineCombination_mem_affineSpan_faceOpposite_iff (s := s) hw).164 (hwf ▸ s.altitudeFoot_mem_affineSpan_faceOpposite i)65 have key : ∀ j, j ≠ i → s.height i ^ 2 = r ^ 2 / 2 * (1 + w j) := by66 intro j hj67 rw [← s.inner_vsub_vsub_altitudeFoot_eq_height_sq (Ne.symm hj), hwf,68 ← sum_smul_const_vsub_eq_vsub_affineCombination _ _ _ _ hw, inner_sum]69 have hk : ∀ k, ⟪s.points i -ᵥ s.points j, w k • (s.points i -ᵥ s.points k)⟫ =70 r ^ 2 / 2 * w k + if k = j then r ^ 2 / 2 * w j else 0 := by71 intro k72 rw [inner_smul_right]73 by_cases hki : k = i74 · subst hki75 simp [hwi, Ne.symm hj]76 · rw [inner_vsub_vsub_of_dist_eq hr (Ne.symm hj) (Ne.symm hki)]77 split_ifs with h78 · subst h; ring79 · ring80 simp_rw [hk, sum_add_distrib, ← mul_sum, hw, sum_ite_eq', mem_univ, ite_true]81 ring82 have hsum : ∑ j ∈ univ.erase i, w j = 1 := by83 rw [← hw, ← add_sum_erase _ _ (mem_univ i), hwi, zero_add]84 have h2 : ∑ j ∈ univ.erase i, s.height i ^ 2 = ∑ j ∈ univ.erase i, r ^ 2 / 2 * (1 + w j) :=85 sum_congr rfl fun j hj ↦ key j (ne_of_mem_erase hj)86 have hn : (n : ℝ) ≠ 0 := Nat.cast_ne_zero.2 (NeZero.ne n)87 rw [sum_const, card_erase_of_mem (mem_univ i), card_univ, Fintype.card_fin,88 ← mul_sum, sum_add_distrib, hsum, sum_const, card_erase_of_mem (mem_univ i), card_univ,89 Fintype.card_fin] at h290 simp only [Nat.add_sub_cancel, nsmul_eq_mul, mul_one] at h291 field_simp92 linarith9394/-- In a simplex of dimension `n` whose edges all have length `r`, each height is95`√((n + 1) / (2 n)) * r`. -/96theorem height_of_dist_eq {s : Simplex ℝ P n} {r : ℝ}97 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) (i : Fin (n + 1)) :98 s.height i = Real.sqrt ((n + 1) / (2 * n)) * r := by99 have hr0 : 0 ≤ r := by100 have : Nontrivial (Fin (n + 1)) := Fin.nontrivial_iff_two_le.2 (by have := NeZero.pos n; omega)101 obtain ⟨j, hj⟩ := exists_ne i102 rw [← hr j i hj]103 exact dist_nonneg104 rw [← Real.sqrt_sq (s.height_pos i).le, height_sq_of_dist_eq hr i,105 Real.sqrt_mul (by positivity), Real.sqrt_sq hr0]106107/-- **Viviani's theorem in every dimension.** In a simplex of dimension `n` whose edges all108have length `r`, the distances from any point of the closed simplex to the `n + 1` facets add up109to `√((n + 1) / (2 n)) * r`. -/110theorem viviani_simplex {s : Simplex ℝ P n} {r : ℝ}111 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {p : P}112 (hp : p ∈ s.closedInterior) :113 ∑ i, infDist p (facetSpan s i) = Real.sqrt ((n + 1) / (2 * n)) * r :=114 sum_infDist_facetSpan_eq_of_height_eq (height_of_dist_eq hr) hp115116/-- The signed form of `viviani_simplex`, valid at every point of the affine span. -/117theorem viviani_simplex_signed {s : Simplex ℝ P n} {r : ℝ}118 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {p : P}119 (hp : p ∈ affineSpan ℝ (Set.range s.points)) :120 ∑ i, s.signedInfDist i p = Real.sqrt ((n + 1) / (2 * n)) * r :=121 sum_signedInfDist_eq_of_height_eq (height_of_dist_eq hr) hp122123end Viviani
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Viviani.Regular910/-!11# Tetrahedra1213* `viviani_tetrahedron`: in a regular tetrahedron of edge `r`, the distances from a point of the14 closed tetrahedron to the four face planes add up to `√(2 / 3) * r`.15* `viviani_disphenoid`: regularity is not needed. If opposite edges are equal in pairs (a16 *disphenoid*; its four faces are congruent triangles) the distance sum is still constant.17 The proof is Heron's formula: the four faces have the same side lengths, hence the same area,18 hence (by `height_eq_iff_volume_faceOpposite_eq`) the four heights are equal.1920So in three dimensions the converse of Viviani's theorem does not single out the regular21tetrahedron: by `sum_infDist_facetSpan_const_iff_volume` the distance sum is constant exactly22when the four faces have equal area.23-/2425public section2627open EuclideanGeometry Metric Affine Finset2829namespace Viviani3031variable {V P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V] [MetricSpace P]32 [NormedAddTorsor V P]3334set_option linter.unusedSimpArgs false in -- each of the four cases uses a different subset35/-- All four faces of a disphenoid have the same area (by Heron's formula). -/36theorem volume_faceOpposite_sq_of_disphenoid (s : Simplex ℝ P 3)37 (h₁ : dist (s.points 0) (s.points 1) = dist (s.points 2) (s.points 3))38 (h₂ : dist (s.points 0) (s.points 2) = dist (s.points 1) (s.points 3))39 (h₃ : dist (s.points 0) (s.points 3) = dist (s.points 1) (s.points 2)) (i : Fin 4) :40 (s.faceOpposite i).volume ^ 2 = (s.faceOpposite 3).volume ^ 2 := by41 have H : ∀ t : Triangle ℝ P, t.volume ^ 2 = _ :=42 fun t ↦ t.volume_sq_eq_mul_sub_mul_sub_mul_sub (i₁ := 0) (i₂ := 1) (i₃ := 2)43 (by decide) (by decide) (by decide)44 rw [H (s.faceOpposite i), H (s.faceOpposite 3)]45 simp only [Simplex.faceOpposite_point_eq_point_succAbove]46 have c₁ : dist (s.points 1) (s.points 0) = dist (s.points 2) (s.points 3) := by47 rw [dist_comm, h₁]48 have c₂ : dist (s.points 2) (s.points 0) = dist (s.points 1) (s.points 3) := by49 rw [dist_comm, h₂]50 have c₃ : dist (s.points 3) (s.points 0) = dist (s.points 1) (s.points 2) := by51 rw [dist_comm, h₃]52 have c₄ : dist (s.points 2) (s.points 1) = dist (s.points 1) (s.points 2) := dist_comm _ _53 have c₅ : dist (s.points 3) (s.points 1) = dist (s.points 1) (s.points 3) := dist_comm _ _54 have c₆ : dist (s.points 3) (s.points 2) = dist (s.points 2) (s.points 3) := dist_comm _ _55 fin_cases i <;> simp [Fin.succAbove, h₁, h₂, h₃, c₁, c₂, c₃, c₄, c₅, c₆] <;> ring565758/-- The four heights of a disphenoid are equal. -/59theorem height_eq_of_disphenoid (s : Simplex ℝ P 3)60 (h₁ : dist (s.points 0) (s.points 1) = dist (s.points 2) (s.points 3))61 (h₂ : dist (s.points 0) (s.points 2) = dist (s.points 1) (s.points 3))62 (h₃ : dist (s.points 0) (s.points 3) = dist (s.points 1) (s.points 2)) (i j : Fin 4) :63 s.height i = s.height j := by64 rw [height_eq_iff_volume_faceOpposite_eq]65 have hi := volume_faceOpposite_sq_of_disphenoid s h₁ h₂ h₃ i66 have hj := volume_faceOpposite_sq_of_disphenoid s h₁ h₂ h₃ j67 exact (pow_left_inj₀ (s.faceOpposite i).volume_pos.le (s.faceOpposite j).volume_pos.le68 two_ne_zero).1 (hi.trans hj.symm)6970/-- **Viviani's theorem for a disphenoid.** If the opposite edges of a tetrahedron are equal in71pairs, the distances from any point of the closed tetrahedron to the four face planes add up to72the height. -/73theorem viviani_disphenoid (s : Simplex ℝ P 3)74 (h₁ : dist (s.points 0) (s.points 1) = dist (s.points 2) (s.points 3))75 (h₂ : dist (s.points 0) (s.points 2) = dist (s.points 1) (s.points 3))76 (h₃ : dist (s.points 0) (s.points 3) = dist (s.points 1) (s.points 2)) {p : P}77 (hp : p ∈ s.closedInterior) :78 ∑ i, infDist p (facetSpan s i) = s.height 0 :=79 sum_infDist_facetSpan_eq_of_height_eq (fun i ↦ height_eq_of_disphenoid s h₁ h₂ h₃ i 0) hp8081/-- **Viviani's theorem for the regular tetrahedron.** The distances from any point of a closed82regular tetrahedron of edge `r` to the four face planes add up to `√(2 / 3) * r`. -/83theorem viviani_tetrahedron {s : Simplex ℝ P 3} {r : ℝ}84 (hr : ∀ i j, i ≠ j → dist (s.points i) (s.points j) = r) {p : P}85 (hp : p ∈ s.closedInterior) :86 ∑ i, infDist p (facetSpan s i) = Real.sqrt (2 / 3) * r := by87 rw [viviani_simplex hr hp]88 norm_num8990end Viviani
1/-2Copyright (c) 2026 redzwn. All rights reserved.3Released under Apache 2.0 license as described in the file LICENSE.4Authors: redzwn, Claude (Anthropic)5-/6module78public import Viviani.Classical910/-!11# Sanity checks1213The hypotheses of the theorems are satisfied by a genuine triangle, and the theorems say what14they should on it. In the plane `EuclideanSpace ℝ (Fin 2)` take1516 `A₀ = (0, 0)`, `B₀ = (2, 0)`, `C₀ = (1, √3)`,1718an equilateral triangle of side `2`. Viviani's theorem then says that the three distances from19any point of the triangle add up to `√3`.20-/2122public section2324open Metric2526namespace Viviani.Examples2728/-- The point `(0, 0)`. -/29noncomputable def A₀ : EuclideanSpace ℝ (Fin 2) := !₂[0, 0]3031/-- The point `(2, 0)`. -/32noncomputable def B₀ : EuclideanSpace ℝ (Fin 2) := !₂[2, 0]3334/-- The point `(1, √3)`. -/35noncomputable def C₀ : EuclideanSpace ℝ (Fin 2) := !₂[1, Real.sqrt 3]3637private lemma sqrt_four : Real.sqrt 4 = 2 := by38 rw [show (4 : ℝ) = 2 ^ 2 by norm_num, Real.sqrt_sq (by norm_num)]3940theorem dist_A₀_B₀ : dist A₀ B₀ = 2 := by41 rw [EuclideanSpace.dist_eq]42 simp [A₀, B₀, Fin.sum_univ_two, Real.dist_eq]4344theorem dist_B₀_C₀ : dist B₀ C₀ = 2 := by45 rw [EuclideanSpace.dist_eq]46 simp [B₀, C₀, Fin.sum_univ_two, Real.dist_eq, sq_abs]47 norm_num [sqrt_four]4849theorem dist_C₀_A₀ : dist C₀ A₀ = 2 := by50 rw [EuclideanSpace.dist_eq]51 simp [C₀, A₀, Fin.sum_univ_two, Real.dist_eq, sq_abs]52 norm_num [sqrt_four]5354/-- The three points form a genuine triangle. -/55theorem affineIndependent : AffineIndependent ℝ ![A₀, B₀, C₀] :=56 affineIndependent_of_dist_eq (dist_A₀_B₀.trans dist_B₀_C₀.symm)57 (dist_C₀_A₀.trans dist_B₀_C₀.symm) (by rw [← dist_pos, dist_A₀_B₀]; norm_num)5859/-- Viviani's theorem on the concrete triangle: the three distances add up to `√3`. -/60theorem sum_eq_sqrt_three {P : EuclideanSpace ℝ (Fin 2)} (hP : P ∈ convexHull ℝ {A₀, B₀, C₀}) :61 infDist P (segment ℝ B₀ C₀) + infDist P (segment ℝ C₀ A₀) + infDist P (segment ℝ A₀ B₀) =62 Real.sqrt 3 := by63 rw [viviani_segment (dist_A₀_B₀.trans dist_B₀_C₀.symm) (dist_C₀_A₀.trans dist_B₀_C₀.symm) hP,64 dist_B₀_C₀]65 ring6667end Viviani.Examples
Chapter 8
How it was checked
A proof assistant is only as trustworthy as the program that checks it, and a check is only worth something if it can fail. So the proofs were checked several ways, including by a checker written by different people in a different language, and the checking itself was tested by planting defects.
What is still taken on trust: the three standard axioms, the correctness of Lean’s kernel and of the second checker, and that the Lean statements say what the English says. The last is why every statement on this page is shown next to its plain reading.
| Check | What it does | Result |
|---|---|---|
| Lean 4 compiler and kernel | Elaborates every file and checks every proof term in Lean’s small trusted . Warnings are treated as failures. | No errors, no warnings |
| Axiom audit | Walks over every declaration in the project, including the automatically generated ones, and lists the axioms each depends on. | 85 declarations; only propext, Classical.choice, Quot.sound |
| Statement fingerprints | Prints the 22 main statements as Lean understands them and compares them with a recorded copy, so a statement cannot be weakened unnoticed. | Identical |
| leanchecker | Replays each compiled module through the kernel again, from the stored proof terms. | All 6 modules passed |
| nanoda | An independent type checker for Lean, written in Rust. It re-checks the main theorems together with every declaration they depend on, back to the axioms. | 48,926 declarations, no errors |
| Tamper tests | The same nanoda setup is handed a proof that uses sorry, one that adds its own axiom, and one that uses native_decide; and the fingerprint check is handed a swapped statement. | All four rejected |
| A concrete triangle | The triangle (0, 0), (2, 0), (1, √3) is shown to satisfy the hypotheses, and the theorem is applied to it: the sum is √3. | Proved |
- Lean
- 4.36.0-rc2 (commit
b35cb10) - Mathlib
7bcc5e95ffc0d80d94adc6f17715f224d59d209b- Checked on
- 13 November 2026
Full output of scripts/verify.sh, including the SHA-256 of every source file
== 1. Versions
Lean (version 4.36.0-rc2, x86_64-unknown-linux-gnu, commit b35cb10cf2ced51a2fd1880cfdbf75c71ab7a35a, Release)
mathlib 7bcc5e95ffc0d80d94adc6f17715f224d59d209b
== 2. Build (the Lean kernel checks every proof); warnings count as failures
Build completed successfully (18252 jobs).
== 3. No sorry, no axiom, no native_decide in the sources
none
== 4. Axiom audit of every declaration in the project
audit: 85 declarations (81 theorems) in the Viviani modules; every one depends only on axioms among [propext, Classical.choice, Quot.sound]
'Viviani.viviani' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.viviani_segment' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.viviani_iff' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.sum_infDist_eq_of_affineIndependent' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.sum_infDist_facetSpan_const_iff_volume' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.viviani_simplex' depends on axioms: [propext, Classical.choice, Quot.sound]
'Viviani.viviani_disphenoid' depends on axioms: [propext, Classical.choice, Quot.sound]
== 5. Statements are the ones on record (scripts/statements.expected)
identical (22 statements)
== 6. leanchecker: replay every module of the project in a fresh kernel
ok Viviani.Simplex
ok Viviani.Triangle
ok Viviani.Regular
ok Viviani.Tetrahedron
ok Viviani.Classical
ok Viviani.Examples
== 7. nanoda: an independent type checker (written in Rust) re-checks the main theorems
and everything they depend on, down to the axioms
Checked 48926 declarations with no typechecker errors, 1 pretty printer errors: [
"Unable to print axioms",
]
== 8. Tamper tests: each planted defect must be rejected
rejected Sorry: unpermitted axiom "sorryAx"
rejected Axiom: unpermitted axiom "convenient"
rejected NativeDecide: unpermitted axiom "bad._native.native_decide.ax_1_1"
rejected Statement: a swapped statement no longer matches scripts/statements.expected
== 9. SHA-256 of the sources
8d3c5c5c21a5d29f021eda2aeacdf5b087471cb7eb917bf4aeaca04819e3ca76 Viviani.lean
839274d92ecaef5603f923718e0067361ba8bafd8093e86432596044f541dcc0 Viviani/Classical.lean
fd801abdcd57b22cc5d62c47796c31c8f3175b089b7ccf1b23a6240677d2a0db Viviani/Examples.lean
7ec0b75d97d13054389128f7a31e2fbaf85cfb24aa4cf2b8945c5019dfefda79 Viviani/Regular.lean
e65fa22f838b02ff3ead1916f89d476c423ea23da79abc460fa11d90e779ab10 Viviani/Simplex.lean
af88584e6020ca5d64c454466737e8d62fd009cb6776dc8b1949d150bf7940a9 Viviani/Tetrahedron.lean
54b32367cd9efa717de663ad71c6da9f9c364bc8d57498528489fbfa72136a88 Viviani/Triangle.lean
1b0e320aacfb6e4d7c3364280fe758f7e3a49d3506735cfd57f883fac1576701 lakefile.toml
fbc69eb9d6e8aa76067e87309793e962e664e83650bd65a26f0880e0cb073dc9 lake-manifest.json
11c697aebe5b539d74f5069e497a588e7dfab1c4eeb74313f28357e351c55ca3 lean-toolchain
ALL CHECKS PASSED
nanoda prints “1 pretty printer errors: Unable to print axioms”. That message is about displaying the axioms at the end of the run, not about checking; the count of type-checking errors is zero.
Chapter 9
Galileo’s last student
Vincenzo Viviani was a Florentine mathematician who, as a teenager, became Galileo’s assistant in his final years. He spent much of his life protecting Galileo’s legacy, and is remembered in geometry for a curve and for this theorem.
Viviani stated the theorem in the seventeenth century. It has kept attracting new proofs and extensions ever since, most of them short enough to fit on a page.
- 1622Born in Florence on 5 April.
- 1639Moves into Galileo’s house at Arcetri to assist him. Torricelli joins them in 1641.
- 1642Galileo dies. Viviani was his last disciple.
- 1647After Torricelli’s death, is appointed to Torricelli’s position at the Accademia dell’Arte del Disegno in Florence.
- 1654Writes his account of Galileo’s life, which stays unpublished until 1717.
- 1655Edits the first edition of Galileo’s collected works, completed in 1656.
- 1703Dies in Florence on 22 September, aged 81.
- ·Three centuries later, the theorem is still finding new proofs.
- 2003Hans Samelson publishes a proof without words using vectors.
- 2005Ken-Ichiroh Kawasaki gives a proof without words using only rotations. With Yagi and Yanagawa he treats the regular tetrahedron the same year.
- 2006Zhibo Chen and Tian Liang prove the converse: a triangle with constant distance sum is equilateral.
- 2010Elias Abboud describes what happens in every convex polygon: the sum is constant along parallel segments.
- 2012Li Zhou generalizes the idea to “Viviani polytopes”.
- 2014Burkard Polster shows that in any triangle the sum lies between the shortest and the tallest height.
- 2026The theorem is formalized in Lean, more than once: in the lean-genius collection (June), by Joshua Bald (September), and in the development on this page (November).
Check it yourself
You don’t need to trust this page. Any computer can re-run the check; no special hardware is needed.
- Install Lean with elan, the Lean version manager.
- Clone the repository.
git clone https://github.com/redzwn/viviani-lean cd viviani-lean - Fetch Mathlib’s prebuilt files and build. A successful build prints no errors and no warnings.
lake exe cache get lake build - Run every check described above, tamper tests included.
scripts/verify.sh - Or ask Lean one question directly: what does the main theorem depend on?
echo 'import Viviani #print axioms Viviani.viviani_segment' > Check.lean lake env lean Check.lean
The source files are also available here: Classical.lean, Simplex.lean, Triangle.lean, Regular.lean, Tetrahedron.lean, Examples.lean.