Skip to content

\bmod as a binary operator - #264

Merged
kostub merged 2 commits into
masterfrom
feature/modular-arithmetic-pr1
Jul 25, 2026
Merged

\bmod as a binary operator#264
kostub merged 2 commits into
masterfrom
feature/modular-arithmetic-pr1

Conversation

@kostub

@kostub kostub commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Part 1 of 3 in the modular-arithmetic stack.

Plan: docs/plans/2026-07-25-modular-arithmetic.md
LLD: docs/lld/2026-07-13-modular-arithmetic.md

Goal

\bmod parses, renders upright with binary-operator spacing, and round-trips through serialization. Independent of the macro mechanism — it is a single symbol-table entry, and it ships user-visible value on its own.

Commits

  1. [item 1] Add \bmod as a binary-operator symbol
  2. [item 2] Test \bmod boundary demotion and upright rendering

Stack

  • PR 1 (this one)\bmod as a binary operator
  • PR 2 — MTMacroAtom and two-phase finalized
  • PR 3 — Macro registry, template parsing, and the three commands

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ts3f5UtUywaqimvE4U1rkw

Summary by CodeRabbit

  • New Features
    • Added support for the LaTeX \bmod command.
    • Modular arithmetic expressions now render “mod” with appropriate operator formatting and spacing.
    • Expressions using \bmod can be parsed and serialized reliably, including cases where the operator appears at a boundary.

kostub added 2 commits July 25, 2026 07:33
Adds \bmod to the operators block of +supportedLatexSymbols as a plain
Bin atom with ASCII nucleus "mod". It renders upright because
changeFont's italic remap only applies to Variable/Number atoms, and
serialization round-trips automatically via the auto-populated reverse
(nucleus, type) map.
Characterization tests over existing finalize/typesetter machinery:
a Bin \bmod with no left/right operand demotes to Unary (TeX Rule 5),
the resulting display never trips the (Open, Bin) kMTSpaceInvalid
assert, the rendered glyphs are the upright ASCII "mod" rather than
italic mathematical alphanumerics, and a demoted Bin still serializes
back to \bmod via the Un-to-Bin fallback in
latexSymbolNameForAtom:. No production change required.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Adds \bmod support to the LaTeX symbol factory as a binary operator rendering upright mod, with tests for parsing, serialization, rendering, discovery, and boundary reclassification.

bmod LaTeX support

Layer / File(s) Summary
Register bmod symbol
iosMath/lib/MTMathAtomFactory.m
Adds the bmod mapping with a binary-operator atom and mod nucleus.
Validate bmod behavior
iosMathTests/MTModularArithmeticTest.m
Tests parsing, display generation, upright rendering, symbol discovery, serialization, and unary reclassification at expression boundaries.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding \bmod support as a binary operator.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/modular-arithmetic-pr1

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
iosMathTests/MTModularArithmeticTest.m (1)

118-124: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the actual binary-operator spacing.

XCTAssertNoThrow only proves that layout construction avoids an invalid-spacing assertion; it does not verify that \bmod receives binary-operator spacing. Add a layout/spacing assertion for an interior expression such as 17 \bmod 5, ideally compared with another Bin atom such as +.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@iosMathTests/MTModularArithmeticTest.m` around lines 118 - 124, Extend
testBmodAtBoundariesBuildsDisplay to inspect layout spacing for an interior
expression such as “17 \bmod 5”, asserting that \bmod uses the same
binary-operator spacing as a comparable “17 + 5” expression. Keep the existing
boundary no-throw assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@iosMathTests/MTModularArithmeticTest.m`:
- Around line 118-124: Extend testBmodAtBoundariesBuildsDisplay to inspect
layout spacing for an interior expression such as “17 \bmod 5”, asserting that
\bmod uses the same binary-operator spacing as a comparable “17 + 5” expression.
Keep the existing boundary no-throw assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d82f327-d2de-4409-959f-2c628e10f78f

📥 Commits

