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.Tree.Ranked.Basic public import Mathlib.Algebra.GroupWithZero.Nat

Symbol codes

The block spelling a symbol of a ranked alphabet: its index in binary, least significant bit first, padded to the alphabet's width. A block decodes to the symbol it spells, which is what makes the padding lossless, and a block denoting no symbol has no arity.

Main definitions

    RankedAlphabet.code β€” the block spelling a symbol.

    RankedAlphabet.decodeBits β€” the value a block denotes.

    RankedAlphabet.arOf β€” the arity of the symbol a block denotes.

Main statements

    RankedAlphabet.length_code β€” every block has the alphabet's width.

    RankedAlphabet.decodeBits_code β€” a block decodes to its own symbol.

    RankedAlphabet.arOf_decodeBits_code β€” and so has that symbol's arity.

    RankedAlphabet.testBit_decodeBits β€” a block's entries are its value's bits.

    RankedAlphabet.getElem_code_eq β€” and a block's entries are the bits of the symbol's index.

    RankedAlphabet.le_maxArity_of_arOf_eq_some β€” an arity a block yields is at most the largest.

Implementation notes

decodeBits is a bare List.rec, so Lean generates no equation lemma for it; decodeBits_cons is stated for the rewrites that need one.

mod_two_mul is stated here rather than taken from Nat's division API: Nat.mod_mul states the same identity and depends on Classical.choice, which the axiom linter rejects for this module, and its modulus is a variable, so omega cannot discharge the identity directly. The proof below runs through Nat.div_add_mod, Nat.mul_add_mod and Nat.mod_eq_of_lt, all of which are choice-free.

Tags

ranked alphabet, code, binary representation, decoding

namespace RankedAlphabetpublic section

The block spelling a symbol: its index in binary, least significant bit first, padded to the alphabet's width.

@[`@[expose]` has no effect outside a `module` fileexpose] def code (R : RankedAlphabet) (i : Fin R.card) : List Bool := (List.range R.width).map fun j ↦ i.val.testBit j

The value a block denotes, its head the least significant bit.

@[`@[expose]` has no effect outside a `module` fileexpose] def decodeBits : List Bool β†’ β„• := List.rec 0 fun b _ ih ↦ 2 * ih + (if b then 1 else 0)

decodeBits unfolded on a non-empty block. decodeBits is a bare List.rec, for which Lean generates no equation lemma.

theorem decodeBits_cons (b : Bool) (l : List Bool) : decodeBits (b :: l) = 2 * decodeBits l + (if b then 1 else 0) := rfl

The arity of the symbol a block denotes, absent at a block denoting none.

@[`@[expose]` has no effect outside a `module` fileexpose] def arOf (R : RankedAlphabet) (v : β„•) : Option β„• := if h : v < R.card then some (R.arity ⟨v, h⟩) else none

Every block has the alphabet's width.

@[simp] theorem length_code (R : RankedAlphabet) (i : Fin R.card) : (R.code i).length = R.width := R:RankedAlphabeti:Fin R.card⊒ (R.code i).length = R.width All goals completed! πŸ™

Splitting a residue by a factor of two, choice-free.

theorem mod_two_mul (v m : β„•) (hm : 0 < m) : v % (2 * m) = 2 * (v / 2 % m) + v % 2 := v:β„•m:β„•hm:0 < m⊒ v % (2 * m) = 2 * (v / 2 % m) + v % 2 v:β„•m:β„•hm:0 < mh2:0 < 2⊒ v % (2 * m) = 2 * (v / 2 % m) + v % 2 v:β„•m:β„•hm:0 < mh2:0 < 2hlt1:v / 2 % m < m⊒ v % (2 * m) = 2 * (v / 2 % m) + v % 2 v:β„•m:β„•hm:0 < mh2:0 < 2hlt1:v / 2 % m < mhlt2:v % 2 < 2⊒ v % (2 * m) = 2 * (v / 2 % m) + v % 2 conv_lhs => v:β„•m:β„•hm:0 < mh2:0 < 2hlt1:v / 2 % m < mhlt2:v % 2 < 2| (2 * (m * (v / 2 / m) + v / 2 % m) + v % 2) % (2 * m) v:β„•m:β„•hm:0 < mh2:0 < 2hlt1:v / 2 % m < mhlt2:v % 2 < 2⊒ (2 * (v / 2 % m) + v % 2) % (2 * m) = 2 * (v / 2 % m) + v % 2 exact Nat.mod_eq_of_lt (v:β„•m:β„•hm:0 < mh2:0 < 2hlt1:v / 2 % m < mhlt2:v % 2 < 2⊒ 2 * (v / 2 % m) + v % 2 < 2 * m All goals completed! πŸ™)

A block decodes to the symbol it spells. The bound i.val < 2 ^ width comes from card_le_two_pow_width, and is what makes the padding lossless.

