All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 14s
wai-key-proxy / does this change touch it (push) Successful in 14s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 25s
wai-app and Create / wai-app · does this change touch it (push) Successful in 25s
wai-audit collector and player integration / does this change touch it (push) Successful in 32s
WAI Studio / does this change touch it (push) Successful in 19s
wai-quantum for JavaScript / does this change touch it (push) Successful in 1m42s
wai-quantum studio and desktop / does this change touch it (push) Successful in 1m51s
wai-sink-swift native sink package / does this change touch it (push) Successful in 2m14s
wai-fleet service / does this change touch it (push) Successful in 2m53s
wai-ledger service / does this change touch it (push) Successful in 3m11s
wai-sink-android native sink library / does this change touch it (push) Successful in 3m7s
WAI operation catalogue / does this change touch it (push) Successful in 3m24s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 3m26s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 3m38s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 5m27s
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 7m6s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 7m20s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 7m8s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 4m34s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 12s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 11s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 37s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 2m41s
wai-key-proxy / proxy · linux (push) Successful in 1m49s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m22s
wai-audit collector and player integration / collector · linux (push) Successful in 2m18s
wai-quantum for JavaScript / javascript · linux (push) Successful in 5m2s
wai-quantum studio and desktop / studio · browser (push) Successful in 5m54s
wai-ledger service / service · linux (push) Successful in 2m11s
wai-fleet service / service · linux (push) Successful in 4m2s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 9m50s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 12m24s
WAI Studio / Studio in WebKit · linux (push) Successful in 13m19s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 11m24s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 1m32s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 6m4s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 4m51s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 15m29s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 19m19s
wai release binaries build on Windows / the seven release binaries · windows (push) Successful in 17m45s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 10m5s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 56s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m6s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m14s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 6m29s
wai-fleet service / service · macos (push) Successful in 39s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 4m0s
wai-ledger service / service · macos (push) Successful in 1m3s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 10m25s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 5m34s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 15m12s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 15m51s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 6m17s
Each is a mapping with a reference module, a corpus and a second implementation written
from its text. None adds a record or changes a byte of an envelope, a receipt or a claim,
and none is wired into a relay, the room service or a page: each module is what a publisher
and a receiver call. Every draft was read at the latest revision in the IETF archive on 10
November 2026.
- Playout timing (draft-nandakumar-moq-tempo-00; co-watch §15, new). joule-moq-room's tempo
module reads and writes the draft's CaptureTimestamp, PlayoutDelay and HopTime, and holds
the instant they state to the room: it is the millisecond in which co-watch's controlled
position enters the object, and a delay above the track's stated bound is refused whatever
instant it gives. A change of delay the room decides is a pause and a play, or a seek, sent
as any control. The catalog member wai_tempo (moq-streaming-format §4.4) carries the bound
and the two catalog extensions the draft leaves to be defined. The draft's property types
are unassigned; the module uses placeholders in MOQT's application range.
- The MoQ FEC catalog binding (draft-ramadan-moq-fec-catalog-00, -fec-core-00,
-fec-block-codes-00; carousel §8.3, new). wai-rs's carousel::moq_fec binds a schedule's
raptorq entries to the FEC instances a catalog declares under moq-object-v1, and back:
an object a block, a pass a repair layer, symbols as symbol-track and repair objects. An
entry binds only where everything carousel §6 derives from it holds of the block sent (the
same symbols a pass, the same K'); a catalog is read failing closed; a rebuilt object is
delivered only at its entry's Location, length and content hash. tests/carousel_moq_fec.rs
writes the corpus and decodes every object with an independent implementation of RFC 6330,
whose symbols for the block-codes draft's Appendix B are that appendix's.
- Multicast endpoints (draft-ramadan-moq-multicast-02; bearer-receipts §9.7, which read -00).
At -02 an endpoint lists only mmtp tracks and their repair tracks, so no WAI track rides
one of the draft's endpoints, and §9.7 now says so. What is implemented is the reading of a
catalog's multicast member, each endpoint standing or ignored with a label; it joins nothing.
- MoQ files (draft-jennings-moq-file-05; live-recording §8.7, new). joule-moq's file module
writes a track's objects as the draft's .moq and .dat files and reads them back byte for
byte, properties and all, so a track and its receipts track verify from the files alone. A
reader opens nothing outside its directory and refuses a file cut short.
- V3C tracks (draft-yuyou-moq-v3c-00; moq-streaming-format §4.5, new). joule-moq's v3c
module splits a wai.pc.vpcc payload into the draft's component tracks and joins it back,
with the catalog member wai_v3c. ISO/IEC 23090-5 was not read: the unit header is RFC 10034
§4.3.1's copy, unit types 7 and above are refused, and the corpus holds opaque test bytes,
no bitstream an encoder wrote.
Planted faults, each caught by the reference and, but where noted, by the second
implementation: a symbol of another object (offered as itself, in place of a block's own
symbol, and, in the test alone, as a repair symbol under a rewritten header); a truncated data
file and a truncated .moq file; a timing claim outside its bound (a delay past the track's
bound, and an instant one millisecond off the controlled position's). Writing the tests
found two holes, both closed: a rebuilt object was not held to its entry's Location, and a
Group missing its last object on a track joins to another payload, which the text now states.
Corpora: wai/moqt-conformance/{moq-file,tempo,v3c}/ and
wai/live-conformance/carousel-moq-fec/, with moq_file_verify.py, tempo_verify.py,
v3c_verify.py and carousel_moq_fec_verify.py. The registry lists wai_tempo and wai_v3c
(export regenerated). wai/ietf/catalog_check.py holds them, fec and the packaging values
fec-repair, fec-source and v3c in three more writers' catalogs, with 31 more planted faults;
draft-charlot-moq-wai-packaging-01 is unchanged. joule-code-conformance.yml and
joule-moq-room.yml run the second implementations and trigger on the corpora and texts their
tests read; wai conform --independent runs four more verifiers.
Not done: no relay rewrites HopTime or reads the Sync Support parameter; nothing sends
stamped objects, FEC tracks or V3C tracks on a session; the drafts' MMTP packaging, FEC on
multicast paths and Reed-Solomon are not mapped.
Co-Authored-By: Claude <[email protected]>
856 lines
58 KiB
Markdown
856 lines
58 KiB
Markdown
# WAI Extension: Carousel Schedule
|
||
|
||
> Status: Draft. Registers the record `wai.bearer.carousel`: a publisher's signed schedule of
|
||
> when each object of a group passes on a bearer instance, and with which application-layer
|
||
> forward error correction. On a bearer with no return path a receiver cannot ask for what it
|
||
> missed: it holds an object only if the passes it heard carried enough encoding symbols. The
|
||
> schedule says what was planned to be sent, in a jwp-receipts claim of class **schedule**
|
||
> (jwp-receipts §2.4.16, §4 here), signed by the group's key beside the group receipt and the
|
||
> transmission claim of [bearer-receipts](bearer-receipts.md) §4. From it a verifier derives,
|
||
> for each object, when a receiver whose losses stay within a stated bound holds it (§6): the
|
||
> deadline a one-way bearer can promise, with the decoding failure the FEC code bounds. A
|
||
> receiver derives the passes it must listen to and when it may turn its radio off (§7). A later
|
||
> schedule adds passes to an earlier one without contradicting it (§4.5), and the receipts a
|
||
> receiver needs ride on the instance itself, so one that joins mid-carousel recovers them there
|
||
> (§8.1, §8.2). A schedule's RaptorQ entries are also the FEC instances a MoQ catalog declares,
|
||
> and their symbols the objects of its repair and symbol tracks (§8.3). WAI
|
||
> defines no transport and no code: the codes are RaptorQ (RFC 6330), Raptor (RFC 5053) and Compact
|
||
> No-Code (RFC 5445), and the schedule declares their parameters. The reference implementation is the
|
||
> `carousel` module of `wai-rs` (feature `carousel`), with the class's body in
|
||
> `claim_receipt::schedule`; the corpus is
|
||
> [`live-conformance/carousel/`](../live-conformance/carousel/), and its second implementation
|
||
> is `live-conformance/carousel_verify.py`.
|
||
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
>
|
||
> Texts read for this revision: RFC 6330 (RaptorQ) with its verified erratum 5548, RFC 5053 (Raptor),
|
||
> RFC 5445 (Compact No-Code), RFC 5052 §9.1 (block partitioning) and 3GPP TS 26.346 V19.3.0 §7.2.2 and
|
||
> §7.2.3. A clause number of one of them in this text is that text's. §11 also cites the IETF's IPR
|
||
> disclosures on RFC 5053, RFC 6330 and their drafts, read as the IETF datatracker shows them, and
|
||
> RFC 8179 §4. For §8.3, each the latest revision in the IETF archive on 10 November 2026:
|
||
> `draft-ramadan-moq-fec-catalog-00`, whole; `draft-ramadan-moq-fec-core-00` §2 to §7, §9.2,
|
||
> §10, §13 and §15; and `draft-ramadan-moq-fec-block-codes-00` §6 to §9 and Appendix B. They
|
||
> are cited as fec-catalog, fec-core and block-codes; `draft-ramadan-moq-fec-01`, which they
|
||
> replace, was not read beyond its abstract, and `draft-ramadan-moq-fec-multicast-00` and
|
||
> `draft-ramadan-moq-mmt-02`, which they cite, were not read.
|
||
|
||
## 1. Scope
|
||
|
||
In scope: the schedule claim and how a later one extends an earlier one (§4); how an object is
|
||
divided into source blocks and symbols under each code (§5); when an object is held by a receiver
|
||
whose losses stay within a bound, and the statements a verifier may check against it (§6); the
|
||
passes a receiver listens to (§7); and what a publisher repeats on the instance for a receiver
|
||
that joins mid-carousel, and whether what it acquired suffices (§8.1, §8.2).
|
||
|
||
Out of scope: encoding and decoding symbols, which are the codes' own (RFC 6330 §5, RFC 5053 §5,
|
||
RFC 5445 §3); a transport, and how symbols are packed into packets; a model of a channel. Every
|
||
statement here holds for every pattern of loss within the stated bound (§2). Whether losses on a
|
||
channel are independent, and how often a bound is exceeded, is a judgement about that
|
||
channel, which a relying party makes and a schedule does not carry. Nothing here states
|
||
presentation, and nothing here states that any receiver heard anything: that is a
|
||
reconciliation's (bearer-receipts §5).
|
||
|
||
Relationship to the core spec: `wai.bearer.carousel` is a **record** ([SPEC](../SPEC.md) §5):
|
||
the schedule claim is a signed record a WAI tool emits and verifies. It names no payload format
|
||
and is not dispatchable.
|
||
|
||
## 2. Terms
|
||
|
||
- **Bearer instance**, **declaration**, **sink**, **relying party**: as bearer-receipts §2. A
|
||
declaration is a transmission claim that stands under bearer-receipts §6.2 step 4.
|
||
- **Source block**, **source symbol**, **repair symbol**, **encoding symbol**, **ESI** (encoding
|
||
symbol ID): as RFC 6330 §2 and RFC 5052. An object is divided into source blocks; a block of
|
||
*K* source symbols is sent as encoding symbols, each named by its ESI in that block.
|
||
- **Pass**: one transmission of encoding symbols of every source block of one object on a
|
||
bearer instance, from its start to its end. An object's passes are numbered from 1.
|
||
- **Instant**: a count of milliseconds since 1970-01-01T00:00:00Z, leap seconds not counted, as
|
||
[channel-lineup](channel-lineup.md) writes one.
|
||
- **Loss bound** *L*: an integer from 0 to 999,999, in parts per million. A receiver is
|
||
**within** *L* when, of the *m* encoding symbols of any one source block sent in any one
|
||
pass, it fails to receive at most ⌊*L* · *m* / 1,000,000⌋, whichever they are. It is a bound
|
||
on symbols: a relying party whose bound is stated on packets carrying several symbols each
|
||
converts it.
|
||
- **Overhead** *h*: 0, 1 or 2, the encoding symbols beyond *K′* a RaptorQ block is held at. A
|
||
Raptor block is held at a margin beyond *K* that *h* selects (§5.3).
|
||
|
||
## 3. Why a schedule beside the receipt
|
||
|
||
The group receipt signs its objects and names no bearer (bearer-receipts §3); the transmission
|
||
claim names the bearer instance and attests that the publisher transmitted the group, which on
|
||
a bearer with no return path says nothing about any receiver. Between the two is what decides
|
||
whether a receiver that heard part of a transmission holds an object: how often each object
|
||
passes, for how long, and with how many repair symbols. That is a plan, made before anything
|
||
is sent, and it is a statement of the publisher's own:
|
||
|
||
- **A relying party can hold a deadline to it.** A publisher that offers an object to every
|
||
receiver within a loss bound by a deadline makes a promise that the schedule either supports
|
||
or does not (§6.4), checked with no receiver and no return path.
|
||
- **A receiver can plan with it.** It learns which passes add symbols it needs, and listens to
|
||
those only (§7). On a receiver powered by a battery the radio is a large part of its energy.
|
||
- **It changes no byte of what it schedules.** The schedule names the group by its link hash
|
||
and each object by its position and content hash, like a reconciliation. A group carried
|
||
before this extension, or by a party that does not use it, verifies exactly as before.
|
||
|
||
The schedule attests a plan, not a transmission: that is the declaration's, and the schedule
|
||
stands only beside one (§6.2). Neither attests that any receiver heard anything.
|
||
|
||
## 4. The schedule claim (`class: "schedule"`)
|
||
|
||
A publisher that transmits a group over a `multicast` or `unidirectional` bearer instance
|
||
**MAY** declare its schedule there in a `schedule` claim, one per bearer instance. Its
|
||
`subject_type` is `receipt` and its `subject` the group receipt's link hash (jwp-receipts §3
|
||
step 3). It is signed by the key that signed the group. It attests that the publisher
|
||
scheduled the passes it names, and nothing about what any sink received.
|
||
|
||
### 4.1 Body
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `bearer_id` | absolute URI | The bearer instance (bearer-receipts §2): the `bearer_id` of the transmission claim it schedules. |
|
||
| `objects` | array | The objects scheduled, one entry each (below). |
|
||
| `attests` | string | **MUST** be `"scheduled"`. Any string passes step 2; another value is refused at step 4. |
|
||
|
||
Each entry of `objects` has exactly these members:
|
||
|
||
| member | type | meaning |
|
||
|---|---|---|
|
||
| `leaf_index` | integer | The object's position in the group (jwp-receipts §2.2). |
|
||
| `content_hash` | 32 B hex | The object's content hash (jwp-receipts §2.1): what a receiver that decodes the object checks it against. |
|
||
| `fec` | string | The code: `raptorq` (RFC 6330, FEC Encoding ID 6), `raptor` (RFC 5053, FEC Encoding ID 1) or `no-code` (RFC 5445, FEC Encoding ID 0). |
|
||
| `oti` | object | The code's object transmission information, as §5 reads it. For `raptorq`, exactly `transfer_length` (F), `symbol_size` (T), `source_blocks` (Z), `sub_blocks` (N) and `alignment` (Al), RFC 6330 §3.3.2 and §3.3.3. For `raptor`, the same members, RFC 5053 §3.2.2 and §3.2.3. For `no-code`, exactly `transfer_length`, `symbol_size` (the encoding symbol length) and `max_block_length` (the maximum source block length, in symbols), RFC 5445 §3.2.2.2. Each an integer. |
|
||
| `repair` | integer | The repair symbols of each source block sent in each pass, beyond its source symbols (§5.4). |
|
||
| `esi` | string | `repeat`: every pass sends the same ESIs. `advance`: each pass sends ESIs no earlier pass sent (§5.4). |
|
||
| `first_ms` | integer | The instant the first pass starts. |
|
||
| `period_ms` | integer | The time from one pass's start to the next's. |
|
||
| `duration_ms` | integer | The time each pass lasts. |
|
||
| `count` | integer | The number of passes. |
|
||
|
||
`fec` and `esi` are read at jwp-receipts §3.1 step 2 against their enumerations, the members of
|
||
`oti` against the code `fec` names, `bearer_id` as an absolute URI, and every integer as an
|
||
unsigned 64-bit one (`field_type`).
|
||
|
||
### 4.2 Rules
|
||
|
||
Checked at jwp-receipts §3.1 step 4, in this order:
|
||
|
||
1. `attests` **MUST** be `"scheduled"` (`schedule_not_scheduled`).
|
||
2. `objects` has at least one entry, and its `leaf_index` values strictly increase
|
||
(`schedule_objects_malformed`): one entry for each object scheduled, in group order.
|
||
3. Each entry in order, the first of these to fail:
|
||
1. its `oti` is one its code admits, §5.1, §5.2 or §5.5 (`schedule_oti_invalid`);
|
||
2. its symbols are ones its code can name, §5.4 (`schedule_symbols_invalid`);
|
||
3. its passes are well formed (`schedule_passes_invalid`): `count` and `duration_ms` are at
|
||
least 1; with `count` at least 2, `period_ms` is at least `duration_ms`, so one pass of
|
||
an object ends before its next starts; the last pass ends,
|
||
`first_ms` + (`count` − 1) · `period_ms` + `duration_ms`, at or before 253,402,300,799,999
|
||
(9999-12-31T23:59:59.999Z); and the octets its passes send, `bytes_sent` (§5.4), are at
|
||
most 2<sup>53</sup> − 1, the greatest integer a receipt states; each computed without
|
||
overflow.
|
||
|
||
### 4.3 Against the group
|
||
|
||
Checked at jwp-receipts §3.1 step 6 against the group receipt the verifier holds, which has
|
||
passed jwp-receipts §3 steps 1 and 2 and whose `group_start` is an RFC 3339 date-time
|
||
(`context_invalid` otherwise; another receipt is `context_mismatch`), the object receipts it
|
||
holds under that group, each of which has passed jwp-receipts §3 step 5, and the envelope bytes
|
||
of those it holds. In order:
|
||
|
||
1. the group's link hash is the claim's `subject` (`schedule_subject_mismatch`);
|
||
2. the claim's `signer_pubkey` is the group's (`schedule_signer_mismatch`): only the publisher
|
||
schedules its own transmission. A node schedules the instance it declares as its own under
|
||
[cache-authority](cache-authority.md) §5.6, in the verdict that holds that instance, which
|
||
replaces this step and no other; every other verdict reads the publisher's schedules alone;
|
||
3. every `leaf_index` is less than the group's `leaf_count` (`schedule_leaf_out_of_range`);
|
||
4. no entry's first pass starts before the group does (`schedule_before_group`): ⌊`first_ms`
|
||
/ 1000⌋ is at least the seconds jwp-receipts §2.4.3 reads from `group_start`, its fraction
|
||
dropped. Nothing can be sent before it exists;
|
||
5. for each entry whose object receipt the verifier holds, the receipt's `content_hash` is the
|
||
entry's (`schedule_content_mismatch`); and for each whose envelope bytes it holds, their
|
||
length is `oti.transfer_length` (`schedule_length_mismatch`).
|
||
|
||
A schedule checked without the group stops at step 5 and is not counted by §6.
|
||
|
||
### 4.4 Authority
|
||
|
||
Confirmed under jwp-receipts §7, a schedule claim needs the authority `schedule`, which is not
|
||
pinned-only and is not `delivery`. An origin gives it to the keys that publish its groups, as it
|
||
gives `transmission`, and a node's operator to the nodes that schedule their own instances
|
||
(cache-authority §4.3).
|
||
|
||
### 4.5 Extending a schedule
|
||
|
||
A publisher **MAY** add to a schedule it has published, after a fade or for a longer transmission,
|
||
by signing a later schedule of the same instance that extends it. Schedule *B* **extends** schedule
|
||
*A* of one `bearer_id` when *B* has, for every entry of *A*, an entry of the same `leaf_index` that
|
||
extends it; and an entry extends another when its passes begin with the other's, at the same
|
||
instants and sending the same symbols:
|
||
|
||
- its `count` is at least the other's;
|
||
- its `period_ms` is the other's, unless the other has one pass, whose period is not read (§4.2
|
||
rule 3.3);
|
||
- each of its other members is the other's.
|
||
|
||
*B* **MAY** also have entries for objects *A* has none for. Every schedule extends itself, and a
|
||
schedule that extends one that extends a third extends the third. *B* is held to §4.2 and §4.3 on
|
||
its own, so rule 3.3 and §5.4's bound on ESIs hold for its greater count.
|
||
|
||
Nothing *A* states is weakened by *B*, at any loss bound and overhead:
|
||
|
||
- each pass of *A* is a pass of *B*, and §5.4 gives it the same ESIs, which depend only on the
|
||
pass and on *m*;
|
||
- an entry available under *A* (§6.3) is available under *B* at the same pass. One not available
|
||
under *A* may be under *B*: by a later pass under `advance`, while under `repeat` and `no-code` the
|
||
first pass decides and the answer is the same;
|
||
- a statement that holds under *A* (§6.4) holds under *B*, and one refused
|
||
`availability_not_within_schedule` or `object_not_scheduled` may hold;
|
||
- a plan under *B* (§7) listens to every pass a plan under *A* listens to from the same instant
|
||
with the same holding, and is complete at the same instant wherever that plan is.
|
||
|
||
What does not extend an earlier schedule, and contradicts it, is §6.2 step 4's: a schedule that
|
||
drops an object, or withdraws, moves or changes a pass.
|
||
|
||
## 5. Blocks and symbols
|
||
|
||
### 5.1 RaptorQ
|
||
|
||
The `oti` of a `raptorq` entry, F, T, Z, N and Al, is one RFC 6330 admits when:
|
||
|
||
- F is from 1 to 942,574,504,275 (§3.3.2 as erratum 5548 corrects it: 56,403 symbols of
|
||
65,535 octets in each of 255 blocks);
|
||
- T is from 1 to 65,535 and a multiple of Al, and Al from 1 to 255 (§3.3.2, §3.3.3, §4.2);
|
||
- Z is from 1 to 255, the range of its 8-bit field (§3.3.3: the parameters are positive);
|
||
- N is from 1 to T / Al, so each sub-block has a sub-symbol of at least Al octets (§4.4.1.2);
|
||
- with *Kt* = ⌈F / T⌉, Z is at most *Kt*, so no source block is empty, and ⌈*Kt* / Z⌉ is at
|
||
most 56,403, *K′*<sub>max</sub> (§4.4.1.2, §5.1.2).
|
||
|
||
The object is then divided as RFC 6330 §4.4.1.2 divides it: (*KL*, *KS*, *ZL*, *ZS*) =
|
||
Partition[*Kt*, Z], where Partition[*I*, *J*] = (⌈*I* / *J*⌉, ⌊*I* / *J*⌋, *I* − ⌊*I* / *J*⌋ · *J*,
|
||
*J* − (*I* − ⌊*I* / *J*⌋ · *J*)). The first *ZL* source blocks have *KL* source symbols each and
|
||
the remaining *ZS* have *KS*. Each symbol is T octets; the last source symbol of the object is
|
||
padded to T.
|
||
|
||
### 5.2 Compact No-Code
|
||
|
||
The `oti` of a `no-code` entry, with L its `transfer_length`, E its `symbol_size` and B its
|
||
`max_block_length`, is one RFC 5445 admits when L is from 1 to 2<sup>48</sup> − 1, E from 1 to
|
||
65,535 and B from 1 to 2<sup>32</sup> − 1 (RFC 5445 §3.2.2.2; an object of no octets has
|
||
nothing to schedule), and the division of RFC 5052 §9.1 fits RFC 5445's 16-bit source block
|
||
number and encoding symbol ID (§3.2.1):
|
||
|
||
- the object has *Ts* = ⌈L / E⌉ source symbols, in *Nb* = ⌈*Ts* / B⌉ source blocks;
|
||
- *A*<sub>large</sub> = ⌈*Ts* / *Nb*⌉, *A*<sub>small</sub> = ⌊*Ts* / *Nb*⌋ and *I* = *Ts* −
|
||
*A*<sub>small</sub> · *Nb*: the first *I* blocks have *A*<sub>large</sub> source symbols and
|
||
the others *A*<sub>small</sub>;
|
||
- *Nb* is at most 65,536, and *A*<sub>large</sub> at most 65,536.
|
||
|
||
Every symbol is E octets but the object's last, which holds what remains of L.
|
||
|
||
### 5.3 What a receiver needs
|
||
|
||
A block of *K* source symbols is **held** when a receiver can recover it:
|
||
|
||
- **`no-code`**: when it has received every one of its *K* source symbols. There are no repair
|
||
symbols; a symbol missed is missed until a pass sends it again.
|
||
- **`raptorq`**: when it has received *K′* + *h* distinct encoding symbols of the block, where
|
||
*K′* is the least value at least *K* in RFC 6330 Table 2 (§5.6; its column of *K′* is restated
|
||
in Appendix A). RFC 6330 §5.8 bounds the decoder's failure to recover a block of *K′* source
|
||
symbols from *K′*, *K′* + 1 and *K′* + 2 encoding symbols, chosen uniformly at random, at
|
||
1 in 100, 1 in 10,000 and 1 in 1,000,000 on average. A block of *K* < *K′* symbols is padded
|
||
to *K′* with symbols the decoder knows (§5.3.1), so it may need fewer than *K′* + *h*
|
||
received; this text asks for the count §5.8 states its bound at, and no fewer.
|
||
- **`raptor`**: when it has received *K* + *a* distinct encoding symbols of the block, where the
|
||
margin *a* is 8, 16 or 28 at *h* 0, 1 or 2. RFC 5053 pads no block to a table of sizes, since §5.7
|
||
indexes every *K* it admits, and it states no bound on a decoder's failure: its decoder recovers a
|
||
block from "any set of encoding symbols only slightly more in number than the number of source
|
||
symbols" (§1), and the example it gives can fail (§5.5.2.2, §5.5.2.3). 3GPP TS 26.346 §7.2.2
|
||
requires a receiver to recover a block from a "mathematically sufficient set", which no count
|
||
establishes. The margins are this text's, set by measurement. With distinct encoding symbols
|
||
chosen uniformly at random, an independent implementation of RFC 5053 failed on 14 to 31 blocks in
|
||
100 at *K* + 2, at most 81 in 10,000 at *K* + 8, at most 2 in 18,000 at *K* + 16 and at most 4 in
|
||
6,000,000 at *K* + 28, at block sizes from 4 to 8,192 symbols (`raptor_margin_measurement` in
|
||
`tests/carousel.rs`, whose samples shrink as blocks grow, to 1,800 blocks of 8,192). That is a
|
||
measurement of one implementation, not a bound, and no bound comes with the count.
|
||
|
||
An **object** is held when every one of its source blocks is.
|
||
|
||
### 5.4 The symbols a pass sends
|
||
|
||
Each pass sends, of each source block of *K* source symbols, *m* = *K* + `repair` encoding
|
||
symbols:
|
||
|
||
- `repeat`: ESIs 0 to *m* − 1 in every pass;
|
||
- `advance`: in pass *j*, ESIs (*j* − 1) · *m* to *j* · *m* − 1. The first pass holds the source
|
||
symbols (ESIs 0 to *K* − 1, RFC 6330 §4.4.2, RFC 5053 §5.3.2) and every later pass only repair
|
||
symbols, none sent before.
|
||
|
||
An entry's symbols are ones its code can name (§4.2 rule 3.2) when:
|
||
|
||
- **`no-code`**: `repair` is 0 and `esi` is `repeat`. The code has no repair symbols, and an ESI
|
||
past a block's last source symbol names nothing;
|
||
- **`raptorq`**: the greatest ESI sent fits the 24-bit field (RFC 6330 §3.2): with *KL* the
|
||
larger block's *K*, *KL* + `repair` is at most 2<sup>24</sup> for `repeat`, and `count` ·
|
||
(*KL* + `repair`) at most 2<sup>24</sup> for `advance`, computed without overflow;
|
||
- **`raptor`**: the greatest ESI sent fits the 16-bit field (RFC 5053 §3.1): *KL* + `repair` is at
|
||
most 2<sup>16</sup> for `repeat`, and `count` · (*KL* + `repair`) at most 2<sup>16</sup> for
|
||
`advance`, computed without overflow.
|
||
|
||
The octets an entry's passes send, `bytes_sent`, are `count` · Σ *m* · T over its source blocks
|
||
for `raptorq` and `raptor`, and `count` · L for `no-code`, whose last symbol is short.
|
||
|
||
### 5.5 Raptor
|
||
|
||
The `oti` of a `raptor` entry, F, T, Z, N and Al, is one RFC 5053 admits when:
|
||
|
||
- F is from 1 to 2<sup>45</sup> − 1 (§3.2.2);
|
||
- T is from 1 to 65,535 and a multiple of Al, and Al from 1 to 255 (§3.2.2, §3.2.3, §4.1);
|
||
- Z is from 1 to 65,535, the range of its 16-bit field (§3.2.3);
|
||
- N is from 1 to 255, the range of its 8-bit field, and at most T / Al, so each sub-block has a
|
||
sub-symbol of at least Al octets (§3.2.3, §5.3.1.2);
|
||
- with *Kt* = ⌈F / T⌉, ⌊*Kt* / Z⌋ is at least 4 and ⌈*Kt* / Z⌉ at most 8,192: §5.7 gives a systematic
|
||
index for those block sizes and no others, and *K*<sub>max</sub> is 8,192 (§5.1.2, §5.3.1.2).
|
||
|
||
The object is then divided as RFC 5053 §5.3.1.2 divides it, by the Partition[] of §5.1: (*KL*, *KS*,
|
||
*ZL*, *ZS*) = Partition[*Kt*, Z]. The first *ZL* source blocks have *KL* source symbols each and the
|
||
remaining *ZS* have *KS*. Each symbol is T octets. The last source symbol of the object is padded to T
|
||
for encoding; RFC 5053 §5.3.2 lets a sender leave that padding out of a packet, and `bytes_sent`
|
||
counts it. A limit a bearer sets on these values, as TS 26.346 §7.2.3 does on a sub-block's size, is
|
||
the bearer's.
|
||
|
||
## 6. When an object is held
|
||
|
||
### 6.1 Inputs
|
||
|
||
The group receipt; the object receipts the verifier holds under it, each with its envelope
|
||
bytes where it holds them; the transmission and schedule claims it holds about the group, each
|
||
signer's in the order of its chain; a loss bound *L*; and an overhead *h*.
|
||
|
||
### 6.2 Steps
|
||
|
||
The loss bound and overhead are checked first: *L* above 999,999 is `loss_bound_invalid`, and
|
||
*h* other than 0, 1 or 2 is `overhead_invalid`; neither derives anything. Then:
|
||
|
||
1. **The group and its objects**: bearer-receipts §6.2 steps 1 and 2.
|
||
2. **The claims**: each claim, in order, under jwp-receipts §3.1 steps 1 to 5, then step 6: a
|
||
transmission claim by bearer-receipts §4.3 and a schedule by §4.3 here. A claim of another
|
||
class is `context_mismatch` at step 6. A claim refused at any step takes no further part.
|
||
3. **The declarations**: bearer-receipts §6.2 step 4.
|
||
4. **The schedules**: the accepted schedule claims by `bearer_id`, each instance's in the order
|
||
of their signer's chain (§6.1), which §4.3 step 2 makes one chain. When one of them does not
|
||
extend the one before it (§4.5), every schedule of that instance is set aside
|
||
(`schedule_contradicted`): a publisher that drops an object from its plan for one
|
||
transmission, or withdraws, moves or changes a pass, has stated neither plan, and a relying
|
||
party holds no promise that can change under it. Otherwise the last of them is the instance's
|
||
schedule, and each before it with another body is **extended**: its passes are the first of
|
||
the last one's, and it takes no further part. Two with the same body are one. The instance's
|
||
schedules are then set aside at the first of these that holds: step 3 set aside the
|
||
declarations of its `bearer_id` (`bearer_contradicted`); no declaration has its `bearer_id`
|
||
(`bearer_not_declared`); its declaration's `bearer` is `bidirectional`
|
||
(`schedule_on_bidirectional`), since a receiver with a path back asks for what it lacks and
|
||
is not served by a plan. Otherwise the instance's schedule **stands**, and so does each before
|
||
it with its body.
|
||
5. **Availability**, for each entry of each standing schedule, by §6.3.
|
||
|
||
### 6.3 Availability
|
||
|
||
For an entry, pass *j* ends at the instant `first_ms` + (*j* − 1) · `period_ms` +
|
||
`duration_ms`. For each source block of *K* source symbols, with *m* = *K* + `repair` and *x* =
|
||
⌊*L* · *m* / 1,000,000⌋ the most a receiver within *L* misses of it in one pass, the first pass
|
||
after which a receiver within *L* holds the block is, under §5.3:
|
||
|
||
- **`raptorq` or `raptor`, `advance`**: the least *j* from 1 to `count` with *j* · (*m* − *x*) at
|
||
least what §5.3 asks of the block. Every pass adds *m* − *x* symbols none before it sent.
|
||
- **`raptorq` or `raptor`, `repeat`**: 1 if *m* − *x* is at least what §5.3 asks, and none otherwise.
|
||
A later pass sends the same ESIs, and the ones a receiver missed may be missed again: within *L*,
|
||
nothing it adds is certain.
|
||
- **`no-code`**: 1 if *x* is 0, and none otherwise, for the same reason.
|
||
|
||
The object is **available** at the end of the greatest of its blocks' passes, or is **not
|
||
available within the schedule** if any block has none. A verifier reports for each entry its
|
||
`available_ms` (or `null`), the pass, `bytes_sent` (§5.4), and, for `raptorq`, the RFC 6330
|
||
§5.8 bound at *h* as `failure_one_in` (100, 10,000 or 1,000,000). For `raptor` it is `null`, since
|
||
RFC 5053 states no bound (§5.3); for `no-code` it is `null`, a block held being recovered exactly.
|
||
|
||
### 6.4 Statements offered about an object or a group
|
||
|
||
A verifier **MAY** be asked to check a statement, on a bearer instance, for a loss bound *L* and
|
||
an overhead *h*:
|
||
|
||
- **object *i* is held by *t***: holds when a standing schedule of the instance has an entry for
|
||
object *i* that is available at an instant at or before *t*. It is refused when no standing
|
||
schedule of the instance has one (`object_not_scheduled`), when it is not available within
|
||
the schedule (`availability_not_within_schedule`), and when it is available after *t*
|
||
(`availability_after_deadline`).
|
||
- **the group is held by *t***: holds when the statement holds for every object of the group,
|
||
0 to `leaf_count` − 1, and is refused with the first object's reason otherwise.
|
||
|
||
A statement that holds says what the publisher planned: a receiver within *L*, hearing every
|
||
pass the schedule names, holds the object by *t*, but for a decoding failure no more likely than
|
||
`failure_one_in` states, and under `raptor` no bound is stated on that failure. It does not say the
|
||
passes were sent (the declaration's) or heard (a reconciliation's).
|
||
|
||
A publisher's own deadline for an object is its timeliness claim's `by_ms`
|
||
([timeliness](timeliness.md) §4.1); held by it, the statement is the publisher's plan against its
|
||
own deadline.
|
||
|
||
## 7. The passes a receiver listens to
|
||
|
||
A receiver that holds a standing schedule's entry, the instant *t* it is at, its loss bound *L*,
|
||
the overhead *h* it decodes at, and, for each source block, the number *n* of distinct encoding
|
||
symbols of it that it holds (for `no-code`, of its source symbols), derives the passes to listen
|
||
to. The loss bound and overhead are checked as §6.2 checks them, and a holding that does not
|
||
give one number for each source block of the object, in the order §5.1 and §5.2 divide it into
|
||
them, or that holds more source symbols of a `no-code` block than it has, is `holding_invalid`.
|
||
Only passes that start at or after *t* count: a pass under way at *t* has sent symbols the
|
||
receiver did not hear. A pass adds, to a block not held, with *m* and *x* as §6.3:
|
||
|
||
- **`raptorq` or `raptor`, `advance`**: *m* − *x*, none of them sent before;
|
||
- **`raptorq` or `raptor`, `repeat`**, and **`no-code`**: max(0, *m* − *n* − *x*): of the *m* − *n*
|
||
ESIs it lacks, *x* may be the ones it misses again.
|
||
|
||
The passes are taken in order of their start. While some block is not held, the next pass is
|
||
listened to if it adds to a block not held, each such block's *n* growing by what it adds; the
|
||
first pass that adds to none ends the plan. The plan is the passes listened to, each with its
|
||
start and end; `complete_ms`, the instant the object is held: *t* when it already is, the last
|
||
pass's end when the passes listened to suffice, and `null` otherwise; and `listen_ms`, the sum
|
||
of their durations: the time the receiver's radio must be on. Outside those passes it **MAY** be
|
||
off. Under `repeat` and `no-code` at most one pass adds anything certain within *L*, since after
|
||
it a block lacks only symbols the receiver may miss again: a receiver that one pass does not
|
||
complete is not certain to be held after any number, and keeps listening, if it does, on its own
|
||
judgement of its channel. A receiver that acquires a schedule extending the one it plans with plans
|
||
with the later from then on: what it holds still counts, since each pass sends what it did (§4.5).
|
||
|
||
## 8. Mappings
|
||
|
||
A bearer that carries files with FEC states the same parameters in its own description of each
|
||
file, for its receivers: on FLUTE (RFC 6726) and the bearers built on it, the FEC Object
|
||
Transmission Information of each file in the file delivery table. A schedule restates them,
|
||
signed by the publisher, beside the group's receipts, so a relying party with no access to the
|
||
bearer can check a promise (§6.4) and a receiver can plan before it tunes (§7). A sink that holds
|
||
both compares them; a difference is the bearer's, and the sink reports it rather than choosing
|
||
one. The bearer's own repetition (its carousel of descriptions and files) is the schedule's
|
||
passes: a pass is one transmission of the object's symbols, whatever the bearer calls it. A receiver
|
||
of 3GPP MBMS download supports the Compact No-Code and Raptor schemes (TS 26.346 §7.2.2), so a
|
||
schedule of such an instance names `no-code` or `raptor`.
|
||
|
||
### 8.1 In-band repetition
|
||
|
||
A receipt held only off the bearer reaches only a receiver that had another path to it, and a
|
||
receiver on a bearer with no return path cannot ask. On an instance whose schedule stands, the
|
||
publisher **MUST** send its **in-band set** on the instance itself, each part as bearer-receipts
|
||
§9.1 carries it:
|
||
|
||
- the group receipt;
|
||
- its claims from the head of their chain (`seq` 0, `parent_receipt_hash` `null`) to the
|
||
instance's last schedule, in the order of the chain, among them the instance's declaration and
|
||
every schedule of it. A receiver that holds no claim of the signer then accepts each at
|
||
jwp-receipts §3.1 step 5, which refuses a claim whose parent it does not hold
|
||
(`parent_not_held`), a parent that on this bearer it cannot fetch;
|
||
- the head revision of the key log of the origin the schedule's `signer_id` names. It anchors
|
||
nothing (bearer-receipts §8.2): it carries a rotation or a revocation to a receiver that has the
|
||
origin anchored.
|
||
|
||
A chain holds every claim its key has signed, so a publisher **SHOULD** sign the groups it schedules
|
||
with a key that signs little else, which its origin lists with `delivery`, `transmission` and
|
||
`schedule` (jwp-receipts §7). A claim of the chain about another group is refused at step 6 against
|
||
this group and breaks nothing: it is held, so the claims after it chain to it (jwp-receipts §3.1
|
||
step 5).
|
||
|
||
**How often.** A schedule's **interval** is the least `period_ms` of its entries of two passes or
|
||
more, or, where every entry has one pass, the least `duration_ms`. From the start of the schedule's
|
||
first pass to the end of its last, the publisher sends the whole set at least once in every span of
|
||
that length. A receiver that joins in that time, and misses nothing of the set, holds it no later
|
||
than one interval after it joins. The set's files are outside every entry: their octets are not in
|
||
`bytes_sent`, and the loss bound of §2 says nothing of them. A receiver that misses a copy takes the
|
||
next.
|
||
|
||
**An extension.** A later schedule (§4.5) is in the set from when it is signed, chained after the
|
||
one it extends. A receiver that held the earlier and acquires the later reads one schedule that
|
||
stands, and one that joins afterwards reads both in the chain.
|
||
|
||
### 8.2 What a receiver acquired
|
||
|
||
A receiver that joined an instance, or a verifier given what it acquired, **MAY** check that it
|
||
suffices. The inputs are the instance's `bearer_id`, the group receipt, the claims in the order of
|
||
their chain, and the key logs acquired, each keyed by the origin it names. §6.2 steps 1 to 4 run over
|
||
the group receipt and the claims alone, with no object held and no claim held before the first; a
|
||
group that fails is refused with its reason, and nothing is derived. Then, at the first of these
|
||
that fails, the set is refused:
|
||
|
||
1. no claim of it is refused at jwp-receipts §3.1 step 5 (`inband_chain_incomplete`), so it holds
|
||
each signer's chain from its head;
|
||
2. a schedule of the instance stands (`inband_not_scheduled`);
|
||
3. a key log acquired is held for the origin that schedule's `signer_id` names, under jwp-receipts
|
||
§7.2 rules 1 to 3, and lists the schedule's key (`inband_key_log_missing`). A `signer_id` that
|
||
names no origin has no log to carry, and is refused here.
|
||
|
||
Otherwise the set **suffices**, and a verifier reports each claim's label and the interval of the
|
||
schedule that stands as `inband_ms`. A set that suffices holds what a receiver needs to plan (§7) and
|
||
to confirm. Confirming is bearer-receipts §8.3's, under anchors the receiver already holds, so a log
|
||
that lists the key as revoked still suffices here, and confirmation reports it. It does not say the
|
||
publisher sent the set as §8.1 asks.
|
||
|
||
### 8.3 The MoQ FEC catalog binding
|
||
|
||
`draft-ramadan-moq-fec-core-00` (fec-core) sends a track's objects with repair symbols on
|
||
tracks of their own, `draft-ramadan-moq-fec-block-codes-00` (block-codes) names RaptorQ as one
|
||
of its two codes, and `draft-ramadan-moq-fec-catalog-00` (fec-catalog) states the
|
||
configuration, an **FEC instance**, in the catalog. A schedule's `raptorq` entries and an
|
||
instance say the same things of the same symbols, one signed and beside the receipts, the
|
||
other in the catalog a receiver configures its decoder from. This section fixes the
|
||
correspondence, in both directions, so a publisher states one geometry twice and a sink that
|
||
holds both compares them, as §8 has it compare a bearer's description with the schedule.
|
||
|
||
**The block the binding sends.** Under the block profile `moq-object-v1` the unit of
|
||
protection is a whole MoQ object, framed as a **Protected Object Unit** (fec-core §5.2): the
|
||
object's Group ID and Object ID, 8 octets each; the length of its Immutable Properties, 2
|
||
octets, and those properties; the length of its payload, 4 octets, and the payload; every
|
||
integer big-endian. The object is the envelope (bearer-receipts §9.1), its Group ID the
|
||
group's on its track and its Object ID its `leaf_index`; its Immutable Properties hold its FEC
|
||
Source Info Property (fec-core §5.6), which names the SS_ID of its first source symbol, and
|
||
whatever else the track puts there. The unit, padded with zeros to a whole symbol, is cut
|
||
into *s* = ⌈(22 + *I* + F) / T⌉ source symbols of T octets, *I* being the octets of the
|
||
Immutable Properties and F the envelope's. Each scheduled object is one block: the publisher
|
||
closes a block at every object, as fec-core §5.4 lets it, so an entry, an object and a block
|
||
correspond, the block's `SS_Start` is the object's SS_ID and its `SSB_length` is *s*. The
|
||
block is encoded as block-codes §6.1 derives: one RFC 6330 object of Transfer Length *s* × T,
|
||
with Z = 1, N = 1 and Al = 8. The unit's integers are fixed-width so that it is the same
|
||
bytes at every MOQT draft (fec-core §5.2, written against -22): -23, -22 and -18 carry the
|
||
Immutable Properties as the same bytes, and a -16 session, whose integers inside them are
|
||
QUIC's, carries other bytes there, so a block is cut from the -22 form whichever draft a
|
||
session speaks.
|
||
|
||
So the code is the entry's, and the object it encodes is not: the entry's object transmission
|
||
information describes the envelope's F octets, K = ⌈F / T⌉ symbols, and the binding's block
|
||
holds the unit's *s* ≥ K. An entry **binds** only where everything §6 derives from it holds
|
||
of the block the binding sends. With *m* = K + `repair`, the symbols of a pass (§5.4):
|
||
|
||
| the entry | or |
|
||
|---|---|
|
||
| its `fec` is `raptorq`, with `source_blocks` 1 and `sub_blocks` 1 | `binding_no_form` |
|
||
| its `symbol_size` is a multiple of 8 and its `alignment` is 8, the derived information's (block-codes §6.1, §7) | `binding_symbol_size` |
|
||
| *s* is at most 56,403 and *K′* of *s* is *K′* of K (§5.3) | `binding_block_differs` |
|
||
| *m* − *s* is at least 1 | `binding_no_repair` |
|
||
| under `advance`, `count` is at most 256, and the greatest ESI the instance names is at most 2^24 − 1 | `binding_no_form` |
|
||
|
||
A pass of the bound entry sends the *s* source symbols and *m* − *s* repair symbols: the
|
||
schedule's *m* symbols of T octets, so its `bytes_sent`, its availability at every loss bound
|
||
and overhead (§6.3) and every statement held to it (§6.4) are true of what is sent, the block
|
||
being held at the same *K′* + *h* of the same *m* a pass. The framing costs *s* − K of the
|
||
entry's repair symbols, and an entry whose framing crosses to another *K′* is refused rather
|
||
than bound to a block its schedule would overstate; a publisher chooses T, or `repair`, so
|
||
that it binds. `raptor` and `no-code` entries have no form: the drafts' registry holds neither
|
||
code. Nor has the drafts' mandatory code, Reed-Solomon, an entry: it is not a code of §4.1.
|
||
|
||
**Instances and layers.** The entry's passes are the instance's repair layers (fec-core §7.3).
|
||
Layer 0 carries P<sub>0</sub> = *m* − *s* repair symbols a block, RS_IDs 0 to *m* − *s* − 1,
|
||
which with the source symbols are the ESIs 0 to *m* − 1 of the first pass (a repair symbol's
|
||
ESI is `SSB_length` + RS_ID). Under `repeat` that is the only layer, and every pass sends its
|
||
objects again, at the same Locations. Under `advance`, pass *j* ≥ 2 is layer *j* − 1, of *m*
|
||
repair symbols: its RS_IDs start at O<sub>*j*−1</sub> = (*j* − 1) · *m* − *s*, so its ESIs are
|
||
(*j* − 1) · *m* to *j* · *m* − 1, the ones §5.4 gives the pass. Entries of one symbol size and
|
||
one list of layers share an instance, whose `sourceSymbols`, the bound K of fec-core, is the
|
||
greatest *s* among them. The first entry's is instance 0, and each other geometry takes the
|
||
next Instance ID in the order it first appears, to 255 (`binding_too_many_instances`). An
|
||
object of instance 0 carries the four-octet FEC Source Info and any other the five-octet
|
||
entry (fec-core §5.6), so *I*, and *s* with it, is computed for the instance an entry goes to:
|
||
instance 0's form first, then the other.
|
||
|
||
Within an instance the SS_IDs run on from object to object in `leaf_index` order, modulo
|
||
2^32, from a first value the publisher chooses; a block's repair objects and symbol-track
|
||
objects are the Group whose ID is the block's extended `SS_Start`, its predecessor's plus the
|
||
predecessor's `SSB_length` (fec-core §7.6).
|
||
|
||
**In the catalog.** The source track's entry carries the `fec` array, one object an instance:
|
||
`instanceId`, `scheme` `"raptorq"`, `blockProfile` `"moq-object-v1"`, `sourceSymbols`,
|
||
`symbolSize`, `repairSymbols` (P<sub>0</sub>), `interleaveDepthMs` (the longest `duration_ms`
|
||
among the instance's entries: a pass is the span a block is sent in), `repairTrack` and
|
||
`symbolTrack`. Each layer is a track of packaging `fec-repair` with `source`, `instanceId`,
|
||
`repairLayer`, `repairSymbols` and `priority`, and the symbol track one of packaging
|
||
`fec-source` (fec-catalog §3; [moq-streaming-format](moq-streaming-format.md) §4.4).
|
||
|
||
**And back.** A receiver reads a catalog's instances for a source track as fec-catalog §5 and
|
||
fec-core §10.3 require, and fails closed: an instance, a repair track or a symbol track that
|
||
breaks a rule of theirs refuses the track's FEC here (`binding_catalog_malformed`), since a
|
||
schedule binds to all of a track's instances. Among the rules: each member of its type and
|
||
range; `repairTrack` and `symbolTrack` each naming one track of the catalog, of its packaging,
|
||
that no other instance names; Instance IDs unique; layers contiguous from 0 with no layer
|
||
declared twice; layer 0's `repairSymbols`, where its track restates it, the instance's; no
|
||
geometry on a repair or symbol track; no repair or symbol track that serves no instance;
|
||
priorities from 192 to 255 and not falling with the layer; no `enhancementRepair`;
|
||
`sourceSymbols` at most 56,403 and `symbolSize` a multiple of 8. A key it does not know it
|
||
ignores. An instance of another scheme or block profile has no form here
|
||
(`binding_no_form`): FEC for it is unavailable, which the drafts do not count a violation.
|
||
|
||
A sink that holds the schedule and the catalog compares them: the catalog's instances are the
|
||
ones the schedule's entries bind to, each of the entry's symbol size and layers, with a
|
||
`sourceSymbols` that holds the largest block and, where it states a span, one that covers the
|
||
longest pass; otherwise `binding_schedule_mismatch`, which the sink reports, choosing
|
||
neither. From an instance, a block's `SSB_length` and the envelope's length, the entry's code
|
||
members are recovered: T, the transfer length, one source block, one sub-block, alignment 8,
|
||
and `repair` = `SSB_length` + P<sub>0</sub> − K. Its passes' instants are the schedule's
|
||
alone.
|
||
|
||
**Symbols.** A symbol-track object carries one source symbol: its Group ID is the block's,
|
||
its Object ID the symbol's ESI and its payload T octets (fec-core §6.2). A repair object's
|
||
payload is the 13-octet Repair FEC Payload ID, `SS_Start` (32 bits), `RSB_length`, RS_ID and
|
||
`SSB_length` (24 bits each), then T octets of symbol (fec-core §7.5); on layer *i* it is
|
||
Object *j* of the block's Group, with RS_ID O<sub>*i*</sub> + *j*, and its `RSB_length` is the
|
||
instance's total over its layers. A receiver places a symbol by where it arrived and discards
|
||
one that does not fit, leaving every block as it was (`binding_symbol_misplaced`): a
|
||
symbol-track object of another block's Group, of a payload that is not T octets, or of an
|
||
Object ID not below the block's `SSB_length` (below `sourceSymbols`, until a repair object
|
||
has told it); a repair object whose payload is not 13 + T octets, whose Group is another
|
||
block's or disagrees with its `SS_Start`, whose `SSB_length` is 0, above `sourceSymbols` or
|
||
not the one the block already has, whose `RSB_length` is not the instance's total, or whose
|
||
RS_ID is not the one its layer and Object ID give.
|
||
|
||
**Rebuilding.** A block held, complete or decoded, is walked as fec-core §9.2 walks it: each
|
||
unit read in turn, every octet inside the block, zeros from the unit's end to the end of its
|
||
last symbol, the walk ending exactly at the block's end; the unit's Immutable Properties read
|
||
as pairs, hold no Immutable Properties Property and exactly one FEC Source Info Property, of
|
||
a form fec-core §5.6 admits, naming for this instance the SS_ID the unit was found at; and no
|
||
Location twice. Anything else delivers no object of the block (`binding_unit_malformed`). A
|
||
rebuilt object is then held to its entry: its Location is the group's and the entry's
|
||
`leaf_index` (`binding_unit_malformed`), and its payload has the entry's `transfer_length`
|
||
and `content_hash` (`binding_content_mismatch`). That last check is the schedule's own, and
|
||
it is what a symbol of another object cannot pass: one offered under its own header is
|
||
discarded, and one put in place of a block's own symbol, by an entity that can write on the
|
||
track, yields a unit that does not walk, names another object, or hashes to other bytes. The
|
||
group's receipts then hold the object as they hold any (bearer-receipts §8.3).
|
||
|
||
| code | when |
|
||
|---|---|
|
||
| `binding_no_form` | The entry's code, source blocks or sub-blocks have no instance, or it has more passes under `advance` than layers; or a catalog's instance is of another scheme or block profile. |
|
||
| `binding_symbol_size` | The entry's symbol size is not a multiple of 8, or its alignment is not 8. |
|
||
| `binding_block_differs` | The unit's symbols fall under another *K′* than the envelope's, or pass 56,403. |
|
||
| `binding_no_repair` | The unit's framing leaves no repair symbol of the entry's. |
|
||
| `binding_too_many_instances` | The schedule's entries need more than 256 instances. |
|
||
| `binding_catalog_malformed` | A catalog's FEC configuration for the track breaks a rule of fec-catalog §5 or fec-core §10.3. |
|
||
| `binding_schedule_mismatch` | The catalog's instances are not the ones the schedule binds to. |
|
||
| `binding_symbol_misplaced` | A symbol-track or repair object does not fit the block it arrived for. |
|
||
| `binding_unit_malformed` | A block's units do not walk, or a rebuilt object is not at its entry's Location. |
|
||
| `binding_content_mismatch` | A rebuilt object is not of its entry's length and content hash. |
|
||
|
||
**The property's type.** fec-core asks for the type `0x11` for the FEC Source Info Property
|
||
and calls it provisional (its §5.6, §15.1). `draft-ietf-moq-loc-05` gives `0x11` to its
|
||
codec string ([loc-carriage](loc-carriage.md) §3), in the same space, so an object cannot
|
||
carry both: a track of packaging `loc` whose objects carry a codec string has no instance
|
||
under this binding until one of the two moves. The reference writes `0x11`, as the draft
|
||
says; a WAI track's objects carry no LOC property.
|
||
|
||
**Reference and corpus.** The reference implementation is the `carousel::moq_fec` module of
|
||
`wai-rs` (feature `carousel`). It encodes and decodes no symbol: the code is RFC 6330's. The
|
||
corpus is [`live-conformance/carousel-moq-fec/`](../live-conformance/carousel-moq-fec/):
|
||
groups of entries with their objects, the instances and catalog tracks they bind to, and each
|
||
object's unit, symbol-track objects and repair objects; entries that do not bind; and catalogs
|
||
one edit from a valid one with what reading each gives. `tests/carousel_moq_fec.rs` writes it
|
||
with an independent implementation of RFC 6330, whose symbols for block-codes Appendix B's two
|
||
blocks are that appendix's, and requires each object back, from what a receiver within each
|
||
loss bound holds at the pass §6.3 states, as the object its entry signs; it plants a symbol of
|
||
another object in each way above and requires that no object comes of it.
|
||
`live-conformance/carousel_moq_fec_verify.py` is the second implementation, written from this
|
||
section: it binds each group and reads each catalog itself, cuts each unit and requires the
|
||
corpus's symbol-track objects and the headers of its repair objects, rebuilds each object from
|
||
its symbol track, and plants the faults that need no decoding. It does not implement RFC
|
||
6330, so the repair symbols' values are checked by the test alone.
|
||
|
||
What this revision does not do: no publisher or relay of the reference sends these tracks on a
|
||
session (the module is the binding, which a publisher and a receiver call); FEC on the
|
||
draft's multicast endpoints is under another block profile and packaging (bearer-receipts
|
||
§9.7); and repair layers added to a running instance (fec-core §7.3.2) are not handled: a
|
||
later schedule that extends an earlier one (§4.5) binds to a longer list of layers, which a
|
||
receiver reads from a new catalog.
|
||
|
||
## 9. Reasons
|
||
|
||
The codes this extension adds, each with where it is raised:
|
||
|
||
| reason | raised at |
|
||
|---|---|
|
||
| `schedule_not_scheduled` | §4.2 rule 1 |
|
||
| `schedule_objects_malformed` | §4.2 rule 2 |
|
||
| `schedule_oti_invalid` | §4.2 rule 3.1 |
|
||
| `schedule_symbols_invalid` | §4.2 rule 3.2 |
|
||
| `schedule_passes_invalid` | §4.2 rule 3.3 |
|
||
| `schedule_subject_mismatch` | §4.3 step 1 |
|
||
| `schedule_signer_mismatch` | §4.3 step 2 |
|
||
| `schedule_leaf_out_of_range` | §4.3 step 3 |
|
||
| `schedule_before_group` | §4.3 step 4 |
|
||
| `schedule_content_mismatch` | §4.3 step 5 |
|
||
| `schedule_length_mismatch` | §4.3 step 5 |
|
||
| `schedule_contradicted` | §6.2 step 4 |
|
||
| `schedule_on_bidirectional` | §6.2 step 4 |
|
||
| `loss_bound_invalid` | §6.2, §7 |
|
||
| `overhead_invalid` | §6.2, §7 |
|
||
| `holding_invalid` | §7 |
|
||
| `object_not_scheduled` | §6.4 |
|
||
| `availability_not_within_schedule` | §6.4 |
|
||
| `availability_after_deadline` | §6.4 |
|
||
| `inband_chain_incomplete` | §8.2 check 1 |
|
||
| `inband_not_scheduled` | §8.2 check 2 |
|
||
| `inband_key_log_missing` | §8.2 check 3 |
|
||
|
||
A schedule also raises `bearer_contradicted` and `bearer_not_declared` (bearer-receipts §10) at
|
||
§6.2 step 4; a group or object the codes of jwp-receipts §3.3; and a claim every code of
|
||
jwp-receipts §3.1 steps 1 to 5 and those of step 6 named in §4.3 and bearer-receipts §4.3.
|
||
|
||
## 10. Conformance
|
||
|
||
[`live-conformance/carousel/`](../live-conformance/carousel/) holds the corpus, written from
|
||
fixed seeds by `wai_carousel_vectors gen` as `vectors.json`. It has:
|
||
|
||
- **Availability**: RaptorQ objects of one and of several source blocks of two sizes, under
|
||
`advance` and `repeat`, at loss bounds of zero and above, reaching a later pass, the last
|
||
pass, and none; at each overhead; Raptor objects the same, among them one not held where the same
|
||
parameters under RaptorQ would be; a Compact No-Code object available at the first pass with
|
||
no loss and at none with any; a group of several objects; the same object simulcast on two
|
||
instances with different schedules.
|
||
- **Statements**: an object and a group held by a deadline, after it, not within the schedule,
|
||
and not scheduled.
|
||
- **Plans**: a receiver tuning in before the first pass, during one, between passes, holding
|
||
part of an object, holding all of it, and one under `repeat` that one pass does not complete.
|
||
- **Extensions** (§4.5): passes added until an object not within the schedule is held; an object
|
||
added, and the later schedule repeated; one pass, its period not read, given a period; `repeat`,
|
||
to which more passes add nothing certain; and each thing that does not extend: a pass withdrawn,
|
||
the first pass, the period, the duration, the repair, the ESIs or the code changed, an object
|
||
dropped, and a schedule that extends the first and withdraws a pass of the second. A plan under an
|
||
extended entry, and a chain on an instance no declaration names.
|
||
- **In-band sets** (§8.2): a set that suffices, with the interval of a repeated schedule, of one of
|
||
single passes and of a mix of the two; with an extension; with a chain that holds a claim about
|
||
another group, without its head and out of order; with no schedule, no declaration, a schedule of
|
||
another instance and a contradicted one; a key log absent, of another origin, naming another
|
||
origin, with a broken signature, unreadable, not listing the key and revoking it; and a group that
|
||
fails.
|
||
- **Negative controls**, one for each reason of §9 that a claim or a verdict raises, and the
|
||
bounds of §5.1, §5.2, §5.4 and §5.5 on both sides.
|
||
|
||
A conforming verifier gives every case the outcome, the reason and everything else the corpus
|
||
states. `wai_carousel_vectors verify` and the test `tests/carousel.rs` check that the file
|
||
regenerates byte for byte, that `wai-rs` gives every case its outcome, that every reason of §9
|
||
appears, that §9 lists exactly the reference's codes, and that Appendix A is the reference's
|
||
table. Against an independent implementation of RFC 6330, the test also requires the reference's
|
||
*K′* and Partition[] to agree for every block size, and decodes each RaptorQ object a case holds
|
||
available at a pass: it encodes the object under the entry's parameters, drops in each pass the
|
||
first ⌊*L* · *m* / 1,000,000⌋ symbols of each block (in the first pass, source symbols, so the
|
||
decoder must decode), and requires the object back from what is left by that pass's end. It does the
|
||
same for each Raptor object against an independent implementation of RFC 5053, and on a fixed sample
|
||
requires that decoder to fail on at least 1 block in 10 at *K* + 2, on at most 1 in 100 at the margin of
|
||
*h* = 0, at most 1 in 10,000 at that of *h* = 1, and on none of 10,000 at that of *h* = 2.
|
||
`live-conformance/carousel_verify.py` is the second implementation, written from this text: it
|
||
shares no code with `wai-rs`; it verifies groups and objects with the JWP corpus's second
|
||
implementation (`jwp-conformance/jwp_verify.py`), claims through steps 1–5 with the claim
|
||
corpus's (`claim-conformance/claim_verify.py`), whose step 4 holds §4.2 with §5.1, §5.2, §5.4 and
|
||
§5.5, and key logs with the signer corpus's (`signer-conformance/signer_verify.py`); it reads
|
||
Appendix A and §9 from this text, writes §4.3, §4.5, §5.3, §6, §7 and §8.2 itself, mutates
|
||
copies of the corpus and requires every copy to fail, and drops each of its own checks in turn
|
||
and requires the corpus to catch the weakened verifier.
|
||
|
||
## 11. Patents
|
||
|
||
The IETF holds disclosures of patents claimed to read on RaptorQ, all from one holder: #1292,
|
||
#1511 and #1553 on the drafts of RFC 6330, #1958 on RFC 6330, and #2554 (19 March 2015), which
|
||
updates #1958 and has not itself been updated. Its licensing declaration is written out in the
|
||
holder's words, not chosen from the disclosure form's options, and gives two terms, by the
|
||
product that implements the code:
|
||
|
||
- **a device that fully implements the specification and implements a wireless wide-area
|
||
standard** (the disclosure's example is a UMTS handset or infrastructure equipment): the
|
||
holder offers a licence as part of its established terms for such devices, with no royalty
|
||
beyond its standard rate for them. The class is **royalty-bearing**, within the licence the
|
||
device already needs;
|
||
- **a device that fully implements the specification and implements no wireless wide-area
|
||
standard**: the holder will not assert the claims against anyone who makes, uses, sells,
|
||
imports or offers it, solely for that implementation; it keeps the right to assert them
|
||
against a party that asserts a patent against its or its affiliates' products, and against a
|
||
product or part that does not fully implement the specification. The class is **royalty-free
|
||
non-assertion**, conditional on full implementation and suspended for a party that asserts.
|
||
|
||
Raptor (RFC 5053), which §4.1 names, has disclosures from the same
|
||
holder and an earlier one: #615, #618, #702 and #734 on its drafts; #1185 (8 September 2009) on
|
||
RFC 5053, which declared a reasonable and non-discriminatory licence with possible royalty or
|
||
fee; and #1289, #1508, #1744 and #1957 (6 February 2013), each updating the one before. #1957 has
|
||
not been updated, and gives the same two terms as #2554. The holder's general licence statement,
|
||
#1176 (20 July 2009), relates to no one document: it offers a non-exclusive licence on reasonable
|
||
and non-discriminatory terms, on the basis of reciprocity, for the holder's submissions that an
|
||
IETF standard incorporates.
|
||
|
||
No disclosure relates to RFC 5445 or its draft, nor to RFC 5052, whose division §5.2 applies.
|
||
The IETF takes no position on the validity or scope of any disclosed claim (RFC 8179 §4).
|
||
|
||
Therefore:
|
||
|
||
- **`no-code` is the default.** Neither RaptorQ nor Raptor is royalty-free for every
|
||
implementer: whether a royalty is due turns on the product, which a schedule does not state.
|
||
Under SPEC §5 a capability that is not royalty-free is never a default, and the same holds
|
||
here: a tool that writes schedules writes `no-code` unless it is set to write `raptorq` or
|
||
`raptor`.
|
||
- **The reference implementation encodes and decodes no symbol** (§1). Its `carousel` module
|
||
computes, from the parameters an entry states, the division of §5 and the counts of §6 and §7.
|
||
Its test decodes RaptorQ with an independent implementation of RFC 6330, and Raptor with one of
|
||
RFC 5053, each a development dependency of `wai-rs`, which no build of the library carries.
|
||
- Whether a product that encodes or decodes RaptorQ or Raptor symbols needs a licence under
|
||
these disclosures is a decision for its distributor.
|
||
|
||
## 12. Security and privacy considerations
|
||
|
||
- **A schedule is the publisher's word.** Nothing checks that the passes were sent as planned;
|
||
a declaration attests transmission and no more, and a relying party that needs to know what
|
||
receivers held counts reconciliations (bearer-receipts §6). A schedule that promises more
|
||
than was sent is a false statement signed by the group's key, and is evidence of it.
|
||
- **An extension is the publisher's word too.** It adds to what the publisher said it would send,
|
||
and is evidence as the schedule it extends is. It changes no statement that held (§4.5), and a
|
||
later schedule that would is set aside with the rest of its instance's (§6.2 step 4).
|
||
- **What rides on the bearer is verified as anything is.** Whoever controls the bearer can withhold
|
||
the in-band set, and a receiver that does not acquire it holds nothing to plan with; it cannot
|
||
alter a signed claim, and a key log it carries anchors nothing (bearer-receipts §8.2).
|
||
- **A loss bound is the relying party's.** Every statement holds for every pattern of loss
|
||
within it (§2). A channel that exceeds it, in a fade or under interference, voids nothing in
|
||
the schedule and nothing here predicts how often that happens.
|
||
- **It discloses the plan, not the audience.** A schedule names objects, times and code
|
||
parameters, which the bearer's own file descriptions already carry to every receiver. A
|
||
receiver that plans with it (§7) sends nothing, and a unidirectional bearer's property that
|
||
the source learns nothing of its receivers (unidirectional-delivery §2) is kept.
|
||
- **Bounded work.** An entry's arithmetic is a few products per block size, and an object has
|
||
blocks of at most two sizes under every code; a schedule's
|
||
cost is linear in its entries, whatever the passes' count, and so is comparing it with the one
|
||
before it (§4.5).
|
||
|
||
## Appendix A. The values of K′
|
||
|
||
The column *K′* of RFC 6330 Table 2 (§5.6), in increasing order, restated so that §5.3 can be
|
||
applied from this text. The other columns of that table are the code's, and nothing here uses
|
||
them.
|
||
|
||
<!-- k-prime:begin -->
|
||
```
|
||
10, 12, 18, 20, 26, 30, 32, 36, 42, 46, 48, 49, 55, 60, 62, 69,
|
||
75, 84, 88, 91, 95, 97, 101, 114, 119, 125, 127, 138, 140, 149, 153, 160,
|
||
166, 168, 179, 181, 185, 187, 200, 213, 217, 225, 236, 242, 248, 257, 263, 269,
|
||
280, 295, 301, 305, 324, 337, 341, 347, 355, 362, 368, 372, 380, 385, 393, 405,
|
||
418, 428, 434, 447, 453, 466, 478, 486, 491, 497, 511, 526, 532, 542, 549, 557,
|
||
563, 573, 580, 588, 594, 600, 606, 619, 633, 640, 648, 666, 675, 685, 693, 703,
|
||
718, 728, 736, 747, 759, 778, 792, 802, 811, 821, 835, 845, 860, 870, 891, 903,
|
||
913, 926, 938, 950, 963, 977, 989, 1002, 1020, 1032, 1050, 1074, 1085, 1099, 1111, 1136,
|
||
1152, 1169, 1183, 1205, 1220, 1236, 1255, 1269, 1285, 1306, 1347, 1361, 1389, 1404, 1420, 1436,
|
||
1461, 1477, 1502, 1522, 1539, 1561, 1579, 1600, 1616, 1649, 1673, 1698, 1716, 1734, 1759, 1777,
|
||
1800, 1824, 1844, 1863, 1887, 1906, 1926, 1954, 1979, 2005, 2040, 2070, 2103, 2125, 2152, 2195,
|
||
2217, 2247, 2278, 2315, 2339, 2367, 2392, 2416, 2447, 2473, 2502, 2528, 2565, 2601, 2640, 2668,
|
||
2701, 2737, 2772, 2802, 2831, 2875, 2906, 2938, 2979, 3015, 3056, 3101, 3151, 3186, 3224, 3265,
|
||
3299, 3344, 3387, 3423, 3466, 3502, 3539, 3579, 3616, 3658, 3697, 3751, 3792, 3840, 3883, 3924,
|
||
3970, 4015, 4069, 4112, 4165, 4207, 4252, 4318, 4365, 4418, 4468, 4513, 4567, 4626, 4681, 4731,
|
||
4780, 4838, 4901, 4954, 5008, 5063, 5116, 5172, 5225, 5279, 5334, 5391, 5449, 5506, 5566, 5637,
|
||
5694, 5763, 5823, 5896, 5975, 6039, 6102, 6169, 6233, 6296, 6363, 6427, 6518, 6589, 6655, 6730,
|
||
6799, 6878, 6956, 7033, 7108, 7185, 7281, 7360, 7445, 7520, 7596, 7675, 7770, 7855, 7935, 8030,
|
||
8111, 8194, 8290, 8377, 8474, 8559, 8654, 8744, 8837, 8928, 9019, 9111, 9206, 9303, 9400, 9497,
|
||
9601, 9708, 9813, 9916, 10017, 10120, 10241, 10351, 10458, 10567, 10676, 10787, 10899, 11015, 11130, 11245,
|
||
11358, 11475, 11590, 11711, 11829, 11956, 12087, 12208, 12333, 12460, 12593, 12726, 12857, 13002, 13143, 13284,
|
||
13417, 13558, 13695, 13833, 13974, 14115, 14272, 14415, 14560, 14713, 14862, 15011, 15170, 15325, 15496, 15651,
|
||
15808, 15977, 16161, 16336, 16505, 16674, 16851, 17024, 17195, 17376, 17559, 17742, 17929, 18116, 18309, 18503,
|
||
18694, 18909, 19126, 19325, 19539, 19740, 19939, 20152, 20355, 20564, 20778, 20988, 21199, 21412, 21629, 21852,
|
||
22073, 22301, 22536, 22779, 23010, 23252, 23491, 23730, 23971, 24215, 24476, 24721, 24976, 25230, 25493, 25756,
|
||
26022, 26291, 26566, 26838, 27111, 27392, 27682, 27959, 28248, 28548, 28845, 29138, 29434, 29731, 30037, 30346,
|
||
30654, 30974, 31285, 31605, 31948, 32272, 32601, 32932, 33282, 33623, 33961, 34302, 34654, 35031, 35395, 35750,
|
||
36112, 36479, 36849, 37227, 37606, 37992, 38385, 38787, 39176, 39576, 39980, 40398, 40816, 41226, 41641, 42067,
|
||
42490, 42916, 43388, 43840, 44279, 44729, 45183, 45638, 46104, 46574, 47047, 47523, 48007, 48489, 48976, 49470,
|
||
49978, 50511, 51017, 51530, 52062, 52586, 53114, 53650, 54188, 54735, 55289, 55843, 56403
|
||
```
|
||
<!-- k-prime:end -->
|