@@ -6,15 +6,17 @@ const std = @import("std");
66pub fn gcd (a : anytype , b : anytype ) @TypeOf (a , b ) {
77 const N = switch (@TypeOf (a , b )) {
88 // convert comptime_int to some sized int type for @ctz
9- comptime_int = > std .math .IntFittingRange (@min ( a , b ) , @max (a , b )),
9+ comptime_int = > std .math .IntFittingRange (0 , @max (a , b )),
1010 else = > | T | T ,
1111 };
12+
1213 if (@typeInfo (N ) != .int or @typeInfo (N ).int .signedness != .unsigned ) {
13- @compileError ("`a` and `b` must be usigned integers" );
14+ @compileError ("`a` and `b` must be unsigned integers" );
1415 }
1516
1617 // using an optimised form of Stein's algorithm:
1718 // https://en.wikipedia.org/wiki/Binary_GCD_algorithm
19+
1820 std .debug .assert (a != 0 or b != 0 );
1921
2022 if (a == 0 ) return b ;
@@ -26,25 +28,27 @@ pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
2628 const xz = @ctz (x );
2729 const yz = @ctz (y );
2830 const shift = @min (xz , yz );
29- x >>= @intCast (xz );
30- y >>= @intCast (yz );
31-
32- var diff = y -% x ;
33- while (diff != 0 ) : (diff = y -% x ) {
34- // ctz is invariant under negation, we
35- // put it here to ease data dependencies,
36- // makes the CPU happy.
37- const zeros = @ctz (diff );
38- if (x > y ) diff = -% diff ;
39- y = @min (x , y );
40- x = diff >> @intCast (zeros );
31+ x = @shrExact (x , @intCast (xz ));
32+ y = @shrExact (y , @intCast (yz ));
33+
34+ var y_minus_x = y -% x ;
35+ while (y_minus_x != 0 ) : (y_minus_x = y -% x ) {
36+ const copy_x = x ;
37+ const zeros = @ctz (y_minus_x );
38+ const carry = x < y ;
39+ x -%= y ;
40+ if (carry ) {
41+ x = y_minus_x ;
42+ y = copy_x ;
43+ }
44+ x = @shrExact (x , @intCast (zeros ));
4145 }
42- return y << @intCast (shift );
46+
47+ return @shlExact (y , @intCast (shift ));
4348}
4449
4550test gcd {
4651 const expectEqual = std .testing .expectEqual ;
47-
4852 try expectEqual (gcd (0 , 5 ), 5 );
4953 try expectEqual (gcd (5 , 0 ), 5 );
5054 try expectEqual (gcd (8 , 12 ), 4 );
0 commit comments