use crate::format::*;
pub const MAX_RICE_PARTITION_ORDER: u32 = 15;
pub struct RicePartition {
pub order: u32,
pub parameters: Vec<u32>,
pub is_rice2: bool,
}
#[inline]
fn ilog2_u64(v: u64) -> u32 {
debug_assert!(v > 0);
63 - v.leading_zeros()
}
pub fn max_partition_order_from_blocksize(blocksize: u32) -> u32 {
blocksize.trailing_zeros().min(MAX_RICE_PARTITION_ORDER)
}
fn max_partition_order_limited(limit: u32, blocksize: u32, predictor_order: u32) -> u32 {
let mut o = limit;
while o > 0 && (blocksize >> o) <= predictor_order {
o -= 1;
}
o
}
fn precompute_partition_info_sums(
residual: &[i32],
predictor_order: u32,
min_partition_order: u32,
max_partition_order: u32,
) -> Vec<u64> {
let residual_samples = residual.len() as u32;
let blocksize = residual_samples + predictor_order;
let default_partition_samples = blocksize >> max_partition_order;
let total = (1usize << (max_partition_order + 1)) - (1usize << min_partition_order);
let mut sums = vec![0u64; total];
let partitions_max = 1usize << max_partition_order;
let mut rs = 0usize;
for (partition, slot) in sums.iter_mut().enumerate().take(partitions_max) {
let psamps = if partition == 0 {
default_partition_samples - predictor_order
} else {
default_partition_samples
} as usize;
let mut s = 0u64;
for _ in 0..psamps {
s += (residual[rs] as i64).unsigned_abs();
rs += 1;
}
*slot = s;
}
let mut from = 0usize;
let mut to = partitions_max;
let mut partitions = partitions_max;
let mut po = max_partition_order;
while po > min_partition_order {
po -= 1;
partitions >>= 1;
for _ in 0..partitions {
sums[to] = sums[from] + sums[from + 1];
to += 1;
from += 2;
}
}
sums
}
fn count_rice_bits(rice_parameter: u32, partition_samples: u32, abs_sum: u64) -> u32 {
let extra = if rice_parameter > 0 {
abs_sum >> (rice_parameter - 1)
} else {
abs_sum << 1
};
let bits = u64::from(ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)
+ u64::from(1 + rice_parameter) * u64::from(partition_samples)
+ extra
- u64::from(partition_samples >> 1);
bits.min(u64::from(u32::MAX)) as u32
}
fn set_partitioned_rice(
abs_sums: &[u64],
residual_samples: u32,
predictor_order: u32,
rice_parameter_limit: u32,
partition_order: u32,
) -> Option<(Vec<u32>, u32)> {
let partitions = 1usize << partition_order;
let mut params = vec![0u32; partitions];
let mut bits = u64::from(
ENTROPY_CODING_METHOD_TYPE_LEN + ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN,
);
let partition_samples_base = (residual_samples + predictor_order) >> partition_order;
let divisor_base = 0x40000u32 / partition_samples_base;
for (partition, slot) in params.iter_mut().enumerate() {
let (partition_samples, divisor) = if partition > 0 {
(partition_samples_base, divisor_base)
} else {
if partition_samples_base <= predictor_order {
return None;
}
let ps = partition_samples_base - predictor_order;
(ps, 0x40000u32 / ps)
};
let mean = abs_sums[partition];
let mut rice_parameter = if mean < 2 {
0
} else {
let scaled = mean.wrapping_sub(1).wrapping_mul(u64::from(divisor)) >> 18;
if scaled == 0 {
0
} else {
ilog2_u64(scaled) + 1
}
};
if rice_parameter >= rice_parameter_limit {
rice_parameter = rice_parameter_limit - 1;
}
*slot = rice_parameter;
let partition_bits = count_rice_bits(rice_parameter, partition_samples, mean);
bits = if u64::from(partition_bits) < u64::from(u32::MAX) - bits {
bits + u64::from(partition_bits)
} else {
u64::from(u32::MAX)
};
}
Some((params, bits.min(u64::from(u32::MAX)) as u32))
}
pub fn find_best_partition_order(
residual: &[i32],
predictor_order: u32,
rice_parameter_limit: u32,
min_partition_order: u32,
max_partition_order: u32,
) -> (RicePartition, u32) {
let residual_samples = residual.len() as u32;
let blocksize = residual_samples + predictor_order;
let max_po = max_partition_order_limited(max_partition_order, blocksize, predictor_order);
let min_po = min_partition_order.min(max_po);
let abs_sums = precompute_partition_info_sums(residual, predictor_order, min_po, max_po);
let mut best_bits = 0u32;
let mut best_params: Vec<u32> = Vec::new();
let mut best_order = 0u32;
let mut found = false;
let mut offset = 0usize;
let mut po = max_po;
loop {
let slice = &abs_sums[offset..offset + (1usize << po)];
match set_partitioned_rice(
slice,
residual_samples,
predictor_order,
rice_parameter_limit,
po,
) {
Some((params, bits)) => {
if !found || bits < best_bits {
best_bits = bits;
best_params = params;
best_order = po;
found = true;
}
}
None => break,
}
offset += 1usize << po;
if po == min_po {
break;
}
po -= 1;
}
let is_rice2 = best_params
.iter()
.any(|&p| p >= ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER);
(
RicePartition {
order: best_order,
parameters: best_params,
is_rice2,
},
best_bits,
)
}