1-- Kuhn simplices through the face opposite the bottom vertex: rotating the2-- permutation moves the bottom vertex to the top, and the two simplices3-- through such a face are the original one and its rotation4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.FixedPoint.KuhnRotation10import KuhnFaces11import Nat.CyclicShift1213def rotated (n upd : Set) : Set:= compose(upd, rotation(n), n, n, n)1415theorem rotated.bijective (n : Nat) (upd : Set)16requires updBijective : BijectiveBetween(upd, n, n)17shows BijectiveBetween(rotated(n, upd), n, n)18proof19have turn := rotation.bijective at n20unfold rotated21exact compose.bijective with turn, updBijective2223theorem rotated.value (n : Nat) (upd l : Set)24requires updBijective : BijectiveBetween(upd, n, n)25requires hl : l ∈ n26shows rotated(n, upd)(l) = upd(rotation(n)(l))27proof28have updFunction := Injective.function with (Bijective.injective with updBijective)29have turn := rotation.function at n with*30have same : rotated(n, upd)(l) = compose(upd, rotation(n), n, n, n)(l)31unfold rotated32rfl33rw same34exact compose.apply with turn, updFunction, hl3536theorem rotated.step (n : Nat) (upd l : Set)37requires updBijective : BijectiveBetween(upd, n, n)38requires hl : l ∈ n39requires hs : succ(l) ∈ n40shows rotated(n, upd)(l) = upd(succ(l))41proof42have value := rotated.value with updBijective, hl43have turn := rotation.step with hl, hs44rw [value, turn]45rfl4647theorem rotated.wrap (n : Nat) (upd l : Set)48requires updBijective : BijectiveBetween(upd, n, n)49requires hl : l ∈ n50requires top : succ(l) = n51shows rotated(n, upd)(l) = upd(0)52proof53have value := rotated.value with updBijective, hl54have turn := rotation.wrap with hl, top55rw [value, turn]56rfl5758-- The first k rotated coordinates are coordinates 1, …, k of the original.59theorem raised.rotated (n : Nat) (upd k : Set)60requires updBijective : BijectiveBetween(upd, n, n)61requires hk : k ∈ n62shows raised(n, upd, succ(k)) = insert(upd(0), raised(n, rotated(n, upd), k))63proof64have updFunction := Injective.function with (Bijective.injective with updBijective)65have kNat := natural.member at n with hk66have kLess := Less.of_membership with hk67have kInside :=Ordinal.member_subset with (Ordinal.natural at n), hk68have zeroLess : 0 < n byarith69have zeroIn := membership.of_Less with zeroLess70ext y71constructor72 . intro hy73obtain⟨inN, m, hm, shape⟩:= raised.membership.forward with hy74have mNat := natural.member at succ(k) with hm75have mLess := Less.of_membership with hm76havesplit:= natural.zero_or_successor with mNat77apply insert.membership.backward78casessplitwith zero | successor79 . left80rw [shape, zero]81rfl82 . right83obtain⟨l, lNat, mEq⟩:= successor84rw mEq at mLess85have lLess : l < k byarith86have lIn := membership.of_Less with lLess87have lInN := kInside with l, lIn88have sLess : succ(l) < n byarith89have sIn := membership.of_Less with sLess90have value := rotated.step with updBijective, lInN, sIn91apply raised.membership.backward92constructor93 . exact inN94 . witness l95constructor96 . exact lIn97 . rw [shape, mEq, value]98rfl99 . intro hy100havesplit:= insert.membership.forward with hy101casessplitwith same | below102 . apply raised.membership.backward103constructor104 . rw same105exact FunctionBetween.apply_closed with updFunction, zeroIn106 . witness0107constructor108 . have less : 0 < succ(k) byarith109exact membership.of_Less with less110 . exact same111 . obtain⟨inN, l, hl, shape⟩:= raised.membership.forward with below112have lInN := kInside with l, hl113have lNat := natural.member at n with lInN114have lLess := Less.of_membership with hl115have sLess : succ(l) < n byarith116have sIn := membership.of_Less with sLess117have value := rotated.step with updBijective, lInN, sIn118apply raised.membership.backward119constructor120 . exact inN121 . witness succ(l)122constructor123 . have less : succ(l) < succ(k) byarith124exact membership.of_Less with less125 . rw [shape, value]126rfl127128theorem raised.rotated_fresh (n : Nat) (upd k : Set)129requires updBijective : BijectiveBetween(upd, n, n)130requires hk : k ∈ n131shows¬upd(0) ∈ raised(n, rotated(n, upd), k)132proof133have updInjective := Bijective.injective with updBijective134have kNat := natural.member at n with hk135have kLess := Less.of_membership with hk136have kInside :=Ordinal.member_subset with (Ordinal.natural at n), hk137have zeroLess : 0 < n byarith138have zeroIn := membership.of_Less with zeroLess139intro inside140obtain⟨inN, l, hl, shape⟩:= raised.membership.forward with inside141have lInN := kInside with l, hl142have lNat := natural.member at n with lInN143have lLess := Less.of_membership with hl144have sLess : succ(l) < n byarith145have sIn := membership.of_Less with sLess146have value := rotated.step with updBijective, lInN, sIn147rw value at shape148have same := Injective.apply_cancel with updInjective, zeroIn, sIn, shape149arith150151theorem indicator.insert_fresh (a A j : Set)152requires fresh : ¬a ∈ A153shows indicator(insert(a, A), j) = indicator({a}, j) + indicator(A, j)154proof155have alternatives : j = a ∨ j ≠ a bytauto156cases alternatives with same | different157 . have inInsert : j ∈ insert(a, A)158apply insert.membership.backward159left160exact same161have absent : ¬j ∈ A by rwa same162have one := indicator.member with inInsert163haveself : indicator({a}, j) =1164rw same165exact indicator.singleton_self166have zero := indicator.absent with absent167rw [one, self, zero]168arith169 . have equivalent : j ∈ insert(a, A) ↔ j ∈ A170constructor171 . intro h172havesplit:= insert.membership.forward with h173casessplitwith equal | inside174 . exfalso175exact different with equal176 . exact inside177 . intro h178apply insert.membership.backward179right180exact h181have same := indicator.congr with equivalent182have separated : a ≠ j183intro equal184apply different185 symm186exact equal187have zero := indicator.singleton_other with separated188rw [same, zero]189ring190191-- The rotated simplex, based at the second vertex, lists vertices 1, …, n192-- of the original followed by a new top.193theorem kuhnVertex.rotated (n : Nat) (base upd k : Set)194requires baseFunction : base : n → ℕ195requires updBijective : BijectiveBetween(upd, n, n)196requires hk : k ∈ n197shows kuhnVertex(n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd))(k) = kuhnVertex(n, base, upd)(succ(k))198proof199have updInjective := Bijective.injective with updBijective200have kNat := natural.member at n with hk201have kLess := Less.of_membership with hk202have zeroLess : 0 < n byarith203have positive := membership.of_Less with zeroLess204have oneLess : succ(0) < succ(n) byarith205have oneIn := membership.of_Less with oneLess206have kInSucc := membership.into_successor with hk207have sLess : succ(k) < succ(n) byarith208have sIn := membership.of_Less with sLess209have oneFunction := kuhnVertex.function_on at n, base, upd, succ(0) with baseFunction, oneIn210have leftFunction := kuhnVertex.function_on at n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd), k with oneFunction, kInSucc211have rightFunction := kuhnVertex.function_on at n, base, upd, succ(k) with baseFunction, sIn212apply FunctionBetween.ext with leftFunction, rightFunction213intros j hj214have leftValue := kuhnVertex.coordinate at n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd), k, j with oneFunction, kInSucc, hj215have oneValue := kuhnVertex.coordinate_succ with baseFunction, updInjective, positive, hj216have zeroValue := kuhnVertex.zero_coordinate at n, base, upd, j with baseFunction, hj217have rightValue := kuhnVertex.coordinate at n, base, upd, succ(k), j with baseFunction, sIn, hj218have image := raised.rotated with updBijective, hk219have fresh := raised.rotated_fresh with updBijective, hk220havesplit:= indicator.insert_fresh at upd(0), raised(n, rotated(n, upd), k), j with fresh221have baseNatural := FunctionBetween.apply_closed with baseFunction, hj222have baseNat := nat.omega.backward with baseNatural223rw [leftValue, oneValue, zeroValue, rightValue, image, split]224exact add.associative225226theorem kuhnSimplex.rotated_face (n : Nat) (base upd : Set)227requires baseFunction : base : n → ℕ228requires updBijective : BijectiveBetween(upd, n, n)229requires positive : 0∈ n230shows kuhnSimplex(n, base, upd) ∖ {kuhnVertex(n, base, upd)(0)} =231 kuhnSimplex(n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd)) ∖232 {kuhnVertex(n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd))(n)}233proof234have updInjective := Bijective.injective with updBijective235have rotatedInjective := Bijective.injective with (rotated.bijective with updBijective)236have zeroLess := Less.of_membership with positive237have oneLess : succ(0) < succ(n) byarith238have oneIn := membership.of_Less with oneLess239have oneFunction := kuhnVertex.function_on at n, base, upd, succ(0) with baseFunction, oneIn240have zeroIn := membership.into_successor with positive241have nIn := bootstrap.succ.self at n242ext x243constructor244 . intro hx245have parts := difference.singleton_membership.forward with hx246obtain⟨inSpace, k, hk, shape⟩:= kuhnSimplex.membership.forward with parts.left247have kNat := natural.member at succ(n) with hk248have kLess := Less.of_membership with hk249havesplit:= natural.zero_or_successor with kNat250casessplitwith zero | successor251 . exfalso252apply parts.right253rw [shape, zero]254rfl255 . obtain⟨m, mNat, kEq⟩:= successor256rw kEq at kLess257have mLess : m < n byarith258have mIn := membership.of_Less with mLess259have mInSucc := membership.into_successor with mIn260have agree := kuhnVertex.rotated with baseFunction, updBijective, mIn261apply difference.singleton_membership.backward262constructor263 . rw [shape, kEq, ← agree]264exact kuhnVertex.member with oneFunction, mInSucc265 . intro equal266rw [shape, kEq, ← agree] at equal267have same := kuhnVertex.injective with oneFunction, rotatedInjective, mInSucc, nIn, equal268rw same at mIn269exact membership.irreflexive with mIn270 . intro hx271have parts := difference.singleton_membership.forward with hx272obtain⟨inSpace, m, hm, shape⟩:= kuhnSimplex.membership.forward with parts.left273have mIn : m ∈ n274havesplit:= bootstrap.succ.membership.forward with hm275casessplitwith below | same276 . exact below277 . exfalso278apply parts.right279rw [shape, same]280rfl281have mNat := natural.member at n with mIn282have mLess := Less.of_membership with mIn283have sLess : succ(m) < succ(n) byarith284have sIn := membership.of_Less with sLess285have agree := kuhnVertex.rotated with baseFunction, updBijective, mIn286apply difference.singleton_membership.backward287constructor288 . rw [shape, agree]289exact kuhnVertex.member with baseFunction, sIn290 . intro equal291rw [shape, agree] at equal292have same := kuhnVertex.injective with baseFunction, updInjective, sIn, zeroIn, equal293arith294295-- Away from the bottom vertex, coordinate upd(0) is constant on a simplex.296theorem kuhnSimplex.bottom_face_constant (n : Nat) (base upd x : Set)297requires baseFunction : base : n → ℕ298requires updBijective : BijectiveBetween(upd, n, n)299requires positive : 0∈ n300requires hx : x ∈ kuhnSimplex(n, base, upd) ∖ {kuhnVertex(n, base, upd)(0)}301shows x(upd(0)) = kuhnVertex(n, base, upd)(succ(0))(upd(0))302proof303have updFunction := Injective.function with (Bijective.injective with updBijective)304have jIn := FunctionBetween.apply_closed with updFunction, positive305have zeroLess := Less.of_membership with positive306have oneLess : succ(0) < succ(n) byarith307have oneIn := membership.of_Less with oneLess308have parts := difference.singleton_membership.forward with hx309obtain⟨inSpace, k, hk, shape⟩:= kuhnSimplex.membership.forward with parts.left310have kNat := natural.member at succ(n) with hk311have zeroInK : 0∈ k312havesplit:= natural.zero_or_successor with kNat313casessplitwith zero | successor314 . exfalso315apply parts.right316rw [shape, zero]317rfl318 . obtain⟨m, mNat, kEq⟩:= successor319rw kEq320have less : 0 < succ(m) byarith321exact membership.of_Less with less322have raisedK : upd(0) ∈ raised(n, upd, k)323apply raised.membership.backward324constructor325 . exact jIn326 . witness0327constructor328 . exact zeroInK329 . rfl330have raisedOne : upd(0) ∈ raised(n, upd, succ(0))331apply raised.membership.backward332constructor333 . exact jIn334 . witness0335constructor336 . exact bootstrap.succ.self at0337 . rfl338have valueK := kuhnVertex.coordinate at n, base, upd, k, upd(0) with baseFunction, hk, jIn339have valueOne := kuhnVertex.coordinate at n, base, upd, succ(0), upd(0) with baseFunction, oneIn, jIn340have oneK := indicator.member with raisedK341have oneOne := indicator.member with raisedOne342rw [shape, valueK, valueOne, oneK, oneOne]343rfl344345-- If every vertex but the k-th shares a coordinate value, the k-th vertex is346-- the bottom or the top.347theorem kuhnSimplex.constant_face_index (n : Nat) (base upd k j v : Set)348requires baseFunction : base : n → ℕ349requires updBijective : BijectiveBetween(upd, n, n)350requires hk : k ∈ succ(n)351requires hj : j ∈ n352requires constant : ∀ m ∈ succ(n), m ≠ k → kuhnVertex(n, base, upd)(m)(j) = v353shows k =0∨ k = n354proof355by_contra neither356have notZero : 0≠ k357intro same358apply neither359left360 symm361exact same362have notTop : n ≠ k363intro same364apply neither365right366 symm367exact same368have zeroLess : 0 < succ(n) byarith369have zeroIn := membership.of_Less with zeroLess370have nIn := bootstrap.succ.self at n371have atZero := constant with zeroIn, notZero372have atTop := constant with nIn, notTop373have zeroValue := kuhnVertex.zero_coordinate at n, base, upd, j with baseFunction, hj374have topValue := kuhnVertex.top_coordinate with baseFunction, updBijective, hj375rw zeroValue at atZero376rw topValue at atTop377have baseNatural := FunctionBetween.apply_closed with baseFunction, hj378rw← atZero at atTop379arith380381-- Through the face opposite the bottom vertex there are exactly the original382-- simplex and its rotation.383theorem kuhnSimplex.bottom_face_neighbours (n : Nat) (base upd b2 u2 c : Set)384requires baseFunction : base : n → ℕ385requires updBijective : BijectiveBetween(upd, n, n)386requires f2 : b2 : n → ℕ387requires bij2 : BijectiveBetween(u2, n, n)388requires positive : 0∈ n389requires hc : c ∈ kuhnSimplex(n, b2, u2)390requires same : kuhnSimplex(n, b2, u2) ∖ {c} = kuhnSimplex(n, base, upd) ∖ {kuhnVertex(n, base, upd)(0)}391shows kuhnSimplex(n, b2, u2) = kuhnSimplex(n, base, upd) ∨392 kuhnSimplex(n, b2, u2) = kuhnSimplex(n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd))393proof394have updFunction := Injective.function with (Bijective.injective with updBijective)395have injective2 := Bijective.injective with bij2396have jIn := FunctionBetween.apply_closed with updFunction, positive397have zeroLess := Less.of_membership with positive398have oneLess : succ(0) < succ(n) byarith399have oneIn := membership.of_Less with oneLess400have oneFunction := kuhnVertex.function_on at n, base, upd, succ(0) with baseFunction, oneIn401have rotatedBijective := rotated.bijective with updBijective402obtain⟨inSpace, k, hk, shape⟩:= kuhnSimplex.membership.forward with hc403have constant : ∀ m ∈ succ(n), m ≠ k → kuhnVertex(n, b2, u2)(m)(upd(0)) = kuhnVertex(n, base, upd)(succ(0))(upd(0))404intros m hm different405have vertexMember := kuhnVertex.member at n, b2, u2, m with f2, hm406have inFace : kuhnVertex(n, b2, u2)(m) ∈ kuhnSimplex(n, b2, u2) ∖ {c}407apply difference.singleton_membership.backward408constructor409 . exact vertexMember410 . intro equal411rw shape at equal412have index := kuhnVertex.injective with f2, injective2, hm, hk, equal413exact different with index414rw same at inFace415exact kuhnSimplex.bottom_face_constant with baseFunction, updBijective, positive, inFace416have index := kuhnSimplex.constant_face_index with f2, bij2, hk, jIn, constant417cases index with zero | top418 . left419rw [shape, zero] at same420exact kuhnSimplex.eq_bottom with f2, bij2, baseFunction, updBijective, positive, same421 . right422rw [shape, top] at same423have face := kuhnSimplex.rotated_face with baseFunction, updBijective, positive424rw face at same425exact kuhnSimplex.eq_top with f2, bij2, oneFunction, rotatedBijective, positive, same426427theorem kuhnSimplex.rotated_ne (n : Nat) (base upd : Set)428requires baseFunction : base : n → ℕ429requires updBijective : BijectiveBetween(upd, n, n)430requires positive : 0∈ n431shows kuhnSimplex(n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd)) ≠ kuhnSimplex(n, base, upd)432proof433have updInjective := Bijective.injective with updBijective434have rotatedInjective := Bijective.injective with (rotated.bijective with updBijective)435have zeroLess := Less.of_membership with positive436have oneLess : succ(0) < succ(n) byarith437have oneIn := membership.of_Less with oneLess438have oneFunction := kuhnVertex.function_on at n, base, upd, succ(0) with baseFunction, oneIn439have nIn := bootstrap.succ.self at n440intro equal441have topMember := kuhnVertex.member at n, kuhnVertex(n, base, upd)(succ(0)), rotated(n, upd), n with oneFunction, nIn442rw equal at topMember443have bounds := kuhnSimplex.sum_bounds with baseFunction, updInjective, topMember444have topSum := kuhnVertex.sum with oneFunction, rotatedInjective, nIn445have oneSum := kuhnVertex.sum with baseFunction, updInjective, oneIn446have upper := bounds.right447rw [topSum, oneSum] at upper448have baseWeights : ∀ j ∈ n, base(j) ∈ ℕ449intros j hj450exact FunctionBetween.apply_closed with baseFunction, hj451have baseSum := natSum.natural with (Finite.natural at n), baseWeights452arith453