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.NatSymbol 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 sectionThe block spelling a symbol: its index in binary, least significant bit first, padded to the alphabet's width.
@[expose] def code (R : RankedAlphabet) (i : Fin R.card) : List Bool :=
(List.range R.width).map fun j β¦ i.val.testBit jThe value a block denotes, its head the least significant bit.
@[expose] 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) := rflThe arity of the symbol a block denotes, absent at a block denoting none.
@[expose] def arOf (R : RankedAlphabet) (v : β) : Option β :=
if h : v < R.card then some (R.arity β¨v, hβ©) else noneEvery 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 (by v:βm:βhm:0 < mh2:0 < 2hlt1:v / 2 % m < mhlt2:v % 2 < 2β’ 2 * (v / 2 % m) + v % 2 < 2 * m omega 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 := by R:RankedAlphabeti:Fin R.cardβ’ decodeBits (R.code i) = βi
have hlt : i.val < 2 ^ R.width :=
Nat.lt_of_lt_of_le i.isLt R.card_le_two_pow_width R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthβ’ decodeBits (R.code i) = βi
have key : β (n : β) (v : β),
decodeBits ((List.range n).map fun j β¦ v.testBit j) = v % 2 ^ n := by
refine Nat.rec (motive := fun n β¦ β v : β,
decodeBits ((List.range n).map fun j β¦ v.testBit j) = v % 2 ^ n)
(fun v β¦ by R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthv:ββ’ decodeBits (List.map (fun j β¦ v.testBit j) (List.range Nat.zero)) = v % 2 ^ Nat.zero
simp only [List.range_zero, List.map_nil, decodeBits, Nat.pow_zero, Nat.mod_one] All goals completed! π)
(fun n ihn v β¦ ?_)
rw [List.range_succ_eq_map, R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ decodeBits (List.map (fun j β¦ v.testBit j) (0 :: List.map Nat.succ (List.range n))) = v % 2 ^ n.succ List.map_cons, R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ decodeBits (v.testBit 0 :: List.map (fun j β¦ v.testBit j) (List.map Nat.succ (List.range n))) = v % 2 ^ n.succ List.map_map R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ decodeBits (v.testBit 0 :: List.map ((fun j β¦ v.testBit j) β Nat.succ) (List.range n)) = v % 2 ^ n.succ] R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ decodeBits (v.testBit 0 :: List.map ((fun j β¦ v.testBit j) β Nat.succ) (List.range n)) = v % 2 ^ n.succ
simp only [Function.comp_def, Nat.succ_eq_add_one, Nat.testBit_add_one] R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ decodeBits (v.testBit 0 :: List.map (fun x β¦ (v / 2).testBit x) (List.range n)) = v % 2 ^ (n + 1)
rw [decodeBits_cons, R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * decodeBits (List.map (fun x β¦ (v / 2).testBit x) (List.range n)) + if v.testBit 0 = true then 1 else 0) =
v % 2 ^ (n + 1) ihn (v / 2), R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * (v / 2 % 2 ^ n) + if v.testBit 0 = true then 1 else 0) = v % 2 ^ (n + 1) Nat.testBit_zero, R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = v % 2 ^ (n + 1) pow_succ', R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = v % (2 * 2 ^ n)
mod_two_mul _ _ (Nat.two_pow_pos n) R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2] R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:ββ’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2
rcases Nat.mod_two_eq_zero_or_one v with h | h inl R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:βh:v % 2 = 0β’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2inr R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:βh:v % 2 = 1β’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2 <;> inl R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:βh:v % 2 = 0β’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2inr R:RankedAlphabeti:Fin R.cardhlt:βi < 2 ^ R.widthn:βihn:β (v : β), decodeBits (List.map (fun j β¦ v.testBit j) (List.range n)) = v % 2 ^ nv:βh:v % 2 = 1β’ (2 * (v / 2 % 2 ^ n) + if decide (v % 2 = 1) = true then 1 else 0) = 2 * (v / 2 % 2 ^ n) + v % 2 simp [h, Nat.add_comm] 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
rw [code, 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 (List.map (fun j β¦ (βi).testBit j) (List.range R.width)) = βi key, 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β’ βi % 2 ^ R.width = βi Nat.mod_eq_of_lt hlt 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β’ β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) := by R:RankedAlphabeti:Fin R.cardβ’ R.arOf (decodeBits (R.code i)) = some (R.arity i)
rw [decodeBits_code, R:RankedAlphabeti:Fin R.cardβ’ R.arOf βi = some (R.arity i) arOf, R:RankedAlphabeti:Fin R.cardβ’ (if h : βi < R.card then some (R.arity β¨βi, hβ©) else none) = some (R.arity i) dite_eq_left i.isLt R:RankedAlphabeti:Fin R.cardβ’ some (R.arity β¨β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 := by R:RankedAlphabeti:Fin R.cardn:βh:n < (R.code i).lengthβ’ (R.code i)[n] = (βi).testBit n
simp only [code, List.getElem_map, List.getElem_range] 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 (by n:βh:n < [].lengthβ’ Β¬n < [].length simp All goals completed! π))
(fun b bs ih n h β¦ by 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
| zero => zero 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]
rw [decodeBits_cons, zero b:Boolbs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]h:0 < (b :: bs).lengthβ’ (2 * decodeBits bs + if b = true then 1 else 0).testBit 0 = (b :: bs)[0] Nat.testBit_zero, zero 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)[0] List.getElem_cons_zero zero 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] zero 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
cases b zero.false 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) = falsezero.true bs: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 <;> zero.false 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) = falsezero.true bs: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 simp All goals completed! π
| succ n => succ 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]
rw [decodeBits_cons, succ 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).testBit (n + 1) = (b :: bs)[n + 1] Nat.testBit_add_one succ 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]] succ 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]
have hd : (2 * decodeBits bs + (if b then 1 else 0)) / 2 = decodeBits bs := by 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 b false bs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:βh:n + 1 < (false :: bs).lengthβ’ (2 * decodeBits bs + if false = true then 1 else 0) / 2 = decodeBits bstrue bs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:βh:n + 1 < (true :: bs).lengthβ’ (2 * decodeBits bs + if true = true then 1 else 0) / 2 = decodeBits bs <;> false bs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:βh:n + 1 < (false :: bs).lengthβ’ (2 * decodeBits bs + if false = true then 1 else 0) / 2 = decodeBits bstrue bs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:βh:n + 1 < (true :: bs).lengthβ’ (2 * decodeBits bs + if true = true then 1 else 0) / 2 = decodeBits bs (simp only [Bool.false_eq_true, ite_false, ite_true] true bs:List Boolih:β (n : β) (x : n < bs.length), (decodeBits bs).testBit n = bs[n]n:βh:n + 1 < (true :: bs).lengthβ’ (2 * decodeBits bs + 1) / 2 = decodeBits bs; omega All goals completed! π) succ 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]
rw [hd, succ 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β’ (decodeBits bs).testBit n = (b :: bs)[n + 1] ih n (by 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β’ n < bs.length simpa using h All goals completed! π)] succ 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]
simp 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 := by R:RankedAlphabetv:βr:βh:R.arOf v = some rβ’ r β€ R.maxArity
rw [arOf R:RankedAlphabetv:βr:βh:(if h : v < R.card then some (R.arity β¨v, hβ©) else none) = some rβ’ r β€ R.maxArity] at h R:RankedAlphabetv:βr:βh:(if h : v < R.card then some (R.arity β¨v, hβ©) else none) = some rβ’ r β€ R.maxArity
split at h isTrue R:RankedAlphabetv:βr:βhβ:v < R.cardh:some (R.arity β¨v, hββ©) = some rβ’ r β€ R.maxArityisFalse R:RankedAlphabetv:βr:βhβ:Β¬v < R.cardh:none = some rβ’ r β€ R.maxArity
Β· isTrue R:RankedAlphabetv:βr:βhβ:v < R.cardh:some (R.arity β¨v, hββ©) = some rβ’ r β€ R.maxArity rename_i hlt isTrue R:RankedAlphabetv:βr:βhlt:v < R.cardh:some (R.arity β¨v, hββ©) = some rβ’ r β€ R.maxArity
rw [β Option.some.inj h isTrue R:RankedAlphabetv:βr:βhlt:v < R.cardh:some (R.arity β¨v, hββ©) = some rβ’ R.arity β¨v, hltβ© β€ R.maxArity] isTrue R:RankedAlphabetv:βr:βhlt:v < R.cardh:some (R.arity β¨v, hββ©) = some rβ’ R.arity β¨v, hltβ© β€ R.maxArity
exact arity_le_maxArity R β¨v, hltβ© All goals completed! π
Β· isFalse R:RankedAlphabetv:βr:βhβ:Β¬v < R.cardh:none = some rβ’ r β€ R.maxArity exact absurd h (by R:RankedAlphabetv:βr:βhβ:Β¬v < R.cardh:none = some rβ’ Β¬none = some r nofun All goals completed! π)endend RankedAlphabet