const MODE_MV_COUNT_SAT: u32 = 20;
const COUNT_TO_UPDATE_FACTOR: [u32; 21] = [
0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, 70, 76, 83, 89, 96, 102, 108, 115, 121, 128,
];
#[inline]
fn round_pow2(v: u32, n: u32) -> u32 {
(v + (1 << (n - 1))) >> n
}
#[inline]
pub fn get_prob(num: u32, den: u32) -> u8 {
debug_assert!(den != 0);
let p = ((num as u64 * 256 + (den as u64 / 2)) / den as u64) as i32;
p.clamp(1, 255) as u8
}
#[inline]
pub fn get_binary_prob(n0: u32, n1: u32) -> u8 {
let den = n0 + n1;
if den == 0 {
128
} else {
get_prob(n0, den)
}
}
#[inline]
pub fn weighted_prob(p1: u8, p2: u8, factor: u32) -> u8 {
round_pow2(p1 as u32 * (256 - factor) + p2 as u32 * factor, 8) as u8
}
#[inline]
pub fn merge_probs(pre: u8, ct: [u32; 2], count_sat: u32, max_update_factor: u32) -> u8 {
let prob = get_binary_prob(ct[0], ct[1]);
let count = (ct[0] + ct[1]).min(count_sat);
let factor = max_update_factor * count / count_sat;
weighted_prob(pre, prob, factor)
}
#[inline]
pub fn mode_mv_merge_probs(pre: u8, ct: [u32; 2]) -> u8 {
let den = ct[0] + ct[1];
if den == 0 {
pre
} else {
let count = den.min(MODE_MV_COUNT_SAT);
let factor = COUNT_TO_UPDATE_FACTOR[count as usize];
let prob = get_prob(ct[0], den);
weighted_prob(pre, prob, factor)
}
}
pub fn tree_merge_probs(tree: &[i8], pre: &[u8], counts: &[u32], probs: &mut [u8]) {
fn rec(i: usize, tree: &[i8], pre: &[u8], counts: &[u32], probs: &mut [u8]) -> u32 {
let l = tree[i];
let left = if l <= 0 {
counts[(-l) as usize]
} else {
rec(l as usize, tree, pre, counts, probs)
};
let r = tree[i + 1];
let right = if r <= 0 {
counts[(-r) as usize]
} else {
rec(r as usize, tree, pre, counts, probs)
};
probs[i >> 1] = mode_mv_merge_probs(pre[i >> 1], [left, right]);
left + right
}
rec(0, tree, pre, counts, probs);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_prob_rounds_and_clamps() {
assert_eq!(get_prob(10, 20), 128); assert_eq!(get_prob(20, 20), 255); assert_eq!(get_prob(0, 20), 1); assert_eq!(get_binary_prob(0, 0), 128);
}
#[test]
fn weighted_prob_blends() {
assert_eq!(weighted_prob(100, 200, 0), 100);
assert_eq!(weighted_prob(100, 200, 256), 200);
assert_eq!(weighted_prob(100, 200, 64), 125); }
#[test]
fn mode_mv_merge_matches_libvpx() {
assert_eq!(mode_mv_merge_probs(128, [0, 0]), 128); assert_eq!(mode_mv_merge_probs(128, [20, 0]), 192);
assert_eq!(mode_mv_merge_probs(128, [10, 10]), 128);
}
#[test]
fn merge_probs_coef() {
assert_eq!(merge_probs(128, [24, 0], 24, 112), 184);
assert_eq!(merge_probs(200, [0, 0], 24, 112), 200);
}
#[test]
fn tree_merge_two_leaf() {
let tree = [0i8, -1];
let pre = [128u8];
let counts = [20u32, 0];
let mut probs = [0u8];
tree_merge_probs(&tree, &pre, &counts, &mut probs);
assert_eq!(probs[0], 192);
}
}