use crate::error::{DecodeError, DecodeResult};
use crate::gamma::{BitReader, BitWriter};
pub fn encode_significand(writer: &mut BitWriter, digits: &[u8], negative: bool) {
if digits.is_empty() {
return;
}
if negative {
encode_complemented_digits(writer, digits);
} else {
encode_digits_to_writer(writer, digits);
}
}
fn encode_complemented_digits(writer: &mut BitWriter, digits: &[u8]) {
let last_nonzero = digits.iter().rposition(|&d| d != 0).unwrap_or(0);
let first_complemented = if last_nonzero == 0 {
10 - digits[0]
} else {
9 - digits[0]
};
writer.write_bits(u64::from(first_complemented), 4);
let mut pos = 1;
while pos < digits.len() {
let mut declet_value = 0u16;
for i in 0..3 {
let idx = pos + i;
let digit = if idx < digits.len() {
let d = digits[idx];
match idx.cmp(&last_nonzero) {
std::cmp::Ordering::Less => 9 - d,
std::cmp::Ordering::Equal => 10 - d,
std::cmp::Ordering::Greater => 0, }
} else {
0 };
declet_value = declet_value * 10 + u16::from(digit);
}
writer.write_bits(u64::from(declet_value), 10);
pos += 3;
}
}
#[inline]
fn encode_digits_to_writer(writer: &mut BitWriter, digits: &[u8]) {
writer.write_bits(u64::from(digits[0]), 4);
let mut pos = 1;
while pos < digits.len() {
let mut declet_value = 0u16;
for i in 0..3 {
if pos + i < digits.len() {
let digit = digits[pos + i];
declet_value = declet_value * 10 + u16::from(digit);
} else {
declet_value *= 10;
}
}
writer.write_bits(u64::from(declet_value), 10);
pos += 3;
}
}
fn compute_complement_in_place(digits: &mut [u8]) {
let mut borrow = 0u8;
for i in (0..digits.len()).rev() {
let minuend = if i == 0 { 10u8 } else { 0u8 };
let sub = digits[i] + borrow;
if minuend < sub {
digits[i] = minuend + 10 - sub;
borrow = 1;
} else {
digits[i] = minuend - sub;
borrow = 0;
}
}
}
pub fn decode_significand(reader: &mut BitReader, negative: bool) -> DecodeResult<Vec<u8>> {
if !reader.has_bits() {
return Err(DecodeError::UnexpectedEndOfInput);
}
let remaining = reader.remaining_bits();
let num_declets = remaining.saturating_sub(4) / 10;
let mut digits = Vec::with_capacity(1 + num_declets * 3);
#[allow(clippy::cast_possible_truncation)]
let first_tetrade = reader.read_bits(4)? as u8;
if first_tetrade > 9 {
return Err(DecodeError::InvalidTetrade(first_tetrade));
}
digits.push(first_tetrade);
while reader.remaining_bits() >= 10 {
#[allow(clippy::cast_possible_truncation)]
let declet = reader.read_bits(10)? as u16;
if declet > 999 {
return Err(DecodeError::InvalidDeclet(declet));
}
#[allow(clippy::cast_possible_truncation)]
let d1 = (declet / 100) as u8;
#[allow(clippy::cast_possible_truncation)]
let d2 = ((declet / 10) % 10) as u8;
#[allow(clippy::cast_possible_truncation)]
let d3 = (declet % 10) as u8;
digits.push(d1);
digits.push(d2);
digits.push(d3);
}
if negative {
compute_complement_in_place(&mut digits);
}
if digits.is_empty() {
return Err(DecodeError::InvalidSignificand(
"empty significand".to_string(),
));
}
if digits[0] == 0 || digits[0] > 9 {
return Err(DecodeError::InvalidSignificand(format!(
"first digit is {}",
digits[0]
)));
}
Ok(digits)
}
#[cfg(test)]
mod tests {
use super::*;
fn strip_trailing_zeros(digits: &[u8]) -> &[u8] {
let mut end = digits.len();
while end > 1 && digits[end - 1] == 0 {
end -= 1;
}
&digits[..end]
}
fn encode_sig_to_bytes(digits: &[u8], negative: bool) -> Vec<u8> {
let mut writer = BitWriter::new();
encode_significand(&mut writer, digits, negative);
writer.into_bytes()
}
#[test]
fn test_encode_decode_positive() {
let digits = vec![1, 0, 3, 2];
let encoded = encode_sig_to_bytes(&digits, false);
let mut reader = BitReader::new(&encoded);
let decoded = decode_significand(&mut reader, false).unwrap();
assert_eq!(decoded, digits);
}
#[test]
fn test_encode_decode_negative() {
let digits = vec![1, 0, 3, 2];
let encoded = encode_sig_to_bytes(&digits, true);
let mut reader = BitReader::new(&encoded);
let decoded = decode_significand(&mut reader, true).unwrap();
assert_eq!(decoded, digits);
}
#[test]
fn test_significand_from_paper() {
let digits = vec![7, 0, 7, 1, 0, 6];
let encoded = encode_sig_to_bytes(&digits, false);
let mut reader = BitReader::new(&encoded);
let decoded = decode_significand(&mut reader, false).unwrap();
assert_eq!(&decoded[..6], &digits[..]);
}
#[test]
fn test_strip_trailing() {
let digits = vec![1, 2, 3, 0, 0, 0];
let stripped = strip_trailing_zeros(&digits);
assert_eq!(stripped, &[1, 2, 3]);
let digits2 = vec![1, 0, 0];
let stripped2 = strip_trailing_zeros(&digits2);
assert_eq!(stripped2, &[1]);
}
}