For reference, my links :
RealTime Data Compression Finite State Entropy - A new breed of entropy coder
Asymmetric Numeral System - Polar
arxiv [1311.2540] Asymmetric numeral systems entropy coding combining speed of Huffman coding with compression rate of arithmetic
encode.ru - Asymetric Numeral System
encode.ru - FSE
Large text benchmark - fpaqa ans
New entropy coding faster than Huffman, compression rate like arithmetic - Google Groups
I actually found Polar's page & code the easiest to follow, but it's also the least precise and the least optimized. Yann Collet's fse.c is very good but contains various optimizations that make it hard to jump into and understand exactly where those things came from. Yann's blog has some good exposition as well.
So let's back way up.
ANS adds a sequence of values into a single integer "state".
The most similar thing that we're surely all familiar with is the way that we pack integers together for IO
or network transmission. eg. when you have a value that can be in [0,2) and one in [0,6) and one in [0,11)
you have a range of 3*7*12 = 252 so you can fit those all in one byte, and you use packing like :
// encode : put val into state
void encode(int & state, int val, int mod)
ASSERT( val >= 0 && val < mod );
state = state*mod + val;
// decode : remove a value from state and return it
int decode(int & state, int mod )
int val = state % mod;
state /= mod;
Obviously at this point we're just packing integers, there's no entropy coding, we can't do unequal probabilities.
The key thing that we will keep using in ANS is in the decode - the current "state" has a whole sequence of values
in it, but we can extract our current value by doing a mod at the bottom.
That is, say "mod" = 3, then this decode function can be written as a transition table :
state next_state val
0 0 0
1 0 1
2 0 2
3 1 0
4 1 1
5 1 2
6 2 0
In the terminology of ANS we can describe this as "0120120120..." or just "012" and the repeating is implied.
That is, the bottom bits of "state" tell us the current symbol by looking up in that string, and then those
bottom bits are removed and we can decode more symbols.
Note that encode/decode is LIFO. The integer "state" is a stack - we're pushing values into the bottom and popping them from the bottom.
This simple encode/decode is also not streaming. That is, to put an unbounded number of values into state we would need an infinite length integer. We'll get back to this some day.