#![allow(clippy::unwrap_used, clippy::expect_used)]
use chronix_encoding::{
ColumnDecoder, ColumnEncoder, DecodedColumn, DeltaOfDeltaDecoder, DeltaOfDeltaEncoder,
EncodingType, PcoDecoder, PcoEncoder,
};
const N: usize = 8192;
const SECOND: i64 = 1_000_000_000;
const BASE: i64 = 1_700_000_000_000_000_000;
fn lcg(seed: &mut u64) -> u64 {
*seed = seed
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
*seed >> 33
}
fn workloads() -> Vec<(&'static str, Vec<i64>)> {
let mut seed = 0x5eed_u64;
let regular: Vec<i64> = (0..N as i64).map(|i| BASE + i * SECOND).collect();
let jitter: Vec<i64> = (0..N as i64)
.map(|i| {
let j = (lcg(&mut seed) % 10_000_001) as i64 - 5_000_000;
BASE + i * SECOND + j
})
.collect();
let mut gappy = Vec::with_capacity(N);
let mut t = BASE;
for _ in 0..N {
gappy.push(t);
t += SECOND;
if lcg(&mut seed).is_multiple_of(100) {
t += SECOND * (1 + (lcg(&mut seed) % 5) as i64);
}
}
let buckets: Vec<i64> = (0..N as i64).map(|i| BASE + i * 15 * 60 * SECOND).collect();
vec![
("regular 1 s", regular),
("1 s ± 5 ms jitter", jitter),
("1 s with 1% gaps", gappy),
("15-min buckets", buckets),
]
}
fn ratio(raw: usize, encoded: usize) -> f64 {
raw as f64 / encoded as f64
}
#[test]
fn delta_of_delta_versus_pco_on_timestamps() {
println!(
"\n{:<20} {:>8} {:>8} {:>8}",
"workload", "DoD", "pco", "unified"
);
let mut results = Vec::new();
for (name, values) in workloads() {
let raw = values.len() * 8;
let dod = DeltaOfDeltaEncoder::encode(&values).unwrap();
assert_eq!(DeltaOfDeltaDecoder::decode(&dod).unwrap(), values);
let pco = PcoEncoder::encode_timestamps(&values).unwrap();
assert_eq!(PcoDecoder::decode_i64(&pco).unwrap(), values);
let block = ColumnEncoder::encode_timestamps(&values).unwrap();
match ColumnDecoder::decode(&block).unwrap() {
DecodedColumn::I64(back) => assert_eq!(back, values),
other => panic!("timestamps decoded to {other:?}"),
}
println!(
"{:<20} {:>7.1}x {:>7.1}x {:>7.1}x ({})",
name,
ratio(raw, dod.len()),
ratio(raw, pco.len()),
ratio(raw, block.payload.len() + 1),
block.encoding,
);
results.push((name, ratio(raw, dod.len()), ratio(raw, pco.len()), block));
}
for (name, dod, pco, block) in &results {
match *name {
"regular 1 s" | "15-min buckets" => assert!(
*pco >= *dod * 0.8,
"{name}: pco {pco:.1}x lost more than 20% to DoD {dod:.1}x"
),
_ => assert!(
*pco >= *dod * 2.0,
"{name}: pco {pco:.1}x is no longer well ahead of DoD {dod:.1}x"
),
}
let best = dod.max(*pco);
let unified = ratio(N * 8, block.payload.len() + 1);
assert!(
unified >= best * 0.95,
"{name}: unified {unified:.1}x ({}) is behind the better codec {best:.1}x",
block.encoding
);
if *name == "1 s ± 5 ms jitter" {
assert_eq!(
block.encoding,
EncodingType::PcoI64,
"jittered timestamps must be pco-encoded by default"
);
}
}
}
#[test]
fn timestamp_headline_ratios() {
const JITTER_STATES: f64 = 10_000_001.0;
let (_, jitter) = workloads().swap_remove(1);
let raw = jitter.len() * 8;
let dod = DeltaOfDeltaEncoder::encode(&jitter).unwrap().len();
let pco = PcoEncoder::encode_timestamps(&jitter).unwrap().len();
let bound_bytes_per_value = JITTER_STATES.log2() / 8.0;
let bound_ratio = 8.0 / bound_bytes_per_value;
println!(
"jittered 1 s: DoD {:.1}x, pco {:.1}x (Shannon bound {:.2}x)",
ratio(raw, dod),
ratio(raw, pco),
bound_ratio
);
assert!(
ratio(raw, dod) < 1.0,
"DoD on jittered timestamps is no longer expanding ({:.1}x) — recheck the default",
ratio(raw, dod)
);
assert!(
ratio(raw, pco) >= bound_ratio * 0.95,
"pco on jittered 1 s fell to {:.2}x, more than 5% off the {bound_ratio:.2}x bound",
ratio(raw, pco)
);
}
#[test]
fn pco_delta_spec_comparison() {
use pco::standalone::simple_compress;
use pco::{ChunkConfig, DeltaSpec};
for (name, values) in workloads() {
let raw = values.len() * 8;
let auto = simple_compress(&values, &ChunkConfig::default())
.unwrap()
.len();
let c1 = simple_compress(
&values,
&ChunkConfig::default().with_delta_spec(DeltaSpec::TryConsecutive(1)),
)
.unwrap()
.len();
let c2 = simple_compress(
&values,
&ChunkConfig::default().with_delta_spec(DeltaSpec::TryConsecutive(2)),
)
.unwrap()
.len();
let l = simple_compress(
&values,
&ChunkConfig::default().with_delta_spec(DeltaSpec::TryLookback),
)
.unwrap()
.len();
let lvl12 = simple_compress(&values, &ChunkConfig::default().with_compression_level(12))
.unwrap()
.len();
println!(
"{name:<20} auto {:.1}x c1 {:.1}x c2 {:.1}x lookback {:.1}x auto@12 {:.1}x",
ratio(raw, auto),
ratio(raw, c1),
ratio(raw, c2),
ratio(raw, l),
ratio(raw, lvl12)
);
}
}