Skip to content

Commit 2b70de6

Browse files
✨ feat(Integer): Implement GCD methods.
1 parent 6535fa6 commit 2b70de6

File tree

3 files changed

+381
-0
lines changed

3 files changed

+381
-0
lines changed

src/Integer.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
jz , cmp , eq ,
77
add , _sub , mul , _idivmod , _pow_double ,
88
increment ,
9+
euclidean_algorithm , extended_euclidean_algorithm ,
910
} from '@aureooms/js-integer-big-endian' ;
1011

1112
import { MIN_NUMBER , MAX_NUMBER , MAX_BASE } from './_limits' ;
@@ -367,6 +368,41 @@ export class Integer {
367368
return this.cmp( other ) < 0 ;
368369
}
369370

371+
gcd ( other ) {
372+
const r = this.base ;
373+
const a = this.limbs ;
374+
const b = other._limbs_in_base( r ) ;
375+
const [ d , di , dj ] = euclidean_algorithm( r , a , 0 , a.length , b , 0 , b.length ) ;
376+
const gcd = _alloc( dj - di ) ;
377+
_copy( d , di , dj , gcd , 0 ) ;
378+
return new Integer( r , 0 , gcd ) ;
379+
}
380+
381+
egcd ( other ) {
382+
const r = this.base ;
383+
const a = this.limbs ;
384+
const b = other._limbs_in_base( r ) ;
385+
const [ R0 , R0i , S0 , S0i , T0 , T0i , S1 , S1i , T1 , T1i , steps ] = extended_euclidean_algorithm( r , a , 0 , a.length , b , 0 , b.length ) ;
386+
const gcd = _alloc( R0.length - R0i ) ;
387+
_copy( R0 , R0i , R0.length , gcd , 0 ) ;
388+
const x = _alloc( S0.length - S0i ) ;
389+
_copy( S0 , S0i , S0.length , x , 0 ) ;
390+
const y = _alloc( T0.length - T0i ) ;
391+
_copy( T0 , T0i , T0.length , y , 0 ) ;
392+
const u = _alloc( S1.length - S1i ) ;
393+
_copy( S1 , S1i , S1.length , u , 0 ) ;
394+
const v = _alloc( T1.length - T1i ) ;
395+
_copy( T1 , T1i , T1.length , v , 0 ) ;
396+
return { // TODO use immutable zero
397+
gcd: new Integer(r, 0, gcd) ,
398+
x: x.length ? new Integer(r, this.is_negative ^ ((steps % 2)-1), x) : new Integer(r, 0, [0]) ,
399+
y: y.length ? new Integer(r, other.is_negative ^ (-(steps % 2)), y) : new Integer(r, 0, [0]) ,
400+
u: u.length ? new Integer(r, this.is_negative ^ (-(steps % 2)), u) : new Integer(r, 0, [0]) ,
401+
v: v.length ? new Integer(r, other.is_negative ^ ((steps % 2)-1), v) : new Integer(r, 0, [0]) ,
402+
} ;
403+
}
404+
405+
370406
ne ( other ) {
371407
return this.cmp( other ) !== 0 ;
372408
}

