Skip to main content

uor_matmul_codec/
codecs.rs

1//! The tiers (§6.2).
2//!
3//! Instantiations, not special cases. Each is roughly twenty lines, and none of
4//! them contains any arithmetic that the others do not: a tier names a decode,
5//! and the accumulation downstream of it is the same accumulation for every
6//! tier in this file.
7
8use core::marker::PhantomData;
9
10use bytemuck::TransparentWrapper as _;
11use uor_matmul_core::{Alphabet, Bound, FloatElement, IntegerElement, Whole};
12
13use crate::tier::{Codec, Enumerable, IndexStream, TierId};
14
15/// How many codes a `u16`-indexed table can actually be reached with.
16///
17/// A property of the code type, not a limit on the table: a `Grid<70000>` is a
18/// well-formed decode whose entries past this are unreachable, and saying so is
19/// how the enumeration stays honest about its own size.
20const U16_CODES: usize = u16::MAX as usize + 1;
21
22/// The three parameters `Offset` carries without storing: the input bound, the
23/// output bound, and the element type. Invariant in none of them, so the marker
24/// is a function pointer rather than a `PhantomData<T>`.
25type OffsetMarker<BdIn, BdOut, E> = PhantomData<fn() -> (BdIn, BdOut, E)>;
26
27/// The same marker for the width-parameterized constant-table tiers: the
28/// element, the bound, and the code width, none of them stored.
29type TierMarker<E, Bd, K> = PhantomData<fn() -> (E, Bd, K)>;
30
31// ---------------------------------------------------------------------------
32// Identity
33// ---------------------------------------------------------------------------
34
35/// Decoding is a validated copy.
36///
37/// Zero-sized. The validation is the alphabet wrap itself, performed once at
38/// the boundary by `as_alphabet_full` (which cannot fail) or `as_alphabet`
39/// (which reports the observed bound rather than failing), so the code type is
40/// [`Alphabet`] and there is nothing left for the decode to check (§5.2, §6.2).
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
42pub struct Identity;
43
44impl<E: IntegerElement, Bd: Bound> Codec<E, Bd> for Identity {
45    type Code = Alphabet<E, Bd>;
46    const MAX_BLOCK: usize = 1;
47    const TIER: TierId = TierId::Identity;
48
49    fn decode_element(&self, code: Self::Code, _i: usize) -> Alphabet<E, Bd> {
50        code
51    }
52
53    fn decode_seq(&self, codes: &[Self::Code], out: &mut [Alphabet<E, Bd>]) -> usize {
54        out[..codes.len()].copy_from_slice(codes);
55        codes.len()
56    }
57}
58
59// `Identity` does not implement `Enumerable`, deliberately. Its code space is
60// the alphabet itself --- `2^32` values over `i32` --- and its block is one, so
61// a table indexed by it would be both unholdable and pointless: `tabulation_pays`
62// refuses `MAX_BLOCK == 1` on op count anyway. The absence is enforced by the
63// type system rather than by a runtime refusal, which is why the tabulated
64// traversal needs no branch for it (§4).
65
66// ---------------------------------------------------------------------------
67// Grid
68// ---------------------------------------------------------------------------
69
70/// Any lookup codec, of any code width.
71///
72/// The 16-entry i4 grid is `Grid<16>`; a 65536-entry one is `Grid<65536>`. The
73/// library carries no hardcoded table size, and no size is privileged.
74///
75/// The code space of an `N`-entry grid is `Z/N`, so an arbitrary `u16` indexes
76/// it modulo `N`. That is not a clamp and not an error path: it is what
77/// "index into a table of `N` entries" means, and it is why this decode is
78/// total on all `2^16` codes (C6).
79#[derive(Clone, Copy, Debug)]
80pub struct Grid<'a, E: IntegerElement, Bd: Bound, const N: usize> {
81    table: &'a [Alphabet<E, Bd>; N],
82}
83
84impl<'a, E: IntegerElement, Bd: Bound, const N: usize> Grid<'a, E, Bd, N> {
85    /// Borrow a decode table. The table is `Alphabet<E, Bd>`, so its image is
86    /// in the alphabet by construction and there is nothing to validate.
87    pub const fn new(table: &'a [Alphabet<E, Bd>; N]) -> Self {
88        Self { table }
89    }
90
91    /// The table.
92    pub const fn table(&self) -> &'a [Alphabet<E, Bd>; N] {
93        self.table
94    }
95}
96
97impl<E: IntegerElement, Bd: Bound, const N: usize> Codec<E, Bd> for Grid<'_, E, Bd, N> {
98    type Code = u16;
99    const MAX_BLOCK: usize = 1;
100    const TIER: TierId = TierId::Grid;
101
102    fn decode_element(&self, code: Self::Code, _i: usize) -> Alphabet<E, Bd> {
103        self.table[(code as usize) % N]
104    }
105}
106
107impl<E: IntegerElement, Bd: Bound, const N: usize> Enumerable<E, Bd> for Grid<'_, E, Bd, N> {
108    // The reachable code space, which is the table size unless the table is
109    // wider than the code type can address.
110    const CODE_SPACE: usize = if N < U16_CODES { N } else { U16_CODES };
111
112    fn code_at(index: usize) -> Self::Code {
113        // `index < CODE_SPACE <= U16_CODES`, so the cast is the identity on the
114        // enumeration's own domain. The `%` is what makes it total off it, and
115        // the `max(1)` keeps it total for a table with no entries at all --- a
116        // codec `tabulation_pays` refuses, so no traversal reaches it.
117        (index % Self::CODE_SPACE.max(1)) as u16
118    }
119
120    fn index_of(code: Self::Code) -> usize {
121        // The same reduction `decode_element` performs, so equal indices and
122        // equal decodes are the same relation.
123        (code as usize) % Self::CODE_SPACE.max(1)
124    }
125
126    fn as_index_stream(codes: &[u16]) -> Option<IndexStream<'_>> {
127        // `index_of` is `% CODE_SPACE`, which is `& (CODE_SPACE - 1)` exactly
128        // when the space is a power of two --- and then the stored `u16` is the
129        // index, masked. At any other entry count the two differ and the
130        // traversal builds the stream, at the same bytes.
131        Self::CODE_SPACE
132            .is_power_of_two()
133            .then_some(IndexStream::U16(codes))
134    }
135}
136
137// ---------------------------------------------------------------------------
138// Packed
139// ---------------------------------------------------------------------------
140
141/// Unpacks `P` sub-codes from one stored byte, then defers to `C`.
142///
143/// The i4 tier is `Packed<Grid<16>, 2>`. **Low sub-code first** --- normative,
144/// pinned by case `nibble-order` and asserted by `CK-03`.
145///
146/// `P` is arbitrary: two nibbles, four two-bit codes, eight one-bit codes. The
147/// sub-code width is `8 / P`, so `P` must divide 8, which [`Packed::new`]
148/// checks once at construction.
149#[derive(Clone, Copy, Debug)]
150pub struct Packed<C, const P: usize> {
151    inner: C,
152}
153
154impl<C, const P: usize> Packed<C, P> {
155    /// Bits per sub-code.
156    pub const SUB_BITS: u32 = (8 / P) as u32;
157
158    /// Wrap `inner`, unpacking `P` sub-codes per byte.
159    ///
160    /// `None` when `P` does not divide 8, which means no such packing exists.
161    pub fn new(inner: C) -> Option<Self> {
162        if P == 0 || 8 % P != 0 {
163            return None;
164        }
165        Some(Self { inner })
166    }
167}
168
169impl<E, Bd, C, const P: usize> Codec<E, Bd> for Packed<C, P>
170where
171    E: IntegerElement,
172    Bd: Bound,
173    C: Codec<E, Bd>,
174    C::Code: From<u8>,
175{
176    type Code = u8;
177    const MAX_BLOCK: usize = P * C::MAX_BLOCK;
178    const TIER: TierId = TierId::Packed;
179    const IS_FIXED_WIDTH: bool = C::IS_FIXED_WIDTH;
180
181    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, Bd> {
182        let bits = Self::SUB_BITS;
183        let mask: u8 = if bits >= 8 {
184            u8::MAX
185        } else {
186            (1u8 << bits) - 1
187        };
188        // Low sub-code first. This is normative: a reader that took the high
189        // sub-code first would decode a different matrix from the same bytes,
190        // so the order is pinned rather than left to convention (CK-03).
191        let p = i / C::MAX_BLOCK;
192        let sub = (code >> (bits * p as u32)) & mask;
193        self.inner
194            .decode_element(C::Code::from(sub), i % C::MAX_BLOCK)
195    }
196}
197
198/// How many distinct sub-codes one field of a packed byte can store.
199///
200/// The inner codec's own space, unless the field is narrower than that space ---
201/// a `Grid<256>` packed two to a byte can only ever be handed a nibble, so its
202/// reachable sub-space is sixteen and not two hundred and fifty-six. Getting
203/// this wrong in either direction breaks a law: too large and `code_at` names
204/// codes the byte cannot hold, too small and `index_of` collides.
205const fn sub_space(inner: usize, sub_bits: u32) -> usize {
206    let per_field = 1usize << sub_bits;
207    if inner < per_field {
208        inner
209    } else {
210        per_field
211    }
212}
213
214impl<E, Bd, C, const P: usize> Enumerable<E, Bd> for Packed<C, P>
215where
216    E: IntegerElement,
217    Bd: Bound,
218    C: Enumerable<E, Bd>,
219    C::Code: From<u8>,
220{
221    // Mixed radix in the sub-space, which is at most `2^(SUB_BITS * P) = 256`,
222    // so this cannot overflow whatever `P` and `C` are.
223    const CODE_SPACE: usize = sub_space(C::CODE_SPACE, Self::SUB_BITS).pow(P as u32);
224
225    fn code_at(index: usize) -> Self::Code {
226        let bits = Self::SUB_BITS;
227        let radix = sub_space(C::CODE_SPACE, bits).max(1);
228        let mut rest = index % Self::CODE_SPACE.max(1);
229        let mut code = 0u8;
230        for field in 0..P {
231            let digit = rest % radix;
232            rest /= radix;
233            // Low sub-code first, the same order `decode_element` reads them in.
234            code |= (digit as u8) << (bits * field as u32);
235        }
236        code
237    }
238
239    fn index_of(code: Self::Code) -> usize {
240        let bits = Self::SUB_BITS;
241        let mask: u8 = if bits >= 8 {
242            u8::MAX
243        } else {
244            (1u8 << bits) - 1
245        };
246        let radix = sub_space(C::CODE_SPACE, bits).max(1);
247        let mut index = 0usize;
248        let mut place = 1usize;
249        for field in 0..P {
250            let sub = (code >> (bits * field as u32)) & mask;
251            let digit = C::index_of(C::Code::from(sub)) % radix;
252            index += digit * place;
253            place *= radix;
254        }
255        index
256    }
257}
258
259// ---------------------------------------------------------------------------
260// Book
261// ---------------------------------------------------------------------------
262
263/// Any codebook: `N` entries of `BLK` alphabet elements each.
264///
265/// E8 is `Book<256, 8>`. Nothing privileges that shape; `BLK` and `N` are
266/// parameters, and no quality claim attaches to any table (N3).
267#[derive(Clone, Copy, Debug)]
268pub struct Book<
269    'a,
270    E: IntegerElement,
271    Bd: Bound,
272    const N: usize,
273    const BLK: usize,
274    K: SymbolCode = u16,
275> {
276    table: &'a [[Alphabet<E, Bd>; BLK]; N],
277    // The width is a parameter of the decode, not of the table: the struct
278    // stores nothing of it.
279    _code: PhantomData<fn() -> K>,
280}
281
282impl<'a, E: IntegerElement, Bd: Bound, const N: usize, const BLK: usize, K: SymbolCode>
283    Book<'a, E, Bd, N, BLK, K>
284{
285    /// Borrow a codebook. On an embedded target this is a pointer into flash.
286    pub const fn new(table: &'a [[Alphabet<E, Bd>; BLK]; N]) -> Self {
287        Self {
288            table,
289            _code: PhantomData,
290        }
291    }
292
293    /// The codebook.
294    pub const fn table(&self) -> &'a [[Alphabet<E, Bd>; BLK]; N] {
295        self.table
296    }
297}
298
299impl<E: IntegerElement, Bd: Bound, const N: usize, const BLK: usize, K: SymbolCode> Codec<E, Bd>
300    for Book<'_, E, Bd, N, BLK, K>
301{
302    type Code = K;
303    const MAX_BLOCK: usize = BLK;
304    const TIER: TierId = TierId::Book;
305
306    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, Bd> {
307        self.table[K::index(code) % N][i % BLK]
308    }
309
310    fn decode_into(&self, code: Self::Code, out: &mut [Alphabet<E, Bd>]) -> usize {
311        out[..BLK].copy_from_slice(&self.table[K::index(code) % N]);
312        BLK
313    }
314}
315
316impl<E: IntegerElement, Bd: Bound, const N: usize, const BLK: usize, K: SymbolCode>
317    Enumerable<E, Bd> for Book<'_, E, Bd, N, BLK, K>
318{
319    const CODE_SPACE: usize = if N < K::CODES { N } else { K::CODES };
320
321    fn code_at(index: usize) -> Self::Code {
322        K::at(index % Self::CODE_SPACE.max(1))
323    }
324
325    fn index_of(code: Self::Code) -> usize {
326        K::index(code) % Self::CODE_SPACE.max(1)
327    }
328
329    fn as_index_stream(codes: &[K]) -> Option<IndexStream<'_>> {
330        // `index_of` is `% CODE_SPACE`, which is `& (CODE_SPACE - 1)` exactly
331        // when the space is a power of two --- and then the stored code, at
332        // either width, is the index, masked. At any other entry count the two
333        // differ and the traversal builds the stream, at the same bytes.
334        K::index_stream(codes, Self::CODE_SPACE)
335    }
336}
337
338// ---------------------------------------------------------------------------
339// Sign
340// ---------------------------------------------------------------------------
341
342/// Weights in `{-1, +1}`, one bit per element, with no table at all.
343///
344/// The `Packed<Grid<2>,8>` spelling of the same decode (`CK-13`) tabulates but
345/// can never answer [`Enumerable::as_index_stream`]: a packed byte's index is
346/// a mixed-radix decomposition of it, so the tabulated traversal builds the
347/// index stream it gathers from. Measured, that build is a fifth to a third of
348/// the work at a one-row tile (ANALYSIS.md, "What the missing index stream
349/// costs the sign composition"). This tier spells the same decode with the
350/// code *being* the index --- `Code = u16`, one bit per element of the block
351/// --- so the operand's own memory is the stream and there is nothing to
352/// build. `CK-11` pins the two spellings byte for byte.
353///
354/// The codebook is the constant `{-1, +1}`, so there is nothing to borrow and
355/// no lifetime: the decode is a bit test. **Bit `t` of the code is element
356/// `t`; set is `+1`, clear is `-1`, low bit first** --- the order `Packed`
357/// reads its sub-codes in (`CK-03`), so a `Sign` code spells the block the
358/// composition spells from the same byte value. The convention is normative:
359/// the encoder side is out of tree, and this decode is the whole contract
360/// with it.
361#[derive(Clone, Copy, Debug)]
362pub struct Sign<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode = u16> {
363    _marker: TierMarker<E, Bd, K>,
364}
365
366impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Sign<E, Bd, BLK, K> {
367    /// The one non-existence check: an alphabet that does not admit `+-1` has
368    /// no sign codec. Decided at construction, before any arithmetic, like
369    /// every other non-existence in this library (C6).
370    pub const fn new() -> Option<Self> {
371        if Bd::VALUE < 1 || E::FULL < 1 {
372            return None;
373        }
374        Some(Self {
375            _marker: PhantomData,
376        })
377    }
378}
379
380impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Codec<E, Bd>
381    for Sign<E, Bd, BLK, K>
382{
383    type Code = K;
384    const MAX_BLOCK: usize = BLK;
385    const TIER: TierId = TierId::Sign;
386
387    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, Bd> {
388        let bit = i % BLK;
389        // The guard keeps the shift total past the code type's own width: at
390        // `BLK > K::BITS` a code holds no bit for those positions, and they
391        // decode to `-1` like any clear bit --- the same rule at either width.
392        let v = if bit < K::BITS as usize && (K::index(code) >> bit) & 1 == 1 {
393            E::ONE
394        } else {
395            // `0 - 1`: exact for every integer element, never a wrap.
396            E::ZERO.sub(E::ONE)
397        };
398        // `new` established that the declared alphabet admits `+-1`, so the
399        // wrap cannot put a value outside its bound: it is that O(1) check
400        // being cashed in, exactly as `Offset`'s decode cashes in its own.
401        Alphabet::wrap(v)
402    }
403}
404
405impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Enumerable<E, Bd>
406    for Sign<E, Bd, BLK, K>
407{
408    // Every bit pattern of the block, capped at what the code type can
409    // address --- the same honesty about the code type's width as `Grid`'s.
410    const CODE_SPACE: usize = if BLK < K::BITS as usize {
411        1 << BLK
412    } else {
413        K::CODES
414    };
415
416    // The whole point of the tier: the code *is* the index, and the decode is
417    // the bit test. The Gray-walk build reads this flag and nothing else.
418    const SIGN_BIT_BOOK: bool = true;
419
420    fn code_at(index: usize) -> Self::Code {
421        K::at(index % Self::CODE_SPACE.max(1))
422    }
423
424    fn index_of(code: Self::Code) -> usize {
425        // A mask, not a remainder: the space is a power of two at every block
426        // width, and the stored code *is* the index, which is the whole point
427        // of the tier.
428        K::index(code) & (Self::CODE_SPACE - 1)
429    }
430
431    fn as_index_stream(codes: &[K]) -> Option<IndexStream<'_>> {
432        // Always --- not at the power-of-two entry counts, as `Grid` and
433        // `Book` answer, but at every block width, because the code addresses
434        // the enumeration unconditionally: the space is a power of two at
435        // every `BLK`. This is the answer `Packed` can never give, and the gap
436        // this tier exists to close.
437        K::index_stream(codes, Self::CODE_SPACE)
438    }
439}
440
441// ---------------------------------------------------------------------------
442// Ternary
443// ---------------------------------------------------------------------------
444
445/// Weights in `{-1, 0, +1}`, two bits per element, with no table at all.
446///
447/// The `Packed<Grid<4>,4>` spelling of the same decode (`CK-10`) tabulates but
448/// can never answer [`Enumerable::as_index_stream`]: a packed byte's index is
449/// a mixed-radix decomposition of it, so the tabulated traversal builds the
450/// index stream it gathers from. This tier spells the same decode with the
451/// code *being* the index --- `Code = u16`, one base-4 digit per element of
452/// the block, and a base-4 digit stream is a power-of-two code space (4^BLK =
453/// 2^(2BLK)) --- so the operand's own memory is the stream and there is
454/// nothing to build. `CK-12` pins the two spellings byte for byte.
455///
456/// The codebook is the constant `{-1, 0, +1}`, so there is nothing to borrow
457/// and no lifetime: the decode is a two-bit field test. **Digit `t` of the
458/// code --- bits `2t` and `2t+1` --- is element `t`; 0 is `-1`, 1 is `0`, 2 is
459/// `+1`, and 3, the dead digit no ternary encoder emits, decodes to `0`, low
460/// digit first** --- the order `Packed` reads its sub-codes in (`CK-03`) and
461/// the decode the spelling's `[-1, 0, +1, dead]` table gives every digit
462/// (`CK-10`), so a `Ternary` code spells the block the composition spells from
463/// the same byte value. The convention is normative: the encoder side is out
464/// of tree, and this decode is the whole contract with it.
465#[derive(Clone, Copy, Debug)]
466pub struct Ternary<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode = u16> {
467    _marker: TierMarker<E, Bd, K>,
468}
469
470impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Ternary<E, Bd, BLK, K> {
471    /// The one non-existence check: an alphabet that does not admit `+-1` has
472    /// no ternary codec --- `0` is in every alphabet. Decided at construction,
473    /// before any arithmetic, like every other non-existence in this library
474    /// (C6).
475    pub const fn new() -> Option<Self> {
476        if Bd::VALUE < 1 || E::FULL < 1 {
477            return None;
478        }
479        Some(Self {
480            _marker: PhantomData,
481        })
482    }
483}
484
485impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Codec<E, Bd>
486    for Ternary<E, Bd, BLK, K>
487{
488    type Code = K;
489    const MAX_BLOCK: usize = BLK;
490    const TIER: TierId = TierId::Ternary;
491
492    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, Bd> {
493        let shift = 2 * (i % BLK);
494        // The guard keeps the shift total past the code type's own width: at
495        // `BLK > K::BITS / 2` a code holds no digit for those positions, and
496        // they decode to `-1` like any zero field --- the same rule at either
497        // width.
498        let digit = if shift < K::BITS as usize {
499            (K::index(code) >> shift) & 3
500        } else {
501            0
502        };
503        let v = match digit {
504            // `0 - 1`: exact for every integer element, never a wrap.
505            0 => E::ZERO.sub(E::ONE),
506            2 => E::ONE,
507            // 1 is the zero digit, and 3 the dead one --- which also decodes
508            // to 0, a priced duplicate of it rather than an error (`CK-10`).
509            _ => E::ZERO,
510        };
511        // `new` established that the declared alphabet admits `+-1`, so the
512        // wrap cannot put a value outside its bound: it is that O(1) check
513        // being cashed in, exactly as `Sign`'s decode cashes in its own.
514        Alphabet::wrap(v)
515    }
516}
517
518impl<E: IntegerElement, Bd: Bound, const BLK: usize, K: SymbolCode> Enumerable<E, Bd>
519    for Ternary<E, Bd, BLK, K>
520{
521    // 4^BLK, capped at what the code type can address --- a power of two at
522    // every block width, the same honesty about the code type's width as
523    // `Grid`'s, and what makes the index stream unconditional below.
524    const CODE_SPACE: usize = if 2 * BLK < K::BITS as usize {
525        1 << (2 * BLK)
526    } else {
527        K::CODES
528    };
529
530    fn code_at(index: usize) -> Self::Code {
531        K::at(index % Self::CODE_SPACE.max(1))
532    }
533
534    fn index_of(code: Self::Code) -> usize {
535        // A mask, not a remainder: the space is a power of two at every block
536        // width, and the stored code *is* the index, which is the whole point
537        // of the tier.
538        K::index(code) & (Self::CODE_SPACE - 1)
539    }
540
541    fn as_index_stream(codes: &[K]) -> Option<IndexStream<'_>> {
542        // Always, as `Sign` answers and for one reason more: 4^BLK is 2^(2BLK),
543        // so the code addresses the enumeration unconditionally. This is the
544        // answer `Packed` can never give, and the gap this tier exists to close.
545        K::index_stream(codes, Self::CODE_SPACE)
546    }
547}
548
549// ---------------------------------------------------------------------------
550// Offset
551// ---------------------------------------------------------------------------
552
553/// `d(c) - z`: asymmetric quantization as a codec composition, not a feature.
554///
555/// A zero point is a codec, which is why the library needs no separate
556/// "asymmetric" mode and no branch for one (§1.3).
557///
558/// Decoding widens the alphabet: an inner value bounded by `BdIn` and a zero
559/// point of magnitude `|z|` produce a value bounded by `BdIn + |z|`. That is
560/// why the output bound is a separate parameter, and why [`Offset::new`]
561/// checks, once and in O(1), that the declared output bound covers the image
562/// and that no value overflows the element type. A `None` means no such codec
563/// exists --- the same category as a non-conformant shape, decided before any
564/// arithmetic (C6).
565#[derive(Clone, Copy, Debug)]
566pub struct Offset<E: IntegerElement, BdIn: Bound, BdOut: Bound, C: Codec<E, BdIn>> {
567    inner: C,
568    zero: E,
569    _marker: OffsetMarker<BdIn, BdOut, E>,
570}
571
572impl<E: IntegerElement, BdIn: Bound, BdOut: Bound, C: Codec<E, BdIn>> Offset<E, BdIn, BdOut, C> {
573    /// Compose `inner` with the zero point `zero`.
574    ///
575    /// `None` when `BdIn + |zero|` exceeds the declared output bound or the
576    /// element type's own range, in which case the decode this describes does
577    /// not land in the alphabet it claims to.
578    pub fn new(inner: C, zero: E) -> Option<Self> {
579        let image = BdIn::VALUE.checked_add(zero.magnitude())?;
580        if image > BdOut::VALUE || image > E::FULL {
581            return None;
582        }
583        Some(Self {
584            inner,
585            zero,
586            _marker: PhantomData,
587        })
588    }
589
590    /// The zero point.
591    pub const fn zero(&self) -> E {
592        self.zero
593    }
594}
595
596impl<E, BdIn, BdOut, C> Codec<E, BdOut> for Offset<E, BdIn, BdOut, C>
597where
598    E: IntegerElement,
599    BdIn: Bound,
600    BdOut: Bound,
601    C: Codec<E, BdIn>,
602{
603    type Code = C::Code;
604    const MAX_BLOCK: usize = C::MAX_BLOCK;
605    const TIER: TierId = TierId::Offset;
606    const IS_FIXED_WIDTH: bool = C::IS_FIXED_WIDTH;
607
608    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, BdOut> {
609        let inner = self.inner.decode_element(code, i).get();
610        // Exact: `new` established that `BdIn + |zero| <= min(BdOut, E::FULL)`,
611        // so this subtraction neither overflows the element type nor leaves the
612        // declared output alphabet. The wrap is therefore not an unchecked
613        // assertion; it is the O(1) check in `new` being cashed in.
614        Alphabet::<E, BdOut>::wrap(inner.sub(self.zero))
615    }
616}
617
618impl<E, BdIn, BdOut, C> Enumerable<E, BdOut> for Offset<E, BdIn, BdOut, C>
619where
620    E: IntegerElement,
621    BdIn: Bound,
622    BdOut: Bound,
623    C: Enumerable<E, BdIn>,
624{
625    // A zero point relabels the *image*, never the code space: the same codes
626    // name the same blocks, shifted. So the enumeration is the inner one,
627    // unchanged, and a tabulated asymmetric quantization needs nothing new.
628    const CODE_SPACE: usize = C::CODE_SPACE;
629
630    fn code_at(index: usize) -> Self::Code {
631        C::code_at(index)
632    }
633
634    fn index_of(code: Self::Code) -> usize {
635        C::index_of(code)
636    }
637
638    fn as_index_stream(codes: &[Self::Code]) -> Option<IndexStream<'_>> {
639        // A zero point relabels the image, never the code space, so whether the
640        // code addresses the enumeration is the inner codec's answer unchanged.
641        C::as_index_stream(codes)
642    }
643}
644
645// ---------------------------------------------------------------------------
646// Runs
647// ---------------------------------------------------------------------------
648
649/// Sparse storage as a codec, not as a separate algorithm and not as a separate
650/// crate (D-16).
651///
652/// A run is `(length, code)`: `length` consecutive decoded elements, all of
653/// them `d(code)`. A gap is a run whose code decodes to the alphabet's zero, so
654/// the zeros are *explicit* and the arithmetic downstream stays dense. No
655/// sparse-specific speedup is claimed; the benefit is residency, and `CG-03`
656/// measures it like any other codec's.
657///
658/// A code is a *run index*, and the run's length is what [`Codec::decode_len`]
659/// reports. `MAX_RUN` is the longest run the caller declares, so this tier is
660/// variable-length within a compile-time bound --- which is exactly what the
661/// `MAX_BLOCK` spelling is for (S4, S5b).
662#[derive(Clone, Copy, Debug)]
663pub struct Runs<'a, E: IntegerElement, Bd: Bound, C: Codec<E, Bd>, const MAX_RUN: usize> {
664    runs: &'a [(u32, C::Code)],
665    inner: C,
666    _marker: PhantomData<fn() -> (E, Bd)>,
667}
668
669impl<'a, E: IntegerElement, Bd: Bound, C: Codec<E, Bd>, const MAX_RUN: usize>
670    Runs<'a, E, Bd, C, MAX_RUN>
671{
672    /// Borrow a run list.
673    ///
674    /// `None` when a run is empty or longer than `MAX_RUN`, both of which mean
675    /// the list describes no stream. Decided at construction, before any
676    /// arithmetic, like every other non-existence in this library (C6).
677    pub fn new(inner: C, runs: &'a [(u32, C::Code)]) -> Option<Self> {
678        if runs
679            .iter()
680            .any(|&(len, _)| len == 0 || len as usize > MAX_RUN)
681        {
682            return None;
683        }
684        Some(Self {
685            runs,
686            inner,
687            _marker: PhantomData,
688        })
689    }
690
691    /// The number of stored runs, which is the residency this tier buys.
692    pub const fn run_count(&self) -> usize {
693        self.runs.len()
694    }
695
696    /// The decoded length of the whole run list.
697    ///
698    /// `CK-06` asserts this equals the declared row width, which is the
699    /// invariant that lets a variable-length tier live inside `Codec`.
700    pub fn decoded_len(&self) -> usize {
701        self.runs.iter().map(|&(len, _)| len as usize).sum()
702    }
703}
704
705impl<E: IntegerElement, Bd: Bound, C: Codec<E, Bd>, const MAX_RUN: usize> Codec<E, Bd>
706    for Runs<'_, E, Bd, C, MAX_RUN>
707{
708    type Code = u32;
709    const MAX_BLOCK: usize = MAX_RUN;
710    const TIER: TierId = TierId::Runs;
711    // The one variable-length tier: a run is as long as it is.
712    const IS_FIXED_WIDTH: bool = false;
713
714    fn decode_len(&self, run: Self::Code) -> usize {
715        self.runs
716            .get(run as usize)
717            .map_or(0, |&(len, _)| len as usize)
718    }
719
720    fn decode_element(&self, run: Self::Code, i: usize) -> Alphabet<E, Bd> {
721        match self.runs.get(run as usize) {
722            // Every element of a run decodes the run's code. For an inner tier
723            // that is itself a block codec, the position within the block is
724            // `i % C::MAX_BLOCK`, so a run of an E8 codeword repeats the whole
725            // codeword rather than its first element.
726            Some(&(_, code)) => self.inner.decode_element(code, i % C::MAX_BLOCK),
727            // Past the end of the run list: the alphabet's zero, explicitly.
728            None => Alphabet::ZERO,
729        }
730    }
731}
732
733// `Runs` does not implement `Enumerable`. A run code carries a *length*, so the
734// table would have to be indexed by `(value, length)` rather than by code, and
735// the length is artifact-local data rather than the codec's fixed enumerable
736// space. Such a pair is not the `code -> fixed block` declaration tabulation
737// requires. The absence is structural at the traversal's type boundary, not a
738// performance capability withheld behind a runtime refusal (§4).
739
740// ---------------------------------------------------------------------------
741// Transcode
742// ---------------------------------------------------------------------------
743
744/// A total map from one code space to another.
745///
746/// This is the first half of the upstream `transcode_decode` composite: the
747/// part that is a relabelling rather than a decode.
748pub trait CodeMap<In: Copy, Out: Copy>: Send + Sync {
749    /// Relabel. Total on the whole input code space.
750    fn map(&self, code: In) -> Out;
751}
752
753impl<In: Copy, Out: Copy, F> CodeMap<In, Out> for F
754where
755    F: Fn(In) -> Out + Send + Sync,
756{
757    fn map(&self, code: In) -> Out {
758        self(code)
759    }
760}
761
762/// The upstream `transcode_decode` composite: relabel, then decode.
763///
764/// Closed under composition, which is what makes a tier change a *change of
765/// artifact* rather than a change of arithmetic (`CK-04`, `CL-MM03`).
766///
767/// The plan spells this `Transcode<C1, C2>`; the first parameter is a
768/// [`CodeMap`] rather than a second [`Codec`], because a decode into the
769/// alphabet cannot be the input of another decode --- only a relabelling can.
770#[derive(Clone, Copy, Debug)]
771pub struct Transcode<M, C, In> {
772    map: M,
773    inner: C,
774    _marker: PhantomData<fn() -> In>,
775}
776
777impl<M, C, In> Transcode<M, C, In> {
778    /// Compose a relabelling with a decode.
779    pub const fn new(map: M, inner: C) -> Self {
780        Self {
781            map,
782            inner,
783            _marker: PhantomData,
784        }
785    }
786}
787
788impl<E, Bd, M, C, In> Codec<E, Bd> for Transcode<M, C, In>
789where
790    E: IntegerElement,
791    Bd: Bound,
792    C: Codec<E, Bd>,
793    M: CodeMap<In, C::Code>,
794    In: Copy + Send + Sync + 'static,
795{
796    type Code = In;
797    const MAX_BLOCK: usize = C::MAX_BLOCK;
798    const TIER: TierId = TierId::Transcode;
799    const IS_FIXED_WIDTH: bool = C::IS_FIXED_WIDTH;
800
801    fn decode_element(&self, code: Self::Code, i: usize) -> Alphabet<E, Bd> {
802        self.inner.decode_element(self.map.map(code), i)
803    }
804}
805
806// `Transcode` does not implement `Enumerable`, and the reason is the shape of
807// this repository's spelling rather than anything about tabulation. The plan
808// spells the composite `Transcode<C1, C2>` with two codecs; here the first
809// parameter is a [`CodeMap`] --- a *function* --- because a decode into the
810// alphabet cannot be the input of another decode, only a relabelling can. A
811// function's domain cannot be enumerated and `code_at` would need its inverse.
812//
813// Nothing is lost. `decode(c) = inner.decode(map(c))`, so the table over the
814// inner code space *is* the table for the composite: a caller tabulates `C` and
815// reaches the entry with `C::index_of(map(code))`. The relabelling is a property
816// of the code stream, and tabulation has already left the code stream behind by
817// the time it reads the table.
818
819// ---------------------------------------------------------------------------
820// Arena
821// ---------------------------------------------------------------------------
822
823/// A code type whose own order is an enumeration's order.
824///
825/// `u16` and `u8`, and nothing else: the width is a fact about the artifact's
826/// storage, and the two widths are the two residencies a codec has a use for
827/// --- two bytes a symbol, and one where the code space fits a byte.
828///
829/// The trait exists so that [`Arena`], [`Book`], [`Sign`] and [`Ternary`] are
830/// one tier at two widths rather than two tiers with one decode: the table
831/// read, the bit fields, the enumeration, and the canonicalization are written
832/// once, against these methods.
833pub trait SymbolCode: Copy + Send + Sync + 'static {
834    /// How many distinct codes the type holds.
835    const CODES: usize;
836
837    /// The type's own width in bits, for the shift guard a bit-field decode
838    /// totals against: a position past it reads as a clear field, at either
839    /// width.
840    const BITS: u32;
841
842    /// The code at `index` in the type's own order. Total below `CODES`.
843    fn at(index: usize) -> Self;
844
845    /// Where `code` sits in the type's own order.
846    fn index(code: Self) -> usize;
847
848    /// The stored code stream read as the index stream it already is, when it
849    /// is one.
850    ///
851    /// `index_of` is `% space` at every tier here, which is `& (space - 1)`
852    /// exactly when the space is a power of two --- and then the stored code,
853    /// masked, *is* the index, at either width, and the traversal borrows it.
854    /// At any other entry count the two differ and the traversal builds the
855    /// stream, at the same bytes. The variant names the width: a byte stream
856    /// answers [`IndexStream::U8`], and the gather dispatches on it once.
857    fn index_stream(codes: &[Self], space: usize) -> Option<IndexStream<'_>>;
858}
859
860impl SymbolCode for u16 {
861    const CODES: usize = U16_CODES;
862    const BITS: u32 = u16::BITS;
863
864    fn at(index: usize) -> Self {
865        // `index < CODE_SPACE <= CODES` at every call site, so the cast is the
866        // identity on the enumeration's own domain.
867        index as u16
868    }
869
870    fn index(code: Self) -> usize {
871        code as usize
872    }
873
874    fn index_stream(codes: &[Self], space: usize) -> Option<IndexStream<'_>> {
875        space.is_power_of_two().then_some(IndexStream::U16(codes))
876    }
877}
878
879impl SymbolCode for u8 {
880    const CODES: usize = u8::MAX as usize + 1;
881    const BITS: u32 = u8::BITS;
882
883    fn at(index: usize) -> Self {
884        // As `u16`'s: total on the enumeration's own domain.
885        index as u8
886    }
887
888    fn index(code: Self) -> usize {
889        code as usize
890    }
891
892    fn index_stream(codes: &[Self], space: usize) -> Option<IndexStream<'_>> {
893        space.is_power_of_two().then_some(IndexStream::U8(codes))
894    }
895}
896
897/// A codebook of one artifact's distinct bit patterns.
898///
899/// Mechanically the arena is a one-element block grid, and its decode is the
900/// same table read. What makes it a tier rather than a [`Grid`] is the
901/// construction discipline the token carries into the kappa manifest: the
902/// table is the source stream's distinct symbols in canonical order, built by
903/// [`canonicalize`], so two artifacts holding the same values share a
904/// codebook --- and an address --- whatever order their streams stored them
905/// in (§6.4, `CK-10`).
906///
907/// The element type is a float: the symbols are bit patterns, and the bound is
908/// [`Whole`] because membership is discharged by the table's construction and
909/// no magnitude question applies to a float (§5.2b).
910///
911/// The code width is a parameter, `u16` by default and `u8` where the
912/// artifact's distinct patterns fit a byte (`CK-14`): one type at two
913/// residencies, not two tiers. At a power-of-two code space the stored code
914/// masked *is* the index at either width, so both spellings answer their own
915/// stream to the tabulated gather --- `IndexStream::U16` and
916/// `IndexStream::U8` --- and the traversal builds nothing.
917#[derive(Clone, Copy, Debug)]
918pub struct Arena<'a, E: FloatElement, const N: usize, K: SymbolCode = u16> {
919    table: &'a [Alphabet<E, Whole<E>>; N],
920    // The width is a parameter of the decode, not of the table: the struct
921    // stores nothing of it.
922    _code: PhantomData<fn() -> K>,
923}
924
925impl<'a, E: FloatElement, const N: usize, K: SymbolCode> Arena<'a, E, N, K> {
926    /// Borrow a canonical codebook. The canonicalization is [`canonicalize`]'s
927    /// discipline; like [`Grid`], the borrow itself validates nothing (§6.2).
928    pub const fn new(table: &'a [Alphabet<E, Whole<E>>; N]) -> Self {
929        Self {
930            table,
931            _code: PhantomData,
932        }
933    }
934
935    /// The codebook.
936    pub const fn table(&self) -> &'a [Alphabet<E, Whole<E>>; N] {
937        self.table
938    }
939}
940
941impl<E: FloatElement, const N: usize, K: SymbolCode> Codec<E, Whole<E>> for Arena<'_, E, N, K> {
942    type Code = K;
943    const MAX_BLOCK: usize = 1;
944    const TIER: TierId = TierId::Arena;
945
946    fn decode_element(&self, code: Self::Code, _i: usize) -> Alphabet<E, Whole<E>> {
947        // The same total reduction `Grid` performs: an arbitrary code indexes
948        // a table of `N` entries modulo `N` (C6).
949        self.table[K::index(code) % N]
950    }
951}
952
953impl<E: FloatElement, const N: usize, K: SymbolCode> Enumerable<E, Whole<E>>
954    for Arena<'_, E, N, K>
955{
956    // The reachable code space, as for `Grid`: the table size unless the table
957    // is wider than the code type can address.
958    const CODE_SPACE: usize = if N < K::CODES { N } else { K::CODES };
959
960    fn code_at(index: usize) -> Self::Code {
961        K::at(index % Self::CODE_SPACE.max(1))
962    }
963
964    fn index_of(code: Self::Code) -> usize {
965        // The same reduction `decode_element` performs, so equal indices and
966        // equal decodes are the same relation.
967        K::index(code) % Self::CODE_SPACE.max(1)
968    }
969
970    fn as_index_stream(codes: &[K]) -> Option<IndexStream<'_>> {
971        K::index_stream(codes, Self::CODE_SPACE)
972    }
973}
974
975/// Sort `values` into canonical arena order and move the distinct symbols to
976/// the front, returning how many there are. The codebook is `&values[..n]`.
977///
978/// Canonical order is unsigned order on bit patterns, and equality is pattern
979/// equality: `-0.0` and `+0.0` are distinct symbols, two NaNs with different
980/// payloads are distinct symbols, and deciding either takes no float
981/// comparison (`CK-10`, `CU-01`). The sort is a heapsort: in place,
982/// allocation-free, and `O(n log n)` whatever the stream, so an adversarial
983/// order meets the same bound as a friendly one.
984pub fn canonicalize<E: FloatElement>(values: &mut [E]) -> usize {
985    heapsort_by_pattern(values);
986    // Equal patterns are adjacent after the sort; keep the first of each run.
987    let mut n = 0usize;
988    for i in 0..values.len() {
989        if n == 0 || values[i].symbol_bits() != values[n - 1].symbol_bits() {
990            values[n] = values[i];
991            n += 1;
992        }
993    }
994    n
995}
996
997/// Heapsort by bit pattern: a total order with no float comparisons.
998fn heapsort_by_pattern<E: FloatElement>(values: &mut [E]) {
999    fn sift_down<E: FloatElement>(values: &mut [E], mut root: usize, end: usize) {
1000        loop {
1001            let child = 2 * root + 1;
1002            if child >= end {
1003                return;
1004            }
1005            let mut swap = root;
1006            if values[swap].symbol_bits() < values[child].symbol_bits() {
1007                swap = child;
1008            }
1009            if child + 1 < end && values[swap].symbol_bits() < values[child + 1].symbol_bits() {
1010                swap = child + 1;
1011            }
1012            if swap == root {
1013                return;
1014            }
1015            values.swap(root, swap);
1016            root = swap;
1017        }
1018    }
1019
1020    let len = values.len();
1021    for start in (0..len / 2).rev() {
1022        sift_down(values, start, len);
1023    }
1024    for end in (1..len).rev() {
1025        values.swap(0, end);
1026        sift_down(values, 0, end);
1027    }
1028}