use ordecimal::Decimal;
fn bits_string(bytes: &[u8]) -> String {
bytes
.iter()
.flat_map(|b| {
(0..8)
.rev()
.map(move |i| if (b >> i) & 1 == 1 { '1' } else { '0' })
})
.collect()
}
fn bits_to_bytes(bits: &str) -> Vec<u8> {
let padded_len = bits.len().div_ceil(8) * 8;
let mut bytes = Vec::with_capacity(padded_len / 8);
let bits_iter = bits.chars().chain(std::iter::repeat('0'));
for chunk in bits_iter.take(padded_len).collect::<Vec<_>>().chunks(8) {
let mut byte = 0u8;
for (i, &bit) in chunk.iter().enumerate() {
if bit == '1' {
byte |= 1 << (7 - i);
}
}
bytes.push(byte);
}
bytes
}
#[test]
fn test_exact_bits_paper_example_1_neg_103_2() {
let expected = bits_to_bytes("00001111000111100100");
let value: Decimal = "-103.2".parse().unwrap();
assert_eq!(value.as_bytes(), &expected, "-103.2 bit pattern mismatch");
}
#[test]
fn test_exact_bits_paper_example_2_neg_0_0405() {
let expected = bits_to_bytes("001100001011110110110");
let value: Decimal = "-0.0405".parse().unwrap();
assert_eq!(value.as_bytes(), &expected, "-0.0405 bit pattern mismatch");
}
#[test]
fn test_exact_bits_paper_example_3_pos_0_707106() {
let expected = bits_to_bytes("100100111000100011100001111");
let value: Decimal = "0.707106".parse().unwrap();
assert_eq!(value.as_bytes(), &expected, "0.707106 bit pattern mismatch");
}
#[test]
fn test_exact_bits_paper_example_4_pos_4005012345() {
let expected = bits_to_bytes("1011100110100000000010100000011000101011001");
let value: Decimal = "4005012345".parse().unwrap();
assert_eq!(
value.as_bytes(),
&expected,
"4005012345 bit pattern mismatch"
);
}
#[test]
fn test_exact_bits_small_integers() {
let cases = [
("1", "101000001"), ("2", "101000010"), ("3", "101000011"), ("4", "101000100"), ("5", "101000101"), ("6", "101000110"), ("7", "101000111"), ("8", "101001000"), ("9", "101001001"), ("10", "101010001"), ];
for (num, expected_bits) in &cases {
let expected = bits_to_bytes(expected_bits);
let value: Decimal = num.parse().unwrap();
assert_eq!(
value.as_bytes(),
&expected,
"integer {} bit pattern mismatch: got {}, expected {}",
num,
bits_string(value.as_bytes()),
expected_bits
);
}
}
#[test]
fn test_exact_bits_small_negative_integers() {
let cases = [
("-1", "000111001"), ("-2", "000111000"), ("-3", "000110111"), ("-4", "000110110"), ("-5", "000110101"), ("-6", "000110100"), ("-7", "000110011"), ("-8", "000110010"), ("-9", "000110001"), ("-10", "000101001"), ];
for (num, expected_bits) in &cases {
let expected = bits_to_bytes(expected_bits);
let value: Decimal = num.parse().unwrap();
assert_eq!(
value.as_bytes(),
&expected,
"integer {} bit pattern mismatch: got {}, expected {}",
num,
bits_string(value.as_bytes()),
expected_bits
);
}
}
#[test]
fn test_roundtrip_paper_examples() {
let test_cases = ["-103.2", "-0.0405", "0.707106", "4005012345"];
for case in &test_cases {
let original: Decimal = case.parse().unwrap();
let encoded = original.as_bytes();
let decoded = Decimal::from_bytes(encoded).unwrap();
let orig_decoded = original.decode().unwrap();
let dec_decoded = decoded.decode().unwrap();
assert_eq!(
orig_decoded.positive, dec_decoded.positive,
"Sign mismatch for {}",
case
);
assert_eq!(
orig_decoded.exponent_positive, dec_decoded.exponent_positive,
"Exp sign mismatch for {}",
case
);
assert_eq!(
orig_decoded.exponent, dec_decoded.exponent,
"Exponent mismatch for {}",
case
);
assert!(
dec_decoded
.significand
.starts_with(&orig_decoded.significand),
"Significand mismatch for {}: {:?} vs {:?}",
case,
orig_decoded.significand,
dec_decoded.significand
);
}
}
#[test]
fn test_order_preservation_detailed() {
let test_numbers = [
"-1000", "-100", "-10", "-5", "-1", "-0.5", "-0.1", "-0.01", "0", "0.01", "0.1", "0.5",
"1", "5", "10", "100", "1000",
];
let encoded_pairs: Vec<(&str, Decimal)> = test_numbers
.iter()
.map(|num| (*num, num.parse::<Decimal>().unwrap()))
.collect();
for i in 1..encoded_pairs.len() {
let (num1, dec1) = &encoded_pairs[i - 1];
let (num2, dec2) = &encoded_pairs[i];
assert!(
dec1 < dec2,
"Order not preserved: {} < {} failed\n {}\n {}",
num1,
num2,
bits_string(dec1.as_bytes()),
bits_string(dec2.as_bytes())
);
}
}
#[test]
fn test_special_values_encoding() {
assert_eq!(
Decimal::neg_infinity().as_bytes(),
&[0b0000_0000],
"-INF should be 00"
);
assert_eq!(
Decimal::zero().as_bytes(),
&[0b1000_0000],
"+0 should be 10"
);
assert_eq!(
Decimal::infinity().as_bytes(),
&[0b1100_0000],
"+INF should be 11"
);
assert_eq!(
Decimal::nan().as_bytes(),
&[0b1110_0000],
"NaN should be 111"
);
}
#[test]
fn test_special_values_ordering() {
let neg_inf = Decimal::neg_infinity();
let zero = Decimal::zero();
let pos_inf = Decimal::infinity();
assert!(neg_inf < zero);
assert!(zero < pos_inf);
assert!(neg_inf < pos_inf);
let neg1: Decimal = "-1".parse().unwrap();
let pos1: Decimal = "1".parse().unwrap();
assert!(neg_inf < neg1);
assert!(neg1 < zero);
assert!(zero < pos1);
assert!(pos1 < pos_inf);
}
#[test]
fn test_zero_encoding() {
let zero: Decimal = "0".parse().unwrap();
assert_eq!(
zero.as_bytes(),
&[0b1000_0000],
"Zero should be encoded as 10 (padded)"
);
}
#[test]
fn test_encoding_size_integers_1_to_9() {
for i in 1..=9 {
let value: Decimal = i.to_string().parse().unwrap();
assert_eq!(
value.as_bytes().len(),
2,
"Integer {} should encode to 2 bytes (9 bits)",
i
);
}
}