use crate::binary::BinaryBuf;
use core::iter;
pub fn encode_significand_trailing_digits<D: BinaryBuf, const N: usize>(
decimal: &mut D,
mut chunks: [&[u8]; N],
) -> MostSignificantDigit {
let mut chunk_index = Some(N - 1);
let max_digits = decimal.trailing_significand_digits();
debug_assert_eq!(0, max_digits % 3, "{}", max_digits);
let decimal = decimal.bytes_mut();
let mut digit_index = 0;
let mut bit_index = 0;
while digit_index < max_digits {
match next_ascii_declet_rev(&mut chunks, &mut chunk_index) {
Some(ascii) => {
let bcd = encode_ascii_declet_to_bcd(ascii);
encode_bcd_declet_to_dpd(bcd, decimal, &mut bit_index);
digit_index += 3;
}
None => break,
}
}
if chunk_index.is_some() {
MostSignificantDigit::from_ascii(chunks[0][0])
}
else {
MostSignificantDigit::zero()
}
}
pub fn encode_significand_trailing_digits_repeat<D: BinaryBuf>(
decimal: &mut D,
digit: u8,
) -> MostSignificantDigit {
let max_digits = decimal.trailing_significand_digits();
debug_assert_eq!(0, max_digits % 3, "{}", max_digits);
let decimal = decimal.bytes_mut();
let mut digit_index = 0;
let mut bit_index = 0;
while digit_index < max_digits {
let bcd = encode_ascii_declet_to_bcd([digit, digit, digit]);
encode_bcd_declet_to_dpd(bcd, decimal, &mut bit_index);
digit_index += 3;
}
MostSignificantDigit::from_ascii(digit)
}
pub fn decode_significand_trailing_declets<D: BinaryBuf>(
decimal: &D,
) -> impl Iterator<Item = [u8; 3]> + '_ {
let mut bit_index = decimal.trailing_significand_width_bits();
let decimal = decimal.bytes();
iter::from_fn(move || {
if bit_index > 0 {
let bcd = decode_dpd_declet_to_bcd(decimal, &mut bit_index);
Some(decode_bcd_declet_to_ascii(bcd))
}
else {
None
}
})
}
fn next_ascii_declet_rev(chunks: &mut [&[u8]], chunk_index: &mut Option<usize>) -> Option<[u8; 3]> {
match *chunk_index {
Some(c) => {
let chunk = chunks[c];
let (mut out, mut out_index) = match chunk.len() {
1 => {
let out = [chunk[0], b'0', b'0'];
*chunk_index = c.checked_sub(1);
(out, 1)
}
2 => {
let out = [chunk[1], chunk[0], b'0'];
*chunk_index = c.checked_sub(1);
(out, 2)
}
3 => {
let out = [chunk[2], chunk[1], chunk[0]];
*chunk_index = c.checked_sub(1);
return Some(out);
}
_ => {
debug_assert_ne!(0, chunk.len(), "all chunks must have at least 1 digit");
let out = [
chunk[chunk.len() - 1],
chunk[chunk.len() - 2],
chunk[chunk.len() - 3],
];
chunks[c] = &chunk[..chunk.len() - 3];
return Some(out);
}
};
while let Some(c) = *chunk_index {
if out_index == 3 {
return Some(out);
}
let chunk = chunks[c];
match chunk.len() {
0 => *chunk_index = c.checked_sub(1),
1 => {
out[out_index] = chunk[0];
out_index += 1;
*chunk_index = c.checked_sub(1);
}
_ => {
out[out_index] = chunk[chunk.len() - 1];
out_index += 1;
chunks[c] = &chunk[..chunk.len() - 1];
}
}
}
Some(out)
}
None => None,
}
}
#[derive(Clone, Copy)]
pub struct MostSignificantDigit(u8);
impl MostSignificantDigit {
pub(crate) fn zero() -> Self {
MostSignificantDigit(0)
}
pub(crate) fn from_ascii(digit: u8) -> Self {
MostSignificantDigit(encode_ascii_digit_to_bcd(digit))
}
pub(crate) fn from_bcd(digit: u8) -> Self {
MostSignificantDigit(digit)
}
pub fn get_bcd(self) -> u8 {
self.0
}
pub fn get_ascii(self) -> u8 {
decode_bcd_digit_to_ascii(self.0)
}
}
fn encode_ascii_digit_to_bcd(ascii: u8) -> u8 {
ascii - b'0'
}
fn decode_bcd_digit_to_ascii(bcd: u8) -> u8 {
bcd + b'0'
}
fn encode_ascii_declet_to_bcd(ascii: [u8; 3]) -> u16 {
let d0 = encode_ascii_digit_to_bcd(ascii[0]) as u16;
let d1 = (encode_ascii_digit_to_bcd(ascii[1]) as u16) << 4;
let d2 = (encode_ascii_digit_to_bcd(ascii[2]) as u16) << 8;
d2 | d1 | d0
}
fn decode_bcd_declet_to_ascii(bcd: u16) -> [u8; 3] {
const D2: u16 = 0b0000_1111_0000_0000u16;
const D1: u16 = 0b0000_0000_1111_0000u16;
const D0: u16 = 0b0000_0000_0000_1111u16;
let d2 = decode_bcd_digit_to_ascii(((bcd & D2) >> 8) as u8);
let d1 = decode_bcd_digit_to_ascii(((bcd & D1) >> 4) as u8);
let d0 = decode_bcd_digit_to_ascii((bcd & D0) as u8);
[d2, d1, d0]
}
fn encode_bcd_declet_to_dpd(bcd: u16, decimal: &mut [u8], decimal_bit_index: &mut usize) {
const D: u16 = D2 | D1 | D0;
const D01: u16 = D0 | D1;
const D12: u16 = D1 | D2;
const D02: u16 = D0 | D2;
const D2: u16 = 0b0000_0000_0000_1000;
const DG: u16 = 0b0000_0000_0000_0100;
const DH: u16 = 0b0000_0000_0000_0010;
const DI: u16 = 0b0000_0000_0000_0001;
const D1: u16 = 0b0000_0000_1000_0000;
const DD: u16 = 0b0000_0000_0100_0000;
const DE: u16 = 0b0000_0000_0010_0000;
const DF: u16 = 0b0000_0000_0001_0000;
const D0: u16 = 0b0000_1000_0000_0000;
const DA: u16 = 0b0000_0100_0000_0000;
const DB: u16 = 0b0000_0010_0000_0000;
const DC: u16 = 0b0000_0001_0000_0000;
const B0: u16 = 0b0000_0000_0000_0001;
let dpd = match bcd & D {
0 => {
let bit0 = (bcd & DA) >> 1;
let bit1 = (bcd & DB) >> 1;
let bit2 = (bcd & DC) >> 1;
let bit3 = bcd & DD;
let bit4 = bcd & DE;
let bit5 = bcd & DF;
let bit6 = 0;
let bit7 = bcd & DG;
let bit8 = bcd & DH;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D2 => {
let bit0 = (bcd & DA) >> 1;
let bit1 = (bcd & DB) >> 1;
let bit2 = (bcd & DC) >> 1;
let bit3 = bcd & DD;
let bit4 = bcd & DE;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = 0;
let bit8 = 0;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D1 => {
let bit0 = (bcd & DA) >> 1;
let bit1 = (bcd & DB) >> 1;
let bit2 = (bcd & DC) >> 1;
let bit3 = (bcd & DG) << 4;
let bit4 = (bcd & DH) << 4;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = 0;
let bit8 = B0 << 1;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D0 => {
let bit0 = (bcd & DG) << 7;
let bit1 = (bcd & DH) << 7;
let bit2 = (bcd & DC) >> 1;
let bit3 = bcd & DD;
let bit4 = bcd & DE;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = B0 << 2;
let bit8 = 0;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D01 => {
let bit0 = (bcd & DG) << 7;
let bit1 = (bcd & DH) << 7;
let bit2 = (bcd & DC) >> 1;
let bit3 = 0;
let bit4 = 0;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = B0 << 2;
let bit8 = B0 << 1;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D12 => {
let bit0 = (bcd & DA) >> 1;
let bit1 = (bcd & DB) >> 1;
let bit2 = (bcd & DC) >> 1;
let bit3 = B0 << 6;
let bit4 = 0;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = B0 << 2;
let bit8 = B0 << 1;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D02 => {
let bit0 = (bcd & DD) << 3;
let bit1 = (bcd & DE) << 3;
let bit2 = (bcd & DC) >> 1;
let bit3 = 0;
let bit4 = B0 << 5;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = B0 << 2;
let bit8 = B0 << 1;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
D => {
let bit0 = 0;
let bit1 = 0;
let bit2 = (bcd & DC) >> 1;
let bit3 = B0 << 6;
let bit4 = B0 << 5;
let bit5 = bcd & DF;
let bit6 = B0 << 3;
let bit7 = B0 << 2;
let bit8 = B0 << 1;
let bit9 = bcd & DI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9
}
_ => unreachable!(),
};
let decimal_byte_shift = (*decimal_bit_index % 8) as u32;
let decimal_byte_index = *decimal_bit_index / 8;
decimal[decimal_byte_index] |= (dpd << decimal_byte_shift) as u8;
decimal[decimal_byte_index + 1] |= (dpd >> (8 - decimal_byte_shift)) as u8;
*decimal_bit_index += 10;
}
fn decode_dpd_declet_to_bcd(decimal: &[u8], decimal_bit_index: &mut usize) -> u16 {
*decimal_bit_index -= 10;
let decimal_byte_shift = (*decimal_bit_index % 8) as u32;
let decimal_byte_index = *decimal_bit_index / 8;
let dpd0 = (decimal[decimal_byte_index] as u16) >> decimal_byte_shift;
let dpd1 = (decimal[decimal_byte_index + 1] as u16) << (8 - decimal_byte_shift);
let dpd = dpd0 | dpd1;
const B0: u16 = 0b0000_0000_0000_0001u16;
const B1: u16 = 0b0000_0000_0000_0010u16;
const B2: u16 = 0b0000_0000_0000_0100u16;
const B3: u16 = 0b0000_0000_0000_1000u16;
const B4: u16 = 0b0000_0000_0001_0000u16;
const B5: u16 = 0b0000_0000_0010_0000u16;
const B6: u16 = 0b0000_0000_0100_0000u16;
const B7: u16 = 0b0000_0000_1000_0000u16;
const B8: u16 = 0b0000_0001_0000_0000u16;
const B9: u16 = 0b0000_0010_0000_0000u16;
const B13: u16 = B1 | B3;
const B23: u16 = B2 | B3;
const B123: u16 = B1 | B2 | B3;
const B1235: u16 = B1 | B2 | B3 | B5;
const B1236: u16 = B1 | B2 | B3 | B6;
const B12356: u16 = B1 | B2 | B3 | B5 | B6;
const BA: u16 = B9;
const BB: u16 = B8;
const BC: u16 = B7;
const BD: u16 = B6;
const BE: u16 = B5;
const BF: u16 = B4;
const BG: u16 = B2;
const BH: u16 = B1;
const BI: u16 = B0;
match dpd {
dpd if dpd & B3 == 0 => {
let bit0 = 0;
let bit1 = (dpd & BA) << 1;
let bit2 = (dpd & BB) << 1;
let bit3 = (dpd & BC) << 1;
let bit4 = 0;
let bit5 = dpd & BD;
let bit6 = dpd & BE;
let bit7 = dpd & BF;
let bit8 = 0;
let bit9 = dpd & BG;
let bit10 = dpd & BH;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B123 == B3 => {
let bit0 = 0;
let bit1 = (dpd & BA) << 1;
let bit2 = (dpd & BB) << 1;
let bit3 = (dpd & BC) << 1;
let bit4 = 0;
let bit5 = dpd & BD;
let bit6 = dpd & BE;
let bit7 = dpd & BF;
let bit8 = B0 << 3;
let bit9 = 0;
let bit10 = 0;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B123 == B13 => {
let bit0 = 0;
let bit1 = (dpd & BA) << 1;
let bit2 = (dpd & BB) << 1;
let bit3 = (dpd & BC) << 1;
let bit4 = B0 << 7;
let bit5 = 0;
let bit6 = 0;
let bit7 = dpd & BF;
let bit8 = 0;
let bit9 = (dpd & B6) >> 4;
let bit10 = (dpd & B5) >> 4;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B123 == B23 => {
let bit0 = B0 << 11;
let bit1 = 0;
let bit2 = 0;
let bit3 = (dpd & BC) << 1;
let bit4 = 0;
let bit5 = dpd & BD;
let bit6 = dpd & BE;
let bit7 = dpd & BF;
let bit8 = 0;
let bit9 = (dpd & B9) >> 7;
let bit10 = (dpd & B8) >> 7;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B12356 == B123 => {
let bit0 = B0 << 11;
let bit1 = 0;
let bit2 = 0;
let bit3 = (dpd & BC) << 1;
let bit4 = B0 << 7;
let bit5 = 0;
let bit6 = 0;
let bit7 = dpd & BF;
let bit8 = 0;
let bit9 = (dpd & B9) >> 7;
let bit10 = (dpd & B8) >> 7;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B12356 == B1236 => {
let bit0 = 0;
let bit1 = (dpd & BA) << 1;
let bit2 = (dpd & BB) << 1;
let bit3 = (dpd & BC) << 1;
let bit4 = B0 << 7;
let bit5 = 0;
let bit6 = 0;
let bit7 = dpd & BF;
let bit8 = B0 << 3;
let bit9 = 0;
let bit10 = 0;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B12356 == B1235 => {
let bit0 = B0 << 11;
let bit1 = 0;
let bit2 = 0;
let bit3 = (dpd & BC) << 1;
let bit4 = 0;
let bit5 = (dpd & B9) >> 3;
let bit6 = (dpd & B8) >> 3;
let bit7 = dpd & BF;
let bit8 = B0 << 3;
let bit9 = 0;
let bit10 = 0;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
dpd if dpd & B12356 == B12356 => {
let bit0 = B0 << 11;
let bit1 = 0;
let bit2 = 0;
let bit3 = (dpd & BC) << 1;
let bit4 = B0 << 7;
let bit5 = 0;
let bit6 = 0;
let bit7 = dpd & BF;
let bit8 = B0 << 3;
let bit9 = 0;
let bit10 = 0;
let bit11 = dpd & BI;
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11
}
_ => unreachable!(),
}
}
pub(crate) fn precision_digits(storage_width_bits: usize) -> usize {
9 * storage_width_bits / 32 - 2
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::FixedBinaryBuf;
use std::str;
#[test]
fn precision_32() {
assert_eq!(7, precision_digits(32));
}
#[test]
fn precision_64() {
assert_eq!(16, precision_digits(64));
}
#[test]
fn precision_96() {
assert_eq!(25, precision_digits(96));
}
#[test]
fn precision_128() {
assert_eq!(34, precision_digits(128));
}
#[test]
fn precision_160() {
assert_eq!(43, precision_digits(160));
}
#[test]
fn precision_256() {
assert_eq!(70, precision_digits(256));
}
#[test]
fn encode_decode_dpd_declet_all() {
for b0 in b'0'..=b'9' {
for b1 in b'0'..=b'9' {
for b2 in b'0'..=b'9' {
let digits = [b0, b1, b2];
let digits = str::from_utf8(&digits).expect("digit is valid UTF8");
let mut dpd = [0, 0];
let mut i = 0;
encode_bcd_declet_to_dpd(
encode_ascii_declet_to_bcd([b2, b1, b0]),
&mut dpd,
&mut i,
);
let decoded_ascii =
decode_bcd_declet_to_ascii(decode_dpd_declet_to_bcd(&dpd, &mut i));
assert_eq!([b0, b1, b2], decoded_ascii, "{}", digits);
}
}
}
}
#[test]
fn encode_decode_dpd_declets_across_bytes() {
let digits = "277386910789029981476348954311894750984836542397645";
let mut dpd = [0u8; 64];
let mut i = 0;
let digits_rev = {
let mut digits = Vec::from(digits);
digits.reverse();
digits
};
for ascii in digits_rev.chunks(3) {
let bcd = encode_ascii_declet_to_bcd([ascii[0], ascii[1], ascii[2]]);
encode_bcd_declet_to_dpd(bcd, &mut dpd, &mut i);
}
let mut decoded_digits = Vec::new();
while i > 0 {
let bcd = decode_dpd_declet_to_bcd(&dpd, &mut i);
let ascii = decode_bcd_declet_to_ascii(bcd);
decoded_digits.extend(ascii);
}
assert_eq!(
digits,
str::from_utf8(&decoded_digits).expect("digits are valid UTF8")
);
}
#[test]
fn encode_decode_significand_trailing_digits() {
let digits = "129054729387659";
let mut decimal = FixedBinaryBuf::<8, i32>::ZERO;
encode_significand_trailing_digits(&mut decimal, [digits.as_bytes()]);
let decoded = decode_significand_trailing_declets(&decimal)
.flatten()
.map(|b| b as char)
.collect::<String>();
assert_eq!(digits, decoded);
}
#[test]
fn encode_decode_significand_trailing_digits_repeat() {
for digit in b'0'..=b'9' {
let mut decimal = FixedBinaryBuf::<8, i32>::ZERO;
encode_significand_trailing_digits_repeat(&mut decimal, digit);
assert!(decode_significand_trailing_declets(&decimal)
.flatten()
.all(|b| b == digit));
}
}
}