jamhash 0.1.3

Fast non-cryptographic hashing for canonical packed DNA kmers
Documentation
# `jamhash_u64`

`jamhash_u64` is the stable algorithm exposed as `jamhash::jamhash_u64`. It
accepts any `u64` and returns a non-cryptographic 64-bit hash.

## Formula

For 64-bit values `a` and `b`, define:

```text
fold(a, b) = low64(u128(a) * u128(b)) XOR high64(u128(a) * u128(b))
```

The complete algorithm is:

```text
C1 = 0xb8e1afed6a267e96
C2 = 0x082efa98ec4e6c89

p1 = rotate_left(input, 16)
p2 = rotate_left(input, 48)
f1 = fold(p1, C1)
f2 = fold(p2, C2)
output = fold(f1, f2)
```

There is no seed, byte conversion, final XOR, or output remapping.
`jamhash_u64(0) == 0` is intentional and stable.

## Test vectors

| Input | Output |
|---:|---:|
| `0x0000000000000000` | `0x0000000000000000` |
| `0x0000000000000001` | `0x2d673a90c3c14c02` |
| `0x0000006f93b4b1bc` | `0xaf47a96184814793` |
| `0xffffffffffffffff` | `0xffffffffffffffff` |

These vectors are release-gated by `tests/public_api.rs`.

## Packed DNA input

The primary input is a canonical DNA kmer packed into two bits per base. A
`u64` holds at most 32 bases. Encoding, canonicalization, orientation selection,
invalid-base handling, and clearing unused high bits are caller responsibilities.

The documented DNA convention uses `A=00`, `C=01`, `G=10`, `T=11`,
left-to-right packing, and `min(forward, reverse_complement)` canonicalization.
Current `jam-rs` screening paths exclude hash zero before applying their
FracMinHash threshold.

## Compatibility

Patch and minor releases preserve every `u64` input/output mapping. Changing a
constant, rotation, multiplication, folding step, zero behavior, or output
mapping is an explicit compatibility break.

`jamhash_bytes` and `JamHasher` are separate algorithms and are not covered by
this mapping promise. Their paths for four bytes and longer use native-endian
reads. Their outputs are therefore architecture-local and must not be used as
persistent or exchanged identifiers.

`JamHasher` hashes a sequence of non-empty write frames. Frame boundaries affect
the result, empty writes are no-ops, and one frame is not equivalent to
`jamhash_bytes`.