1-- The zero vector, negation, scaling, and cross product of ℝ³ as graphs on2-- 3, with Lagrange's identity and the linearity of the matrix action3-- 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.Vectors39import Rotation.Generators1011-- The zero vector.12defgraph zeroVector (i ∈3) : ℝ := (0:R)13proof14typecheck1516instance zeroVector.vector17shows zeroVector : 3→ ℝ18proof19exact zeroVector.function2021theorem zeroVector.entry (i : Set)22requires index : i ∈323shows zeroVector(i) = (0:R)24proof25exact zeroVector.value with index2627instance zeroVector.space28shows zeroVector ∈ ℝ³29proof30exact ℝ³.of_function with zeroVector.vector3132theorem zeroVector.normSq33shows normSq(zeroVector) = (0:R)34proof35have e0 := zeroVector.entry at0with index.zero_mem36have e1 := zeroVector.entry at1with index.one_mem37have e2 := zeroVector.entry at2with index.two_mem38rw [normSq.value, e0, e1, e2]39ring4041-- The negation of a vector.42defgraph vectorNeg (v : Set) (i ∈3) : ℝ := -(v(i):R)43requires vector : v : 3→ ℝ44proof45have component := vector.component_real at v, i with vector, member46typecheck4748instance vectorNeg.vector (v : Set)49requires vector : v : 3→ ℝ50shows vectorNeg(v) : 3→ ℝ51proof52exact vectorNeg.function with vector5354theorem vectorNeg.entry (v i : Set)55requires vector : v : 3→ ℝ56requires index : i ∈357shows vectorNeg(v)(i) = -(v(i):R)58proof59exact vectorNeg.value with vector, index6061instance vectorNeg.space (v : Set)62requires member : v ∈ ℝ³63shows vectorNeg(v) ∈ ℝ³64proof65have vector := ℝ³.function with member66have image := vectorNeg.vector with vector67exact ℝ³.of_function with image6869theorem vectorNeg.normSq (v : Set)70requires vector : v : 3→ ℝ71shows normSq(vectorNeg(v)) = normSq(v)72proof73have v0 := vector.component_real at v, 0with vector, index.zero_mem74have v1 := vector.component_real at v, 1with vector, index.one_mem75have v2 := vector.component_real at v, 2with vector, index.two_mem76have e0 := vectorNeg.entry at v, 0with vector, index.zero_mem77have e1 := vectorNeg.entry at v, 1with vector, index.one_mem78have e2 := vectorNeg.entry at v, 2with vector, index.two_mem79rw [normSq.value, normSq.value, e0, e1, e2]80ring8182theorem vectorNeg.sphere (v : Set)83requires member : v ∈ S²84shows vectorNeg(v) ∈ S²85proof86obtain⟨inSpace, unit⟩:= S².membership.forward with member87have vector := ℝ³.function with inSpace88have negIn := vectorNeg.space with inSpace89have norm := vectorNeg.normSq with vector90have negUnit : normSq(vectorNeg(v)) = (1:R)91rw norm92exact unit93exact S².membership.backward with⟨negIn, negUnit⟩9495theorem vectorNeg.involutive (v : Set)96requires vector : v : 3→ ℝ97shows vectorNeg(vectorNeg(v)) = v98proof99have inner := vectorNeg.vector with vector100have outer := vectorNeg.vector with inner101apply FunctionBetween.ext with outer, vector102intros i hi103have component := vector.component_real at v, i with vector, hi104have first := vectorNeg.entry at vectorNeg(v), i with inner, hi105have second := vectorNeg.entry at v, i with vector, hi106rw [first, second]107ring108109-- Scaling a vector by a real.110defgraph vectorScale (c v : Set) (i ∈3) : ℝ := (c:R) * (v(i):R)111requires scalar : c ∈ ℝ112requires vector : v : 3→ ℝ113proof114have component := vector.component_real at v, i with vector, member115typecheck116117instance vectorScale.vector (c v : Set)118requires scalar : c ∈ ℝ119requires vector : v : 3→ ℝ120shows vectorScale(c, v) : 3→ ℝ121proof122exact vectorScale.function with scalar, vector123124theorem vectorScale.entry (c v i : Set)125requires scalar : c ∈ ℝ126requires vector : v : 3→ ℝ127requires index : i ∈3128shows vectorScale(c, v)(i) = (c:R) * (v(i):R)129proof130exact vectorScale.value with scalar, vector, index131132instance vectorScale.space (c v : Set)133requires scalar : c ∈ ℝ134requires member : v ∈ ℝ³135shows vectorScale(c, v) ∈ ℝ³136proof137have vector := ℝ³.function with member138have image := vectorScale.vector with scalar, vector139exact ℝ³.of_function with image140141theorem vectorScale.normSq (c v : Set)142requires scalar : c ∈ ℝ143requires vector : v : 3→ ℝ144shows normSq(vectorScale(c, v)) = (c:R) * (c:R) * normSq(v)145proof146have v0 := vector.component_real at v, 0with vector, index.zero_mem147have v1 := vector.component_real at v, 1with vector, index.one_mem148have v2 := vector.component_real at v, 2with vector, index.two_mem149have e0 := vectorScale.entry at c, v, 0with scalar, vector, index.zero_mem150have e1 := vectorScale.entry at c, v, 1with scalar, vector, index.one_mem151have e2 := vectorScale.entry at c, v, 2with scalar, vector, index.two_mem152rw [normSq.value, normSq.value, e0, e1, e2]153ring154155theorem vectorScale.one (v : Set)156requires vector : v : 3→ ℝ157shows vectorScale((1:R), v) = v158proof159have oneReal : (1:R) ∈ ℝ bytypecheck160have scaled := vectorScale.vector with oneReal, vector161apply FunctionBetween.ext with scaled, vector162intros i hi163have component := vector.component_real at v, i with vector, hi164have entry := vectorScale.entry at (1:R), v, i with oneReal, vector, hi165rw entry166ring167168theorem vectorScale.neg_one (v : Set)169requires vector : v : 3→ ℝ170shows vectorScale(-(1:R), v) = vectorNeg(v)171proof172have negReal : -(1:R) ∈ ℝ bytypecheck173have scaled := vectorScale.vector with negReal, vector174have negated := vectorNeg.vector with vector175apply FunctionBetween.ext with scaled, negated176intros i hi177have component := vector.component_real at v, i with vector, hi178have entry := vectorScale.entry at -(1:R), v, i with negReal, vector, hi179have negEntry := vectorNeg.entry at v, i with vector, hi180rw [entry, negEntry]181ring182183-- The matrix action is linear: it commutes with negation and scaling.184theorem matrixVector.neg (M : realRing ^ 3×3) (v : Set)185requires vector : v : 3→ ℝ186shows M · vectorNeg(v) = vectorNeg(M · v)187proof188have negated := vectorNeg.vector with vector189haveleft:= matrixVector.vector at M, vectorNeg(v) with*190have image := matrixVector.vector at M, v with*191haveright:= vectorNeg.vector with image192apply FunctionBetween.ext withleft, right193intros i hi194obtain⟨m0, m1, m2⟩:= Matrix.row_real at M, i with hi195have v0 := vector.component_real at v, 0with vector, index.zero_mem196have v1 := vector.component_real at v, 1with vector, index.one_mem197have v2 := vector.component_real at v, 2with vector, index.two_mem198have leftEntry := matrixVector.entry at M, vectorNeg(v), i with*199have rightEntry := vectorNeg.entry at M · v, i with image, hi200have inner := matrixVector.entry at M, v, i with*201have e0 := vectorNeg.entry at v, 0with vector, index.zero_mem202have e1 := vectorNeg.entry at v, 1with vector, index.one_mem203have e2 := vectorNeg.entry at v, 2with vector, index.two_mem204rw [leftEntry, rightEntry, inner, e0, e1, e2]205ring206207theorem matrixVector.scale (M : realRing ^ 3×3) (c v : Set)208requires scalar : c ∈ ℝ209requires vector : v : 3→ ℝ210shows M · vectorScale(c, v) = vectorScale(c, M · v)211proof212have scaled := vectorScale.vector with scalar, vector213haveleft:= matrixVector.vector at M, vectorScale(c, v) with*214have image := matrixVector.vector at M, v with*215haveright:= vectorScale.vector with scalar, image216apply FunctionBetween.ext withleft, right217intros i hi218obtain⟨m0, m1, m2⟩:= Matrix.row_real at M, i with hi219have v0 := vector.component_real at v, 0with vector, index.zero_mem220have v1 := vector.component_real at v, 1with vector, index.one_mem221have v2 := vector.component_real at v, 2with vector, index.two_mem222have leftEntry := matrixVector.entry at M, vectorScale(c, v), i with*223have rightEntry := vectorScale.entry at c, M · v, i with scalar, image, hi224have inner := matrixVector.entry at M, v, i with*225have e0 := vectorScale.entry at c, v, 0with scalar, vector, index.zero_mem226have e1 := vectorScale.entry at c, v, 1with scalar, vector, index.one_mem227have e2 := vectorScale.entry at c, v, 2with scalar, vector, index.two_mem228rw [leftEntry, rightEntry, inner, e0, e1, e2]229ring230231-- The cross product u × v, by its three coordinates.232defgraph cross (u v : Set) (i ∈3) : ℝ :=233 pick3((u(1):R) * (v(2):R) - (u(2):R) * (v(1):R),234 (u(2):R) * (v(0):R) - (u(0):R) * (v(2):R),235 (u(0):R) * (v(1):R) - (u(1):R) * (v(0):R), i)236requires vectorU : u : 3→ ℝ237requires vectorV : v : 3→ ℝ238proof239have u0 := vector.component_real at u, 0with vectorU, index.zero_mem240have u1 := vector.component_real at u, 1with vectorU, index.one_mem241have u2 := vector.component_real at u, 2with vectorU, index.two_mem242have v0 := vector.component_real at v, 0with vectorV, index.zero_mem243have v1 := vector.component_real at v, 1with vectorV, index.one_mem244have v2 := vector.component_real at v, 2with vectorV, index.two_mem245have first : (u(1):R) * (v(2):R) - (u(2):R) * (v(1):R) ∈ ℝ bytypecheck246have second : (u(2):R) * (v(0):R) - (u(0):R) * (v(2):R) ∈ ℝ bytypecheck247have third : (u(0):R) * (v(1):R) - (u(1):R) * (v(0):R) ∈ ℝ bytypecheck248exact pick3.real with first, second, third, member249250instance cross.vector (u v : Set)251requires vectorU : u : 3→ ℝ252requires vectorV : v : 3→ ℝ253shows cross(u, v) : 3→ ℝ254proof255exact cross.function with vectorU, vectorV256257theorem cross.zero (u v : Set)258requires vectorU : u : 3→ ℝ259requires vectorV : v : 3→ ℝ260shows cross(u, v)(0) = (u(1):R) * (v(2):R) - (u(2):R) * (v(1):R)261proof262have value := cross.value at u, v, 0with vectorU, vectorV, index.zero_mem263rw [value, pick3.zero]264rfl265266theorem cross.one (u v : Set)267requires vectorU : u : 3→ ℝ268requires vectorV : v : 3→ ℝ269shows cross(u, v)(1) = (u(2):R) * (v(0):R) - (u(0):R) * (v(2):R)270proof271have value := cross.value at u, v, 1with vectorU, vectorV, index.one_mem272rw [value, pick3.one]273rfl274275theorem cross.two (u v : Set)276requires vectorU : u : 3→ ℝ277requires vectorV : v : 3→ ℝ278shows cross(u, v)(2) = (u(0):R) * (v(1):R) - (u(1):R) * (v(0):R)279proof280have value := cross.value at u, v, 2with vectorU, vectorV, index.two_mem281rw [value, pick3.two]282rfl283284instance cross.space (u v : Set)285requires memberU : u ∈ ℝ³286requires memberV : v ∈ ℝ³287shows cross(u, v) ∈ ℝ³288proof289have vectorU := ℝ³.function with memberU290have vectorV := ℝ³.function with memberV291have image := cross.vector with vectorU, vectorV292exact ℝ³.of_function with image293294-- The cross product is orthogonal to both factors, and Lagrange's295-- identity gives its squared norm.296theorem cross.dot_left (u v : Set)297requires vectorU : u : 3→ ℝ298requires vectorV : v : 3→ ℝ299shows dot3(u, cross(u, v)) = (0:R)300proof301have u0 := vector.component_real at u, 0with vectorU, index.zero_mem302have u1 := vector.component_real at u, 1with vectorU, index.one_mem303have u2 := vector.component_real at u, 2with vectorU, index.two_mem304have v0 := vector.component_real at v, 0with vectorV, index.zero_mem305have v1 := vector.component_real at v, 1with vectorV, index.one_mem306have v2 := vector.component_real at v, 2with vectorV, index.two_mem307have c0 := cross.zero with vectorU, vectorV308have c1 := cross.one with vectorU, vectorV309have c2 := cross.two with vectorU, vectorV310rw [dot3.value, c0, c1, c2]311ring312313theorem cross.dot_right (u v : Set)314requires vectorU : u : 3→ ℝ315requires vectorV : v : 3→ ℝ316shows dot3(v, cross(u, v)) = (0:R)317proof318have u0 := vector.component_real at u, 0with vectorU, index.zero_mem319have u1 := vector.component_real at u, 1with vectorU, index.one_mem320have u2 := vector.component_real at u, 2with vectorU, index.two_mem321have v0 := vector.component_real at v, 0with vectorV, index.zero_mem322have v1 := vector.component_real at v, 1with vectorV, index.one_mem323have v2 := vector.component_real at v, 2with vectorV, index.two_mem324have c0 := cross.zero with vectorU, vectorV325have c1 := cross.one with vectorU, vectorV326have c2 := cross.two with vectorU, vectorV327rw [dot3.value, c0, c1, c2]328ring329330theorem cross.normSq (u v : Set)331requires vectorU : u : 3→ ℝ332requires vectorV : v : 3→ ℝ333shows normSq(cross(u, v)) = normSq(u) * normSq(v) - dot3(u, v) * dot3(u, v)334proof335have u0 := vector.component_real at u, 0with vectorU, index.zero_mem336have u1 := vector.component_real at u, 1with vectorU, index.one_mem337have u2 := vector.component_real at u, 2with vectorU, index.two_mem338have v0 := vector.component_real at v, 0with vectorV, index.zero_mem339have v1 := vector.component_real at v, 1with vectorV, index.one_mem340have v2 := vector.component_real at v, 2with vectorV, index.two_mem341have c0 := cross.zero with vectorU, vectorV342have c1 := cross.one with vectorU, vectorV343have c2 := cross.two with vectorU, vectorV344rw [normSq.value, normSq.value, normSq.value, dot3.value, c0, c1, c2]345ring346