Algorithms
This page documents the checksum and structural validation algorithms implemented by Numerik. All validation is digit-array arithmetic — no regular expressions are used.
Weights: 1, 3, 7, 9, 1, 3, 7, 9, 1, 3
- Strip whitespace. Reject inputs longer than 32 characters. Assert exactly 11 digits; fail with
InvalidLengthotherwise. - Reject non-digit characters; fail with
InvalidCharacters. - Decode the birth date using the century encoding table below. Fail with
InvalidMonthif the stored month does not fall in any known range. Fail withInvalidDateif the decoded date is not a real calendar date. In strict mode, fail withFutureDateif the birth date is in the future. - Compute the checksum: multiply each of the first 10 digits by its corresponding weight and sum the results. Take
sum mod 10, subtract from10, takemod 10again. The result must equal digit 11; fail withInvalidChecksumotherwise. - In strict mode, reject inputs where all 11 digits are identical; fail with
AllSameDigit.
Century encoding
Section titled “Century encoding”The PESEL month digits encode both the birth month and the birth century by adding an offset:
| Stored month range | Real month | Birth century |
|---|---|---|
| 01 – 12 | 01 – 12 | 1900 – 1999 |
| 21 – 32 | 01 – 12 | 2000 – 2099 |
| 41 – 52 | 01 – 12 | 2100 – 2199 |
| 61 – 72 | 01 – 12 | 2200 – 2299 |
| 81 – 92 | 01 – 12 | 1800 – 1899 |
Worked example
Section titled “Worked example”Input: 9 2 0 6 0 5 1 2 1 8 6
| Position | Digit | Weight | Product |
|---|---|---|---|
| 1 | 9 | 1 | 9 |
| 2 | 2 | 3 | 6 |
| 3 | 0 | 7 | 0 |
| 4 | 6 | 9 | 54 |
| 5 | 0 | 1 | 0 |
| 6 | 5 | 3 | 15 |
| 7 | 1 | 7 | 7 |
| 8 | 2 | 9 | 18 |
| 9 | 1 | 1 | 1 |
| 10 | 8 | 3 | 24 |
Sum = 9 + 6 + 0 + 54 + 0 + 15 + 7 + 18 + 1 + 24 = 134
134 mod 10 = 4 → 10 - 4 = 6 → 6 mod 10 = 6 — digit 11 is 6 ✓
Weights: 6, 5, 7, 2, 3, 4, 5, 6, 7
- Strip hyphens and spaces. Reject inputs longer than 32 characters. Assert exactly 10 digits; fail with
InvalidLength. - Reject non-digit characters; fail with
InvalidCharacters. - Assert the first 3 digits are not
000; fail withInvalidFormat. - Multiply each of the first 9 digits by its weight. Sum the products. Take
sum mod 11. The result must equal digit 10; since a single digit can only be 0–9, a modulo result of10can never match and always fails withInvalidChecksum.
Worked example
Section titled “Worked example”Input: 5 2 6 0 2 5 0 2 7 4
| Position | Digit | Weight | Product |
|---|---|---|---|
| 1 | 5 | 6 | 30 |
| 2 | 2 | 5 | 10 |
| 3 | 6 | 7 | 42 |
| 4 | 0 | 2 | 0 |
| 5 | 2 | 3 | 6 |
| 6 | 5 | 4 | 20 |
| 7 | 0 | 5 | 0 |
| 8 | 2 | 6 | 12 |
| 9 | 7 | 7 | 49 |
Sum = 30 + 10 + 42 + 0 + 6 + 20 + 0 + 12 + 49 = 169
169 mod 11 = 4 — digit 10 is 4 ✓
REGON uses a two-stage algorithm. The 14-digit form first validates the base 9 digits before validating the full 14.
9-digit REGON
Section titled “9-digit REGON”Weights: 8, 9, 2, 3, 4, 5, 6, 7
- Strip whitespace. Assert exactly 9 digits.
- Multiply the first 8 digits by the 9-digit weights. Sum. Take
sum mod 11. If the result is10, the checksum digit must be0. Otherwise the result must equal digit 9.
14-digit REGON
Section titled “14-digit REGON”Weights: 2, 4, 8, 5, 0, 9, 7, 3, 6, 1, 2, 4, 8
- Strip whitespace. Assert exactly 14 digits.
- Validate digits 1–9 as a standalone 9-digit REGON (see above).
- Multiply the first 13 digits by the 14-digit weights. Sum. Take
sum mod 11. If the result is10, digit 14 must be0. Otherwise the result must equal digit 14.
KRS has no public checksum algorithm. It is a sequential registry number assigned by the court register. Numerik validates format and range only:
- Strip whitespace. Reject inputs longer than 32 characters. Assert ≤ 10 digits.
- Reject non-digit characters.
- Assert numeric value is greater than
0; fail withAllZeros. - In strict mode, zero-pad to 10 digits and reject inputs where all digits are identical; fail with
AllSameDigit.
For authoritative verification that a KRS number is actually assigned, query the official KRS registry API.
NRB uses the standard IBAN MOD-97 algorithm:
- Strip spaces and hyphens. Strip the optional
PLcountry prefix. Reject inputs over 40 characters. - Assert exactly 26 digits remain.
- Assert all characters are digits.
- Rearrange as: digits 3–26 (the BBAN) +
2521(numeric encoding ofPL) + digits 1–2 (check digits). - Compute the resulting number modulo 97. The result must equal
1.
VAT-EU
Section titled “VAT-EU”VAT-EU validation is a thin wrapper over NIP:
- Strip spaces and hyphens. Reject inputs over 32 characters.
- Assert the first two characters are
PL(case-insensitive); fail withInvalidFormat. - Strip the prefix. Assert exactly 10 digits remain.
- Apply the full NIP algorithm to the extracted digits.
IBAN validation is a thin wrapper over NRB:
- Strip spaces and hyphens. Reject inputs over 40 characters.
- Assert the first two characters are
PL(case-insensitive); fail withInvalidFormat. - Strip the prefix. Assert exactly 26 digits remain.
- Apply the full NRB MOD-97 algorithm to the extracted digits.
ICAO 9303
Section titled “ICAO 9303”Used by both ID Card and Passport — the same international standard applied to travel document numbers.
Weights: 7, 3, 1 repeating over positions 1–8 (8 input characters). Position 9 is the check digit.
Character values:
- Digits
0–9→ face value (0–9) - Letters
A–Z→10–35(A=10,B=11, …Z=35)
- Strip spaces and hyphens. Convert to uppercase. Reject inputs over 32 characters.
- Assert exactly 9 characters; fail with
InvalidLength. - Validate character types per identifier (see individual pages).
- Multiply each of the first 8 characters by its weight and sum the products.
- Compute
sum mod 10. The result must equal the 9th character; fail withInvalidChecksum.
Calculation example
Section titled “Calculation example”Number: A B C 1 2 3 4 5 4 (ID card)
| Position | Char | Value | Weight | Product |
|---|---|---|---|---|
| 1 | A | 10 | 7 | 70 |
| 2 | B | 11 | 3 | 33 |
| 3 | C | 12 | 1 | 12 |
| 4 | 1 | 1 | 7 | 7 |
| 5 | 2 | 2 | 3 | 6 |
| 6 | 3 | 3 | 1 | 3 |
| 7 | 4 | 4 | 7 | 28 |
| 8 | 5 | 5 | 3 | 15 |
Sum = 70 + 33 + 12 + 7 + 6 + 3 + 28 + 15 = 174
174 mod 10 = 4 — position 9 is 4 ✓