Skip to content

Commit b0346b2

Browse files
committed
std/math/big: optimize lehmerGCD
1 parent 0c956bd commit b0346b2

2 files changed

Lines changed: 42 additions & 64 deletions

File tree

std/math/big/int.jule

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,6 @@ impl Int {
545545
// temp variables for multiprecision update
546546
mut q := Int{}
547547
mut r := Int{}
548-
mut s := Int{}
549-
mut t := Int{}
550548

551549
// ensure A >= B
552550
if cmpW(A.abs, B.abs) < 0 {
@@ -563,24 +561,25 @@ impl Int {
563561
// Simulate the effect of the single-precision steps using the cosequences.
564562
// A = u0*A + v0*B
565563
// B = u1*A + v1*B
566-
lehmerUpdate(&A, &B, &q, &r, &s, &t, u0, u1, v0, v1, even)
564+
lehmerUpdate(&A, &B, &q, &r, u0, u1, v0, v1, even)
565+
567566
if extended {
568567
// Ua = u0*Ua + v0*Ub
569568
// Ub = u1*Ua + v1*Ub
570-
lehmerUpdate(&Ua, &Ub, &q, &r, &s, &t, u0, u1, v0, v1, even)
569+
lehmerUpdate(&Ua, &Ub, &q, &r, u0, u1, v0, v1, even)
571570
}
572571
} else {
573572
// Single-digit calculations failed to simulate any quotients.
574573
// Do a standard Euclidean step.
575-
euclidUpdate(&A, &B, &Ua, &Ub, &q, &r, &s, &t, extended)
574+
euclidUpdate(&A, &B, &Ua, &Ub, &q, &r, extended)
576575
}
577576
}
578577

579578
if len(B.abs) > 0 {
580579
// extended Euclidean algorithm base case if B is a single Word
581580
if len(A.abs) > 1 {
582581
// A is longer than a single Word, so one update is needed.
583-
euclidUpdate(&A, &B, &Ua, &Ub, &q, &r, &s, &t, extended)
582+
euclidUpdate(&A, &B, &Ua, &Ub, &q, &r, extended)
584583
}
585584
if len(B.abs) > 0 {
586585
// A and B are both a single Word.
@@ -601,15 +600,9 @@ impl Int {
601600
even = !even
602601
}
603602

604-
setWV(&t.abs, ua)
605-
setWV(&s.abs, va)
606-
t.neg = !even
607-
s.neg = even
608-
609-
t.Mul(&Ua, &t)
610-
s.Mul(&Ub, &s)
611-
612-
Ua.Add(&t, &s)
603+
mulWI(&Ua, &Ua, !even, ua)
604+
mulWI(&Ub, &Ub, even, va)
605+
Ua.Add(&Ua, &Ub)
613606
} else {
614607
for bWord != 0 {
615608
aWord, bWord = bWord, aWord%bWord
@@ -1162,41 +1155,33 @@ fn lehmerSimulate(A: Int, B: Int): (u0: Word, u1: Word, v0: Word, v1: Word, even
11621155
// For even == true: u0, v1 >= 0 && u1, v0 <= 0
11631156
// For even == false: u0, v1 <= 0 && u1, v0 >= 0
11641157
// q, r, s, t are temporary variables to avoid allocations in the multiplication.
1165-
fn lehmerUpdate(mut &A: *Int, mut &B: *Int, mut &q: *Int, mut &r: *Int, mut &s: *Int, mut &t: *Int,
1158+
fn lehmerUpdate(mut &A: *Int, mut &B: *Int, mut &q: *Int, mut &r: *Int,
11661159
u0: Word, u1: Word, v0: Word, v1: Word, even: bool) {
1167-
setWV(&t.abs, u0)
1168-
setWV(&s.abs, v0)
1169-
t.neg = !even
1170-
s.neg = even
1171-
1172-
t.Mul(A, t)
1173-
s.Mul(B, s)
1174-
1175-
setWV(&r.abs, u1)
1176-
setWV(&q.abs, v1)
1177-
r.neg = even
1178-
q.neg = !even
1179-
1180-
r.Mul(A, r)
1181-
q.Mul(B, q)
1160+
mulWI(q, B, even, v0)
1161+
mulWI(r, A, even, u1)
1162+
mulWI(A, A, !even, u0)
1163+
mulWI(B, B, !even, v1)
1164+
A.Add(A, q)
1165+
B.Add(B, r)
1166+
}
11821167

1183-
A.Add(t, s)
1184-
B.Add(r, q)
1168+
fn mulWI(mut &z: *Int, mut &x: *Int, neg: bool, w: Word) {
1169+
mulAddWW(&z.abs, x.abs, w, 0)
1170+
z.neg = x.neg != neg
11851171
}
11861172

11871173
// Performs a single step of the Euclidean GCD algorithm
11881174
// if extended is true, it also updates the cosequence Ua, Ub.
11891175
fn euclidUpdate(mut &A: *Int, mut &B: *Int, mut &Ua: *Int, mut &Ub: *Int, mut &q: *Int,
1190-
mut &r: *Int, mut &s: *Int, mut &t: *Int, extended: bool) {
1176+
mut &r: *Int, extended: bool) {
11911177
q.QuoRem(A, B, r)
11921178

1193-
*A, *B, *r = *B, *r, *A
1194-
11951179
if extended {
1196-
// Ua, Ub = Ub, Ua - q*Ub
1197-
t.Set(Ub)
1198-
s.Mul(Ub, q)
1199-
Ub.Sub(Ua, s)
1200-
Ua.Set(t)
1180+
// Ua, Ub = Ub, Ua-q*Ub
1181+
q.Mul(q, Ub)
1182+
*Ua, *Ub = *Ub, *Ua
1183+
Ub.Sub(Ub, q)
12011184
}
1185+
1186+
*A, *B, *r = *B, *r, *A
12021187
}

std/math/big/worddiv.jule

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ a full wide digit for the last step:
278278
---------------------
279279
[ rem ]
280280

281-
Today, the code in divRecStep works like the first example. Perhaps in
281+
Today, the code in divRecursiveStep works like the first example. Perhaps in
282282
the future we will make it work like the alternative, to avoid a special case
283283
in the final iteration.
284284

@@ -621,7 +621,7 @@ fn divLargeW(mut &z: *[]Word, mut &u: *[]Word, uIn: []Word, vIn: []Word) {
621621
if n < divRecursiveThreshold {
622622
divBasic(z, u, v)
623623
} else {
624-
divRec(z, u, v)
624+
divRecursive(z, u, v)
625625
}
626626

627627
*z = normW(*z)
@@ -631,7 +631,7 @@ fn divLargeW(mut &z: *[]Word, mut &u: *[]Word, uIn: []Word, vIn: []Word) {
631631
*u = normW(*u)
632632
}
633633

634-
// The number of divisor digits at which point divRec is faster than divBasic.
634+
// The number of divisor digits at which point divRecursive is faster than divBasic.
635635
const divRecursiveThreshold = 100
636636

637637
// Reports whether the two digit numbers x1 x2 > y1 y2.
@@ -685,16 +685,9 @@ fn divBasic(mut &q: *[]Word, mut &u: *[]Word, v: []Word) {
685685
if rhat < prevRhat {
686686
break
687687
}
688-
// No need for a full mulWW: x1, x2 = mulWW(qhat, vn2)
688+
// TODO: No need for a full mulWW.
689689
// x2 += vn2; if x2 overflows, x1++
690-
// TODO: Make sure this is a good exchange with mulWW version.
691-
{
692-
_x2, carry := bits::Add(uint(x2), uint(vn2), 0)
693-
if carry == 0 {
694-
x1++
695-
}
696-
x2 = Word(_x2)
697-
}
690+
x1, x2 = mulWW(qhat, vn2)
698691
}
699692
}
700693

@@ -732,8 +725,8 @@ fn divBasic(mut &q: *[]Word, mut &u: *[]Word, v: []Word) {
732725
// It overwrites z with ⌊u/v⌋ and overwrites u with the remainder r.
733726
// z must be large enough to hold ⌊u/v⌋.
734727
// This function is just for allocating and freeing temporaries
735-
// around divRecStep, the real implementation.
736-
fn divRec(mut &z: *[]Word, mut &u: *[]Word, mut v: []Word) {
728+
// around divRecursiveStep, the real implementation.
729+
fn divRecursive(mut &z: *[]Word, mut &u: *[]Word, mut v: []Word) {
737730
// Recursion depth is (much) less than 2 log₂(len(v)).
738731
// Allocate a slice of temporaries to be reused across recursion,
739732
// plus one extra temporary not live across the recursion.
@@ -742,15 +735,15 @@ fn divRec(mut &z: *[]Word, mut &u: *[]Word, mut v: []Word) {
742735
mut temps := make([][]Word, recDepth)
743736

744737
clearW(*z)
745-
divRecStep(z, *u, v, 0, tmp, temps)
738+
divRecursiveStep(z, *u, v, 0, tmp, temps)
746739
}
747740

748741
// The actual implementation of recursive division.
749742
// It adds ⌊u/v⌋ to z and overwrites u with the remainder r.
750743
// z must be large enough to hold ⌊u/v⌋.
751744
// It uses temps[depth] (allocating if needed) as a temporary live across
752745
// the recursive call. It also uses tmp, but not live across the recursion.
753-
fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
746+
fn divRecursiveStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
754747
depth: int, mut tmp: []Word, mut temps: [][]Word) {
755748
// u is a subsection of the original and may have leading zeros.
756749
// TODO: The v = normW(v) is useless and should be removed.
@@ -783,7 +776,7 @@ fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
783776

784777
// Allocate words for qhat below.
785778
if temps[depth] == nil {
786-
temps[depth] = getW(n) // TODO: Can be just W+1.
779+
temps[depth] = getW(W + 1)
787780
} else {
788781
temps[depth] = makeW(temps[depth], W+1)
789782
}
@@ -816,11 +809,11 @@ fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
816809
// Compute the 2-by-1 guess q̂, leaving r̂ in uu[s:W+n].
817810
mut qhat := temps[depth]
818811
clearW(qhat)
819-
divRecStep(&qhat, uu[s:W+n], v[s:], depth+1, tmp, temps)
812+
divRecursiveStep(&qhat, uu[s:W+n], v[s:], depth+1, tmp, temps)
820813
qhat = normW(qhat)
821814

822815
// Extend to a 3-by-2 quotient and remainder.
823-
// Because divRecStep overwrote the top part of uu with
816+
// Because divRecursiveStep overwrote the top part of uu with
824817
// the remainder r̂, the full uu already contains the equivalent
825818
// of r̂·B + uₙ₋₂ from the “Refining Guesses” discussion.
826819
// Subtracting q̂·vₙ₋₂ from it will compute the full-length remainder.
@@ -847,7 +840,7 @@ fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
847840
addAtW(uu[s:], v[s:], 0)
848841
}
849842
if cmpW(qhatv, normW(uu)) > 0 {
850-
panic("math/big: divRecStep: impossible")
843+
panic("math/big: divRecursiveStep: impossible")
851844
}
852845
c := subVV(uu[:len(qhatv)], uu[:len(qhatv)], qhatv)
853846
if c > 0 {
@@ -864,7 +857,7 @@ fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
864857
s := W - 1
865858
mut qhat := temps[depth]
866859
clearW(qhat)
867-
divRecStep(&qhat, normW(u[s:]), v[s:], depth+1, tmp, temps)
860+
divRecursiveStep(&qhat, normW(u[s:]), v[s:], depth+1, tmp, temps)
868861
qhat = normW(qhat)
869862
mut qhatv := makeW(tmp, 3*n)
870863
clearW(qhatv)
@@ -882,14 +875,14 @@ fn divRecStep(mut &z: *[]Word, mut u: []Word, mut v: []Word,
882875
}
883876
}
884877
if cmpW(qhatv, normW(u)) > 0 {
885-
panic("math/big: divRecStep: impossible")
878+
panic("math/big: divRecursiveStep: impossible")
886879
}
887880
mut c := subVV(u[0:len(qhatv)], u[0:len(qhatv)], qhatv)
888881
if c > 0 {
889882
c = subVW(u[len(qhatv):], u[len(qhatv):], c)
890883
}
891884
if c > 0 {
892-
panic("math/big: divRecStep: impossible")
885+
panic("math/big: divRecursiveStep: impossible")
893886
}
894887

895888
// Done!

0 commit comments

Comments
 (0)