1use alloc::string::{String, ToString};
13use alloc::vec::Vec;
14
15const BASE: u64 = 1_000_000_000;
18const BASE_DIGITS: usize = 9;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct BigNumeric {
25 neg: bool,
26 limbs: Vec<u32>,
27 scale: u16,
28}
29
30impl BigNumeric {
31 #[must_use]
33 pub fn is_zero(&self) -> bool {
34 self.limbs.is_empty()
35 }
36
37 #[must_use]
38 pub fn scale(&self) -> u16 {
39 self.scale
40 }
41
42 #[must_use]
45 pub fn parts(&self) -> (bool, &[u32], u16) {
46 (self.neg, &self.limbs, self.scale)
47 }
48
49 #[must_use]
52 pub fn from_parts(neg: bool, limbs: Vec<u32>, scale: u16) -> Self {
53 let mut out = BigNumeric { neg, limbs, scale };
54 out.normalize();
55 out
56 }
57
58 fn normalize(&mut self) {
60 while self.limbs.last() == Some(&0) {
61 self.limbs.pop();
62 }
63 if self.limbs.is_empty() {
64 self.neg = false;
65 }
66 }
67
68 #[must_use]
70 pub fn from_i128(mut v: i128, scale: u16) -> Self {
71 let neg = v < 0;
72 let mut limbs = Vec::new();
73 let mut mag = v.unsigned_abs();
75 let _ = &mut v;
76 while mag != 0 {
77 limbs.push((mag % u128::from(BASE)) as u32);
78 mag /= u128::from(BASE);
79 }
80 let mut out = BigNumeric { neg, limbs, scale };
81 out.normalize();
82 out
83 }
84
85 #[must_use]
88 pub fn to_i128(&self) -> Option<i128> {
89 let mut mag: u128 = 0;
90 for &limb in self.limbs.iter().rev() {
91 mag = mag
92 .checked_mul(u128::from(BASE))?
93 .checked_add(u128::from(limb))?;
94 }
95 if self.neg {
96 if mag <= (i128::MAX as u128) + 1 {
98 Some((mag as i128).wrapping_neg())
99 } else {
100 None
101 }
102 } else {
103 i128::try_from(mag).ok()
104 }
105 }
106
107 fn cmp_mag(a: &[u32], b: &[u32]) -> core::cmp::Ordering {
109 use core::cmp::Ordering;
110 match a.len().cmp(&b.len()) {
111 Ordering::Equal => {
112 for i in (0..a.len()).rev() {
113 match a[i].cmp(&b[i]) {
114 Ordering::Equal => {}
115 other => return other,
116 }
117 }
118 Ordering::Equal
119 }
120 other => other,
121 }
122 }
123
124 fn add_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
126 let mut out = Vec::with_capacity(a.len().max(b.len()) + 1);
127 let mut carry: u64 = 0;
128 for i in 0..a.len().max(b.len()) {
129 let av = u64::from(a.get(i).copied().unwrap_or(0));
130 let bv = u64::from(b.get(i).copied().unwrap_or(0));
131 let s = av + bv + carry;
132 out.push((s % BASE) as u32);
133 carry = s / BASE;
134 }
135 if carry != 0 {
136 out.push(carry as u32);
137 }
138 out
139 }
140
141 fn sub_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
143 let mut out = Vec::with_capacity(a.len());
144 let mut borrow: i64 = 0;
145 for i in 0..a.len() {
146 let av = i64::from(a[i]);
147 let bv = i64::from(b.get(i).copied().unwrap_or(0));
148 let mut d = av - bv - borrow;
149 if d < 0 {
150 d += BASE as i64;
151 borrow = 1;
152 } else {
153 borrow = 0;
154 }
155 out.push(d as u32);
156 }
157 while out.last() == Some(&0) {
158 out.pop();
159 }
160 out
161 }
162
163 fn mul_pow10(limbs: &[u32], k: u32) -> Vec<u32> {
165 if limbs.is_empty() {
166 return Vec::new();
167 }
168 let whole = (k as usize) / BASE_DIGITS;
169 let rem = (k as usize) % BASE_DIGITS;
170 let mut cur: Vec<u32> = if rem == 0 {
172 limbs.to_vec()
173 } else {
174 let factor = 10u64.pow(rem as u32);
175 let mut out = Vec::with_capacity(limbs.len() + 1);
176 let mut carry: u64 = 0;
177 for &l in limbs {
178 let v = u64::from(l) * factor + carry;
179 out.push((v % BASE) as u32);
180 carry = v / BASE;
181 }
182 if carry != 0 {
183 out.push(carry as u32);
184 }
185 out
186 };
187 if whole > 0 {
189 let mut shifted = Vec::with_capacity(cur.len() + whole);
190 shifted.resize(whole, 0);
191 shifted.append(&mut cur);
192 cur = shifted;
193 }
194 cur
195 }
196
197 fn align(&self, other: &Self) -> (Vec<u32>, Vec<u32>, u16) {
200 use core::cmp::Ordering;
201 match self.scale.cmp(&other.scale) {
202 Ordering::Equal => (self.limbs.clone(), other.limbs.clone(), self.scale),
203 Ordering::Less => {
204 let k = u32::from(other.scale - self.scale);
205 (
206 Self::mul_pow10(&self.limbs, k),
207 other.limbs.clone(),
208 other.scale,
209 )
210 }
211 Ordering::Greater => {
212 let k = u32::from(self.scale - other.scale);
213 (
214 self.limbs.clone(),
215 Self::mul_pow10(&other.limbs, k),
216 self.scale,
217 )
218 }
219 }
220 }
221
222 #[allow(clippy::should_implement_trait)]
230 #[must_use]
231 pub fn cmp(&self, other: &Self) -> core::cmp::Ordering {
232 use core::cmp::Ordering;
233 if let (true, true) = (self.is_zero(), other.is_zero()) {
234 return Ordering::Equal;
235 }
236 match (self.neg, other.neg) {
237 (false, true) => return Ordering::Greater,
238 (true, false) => return Ordering::Less,
239 _ => {}
240 }
241 let (a, b, _) = self.align(other);
242 let mag = Self::cmp_mag(&a, &b);
243 if self.neg { mag.reverse() } else { mag }
244 }
245
246 #[must_use]
247 pub fn add(&self, other: &Self) -> Self {
248 let (a, b, scale) = self.align(other);
249 let out = if self.neg == other.neg {
250 BigNumeric {
251 neg: self.neg,
252 limbs: Self::add_mag(&a, &b),
253 scale,
254 }
255 } else {
256 match Self::cmp_mag(&a, &b) {
258 core::cmp::Ordering::Less => BigNumeric {
259 neg: other.neg,
260 limbs: Self::sub_mag(&b, &a),
261 scale,
262 },
263 _ => BigNumeric {
264 neg: self.neg,
265 limbs: Self::sub_mag(&a, &b),
266 scale,
267 },
268 }
269 };
270 let mut out = out;
271 out.normalize();
272 out
273 }
274
275 #[must_use]
276 pub fn neg(&self) -> Self {
277 let mut out = self.clone();
278 if !out.is_zero() {
279 out.neg = !out.neg;
280 }
281 out
282 }
283
284 #[must_use]
285 pub fn sub(&self, other: &Self) -> Self {
286 self.add(&other.neg())
287 }
288
289 #[must_use]
290 pub fn mul(&self, other: &Self) -> Self {
291 if self.is_zero() || other.is_zero() {
292 return BigNumeric {
293 neg: false,
294 limbs: Vec::new(),
295 scale: self.scale + other.scale,
296 };
297 }
298 let mut acc = alloc::vec![0u64; self.limbs.len() + other.limbs.len()];
299 for (i, &a) in self.limbs.iter().enumerate() {
300 let mut carry: u64 = 0;
301 for (j, &b) in other.limbs.iter().enumerate() {
302 let cur = acc[i + j] + u64::from(a) * u64::from(b) + carry;
303 acc[i + j] = cur % BASE;
304 carry = cur / BASE;
305 }
306 acc[i + other.limbs.len()] += carry;
307 }
308 let mut limbs = Vec::with_capacity(acc.len());
310 let mut carry: u64 = 0;
311 for v in acc {
312 let cur = v + carry;
313 limbs.push((cur % BASE) as u32);
314 carry = cur / BASE;
315 }
316 while carry != 0 {
317 limbs.push((carry % BASE) as u32);
318 carry /= BASE;
319 }
320 let mut out = BigNumeric {
321 neg: self.neg != other.neg,
322 limbs,
323 scale: self.scale + other.scale,
324 };
325 out.normalize();
326 out
327 }
328
329 fn mul_scalar(limbs: &[u32], factor: u64) -> Vec<u32> {
331 if factor == 0 || limbs.is_empty() {
332 return Vec::new();
333 }
334 let mut out = Vec::with_capacity(limbs.len() + 1);
335 let mut carry: u64 = 0;
336 for &l in limbs {
337 let v = u64::from(l) * factor + carry;
338 out.push((v % BASE) as u32);
339 carry = v / BASE;
340 }
341 while carry != 0 {
342 out.push((carry % BASE) as u32);
343 carry /= BASE;
344 }
345 out
346 }
347
348 fn div_rem_mag(u: &[u32], v: &[u32]) -> (Vec<u32>, Vec<u32>) {
353 use core::cmp::Ordering;
354 if Self::cmp_mag(u, v) == Ordering::Less {
356 let mut r = u.to_vec();
357 while r.last() == Some(&0) {
358 r.pop();
359 }
360 return (Vec::new(), r);
361 }
362 let n = v.len();
363 if n == 1 {
365 let d = u64::from(v[0]);
366 let mut rem: u64 = 0;
367 let mut q = alloc::vec![0u32; u.len()];
368 for i in (0..u.len()).rev() {
369 let cur = rem * BASE + u64::from(u[i]);
370 q[i] = (cur / d) as u32;
371 rem = cur % d;
372 }
373 while q.last() == Some(&0) {
374 q.pop();
375 }
376 let r = if rem == 0 {
377 Vec::new()
378 } else {
379 alloc::vec![rem as u32]
380 };
381 return (q, r);
382 }
383 let d = BASE / (u64::from(v[n - 1]) + 1);
385 let vn = Self::mul_scalar(v, d);
386 let vn = {
387 let mut vn = vn;
388 vn.resize(n, 0); vn
390 };
391 let mut un = Self::mul_scalar(u, d);
392 let m = u.len() - n; un.resize(u.len() + 1, 0); let mut q = alloc::vec![0u32; m + 1];
395 for j in (0..=m).rev() {
397 let num = u128::from(un[j + n]) * u128::from(BASE) + u128::from(un[j + n - 1]);
399 let mut qhat = num / u128::from(vn[n - 1]);
400 let mut rhat = num % u128::from(vn[n - 1]);
401 while qhat >= u128::from(BASE)
402 || qhat * u128::from(vn[n - 2])
403 > rhat * u128::from(BASE) + u128::from(un[j + n - 2])
404 {
405 qhat -= 1;
406 rhat += u128::from(vn[n - 1]);
407 if rhat >= u128::from(BASE) {
408 break;
409 }
410 }
411 let mut borrow: i64 = 0;
413 let mut carry: u64 = 0;
414 for i in 0..n {
415 let p = qhat * u128::from(vn[i]) + u128::from(carry);
416 carry = (p / u128::from(BASE)) as u64;
417 let sub = (p % u128::from(BASE)) as i64;
418 let mut t = i64::from(un[j + i]) - sub - borrow;
419 if t < 0 {
420 t += BASE as i64;
421 borrow = 1;
422 } else {
423 borrow = 0;
424 }
425 un[j + i] = t as u32;
426 }
427 let mut t = i64::from(un[j + n]) - carry as i64 - borrow;
428 if t < 0 {
430 qhat -= 1;
431 let mut c: u64 = 0;
432 for i in 0..n {
433 let s = u64::from(un[j + i]) + u64::from(vn[i]) + c;
434 un[j + i] = (s % BASE) as u32;
435 c = s / BASE;
436 }
437 t += (BASE as i64) + c as i64;
438 }
439 un[j + n] = t as u32;
440 q[j] = qhat as u32;
441 }
442 let mut rem = un[..n].to_vec();
444 while rem.last() == Some(&0) {
445 rem.pop();
446 }
447 let (rem, _) = Self::div_rem_mag(&rem, &[d as u32]);
448 while q.last() == Some(&0) {
449 q.pop();
450 }
451 (q, rem)
452 }
453
454 #[must_use]
457 pub fn div_rem_int(&self, other: &Self) -> (Self, Self) {
458 let (q, r) = Self::div_rem_mag(&self.limbs, &other.limbs);
459 let mut quo = BigNumeric {
460 neg: self.neg != other.neg,
461 limbs: q,
462 scale: 0,
463 };
464 let mut rem = BigNumeric {
465 neg: self.neg,
466 limbs: r,
467 scale: 0,
468 };
469 quo.normalize();
470 rem.normalize();
471 (quo, rem)
472 }
473
474 #[must_use]
478 pub fn div(&self, other: &Self, result_scale: u16) -> Option<Self> {
479 if other.is_zero() {
480 return None;
481 }
482 let want = i32::from(result_scale) + 1 + i32::from(other.scale) - i32::from(self.scale);
485 let num = if want > 0 {
486 Self::mul_pow10(&self.limbs, want as u32)
487 } else {
488 self.limbs.clone()
489 };
490 let den = if want < 0 {
491 Self::mul_pow10(&other.limbs, (-want) as u32)
492 } else {
493 other.limbs.clone()
494 };
495 let (mut q, _rem) = Self::div_rem_mag(&num, &den);
496 let guard = if q.is_empty() { 0 } else { q[0] % 10 };
499 q = Self::div_rem_mag(&q, &[10]).0; if guard >= 5 {
501 q = Self::add_mag(&q, &[1]);
502 }
503 let mut out = BigNumeric {
504 neg: self.neg != other.neg,
505 limbs: q,
506 scale: result_scale,
507 };
508 out.normalize();
509 Some(out)
510 }
511
512 fn isqrt_mag(&self) -> Self {
517 use core::cmp::Ordering;
518 let n = BigNumeric {
519 neg: false,
520 limbs: self.limbs.clone(),
521 scale: 0,
522 };
523 if n.is_zero() {
524 return BigNumeric {
525 neg: false,
526 limbs: Vec::new(),
527 scale: 0,
528 };
529 }
530 let top = *n.limbs.last().unwrap();
532 let ndigits = (n.limbs.len() - 1) * BASE_DIGITS + top.to_string().len();
533 let half = ndigits.div_ceil(2);
535 let one = BigNumeric::from_i128(1, 0);
536 let two = BigNumeric::from_i128(2, 0);
537 let mut x = BigNumeric {
538 neg: false,
539 limbs: Self::mul_pow10(&one.limbs, half as u32),
540 scale: 0,
541 };
542 loop {
544 let (div, _) = n.div_rem_int(&x);
545 let sum = x.add(&div);
546 let (next, _) = sum.div_rem_int(&two);
547 if next.cmp(&x) != Ordering::Less {
548 break;
549 }
550 x = next;
551 }
552 while x.mul(&x).cmp(&n) == Ordering::Greater {
554 x = x.sub(&one);
555 }
556 x
557 }
558
559 #[must_use]
565 pub fn sqrt(&self, result_scale: u16) -> Option<Self> {
566 use core::cmp::Ordering;
567 if self.neg && !self.is_zero() {
568 return None;
569 }
570 if self.is_zero() {
571 return Some(BigNumeric {
572 neg: false,
573 limbs: Vec::new(),
574 scale: result_scale,
575 });
576 }
577 let shift = 2 * (i32::from(result_scale) + 1) - i32::from(self.scale);
581 let mant = BigNumeric {
582 neg: false,
583 limbs: self.limbs.clone(),
584 scale: 0,
585 };
586 let radicand = if shift >= 0 {
587 BigNumeric {
588 neg: false,
589 limbs: Self::mul_pow10(&mant.limbs, shift as u32),
590 scale: 0,
591 }
592 } else {
593 let (q, _) = Self::div_rem_mag(&mant.limbs, &Self::mul_pow10(&[1], (-shift) as u32));
594 BigNumeric {
595 neg: false,
596 limbs: q,
597 scale: 0,
598 }
599 };
600 let root = radicand.isqrt_mag();
601 let ten = BigNumeric::from_i128(10, 0);
603 let (q, r) = root.div_rem_int(&ten);
604 let five = BigNumeric::from_i128(5, 0);
605 let mut rounded = if r.cmp(&five) != Ordering::Less {
606 q.add(&BigNumeric::from_i128(1, 0))
607 } else {
608 q
609 };
610 rounded.scale = result_scale;
611 rounded.normalize();
612 Some(rounded)
613 }
614
615 #[must_use]
619 pub fn round_to(&self, target_scale: u16) -> Self {
620 use core::cmp::Ordering;
621 match self.scale.cmp(&target_scale) {
622 Ordering::Equal => self.clone(),
623 Ordering::Less => {
624 let k = u32::from(target_scale - self.scale);
625 let mut out = BigNumeric {
626 neg: self.neg,
627 limbs: Self::mul_pow10(&self.limbs, k),
628 scale: target_scale,
629 };
630 out.normalize();
631 out
632 }
633 Ordering::Greater => {
634 let k = u32::from(self.scale - target_scale);
635 let divisor = Self::mul_pow10(&[1], k);
636 let (mut q, r) = Self::div_rem_mag(&self.limbs, &divisor);
637 let two_r = Self::mul_scalar(&r, 2);
639 if Self::cmp_mag(&two_r, &divisor) != Ordering::Less {
640 q = Self::add_mag(&q, &[1]);
641 }
642 let mut out = BigNumeric {
643 neg: self.neg,
644 limbs: q,
645 scale: target_scale,
646 };
647 out.normalize();
648 out
649 }
650 }
651 }
652
653 fn transcendental_scale(&self) -> u16 {
657 let (_, digits, scale) = self.parts();
658 let ndigits = if digits.is_empty() {
660 1
661 } else {
662 let top = *digits.last().unwrap();
663 (digits.len() - 1) * BASE_DIGITS + top.to_string().len()
664 };
665 let int_digits = ndigits as i64 - i64::from(scale);
666 if int_digits <= 1 {
667 16
668 } else {
669 (17 - int_digits).max(0) as u16
670 }
671 }
672
673 #[must_use]
680 pub fn exp(&self) -> Self {
681 const WS: u16 = 28;
682 let raw = if self.neg && !self.is_zero() {
683 let pos = self.neg().exp_core(WS);
685 BigNumeric::from_i128(1, 0).div(&pos, WS).unwrap()
686 } else {
687 self.exp_core(WS)
688 };
689 raw.round_to(raw.transcendental_scale())
690 }
691
692 fn exp_core(&self, ws: u16) -> Self {
697 let one = BigNumeric::from_i128(1, 0);
698 if self.is_zero() {
699 return one.round_to(ws);
700 }
701 let sixteenth = BigNumeric::from_decimal_str("0.0625").unwrap();
704 let two = BigNumeric::from_i128(2, 0);
705 let mut halvings = 0u32;
706 let mut t = self.clone();
707 while t.cmp(&sixteenth) == core::cmp::Ordering::Greater {
708 t = t.div(&two, ws).unwrap();
709 halvings += 1;
710 }
711 let mut sum = one.round_to(ws);
713 let mut term = one.round_to(ws);
714 let mut k: i128 = 1;
715 loop {
716 term = term.mul(&t).round_to(ws);
717 term = term.div(&BigNumeric::from_i128(k, 0), ws).unwrap();
718 if term.is_zero() {
719 break;
720 }
721 sum = sum.add(&term);
722 k += 1;
723 }
724 for _ in 0..halvings {
726 sum = sum.mul(&sum).round_to(ws);
727 }
728 sum
729 }
730
731 #[must_use]
736 pub fn pow_numeric(&self, exp: &Self) -> Option<Self> {
737 if self.neg || self.is_zero() {
738 return None;
739 }
740 const WS: u16 = 30;
741 let ln_hi = self.ln_at(WS)?;
742 let prod = exp.mul(&ln_hi).round_to(WS);
743 Some(prod.exp())
744 }
745
746 fn ln_at(&self, ws: u16) -> Option<Self> {
750 if self.neg || self.is_zero() {
751 return None;
752 }
753 let two = BigNumeric::from_i128(2, 0);
754 let ln2 =
755 BigNumeric::from_decimal_str("0.6931471805599453094172321214581765680755").unwrap();
756 let four_thirds = BigNumeric::from_decimal_str("1.3333333333333333").unwrap();
757 let two_thirds = BigNumeric::from_decimal_str("0.6666666666666667").unwrap();
758 let mut m = self.round_to(ws);
759 let mut e: i128 = 0;
760 while m.cmp(&four_thirds) != core::cmp::Ordering::Less {
761 m = m.div(&two, ws).unwrap();
762 e += 1;
763 }
764 while m.cmp(&two_thirds) == core::cmp::Ordering::Less {
765 m = m.mul(&two).round_to(ws);
766 e -= 1;
767 }
768 let one = BigNumeric::from_i128(1, 0);
769 let t = m.sub(&one).div(&m.add(&one), ws).unwrap();
770 let t2 = t.mul(&t).round_to(ws);
771 let mut sum = t.clone();
772 let mut power = t.clone();
773 let mut k: i128 = 3;
774 loop {
775 power = power.mul(&t2).round_to(ws);
776 let term = power.div(&BigNumeric::from_i128(k, 0), ws).unwrap();
777 if term.is_zero() {
778 break;
779 }
780 sum = sum.add(&term);
781 k += 2;
782 }
783 let ln_m = sum.mul(&two).round_to(ws);
784 let e_ln2 = ln2.mul(&BigNumeric::from_i128(e, 0)).round_to(ws);
785 Some(ln_m.add(&e_ln2))
786 }
787
788 #[must_use]
793 pub fn ln(&self) -> Option<Self> {
794 let raw = self.ln_at(30)?;
795 Some(raw.round_to(raw.transcendental_scale()))
796 }
797
798 #[must_use]
804 pub fn log_base(&self, base: &Self) -> Option<Self> {
805 const WS: u16 = 32;
806 let ln_x = self.ln_at(WS)?;
807 let ln_b = base.ln_at(WS)?;
808 if ln_b.is_zero() {
809 return None;
810 }
811 let raw = ln_x.div(&ln_b, WS)?;
812 Some(raw.round_to(raw.transcendental_scale()))
813 }
814
815 #[must_use]
817 pub fn log10(&self) -> Option<Self> {
818 self.log_base(&Self::from_i128(10, 0))
819 }
820
821 #[must_use]
823 pub fn to_decimal_str(&self) -> String {
824 if self.is_zero() {
825 if self.scale == 0 {
826 return String::from("0");
827 }
828 return alloc::format!("0.{}", "0".repeat(self.scale as usize));
829 }
830 let mut digits = String::new();
832 for (idx, &limb) in self.limbs.iter().rev().enumerate() {
833 if idx == 0 {
834 digits.push_str(&limb.to_string());
835 } else {
836 digits.push_str(&alloc::format!("{limb:0width$}", width = BASE_DIGITS));
837 }
838 }
839 let scale = self.scale as usize;
840 let body = if scale == 0 {
841 digits
842 } else {
843 if digits.len() <= scale {
845 let pad = scale + 1 - digits.len();
846 let padded = alloc::format!("{}{}", "0".repeat(pad), digits);
847 let point = padded.len() - scale;
848 alloc::format!("{}.{}", &padded[..point], &padded[point..])
849 } else {
850 let point = digits.len() - scale;
851 alloc::format!("{}.{}", &digits[..point], &digits[point..])
852 }
853 };
854 if self.neg {
855 alloc::format!("-{body}")
856 } else {
857 body
858 }
859 }
860
861 #[must_use]
864 pub fn from_decimal_str(s: &str) -> Option<Self> {
865 let s = s.trim();
866 let (neg, rest) = match s.strip_prefix('-') {
867 Some(r) => (true, r),
868 None => (false, s.strip_prefix('+').unwrap_or(s)),
869 };
870 let (int_part, frac_part) = match rest.split_once('.') {
871 Some((i, f)) => (i, f),
872 None => (rest, ""),
873 };
874 if int_part.is_empty() && frac_part.is_empty() {
875 return None;
876 }
877 if !int_part.bytes().all(|b| b.is_ascii_digit())
878 || !frac_part.bytes().all(|b| b.is_ascii_digit())
879 {
880 return None;
881 }
882 let scale = u16::try_from(frac_part.len()).ok()?;
883 let mut all: String = String::with_capacity(int_part.len() + frac_part.len());
884 all.push_str(int_part);
885 all.push_str(frac_part);
886 let trimmed = all.trim_start_matches('0');
888 let digits = if trimmed.is_empty() { "0" } else { trimmed };
889 let bytes = digits.as_bytes();
891 let mut limbs = Vec::new();
892 let mut i = bytes.len();
893 while i > 0 {
894 let start = i.saturating_sub(BASE_DIGITS);
895 let chunk = core::str::from_utf8(&bytes[start..i]).ok()?;
896 limbs.push(chunk.parse::<u32>().ok()?);
897 i = start;
898 }
899 let mut out = BigNumeric { neg, limbs, scale };
900 out.normalize();
901 Some(out)
902 }
903}
904
905#[cfg(test)]
906mod tests {
907 use super::*;
908
909 struct Lcg(u64);
911 impl Lcg {
912 fn next(&mut self) -> u64 {
913 self.0 = self
914 .0
915 .wrapping_mul(6364136223846793005)
916 .wrapping_add(1442695040888963407);
917 self.0
918 }
919 fn i128_small(&mut self) -> i128 {
920 let m = (self.next() % 2_000_000_000_000_000_000) as i128;
922 if self.next() & 1 == 0 { m } else { -m }
923 }
924 }
925
926 #[test]
927 fn i128_bridge_round_trips() {
928 for v in [
929 0i128,
930 1,
931 -1,
932 123,
933 -456,
934 i64::MAX as i128,
935 i128::MAX,
936 i128::MIN,
937 10i128.pow(30),
938 ] {
939 assert_eq!(BigNumeric::from_i128(v, 0).to_i128(), Some(v), "v={v}");
940 }
941 }
942
943 #[test]
944 fn decimal_str_round_trips() {
945 for s in [
946 "0",
947 "123",
948 "-123",
949 "1.50",
950 "-0.001",
951 "1000000000",
952 "999999999999999999999999999999",
953 ] {
954 let b = BigNumeric::from_decimal_str(s).unwrap();
955 assert_eq!(b.to_decimal_str(), s, "s={s}");
956 }
957 }
958
959 #[test]
960 fn fuzz_vs_i128() {
961 let mut rng = Lcg(0x1234_5678_9abc_def0);
962 for _ in 0..20_000 {
963 let a = rng.i128_small();
964 let b = rng.i128_small();
965 let ba = BigNumeric::from_i128(a, 0);
966 let bb = BigNumeric::from_i128(b, 0);
967 assert_eq!(ba.add(&bb).to_i128(), Some(a + b), "add {a}+{b}");
968 assert_eq!(ba.sub(&bb).to_i128(), Some(a - b), "sub {a}-{b}");
969 assert_eq!(ba.mul(&bb).to_i128(), Some(a * b), "mul {a}*{b}");
970 assert_eq!(ba.cmp(&bb), a.cmp(&b), "cmp {a} vs {b}");
971 }
972 }
973
974 #[test]
975 fn overflow_stays_big() {
976 let a = BigNumeric::from_i128(10i128.pow(30), 0);
978 let p = a.mul(&a);
979 assert_eq!(p.to_i128(), None);
980 let mut expect = String::from("1");
981 expect.push_str(&"0".repeat(60));
982 assert_eq!(p.to_decimal_str(), expect);
983 }
984
985 #[test]
986 fn scale_align_add() {
987 let a = BigNumeric::from_decimal_str("1.5").unwrap();
989 let b = BigNumeric::from_decimal_str("0.25").unwrap();
990 assert_eq!(a.add(&b).to_decimal_str(), "1.75");
991 }
992
993 #[test]
994 fn fuzz_div_int_vs_i128() {
995 let mut rng = Lcg(0xdead_beef_cafe_babe);
996 for _ in 0..20_000 {
997 let a = rng.i128_small();
998 let mut b = rng.i128_small();
999 if b == 0 {
1000 b = 1;
1001 }
1002 let (q, r) = BigNumeric::from_i128(a, 0).div_rem_int(&BigNumeric::from_i128(b, 0));
1003 assert_eq!(q.to_i128(), Some(a / b), "quot {a}/{b}");
1004 assert_eq!(r.to_i128(), Some(a % b), "rem {a}%{b}");
1005 }
1006 }
1007
1008 #[test]
1009 fn div_multi_limb() {
1010 let a = BigNumeric::from_decimal_str("123456789012345678901234567890").unwrap();
1012 let b = BigNumeric::from_decimal_str("987654321987654321").unwrap();
1013 let (q, r) = a.div_rem_int(&b);
1014 let recon = q.mul(&b).add(&r);
1016 assert_eq!(recon.to_decimal_str(), a.to_decimal_str());
1017 assert_eq!(b.cmp(&r), core::cmp::Ordering::Greater); }
1019
1020 #[test]
1021 fn div_fixed_point() {
1022 let ten = BigNumeric::from_decimal_str("10").unwrap();
1023 let three = BigNumeric::from_decimal_str("3").unwrap();
1024 assert_eq!(ten.div(&three, 4).unwrap().to_decimal_str(), "3.3333");
1025 let one = BigNumeric::from_decimal_str("1").unwrap();
1026 let seven = BigNumeric::from_decimal_str("7").unwrap();
1027 assert_eq!(one.div(&seven, 6).unwrap().to_decimal_str(), "0.142857");
1028 let eight = BigNumeric::from_decimal_str("8").unwrap();
1030 assert_eq!(one.div(&eight, 2).unwrap().to_decimal_str(), "0.13");
1031 assert!(
1033 one.div(&BigNumeric::from_decimal_str("0").unwrap(), 4)
1034 .is_none()
1035 );
1036 }
1037
1038 #[test]
1039 fn isqrt_exact_and_floor() {
1040 let n = BigNumeric::from_decimal_str("152415787532388367501905199875019052100").unwrap();
1042 assert_eq!(n.isqrt_mag().to_decimal_str(), "12345678901234567890");
1043 for (v, want) in [
1045 ("0", "0"),
1046 ("1", "1"),
1047 ("2", "1"),
1048 ("10", "3"),
1049 ("15", "3"),
1050 ("16", "4"),
1051 ("99", "9"),
1052 ("100", "10"),
1053 ] {
1054 let b = BigNumeric::from_decimal_str(v).unwrap();
1055 assert_eq!(b.isqrt_mag().to_decimal_str(), want, "isqrt({v})");
1056 }
1057 }
1058
1059 #[test]
1060 fn sqrt_scale_and_rounding() {
1061 let two = BigNumeric::from_decimal_str("2").unwrap();
1063 assert_eq!(two.sqrt(15).unwrap().to_decimal_str(), "1.414213562373095");
1064 let ten = BigNumeric::from_decimal_str("10").unwrap();
1066 assert_eq!(ten.sqrt(15).unwrap().to_decimal_str(), "3.162277660168379");
1067 let nine = BigNumeric::from_decimal_str("9").unwrap();
1069 assert_eq!(nine.sqrt(15).unwrap().to_decimal_str(), "3.000000000000000");
1070 let big = BigNumeric::from_decimal_str("152415787532388367501905199875019052100").unwrap();
1072 assert_eq!(
1073 big.sqrt(0).unwrap().to_decimal_str(),
1074 "12345678901234567890"
1075 );
1076 assert!(
1078 BigNumeric::from_decimal_str("-4")
1079 .unwrap()
1080 .sqrt(2)
1081 .is_none()
1082 );
1083 assert_eq!(
1084 BigNumeric::from_decimal_str("0")
1085 .unwrap()
1086 .sqrt(3)
1087 .unwrap()
1088 .to_decimal_str(),
1089 "0.000"
1090 );
1091 }
1092
1093 #[test]
1094 fn fuzz_isqrt_vs_i128() {
1095 let mut state: u64 = 0x1234_5678_9abc_def0;
1098 let mut next = || {
1099 state = state
1100 .wrapping_mul(6364136223846793005)
1101 .wrapping_add(1442695040888963407);
1102 state
1103 };
1104 for _ in 0..20_000 {
1105 let n = u128::from(next()) | (u128::from(next()) << 64);
1106 let n = n % (1u128 << 100); let b = BigNumeric::from_i128(n as i128, 0);
1108 let root = b.isqrt_mag();
1109 let rl = root.to_i128().unwrap() as u128;
1110 assert!(rl * rl <= n, "root^2 > n for n={n}");
1111 assert!((rl + 1) * (rl + 1) > n, "(root+1)^2 <= n for n={n}");
1112 }
1113 }
1114}