Imports
/- Copyright (c) 2026 Terence Rokop. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Terence Rokop -/ module public import Geb.Mathlib.Data.PFunctor.Presheaf.Finite.Basic public import Mathlib.CategoryTheory.Category.Preorder public import Mathlib.Order.Fin.Basic

Prototype: the dependent-type universe as a presheaf polynomial functor on the walking arrow

Throwaway exploration, not upstream-eligible content. Every declaration here is Classical.choice-free.

The universe of [GhaniNordvallForsbergMalatesta2015] (Examples 2.5 and 2.6), whose inductive-recursive presentation is Geb/Mathlib/Data/PFunctor/IndRec/Universes.lean, presented instead as a presheaf polynomial endofunctor on presheaves over the walking arrow. A presheaf Z on the walking arrow is a map Z 1 → Z 0, read as the typing map from terms to codes; the inductive-recursive decoding IRdecode u is replaced by the fiber of that map over u, so the decoding is no longer a function into a universe of types and the functor is an endofunctor on a genuine presheaf category rather than on a slice of Type over itself.

The base category is the preorder on Fin 2: the object 0 indexes the codes, the object 1 the terms, and the unique morphism 0 ⟶ 1 induces the typing map Z 1 → Z 0. Shapes and directions are both finite, so the functor is a FinitePresheafPFunctor and membership in the fibers of its W-type is decided by FinitePresheafPFunctor.memWBool.

What the finite arities do and do not capture

A shape's arity is a presheaf on the base fixed independently of Z, so a code node has a fixed finite number of subcode and subterm slots. The inductive-recursive sigma code carries a whole family IRdecode u → Code indexed by the decoding of its first argument; no arity independent of Z indexes that family. What the finite arities do capture is the family evaluated at a term: the pair shape has two term directions and two code directions, and the naturality of a direction assignment (PresheafDomPFunctorData.IsNatural) forces the code direction to receive the code of the term direction. Hereditary naturality of a W-tree is therefore exactly the statement that the tree is well-typed, and the presheaf restriction of a term tree is its type. The sigma and pi shapes accordingly denote the non-dependent product and function space, and pi has no introduction shape, a lambda being an infinitary constructor in the same way.

Main definitions

    Shp — the shapes: the base code, the two binder codes, the base-type terms, and the pair term.

    PairDir / Dir — the direction types: the two components of a pair term and their two codes, and the direction type of each shape.

    rDir / qShp — the direction-input and shape-output maps.

    codeDir / codeShp / reindexDir — the code direction of a direction, the code shape of a shape, and the inclusion of a code shape's directions into the directions of the shapes restricting to it.

    dirTo / restrShp / reindexTo — the direction restriction, shape restriction, and arity reindexing, as bare functions of the target index.

    universeData / universeFunctor / finiteUniverse — the operations, the functor with its seven functor laws, and the functor bundled with its finiteness evidence.

References

    [GhaniNordvallForsbergMalatesta2015]

    [HancockMcBrideGhaniMalatestaAltenkirch2013]

    [Weber2007]

Tags

prototype, inductive-recursive, inductive-inductive, presheaf, universe, walking arrow, parametric right adjoint

@[expose] public sectionopen CategoryTheorynamespace GebProto.PresheafUniverse

Choice-free FinEnum evidence

A choice-free FinEnum (Fin 2) for the base category's objects, from the identity equivalence rather than FinEnum.fin (which routes through Classical.choice).

instance finEnumFin2 : FinEnum (Fin 2) where card := 2 equiv := Equiv.refl (Fin 2) decEq := inferInstance

A choice-free FinEnum (PLift p) for a decidable proposition p: one element when p holds, none otherwise.

instance finEnumPLift {p : Prop} [Decidable p] : FinEnum (PLift p) := if h : p then { card := 1 equiv := { toFun := fun _ 0 invFun := fun _ h left_inv := fun x p:Propinst✝:Decidable ph:px:PLift p(fun x { down := h }) ((fun x 0) x) = x p:Propinst✝:Decidable ph:pdown✝:p(fun x { down := h }) ((fun x 0) { down := down✝ }) = { down := down✝ }; All goals completed! 🐙 right_inv := fun i Fin.cases rfl (fun j j.elim0) i } decEq := fun a b isTrue (p:Propinst✝:Decidable ph:pa:PLift pb:PLift pa = b p:Propinst✝:Decidable ph:pb:PLift pdown✝:p{ down := down✝ } = b; p:Propinst✝:Decidable ph:pdown✝¹:pdown✝:p{ down := down✝¹ } = { down := down✝ }; All goals completed! 🐙) } else { card := 0 equiv := { toFun := fun x absurd x.down h invFun := fun i i.elim0 left_inv := fun x absurd x.down h right_inv := fun i i.elim0 } decEq := fun a _ absurd a.down h }

