1#![doc = include_str ! ("../README.md")]
11#![cfg_attr(not(feature = "std"), no_std)]
12#![allow(dead_code)]
14#![deny(non_ascii_idents)]
15#![deny(unsafe_code)]
16#![warn(missing_debug_implementations)]
17#![warn(missing_docs)]
18#![warn(trivial_casts, trivial_numeric_casts)]
19#![warn(unused)]
20#![warn(clippy::cast_possible_truncation)]
22#![warn(clippy::cast_possible_wrap)]
23#![warn(clippy::cast_precision_loss)]
24#![warn(clippy::cast_sign_loss)]
25#![warn(clippy::cognitive_complexity)]
26#![warn(clippy::enum_glob_use)]
27#![warn(clippy::equatable_if_let)]
28#![warn(clippy::fallible_impl_from)]
29#![warn(clippy::if_not_else)]
30#![warn(clippy::if_then_some_else_none)]
31#![warn(clippy::implicit_clone)]
32#![warn(clippy::integer_division)]
33#![warn(clippy::manual_assert)]
34#![warn(clippy::match_same_arms)]
35#![warn(clippy::mismatching_type_param_order)]
36#![warn(clippy::missing_const_for_fn)]
37#![warn(clippy::missing_errors_doc)]
38#![warn(clippy::missing_panics_doc)]
39#![warn(clippy::multiple_crate_versions)]
40#![warn(clippy::multiple_inherent_impl)]
41#![warn(clippy::must_use_candidate)]
42#![warn(clippy::needless_pass_by_value)]
43#![warn(clippy::print_stderr)]
44#![warn(clippy::print_stdout)]
45#![warn(clippy::semicolon_if_nothing_returned)]
46#![warn(clippy::str_to_string)]
47#![warn(clippy::undocumented_unsafe_blocks)]
48#![warn(clippy::unicode_not_nfc)]
49#![warn(clippy::unimplemented)]
50#![warn(clippy::unseparated_literal_suffix)]
51#![warn(clippy::unused_self)]
52#![warn(clippy::unwrap_in_result)]
53#![warn(clippy::use_self)]
54#![warn(clippy::used_underscore_binding)]
55#![warn(clippy::wildcard_imports)]
56
57use core::{cmp::Ordering, ops::Neg};
58
59pub use parser::{str_to_dec, ParseDecimalError};
60pub use powers_of_ten::{checked_mul_pow_ten, mul_pow_ten, ten_pow};
61pub use rounding::{
62 i128_div_rounded, i128_mul_div_ten_pow_rounded, i128_shifted_div_rounded,
63 Round, RoundingMode,
64};
65
66mod parser;
67mod powers_of_ten;
68mod rounding;
69
70pub const MAX_N_FRAC_DIGITS: u8 = 18;
72
73#[doc(hidden)]
74#[inline]
75#[must_use]
76pub fn adjust_coeffs(x: i128, p: u8, y: i128, q: u8) -> (i128, i128) {
77 match p.cmp(&q) {
78 Ordering::Equal => (x, y),
79 Ordering::Greater => (x, mul_pow_ten(y, p - q)),
80 Ordering::Less => (mul_pow_ten(x, q - p), y),
81 }
82}
83
84#[doc(hidden)]
85#[inline]
86#[must_use]
87pub fn checked_adjust_coeffs(
88 x: i128,
89 p: u8,
90 y: i128,
91 q: u8,
92) -> (Option<i128>, Option<i128>) {
93 match p.cmp(&q) {
94 Ordering::Equal => (Some(x), Some(y)),
95 Ordering::Greater => (Some(x), checked_mul_pow_ten(y, p - q)),
96 Ordering::Less => (checked_mul_pow_ten(x, q - p), Some(y)),
97 }
98}
99
100#[doc(hidden)]
104#[inline]
105#[must_use]
106#[allow(clippy::integer_division)]
107pub const fn i128_div_mod_floor(x: i128, y: i128) -> (i128, i128) {
108 let (q, r) = (x / y, x % y);
109 if (r > 0 && y < 0) || (r < 0 && y > 0) {
110 (q - 1, r + y)
111 } else {
112 (q, r)
113 }
114}
115
116#[allow(clippy::unusual_byte_groupings)]
121#[doc(hidden)]
122#[inline]
123#[must_use]
124pub const fn u8(val: u8) -> u32 {
125 let val = val as u32;
126
127 const C1: u32 = 0b11_00000000 - 10; const C2: u32 = 0b10_00000000 - 100; ((val + C1) & (val + C2)) >> 8
143}
144
145#[allow(clippy::unusual_byte_groupings)]
147#[doc(hidden)]
148#[inline]
149const fn less_than_5(val: u32) -> u32 {
150 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
166}
167
168#[doc(hidden)]
170#[inline]
171#[must_use]
172pub const fn u16(val: u16) -> u32 {
173 less_than_5(val as u32)
174}
175
176#[doc(hidden)]
178#[inline]
179#[must_use]
180pub const fn u32(mut val: u32) -> u32 {
181 let mut log = 0;
182 if val >= 100_000 {
183 val /= 100_000;
184 log += 5;
185 }
186 log + less_than_5(val)
187}
188
189#[doc(hidden)]
191#[inline]
192#[must_use]
193#[allow(clippy::cast_possible_truncation)]
194pub const fn u64(mut val: u64) -> u32 {
195 let mut log = 0;
196 if val >= 10_000_000_000 {
197 val /= 10_000_000_000;
198 log += 10;
199 }
200 if val >= 100_000 {
201 val /= 100_000;
202 log += 5;
203 }
204 log + less_than_5(val as u32)
205}
206
207#[doc(hidden)]
209#[inline]
210#[must_use]
211#[allow(clippy::cast_possible_truncation)]
212pub const fn u128(mut val: u128) -> u32 {
213 let mut log = 0;
214 if val >= 100_000_000_000_000_000_000_000_000_000_000 {
215 val /= 100_000_000_000_000_000_000_000_000_000_000;
216 log += 32;
217 return log + u32(val as u32);
218 }
219 if val >= 10_000_000_000_000_000 {
220 val /= 10_000_000_000_000_000;
221 log += 16;
222 }
223 log + u64(val as u64)
224}
225
226#[doc(hidden)]
229#[inline]
230#[must_use]
231#[allow(clippy::cast_possible_truncation)]
232pub const fn i128_magnitude(i: i128) -> u8 {
233 u128(i.unsigned_abs()) as u8
236}
237
238fn u128_msb(mut i: u128) -> u8 {
241 debug_assert_ne!(i, 0);
242 const IDX_MAP: [u8; 16] =
243 [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4];
244 let mut n: u8 = 0;
245 if i & 0xffffffffffffffff0000000000000000_u128 != 0 {
246 n = 64;
247 i >>= 64;
248 };
249 if i & 0x0000000000000000ffffffff00000000_u128 != 0 {
250 n += 32;
251 i >>= 32;
252 };
253 if i & 0x000000000000000000000000ffff0000_u128 != 0 {
254 n += 16;
255 i >>= 16;
256 };
257 if i & 0x0000000000000000000000000000ff00_u128 != 0 {
258 n += 8;
259 i >>= 8;
260 };
261 if i & 0x000000000000000000000000000000f0_u128 != 0 {
262 n += 4;
263 i >>= 4;
264 };
265 n + IDX_MAP[i as usize] - 1
266}
267
268#[inline(always)]
269const fn u128_hi(u: u128) -> u128 {
270 u >> 64
271}
272
273#[inline(always)]
274const fn u128_lo(u: u128) -> u128 {
275 u & 0xffffffffffffffff
276}
277
278#[inline(always)]
279const fn u128_mul_u128(x: u128, y: u128) -> (u128, u128) {
280 let xh = u128_hi(x);
281 let xl = u128_lo(x);
282 let yh = u128_hi(y);
283 let yl = u128_lo(y);
284 let mut t = xl * yl;
285 let mut rl = u128_lo(t);
286 t = xl * yh + u128_hi(t);
287 let mut rh = u128_hi(t);
288 t = xh * yl + u128_lo(t);
289 rl += u128_lo(t) << 64;
290 rh += xh * yh + u128_hi(t);
291 (rh, rl)
292}
293
294#[inline(always)]
299#[allow(clippy::integer_division)]
300#[allow(clippy::missing_const_for_fn)] fn u256_idiv_u64(xh: &mut u128, xl: &mut u128, y: u64) -> u128 {
302 if y == 1 {
303 return 0;
304 }
305 let y = y as u128;
306 let mut th = u128_hi(*xh);
307 let mut r = th % y;
308 let mut tl = (r << 64) + u128_lo(*xh);
309 *xh = ((th / y) << 64) + tl / y;
310 r = tl % y;
311 th = (r << 64) + u128_hi(*xl);
312 r = th % y;
313 tl = (r << 64) + u128_lo(*xl);
314 *xl = ((th / y) << 64) + tl / y;
315 tl % y
316}
317
318#[inline(always)]
328#[allow(clippy::integer_division)]
329fn u256_idiv_u128_special(xh: &mut u128, xl: &mut u128, mut y: u128) -> u128 {
330 debug_assert!(*xh < y);
331 const B: u128 = 1 << 64;
332 let n_bits = 127 - u128_msb(y);
335 y <<= n_bits;
336 let yn1 = u128_hi(y);
337 let yn0 = u128_lo(y);
338 let sh = if n_bits == 0 {
340 0
341 } else {
342 *xl >> (128 - n_bits)
343 };
344 let xn32 = *xh << n_bits | sh;
345 let xn10 = *xl << n_bits;
346 let xn1 = u128_hi(xn10);
347 let xn0 = u128_lo(xn10);
348 let mut q1 = xn32 / yn1;
349 let mut rhat = xn32 % yn1;
350 while q1 >= B || q1 * yn0 > rhat * B + xn1 {
355 q1 -= 1;
356 rhat += yn1;
357 if rhat >= B {
358 break;
359 }
360 }
361 let t = xn32
374 .wrapping_mul(B)
375 .wrapping_add(xn1)
376 .wrapping_sub(q1.wrapping_mul(y));
377 let mut q0 = t / yn1;
378 rhat = t % yn1;
379 while q0 >= B || q0 * yn0 > rhat * B + xn0 {
380 q0 -= 1;
381 rhat += yn1;
382 if rhat >= B {
383 break;
384 }
385 }
386 *xh = 0;
388 *xl = q1 * B + q0;
389 (t.wrapping_mul(B)
391 .wrapping_add(xn0)
392 .wrapping_sub(q0.wrapping_mul(y)))
393 >> n_bits
394}
395
396#[inline(always)]
398#[allow(clippy::cast_possible_truncation)]
399fn u256_idiv_u128(xh: &mut u128, xl: &mut u128, y: u128) -> u128 {
400 if u128_hi(y) == 0 {
401 return u256_idiv_u64(xh, xl, u128_lo(y) as u64);
402 }
403 if *xh < y {
404 return u256_idiv_u128_special(xh, xl, y);
405 }
406 let mut t = *xh % y;
407 let r = u256_idiv_u128_special(&mut t, xl, y);
408 *xh /= y;
409 r
410}
411
412#[doc(hidden)]
417#[must_use]
418#[allow(clippy::cast_sign_loss)]
419#[allow(clippy::cast_possible_wrap)]
420pub fn i128_shifted_div_mod_floor(
421 x: i128,
422 p: u8,
423 y: i128,
424) -> Option<(i128, i128)> {
425 let (mut xh, mut xl) =
426 u128_mul_u128(x.unsigned_abs(), ten_pow(p) as u128);
427 let r = u256_idiv_u128(&mut xh, &mut xl, y.unsigned_abs());
428 if xh != 0 || xl > i128::MAX as u128 {
429 return None;
430 }
431 let mut q = xl as i128;
433 let mut r = r as i128;
435 if x.is_negative() {
436 if y.is_negative() {
437 r = r.neg();
438 } else {
439 q = q.neg() - 1;
440 r = y - r;
441 }
442 } else if y.is_negative() {
443 q = q.neg() - 1;
444 r -= y;
445 }
446 Some((q, r))
447}
448
449#[doc(hidden)]
454#[must_use]
455#[allow(clippy::cast_possible_wrap)]
456pub fn i256_div_mod_floor(
457 x1: i128,
458 x2: i128,
459 y: i128,
460) -> Option<(i128, i128)> {
461 debug_assert!(y > 0);
462 let (mut xh, mut xl) =
463 u128_mul_u128(x1.unsigned_abs(), x2.unsigned_abs());
464 let r = u256_idiv_u128(&mut xh, &mut xl, y.unsigned_abs());
465 if xh != 0 || xl > i128::MAX as u128 {
466 return None;
467 }
468 let mut q = xl as i128;
470 let mut r = r as i128;
472 if x1.is_negative() != x2.is_negative() {
473 q = q.neg() - 1;
474 r = y - r;
475 }
476 Some((q, r))
477}