Reviewing files that changed from the base of the PR and between d25097f and c298171.

📒 Files selected for processing (2)
  • iosMath/lib/MTMathAtomFactory.m
  • iosMathTests/MTModularArithmeticTest.m

@kostub kostub left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review — PR 1 (\bmod as a binary operator)

Reviewed against docs/plans/2026-07-25-modular-arithmetic.md (PR 1, Tasks 1-2) and docs/lld/2026-07-13-modular-arithmetic.md. Verified locally on a detached worktree at c298171: swift test --filter MTModularArithmeticTest → 7/7 pass; full suite → 446 tests, 0 failures.

Strengths

  • The production change is exactly one symbol-table entry, and it is the right modeling. TeX defines \bmod as \mathbin{\operator@font mod}, so kMTMathAtomBinaryOperator with an ASCII nucleus is faithful — not an Op like \sin.
  • Multi-character nuclei already have precedent in the same +supportedLatexSymbols table (\log, \sin, \lim), so nothing new is being asked of the reverse map or the typesetter.
  • Boundary-demotion coverage is genuinely valuable, not box-ticking: (Open, Bin) is kMTSpaceInvalid and asserts at MTTypesetter.m:1093, so a \bmod at list start is a real crash surface. Good that Task 2 pins it.
  • The Un→Bin fallback in +latexSymbolNameForAtom: pre-dates this PR; testDemotedBmodSerializes locking it in for the new symbol is the right call.

I also probed some edge cases the tests don't cover — \left(\bmod b\right), \{\bmod b\}, \frac{\bmod b}{c}, a^{b \bmod c} — all build cleanly without tripping the assert. No action needed; noting so it isn't re-derived later.

Issues

1. (Important) The new test file is not registered in iosMath.xcodeproj/project.pbxproj.

The project is a classic pbxproj — zero fileSystemSynchronizedGroups — so files must be added explicitly. Every other tracked test file has 4 references:

4  MTColorDecoderTest.m      4  MTMathListBuilderTest.m
4  MTFontManagerTest.m       4  MTMathListTest.m
4  MTInkWidthTest.m          4  MTTypesetterTest.m
0  MTConcurrencyTest.m       0  MTModularArithmeticTest.m   <-- this PR

MTInkWidthTest.m is recent, so the convention is live; MTConcurrencyTest.m looks like a pre-existing oversight rather than a precedent. The consequence is that xcodebuild test -project iosMath.xcodeproj -scheme iosMath — the documented iOS test path in CLAUDE.md — silently skips all 7 tests, and will keep skipping the feature's tests through PR 2 and PR 3. The plan calls this "mechanical; not required for swift test", which is true but means it currently belongs to no PR.

MacOSMath.xcodeproj contains no Obj-C test files at all, so only the iOS project needs the change.

2. (Important) Nothing asserts the spacing — the one thing that makes \bmod a Bin instead of an Ord.

Tests cover type, nucleus, fontStyle, serialization, discoverability, uprightness, demotion, and no-crash. The rendered gap is untested. Measured locally at 20pt:

17 \bmod 5          width 76.669
17 \mathrm{mod} 5   width 67.780
delta 8.889 = 2 x 4mu   (muUnit = 20/18 = 1.111)

That 4mu-per-side result is precisely the value LLD §4.3 knowingly trades against LaTeX's 5mu, and it is what the PR goal claims ("renders upright with binary-operator spacing"). A width comparison against \mathrm{mod}, or an assertion on the sub-display x offsets, would pin the deviation and catch any future change to the binary row of getInterElementSpaces().

3. (Minor-to-Important) renderedTextForDisplay: fails silently on unhandled display classes.

It returns @"" for anything that is neither MTCTLineDisplay nor MTMathListDisplayMTFractionDisplay, MTRadicalDisplay, MTGlyphDisplay, MTLargeOpLimitsDisplay, MTAccentDisplay. Paired with the negative assertion in testBmodRendersUpright:

XCTAssertFalse([text containsString:@"\U0001D45A"], @"italic m in %@", text);

