use crate::storage::Kind;
use numeric::precision::{Fixed, Places};
use std::cmp::Ordering;
use zarch::wide::U256;
pub const MAX_DIGITS: usize = 31;
pub fn fixed(negative: bool, magnitude: U256, places: Places) -> Fixed {
Fixed { negative: negative && !magnitude.is_zero(), magnitude, places }
}
pub fn pow10(n: u32) -> U256 {
U256::pow10(n)
}
pub fn align(value: &Fixed, scale: u32, rounded: bool) -> Option<U256> {
let from = value.places.dec;
if scale >= from {
return value.magnitude.checked_mul(pow10(scale - from));
}
let (q, r) = value.magnitude.div_rem(pow10(from - scale));
let half = pow10(from - scale - 1).checked_mul(U256::from_u128(5))?;
Some(if rounded && r >= half { q + U256::from_u128(1) } else { q })
}
pub fn compare_fixed(a: &Fixed, b: &Fixed) -> Ordering {
let dec = a.places.dec.max(b.places.dec);
let (ma, mb) = (align(a, dec, false).unwrap_or_default(), align(b, dec, false).unwrap_or_default());
match (a.negative, b.negative) {
(false, false) => ma.cmp(&mb),
(true, true) => mb.cmp(&ma),
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
}
}
pub fn places_of(kind: Kind) -> Places {
let (digits, scale) = kind.digits_scale().unwrap_or((0, 0));
Places::new(digits.saturating_sub(scale), scale)
}
pub fn scaled_up(f: Fixed, k: u32) -> Fixed {
if k == 0 {
return f;
}
fixed(f.negative, f.magnitude.checked_mul(pow10(k)).unwrap_or_default(), Places::new(f.places.int + k, f.places.dec))
}
pub fn scaled_down(f: Fixed, k: u32) -> Fixed {
Fixed { places: Places::new(f.places.int.saturating_sub(k), f.places.dec + k), ..f }
}
pub fn zoned_digits(magnitude: u128, digits: usize, sign_zone: u8) -> Vec<u8> {
let mut out = vec![0xF0u8; digits];
let mut m = magnitude;
for b in out.iter_mut().rev() {
*b = 0xF0 | (m % 10) as u8;
m /= 10;
}
if let Some(last) = out.last_mut() {
*last = (sign_zone << 4) | (*last & 0x0F);
}
out
}