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::string_to_string)]
48#![warn(clippy::undocumented_unsafe_blocks)]
49#![warn(clippy::unicode_not_nfc)]
50#![warn(clippy::unimplemented)]
51#![warn(clippy::unseparated_literal_suffix)]
52#![warn(clippy::unused_self)]
53#![warn(clippy::unwrap_in_result)]
54#![warn(clippy::use_self)]
55#![warn(clippy::used_underscore_binding)]
56#![warn(clippy::wildcard_imports)]
57
58use core::{cmp::Ordering, ops::Neg};
59
60pub use parser::{str_to_dec, ParseDecimalError};
61pub use powers_of_ten::{checked_mul_pow_ten, mul_pow_ten, ten_pow};
62pub use rounding::{
63 i128_div_rounded, i128_mul_div_ten_pow_rounded, i128_shifted_div_rounded,
64 Round, RoundingMode,
65};
66
67mod parser;
68mod powers_of_ten;
69mod rounding;
70
71pub const MAX_N_FRAC_DIGITS: u8 = 18;
73
74#[doc(hidden)]
75#[inline]
76#[must_use]
77pub fn adjust_coeffs(x: i128, p: u8, y: i128, q: u8) -> (i128, i128) {
78 match p.cmp(&q) {
79 Ordering::Equal => (x, y),
80 Ordering::Greater => (x, mul_pow_ten(y, p - q)),
81 Ordering::Less => (mul_pow_ten(x, q - p), y),
82 }
83}
84
85#[doc(hidden)]
86#[inline]
87#[must_use]
88pub fn checked_adjust_coeffs(
89 x: i128,
90 p: u8,
91 y: i128,
92 q: u8,
93) -> (Option<i128>, Option<i128>) {
94 match p.cmp(&q) {
95 Ordering::Equal => (Some(x), Some(y)),
96 Ordering::Greater => (Some(x), checked_mul_pow_ten(y, p - q)),
97 Ordering::Less => (checked_mul_pow_ten(x, q - p), Some(y)),
98 }
99}
100
101#[doc(hidden)]
105#[inline]
106#[must_use]
107#[allow(clippy::integer_division)]
108pub const fn i128_div_mod_floor(x: i128, y: i128) -> (i128, i128) {
109 let (q, r) = (x / y, x % y);
110 if (r > 0 && y < 0) || (r < 0 && y > 0) {
111 (q - 1, r + y)
112 } else {
113 (q, r)
114 }
115}
116
117#[allow(clippy::unusual_byte_groupings)]
122#[doc(hidden)]
123#[inline]
124#[must_use]
125pub const fn u8(val: u8) -> u32 {
126 let val = val as u32;
127
128 const C1: u32 = 0b11_00000000 - 10; const C2: u32 = 0b10_00000000 - 100; ((val + C1) & (val + C2)) >> 8
144}
145
146#[allow(clippy::unusual_byte_groupings)]
148#[doc(hidden)]
149#[inline]
150const fn less_than_5(val: u32) -> u32 {
151 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
167}
168
169#[doc(hidden)]
171#[inline]
172#[must_use]
173pub const fn u16(val: u16) -> u32 {
174 less_than_5(val as u32)
175}
176
177#[doc(hidden)]
179#[inline]
180#[must_use]
181pub const fn u32(mut val: u32) -> u32 {
182 let mut log = 0;
183 if val >= 100_000 {
184 val /= 100_000;
185 log += 5;
186 }
187 log + less_than_5(val)
188}
189
190#[doc(hidden)]
192#[inline]
193#[must_use]
194#[allow(clippy::cast_possible_truncation)]
195pub const fn u64(mut val: u64) -> u32 {
196 let mut log = 0;
197 if val >= 10_000_000_000 {
198 val /= 10_000_000_000;
199 log += 10;
200 }
201 if val >= 100_000 {
202 val /= 100_000;
203 log += 5;
204 }
205 log + less_than_5(val as u32)
206}
207
208#[doc(hidden)]
210#[inline]
211#[must_use]
212#[allow(clippy::cast_possible_truncation)]
213pub const fn u128(mut val: u128) -> u32 {
214 let mut log = 0;
215 if val >= 100_000_000_000_000_000_000_000_000_000_000 {
216 val /= 100_000_000_000_000_000_000_000_000_000_000;
217 log += 32;
218 return log + u32(val as u32);
219 }
220 if val >= 10_000_000_000_000_000 {
221 val /= 10_000_000_000_000_000;
222 log += 16;
223 }
224 log + u64(val as u64)
225}
226
227#[doc(hidden)]
230#[inline]
231#[must_use]
232#[allow(clippy::cast_possible_truncation)]
233pub const fn i128_magnitude(i: i128) -> u8 {
234 u128(i.unsigned_abs()) as u8
237}
238
239fn u128_msb(mut i: u128) -> u8 {
242 debug_assert_ne!(i, 0);
243 const IDX_MAP: [u8; 16] =
244 [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4];
245 let mut n: u8 = 0;
246 if i & 0xffffffffffffffff0000000000000000_u128 != 0 {
247 n = 64;
248 i >>= 64;
249 };
250 if i & 0x0000000000000000ffffffff00000000_u128 != 0 {
251 n += 32;
252 i >>= 32;
253 };
254 if i & 0x000000000000000000000000ffff0000_u128 != 0 {
255 n += 16;
256 i >>= 16;
257 };
258 if i & 0x0000000000000000000000000000ff00_u128 != 0 {
259 n += 8;
260 i >>= 8;
261 };
262 if i & 0x000000000000000000000000000000f0_u128 != 0 {
263 n += 4;
264 i >>= 4;
265 };
266 n + IDX_MAP[i as usize] - 1
267}
268
269#[inline(always)]
270const fn u128_hi(u: u128) -> u128 {
271 u >> 64
272}
273
274#[inline(always)]
275const fn u128_lo(u: u128) -> u128 {
276 u & 0xffffffffffffffff
277}
278
279#[inline(always)]
280const fn u128_mul_u128(x: u128, y: u128) -> (u128, u128) {
281 let xh = u128_hi(x);
282 let xl = u128_lo(x);
283 let yh = u128_hi(y);
284 let yl = u128_lo(y);
285 let mut t = xl * yl;
286 let mut rl = u128_lo(t);
287 t = xl * yh + u128_hi(t);
288 let mut rh = u128_hi(t);
289 t = xh * yl + u128_lo(t);
290 rl += u128_lo(t) << 64;
291 rh += xh * yh + u128_hi(t);
292 (rh, rl)
293}
294
295#[inline(always)]
300#[allow(clippy::integer_division)]
301fn 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}