use crate::abac::{BitModel, Decoder, Encoder, UintModel};
use crate::wavelet::{Band, Pyramid};
pub const KB: usize = 14;
pub const CAP: u16 = 65535;
pub const NCLASS: usize = 10;
pub trait Bits {
fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool;
fn bitp(&mut self, bit: bool, p1: u32) -> bool;
fn uint(&mut self, v: u64, m: &mut UintModel) -> u64;
#[inline]
fn tag(&mut self, _t: usize) {}
}
#[derive(Default, Debug)]
pub struct Meter {
pub bits: [f64; 8],
cur: usize,
}
impl Bits for Meter {
fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool {
self.bits[self.cur] += crate::abac::cost_bits(bit, m.p1());
m.update(bit);
bit
}
fn bitp(&mut self, bit: bool, p1: u32) -> bool {
self.bits[self.cur] += crate::abac::cost_bits(bit, p1);
bit
}
fn uint(&mut self, v: u64, m: &mut UintModel) -> u64 {
let mut e = Encoder::new();
m.encode(&mut e, v);
self.bits[self.cur] += 8.0 * e.finish().len() as f64 - 32.0;
v
}
fn tag(&mut self, t: usize) {
self.cur = t;
}
}
impl Bits for Encoder {
#[inline]
fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool {
self.encode(bit, m);
bit
}
#[inline]
fn bitp(&mut self, bit: bool, p1: u32) -> bool {
self.encode_p(bit, p1);
bit
}
fn uint(&mut self, v: u64, m: &mut UintModel) -> u64 {
m.encode(self, v);
v
}
}
impl Bits for Decoder<'_> {
#[inline]
fn bit(&mut self, _: bool, m: &mut BitModel) -> bool {
self.decode(m)
}
#[inline]
fn bitp(&mut self, _: bool, p1: u32) -> bool {
self.decode_p(p1)
}
fn uint(&mut self, _: u64, m: &mut UintModel) -> u64 {
m.decode(self)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Feat {
pub nb: [u16; 8],
pub sg: [i8; 3],
pub par: u16,
pub pnb: u16,
pub class: u8,
pub mask: u8,
pub has_par: bool,
pub pm: u16,
pub sv: [i32; NSV],
pub pv: i32,
}
pub const NSV: usize = 18;
pub fn band_class(b: &Band) -> u8 {
if b.mask == 0 {
return (NCLASS - 1) as u8;
}
let nh = b.mask.count_ones() as usize;
(b.level.min(2) * 3 + nh - 1) as u8
}
pub trait CoefModel {
fn code<B: Bits>(&mut self, b: &mut B, f: &Feat, v: i32) -> i32;
}
fn bucket(a: u32, th: &[u32]) -> usize {
th.iter().position(|&t| a <= t).unwrap_or(th.len())
}
const A_TH: [u32; 11] = [0, 1, 2, 3, 5, 7, 10, 14, 20, 30, 48];
const NA: usize = A_TH.len() + 1;
const P_TH: [u32; 4] = [0, 2, 6, 16];
const NP: usize = P_TH.len() + 1;
pub const NE: usize = 18;
#[inline]
pub fn mag_scale(f: &Feat) -> usize {
let n = f.nb.map(|v| v as u64);
let mut s = 2 * (n[0] + n[1] + n[2]) + n[3] + n[4] + n[5] + n[6];
let mut w = 10;
s += 2 * f.pm as u64;
if f.has_par {
s += 2 * f.par as u64;
w += 2;
}
let v = 1 + 2 * s / w;
(v.ilog2() as usize).min(NE - 1)
}
#[derive(Clone, Debug)]
pub struct HandModel {
sig: Vec<BitModel>,
mag: Vec<BitModel>,
low: Vec<BitModel>,
esc: Vec<UintModel>,
sign: Vec<BitModel>,
}
impl Default for HandModel {
fn default() -> Self {
Self {
sig: vec![BitModel::default(); NCLASS * NA * NP],
mag: vec![BitModel::default(); NCLASS * NE * KB],
low: vec![BitModel::default(); NE * 4],
esc: vec![UintModel::default(); NCLASS],
sign: vec![BitModel::default(); NCLASS * 27],
}
}
}
#[inline]
pub fn activity(f: &Feat) -> u32 {
let n = f.nb.map(|v| v.min(15) as u32);
2 * (n[0] + n[1] + n[2]) + n[3] + n[4] + n[5] + n[6] + n[7]
}
impl CoefModel for HandModel {
fn code<B: Bits>(&mut self, b: &mut B, f: &Feat, v: i32) -> i32 {
let c = f.class as usize;
let a = activity(f) + f.pm.min(15) as u32;
let pa = 4 * f.par.min(15) as u32 + f.pnb.min(60) as u32;
let ab = bucket(a, &A_TH);
let pb = if f.has_par {
1 + bucket(pa, &P_TH[..NP - 2])
} else {
0
};
let mag = v.unsigned_abs();
b.tag(0);
if !b.bit(mag > 0, &mut self.sig[(c * NA + ab) * NP + pb]) {
return 0;
}
b.tag(1);
let e = mag_scale(f);
let k = e.saturating_sub(2) as u32;
let r = mag.wrapping_sub(1);
let hi = r >> k;
let mut h = 0u32;
while (h as usize) < KB {
if !b.bit(hi > h, &mut self.mag[(c * NE + e) * KB + h as usize]) {
break;
}
h += 1;
}
if h as usize == KB {
h += b.uint(hi.wrapping_sub(h) as u64, &mut self.esc[c]) as u32;
}
let mut lo = 0u32;
b.tag(2);
for j in (0..k).rev() {
let bit = (r >> j) & 1 == 1;
let pos = (k - 1 - j) as usize;
let got = if pos < 2 {
b.bit(
bit,
&mut self.low[(e * 2 + pos) * 2 + (pos == 1 && lo & 1 == 1) as usize],
)
} else {
b.bitp(bit, crate::abac::ONE / 2)
};
lo = (lo << 1) | got as u32;
}
let m = 1 + ((h << k) | lo);
b.tag(3);
let sctx = c * 27 + ((f.sg[0] + 1) * 9 + (f.sg[1] + 1) * 3 + (f.sg[2] + 1)) as usize;
let neg = b.bit(v < 0, &mut self.sign[sctx]);
if neg {
-(m as i32)
} else {
m as i32
}
}
}
#[inline]
pub fn features(
p: &Pyramid,
bi: usize,
pbi: Option<usize>,
q: &[i32],
z: usize,
y: usize,
x: usize,
) -> Feat {
let b = &p.bands[bi];
let [_, ny, nx] = p.dims;
let at = |z: usize, y: usize, x: usize| q[(z * ny + y) * nx + x];
let mag = |v: i32| (v.unsigned_abs().min(CAP as u32)) as u16;
let sgn = |v: i32| v.signum() as i8;
let (z0, y0, x0) = (b.start[0], b.start[1], b.start[2]);
let x1 = b.end[2];
let mut f = Feat {
class: band_class(b),
mask: b.mask,
..Default::default()
};
if x > x0 {
let w = at(z, y, x - 1);
{
let t = w;
f.nb[0] = mag(t);
f.sv[0] = t;
}
f.sg[0] = sgn(w);
if x > x0 + 1 {
{
let t = at(z, y, x - 2);
f.nb[7] = mag(t);
f.sv[7] = t;
}
}
}
if y > y0 + 1 {
f.sv[9] = at(z, y - 2, x);
}
if z > z0 + 1 {
f.sv[10] = at(z - 2, y, x);
}
if z > z0 && x + 1 < x1 {
f.sv[11] = at(z - 1, y, x + 1);
}
if y > y0 {
let n = at(z, y - 1, x);
{
let t = n;
f.nb[1] = mag(t);
f.sv[1] = t;
}
f.sg[1] = sgn(n);
if x > x0 {
{
let t = at(z, y - 1, x - 1);
f.nb[3] = mag(t);
f.sv[3] = t;
}
}
if x + 1 < x1 {
{
let t = at(z, y - 1, x + 1);
f.nb[4] = mag(t);
f.sv[4] = t;
}
}
}
if z > z0 {
let u = at(z - 1, y, x);
{
let t = u;
f.nb[2] = mag(t);
f.sv[2] = t;
}
f.sg[2] = sgn(u);
if x > x0 {
{
let t = at(z - 1, y, x - 1);
f.nb[5] = mag(t);
f.sv[5] = t;
}
}
if y > y0 {
{
let t = at(z - 1, y - 1, x);
f.nb[6] = mag(t);
f.sv[6] = t;
}
}
}
if let Some(pi) = pbi {
let pb = &p.bands[pi];
let pp = p.parent_pos(b, pb, [z, y, x]);
f.has_par = true;
{
let t = at(pp[0], pp[1], pp[2]);
f.par = mag(t);
f.sv[8] = t;
}
let mut s = 0u32;
for a in 0..3 {
let mut lo = pp;
if lo[a] > pb.start[a] {
lo[a] -= 1;
let t = at(lo[0], lo[1], lo[2]);
f.sv[12 + 2 * a] = t;
s += t.unsigned_abs().min(CAP as u32);
}
let mut hi = pp;
if hi[a] + 1 < pb.end[a] {
hi[a] += 1;
let t = at(hi[0], hi[1], hi[2]);
f.sv[13 + 2 * a] = t;
s += t.unsigned_abs().min(CAP as u32);
}
}
f.pnb = s.min(CAP as u32) as u16;
}
f
}
pub const NTAP: usize = 7;
pub const PSHIFT: u32 = 12;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Pred {
pub on: bool,
pub w: [i16; NTAP],
}
#[inline]
fn taps(
p: &Pyramid,
bi: usize,
pbi: Option<usize>,
q: &[i32],
z: usize,
y: usize,
x: usize,
) -> [i32; NTAP] {
let b = &p.bands[bi];
let [_, ny, nx] = p.dims;
let at = |z: usize, y: usize, x: usize| q[(z * ny + y) * nx + x];
let (zi, yi, xi) = (z > b.start[0], y > b.start[1], x > b.start[2]);
let mut t = [0i32; NTAP];
if xi {
t[0] = at(z, y, x - 1);
}
if yi {
t[1] = at(z, y - 1, x);
if xi {
t[3] = at(z, y - 1, x - 1);
}
}
if zi {
t[2] = at(z - 1, y, x);
if xi {
t[4] = at(z - 1, y, x - 1);
}
if yi {
t[5] = at(z - 1, y - 1, x);
}
}
if let Some(pi) = pbi {
let pb = &p.bands[pi];
let pp = p.parent_pos(b, pb, [z, y, x]);
t[6] = at(pp[0], pp[1], pp[2]);
}
t
}
#[inline]
fn predict(w: &[i16; NTAP], t: &[i32; NTAP]) -> i32 {
let s: i64 = w.iter().zip(t).map(|(&a, &b)| a as i64 * b as i64).sum();
((s + (1 << (PSHIFT - 1))) >> PSHIFT).clamp(-(1 << 30), 1 << 30) as i32
}
pub fn fit_predictors(p: &Pyramid, q: &[i32]) -> Vec<Pred> {
let [_, ny, nx] = p.dims;
(0..p.bands.len())
.map(|bi| {
let band = &p.bands[bi];
let pbi = p.parent_band(bi);
let mut g = [[0.0f64; NTAP]; NTAP];
let mut h = [0.0f64; NTAP];
let mut n = 0usize;
for z in band.start[0]..band.end[0] {
for y in band.start[1]..band.end[1] {
for x in band.start[2]..band.end[2] {
let t = taps(p, bi, pbi, q, z, y, x);
let v = q[(z * ny + y) * nx + x] as f64;
if v == 0.0 && t.iter().all(|&a| a == 0) {
continue;
}
n += 1;
for i in 0..NTAP {
h[i] += t[i] as f64 * v;
for j in 0..NTAP {
g[i][j] += t[i] as f64 * t[j] as f64;
}
}
}
}
}
if n < 32 {
return Pred::default();
}
let mut a = g;
let mut r = h;
for i in 0..NTAP {
a[i][i] += 1e-3 * (g[i][i] + 1.0);
}
for c in 0..NTAP {
let piv = a[c][c];
if piv.abs() < 1e-12 {
continue;
}
for rr in 0..NTAP {
if rr != c {
let f = a[rr][c] / piv;
for k in 0..NTAP {
a[rr][k] -= f * a[c][k];
}
r[rr] -= f * r[c];
}
}
}
let mut w = [0i16; NTAP];
for i in 0..NTAP {
let wi = if a[i][i].abs() > 1e-12 {
r[i] / a[i][i]
} else {
0.0
};
w[i] = (wi * (1 << PSHIFT) as f64).round().clamp(-32767.0, 32767.0) as i16;
}
let (mut c0, mut c1) = (0.0f64, 0.0f64);
for z in band.start[0]..band.end[0] {
for y in band.start[1]..band.end[1] {
for x in band.start[2]..band.end[2] {
let v = q[(z * ny + y) * nx + x];
let pr = predict(&w, &taps(p, bi, pbi, q, z, y, x));
c0 += (1.0 + v.unsigned_abs() as f64).log2();
c1 += (1.0 + (v - pr).unsigned_abs() as f64).log2();
}
}
}
if c1 < 0.98 * c0 {
Pred { on: true, w }
} else {
Pred::default()
}
})
.collect()
}
pub fn write_preds(w: &mut crate::bitio::ByteWriter, preds: &[Pred]) {
for pr in preds {
w.u8(pr.on as u8);
if pr.on {
for &v in &pr.w {
w.svarint(v as i64);
}
}
}
}
pub fn read_preds(
r: &mut crate::bitio::ByteReader,
nbands: usize,
) -> Result<Vec<Pred>, crate::bitio::DecodeError> {
(0..nbands)
.map(|_| {
let mut pr = Pred {
on: r.u8()? != 0,
..Default::default()
};
if pr.on {
for v in pr.w.iter_mut() {
*v = r.svarint()?.clamp(-32767, 32767) as i16;
}
}
Ok(pr)
})
.collect()
}
fn traverse(
p: &Pyramid,
q: &mut [i32],
res: &mut [i32],
preds: &[Pred],
mut f: impl FnMut(&Feat, i32, i32) -> i32,
) {
let [_, ny, nx] = p.dims;
for bi in 0..p.bands.len() {
let pbi = p.parent_band(bi);
let band = p.bands[bi].clone();
let pr = preds.get(bi).copied().unwrap_or_default();
for z in band.start[0]..band.end[0] {
for y in band.start[1]..band.end[1] {
for x in band.start[2]..band.end[2] {
let i = (z * ny + y) * nx + x;
let mut ft = features(p, bi, pbi, res, z, y, x);
let pv = if pr.on {
predict(&pr.w, &taps(p, bi, pbi, q, z, y, x))
} else {
0
};
ft.pm = pv.unsigned_abs().min(CAP as u32) as u16;
ft.pv = pv;
let r = f(&ft, pv, q[i].wrapping_sub(pv));
res[i] = r;
q[i] = r + pv;
}
}
}
}
}
fn tile_edge(nd: usize) -> usize {
match nd {
0 | 1 => 256,
2 => 16,
_ => 8,
}
}
fn band_tiles(b: &crate::wavelet::Band, nd: usize) -> ([usize; 3], [usize; 3]) {
let t = tile_edge(nd);
let mut e = [1usize; 3];
let mut n = [1usize; 3];
for k in 0..3 {
let len = b.end[k] - b.start[k];
if len > 1 {
e[k] = t;
n[k] = len.div_ceil(t);
}
}
(e, n)
}
pub fn tile_live(p: &Pyramid, q: &[i32], preds: &[Pred]) -> Vec<Vec<bool>> {
let [_, ny, nx] = p.dims;
let nd = p.dims.iter().filter(|&&d| d > 1).count();
let mut qq = q.to_vec();
let mut res = vec![0i32; q.len()];
traverse(p, &mut qq, &mut res, preds, |_, _, r| r);
p.bands
.iter()
.map(|b| {
let (e, n) = band_tiles(b, nd);
let mut live = vec![false; n[0] * n[1] * n[2]];
for z in b.start[0]..b.end[0] {
for y in b.start[1]..b.end[1] {
for x in b.start[2]..b.end[2] {
if res[(z * ny + y) * nx + x] != 0 {
let t = (((z - b.start[0]) / e[0]) * n[1] + (y - b.start[1]) / e[1])
* n[2]
+ (x - b.start[2]) / e[2];
live[t] = true;
}
}
}
}
live
})
.collect()
}
pub fn code_bands_tiled<B: Bits, M: CoefModel>(
p: &Pyramid,
q: &mut [i32],
preds: &[Pred],
b: &mut B,
model: &mut M,
live: Option<&[Vec<bool>]>,
) {
let [_, ny, nx] = p.dims;
let nd = p.dims.iter().filter(|&&d| d > 1).count();
let mut res = vec![0i32; q.len()];
let mut flag = [BitModel::new(1 << 15); NCLASS * 2];
for bi in 0..p.bands.len() {
let pbi = p.parent_band(bi);
let band = p.bands[bi].clone();
let (e, n) = band_tiles(&band, nd);
let c = band_class(&band) as usize;
let mut prev = 1usize;
let alive: Vec<bool> = (0..n[0] * n[1] * n[2])
.map(|t| {
let v = live.is_some_and(|l| l[bi][t]);
let v = b.bit(v, &mut flag[c * 2 + prev]);
prev = v as usize;
v
})
.collect();
let pr = preds.get(bi).copied().unwrap_or_default();
for z in band.start[0]..band.end[0] {
for y in band.start[1]..band.end[1] {
for x in band.start[2]..band.end[2] {
let i = (z * ny + y) * nx + x;
let t = (((z - band.start[0]) / e[0]) * n[1] + (y - band.start[1]) / e[1])
* n[2]
+ (x - band.start[2]) / e[2];
let pv = if pr.on {
predict(&pr.w, &taps(p, bi, pbi, q, z, y, x))
} else {
0
};
let r = if alive[t] {
let mut ft = features(p, bi, pbi, &res, z, y, x);
ft.pm = pv.unsigned_abs().min(CAP as u32) as u16;
ft.pv = pv;
model.code(b, &ft, q[i].wrapping_sub(pv))
} else {
0
};
res[i] = r;
q[i] = r.wrapping_add(pv);
}
}
}
}
}
pub fn code_bands<B: Bits, M: CoefModel>(
p: &Pyramid,
q: &mut [i32],
preds: &[Pred],
b: &mut B,
model: &mut M,
) {
let mut res = vec![0i32; q.len()];
traverse(p, q, &mut res, preds, |f, _, r| model.code(b, f, r));
}
pub fn visit<F: FnMut(&Feat, i32)>(p: &Pyramid, q: &[i32], preds: &[Pred], mut f: F) {
let mut qq = q.to_vec();
let mut res = vec![0i32; q.len()];
traverse(p, &mut qq, &mut res, preds, |ft, _, r| {
f(ft, r);
r
});
}
pub fn encode_hand(p: &Pyramid, q: &[i32], preds: &[Pred]) -> Vec<u8> {
let mut e = Encoder::new();
let mut qq = q.to_vec();
code_bands(p, &mut qq, preds, &mut e, &mut HandModel::default());
e.finish()
}
pub fn decode_hand(p: &Pyramid, bytes: &[u8], preds: &[Pred]) -> Vec<i32> {
let mut d = Decoder::new(bytes);
let mut q = vec![0i32; p.len()];
code_bands(p, &mut q, preds, &mut d, &mut HandModel::default());
q
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wavelet::{forward_97, MAX_LEVELS};
#[test]
fn round_trip_hand_model() {
let d = [12, 20, 33];
let p = Pyramid::new(d, MAX_LEVELS);
let mut x: Vec<f64> = (0..p.len())
.map(|i| ((i as f64) * 0.013).sin() * 100.0 + (i % 17) as f64)
.collect();
forward_97(&p, &mut x);
let mut q: Vec<i32> = x.iter().map(|v| (v / 0.7).round() as i32).collect();
q[5] = 1_000_000; q[77] = -123_456;
for preds in [vec![], fit_predictors(&p, &q)] {
let bytes = encode_hand(&p, &q, &preds);
assert_eq!(decode_hand(&p, &bytes, &preds), q);
assert_eq!(encode_hand(&p, &q, &preds), bytes, "deterministic");
}
}
}