Research source · Plain text

FiniteEnum.lean

site/public/formal/persistent-stacks/lean/FPRDTransfer/FiniteEnum.lean

441 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: aecd0a0f8a6ac773c1816cc108a26973d30753be19f72f978eb975e42c926e2c

import Std
import FPRDTransfer.LMR

namespace FPRD

universe u v

/-- An executable exhaustive list for a finite type.  Duplicate entries are
allowed at the construction boundary and removed before cardinality or
transport to `Fin n`. -/
class FiniteEnum (α : Type u) where
  elems : List α
  complete : ∀ a, a ∈ elems

/-- The minimal bundled equivalence needed by this dependency-light package. -/
structure FiniteEquiv (α : Type u) (β : Type v) where
  toFun : α → β
  invFun : β → α
  left_inv : ∀ a, invFun (toFun a) = a
  right_inv : ∀ b, toFun (invFun b) = b

namespace FiniteEquiv

instance : CoeFun (FiniteEquiv α β) (fun _ => α → β) := ⟨FiniteEquiv.toFun⟩

def symm (e : FiniteEquiv α β) : FiniteEquiv β α where
  toFun := e.invFun
  invFun := e.toFun
  left_inv := e.right_inv
  right_inv := e.left_inv

@[simp] theorem symm_apply_apply (e : FiniteEquiv α β) (a : α) :
    e.symm (e a) = a := e.left_inv a

@[simp] theorem apply_symm_apply (e : FiniteEquiv α β) (b : β) :
    e (e.symm b) = b := e.right_inv b

end FiniteEquiv

namespace FiniteEnum

variable {α : Type u} {β : Type v}

/-- Executable membership driven directly by `DecidableEq`, independent of a
potentially unrelated `BEq` implementation. -/
def containsEq [DecidableEq α] (a : α) : List α → Bool
  | [] => false
  | b :: bs => if b = a then true else containsEq a bs

@[simp] theorem containsEq_eq_true [DecidableEq α] {a : α} {as : List α} :
    containsEq a as = true ↔ a ∈ as := by
  induction as with
  | nil => simp [containsEq]
  | cons b bs ih =>
      by_cases h : b = a
      · subst b
        simp [containsEq]
      · constructor
        · intro hc
          have ht : containsEq a bs = true := by simpa [containsEq, h] using hc
          exact List.mem_cons.mpr (Or.inr (ih.mp ht))
        · intro hm
          have ht : a ∈ bs :=
            (List.mem_cons.mp hm).resolve_left (Ne.symm h)
          simpa [containsEq, h] using ih.mpr ht

/-- Propositional-equality-based deduplication, kept local so the transport
does not depend on a potentially unrelated `BEq` implementation. -/
def dedup [DecidableEq α] : List α → List α
  | [] => []
  | a :: as =>
      let rest := dedup as
      if containsEq a rest then rest else a :: rest

@[simp] theorem mem_dedup [DecidableEq α] {a : α} {as : List α} :
    a ∈ dedup as ↔ a ∈ as := by
  induction as generalizing a with
  | nil => simp [dedup]
  | cons b bs ih =>
      simp only [dedup]
      split <;> simp_all

theorem nodup_dedup [DecidableEq α] (as : List α) : (dedup as).Nodup := by
  induction as with
  | nil => simp [dedup]
  | cons a as ih =>
      simp only [dedup]
      split <;> simp_all

/-- The canonical duplicate-free list associated with an enumeration. -/
def unique (α : Type u) [FiniteEnum α] [DecidableEq α] : List α :=
  dedup (FiniteEnum.elems (α := α))

theorem complete_unique (α : Type u) [FiniteEnum α] [DecidableEq α] (a : α) :
    a ∈ unique α := by
  simp [unique, FiniteEnum.complete]

