# validation.uk-modulus-table
Reads the two files UK sort code modulus checking runs on, as Vocalink
publishes them for Pay.UK, into the table that
`validation.uk-sort-code-account` checks against:
- **VALACDOS.txt**, the modulus weight table: one line per sort code range,
giving the check to run (MOD10, MOD11 or DBLAL), fourteen weights and an
optional exception number.
- **SCSUBTAB.txt**, the sort code substitution table for exception 5: one line
per sort code, with the sort code to weight in its place.
**Neither file is in this package.** Both are Vocalink's copyright ("All
rights reserved"), so the registry ships the algorithm and the parser, and you
supply the data. Download both from Vocalink's modulus checking page,
(Vocalink runs it for
Pay.UK), and pass their text in. Vocalink updates VALACDOS.txt several times a
year as banks add and retire sort codes, and the page lists the date of each
release: fetch the new file when it changes, as you would any reference data.
A table that is out of date answers "unchecked" for a new sort code rather
than rejecting it, so a stale file fails safe, but it does stop checking.
## Usage
Read the files, parse them once when your application starts, and pass the
table to every check. Parsing the full file takes a few milliseconds; do not
do it per request.
TypeScript:
```ts
import { readFileSync } from "node:fs";
import { parseUkModulusTable } from "#fune/validation.uk-modulus-table@^1";
import { validateUkSortCodeAccount } from "#fune/validation.uk-sort-code-account@^3";
const table = parseUkModulusTable(
readFileSync("data/valacdos.txt", "utf8"),
readFileSync("data/scsubtab.txt", "utf8"),
);
validateUkSortCodeAccount("08-99-99", "66374958", table);
```
Python:
```python
from pathlib import Path
from fune.validation.uk_modulus_table import parse_uk_modulus_table # validation.uk-modulus-table@^1
from fune.validation.uk_sort_code_account import validate_uk_sort_code_account # validation.uk-sort-code-account@^3
table = parse_uk_modulus_table(
Path("data/valacdos.txt").read_text(encoding="utf-8"),
Path("data/scsubtab.txt").read_text(encoding="utf-8"),
)
validate_uk_sort_code_account("08-99-99", "66374958", table)
```
Rust:
```rust
fune!(validation.uk-modulus-table@^1);
fune!(validation.uk-sort-code-account@^3);
let table = parse_uk_modulus_table(
&std::fs::read_to_string("data/valacdos.txt")?,
&std::fs::read_to_string("data/scsubtab.txt")?,
);
validate_uk_sort_code_account("08-99-99", "66374958", &table);
```
## The format it reads
Each non-blank line of VALACDOS.txt is 17 or 18 fields separated by spaces
(Vocalink pads the columns; any run of spaces or tabs is one separator):
```
start end algorithm u v w x y z a b c d e f g h [exception]
```
`start` and `end` are six-digit sort codes, inclusive. The fourteen weights
are whole numbers, and may be negative in a MOD10 or MOD11 row. The exception
is 1 to 14. A sort code that falls in two rows gets two checks, the first row
first, so **rows keep their file order**; do not sort or de-duplicate them.
Each non-blank line of SCSUBTAB.txt is two six-digit sort codes: the original
and its substitute. Pass `""` if you have no SCSUBTAB.txt; exception 5 rows
then weight every sort code as itself, which is only right for sort codes the
substitution table does not list.
Windows line endings and a leading byte order mark are accepted.
## Errors
Anything else is an error naming the file and the line (blank lines count, so
the number matches your editor's): a wrong number of fields, a sort code that
is not six ASCII digits, a range that ends before it starts, an unknown
algorithm (they are upper case), a weight that is not a whole number of at
most four digits, a negative weight in a DBLAL row (a digit sum of a negative
product is not defined, and the specification never needs one), an exception
outside 1 to 14, or a VALACDOS.txt with no rows at all. A parser that skipped
a bad line would quietly turn a checkable sort code into an unchecked one.
## Tests
The vectors use short invented lines in VALACDOS.txt's layout, not rows of
the real file: invented sort code ranges, weights and exceptions.
## Sources
- Vocalink, "Validating account numbers: UK Modulus Checking", section 2 (the
file layouts and the meaning of each column), and the modulus checking
page that links the current files: