# parquet.alp

> A module of parquet's API.

ALP — Adaptive Lossless floating-Point, Parquet encoding 10.

From "ALP: Adaptive Lossless floating-Point Compression" (Afroozeh, Kuffo and
Boncz, SIGMOD 2024), specified for Parquet in `AlpEncoding.md`.

The idea is that most floating-point data in the wild is decimal in origin —
prices, sensor readings, coordinates — so `v * 10^e / 10^f` lands exactly on an
integer for some small `e` and `f`. Those integers are then frame-of-reference
encoded and bit-packed, which is far denser than the original mantissas. Values
that do not survive the round trip are stored verbatim as exceptions, so the
encoding stays lossless whatever the input.

A page is a 7-byte header, an offset array, then independent vectors:

```text
compression_mode : u8      0 = ALP
integer_encoding : u8      0 = frame of reference + bit-packing
log_vector_size  : u8      log2 of the values per vector, 3..15
num_elements     : i32 LE  values in the page
offsets          : u32 LE * num_vectors, from the start of the offset array
```

and each vector is

```text
exponent         : u8      e
factor           : u8      f, <= e
num_exceptions   : u16 LE
frame_of_ref     : i32/i64 LE   4 bytes for FLOAT, 8 for DOUBLE
bit_width        : u8
packed           : ceil(n * bit_width / 8) bytes, LSB-first
exc_positions    : u16 LE * num_exceptions
exc_values       : the original IEEE 754 values
```

Decoding one value is `(delta + frame_of_reference) * 10^f / 10^e`, then the
exceptions are written over the positions they name.

## Functions

### `decode_alp`

```mojo
def decode_alp[dt: DType](data: Span[UInt8], count: Int) -> PhysBuffer
```

Decode an ALP page of `count` FLOAT (`dt=float32`) or DOUBLE values.