/-- Transport an exhaustive enumeration across a proved type equivalence. -/
@[reducible] def ofEquiv (source : FiniteEnum α) (e : FiniteEquiv α β) :
    FiniteEnum β where
  elems := source.elems.map e
  complete b := by
    apply List.mem_map.mpr
    exact ⟨e.symm b, source.complete (e.symm b), e.right_inv b⟩

/-- Executable cardinality of the duplicate-free enumeration. -/
def card (α : Type u) [FiniteEnum α] [DecidableEq α] : Nat :=
  (unique α).length

/-- First position of an element, or the list length when it is absent. -/
def indexOf [DecidableEq α] (a : α) : List α → Nat
  | [] => 0
  | b :: bs => if b = a then 0 else indexOf a bs + 1

theorem indexOf_lt_length_of_mem [DecidableEq α] {a : α} {as : List α}
    (h : a ∈ as) : indexOf a as < as.length := by
  induction as with
  | nil => simp at h
  | cons b bs ih =>
      simp only [indexOf, List.length_cons]
      by_cases hba : b = a
      · simp [hba]
      · have : a ∈ bs := (List.mem_cons.mp h).resolve_left (Ne.symm hba)
        simp [hba, ih this]

theorem get_indexOf [DecidableEq α] {a : α} {as : List α} (h : a ∈ as) :
    as.get ⟨indexOf a as, indexOf_lt_length_of_mem h⟩ = a := by
  induction as with
  | nil => simp at h
  | cons b bs ih =>
      by_cases hba : b = a
      · subst b
        simp [indexOf]
      · have ht : a ∈ bs := (List.mem_cons.mp h).resolve_left (Ne.symm hba)
        simpa [indexOf, hba] using ih ht