A choice-free FinEnum of a hom-set of the base category, stated at the head and delegating to the ULift/PLift enumeration.

instance finEnumHom (i i' : Fin 2) : FinEnum (i' i) := inferInstanceAs (FinEnum (ULift (PLift (i' i))))

A choice-free FinEnum Empty, supplying the direction enumeration of the leaf shapes.

instance finEnumEmpty : FinEnum Empty where card := 0 equiv := { toFun := fun x x.elim invFun := fun i i.elim0 left_inv := fun x x.elim right_inv := fun i i.elim0 } decEq := fun x x.elim

A choice-free FinEnum Bool, supplying the direction enumeration of the binder shapes.

instance finEnumBool : FinEnum Bool where card := 2 equiv := { toFun := fun b if b then 1 else 0 invFun := fun i i = 1 left_inv := fun b b:Bool(fun i decide (i = 1)) ((fun b if b = true then 1 else 0) b) = b (fun i decide (i = 1)) ((fun b if b = true then 1 else 0) false) = false(fun i decide (i = 1)) ((fun b if b = true then 1 else 0) true) = true (fun i decide (i = 1)) ((fun b if b = true then 1 else 0) false) = false(fun i decide (i = 1)) ((fun b if b = true then 1 else 0) true) = true All goals completed! 🐙 right_inv := fun i Fin.cases rfl (Fin.cases rfl fun j j.elim0) i } decEq := inferInstance

Shapes and directions

The shapes: the base-type code base, the two binder codes sigma and pi, one term shape lit b per element of the base type, and the pair term pair.

The code of the base type.

The dependent-sum former, degenerating to the product; see the module docstring.

The dependent-product former, degenerating to the function space.

The term of the base type at b.

The introduction form of sigma.

inductive Shp where | base | sigma | pi | lit (b : Bool) | pair deriving DecidableEq

The directions of the pair shape: the two component terms and their two codes.

The i-th component term.

The code of the i-th component term.

inductive PairDir where | tm (i : Bool) | ty (i : Bool) deriving DecidableEq

A choice-free FinEnum Shp: the six shapes mapped to Fin 6.

instance finEnumShp : FinEnum Shp where card := 6 equiv := { toFun := fun s match s with | .base => 0 | .sigma => 1 | .pi => 2 | .lit false => 3 | .lit true => 4 | .pair => 5 invFun := fun i match i with | 0 => .base | 1 => .sigma | 2 => .pi | 3 => .lit false | 4 => .lit true | 5 => .pair left_inv := fun s s:Shp(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) s) = s cases s with b:Bool(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) (Shp.lit b)) = Shp.lit b (fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) (Shp.lit false)) = Shp.lit false(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) (Shp.lit true)) = Shp.lit true (fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) (Shp.lit false)) = Shp.lit false(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) (Shp.lit true)) = Shp.lit true All goals completed! 🐙 (fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) Shp.pair) = Shp.pair(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) Shp.pi) = Shp.pi(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) Shp.sigma) = Shp.sigma(fun i match i with | 0 => Shp.base | 1 => Shp.sigma | 2 => Shp.pi | 3 => Shp.lit false | 4 => Shp.lit true | 5 => Shp.pair) ((fun s match s with | Shp.base => 0 | Shp.sigma => 1 | Shp.pi => 2 | Shp.lit false => 3 | Shp.lit true => 4 | Shp.pair => 5) Shp.base) = Shp.base All goals completed! 🐙 right_inv := fun i Fin.cases rfl (Fin.cases rfl (Fin.cases rfl (Fin.cases rfl (Fin.cases rfl (Fin.cases rfl fun j j.elim0))))) i } decEq := inferInstance

A choice-free FinEnum PairDir: the four directions of pair mapped to Fin 4.

