1mod hgcd;
28#[cfg(all(
29 target_arch = "aarch64",
30 target_endian = "little",
31 target_pointer_width = "64",
32 any(
33 target_vendor = "apple",
34 target_os = "linux",
35 target_os = "android",
36 target_os = "windows",
37 target_os = "freebsd",
38 target_os = "netbsd",
39 target_os = "openbsd",
40 target_os = "fuchsia"
41 ),
42 not(miri)
43))]
44#[path = "gcd/aarch64.rs"]
45mod kernels;
46#[cfg(not(all(
47 target_arch = "aarch64",
48 target_endian = "little",
49 target_pointer_width = "64",
50 any(
51 target_vendor = "apple",
52 target_os = "linux",
53 target_os = "android",
54 target_os = "windows",
55 target_os = "freebsd",
56 target_os = "netbsd",
57 target_os = "openbsd",
58 target_os = "fuchsia"
59 ),
60 not(miri)
61)))]
62#[path = "gcd/portable.rs"]
63mod kernels;
64mod limbs;
65
66use super::add::{read_small, significant_len, small, write_small};
67use super::mul::limbs::mul;
68use super::mul::{Operand, Product, STACK_LIMBS, on_heap, on_stack, to_limbs};
69use super::{ByteOrder, Number};
70use core::mem::MaybeUninit;
71use core::ptr;
72use hgcd::{GCD_DC_THRESHOLD, GCDEXT_DC_THRESHOLD, gcd_dc, gcd_dc_itch, gcdext, gcdext_dc_itch};
73use limbs::{
74 Row, Step, add_to, divexact, divexact_itch, gcd_11, gcd_lehmer, gcd_u128, gcdext_1, hgcd2,
75 lehmer_ext_itch, lshift, mul_bound, normalized, rem_1, rshift, sub_from, tdiv_itch, tdiv_qr,
76 with_scratch,
77};
78
79impl Number {
80 pub fn gcd(&self, other: &Number) -> Number {
99 if small(self, other) {
100 let (x, y) = unsafe {
102 (
103 read_small(&self.value, self.byte_order),
104 read_small(&other.value, other.byte_order),
105 )
106 };
107 return small_number(self.byte_order, gcd_u128(x, y), false);
108 }
109 gcd_general(self, other)
110 }
111
112 pub fn lcm(&self, other: &Number) -> Number {
131 if small(self, other) {
132 let (x, y) = unsafe {
134 (
135 read_small(&self.value, self.byte_order),
136 read_small(&other.value, other.byte_order),
137 )
138 };
139 return small_lcm(self.byte_order, x, y);
140 }
141 lcm_general(self, other)
142 }
143
144 pub fn gcd_ext(&self, other: &Number) -> (Number, Number, Number) {
168 if small(self, other) {
169 let (x, y) = unsafe {
171 (
172 read_small(&self.value, self.byte_order),
173 read_small(&other.value, other.byte_order),
174 )
175 };
176 return small_gcd_ext(self.byte_order, x, self.negative, y, other.negative);
177 }
178 gcd_ext_general(self, other)
179 }
180
181 #[track_caller]
207 pub fn mod_inverse(&self, modulus: &Number) -> Option<Number> {
208 if small(self, modulus) {
209 let (x, n) = unsafe {
211 (
212 read_small(&self.value, self.byte_order),
213 read_small(&modulus.value, modulus.byte_order),
214 )
215 };
216 if n == 0 {
217 by_zero();
218 }
219 return small_inverse(x, self.negative, n)
220 .map(|r| small_number(self.byte_order, r, false));
221 }
222 inverse_general(self, modulus)
223 }
224}
225
226#[cold]
228#[inline(never)]
229#[track_caller]
230fn by_zero() -> ! {
231 panic!("attempt to calculate the remainder with a divisor of zero")
232}
233
234#[inline]
239fn small_number(order: ByteOrder, m: u128, negative: bool) -> Number {
240 let len = 16 - (m | 1).leading_zeros() as usize / 8;
241 let mut v = Vec::with_capacity(len);
242 unsafe {
245 write_small(v.as_mut_ptr(), order, m as u64, (m >> 64) as u64, len);
246 v.set_len(len);
247 }
248 Number {
249 value: v,
250 negative: negative && m != 0,
251 byte_order: order,
252 }
253}
254
255fn small_lcm(order: ByteOrder, x: u128, y: u128) -> Number {
259 let g = gcd_u128(x, y);
260 if g == 0 {
261 return small_number(order, 0, false);
262 }
263 let q = if (x | g) >> 64 == 0 {
264 u128::from(x as u64 / g as u64)
265 } else {
266 x / g
267 };
268 let (q0, q1, y0, y1) = (q as u64, (q >> 64) as u64, y as u64, (y >> 64) as u64);
269 let mul = |a: u64, b: u64| u128::from(a) * u128::from(b);
270 let (p00, p01, p10, p11) = (mul(q0, y0), mul(q0, y1), mul(q1, y0), mul(q1, y1));
271 let c1 = (p00 >> 64) + u128::from(p01 as u64) + u128::from(p10 as u64);
272 let c2 = (c1 >> 64) + (p01 >> 64) + (p10 >> 64) + u128::from(p11 as u64);
273 let c3 = (c2 >> 64) + (p11 >> 64);
274 let limbs = [p00 as u64, c1 as u64, c2 as u64, c3 as u64];
275 unsafe { limbs_number(limbs.as_ptr(), 4, false, order) }
277}
278
279type Signed = (u128, bool);
281
282#[inline(always)]
284fn less_times(x: i128, q: u128, y: i128) -> i128 {
285 x.wrapping_sub((q as i128).wrapping_mul(y))
286}
287
288fn gcdext_u128(x: u128, y: u128) -> (u128, i128, i128) {
294 let (mut a, mut b) = (x, y);
295 let (mut sa, mut ta, mut sb, mut tb) = (1i128, 0i128, 0i128, 1i128);
298 while (a | b) >> 64 != 0 {
299 let s = (a | b).leading_zeros();
300 let (ah, bh) = (a << s, b << s);
301 if let Some(m) = hgcd2((ah >> 64) as u64, ah as u64, (bh >> 64) as u64, bh as u64) {
302 let (u00, u01, u10, u11) = (
305 u128::from(m.u00),
306 u128::from(m.u01),
307 u128::from(m.u10),
308 u128::from(m.u11),
309 );
310 (a, b) = (
311 u11.wrapping_mul(a).wrapping_sub(u01.wrapping_mul(b)),
312 u00.wrapping_mul(b).wrapping_sub(u10.wrapping_mul(a)),
313 );
314 let (i00, i01, i10, i11) = (u00 as i128, u01 as i128, u10 as i128, u11 as i128);
315 (sa, sb) = (
316 i11.wrapping_mul(sa).wrapping_sub(i01.wrapping_mul(sb)),
317 i00.wrapping_mul(sb).wrapping_sub(i10.wrapping_mul(sa)),
318 );
319 (ta, tb) = (
320 i11.wrapping_mul(ta).wrapping_sub(i01.wrapping_mul(tb)),
321 i00.wrapping_mul(tb).wrapping_sub(i10.wrapping_mul(ta)),
322 );
323 continue;
324 }
325 if a == b {
327 return smaller_pair(a, (sa, ta), (sb, tb));
328 }
329 if a > b {
330 let q = a / b;
331 a -= q * b;
332 if a == 0 {
333 return (b, sb, tb);
334 }
335 sa = less_times(sa, q, sb);
336 ta = less_times(ta, q, tb);
337 } else {
338 let q = b / a;
339 b -= q * a;
340 if b == 0 {
341 return (a, sa, ta);
342 }
343 sb = less_times(sb, q, sa);
344 tb = less_times(tb, q, ta);
345 }
346 }
347 if a == b {
348 return smaller_pair(a, (sa, ta), (sb, tb));
349 }
350 let (g, u, v) = gcdext_1(a as u64, b as u64);
351 let (u, v) = (i128::from(u), i128::from(v));
352 (
353 u128::from(g),
354 u.wrapping_mul(sa).wrapping_add(v.wrapping_mul(sb)),
355 u.wrapping_mul(ta).wrapping_add(v.wrapping_mul(tb)),
356 )
357}
358
359#[inline]
363fn smaller_pair(g: u128, a: (i128, i128), b: (i128, i128)) -> (u128, i128, i128) {
364 let (ua, ub) = (a.0.unsigned_abs(), b.0.unsigned_abs());
365 let pick_b = ub < ua || (ub == ua && b.0 > 0);
366 let (s, t) = if pick_b { b } else { a };
367 (g, s, t)
368}
369
370fn small_gcd_ext(
373 order: ByteOrder,
374 x: u128,
375 negative_x: bool,
376 y: u128,
377 negative_y: bool,
378) -> (Number, Number, Number) {
379 let sgn = |m: u128, negative: bool| -> Signed { (u128::from(m != 0), negative) };
380 let (g, s, t) = if y == 0 {
381 (x, sgn(x, negative_x), (0, false))
382 } else if x == 0 {
383 (y, (0, false), sgn(y, negative_y))
384 } else if x == y {
385 (x, (0, false), (1, negative_y))
386 } else {
387 let (g, s, t) = gcdext_u128(x, y);
388 (
389 g,
390 (s.unsigned_abs(), (s < 0) != negative_x),
391 (t.unsigned_abs(), (t < 0) != negative_y),
392 )
393 };
394 (
395 small_number(order, g, false),
396 small_number(order, s.0, s.1),
397 small_number(order, t.0, t.1),
398 )
399}
400
401fn small_inverse(x: u128, negative: bool, n: u128) -> Option<u128> {
404 if n == 1 {
405 return Some(0);
406 }
407 let x = x % n;
408 if x == 0 {
409 return None;
410 }
411 let (g, s, _) = gcdext_u128(x, n);
412 if g != 1 {
413 return None;
414 }
415 let r = if s < 0 {
416 n - s.unsigned_abs()
417 } else {
418 s as u128
419 };
420 Some(if negative && r != 0 { n - r } else { r })
421}
422
423unsafe fn limbs_number(r: *const u64, n: usize, negative: bool, order: ByteOrder) -> Number {
432 unsafe {
434 let top = normalized(r, n);
435 let p = if top == 0 {
436 Product {
437 r: ptr::null(),
438 len: 1,
439 negative: false,
440 }
441 } else {
442 Product {
443 r,
444 len: 8 * top - (*r.add(top - 1)).leading_zeros() as usize / 8,
445 negative,
446 }
447 };
448 let mut v = Vec::with_capacity(p.len);
449 p.write(v.as_mut_ptr(), order);
450 v.set_len(p.len);
451 Number {
452 value: v,
453 negative: p.negative,
454 byte_order: order,
455 }
456 }
457}
458
459fn magnitude(n: &Number, negative: bool, order: ByteOrder) -> Number {
462 let be = n.byte_order == ByteOrder::BigEndian;
463 let len = if be {
464 significant_len::<true>(&n.value)
465 } else {
466 significant_len::<false>(&n.value)
467 };
468 let bytes = if be {
469 &n.value[n.value.len() - len..]
470 } else {
471 &n.value[..len]
472 };
473 let value = if len == 0 {
474 vec![0]
475 } else if n.byte_order == order {
476 bytes.to_vec()
477 } else {
478 bytes.iter().rev().copied().collect()
479 };
480 Number {
481 value,
482 negative: negative && len != 0,
483 byte_order: order,
484 }
485}
486
487fn sign(nonzero: bool, negative: bool, order: ByteOrder) -> Number {
490 small_number(order, u128::from(nonzero), negative)
491}
492
493#[inline(always)]
498fn working<R>(need: usize, f: impl FnOnce(*mut u64) -> R) -> R {
499 let run = |s: *mut MaybeUninit<u64>| f(s.cast());
500 if need <= STACK_LIMBS {
501 on_stack(run)
502 } else {
503 on_heap(need, run)
504 }
505}
506
507unsafe fn strip(p: *mut u64, n: usize) -> (usize, usize) {
514 unsafe {
516 let mut skip = 0;
517 while *p.add(skip) == 0 {
518 skip += 1;
519 }
520 let bits = (*p.add(skip)).trailing_zeros();
521 let m = n - skip;
522 if skip > 0 {
523 ptr::copy(p.add(skip), p, m);
524 }
525 if bits > 0 {
526 rshift(p, p, m, bits);
527 }
528 (normalized(p, m), 64 * skip + bits as usize)
529 }
530}
531
532#[inline(never)]
536fn gcd_general(a: &Number, b: &Number) -> Number {
537 let order = a.byte_order;
538 let (x, y) = (Operand::of(a), Operand::of(b));
539 if x.significant == 0 {
540 return magnitude(b, false, order);
541 }
542 if y.significant == 0 {
543 return magnitude(a, false, order);
544 }
545 let (nx, ny) = (x.significant.div_ceil(8), y.significant.div_ceil(8));
546 let (long, short) = (nx.max(ny), nx.min(ny));
547 let need = (nx + 1) + (ny + 1) + (long + 2) + gcd_itch(long, short);
550 working(need, |s| {
551 unsafe {
553 let (pa, pb) = (s, s.add(nx + 1));
554 let r = pb.add(ny + 1);
555 let t = r.add(long + 2);
556 to_limbs(x, pa);
557 to_limbs(y, pb);
558 let (na, za) = strip(pa, nx);
559 let (nb, zb) = strip(pb, ny);
560 let k = za.min(zb);
561 let (pa, na, pb, nb) = if na >= nb {
562 (pa, na, pb, nb)
563 } else {
564 (pb, nb, pa, na)
565 };
566 let (zl, zbits) = (k / 64, (k % 64) as u32);
568 let g = r.add(zl);
569 let gn = gcd_limbs(g, pa, na, pb, nb, t, gcd_itch(long, short));
570 ptr::write_bytes(r, 0, zl);
571 *g.add(gn) = lshift(g, g, gn, zbits);
572 limbs_number(r, zl + gn + 1, false, order)
573 }
574 })
575}
576
577fn gcd_itch(na: usize, nb: usize) -> usize {
579 let lehmer = (na - nb + 1) + tdiv_itch(na, nb).max(nb + 1 + tdiv_itch(nb, 1));
580 if nb >= GCD_DC_THRESHOLD {
581 lehmer.max(gcd_dc_itch(nb))
582 } else {
583 lehmer
584 }
585}
586
587unsafe fn gcd_limbs(
595 g: *mut u64,
596 a: *mut u64,
597 na: usize,
598 b: *mut u64,
599 nb: usize,
600 t: *mut u64,
601 avail: usize,
602) -> usize {
603 unsafe {
605 if nb == 1 {
606 let d = *b;
607 let r = if na == 1 && *a < d {
608 *a
609 } else {
610 rem_1(a, na, d, t)
611 };
612 *g = if r == 0 {
613 d
614 } else {
615 gcd_11(r >> r.trailing_zeros(), d)
616 };
617 return 1;
618 }
619 if na > nb {
620 let qn = na - nb + 1;
624 with_scratch(t, avail, qn + tdiv_itch(na, nb), |t| {
625 tdiv_qr(t, a, na, b, nb, t.add(qn));
626 });
627 if normalized(a, nb) == 0 {
628 ptr::copy_nonoverlapping(b, g, nb);
629 return nb;
630 }
631 }
632 let (mut a, mut b, mut n) = (a, b, nb);
633 if n >= GCD_DC_THRESHOLD {
634 match gcd_dc(&mut a, &mut b, n, t, avail) {
635 Step::Size(k) => n = k,
636 Step::Gcd(p, k) => {
637 ptr::copy(p, g, k);
638 return k;
639 }
640 }
641 }
642 gcd_lehmer(g, a, b, n, t, avail)
643 }
644}
645
646#[inline(never)]
651fn lcm_general(a: &Number, b: &Number) -> Number {
652 let order = a.byte_order;
653 let (x, y) = (Operand::of(a), Operand::of(b));
654 if x.significant == 0 || y.significant == 0 {
655 return small_number(order, 0, false);
656 }
657 let g = a.gcd(b);
658 let one = g.value == [1];
659 let mut l = if x.significant <= y.significant {
660 let q = if one {
661 magnitude(a, false, order)
662 } else {
663 a.div_exact(&g)
664 };
665 q * b
666 } else {
667 let q = if one {
668 magnitude(b, false, b.byte_order)
669 } else {
670 b.div_exact(&g)
671 };
672 a * q
673 };
674 l.negative = false;
675 l
676}
677
678fn ext_dc_itch(n: usize) -> usize {
683 if n >= GCDEXT_DC_THRESHOLD {
684 gcdext_dc_itch(n)
685 } else {
686 0
687 }
688}
689
690#[inline(never)]
692fn gcd_ext_general(a: &Number, b: &Number) -> (Number, Number, Number) {
693 let order = a.byte_order;
694 let (x, y) = (Operand::of(a), Operand::of(b));
695 if y.significant == 0 {
696 return (
697 magnitude(a, false, order),
698 sign(x.significant != 0, a.negative, order),
699 sign(false, false, order),
700 );
701 }
702 if x.significant == 0 {
703 return (
704 magnitude(b, false, order),
705 sign(false, false, order),
706 sign(true, b.negative, order),
707 );
708 }
709 let (nx, ny) = (x.significant.div_ceil(8), y.significant.div_ceil(8));
710 let swap = nx < ny;
713 let (p, q, negative_p, negative_q) = if swap {
714 (y, x, b.negative, a.negative)
715 } else {
716 (x, y, a.negative, b.negative)
717 };
718 let (np, nq) = if swap { (ny, nx) } else { (nx, ny) };
719 let (g, cp, cq) = cofactors(p, np, q, nq, order);
720 let cp = Number {
721 negative: cp.negative != negative_p && cp.value != [0],
722 ..cp
723 };
724 let cq = Number {
725 negative: cq.negative != negative_q && cq.value != [0],
726 ..cq
727 };
728 if swap { (g, cq, cp) } else { (g, cp, cq) }
729}
730
731fn cofactors(
736 p: Operand,
737 np: usize,
738 q: Operand,
739 nq: usize,
740 order: ByteOrder,
741) -> (Number, Number, Number) {
742 let ualloc = nq + 4;
743 let scratch = lehmer_ext_itch(nq)
746 .max(ext_dc_itch(nq))
747 .max((np - nq + 1) + tdiv_itch(np, nq))
748 .max(mul_bound(np, ualloc))
749 .max(divexact_itch(np + ualloc + 1, nq));
750 let need = (np + 1)
751 + (nq + 1)
752 + 2 * ualloc
753 + nq
754 + (ualloc + 1)
755 + (np + ualloc + 1)
756 + (np + 2)
757 + scratch;
758 working(need, |s| {
759 unsafe {
762 let pa = s;
763 let pb = pa.add(np + 1);
764 let u0 = pb.add(nq + 1);
765 let u1 = u0.add(ualloc);
766 let g = u1.add(ualloc);
767 let c = g.add(nq);
768 let prod = c.add(ualloc + 1);
769 let v = prod.add(np + ualloc + 1);
770 let t = v.add(np + 2);
771 to_limbs(p, pa);
772 to_limbs(q, pb);
773 if np > nq {
774 let qn = np - nq + 1;
775 tdiv_qr(t, pa, np, pb, nq, t.add(qn));
776 if normalized(pa, nq) == 0 {
777 let gq = limbs_number(pb, nq, false, order);
779 return (gq, sign(false, false, order), sign(true, false, order));
780 }
781 }
782 ptr::write_bytes(u0, 0, 2 * ualloc);
783 *u1 = 1;
784 let mut row = Row { u0, u1, un: 1 };
785 let (gn, cn, negative) = gcdext(g, c, pa, pb, nq, &mut row, t, scratch);
786 let gq = limbs_number(g, gn, false, order);
787 let cp = limbs_number(c, cn, negative, order);
788 if cn == 0 {
790 return (gq, cp, sign(true, false, order));
792 }
793 to_limbs(p, pa);
794 to_limbs(q, pb);
795 let (long, short) = if np >= cn {
796 ((pa, np), (c, cn))
797 } else {
798 ((c, cn), (pa, np))
799 };
800 let pn = np + cn;
801 mul(prod, long.0, long.1, short.0, short.1, t);
802 if negative {
803 add_to(prod, prod, pn, g, gn);
804 } else {
805 sub_from(prod, prod, pn, g, gn);
806 }
807 let pn = normalized(prod, pn);
808 if pn == 0 {
809 return (gq, cp, sign(false, false, order));
810 }
811 divexact(v, prod, pn, pb, nq, t, scratch);
812 let cq = limbs_number(v, pn - nq + 1, !negative, order);
813 (gq, cp, cq)
814 }
815 })
816}
817
818#[inline(never)]
822#[track_caller]
823fn inverse_general(a: &Number, modulus: &Number) -> Option<Number> {
824 let order = a.byte_order;
825 let (x, m) = (Operand::of(a), Operand::of(modulus));
826 if m.significant == 0 {
827 by_zero();
828 }
829 let nm = m.significant.div_ceil(8);
830 let nx = x.significant.div_ceil(8);
831 let ualloc = nm + 4;
832 let scratch = lehmer_ext_itch(nm)
833 .max(ext_dc_itch(nm))
834 .max((nx.max(nm) - nm + 1) + tdiv_itch(nx.max(nm), nm));
835 let need = (nx.max(nm) + 1) + (nm + 1) + 2 * ualloc + nm + (ualloc + 1) + scratch;
836 working(need, |s| {
837 unsafe {
840 let pa = s;
841 let pm = pa.add(nx.max(nm) + 1);
842 let u0 = pm.add(nm + 1);
843 let u1 = u0.add(ualloc);
844 let g = u1.add(ualloc);
845 let c = g.add(nm);
846 let t = c.add(ualloc + 1);
847 to_limbs(m, pm);
848 if nm == 1 && *pm == 1 {
849 return Some(small_number(order, 0, false));
850 }
851 if x.significant == 0 {
852 return None;
853 }
854 to_limbs(x, pa);
855 let mut na = nx;
856 if nx >= nm {
857 let qn = nx - nm + 1;
858 tdiv_qr(t, pa, nx, pm, nm, t.add(qn));
859 na = normalized(pa, nm);
860 if na == 0 {
861 return None;
862 }
863 }
864 ptr::write_bytes(pa.add(na), 0, nm - na);
865 ptr::write_bytes(u0, 0, 2 * ualloc);
866 *u1 = 1;
867 let mut row = Row { u0, u1, un: 1 };
868 let (gn, cn, negative) = gcdext(g, c, pa, pm, nm, &mut row, t, scratch);
869 if gn != 1 || *g != 1 {
870 return None;
871 }
872 let negative = negative != a.negative;
874 if negative {
875 to_limbs(m, pm);
876 sub_from(pm, pm, nm, c, cn);
877 return Some(limbs_number(pm, nm, false, order));
878 }
879 Some(limbs_number(c, cn, false, order))
880 }
881 })
882}