theorem decodeBits_code (R : RankedAlphabet) (i : Fin R.card) : decodeBits (R.code i) = i.val := R:RankedAlphabeti:Fin R.card⊒ decodeBits (R.code i) = ↑i R:RankedAlphabeti:Fin R.cardhlt:↑i < 2 ^ R.width⊒ decodeBits (R.code i) = ↑i R:RankedAlphabeti:Fin R.cardhlt:↑i < 2 ^ R.widthkey:βˆ€ (n v : β„•), decodeBits (List.map (fun j ↦ v.testBit j) (List.range n)) = v % 2 ^ n⊒ decodeBits (R.code i) = ↑i All goals completed! πŸ™

A block spelling a symbol has that symbol's arity.

theorem arOf_decodeBits_code (R : RankedAlphabet) (i : Fin R.card) : R.arOf (decodeBits (R.code i)) = some (R.arity i) := R:RankedAlphabeti:Fin R.card⊒ R.arOf (decodeBits (R.code i)) = some (R.arity i) All goals completed! πŸ™

The nth bit of a block is the nth bit of the symbol's index.

theorem getElem_code_eq (R : RankedAlphabet) (i : Fin R.card) (n : β„•) (h : n < (R.code i).length) : (R.code i)[n] = i.val.testBit n := R:RankedAlphabeti:Fin R.cardn:β„•h:n < (R.code i).length⊒ (R.code i)[n] = (↑i).testBit n All goals completed! πŸ™

The nth bit of a block's value is the block's nth entry.

theorem testBit_decodeBits : βˆ€ (bs : List Bool) (n : β„•) (_ : n < bs.length), (decodeBits bs).testBit n = bs[n] := List.rec (fun n h ↦ absurd h (n:β„•h:n < [].length⊒ Β¬n < [].length All goals completed! πŸ™)) (fun b bs ih n h ↦ b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:β„•h:n < (b :: bs).length⊒ (decodeBits (b :: bs)).testBit n = (b :: bs)[n] cases n with b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (b :: bs).length⊒ (decodeBits (b :: bs)).testBit 0 = (b :: bs)[0] b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (b :: bs).length⊒ decide ((2 * decodeBits bs + if b = true then 1 else 0) % 2 = 1) = b bs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (false :: bs).length⊒ decide ((2 * decodeBits bs + if false = true then 1 else 0) % 2 = 1) = falsebs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (true :: bs).length⊒ decide ((2 * decodeBits bs + if true = true then 1 else 0) % 2 = 1) = true bs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (false :: bs).length⊒ decide ((2 * decodeBits bs + if false = true then 1 else 0) % 2 = 1) = falsebs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (true :: bs).length⊒ decide ((2 * decodeBits bs + if true = true then 1 else 0) % 2 = 1) = true All goals completed! πŸ™ b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:β„•h:n + 1 < (b :: bs).length⊒ (decodeBits (b :: bs)).testBit (n + 1) = (b :: bs)[n + 1] b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:β„•h:n + 1 < (b :: bs).length⊒ ((2 * decodeBits bs + if b = true then 1 else 0) / 2).testBit n = (b :: bs)[n + 1] b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:β„•h:n + 1 < (b :: bs).lengthhd:(2 * decodeBits bs + if b = true then 1 else 0) / 2 = decodeBits bs⊒ ((2 * decodeBits bs + if b = true then 1 else 0) / 2).testBit n = (b :: bs)[n + 1] b:Boolbs:List Boolih:βˆ€ (n : β„•) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:β„•h:n + 1 < (b :: bs).lengthhd:(2 * decodeBits bs + if b = true then 1 else 0) / 2 = decodeBits bs⊒ bs[n] = (b :: bs)[n + 1] All goals completed! πŸ™)

Every arity a block yields is at most the largest, arOf returning an arity only in the image of arity.

theorem le_maxArity_of_arOf_eq_some (R : RankedAlphabet) {v r : β„•} (h : R.arOf v = some r) : r ≀ R.maxArity := R:RankedAlphabetv:β„•r:β„•h:R.arOf v = some r⊒ r ≀ R.maxArity R:RankedAlphabetv:β„•r:β„•h:(if h : v < R.card then some (R.arity ⟨v, h⟩) else none) = some r⊒ r ≀ R.maxArity R:RankedAlphabetv:β„•r:β„•h✝:v < R.cardh:some (R.arity ⟨v, h✝⟩) = some r⊒ r ≀ R.maxArityR:RankedAlphabetv:β„•r:β„•h✝:Β¬v < R.cardh:none = some r⊒ r ≀ R.maxArity R:RankedAlphabetv:β„•r:β„•h✝:v < R.cardh:some (R.arity ⟨v, h✝⟩) = some r⊒ r ≀ R.maxArity R:RankedAlphabetv:β„•r:β„•hlt:v < R.cardh:some (R.arity ⟨v, h✝⟩) = some r⊒ r ≀ R.maxArity R:RankedAlphabetv:β„•r:β„•hlt:v < R.cardh:some (R.arity ⟨v, h✝⟩) = some r⊒ R.arity ⟨v, hlt⟩ ≀ R.maxArity All goals completed! πŸ™ R:RankedAlphabetv:β„•r:β„•h✝:Β¬v < R.cardh:none = some r⊒ r ≀ R.maxArity exact absurd h (R:RankedAlphabetv:β„•r:β„•h✝:Β¬v < R.cardh:none = some r⊒ Β¬none = some r All goals completed! πŸ™)
endend RankedAlphabet