instance finEnumPairDir : FinEnum PairDir where card := 4 equiv := { toFun := fun d match d with | .tm false => 0 | .tm true => 1 | .ty false => 2 | .ty true => 3 invFun := fun i match i with | 0 => .tm false | 1 => .tm true | 2 => .ty false | 3 => .ty true left_inv := fun d d:PairDir(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) d) = d cases d with b:Bool(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.tm b)) = PairDir.tm b (fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.tm false)) = PairDir.tm false(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.tm true)) = PairDir.tm true (fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.tm false)) = PairDir.tm false(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.tm true)) = PairDir.tm true All goals completed! 🐙 b:Bool(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.ty b)) = PairDir.ty b (fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.ty false)) = PairDir.ty false(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.ty true)) = PairDir.ty true (fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.ty false)) = PairDir.ty false(fun i match i with | 0 => PairDir.tm false | 1 => PairDir.tm true | 2 => PairDir.ty false | 3 => PairDir.ty true) ((fun d match d with | PairDir.tm false => 0 | PairDir.tm true => 1 | PairDir.ty false => 2 | PairDir.ty true => 3) (PairDir.ty true)) = PairDir.ty true All goals completed! 🐙 right_inv := fun i Fin.cases rfl (Fin.cases rfl (Fin.cases rfl (Fin.cases rfl fun j j.elim0))) i } decEq := inferInstance

The direction type of each shape: the binder codes have their two subcodes, the pair term has its two components and their codes, and the remaining shapes are leaves.

@[reducible] def Dir : Shp Type | .base => Empty | .sigma => Bool | .pi => Bool | .lit _ => Empty | .pair => PairDir

The direction-input map: a subcode direction lies over the code object 0, a component-term direction over the term object 1.

def rDir : (a : Shp) Dir a Fin 2 | .base, d => d.elim | .sigma, _ => 0 | .pi, _ => 0 | .lit _, d => d.elim | .pair, .tm _ => 1 | .pair, .ty _ => 0

The shape-output map: the code shapes lie over the code object 0, the term shapes over the term object 1.

def qShp : Shp Fin 2 | .base => 0 | .sigma => 0 | .pi => 0 | .lit _ => 1 | .pair => 1

The restriction data

The code direction of a direction: the direction over the code object that a direction restricts to. The identity on directions already over the code object.

def codeDir : (a : Shp) Dir a Dir a | .base, d => d.elim | .sigma, d => d | .pi, d => d | .lit _, d => d.elim | .pair, .tm i => .ty i | .pair, .ty i => .ty i

The code shape of a shape: the shape over the code object that a shape restricts to, that is, the type of a term shape. The identity on code shapes.

def codeShp : Shp Shp | .base => .base | .sigma => .sigma | .pi => .pi | .lit _ => .base | .pair => .sigma

The inclusion of a code shape's directions into the directions of the shapes restricting to it: the subcodes of a sigma are the codes of a pair's components.

def reindexDir : (a : Shp) Dir (codeShp a) Dir a | .base, d => d | .sigma, d => d | .pi, d => d | .lit _, d => d.elim | .pair, i => .ty i

The direction restriction to a target index: the identity at the term object, codeDir at the code object.

def dirTo : Fin 2 (a : Shp) Dir a Dir a | 0, a, d => codeDir a d | 1, _, d => d

The shape restriction to a target index: the identity at the term object, codeShp at the code object.

def restrShp : Fin 2 Shp Shp | 0, a => codeShp a | 1, a => a

The arity reindexing along a restriction to a target index: the identity at the term object, reindexDir at the code object.

def reindexTo : (j : Fin 2) (a : Shp) Dir (restrShp j a) Dir a | 0, a, d => reindexDir a d | 1, _, d => d

The laws of the restriction data

A code direction lies over the code object.

theorem rDir_codeDir : (a : Shp) (d : Dir a), rDir a (codeDir a d) = 0 | .base, d => d.elim | .sigma, _ => rfl | .pi, _ => rfl | .lit _, d => d.elim | .pair, .tm _ => rfl | .pair, .ty _ => rfl

A direction already over the code object is its own code direction.

theorem codeDir_self : (a : Shp) (d : Dir a), rDir a d = 0 codeDir a d = d | .base, d, _ => d.elim | .sigma, _, _ => rfl | .pi, _, _ => rfl | .lit _, d, _ => d.elim | .pair, .tm _, h => absurd h (i✝:Boolh:rDir Shp.pair (PairDir.tm i✝) = 0¬rDir Shp.pair (PairDir.tm i✝) = 0 All goals completed! 🐙) | .pair, .ty _, _ => rfl

The direction restriction lands over its target index.

