fix: ISBN-10 Generate outer mod-11 and restrict X to check-digit position - #35
Open
binggao1230 wants to merge 1 commit into
Open
fix: ISBN-10 Generate outer mod-11 and restrict X to check-digit position#35binggao1230 wants to merge 1 commit into
binggao1230 wants to merge 1 commit into
Conversation
…tion
Two bugs in the ISBN-10 implementation (isbn.go):
1. Generate: `return 11 - sum%11, nil` missed the outer `% 11` required by
the ISO 2108 check-digit formula `(11 - (sum mod 11)) mod 11`. When the
weighted sum is divisible by 11, Generate returned 11 instead of 0 — an
out-of-range value (valid range is 0-10) that breaks the Generator
contract and contradicts the library's own Verify, which accepts
Verify("0000000000")=true (correct check digit 0). Fixed by applying the
outer `% 11`: `(11 - sum%11) % 11`. Only the sum%11==0 boundary changes
(11 -> 0); all other check digits 1-10 are unchanged.
2. Verify: the `case n == 'X': digit = 10` branch accepted 'X' (value 10)
in any position. Per ISO 2108, 'X' is valid only as the check digit (the
final character). Codes such as "X026515627" (X at position 0) and
"0X00000009" (X at position 1) were falsely accepted. Fixed by rejecting
'X' in any non-final position. Valid ISBNs with 'X' in the last position
(e.g. "000000006X") remain accepted.
Adds isbn_edge_test.go with regression tests for both bugs (Generate
sum%11==0 cases, non-zero regression guards, Verify X-in-wrong-position
false-accepts, valid-X-last controls, and a Generate->Verify round-trip
property over seeds including the sum%11==0 boundary).
`go test ./...`, `go vet ./...`, and `gofmt -l .` all pass clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes two bugs in the ISBN-10 implementation (
isbn.go) and adds regression tests. Both bugs are latent — the existing test suite passes without covering them.Internal-consistency smoking gun (no external oracle needed)
The library disagrees with itself on the
sum % 11 == 0boundary:Verifyconfirms the correct check digit for seed"000000000"is0, butGeneratereturns11— a value outside the valid check-digit range[0, 10]and not representable as a single character.GenerateandVerifycontradict each other within the same library, and theGenerate → Verifyround-trip cannot even be formed for this seed.Bug 1 —
Generatemissing the outer% 11(ISBN-10 check-digit formula)Per ISO 2108, the ISBN-10 check digit is:
isbn.gocomputed11 - sum%11without the outermod 11. When the weighted sum is divisible by 11, this yields11instead of0:"000000000""055555555"The fix applies the outer
% 11:Only the
sum%11==0boundary changes (11 → 0); every other check digit (1–10) is unchanged. Regression guards:Generate("000000001")=9,Generate("000000006")=10(theXcase),Generate("123456789")=10all remain correct.Bug 2 —
VerifyacceptsXin any positionVerifymappedn == 'X'→digit = 10for any position. Per ISO 2108,X(value 10) is valid only as the check digit (the final character). Codes withXin a non-final position were falsely accepted when the weighted sum happened to be divisible by 11:Xposition"X026515627"truefalse"0X00000009"truefalseThe fix rejects
Xin any non-final position. Valid ISBNs withXin the last position remain accepted (e.g.Verify("000000006X")=true).Fix
isbn.go(minimal, style-matching):Generate:return 11 - sum%11, nil→return (11 - sum%11) % 11, nil(+ ISO 2108 comment).Verify: track the loop index and rejectXin any non-final position (+ spec comment:Xis valid only as the check digit).The weighted-sum verification logic is otherwise untouched.
Tests
Adds
isbn_edge_test.go(follows the existingisbn_test.gotable-driven style):Generatesum%11==0cases (000000000→0,055555555→0) + range check.Generatenon-zero regression guards (000000001→9,000000006→10,123456789→10).VerifyX-in-wrong-position false-accepts (X026515627→false,0X00000009→false).Verifyvalid-X-last + controls (000000006X→true,0000000000→true,0000000001→false).Generate → Verifyround-trip property over seeds including thesum%11==0boundary.Verification
go test ./...— PASS (existing + new tests).go vet ./...— clean.gofmt -l .— clean.5504ad35). Post-fix: all RED cases resolved, all controls unchanged, round-trip holds.