Interactive Verhoeff calculator
Two answers appear: whether the number as typed passes the check, and what check digit would be appended to it. Computed locally.
What the Verhoeff algorithm is
The Verhoeff algorithm is a check digit scheme: given a number, it computes one extra digit that is appended to the end. Later, anyone who receives the full number can run the same calculation and confirm the check digit still matches. If a digit was mistyped or two neighbouring digits were swapped, the check fails.
It was published in 1969 by the Dutch mathematician Jacobus Verhoeff, who studied real transcription errors made by Dutch postal workers and designed a scheme that catches the ones people actually make. It was the first decimal check digit algorithm to detect all single-digit errors and all adjacent transpositions using just one check digit.
In India, the Unique Identification Authority of India (UIDAI) uses Verhoeff for the 12th digit of every Aadhaar number and the 16th digit of every Aadhaar Virtual ID.
Why UIDAI chose Verhoeff over Luhn
The better-known Luhn algorithm (used by credit cards) is simpler but weaker. The table below compares the error types each scheme catches. Verhoeff's strength comes from using a non-commutative operation — the order in which digits are combined matters, which is exactly what you need to detect swapped digits.
| Error type | Example | Luhn | Verhoeff | Damm |
|---|---|---|---|---|
| Single digit wrong | 5 → 6 | 100% | 100% | 100% |
| Adjacent digits swapped | 12 → 21 | Misses 09 ↔ 90 | 100% | 100% |
| Twin errors | 11 → 22 | Misses some | ~95% | ~95% |
| Jump transposition | 123 → 321 | No | ~94% | ~94% |
| Phonetic (1x ↔ x0) | 13 → 30 | No | ~95% | ~95% |
Percentages are the approximate share of errors of that type that are detected.
The three tables
Verhoeff works with three lookup tables. You never need to understand the group theory to implement it — you just index into these tables.
Multiplication table d (dihedral group D5)
Combines the running value with the next digit. Notice it is not symmetric: d[1][2] is 3 but d[2][1] is 3 too, whereas d[1][5] is 6 and d[5][1] is 9. That asymmetry is what detects swaps.
| d | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 1 | 1 | 2 | 3 | 4 | 0 | 6 | 7 | 8 | 9 | 5 |
| 2 | 2 | 3 | 4 | 0 | 1 | 7 | 8 | 9 | 5 | 6 |
| 3 | 3 | 4 | 0 | 1 | 2 | 8 | 9 | 5 | 6 | 7 |
| 4 | 4 | 0 | 1 | 2 | 3 | 9 | 5 | 6 | 7 | 8 |
| 5 | 5 | 9 | 8 | 7 | 6 | 0 | 4 | 3 | 2 | 1 |
| 6 | 6 | 5 | 9 | 8 | 7 | 1 | 0 | 4 | 3 | 2 |
| 7 | 7 | 6 | 5 | 9 | 8 | 2 | 1 | 0 | 4 | 3 |
| 8 | 8 | 7 | 6 | 5 | 9 | 3 | 2 | 1 | 0 | 4 |
| 9 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Permutation table p
Each digit is first permuted according to its position (row = position mod 8). This makes the same digit contribute differently depending on where it appears.
| p | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| pos 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| pos 1 | 1 | 5 | 7 | 6 | 2 | 8 | 3 | 0 | 9 | 4 |
| pos 2 | 5 | 8 | 0 | 3 | 7 | 9 | 6 | 1 | 4 | 2 |
| pos 3 | 8 | 9 | 1 | 6 | 0 | 4 | 3 | 5 | 2 | 7 |
| pos 4 | 9 | 4 | 5 | 3 | 1 | 2 | 6 | 8 | 7 | 0 |
| pos 5 | 4 | 2 | 8 | 6 | 5 | 7 | 3 | 9 | 0 | 1 |
| pos 6 | 2 | 7 | 9 | 3 | 8 | 0 | 6 | 4 | 1 | 5 |
| pos 7 | 7 | 0 | 4 | 6 | 9 | 1 | 3 | 2 | 5 | 8 |
Inverse table inv
Only needed when generating a check digit, to find the digit that brings the running value back to 0.
| c | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| inv[c] | 0 | 4 | 3 | 2 | 1 | 5 | 6 | 7 | 8 | 9 |
The algorithm in pseudocode
function verhoeffValid(number):
c = 0
digits = reverse(number) // process right to left
for i from 0 to length(digits) - 1:
c = d[c][ p[i mod 8][ digits[i] ] ]
return c == 0
function verhoeffCheckDigit(numberWithoutCheckDigit):
c = 0
digits = reverse(numberWithoutCheckDigit)
for i from 0 to length(digits) - 1:
c = d[c][ p[(i + 1) mod 8][ digits[i] ] ]
return inv[c]
Validation processes the digits from right to left, starting with the check digit at position 0. Generation is identical except the positions are shifted by one (because the check digit does not exist yet) and the final value is looked up in inv.
Worked example: 9999 4105 7058
This is one of the test Aadhaar numbers UIDAI publishes for its developer sandbox, so it is safe to use as an example. Reverse the digits and walk through them:
| i | Digit | i mod 8 | Permute | Multiply |
|---|---|---|---|---|
| 0 | 8 | 0 | p[0][8] = 8 | d[0][8] = 8 |
| 1 | 5 | 1 | p[1][5] = 8 | d[8][8] = 0 |
| 2 | 0 | 2 | p[2][0] = 5 | d[0][5] = 5 |
| 3 | 7 | 3 | p[3][7] = 5 | d[5][5] = 0 |
| 4 | 5 | 4 | p[4][5] = 2 | d[0][2] = 2 |
| 5 | 0 | 5 | p[5][0] = 4 | d[2][4] = 1 |
| 6 | 1 | 6 | p[6][1] = 7 | d[1][7] = 8 |
| 7 | 4 | 7 | p[7][4] = 9 | d[8][9] = 4 |
| 8 | 9 | 0 | p[0][9] = 9 | d[4][9] = 8 |
| 9 | 9 | 1 | p[1][9] = 4 | d[8][4] = 9 |
| 10 | 9 | 2 | p[2][9] = 2 | d[9][2] = 7 |
| 11 | 9 | 3 | p[3][9] = 7 | d[7][7] = 0 |
The final value is 0, so the number is valid. Change any one digit and the final value will no longer be 0 — try it in the calculator above.
Common implementation mistakes
- Processing left to right. The position index must count from the rightmost digit.
- Using
p[i mod 8]for generation. When computing a check digit, usep[(i + 1) mod 8]because the digits are shifted by the missing check digit. - Forgetting the first-digit rule. Verhoeff is only one of the Aadhaar rules; numbers starting with 0 or 1 are invalid regardless of checksum.
- Validating with spaces in the string. Strip everything that is not a digit first.
Ready-to-use implementations are on the developer page.
Frequently asked questions
What is the Verhoeff algorithm?
The Verhoeff algorithm is a checksum formula published by Jacobus Verhoeff in 1969. It appends one check digit to a decimal number so that any single wrong digit and any swap of two adjacent digits can be detected. It is the check digit used in every Aadhaar number.
Why does Aadhaar use Verhoeff instead of Luhn?
The Luhn algorithm (credit cards) misses one adjacent transposition (09 and 90) and several twin errors. Verhoeff, built on the non-commutative dihedral group D5, detects all single-digit errors and all adjacent transpositions, which matters when 12-digit numbers are typed by hand across thousands of systems.
Which digit of an Aadhaar number is the check digit?
The last (12th) digit. The first 11 digits are random and the 12th is computed from them with the Verhoeff algorithm.
Does the Verhoeff algorithm prove an Aadhaar number is real?
No. It only proves the number is internally consistent. Roughly one in ten random 12-digit strings passes the checksum, so a valid checksum is necessary but not sufficient. Only UIDAI can confirm a number is issued.