test/src/Integer/egcd.js

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
import test from 'ava' ;
2+
3+
import { ZZ } from '../../../src' ;
4+
5+
function macro ( t , a , b , expected ) {
6+
7+
const { gcd , x , y , u , v } = a.egcd(b) ;
8+
9+
t.deepEqual(
10+
{
11+
gcd: expected.gcd.toString(),
12+
x: expected.x.toString(),
13+
y: expected.y.toString(),
14+
u: expected.u.toString(),
15+
v: expected.v.toString(),
16+
} ,
17+
{
18+
gcd: gcd.toString(),
19+
x: x.toString(),
20+
y: y.toString(),
21+
u: u.toString(),
22+
v: v.toString(),
23+
} ,
24+
) ;
25+
26+
t.true( a.mul(x).add(b.mul(y)).eq(gcd) ) ;
27+
t.true( a.mul(u).add(b.mul(v)).iszero() ) ;
28+
29+
}
30+
31+
macro.title = (providedTitle, a, b, { gcd , x , y , u , v }) => `egcd(${a.toString()},${b.toString()}) = ${gcd.toString()} = ${x.toString()} * ${a.toString()} + ${y.toString()} * ${b.toString()} AND ${u.toString()} * ${a.toString()} + ${v.toString()} * ${b.toString()} = 0` ;
32+
33+
test( macro , ZZ.$0() , ZZ.$0() , {
34+
gcd: ZZ.$0() ,
35+
x: ZZ.$1() ,
36+
y: ZZ.$0() ,
37+
u: ZZ.$0() ,
38+
v: ZZ.$1() ,
39+
} ) ;
40+
41+
test( macro , ZZ.$1() , ZZ.$0() , {
42+
gcd: ZZ.$1() ,
43+
x: ZZ.$1() ,
44+
y: ZZ.$0() ,
45+
u: ZZ.$0() ,
46+
v: ZZ.$1() ,
47+
} ) ;
48+
49+
test( macro , ZZ.$_1() , ZZ.$0() , {
50+
gcd: ZZ.$1() ,
51+
x: ZZ.$_1() ,
52+
y: ZZ.$0() ,
53+
u: ZZ.$0() ,
54+
v: ZZ.$1() ,
55+
} ) ;
56+
57+
test( macro , ZZ.$0() , ZZ.$1() , {
58+
gcd: ZZ.$1() ,
59+
x: ZZ.$0() ,
60+
y: ZZ.$1() ,
61+
u: ZZ.$1() ,
62+
v: ZZ.$0() ,
63+
} ) ;
64+
65+
66+
test( macro , ZZ.$1() , ZZ.$1() , {
67+
gcd: ZZ.$1() ,
68+
x: ZZ.$0() ,
69+
y: ZZ.$1() ,
70+
u: ZZ.$1() ,
71+
v: ZZ.$_1() ,
72+
} ) ;
73+
74+
test( macro , ZZ.from(2) , ZZ.from(3) , {
75+
gcd: ZZ.$1() ,
76+
x: ZZ.$_1() ,
77+
y: ZZ.$1() ,
78+
u: ZZ.from(3) ,
79+
v: ZZ.from(-2) ,
80+
} ) ;
81+
82+
test( macro , ZZ.from(3) , ZZ.from(4) , {
83+
gcd: ZZ.$1() ,
84+
x: ZZ.$_1() ,
85+
y: ZZ.$1() ,
86+
u: ZZ.from(4) ,
87+
v: ZZ.from(-3) ,
88+
} ) ;
89+
90+
test( macro , ZZ.from(2) , ZZ.from(4) , {
91+
gcd: ZZ.from(2) ,
92+
x: ZZ.$1() ,
93+
y: ZZ.$0() ,
94+
u: ZZ.from(-2) ,
95+
v: ZZ.from(1) ,
96+
} ) ;
97+
98+
test( macro ,
99+
ZZ.from('240') ,
100+
ZZ.from('46') ,
101+
{
102+
gcd: ZZ.from('2') ,
103+
x: ZZ.from('-9') ,
104+
y: ZZ.from('47') ,
105+
u: ZZ.from('23') ,
106+
v: ZZ.from('-120') ,
107+
}
108+
) ;
109+
110+
test( macro ,
111+
ZZ.from('999') ,
112+
ZZ.from('1') ,
113+
{
114+
gcd: ZZ.from('1') ,
115+
x: ZZ.from('0') ,
116+
y: ZZ.from('1') ,
117+
u: ZZ.from('1') ,
118+
v: ZZ.from('-999') ,
119+
}
120+
) ;
121+
122+
test( macro ,
123+
ZZ.from('999') ,
124+
ZZ.from('2') ,
125+
{
126+
gcd: ZZ.from('1') ,
127+
x: ZZ.from('1') ,
128+
y: ZZ.from('-499') ,
129+
u: ZZ.from('-2') ,
130+
v: ZZ.from('999') ,
131+
}
132+
) ;
133+
134+
test( macro ,
135+
ZZ.from('999') ,
136+
ZZ.from('3') ,
137+
{
138+
gcd: ZZ.from('3') ,
139+
x: ZZ.from('0') ,
140+
y: ZZ.from('1') ,
141+
u: ZZ.from('1') ,
142+
v: ZZ.from('-333') ,
143+
}
144+
) ;
145+
146+
test( macro ,
147+
ZZ.from('999') ,
148+
ZZ.from('4') ,
149+
{
150+
gcd: ZZ.from('1') ,
151+
x: ZZ.from('-1') ,
152+
y: ZZ.from('250') ,
153+
u: ZZ.from('4') ,
154+
v: ZZ.from('-999') ,
155+
}
156+
) ;
157+
158+
test( macro ,
159+
ZZ.from('999') ,
160+
ZZ.from('5') ,
161+
{
162+
gcd: ZZ.from('1') ,
163+
x: ZZ.from('-1') ,
164+
y: ZZ.from('200') ,
165+
u: ZZ.from('5') ,
166+
v: ZZ.from('-999') ,
167+
}
168+
) ;
169+
170+
test( macro ,
171+
ZZ.from('999') ,
172+
ZZ.from('6') ,
173+
{
174+
gcd: ZZ.from('3') ,
175+
x: ZZ.from('1') ,
176+
y: ZZ.from('-166') ,
177+
u: ZZ.from('-2') ,
178+
v: ZZ.from('333') ,
179+
}
180+
) ;
181+
182+
test( macro , ZZ.from('182719837289124387095732094932857943') , ZZ.from('218379128372') , {
183+
gcd: ZZ.from('1') ,
184+
x: ZZ.from('76391188915') ,
185+
y: ZZ.from('-63917214584236084614404336195015177') ,
186+
u: ZZ.from('-218379128372') ,
187+
v: ZZ.from('182719837289124387095732094932857943') ,
188+
} ) ;
189+
190+
test( macro , ZZ.from('89798763754892653453379597352537489494736') , ZZ.from('978') , {
191+
gcd: ZZ.from('6') ,
192+
x: ZZ.from('57') ,
193+
y: ZZ.from('-5233670280193130109246050152448503988957') ,
194+
u: ZZ.from('-163') ,
195+
v: ZZ.from('14966460625815442242229932892089581582456') ,
196+
} ) ;
197+
198+
test( macro , ZZ.from('1234567891011121314151617181920212223242526272829') , ZZ.from('1221') , {
199+
gcd: ZZ.from('3') ,
200+
x: ZZ.from('96') ,
201+
y: ZZ.from('-97066762929621331825188574499869265709486095161') ,
202+
u: ZZ.from('-407') ,
203+
v: ZZ.from('411522630337040438050539060640070741080842090943') ,
204+
} ) ;
205+
206+
test( macro , ZZ.from('8918391893892839282938092838273908') , ZZ.from('9238902830982083209836079238902830') , {
207+
gcd: ZZ.from('18') ,
208+
x: ZZ.from('-196260821226532250117922974202329') ,
209+
y: ZZ.from('189452248728694447618227772406725') ,
210+
u: ZZ.from('513272379499004622768671068827935') ,
211+
v: ZZ.from('-495466216327379960163227379904106') ,
212+
} ) ;
213+
214+
test( macro ,
215+
ZZ.from('37650526072328171936695291762250209370684337226819795603338569781977444693437332193180866661042770508342415236941382410000000000000000') ,
216+
ZZ.from('5696107759173612435215985096515090524728819689625373634782109911819800000000') ,
217+
{
218+
gcd: ZZ.from('12272004900965151087327615491240194950486574150898137749184200000000') ,
219+
x: ZZ.from('144546941') ,
220+
y: ZZ.from('-955436343708733262264503439145449113981822054797863712652831187021') ,
221+
u: ZZ.from('-464154619') ,
222+
v: ZZ.from('3068001225241287771831903872810048737621643537724534437296050000000') ,
223+
}
224+
) ;
225+
226+
test( macro ,
227+
ZZ.from('67119411938628137065042623053470295860352899292113831399681459932104429712541137526114591678492374912832861154037280650094772951318333'),
228+
ZZ.from('48179292498495773577650761587652514616005124004102188905857708802854991702951275654450870129244763211021514910115253990099546035191850322100208219724128209064592'),
229+
{
230+
gcd: ZZ.from('52310721570994454309603316505452077132504750494613585448247191340916543146521771411345313270814742631'),
231+
x: ZZ.from('-296597046898323364982738776313288773735618113657314569865757'),
232+
y: ZZ.from('413194514451840753795917890556861'),
233+
u: ZZ.from('921021370984308903809270483207432932984032809428309483208432'),
234+
v: ZZ.from('-1283090921380921830183472943789243')
235+
}
236+
) ;
237+
238+
test( macro ,
239+
ZZ.from('432098403928041873297392719837982749873983279473289479327493284793287498162393740164165983274864832164875183264') ,
240+
ZZ.from('7326498326418236481264389164986482164387619823741294681431648376821468213648217649873214639821') ,
241+
{
242+
gcd: ZZ.from('1') ,
243+
x: ZZ.from('-70043170308577841790901973523044584915855330486448613200408375753217211410942292553022335592') ,
244+
y: ZZ.from('4130969632145217993835085987649256842613363564725738267141839343576749111824609588437426577104148903196635909') ,
245+
u: ZZ.from('7326498326418236481264389164986482164387619823741294681431648376821468213648217649873214639821') ,
246+
v: ZZ.from('-432098403928041873297392719837982749873983279473289479327493284793287498162393740164165983274864832164875183264') ,
247+
}
248+
) ;
249+
250+
test( macro ,
251+
ZZ.from('432098403928041873297392719837982749873983279473289479327493284793287498162393740164165983274864832164875183264') ,
252+
ZZ.from('73264983264182364812643891649864821643876198237412946814316483768214682136482176498573214639821208') ,
253+
{
254+
gcd: ZZ.from('776') ,
255+
x: ZZ.from('-41694456345076671794355149523694764177222151506203366353494197000190744202549435308862685363723') ,
256+
y: ZZ.from('245903393909088985240590322881406144770470086821972948207378985268602417576411281642626798219110113136324431') ,
257+
u: ZZ.from('94413638227039129913200891301372192840046647213161013935974850216771497598559505797130431236883') ,
258+
v: ZZ.from('-556827840113456022290454535873689110662349586950115308411718150506813786291744510520832452673794886810406164') ,
259+
}
260+
) ;
261+
262+
test( macro ,
263+
ZZ.from('27464428722379302537066207419729547039001666019484342119914191943374771553192802830064910464708682216') ,
264+
ZZ.from('1235538557263503135080009921155585529085345118770499482621630836795609586373233972978224047042123566') ,
265+
{
266+
gcd: ZZ.from('74997500588773686591299665516865519759664950655978') ,
267+
x: ZZ.from('-7538724761555610090827422120944030717270450777967') ,
268+
y: ZZ.from('167576129173946225301933673091336299297968056079975') ,
269+
u: ZZ.from('16474396447399073360821991087445637049163796086747') ,
270+
v: ZZ.from('-366204586909799364719352460854718749306649209643172') ,
271+
}
272+
) ;
273+
274+
test( macro ,
275+
ZZ.from('-27464428722379302537066207419729547039001666019484342119914191943374771553192802830064910464708682216') ,
276+
ZZ.from('1235538557263503135080009921155585529085345118770499482621630836795609586373233972978224047042123566') ,
277+
{
278+
gcd: ZZ.from('74997500588773686591299665516865519759664950655978') ,
279+
x: ZZ.from('7538724761555610090827422120944030717270450777967') ,
280+
y: ZZ.from('167576129173946225301933673091336299297968056079975') ,
281+
u: ZZ.from('-16474396447399073360821991087445637049163796086747') ,
282+
v: ZZ.from('-366204586909799364719352460854718749306649209643172') ,
283+
}
284+
) ;
285+
286+
test( macro ,
287+
ZZ.from('27464428722379302537066207419729547039001666019484342119914191943374771553192802830064910464708682216') ,
288+
ZZ.from('-1235538557263503135080009921155585529085345118770499482621630836795609586373233972978224047042123566') ,
289+
{
290+
gcd: ZZ.from('74997500588773686591299665516865519759664950655978') ,
291+
x: ZZ.from('-7538724761555610090827422120944030717270450777967') ,
292+
y: ZZ.from('-167576129173946225301933673091336299297968056079975') ,
293+
u: ZZ.from('16474396447399073360821991087445637049163796086747') ,
294+
v: ZZ.from('366204586909799364719352460854718749306649209643172') ,
295+
}
296+
) ;
297+
298+
299+
test( macro ,
300+
ZZ.from('-27464428722379302537066207419729547039001666019484342119914191943374771553192802830064910464708682216') ,
301+
ZZ.from('-1235538557263503135080009921155585529085345118770499482621630836795609586373233972978224047042123566') ,
302+
{
303+
gcd: ZZ.from('74997500588773686591299665516865519759664950655978') ,
304+
x: ZZ.from('7538724761555610090827422120944030717270450777967') ,
305+
y: ZZ.from('-167576129173946225301933673091336299297968056079975') ,
306+
u: ZZ.from('-16474396447399073360821991087445637049163796086747') ,
307+
v: ZZ.from('366204586909799364719352460854718749306649209643172') ,
308+
}
309+
) ;
310+
311+
// test generated with https://github.com/aureooms-research/gcd