theorem indexOf_get [DecidableEq α] {as : List α} (hn : as.Nodup)
    (i : Fin as.length) : indexOf (as.get i) as = i.val := by
  induction as with
  | nil => exact Fin.elim0 i
  | cons a as ih =>
      have hnot : a ∉ as := (List.nodup_cons.mp hn).1
      have htail : as.Nodup := (List.nodup_cons.mp hn).2
      refine Fin.cases ?_ (fun j => ?_) i
      · simp [indexOf]
      · have hne : a ≠ as.get j := by
          intro h
          apply hnot
          rw [h]
          exact as.get_mem j
        rw [List.get_cons_succ', indexOf, if_neg hne, ih htail j]
        rfl

/-- Every finite enumeration yields an equivalence with a concrete `Fin n`.
The target size is computed after deduplication. -/
def equivFin (α : Type u) [FiniteEnum α] [DecidableEq α] :
    FiniteEquiv α (Fin (card α)) where
  toFun a := ⟨indexOf a (unique α), indexOf_lt_length_of_mem (complete_unique α a)⟩
  invFun i := (unique α).get i
  left_inv a := get_indexOf (complete_unique α a)
  right_inv i := by
    apply Fin.ext
    exact indexOf_get (nodup_dedup (FiniteEnum.elems (α := α))) i

instance : FiniteEnum Unit where
  elems := [()]
  complete a := by cases a; simp

instance : FiniteEnum Bool where
  elems := [false, true]
  complete a := by cases a <;> simp

instance {n : Nat} : FiniteEnum (Fin n) where
  elems := List.finRange n
  complete a := by simp

instance [FiniteEnum α] : FiniteEnum (Option α) where
  elems := none :: (FiniteEnum.elems (α := α)).map some
  complete a := by
    cases a with
    | none => simp
    | some a => simp [FiniteEnum.complete]

instance [FiniteEnum α] [FiniteEnum β] : FiniteEnum (Sum α β) where
  elems :=
    (FiniteEnum.elems (α := α)).map Sum.inl ++
      (FiniteEnum.elems (α := β)).map Sum.inr
  complete a := by
    cases a with
    | inl a => simp [FiniteEnum.complete]
    | inr b => simp [FiniteEnum.complete]

instance [FiniteEnum α] [FiniteEnum β] : FiniteEnum (α × β) where
  elems := (FiniteEnum.elems (α := α)).flatMap fun a =>
    (FiniteEnum.elems (α := β)).map fun b => (a, b)
  complete p := by
    rcases p with ⟨a, b⟩
    simp [FiniteEnum.complete]

/-- Enumerate functions on `Fin n` by choosing a head and then a tail. -/
def finFunctions (values : List α) : (n : Nat) → List (Fin n → α)
  | 0 => [fun i => Fin.elim0 i]
  | n + 1 => values.flatMap fun a =>
      (finFunctions values n).map fun f => Fin.cases a f

theorem mem_finFunctions (values : List α) (hvalues : ∀ a, a ∈ values) :
    ∀ {n : Nat} (f : Fin n → α), f ∈ finFunctions values n := by
  intro n
  induction n with
  | zero =>
      intro f
      simp only [finFunctions, List.mem_singleton]
      funext i
      exact Fin.elim0 i
  | succ n ih =>
      intro f
      let tail : Fin n → α := fun i => f i.succ
      have htail : tail ∈ finFunctions values n := ih tail
      simp only [finFunctions, List.mem_flatMap, List.mem_map]
      refine ⟨f 0, hvalues (f 0), tail, htail, ?_⟩
      funext i
      exact Fin.cases rfl (fun j => rfl) i

@[reducible] def finFunctionEnum (n : Nat) (e : FiniteEnum α) :
    FiniteEnum (Fin n → α) where
  elems := finFunctions e.elems n
  complete := mem_finFunctions e.elems e.complete

instance {n : Nat} [FiniteEnum α] : FiniteEnum (Fin n → α) :=
  finFunctionEnum n (inferInstance : FiniteEnum α)

/-- Decidable equality for finite functions, proved by splitting the domain at
zero.  Core Lean deliberately supplies no general function equality instance. -/
protected def finFunctionDecEq [DecidableEq α] :
    (n : Nat) → DecidableEq (Fin n → α)
  | 0 => fun _ _ => isTrue (funext fun i => Fin.elim0 i)
  | n + 1 => fun f g =>
      match decEq (f 0) (g 0),
          FiniteEnum.finFunctionDecEq n (fun i => f i.succ) (fun i => g i.succ) with
      | isTrue hzero, isTrue htail =>
          isTrue (funext fun i => Fin.cases hzero (fun j => congrFun htail j) i)
      | isFalse hzero, _ =>
          isFalse fun h => hzero (congrFun h 0)
      | _, isFalse htail =>
          isFalse fun h => htail (funext fun i => congrFun h i.succ)

instance {n : Nat} [DecidableEq α] : DecidableEq (Fin n → α) :=
  FiniteEnum.finFunctionDecEq n

end FiniteEnum

/-- Port words whose length is bounded by the automaton distance. -/
structure BoundedPath (d k : Nat) where
  elems : List (Fin d)
  length_le : elems.length ≤ k
  deriving DecidableEq

namespace BoundedPath

@[reducible] def enumeration (d : Nat) : (k : Nat) → FiniteEnum (BoundedPath d k)
  | 0 =>
      { elems := [⟨[], by simp⟩]
        complete := fun p => by
          have hp : p.elems = [] := List.eq_nil_of_length_eq_zero (Nat.eq_zero_of_le_zero p.length_le)
          cases p
          simp_all }
  | k + 1 =>
      let previous := enumeration d k
      { elems :=
          ⟨[], by simp⟩ ::
            (FiniteEnum.elems (α := Fin d)).flatMap fun a =>
              previous.elems.map fun p =>
                ⟨a :: p.elems, Nat.succ_le_succ p.length_le⟩
        complete := fun p => by
          cases he : p.elems with
          | nil =>
              have hp : p = ⟨[], by simp⟩ := by cases p; simp_all
              simp [hp]
          | cons a as =>
              have has : as.length ≤ k := by
                simpa [he] using p.length_le
              let tail : BoundedPath d k := ⟨as, has⟩
              have htail : tail ∈ previous.elems := previous.complete tail
              have hp : p = ⟨a :: tail.elems, Nat.succ_le_succ tail.length_le⟩ := by
                cases p
                simp_all [tail]
              simp only [List.mem_cons, List.mem_flatMap, List.mem_map]
              exact Or.inr ⟨a, FiniteEnum.complete a, tail, htail, hp.symm⟩ }

instance {d k : Nat} : FiniteEnum (BoundedPath d k) := enumeration d k

end BoundedPath

namespace Unfold

/-- Decidable equality follows the fixed observation depth. -/
protected def decEq {Gamma : Type u} [DecidableEq Gamma] {d : Nat} :
    (k : Nat) → DecidableEq (Unfold d Gamma k)
  | 0, .missing, .missing => isTrue rfl
  | 0, .missing, .leaf _ => isFalse fun h => by cases h
  | 0, .leaf _, .missing => isFalse fun h => by cases h
  | 0, .leaf a, .leaf b =>
      match decEq a b with
      | isTrue h => isTrue (by cases h; rfl)
      | isFalse h => isFalse fun hab => by cases hab; exact h rfl
  | k + 1, .missing, .missing => isTrue rfl
  | k + 1, .missing, .node _ _ => isFalse fun h => by cases h
  | k + 1, .node _ _, .missing => isFalse fun h => by cases h
  | k + 1, .node a childrenA, .node b childrenB => by
      letI : DecidableEq (Unfold d Gamma k) := Unfold.decEq k
      exact match decEq a b with
        | isFalse h => isFalse fun hab => by cases hab; exact h rfl
        | isTrue hlabel =>
            match decEq childrenA childrenB with
            | isTrue hchildren => isTrue (by cases hlabel; cases hchildren; rfl)
            | isFalse hchildren =>
                isFalse fun hab => by cases hab; exact hchildren rfl

instance {Gamma : Type u} [DecidableEq Gamma] {d k : Nat} :
    DecidableEq (Unfold d Gamma k) := Unfold.decEq k

/-- Exhaustive finite unfolding trees at an exact observation depth. -/
@[reducible] def enumeration {Gamma : Type u} [FiniteEnum Gamma] (d : Nat) :
    (k : Nat) → FiniteEnum (Unfold d Gamma k)
  | 0 =>
      let labels : FiniteEnum (Option Gamma) := inferInstance
      { elems := Unfold.missing :: labels.elems.map Unfold.leaf
        complete := fun tree => by
          cases tree with
          | missing => simp
          | leaf label => simp [labels.complete label] }
  | k + 1 =>
      let labels : FiniteEnum (Option Gamma) := inferInstance
      let previous := enumeration d k
      let children := FiniteEnum.finFunctionEnum d previous
      { elems := Unfold.missing :: labels.elems.flatMap fun label =>
          children.elems.map fun child => Unfold.node label child
        complete := fun tree => by
          cases tree with
          | missing => simp
          | node label child =>
              simp only [List.mem_cons, List.mem_flatMap, List.mem_map]
              exact Or.inr
                ⟨label, labels.complete label, child, children.complete child, rfl⟩ }

instance {d k : Nat} {Gamma : Type u} [FiniteEnum Gamma] :
    FiniteEnum (Unfold d Gamma k) := enumeration d k

end Unfold

/- The executable checks below were compiled once before these definitions;
that red run failed on the deliberately absent API. -/

theorem test_boundedPath_card : FiniteEnum.card (BoundedPath 1 2) = 3 := by decide

theorem test_fin_card : FiniteEnum.card (Fin 3) = 3 := by decide

theorem test_option_card : FiniteEnum.card (Option Bool) = 3 := by decide

theorem test_sum_card : FiniteEnum.card (Sum Bool Unit) = 3 := by decide

theorem test_product_card : FiniteEnum.card (Bool × Bool) = 4 := by decide

theorem test_finFunction_card : FiniteEnum.card (Fin 2 → Bool) = 4 := by decide

/-- The exact shape of the scaffold-label carrier produced by the T89 stack
compiler, at a small executable calibration. -/
abbrev StackTupleFixture := Fin 2 → Option Bool

theorem test_stackTuple_card : FiniteEnum.card StackTupleFixture = 9 := by decide

theorem test_stackTuple_equivFin_left (value : StackTupleFixture) :
    (FiniteEnum.equivFin StackTupleFixture).symm
        (FiniteEnum.equivFin StackTupleFixture value) = value := by
  simp

theorem test_stackTuple_equivFin_right
    (i : Fin (FiniteEnum.card StackTupleFixture)) :
    FiniteEnum.equivFin StackTupleFixture
        ((FiniteEnum.equivFin StackTupleFixture).symm i) = i := by
  simp

/-- Small record with the same field shape as T101's root-sharing cell. -/
structure RootCellFixture where
  symbol : Bool
  next : Fin 2
  deriving DecidableEq

def rootCellFixtureEquiv : FiniteEquiv (Bool × Fin 2) RootCellFixture where
  toFun value := ⟨value.1, value.2⟩
  invFun cell := (cell.symbol, cell.next)
  left_inv value := by cases value; rfl
  right_inv cell := by cases cell; rfl

@[reducible] instance : FiniteEnum RootCellFixture :=
  FiniteEnum.ofEquiv (inferInstance : FiniteEnum (Bool × Fin 2)) rootCellFixtureEquiv

/-- The exact finite-function/optional-record shape of T101's root label. -/
abbrev RootLabelShapeFixture := Fin 2 → Option RootCellFixture

theorem test_rootLabelShape_card : FiniteEnum.card RootLabelShapeFixture = 25 := by decide

theorem test_rootLabelShape_equivFin_left (value : RootLabelShapeFixture) :
    (FiniteEnum.equivFin RootLabelShapeFixture).symm
        (FiniteEnum.equivFin RootLabelShapeFixture value) = value := by
  simp

theorem test_rootLabelShape_equivFin_right
    (i : Fin (FiniteEnum.card RootLabelShapeFixture)) :
    FiniteEnum.equivFin RootLabelShapeFixture
        ((FiniteEnum.equivFin RootLabelShapeFixture).symm i) = i := by
  simp

theorem test_unfold_depth0_card : FiniteEnum.card (Unfold 1 Unit 0) = 3 := by decide

theorem test_unfold_depth1_card : FiniteEnum.card (Unfold 1 Unit 1) = 7 := by decide

theorem test_unfold_depth2_card : FiniteEnum.card (Unfold 1 Unit 2) = 15 := by decide

theorem test_unfold_equivFin_left (value : Unfold 1 Unit 2) :
    (FiniteEnum.equivFin (Unfold 1 Unit 2)).symm
        (FiniteEnum.equivFin (Unfold 1 Unit 2) value) = value := by
  simp

theorem test_unfold_equivFin_right
    (i : Fin (FiniteEnum.card (Unfold 1 Unit 2))) :
    FiniteEnum.equivFin (Unfold 1 Unit 2)
        ((FiniteEnum.equivFin (Unfold 1 Unit 2)).symm i) = i := by
  simp

theorem test_equivFin_left (p : BoundedPath 1 2) :
    (FiniteEnum.equivFin (BoundedPath 1 2)).symm
        (FiniteEnum.equivFin (BoundedPath 1 2) p) = p := by
  simp

theorem test_equivFin_right (i : Fin (FiniteEnum.card (BoundedPath 1 2))) :
    FiniteEnum.equivFin (BoundedPath 1 2)
        ((FiniteEnum.equivFin (BoundedPath 1 2)).symm i) = i := by
  simp

end FPRD