theorem rDir_dirTo (a : Shp) (i i' : Fin 2) (d : Dir a) (hd : rDir a d = i) (h : i' i) : rDir a (dirTo i' a d) = i' := a:Shpi:Fin 2i':Fin 2d:Dir ahd:rDir a d = ih:i' irDir a (dirTo i' a d) = i' match i' with a:Shpi:Fin 2i':Fin 2d:Dir ahd:rDir a d = ih:0 irDir a (dirTo 0 a d) = 0 All goals completed! 🐙 a:Shpi:Fin 2i':Fin 2d:Dir ahd:rDir a d = ih:1 irDir a (dirTo 1 a d) = 1 a:Shpi:Fin 2i':Fin 2d:Dir ahd:rDir a d = ih:1 ithis:i = 1rDir a (dirTo 1 a d) = 1 All goals completed! 🐙

The direction restriction to a direction's own index is the identity.

theorem dirTo_self (a : Shp) (i : Fin 2) (d : Dir a) (hd : rDir a d = i) : dirTo i a d = d := a:Shpi:Fin 2d:Dir ahd:rDir a d = idirTo i a d = d match i with a:Shpi:Fin 2d:Dir ahd:rDir a d = 0dirTo 0 a d = d All goals completed! 🐙 a:Shpi:Fin 2d:Dir ahd:rDir a d = 1dirTo 1 a d = d All goals completed! 🐙

The direction restriction is functorial in the target index.

theorem dirTo_dirTo (a : Shp) (i' i'' : Fin 2) (h : i'' i') (d : Dir a) : dirTo i'' a (dirTo i' a d) = dirTo i'' a d := a:Shpi':Fin 2i'':Fin 2h:i'' i'd:Dir adirTo i'' a (dirTo i' a d) = dirTo i'' a d match i', i'' with a:Shpi':Fin 2i'':Fin 2d:Dir ah:0 0dirTo 0 a (dirTo 0 a d) = dirTo 0 a d All goals completed! 🐙 a:Shpi':Fin 2i'':Fin 2d:Dir ah:0 1dirTo 0 a (dirTo 1 a d) = dirTo 0 a d All goals completed! 🐙 a:Shpi':Fin 2i'':Fin 2d:Dir ah:1 1dirTo 1 a (dirTo 1 a d) = dirTo 1 a d All goals completed! 🐙 a:Shpi':Fin 2i'':Fin 2d:Dir ah:1 0dirTo 1 a (dirTo 0 a d) = dirTo 1 a d exact absurd h (a:Shpi':Fin 2i'':Fin 2d:Dir ah:1 0¬1 0 All goals completed! 🐙)

A code shape lies over the code object.

theorem qShp_codeShp : a : Shp, qShp (codeShp a) = 0 | .base => rfl | .sigma => rfl | .pi => rfl | .lit _ => rfl | .pair => rfl

A shape already over the code object is its own code shape.

theorem codeShp_self : a : Shp, qShp a = 0 codeShp a = a | .base, _ => rfl | .sigma, _ => rfl | .pi, _ => rfl | .lit _, h => absurd h (b✝:Boolh:qShp (Shp.lit b✝) = 0¬qShp (Shp.lit b✝) = 0 All goals completed! 🐙) | .pair, h => absurd h (h:qShp Shp.pair = 0¬qShp Shp.pair = 0 All goals completed! 🐙)

The shape restriction lands over its target index.

theorem qShp_restrShp (a : Shp) (j' : Fin 2) (h : j' qShp a) : qShp (restrShp j' a) = j' := a:Shpj':Fin 2h:j' qShp aqShp (restrShp j' a) = j' match j' with a:Shpj':Fin 2h:0 qShp aqShp (restrShp 0 a) = 0 All goals completed! 🐙 a:Shpj':Fin 2h:1 qShp aqShp (restrShp 1 a) = 1 a:Shpj':Fin 2h:1 qShp athis:qShp a = 1qShp (restrShp 1 a) = 1 All goals completed! 🐙

The shape restriction to a shape's own index is the identity.

theorem restrShp_self (a : Shp) (j : Fin 2) (h : qShp a = j) : restrShp j a = a := a:Shpj:Fin 2h:qShp a = jrestrShp j a = a match j with a:Shpj:Fin 2h:qShp a = 0restrShp 0 a = a All goals completed! 🐙 a:Shpj:Fin 2h:qShp a = 1restrShp 1 a = a All goals completed! 🐙

The shape restriction is functorial in the target index.

theorem restrShp_restrShp (a : Shp) (j' j'' : Fin 2) (h : j'' j') : restrShp j'' (restrShp j' a) = restrShp j'' a := a:Shpj':Fin 2j'':Fin 2h:j'' j'restrShp j'' (restrShp j' a) = restrShp j'' a match j', j'' with a:Shpj':Fin 2j'':Fin 2h:0 0restrShp 0 (restrShp 0 a) = restrShp 0 a All goals completed! 🐙 a:Shpj':Fin 2j'':Fin 2h:0 1restrShp 0 (restrShp 1 a) = restrShp 0 a All goals completed! 🐙 a:Shpj':Fin 2j'':Fin 2h:1 1restrShp 1 (restrShp 1 a) = restrShp 1 a All goals completed! 🐙 a:Shpj':Fin 2j'':Fin 2h:1 0restrShp 1 (restrShp 0 a) = restrShp 1 a exact absurd h (a:Shpj':Fin 2j'':Fin 2h:1 0¬1 0 All goals completed! 🐙)

The arity reindexing preserves the input index.

theorem rDir_reindexDir : (a : Shp) (d : Dir (codeShp a)), rDir a (reindexDir a d) = rDir (codeShp a) d | .base, d => d.elim | .sigma, _ => rfl | .pi, _ => rfl | .lit _, d => d.elim | .pair, _ => rfl

The arity reindexing to a target index preserves the input index.

theorem rDir_reindexTo (j' : Fin 2) (a : Shp) (d : Dir (restrShp j' a)) : rDir a (reindexTo j' a d) = rDir (restrShp j' a) d := j':Fin 2a:Shpd:Dir (restrShp j' a)rDir a (reindexTo j' a d) = rDir (restrShp j' a) d match j' with j':Fin 2a:Shpd:Dir (restrShp 0 a)rDir a (reindexTo 0 a d) = rDir (restrShp 0 a) d All goals completed! 🐙 j':Fin 2a:Shpd:Dir (restrShp 1 a)rDir a (reindexTo 1 a d) = rDir (restrShp 1 a) d All goals completed! 🐙