test/src/Integer/gcd.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import test from 'ava' ;
2+
3+
import { ZZ } from '../../../src' ;
4+
5+
function macro ( t , a , b , c ) {
6+
t.is( c.toString() , a.gcd(b).toString() ) ;
7+
}
8+
9+
macro.title = (providedTitle, a, b, c) => `gcd(${a.toString()},${b.toString()}) = ${c.toString()}` ;
10+
11+
test( macro , ZZ.$0() , ZZ.$0() , ZZ.$0() ) ;
12+
test( macro , ZZ.$1() , ZZ.$0() , ZZ.$1() ) ;
13+
test( macro , ZZ.$0() , ZZ.$1() , ZZ.$1() ) ;
14+
test( macro , ZZ.$1() , ZZ.$1() , ZZ.$1() ) ;
15+
test( macro , ZZ.from(2) , ZZ.from(3) , ZZ.$1() ) ;
16+
test( macro , ZZ.from(3) , ZZ.from(4) , ZZ.$1() ) ;
17+
test( macro , ZZ.from(2) , ZZ.from(4) , ZZ.from(2) ) ;
18+
test( macro , ZZ.from('182719837289124387095732094932857943') , ZZ.from(218379128372) , ZZ.$1() ) ;
19+
test( macro , ZZ.from('89798763754892653453379597352537489494736') , ZZ.from(978) , ZZ.from(6) ) ;
20+
test( macro , ZZ.from('1234567891011121314151617181920212223242526272829') , ZZ.from(1221) , ZZ.from(3) ) ;
21+
test( macro , ZZ.from('8918391893892839282938092838273908') , ZZ.from('9238902830982083209836079238902830') , ZZ.from(18) ) ;
22+
test( macro ,
23+
ZZ.from('37650526072328171936695291762250209370684337226819795603338569781977444693437332193180866661042770508342415236941382410000000000000000') ,
24+
ZZ.from('5696107759173612435215985096515090524728819689625373634782109911819800000000') ,
25+
ZZ.from('12272004900965151087327615491240194950486574150898137749184200000000')
26+
) ;
27+
28+
test( macro ,
29+
ZZ.from('67119411938628137065042623053470295860352899292113831399681459932104429712541137526114591678492374912832861154037280650094772951318333'),
30+
ZZ.from('48179292498495773577650761587652514616005124004102188905857708802854991702951275654450870129244763211021514910115253990099546035191850322100208219724128209064592'),
31+
ZZ.from('52310721570994454309603316505452077132504750494613585448247191340916543146521771411345313270814742631')
32+
) ;
33+
34+
// test generated with https://github.com/aureooms-research/gcd

0 commit comments

Comments
 (0)