a traversal that dropped the subtree would pass vacuously. This helper is stack infrastructure — PR 3's \mod{n^2}^3 cases put content inside script displays — so it is worth hardening now: XCTFail on an unhandled MTDisplay subclass, or at minimum XCTAssertGreaterThan(text.length, 0) before any negative assertion.

4. (Minor) Comment attributes \bmod to amsmath.

The comment opens with "LaTeX kernel's binary mod" and then says the spacing "stands in for amsmath's hand-tuned 5mu". \bmod is defined in latex.ltx; \pmod/\mod/\pod are the amsmath ones. Suggest "LaTeX kernel's 5mu".

5. (Minor) The script-style deviation is understated in the comment.

"(4mu text/display, 0 in scripts) stands in for amsmath's hand-tuned 5mu" reads as if the only delta were 1mu. In LaTeX the \mkern5mu on either side of mod is not \nonscript-guarded — only the \mskip-\medmuskip is — so LaTeX keeps 5mu in script style where iosMath drops to 0, i.e. a^{b \bmod c} renders with no gaps at all. LLD §4.3 item 3 acknowledges this; the code comment is where a future reader will actually look, so it should say so too.

6. (Minor) Line-number citations in comments will rot.

The new comment and several test comments cite MTTypesetter.m:539-545, MTMathList.m:1709-1716, MTMathAtomFactory.m:190-239, MTMathAtomFactory.m:205-212. I checked them: the first two are accurate today; 205-212 is off by roughly two lines (the Un→Bin fallback sits at 204-209); 190-239 spans two unrelated methods. Naming the symbol (+preprocessMathList:, +addLatexSymbol:value:, +latexSymbolNameForAtom:) survives edits. Note the nearby pre-existing comment on +latexSymbolNameForAtom: already follows that style.

7. (Minor) No CHANGELOG entry anywhere in the stack.

grep -n "CHANGELOG" docs/plans/2026-07-25-modular-arithmetic.md returns nothing across all three PRs, yet every feature in v2.3.0 through v2.5.0 has one. Landing it in PR 3 is reasonable, but right now it is in no PR's plan.

Assessment

No blocking correctness defects — the symbol entry is right, the tests are real, and the suite is green. Issue 1 should be fixed before merge. Issues 2 and 3 are worth doing in this PR rather than later, since the test helper becomes shared infrastructure for PR 2 and PR 3. The rest are cleanups.

@kostub
kostub merged commit e278be3 into master Jul 25, 2026
2 checks passed
kostub added a commit that referenced this pull request Jul 27, 2026
This reverts commit 8d43971.

PR #265 added MTMacroAtom, the kMTMathAtomMacro type, a two-phase -finalized
(expandMacros, then the existing reclassifying pass), and
-transferScriptsToExpansion:, so that \pmod{n} would serialize back to
\pmod{n} rather than to its expansion. Nothing else uses any of it, and
\pmod/\mod/\pod themselves had not landed yet — so on master today this is
~460 lines of model-layer machinery with no caller.

Preserving the command name through serialization is not an acceptance
criterion for the feature (PRD §10 lists eight; none concern serialization).
It appears only as an open question for the LLD (§9.5), and PRD §11's own
reference table already describes \pmod's atom head as "Space + Open + Ord …"
— an atom sequence, i.e. the expansion. iosMath already declines the same
fidelity elsewhere: \implies serializes as \Longrightarrow.

\pmod, \mod and \pod land in #266 instead as a parse-time expansion, the way
TeX defines them. That also turns out to be the more faithful of the two: with
no macro atom to carry them, \pmod{n}^2 attaches ^2 to the ")" exactly as
LaTeX does, rather than transferring it onto the last scriptable atom of the
expansion.

MTMathList.h and MTMathList.m return to their state at e278be3. The \bmod
tests from #264 are unaffected. 446 tests, 0 failures.


Claude-Session: https://claude.ai/code/session_01Ts3f5UtUywaqimvE4U1rkw

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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