The arity reindexing commutes with the direction restriction: it is a morphism of arity presheaves.

theorem dirTo_reindexTo (j' i' : Fin 2) (a : Shp) (d : Dir (restrShp j' a)) : dirTo i' a (reindexTo j' a d) = reindexTo j' a (dirTo i' (restrShp j' a) d) := j':Fin 2i':Fin 2a:Shpd:Dir (restrShp j' a)dirTo i' a (reindexTo j' a d) = reindexTo j' a (dirTo i' (restrShp j' a) d) match j', i' with j':Fin 2i':Fin 2a:Shpx✝:Fin 2d:Dir (restrShp 1 a)dirTo x✝ a (reindexTo 1 a d) = reindexTo 1 a (dirTo x✝ (restrShp 1 a) d) All goals completed! 🐙 j':Fin 2i':Fin 2a:Shpd:Dir (restrShp 0 a)dirTo 1 a (reindexTo 0 a d) = reindexTo 0 a (dirTo 1 (restrShp 0 a) d) All goals completed! 🐙 j':Fin 2i':Fin 2a:Shpd:Dir (restrShp 0 a)dirTo 0 a (reindexTo 0 a d) = reindexTo 0 a (dirTo 0 (restrShp 0 a) d) j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.base)dirTo 0 Shp.base (reindexTo 0 Shp.base d) = reindexTo 0 Shp.base (dirTo 0 (restrShp 0 Shp.base) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.sigma)dirTo 0 Shp.sigma (reindexTo 0 Shp.sigma d) = reindexTo 0 Shp.sigma (dirTo 0 (restrShp 0 Shp.sigma) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.pi)dirTo 0 Shp.pi (reindexTo 0 Shp.pi d) = reindexTo 0 Shp.pi (dirTo 0 (restrShp 0 Shp.pi) d)j':Fin 2i':Fin 2b✝:Boold:Dir (restrShp 0 (Shp.lit b✝))dirTo 0 (Shp.lit b✝) (reindexTo 0 (Shp.lit b✝) d) = reindexTo 0 (Shp.lit b✝) (dirTo 0 (restrShp 0 (Shp.lit b✝)) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.pair)dirTo 0 Shp.pair (reindexTo 0 Shp.pair d) = reindexTo 0 Shp.pair (dirTo 0 (restrShp 0 Shp.pair) d) j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.base)dirTo 0 Shp.base (reindexTo 0 Shp.base d) = reindexTo 0 Shp.base (dirTo 0 (restrShp 0 Shp.base) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.sigma)dirTo 0 Shp.sigma (reindexTo 0 Shp.sigma d) = reindexTo 0 Shp.sigma (dirTo 0 (restrShp 0 Shp.sigma) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.pi)dirTo 0 Shp.pi (reindexTo 0 Shp.pi d) = reindexTo 0 Shp.pi (dirTo 0 (restrShp 0 Shp.pi) d)j':Fin 2i':Fin 2b✝:Boold:Dir (restrShp 0 (Shp.lit b✝))dirTo 0 (Shp.lit b✝) (reindexTo 0 (Shp.lit b✝) d) = reindexTo 0 (Shp.lit b✝) (dirTo 0 (restrShp 0 (Shp.lit b✝)) d)j':Fin 2i':Fin 2d:Dir (restrShp 0 Shp.pair)dirTo 0 Shp.pair (reindexTo 0 Shp.pair d) = reindexTo 0 Shp.pair (dirTo 0 (restrShp 0 Shp.pair) d) All goals completed! 🐙

The arity reindexing along a shape's own index is the identity.

theorem reindexTo_self (j : Fin 2) (a : Shp) (h : qShp a = j) (d : Dir (restrShp j a)) : reindexTo j a d d := j:Fin 2a:Shph:qShp a = jd:Dir (restrShp j a)reindexTo j a d d match j with j:Fin 2a:Shph:qShp a = 0d:Dir (restrShp 0 a)reindexTo 0 a d d j:Fin 2h:qShp Shp.base = 0d:Dir (restrShp 0 Shp.base)reindexTo 0 Shp.base d dj:Fin 2h:qShp Shp.sigma = 0d:Dir (restrShp 0 Shp.sigma)reindexTo 0 Shp.sigma d dj:Fin 2h:qShp Shp.pi = 0d:Dir (restrShp 0 Shp.pi)reindexTo 0 Shp.pi d dj:Fin 2b✝:Boolh:qShp (Shp.lit b✝) = 0d:Dir (restrShp 0 (Shp.lit b✝))reindexTo 0 (Shp.lit b✝) d dj:Fin 2h:qShp Shp.pair = 0d:Dir (restrShp 0 Shp.pair)reindexTo 0 Shp.pair d d j:Fin 2h:qShp Shp.base = 0d:Dir (restrShp 0 Shp.base)reindexTo 0 Shp.base d d All goals completed! 🐙 j:Fin 2h:qShp Shp.sigma = 0d:Dir (restrShp 0 Shp.sigma)reindexTo 0 Shp.sigma d d All goals completed! 🐙 j:Fin 2h:qShp Shp.pi = 0d:Dir (restrShp 0 Shp.pi)reindexTo 0 Shp.pi d d All goals completed! 🐙 j:Fin 2b✝:Boolh:qShp (Shp.lit b✝) = 0d:Dir (restrShp 0 (Shp.lit b✝))reindexTo 0 (Shp.lit b✝) d d exact absurd h (j:Fin 2b✝:Boolh:qShp (Shp.lit b✝) = 0d:Dir (restrShp 0 (Shp.lit b✝))¬qShp (Shp.lit b✝) = 0 All goals completed! 🐙) j:Fin 2h:qShp Shp.pair = 0d:Dir (restrShp 0 Shp.pair)reindexTo 0 Shp.pair d d exact absurd h (j:Fin 2h:qShp Shp.pair = 0d:Dir (restrShp 0 Shp.pair)¬qShp Shp.pair = 0 All goals completed! 🐙) j:Fin 2a:Shph:qShp a = 1d:Dir (restrShp 1 a)reindexTo 1 a d d All goals completed! 🐙

The arity reindexing is functorial in the target index.

theorem reindexTo_reindexTo (j' j'' : Fin 2) (h : j'' j') (a : Shp) (d : Dir (restrShp j'' a)) (e : Dir (restrShp j'' (restrShp j' a))) (hde : e d) : reindexTo j' a (reindexTo j'' (restrShp j' a) e) reindexTo j'' a d := j':Fin 2j'':Fin 2h:j'' j'a:Shpd:Dir (restrShp j'' a)e:Dir (restrShp j'' (restrShp j' a))hde:e dreindexTo j' a (reindexTo j'' (restrShp j' a) e) reindexTo j'' a d match j', j'' with j':Fin 2j'':Fin 2a:Shpx✝:Fin 2h:x✝ 1d:Dir (restrShp x✝ a)e:Dir (restrShp x✝ (restrShp 1 a))hde:e dreindexTo 1 a (reindexTo x✝ (restrShp 1 a) e) reindexTo x✝ a d j':Fin 2j'':Fin 2a:Shpx✝:Fin 2h:x✝ 1d:Dir (restrShp x✝ a)hde:d dreindexTo 1 a (reindexTo x✝ (restrShp 1 a) d) reindexTo x✝ a d; All goals completed! 🐙 j':Fin 2j'':Fin 2a:Shph:0 0d:Dir (restrShp 0 a)e:Dir (restrShp 0 (restrShp 0 a))hde:e dreindexTo 0 a (reindexTo 0 (restrShp 0 a) e) reindexTo 0 a d j':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.base)e:Dir (restrShp 0 (restrShp 0 Shp.base))hde:e dreindexTo 0 Shp.base (reindexTo 0 (restrShp 0 Shp.base) e) reindexTo 0 Shp.base dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.sigma)e:Dir (restrShp 0 (restrShp 0 Shp.sigma))hde:e dreindexTo 0 Shp.sigma (reindexTo 0 (restrShp 0 Shp.sigma) e) reindexTo 0 Shp.sigma dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.pi)e:Dir (restrShp 0 (restrShp 0 Shp.pi))hde:e dreindexTo 0 Shp.pi (reindexTo 0 (restrShp 0 Shp.pi) e) reindexTo 0 Shp.pi dj':Fin 2j'':Fin 2h:0 0b✝:Boold:Dir (restrShp 0 (Shp.lit b✝))e:Dir (restrShp 0 (restrShp 0 (Shp.lit b✝)))hde:e dreindexTo 0 (Shp.lit b✝) (reindexTo 0 (restrShp 0 (Shp.lit b✝)) e) reindexTo 0 (Shp.lit b✝) dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.pair)e:Dir (restrShp 0 (restrShp 0 Shp.pair))hde:e dreindexTo 0 Shp.pair (reindexTo 0 (restrShp 0 Shp.pair) e) reindexTo 0 Shp.pair d j':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.base)e:Dir (restrShp 0 (restrShp 0 Shp.base))hde:e dreindexTo 0 Shp.base (reindexTo 0 (restrShp 0 Shp.base) e) reindexTo 0 Shp.base dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.sigma)e:Dir (restrShp 0 (restrShp 0 Shp.sigma))hde:e dreindexTo 0 Shp.sigma (reindexTo 0 (restrShp 0 Shp.sigma) e) reindexTo 0 Shp.sigma dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.pi)e:Dir (restrShp 0 (restrShp 0 Shp.pi))hde:e dreindexTo 0 Shp.pi (reindexTo 0 (restrShp 0 Shp.pi) e) reindexTo 0 Shp.pi dj':Fin 2j'':Fin 2h:0 0b✝:Boold:Dir (restrShp 0 (Shp.lit b✝))e:Dir (restrShp 0 (restrShp 0 (Shp.lit b✝)))hde:e dreindexTo 0 (Shp.lit b✝) (reindexTo 0 (restrShp 0 (Shp.lit b✝)) e) reindexTo 0 (Shp.lit b✝) dj':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.pair)e:Dir (restrShp 0 (restrShp 0 Shp.pair))hde:e dreindexTo 0 Shp.pair (reindexTo 0 (restrShp 0 Shp.pair) e) reindexTo 0 Shp.pair d (j':Fin 2j'':Fin 2h:0 0d:Dir (restrShp 0 Shp.pair)hde:d dreindexTo 0 Shp.pair (reindexTo 0 (restrShp 0 Shp.pair) d) reindexTo 0 Shp.pair d; All goals completed! 🐙) j':Fin 2j'':Fin 2a:Shph:1 0d:Dir (restrShp 1 a)e:Dir (restrShp 1 (restrShp 0 a))hde:e dreindexTo 0 a (reindexTo 1 (restrShp 0 a) e) reindexTo 1 a d exact absurd h (j':Fin 2j'':Fin 2a:Shph:1 0d:Dir (restrShp 1 a)e:Dir (restrShp 1 (restrShp 0 a))hde:e d¬1 0 All goals completed! 🐙)

