#![doc = include_str ! ("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(non_ascii_idents)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused)]
#![allow(dead_code)]
#![warn(clippy::cast_possible_truncation)]
#![warn(clippy::cast_precision_loss)]
#![warn(clippy::cognitive_complexity)]
#![warn(clippy::decimal_literal_representation)]
#![warn(clippy::enum_glob_use)]
#![warn(clippy::equatable_if_let)]
#![warn(clippy::fallible_impl_from)]
#![warn(clippy::if_not_else)]
#![warn(clippy::if_then_some_else_none)]
#![warn(clippy::implicit_clone)]
#![warn(clippy::integer_division)]
#![warn(clippy::manual_assert)]
#![warn(clippy::match_same_arms)]
#![warn(clippy::missing_const_for_fn)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
#![warn(clippy::multiple_crate_versions)]
#![warn(clippy::must_use_candidate)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::str_to_string)]
#![warn(clippy::string_to_string)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![warn(clippy::unicode_not_nfc)]
#![warn(clippy::unimplemented)]
#![warn(clippy::unseparated_literal_suffix)]
#![warn(clippy::unused_self)]
#![warn(clippy::unwrap_in_result)]
#![warn(clippy::use_self)]
#![warn(clippy::used_underscore_binding)]
#![warn(clippy::wildcard_imports)]
use core::{cmp::Ordering, ops::Neg};
pub use parser::{str_to_dec, ParseDecimalError};
pub use powers_of_ten::{checked_mul_pow_ten, mul_pow_ten, ten_pow};
pub use rounding::{
i128_div_rounded, i128_mul_div_ten_pow_rounded, i128_shifted_div_rounded,
Round, RoundingMode,
};
mod parser;
mod powers_of_ten;
mod rounding;
pub const MAX_N_FRAC_DIGITS: u8 = 18;
#[doc(hidden)]
#[inline]
pub fn adjust_coeffs(x: i128, p: u8, y: i128, q: u8) -> (i128, i128) {
match p.cmp(&q) {
Ordering::Equal => (x, y),
Ordering::Greater => (x, mul_pow_ten(y, p - q)),
Ordering::Less => (mul_pow_ten(x, q - p), y),
}
}
#[doc(hidden)]
#[inline]
pub fn checked_adjust_coeffs(
x: i128,
p: u8,
y: i128,
q: u8,
) -> (Option<i128>, Option<i128>) {
match p.cmp(&q) {
Ordering::Equal => (Some(x), Some(y)),
Ordering::Greater => (Some(x), checked_mul_pow_ten(y, p - q)),
Ordering::Less => (checked_mul_pow_ten(x, q - p), Some(y)),
}
}
#[doc(hidden)]
#[inline]
pub fn i128_div_mod_floor(x: i128, y: i128) -> (i128, i128) {
let (q, r) = (x / y, x % y);
if (r > 0 && y < 0) || (r < 0 && y > 0) {
(q - 1, r + y)
} else {
(q, r)
}
}
#[allow(clippy::unusual_byte_groupings)]
#[doc(hidden)]
#[inline]
pub const fn u8(val: u8) -> u32 {
let val = val as u32;
const C1: u32 = 0b11_00000000 - 10; const C2: u32 = 0b10_00000000 - 100;
((val + C1) & (val + C2)) >> 8
}
#[allow(clippy::unusual_byte_groupings)]
#[doc(hidden)]
#[inline]
const fn less_than_5(val: u32) -> u32 {
const C1: u32 = 0b011_00000000000000000 - 10; const C2: u32 = 0b100_00000000000000000 - 100; const C3: u32 = 0b111_00000000000000000 - 1000; const C4: u32 = 0b100_00000000000000000 - 10000;
(((val + C1) & (val + C2)) ^ ((val + C3) & (val + C4))) >> 17
}
#[doc(hidden)]
#[inline]
pub const fn u16(val: u16) -> u32 {
less_than_5(val as u32)
}
#[doc(hidden)]
#[inline]
pub const fn u32(mut val: u32) -> u32 {
let mut log = 0;
if val >= 100_000 {
val /= 100_000;
log += 5;
}
log + less_than_5(val)
}
#[doc(hidden)]
#[inline]
pub const fn u64(mut val: u64) -> u32 {
let mut log = 0;
if val >= 10_000_000_000 {
val /= 10_000_000_000;
log += 10;
}
if val >= 100_000 {
val /= 100_000;
log += 5;
}
log + less_than_5(val as u32)
}
#[doc(hidden)]
#[inline]
pub const fn u128(mut val: u128) -> u32 {
let mut log = 0;
if val >= 100_000_000_000_000_000_000_000_000_000_000 {
val /= 100_000_000_000_000_000_000_000_000_000_000;
log += 32;
return log + u32(val as u32);
}
if val >= 10_000_000_000_000_000 {
val /= 10_000_000_000_000_000;
log += 16;
}
log + u64(val as u64)
}
#[doc(hidden)]
#[inline]
pub const fn i128_magnitude(i: i128) -> u8 {
u128(i.unsigned_abs()) as u8
}
fn u128_msb(mut i: u128) -> u8 {
debug_assert_ne!(i, 0);
const IDX_MAP: [u8; 16] = [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4];
let mut n: u8 = 0;
if i & 0xffffffffffffffff0000000000000000_u128 != 0 {
n = 64;
i >>= 64;
};
if i & 0x0000000000000000ffffffff00000000_u128 != 0 {
n += 32;
i >>= 32;
};
if i & 0x000000000000000000000000ffff0000_u128 != 0 {
n += 16;
i >>= 16;
};
if i & 0x0000000000000000000000000000ff00_u128 != 0 {
n += 8;
i >>= 8;
};
if i & 0x000000000000000000000000000000f0_u128 != 0 {
n += 4;
i >>= 4;
};
n + IDX_MAP[i as usize] - 1
}
#[inline(always)]
fn u128_hi(u: u128) -> u128 {
u >> 64
}
#[inline(always)]
fn u128_lo(u: u128) -> u128 {
u & 0xffffffffffffffff
}
#[inline(always)]
fn u128_mul_u128(x: u128, y: u128) -> (u128, u128) {
let xh = u128_hi(x);
let xl = u128_lo(x);
let yh = u128_hi(y);
let yl = u128_lo(y);
let mut t = xl * yl;
let mut rl = u128_lo(t);
t = xl * yh + u128_hi(t);
let mut rh = u128_hi(t);
t = xh * yl + u128_lo(t);
rl += u128_lo(t) << 64;
rh += xh * yh + u128_hi(t);
(rh, rl)
}
#[inline(always)]
fn u256_idiv_u64(xh: &mut u128, xl: &mut u128, y: u64) -> u128 {
if y == 1 {
return 0;
}
let y = y as u128;
let mut th = u128_hi(*xh);
let mut r = th % y;
let mut tl = (r << 64) + u128_lo(*xh);
*xh = ((th / y) << 64) + tl / y;
r = tl % y;
th = (r << 64) + u128_hi(*xl);
r = th % y;
tl = (r << 64) + u128_lo(*xl);
*xl = ((th / y) << 64) + tl / y;
tl % y
}
#[inline(always)]
fn u256_idiv_u128_special(xh: &mut u128, xl: &mut u128, mut y: u128) -> u128 {
debug_assert!(*xh < y);
const B: u128 = 1 << 64;
let n_bits = 127 - u128_msb(y);
y <<= n_bits;
let yn1 = u128_hi(y);
let yn0 = u128_lo(y);
let sh = if n_bits == 0 {
0
} else {
*xl >> (128 - n_bits)
};
let xn32 = *xh << n_bits | sh;
let xn10 = *xl << n_bits;
let xn1 = u128_hi(xn10);
let xn0 = u128_lo(xn10);
let mut q1 = xn32 / yn1;
let mut rhat = xn32 % yn1;
while q1 >= B || q1 * yn0 > rhat * B + xn1 {
q1 -= 1;
rhat += yn1;
if rhat >= B {
break;
}
}
let t = xn32
.wrapping_mul(B)
.wrapping_add(xn1)
.wrapping_sub(q1.wrapping_mul(y));
let mut q0 = t / yn1;
rhat = t % yn1;
while q0 >= B || q0 * yn0 > rhat * B + xn0 {
q0 -= 1;
rhat += yn1;
if rhat >= B {
break;
}
}
*xh = 0;
*xl = q1 * B + q0;
(t.wrapping_mul(B)
.wrapping_add(xn0)
.wrapping_sub(q0.wrapping_mul(y)))
>> n_bits
}
#[inline(always)]
fn u256_idiv_u128(xh: &mut u128, xl: &mut u128, y: u128) -> u128 {
if u128_hi(y) == 0 {
return u256_idiv_u64(xh, xl, u128_lo(y) as u64);
}
if *xh < y {
return u256_idiv_u128_special(xh, xl, y);
}
let mut t = *xh % y;
let r = u256_idiv_u128_special(&mut t, xl, y);
*xh /= y;
r
}
#[doc(hidden)]
pub fn i128_shifted_div_mod_floor(
x: i128,
p: u8,
y: i128,
) -> Option<(i128, i128)> {
let (mut xh, mut xl) = u128_mul_u128(x.unsigned_abs(), ten_pow(p) as u128);
let r = u256_idiv_u128(&mut xh, &mut xl, y.unsigned_abs());
if xh != 0 || xl > i128::MAX as u128 {
return None;
}
let mut q = xl as i128;
let mut r = r as i128;
if x.is_negative() {
if y.is_negative() {
r = r.neg();
} else {
q = q.neg() - 1;
r = y - r;
}
} else if y.is_negative() {
q = q.neg() - 1;
r -= y;
}
Some((q, r))
}
#[doc(hidden)]
pub fn i256_div_mod_floor(x1: i128, x2: i128, y: i128) -> Option<(i128, i128)> {
debug_assert!(y > 0);
let (mut xh, mut xl) = u128_mul_u128(x1.unsigned_abs(), x2.unsigned_abs());
let r = u256_idiv_u128(&mut xh, &mut xl, y.unsigned_abs());
if xh != 0 || xl > i128::MAX as u128 {
return None;
}
let mut q = xl as i128;
let mut r = r as i128;
if x1.is_negative() != x2.is_negative() {
q = q.neg() - 1;
r = y - r;
}
Some((q, r))
}