Skip to content

feat(v2): Intelligent Mail - #233

Merged
s2x merged 1 commit into
v2from
v2-intelligent-mail
Sep 3, 2026
Merged

feat(v2): Intelligent Mail#233
s2x merged 1 commit into
v2from
v2-intelligent-mail

Conversation

@s2x

@s2x s2x commented Sep 3, 2026

Copy link
Copy Markdown
Member

The twenty-eighth generator, and the first four-state code in the library that is not read a character at a time.

intelligent-mail (aliases imb, usps-imb, onecode, usps4cb), pure PHP, no options. Twenty digits of tracking code and then 0, 5, 9 or 11 of routing code, written after a hyphen or run straight on.

What makes it different

The payload becomes one 102-bit number, that number becomes ten thirteen-bit characters, and those hundred and thirty bits are scattered across the sixty-five bars. Nothing is local: every symbol is sixty-five bars wide whether it carries a routing code or not, and changing one digit moves at least a quarter of them (the observed minimum over a twenty-five digit payload is twenty-five bars). That scattering is the damage tolerance — mail is read at speed off folded and stamped envelopes, so a character spread over the full width loses a bit to a smudge instead of being destroyed. An eleven-bit CRC folded into the value catches what is left: detection, not correction.

The one table, measured rather than typed

Routing offsets, CRC-11, base-1365 codewords, and even the 1365-entry character table are all arithmetic — the table is every thirteen-bit pattern with five bits set, then every one with two, each beside its mirror image.

The exception is which bit of which character each bar draws: sixty-five rows USPS-B-3200 prints as letter-and-number pairs. A transposition there moves two bits between characters and is invisible in anything but a bar-for-bar comparison. So tools/intelligent_mail_placement.py solves it instead — the characters are computed, zint draws the bars, and each bar's two bits are matched to the (character, bit) they track across 240 payloads. A unique candidate for all 130 assignments is the whole chain confirmed at once, before a line of PHP ran; same method as tools/maxicode_placement.py, for the same reason.

102 bits without a dependency

Thirty-one digits do not fit in a PHP integer and this library requires no extensions, so Number carries the value as thirteen bytes and does multiply-and-add and divide-with-remainder by hand. Overflow throws rather than wraps.

Verification

Same one-opinion caveat as the rest of the family — no free decoder reads a four-state postal code. 78 reference symbols bar for bar against zint (composer reference:intelligent-mail), reaching all four routing lengths, all five endorsement digits and every check bit both ways; plus the rendered PNG measured back into bars, which is now one test over the family.

Because a symbol this scattered is either right or wrong nearly everywhere at once, the properties underneath the fixture are asserted separately: the character table's two enumeration rules, the bar map being a permutation of all 130 bits, the four routing lengths drawing four different symbols, and the arithmetic surviving a trip past 2^63 and back.

Checks

  • composer lint clean (cs, phpstan, rector, kb-lint)
  • Full suite: 15510 tests, 268870 assertions
  • SCANME_REQUIRE_DECODER=1 round trip: 363 tests, 1408 assertions

The other four-state codes draw a character at a time. Here the payload
becomes one 102-bit number, that number becomes ten thirteen-bit
characters, and those bits are scattered across all sixty-five bars: no
bar belongs to a digit, one changed digit moves at least a quarter of the
symbol, and every symbol is the same width whether it carries a routing
code or not. That is the damage tolerance, and an eleven-bit CRC folded
into the value is what catches what survives it.

Everything in the symbology is arithmetic except one table: which bit of
which character each bar draws. That one is measured against zint rather
than transcribed (tools/intelligent_mail_placement.py), the same way
MaxiCode's placement is -- and a unique solution for all 130 assignments
across 240 payloads confirms the routing offsets, the CRC and the radices
along with it, before a line of PHP ran.

The 102-bit arithmetic is done by hand on thirteen bytes, since thirty-one
digits do not fit in a PHP integer and the library requires no extensions.
Overflow throws rather than wraps.
@s2x
s2x merged commit 57f581f into v2 Sep 3, 2026
5 checks passed
@s2x
s2x deleted the v2-intelligent-mail branch September 3, 2026 20:10
s2x added a commit that referenced this pull request Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant