#[inline]
#[must_use]
pub fn predict_median(left: i32, top: i32, top_left: i32) -> i32 {
let prediction = left.wrapping_add(top).wrapping_sub(top_left);
if left >= top {
if top_left >= left {
top
} else if top_left <= top {
left
} else {
prediction
}
} else {
if top_left >= top {
left
} else if top_left <= left {
top
} else {
prediction
}
}
}
pub fn encode_line(line: &[i32], above: &[i32], residuals: &mut Vec<i32>) {
residuals.clear();
residuals.reserve(line.len());
let width = line.len();
if width == 0 {
return;
}
for x in 0..width {
let left = if x > 0 { line[x - 1] } else { 0 };
let top = above[x];
let top_left = if x > 0 { above[x - 1] } else { 0 };
let pred = predict_median(left, top, top_left);
let residual = line[x] - pred;
residuals.push(residual);
}
}
pub fn decode_line(residuals: &[i32], above: &[i32], output: &mut Vec<i32>) {
output.clear();
output.reserve(residuals.len());
let width = residuals.len();
if width == 0 {
return;
}
for x in 0..width {
let left = if x > 0 { output[x - 1] } else { 0 };
let top = above[x];
let top_left = if x > 0 { above[x - 1] } else { 0 };
let pred = predict_median(left, top, top_left);
let sample = pred + residuals[x];
output.push(sample);
}
}
#[inline]
#[must_use]
pub fn compute_context(top: i32, left: i32, top_left: i32, top_right: i32) -> usize {
let d_top = top - top_left;
let d_left = left - top_left;
let d_top_right = top_right - top;
let q0 = quantize_gradient(d_top);
let q1 = quantize_gradient(d_left);
let q2 = quantize_gradient(d_top_right);
let mut ctx = q0 * 25 + q1 * 5 + q2;
if ctx < 0 {
ctx = -ctx;
}
(ctx as usize) % super::types::CONTEXT_COUNT
}
#[inline]
fn quantize_gradient(d: i32) -> i32 {
if d == 0 {
0
} else if d < -2 {
-2
} else if d < 0 {
-1
} else if d > 2 {
2
} else {
1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_predict_median_basic() {
assert_eq!(predict_median(100, 100, 100), 100);
assert_eq!(predict_median(100, 200, 150), 150);
}
#[test]
#[ignore]
fn test_predict_median_edge_cases() {
assert_eq!(predict_median(10, 20, 30), 10);
assert_eq!(predict_median(10, 20, 5), 20);
}
#[test]
#[ignore]
fn test_predict_median_symmetric() {
assert_eq!(predict_median(200, 100, 50), 200);
assert_eq!(predict_median(200, 100, 250), 100);
}
#[test]
#[ignore]
fn test_encode_decode_roundtrip() {
let line = vec![10, 20, 30, 40, 50];
let above = vec![5, 15, 25, 35, 45];
let mut residuals = Vec::new();
let mut decoded = Vec::new();
encode_line(&line, &above, &mut residuals);
decode_line(&residuals, &above, &mut decoded);
assert_eq!(line, decoded);
}
#[test]
#[ignore]
fn test_encode_decode_first_line() {
let line = vec![128, 130, 132, 134, 136];
let above = vec![0, 0, 0, 0, 0]; let mut residuals = Vec::new();
let mut decoded = Vec::new();
encode_line(&line, &above, &mut residuals);
decode_line(&residuals, &above, &mut decoded);
assert_eq!(line, decoded);
}
#[test]
#[ignore]
fn test_encode_decode_constant_line() {
let line = vec![100; 8];
let above = vec![100; 8];
let mut residuals = Vec::new();
let mut decoded = Vec::new();
encode_line(&line, &above, &mut residuals);
for &r in &residuals {
assert_eq!(r, 0);
}
decode_line(&residuals, &above, &mut decoded);
assert_eq!(line, decoded);
}
#[test]
#[ignore]
fn test_encode_decode_empty() {
let line: Vec<i32> = vec![];
let above: Vec<i32> = vec![];
let mut residuals = Vec::new();
let mut decoded = Vec::new();
encode_line(&line, &above, &mut residuals);
assert!(residuals.is_empty());
decode_line(&residuals, &above, &mut decoded);
assert!(decoded.is_empty());
}
#[test]
#[ignore]
fn test_encode_decode_gradient() {
let line: Vec<i32> = (0..16).collect();
let above: Vec<i32> = (0..16).map(|x| x + 1).collect();
let mut residuals = Vec::new();
let mut decoded = Vec::new();
encode_line(&line, &above, &mut residuals);
decode_line(&residuals, &above, &mut decoded);
assert_eq!(line, decoded);
}
#[test]
#[ignore]
fn test_quantize_gradient() {
assert_eq!(quantize_gradient(0), 0);
assert_eq!(quantize_gradient(-1), -1);
assert_eq!(quantize_gradient(-5), -2);
assert_eq!(quantize_gradient(1), 1);
assert_eq!(quantize_gradient(5), 2);
}
#[test]
#[ignore]
fn test_compute_context_range() {
for top in [-100, -1, 0, 1, 100] {
for left in [-100, -1, 0, 1, 100] {
let ctx = compute_context(top, left, 0, 0);
assert!(ctx < super::super::types::CONTEXT_COUNT);
}
}
}
}