Skip to main content

uor_matmul_codec/
kappa.rs

1//! Content addressing (ยง6.4).
2//!
3//! A weight artifact's identity is the kappa label of its **canonical
4//! manifest**, not of its code bytes, so that a transcode between tiers is
5//! visibly a different artifact with a provably identical decoded stream. That
6//! is `CL-MM01` with an address attached to each side, and `CK-05` is the test
7//! that says it.
8//!
9//! Bulk arrays are referenced by digest rather than inlined, so the manifest
10//! stays inside `uor-addr-1`'s depth and width ceilings and the address stays
11//! cheap.
12//!
13//! # Allocation
14//!
15//! [`Manifest::write_canonical_json`] writes into a caller-supplied buffer and
16//! allocates nothing, so the default build of this crate remains heap-free
17//! (R7). The `kappa` feature additionally pulls in `uor-addr-1` to turn that
18//! JSON into a label; that crate owns its own allocation, which is why the
19//! feature is off by default and why the manifest writer is usable without it.
20
21use uor_matmul_core::{Bound, Shape, NARROW_CAP};
22
23use crate::tier::TierId;
24
25/// Bytes in a kappa label.
26pub const ADDRESS_LABEL_BYTES: usize = 71;
27
28/// A canonical weight-artifact manifest.
29///
30/// The field set and its JSON spelling are normative and are restated in
31/// `ARCHITECTURE.md`. Changing either changes every artifact's identity, which
32/// is why the schema carries a `spec` tag.
33///
34/// There is deliberately no code-width field. The width is a property of the
35/// code *bytes*, which `codes_sha256` already distinguishes: a `u8` spelling
36/// and a `u16` spelling of one tier decode alike and digest differently, so
37/// they are two artifacts with two addresses --- the same rule `CK-05` states
38/// for equal-decoding codecs generally. Nothing downstream of the manifest
39/// reads the codes width-sensitively: the only reader is the decoder, which
40/// learns the width from the artifact's own type.
41#[derive(Clone, Copy, PartialEq, Eq, Debug)]
42pub struct Manifest<'a> {
43    /// Which tier decodes the codes.
44    pub tier: TierId,
45    /// The alphabet bound the decoded stream satisfies.
46    pub bound: u128,
47    /// Rows of the decoded matrix.
48    pub rows: usize,
49    /// Decoded elements per row.
50    pub cols: usize,
51    /// Alphabet elements produced per code.
52    pub block: usize,
53    /// `sha256:<64hex>` of the codebook, or of the empty table for a codec that
54    /// has none.
55    pub codebook_sha256: &'a str,
56    /// `sha256:<64hex>` of the code bytes.
57    pub codes_sha256: &'a str,
58    /// The schema tag.
59    pub spec: &'a str,
60}
61
62/// What a manifest says about addressing the artifact it describes.
63///
64/// Derived, never declared. [`Manifest`]'s field set is normative and carries
65/// no addressing field; it gains none here, because a field would be a second
66/// source for what the tier, the block and the bound already fix (R10) as well
67/// as a change to every artifact's identity.
68///
69/// Those three are the manifest's fields that describe the *code*. The two that
70/// describe the artifact's *bytes* --- `codes_sha256` and `codebook_sha256` ---
71/// are not read, and that absence is the whole of `CS-10`: two artifacts of one
72/// tier, one block and one bound address alike however far apart their code
73/// bytes are, so a traversal chosen from this cannot have probed either one.
74///
75/// It says nothing about whether a *table* over the code space exists. That is
76/// [`crate::Enumerable`]'s question, it is answered by the type at the
77/// tabulated traversal's boundary rather than by a token, and a composing tier
78/// --- [`crate::Packed`], [`crate::Offset`], [`crate::Transcode`] --- reports
79/// its own tier while inheriting its inner codec's enumeration. What is stated
80/// here is the block: how many elements one code names, and how far a lane
81/// carries their partial sums.
82#[derive(Clone, Copy, PartialEq, Eq, Debug)]
83pub enum Addressing {
84    /// No code of this artifact indexes a decode.
85    ///
86    /// Either the manifest names no element per code at all, or the tier is one
87    /// of the two with nothing between a code and an element:
88    /// [`TierId::Identity`], whose codes *are* the alphabet, so its code space
89    /// is as wide as the element type; and [`TierId::Runs`], whose code widths
90    /// are the data, so there is no `p`-th block for anything to be built per.
91    /// Those are the two tiers `ARCHITECTURE.md` names as admitting no table;
92    /// neither implements [`crate::Enumerable`], and no composition of them can,
93    /// because a composing tier reports its own token and its own enumeration.
94    ///
95    /// Reading the tier for this is not a dispatch on the *answer*: two codecs
96    /// with different tiers and equal decodes still write byte-identical output
97    /// (`CK-05`), and the table and the stream are held to the same bytes
98    /// either way (`CD-13`). What it decides is which factorizations exist.
99    Nothing,
100    /// A code names `elements` consecutive elements of one row, so a table
101    /// indexed by the code space can carry their partial sum against an
102    /// activation block of the same length.
103    ARunOf {
104        /// Consecutive elements of a row one code names.
105        ///
106        /// One is well formed and is what every scalar tier declares. It is the
107        /// block over which a table sums nothing --- one code, one product ---
108        /// which is why `tabulation_pays` refuses it on op count and routes the
109        /// arena tier back to the dense traversal.
110        elements: usize,
111        /// Partial sums of one such run that a narrow lane word holds exactly.
112        ///
113        /// A product of two alphabet elements has magnitude at most `bound^2`,
114        /// and a run of `elements` of them at most `elements * bound^2`, so a
115        /// lane holding [`NARROW_CAP`] holds this many runs and no more.
116        ///
117        /// `None` when no run fits one at all: a bound wide enough that a single
118        /// block already exceeds the lane, and in particular the `u128::MAX` a
119        /// float codebook declares through `Whole`, which is not a magnitude.
120        /// The reduction is then carried in the exact accumulator --- where a
121        /// family with no narrow register was always going to carry it.
122        lane_run: Option<usize>,
123    },
124}
125
126impl Addressing {
127    /// The addressing a tier, a block and a bound declare. The whole of the
128    /// derivation, and its only entry point.
129    pub const fn of(tier: TierId, block: usize, bound: u128) -> Self {
130        match tier {
131            TierId::Identity | TierId::Runs => Self::Nothing,
132            // A code that names no element addresses nothing, whatever its tier.
133            // `CodedMatrix::new` refuses such a codec outright, so this is the
134            // same non-existence stated one step earlier.
135            _ if block == 0 => Self::Nothing,
136            _ => Self::ARunOf {
137                elements: block,
138                lane_run: lane_run(block, bound),
139            },
140        }
141    }
142
143    /// Does one code name an element at all?
144    pub const fn addresses_an_element(self) -> bool {
145        matches!(self, Self::ARunOf { .. })
146    }
147
148    /// Does one code name a *run*, so that a table entry is a partial sum of
149    /// more than one product?
150    ///
151    /// This is the term the tabulated traversal's break-even turns on, and it
152    /// is false at `MAX_BLOCK == 1` for the reason stated on `elements` above:
153    /// a table of one product per entry repays no build at any width.
154    pub const fn addresses_a_run(self) -> bool {
155        matches!(self, Self::ARunOf { elements, .. } if elements > 1)
156    }
157}
158
159/// Partial sums of a `block`-long run that one narrow lane word holds exactly.
160///
161/// The same derivation `uor_matmul_core`'s narrow run is, one level up: there it
162/// is products that are counted against [`NARROW_CAP`] and here it is blocks of
163/// them, so the per-code magnitude carries an extra factor of `block` and
164/// nothing else changes. The cap is read from core rather than restated,
165/// because a constant with two sources is a constant with none (R10).
166const fn lane_run(block: usize, bound: u128) -> Option<usize> {
167    if bound == 0 || block == 0 {
168        // An alphabet of one value, or a code that names no element: neither
169        // moves the lane, so no run of them ever fills it. The same answer core's
170        // own run derivation gives at a bound of zero, for the same reason.
171        return Some(usize::MAX);
172    }
173    let square = match bound.checked_mul(bound) {
174        Some(v) => v,
175        // A bound that cannot be squared is not a magnitude: it is `Whole`'s,
176        // which declares that the codebook itself is the alphabet.
177        None => return None,
178    };
179    let per_code = match square.checked_mul(block as u128) {
180        Some(v) => v,
181        None => return None,
182    };
183    let run = NARROW_CAP / per_code;
184    if run == 0 {
185        return None;
186    }
187    // A run wider than the machine can index is the machine's limit, not the
188    // lane's, and clamping says so without inventing a smaller one (R8).
189    if run > usize::MAX as u128 {
190        Some(usize::MAX)
191    } else {
192        Some(run as usize)
193    }
194}
195
196/// The manifest could not be rendered or addressed.
197///
198/// Neither variant can be caused by the *values* in a matrix, only by a
199/// manifest that does not describe an artifact. Like [`uor_matmul_core::
200/// NotAProduct`], this is non-existence, decided before any arithmetic.
201#[derive(Clone, Copy, PartialEq, Eq, Debug)]
202#[non_exhaustive]
203pub enum KappaError {
204    /// The caller's buffer is too small for the canonical JSON.
205    BufferTooSmall {
206        /// Bytes the manifest needs.
207        needed: usize,
208        /// Bytes the caller offered.
209        offered: usize,
210    },
211    /// A digest field is not a `sha256:<64hex>` string.
212    MalformedDigest,
213    /// The addressing transform rejected the manifest.
214    NotAddressable,
215}
216
217impl core::fmt::Display for KappaError {
218    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219        match self {
220            Self::BufferTooSmall { needed, offered } => {
221                write!(
222                    f,
223                    "the canonical manifest needs {needed} bytes, {offered} offered"
224                )
225            }
226            Self::MalformedDigest => write!(f, "a digest field is not sha256:<64hex>"),
227            Self::NotAddressable => write!(f, "the addressing transform rejected the manifest"),
228        }
229    }
230}
231
232/// A cursor that writes into a caller's buffer and never allocates.
233struct Out<'a> {
234    buf: &'a mut [u8],
235    at: usize,
236    overflowed: bool,
237}
238
239impl Out<'_> {
240    fn push(&mut self, bytes: &[u8]) {
241        let end = self.at.saturating_add(bytes.len()); // R3-ok: a buffer cursor, checked below
242        if end > self.buf.len() {
243            self.overflowed = true;
244            self.at = end;
245            return;
246        }
247        self.buf[self.at..end].copy_from_slice(bytes);
248        self.at = end;
249    }
250
251    fn push_u128(&mut self, mut v: u128) {
252        // 39 is the decimal width of `u128::MAX`, so this buffer cannot be
253        // outrun by any input. A derivation, not a choice (R8).
254        let mut digits = [0u8; 39];
255        let mut n = 0;
256        if v == 0 {
257            self.push(b"0");
258            return;
259        }
260        while v > 0 {
261            digits[n] = b'0' + (v % 10) as u8;
262            v /= 10;
263            n += 1;
264        }
265        for i in (0..n).rev() {
266            self.push(&digits[i..=i]);
267        }
268    }
269}
270
271impl Manifest<'_> {
272    /// What this manifest says about addressing the artifact it describes.
273    ///
274    /// Read from the tier, the block and the bound, and from neither digest.
275    /// See [`Addressing`] for why that absence is the claim rather than an
276    /// omission (`CS-10`).
277    pub const fn addressing(&self) -> Addressing {
278        Addressing::of(self.tier, self.block, self.bound)
279    }
280
281    /// Does this artifact stand as the coded operand of `shape` with the
282    /// reduction running *along* its code blocks?
283    ///
284    /// `rows == n` and `cols == k`: one coded row per output column, so a code
285    /// block is a run of the reduction, a table entry is a partial sum of it,
286    /// and the product is `C := A * W^T`. That is the orientation
287    /// `uor_matmul_gemm::TabulatedTriple` takes, and this asks its constructor's
288    /// question of the *declaration*, before there is anything to construct.
289    ///
290    /// Two queries rather than one enum, because at `k == n` a square coded
291    /// operand satisfies both and an enum would have to pick --- inventing a
292    /// distinction the declaration does not make. Which of the two products is
293    /// meant is then named by the triple the caller builds, and neither answer
294    /// is wrong.
295    pub const fn reduces_along_the_block(&self, shape: Shape) -> bool {
296        self.rows == shape.n && self.cols == shape.k
297    }
298
299    /// Does this artifact stand as the coded operand of `shape` with the
300    /// reduction running *across* its code blocks?
301    ///
302    /// `rows == k` and `cols == n`: one coded row per step of the reduction, so
303    /// a code block is a run of `MAX_BLOCK` different *output columns* and there
304    /// is nothing for a partial sum to be a partial sum of. That is the
305    /// orientation `uor_matmul_gemm::CodedTriple` takes --- the streaming one,
306    /// which needs no offer at all. Not a lesser orientation and not a fallback:
307    /// it is the one a `k x n` quantized weight is already stored in.
308    pub const fn reduces_across_the_block(&self, shape: Shape) -> bool {
309        self.rows == shape.k && self.cols == shape.n
310    }
311
312    /// Write the JCS-RFC8785 canonical JSON for this manifest.
313    ///
314    /// Returns the number of bytes written. Object members are emitted in
315    /// lexicographic order of their keys, with no whitespace and no escapes,
316    /// which is what JCS requires and what makes two independently produced
317    /// manifests of the same artifact byte-identical.
318    ///
319    /// Allocates nothing. If `out` is too small the needed length is reported
320    /// rather than a partial write being passed off as a manifest.
321    pub fn write_canonical_json(&self, out: &mut [u8]) -> Result<usize, KappaError> {
322        for d in [self.codebook_sha256, self.codes_sha256] {
323            if !is_sha256(d) {
324                return Err(KappaError::MalformedDigest);
325            }
326        }
327        let mut w = Out {
328            buf: out,
329            at: 0,
330            overflowed: false,
331        };
332
333        // Lexicographic key order: block, bound, codebook_sha256,
334        // codes_sha256, cols, rows, spec, tier.
335        w.push(b"{\"block\":");
336        w.push_u128(self.block as u128);
337        w.push(b",\"bound\":");
338        w.push_u128(self.bound);
339        w.push(b",\"codebook_sha256\":\"");
340        w.push(self.codebook_sha256.as_bytes());
341        w.push(b"\",\"codes_sha256\":\"");
342        w.push(self.codes_sha256.as_bytes());
343        w.push(b"\",\"cols\":");
344        w.push_u128(self.cols as u128);
345        w.push(b",\"rows\":");
346        w.push_u128(self.rows as u128);
347        w.push(b",\"spec\":\"");
348        w.push(self.spec.as_bytes());
349        w.push(b"\",\"tier\":\"");
350        w.push(self.tier.as_str().as_bytes());
351        w.push(b"\"}");
352
353        if w.overflowed {
354            return Err(KappaError::BufferTooSmall {
355                needed: w.at,
356                offered: w.buf.len(),
357            });
358        }
359        Ok(w.at)
360    }
361
362    /// The manifest for a coded matrix, given the two digests the caller has
363    /// computed over the bulk arrays.
364    pub fn of<E, Bd, C>(
365        matrix: &crate::CodedMatrix<'_, E, Bd, C>,
366        codebook_sha256: &'static str,
367        codes_sha256: &'static str,
368        spec: &'static str,
369    ) -> Manifest<'static>
370    where
371        E: uor_matmul_core::Element,
372        Bd: Bound,
373        C: crate::Codec<E, Bd>,
374    {
375        Manifest {
376            tier: C::TIER,
377            bound: Bd::VALUE,
378            rows: matrix.rows(),
379            cols: matrix.cols(),
380            block: C::MAX_BLOCK,
381            codebook_sha256,
382            codes_sha256,
383            spec,
384        }
385    }
386}
387
388fn is_sha256(s: &str) -> bool {
389    let Some(hex) = s.strip_prefix("sha256:") else {
390        return false;
391    };
392    hex.len() == 64
393        && hex
394            .bytes()
395            .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
396}
397
398/// Address a manifest, writing the label into a caller-supplied buffer.
399///
400/// The label is `uor-addr-1`'s JCS-RFC8785 + NFC + SHA-256 transform applied to
401/// [`Manifest::write_canonical_json`]'s output.
402#[cfg(feature = "kappa")]
403pub fn address_into(
404    manifest: &Manifest<'_>,
405    scratch: &mut [u8],
406    out: &mut [u8; ADDRESS_LABEL_BYTES],
407) -> Result<(), KappaError> {
408    let n = manifest.write_canonical_json(scratch)?;
409    let outcome = uor_addr_1::address(&scratch[..n]).map_err(|_| KappaError::NotAddressable)?;
410    // `AddressOutcome::address`, not `.label`: the field is the ASCII wire form,
411    // `sha256:<64 lowercase hex>`, which is the 71 bytes `ADDRESS_LABEL_BYTES`
412    // names. This read `.label`, a field the crate does not have, and said so
413    // only when something built the `kappa` feature --- which nothing did.
414    let label = outcome.address.as_bytes();
415    if label.len() != ADDRESS_LABEL_BYTES {
416        return Err(KappaError::NotAddressable);
417    }
418    out.copy_from_slice(label);
419    Ok(())
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425
426    const D0: &str = "sha256:0000000000000000000000000000000000000000000000000000000000000000";
427    const D1: &str = "sha256:1111111111111111111111111111111111111111111111111111111111111111";
428    const D2: &str = "sha256:2222222222222222222222222222222222222222222222222222222222222222";
429
430    /// CK-08: the manifest is canonical --- keys in lexicographic order, no
431    /// whitespace --- so two independent producers of the same artifact write
432    /// the same bytes and therefore mint the same label.
433    #[test]
434    fn canonical_json_is_byte_stable_ck_08() {
435        let m = Manifest {
436            tier: TierId::Book,
437            bound: 127,
438            rows: 4096,
439            cols: 4096,
440            block: 8,
441            codebook_sha256: D0,
442            codes_sha256: D1,
443            spec: "uor-matmul/1",
444        };
445        let mut buf = [0u8; 512];
446        let n = m.write_canonical_json(&mut buf).unwrap();
447        let text = core::str::from_utf8(&buf[..n]).unwrap();
448        assert_eq!(
449            text,
450            concat!(
451                "{\"block\":8,\"bound\":127,",
452                "\"codebook_sha256\":\"sha256:00000000000000000000000000000000",
453                "00000000000000000000000000000000\",",
454                "\"codes_sha256\":\"sha256:11111111111111111111111111111111",
455                "11111111111111111111111111111111\",",
456                "\"cols\":4096,\"rows\":4096,\"spec\":\"uor-matmul/1\",\"tier\":\"Book\"}"
457            )
458        );
459    }
460
461    /// A short buffer reports what it needed rather than truncating, because a
462    /// truncated manifest would address a different artifact.
463    #[test]
464    fn a_short_buffer_reports_the_need_ck_08() {
465        let m = Manifest {
466            tier: TierId::Identity,
467            bound: 1,
468            rows: 1,
469            cols: 1,
470            block: 1,
471            codebook_sha256: D0,
472            codes_sha256: D1,
473            spec: "uor-matmul/1",
474        };
475        let mut buf = [0u8; 8];
476        match m.write_canonical_json(&mut buf) {
477            Err(KappaError::BufferTooSmall { needed, offered }) => {
478                assert!(needed > 8);
479                assert_eq!(offered, 8);
480            }
481            other => panic!("expected BufferTooSmall, got {other:?}"),
482        }
483    }
484
485    /// A malformed digest is rejected before anything is written: an artifact
486    /// whose bulk arrays are not addressed is not an addressable artifact.
487    #[test]
488    fn a_malformed_digest_is_rejected_ck_08() {
489        let m = Manifest {
490            tier: TierId::Identity,
491            bound: 1,
492            rows: 1,
493            cols: 1,
494            block: 1,
495            codebook_sha256: "not-a-digest",
496            codes_sha256: D1,
497            spec: "uor-matmul/1",
498        };
499        let mut buf = [0u8; 512];
500        assert_eq!(
501            m.write_canonical_json(&mut buf),
502            Err(KappaError::MalformedDigest)
503        );
504    }
505
506    /// CK-08: the arena tier's spelling is pinned like every other token. A
507    /// float alphabet has no magnitude, so the bound field is `Whole`'s value,
508    /// recorded as itself --- and a new token mints new addresses, which is the
509    /// point of the tier (CL-MM01).
510    #[test]
511    fn arena_manifest_spelling_is_byte_stable_ck_08() {
512        let m = Manifest {
513            tier: TierId::Arena,
514            bound: u128::MAX,
515            rows: 4096,
516            cols: 4096,
517            block: 1,
518            codebook_sha256: D0,
519            codes_sha256: D1,
520            spec: "uor-matmul/1",
521        };
522        let mut buf = [0u8; 512];
523        let n = m.write_canonical_json(&mut buf).unwrap();
524        let text = core::str::from_utf8(&buf[..n]).unwrap();
525        assert_eq!(
526            text,
527            concat!(
528                "{\"block\":1,\"bound\":340282366920938463463374607431768211455,",
529                "\"codebook_sha256\":\"sha256:00000000000000000000000000000000",
530                "00000000000000000000000000000000\",",
531                "\"codes_sha256\":\"sha256:11111111111111111111111111111111",
532                "11111111111111111111111111111111\",",
533                "\"cols\":4096,\"rows\":4096,\"spec\":\"uor-matmul/1\",\"tier\":\"Arena\"}"
534            )
535        );
536    }
537
538    /// `CS-10`: addressing is derived from the declaration, and the two fields
539    /// that move with the artifact's bytes are the two it does not read.
540    ///
541    /// Both directions, at the declaration level. A one-sided version --- only
542    /// that equal declarations address alike --- passes for a derivation that
543    /// returns a constant, so the second half asserts that each of the three
544    /// fields it *does* read moves the answer.
545    #[test]
546    fn addressing_is_read_from_the_declaration_cs_10() {
547        let e8 = Manifest {
548            tier: TierId::Book,
549            bound: 128,
550            rows: 4096,
551            cols: 4096,
552            block: 8,
553            codebook_sha256: D0,
554            codes_sha256: D1,
555            spec: "uor-matmul/1",
556        };
557
558        // Two artifacts, one declaration. Only the digests moved --- which is
559        // exactly what "the values changed" means to a manifest --- and the
560        // addressing did not.
561        let other = Manifest {
562            codebook_sha256: D2,
563            codes_sha256: D2,
564            ..e8
565        };
566        assert_ne!(e8, other, "different bytes are a different artifact");
567        let mut lhs = [0u8; 512];
568        let mut rhs = [0u8; 512];
569        let ln = e8.write_canonical_json(&mut lhs).unwrap();
570        let rn = other.write_canonical_json(&mut rhs).unwrap();
571        assert_ne!(lhs[..ln], rhs[..rn], "and a different canonical manifest");
572        assert_eq!(e8.addressing(), other.addressing());
573
574        // The run and the lane, recomputed here rather than recalled: eight
575        // products of two elements of magnitude 128 apiece, against the cap one
576        // narrow word holds, clamped where a 32-bit machine cannot index that
577        // far.
578        let want_run = (NARROW_CAP / (8 * 128 * 128)).min(usize::MAX as u128) as usize;
579        assert_eq!(
580            e8.addressing(),
581            Addressing::ARunOf {
582                elements: 8,
583                lane_run: Some(want_run),
584            }
585        );
586        assert!(e8.addressing().addresses_a_run());
587        assert!(e8.addressing().addresses_an_element());
588
589        // The block moves it. One element per code addresses an element and not
590        // a run, which is the term the tabulated break-even refuses.
591        let scalar = Manifest { block: 1, ..e8 };
592        assert!(scalar.addressing().addresses_an_element());
593        assert!(!scalar.addressing().addresses_a_run());
594        assert_ne!(scalar.addressing(), e8.addressing());
595        assert_eq!(Addressing::of(TierId::Book, 0, 128), Addressing::Nothing);
596
597        // The bound moves it. `Whole`'s `u128::MAX` is not a magnitude, so no
598        // run of any length fits a narrow word and the partial sums are carried
599        // in the exact accumulator --- which is what the arena tier declares.
600        assert_eq!(
601            Addressing::of(TierId::Arena, 1, u128::MAX),
602            Addressing::ARunOf {
603                elements: 1,
604                lane_run: None,
605            }
606        );
607        // And so does a bound one block of which already exceeds the lane.
608        assert_eq!(
609            Addressing::of(TierId::Book, 8, 1u128 << 40),
610            Addressing::ARunOf {
611                elements: 8,
612                lane_run: None,
613            }
614        );
615        // A bound of zero is the alphabet `{0}`: nothing fills the lane, ever.
616        assert_eq!(
617            Addressing::of(TierId::Book, 8, 0),
618            Addressing::ARunOf {
619                elements: 8,
620                lane_run: Some(usize::MAX),
621            }
622        );
623
624        // The tier moves it, for the two tiers with nothing between a code and
625        // an element --- whatever their block and bound say.
626        assert_eq!(
627            Addressing::of(TierId::Identity, 1, 128),
628            Addressing::Nothing
629        );
630        assert_eq!(Addressing::of(TierId::Runs, 8, 128), Addressing::Nothing);
631
632        // Orientation, read from `rows` and `cols` and from nothing else, at a
633        // shape where `k != n` so the two are distinguishable.
634        let shape = Shape { m: 3, k: 64, n: 40 };
635        let along = Manifest {
636            rows: 40,
637            cols: 64,
638            ..e8
639        };
640        let across = Manifest {
641            rows: 64,
642            cols: 40,
643            ..e8
644        };
645        assert!(along.reduces_along_the_block(shape));
646        assert!(!along.reduces_across_the_block(shape));
647        assert!(across.reduces_across_the_block(shape));
648        assert!(!across.reduces_along_the_block(shape));
649        // The two differ in `rows` and `cols` alone, so the orientation came
650        // from the declaration and the code declaration is untouched by it.
651        assert_eq!(along.addressing(), across.addressing());
652
653        // A square coded operand answers both, because at `k == n` the
654        // declaration names no difference and neither answer is wrong.
655        let square = Shape { m: 3, k: 64, n: 64 };
656        let s = Manifest {
657            rows: 64,
658            cols: 64,
659            ..e8
660        };
661        assert!(s.reduces_along_the_block(square));
662        assert!(s.reduces_across_the_block(square));
663    }
664}