The functor

The operations of the universe endofunctor on presheaves over the walking arrow. Reducible so its shape and direction types unfold, which is what lets the decide tests reduce.

@[reducible] def universeData : PresheafPFunctorData (Fin 2) (Fin 2) where A := Shp B := Dir r := fun x rDir x.1 x.2 q := qShp directionRestr := fun a _i i' g d dirTo i' a d.1, rDir_dirTo a _i i' d.1 d.2 (leOfHom g) shapeRestr := fun _j j' g s restrShp j' s.1, qShp_restrShp s.1 j' (le_of_le_of_eq (leOfHom g) s.2.symm) reindex := fun _j j' _g a _i d reindexTo j' a.1 d.1, (rDir_reindexTo j' a.1 d.1).trans d.2

The underlying value of a direction cast along an equality of shapes is the original underlying value.

theorem cast_direction_val {j : Fin 2} {s s' : universeData.Shape j} (h : s = s') {i : Fin 2} (p : universeData.Direction s.1 i) : (cast (congrArg (fun t : universeData.Shape j universeData.Direction t.1 i) h) p).1 p.1 := j:Fin 2s:universeData.toSlicePFunctor.Shape js':universeData.toSlicePFunctor.Shape jh:s = s'i:Fin 2p:universeData.Direction (↑s) i(cast p) p j:Fin 2s:universeData.toSlicePFunctor.Shape ji:Fin 2p:universeData.Direction (↑s) i(cast p) p All goals completed! 🐙

The universe endofunctor: the operations with the seven functor laws.

@[reducible] def universeFunctor : PresheafPFunctor (Fin 2) (Fin 2) where toPresheafPFunctorData := universeData isFunctorial := { directionRestr_id := fun a i funext fun d Subtype.ext (dirTo_self a i d.1 d.2) directionRestr_comp := fun a _i _i' _i'' _f g funext fun d Subtype.ext (dirTo_dirTo a _i' _i'' (leOfHom g) d.1).symm shapeRestr_id := fun j funext fun s Subtype.ext (restrShp_self s.1 j s.2) shapeRestr_comp := fun _j _j' _j'' _g h funext fun s Subtype.ext (restrShp_restrShp s.1 _j' _j'' (leOfHom h)).symm reindex_naturality := fun _j j' _g a _i i' f funext fun d Subtype.ext (dirTo_reindexTo j' i' a.1 d.1) reindex_id := fun j a i b j:Fin 2a:universeData.toSlicePFunctor.Shape ji:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (𝟙 j) a)) iuniverseData.reindex (𝟙 j) a b = cast b j:Fin 2a:universeData.toSlicePFunctor.Shape ji:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (𝟙 j) a)) i(universeData.reindex (𝟙 j) a b) = (cast b) j:Fin 2a:universeData.toSlicePFunctor.Shape ji:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (𝟙 j) a)) i(universeData.reindex (𝟙 j) a b) (cast b) j:Fin 2a:universeData.toSlicePFunctor.Shape ji:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (𝟙 j) a)) ib (cast b) All goals completed! 🐙 reindex_comp := fun _j j' j'' g h a _i b _j:Fin 2j':Fin 2j'':Fin 2g:j' _jh:j'' j'a:universeData.toSlicePFunctor.Shape _j_i:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (h g) a)) _iuniverseData.reindex (h g) a b = universeData.reindex g a (universeData.reindex h (universeData.shapeRestr g a) (cast b)) _j:Fin 2j':Fin 2j'':Fin 2g:j' _jh:j'' j'a:universeData.toSlicePFunctor.Shape _j_i:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (h g) a)) _i(universeData.reindex (h g) a b) = (universeData.reindex g a (universeData.reindex h (universeData.shapeRestr g a) (cast b))) _j:Fin 2j':Fin 2j'':Fin 2g:j' _jh:j'' j'a:universeData.toSlicePFunctor.Shape _j_i:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (h g) a)) _i(universeData.reindex (h g) a b) (universeData.reindex g a (universeData.reindex h (universeData.shapeRestr g a) (cast b))) _j:Fin 2j':Fin 2j'':Fin 2g:j' _jh:j'' j'a:universeData.toSlicePFunctor.Shape _j_i:Fin 2b:universeData.Direction (↑(universeData.shapeRestr (h g) a)) _i(cast b) b All goals completed! 🐙 }

Each shape has finitely many directions.

set_option warn.classDefReducibility false indef finitaryDir : a : Shp, FinEnum (Dir a) | .base => finEnumEmpty | .sigma => finEnumBool | .pi => finEnumBool | .lit _ => finEnumEmpty | .pair => finEnumPairDir

The universe endofunctor is finitary.

set_option warn.classDefReducibility false indef finitaryUniverse : universeFunctor.toPFunctor.Finitary := finitaryDir

The universe endofunctor bundled with its finiteness evidence. Reducible so instance resolution can unfold it and decide reduces through the forwarding instances.

end GebProto.PresheafUniverse