@@ -57,6 +57,174 @@ test Barrett {
5757 try std .testing .expectEqual (@as (u32 , 2147483646 ), b3 .mul (1073741824 , 2147483645 ));
5858}
5959
60+ // (x ^ n) % m
61+ pub fn comptimePowMod (x : comptime_int , n : comptime_int , m : comptime_int ) comptime_int {
62+ if (! (0 <= n and 1 <= m )) {
63+ @compileError ("Arguments do not satisfy 0 <= n and 1 <= m" );
64+ }
65+ if (m == 1 ) {
66+ return 0 ;
67+ }
68+ var _n = n ;
69+ var r = 1 ;
70+ var y = @mod (x , m );
71+ while (_n > 0 ) : (_n >>= 1 ) {
72+ if (_n & 1 == 1 ) {
73+ r = (r * y ) % m ;
74+ }
75+ y = (y * y ) % m ;
76+ }
77+ return r ;
78+ }
79+
80+ test comptimePowMod {
81+ const i32max = std .math .maxInt (i32 );
82+ const i64max = std .math .maxInt (i64 );
83+ const tests = comptime &[_ ]struct { x : comptime_int , n : comptime_int , m : comptime_int , expects : comptime_int }{
84+ .{ .x = 0 , .n = 0 , .m = 1 , .expects = 0 },
85+ .{ .x = 0 , .n = 0 , .m = 3 , .expects = 1 },
86+ .{ .x = 0 , .n = 0 , .m = 723 , .expects = 1 },
87+ .{ .x = 0 , .n = 0 , .m = 998244353 , .expects = 1 },
88+ .{ .x = 0 , .n = 0 , .m = i32max , .expects = 1 },
89+
90+ .{ .x = 0 , .n = 1 , .m = 1 , .expects = 0 },
91+ .{ .x = 0 , .n = 1 , .m = 3 , .expects = 0 },
92+ .{ .x = 0 , .n = 1 , .m = 723 , .expects = 0 },
93+ .{ .x = 0 , .n = 1 , .m = 998244353 , .expects = 0 },
94+ .{ .x = 0 , .n = 1 , .m = i32max , .expects = 0 },
95+
96+ .{ .x = 0 , .n = i64max , .m = 1 , .expects = 0 },
97+ .{ .x = 0 , .n = i64max , .m = 3 , .expects = 0 },
98+ .{ .x = 0 , .n = i64max , .m = 723 , .expects = 0 },
99+ .{ .x = 0 , .n = i64max , .m = 998244353 , .expects = 0 },
100+ .{ .x = 0 , .n = i64max , .m = i32max , .expects = 0 },
101+
102+ .{ .x = 1 , .n = 0 , .m = 1 , .expects = 0 },
103+ .{ .x = 1 , .n = 0 , .m = 3 , .expects = 1 },
104+ .{ .x = 1 , .n = 0 , .m = 723 , .expects = 1 },
105+ .{ .x = 1 , .n = 0 , .m = 998244353 , .expects = 1 },
106+ .{ .x = 1 , .n = 0 , .m = i32max , .expects = 1 },
107+
108+ .{ .x = 1 , .n = 1 , .m = 1 , .expects = 0 },
109+ .{ .x = 1 , .n = 1 , .m = 3 , .expects = 1 },
110+ .{ .x = 1 , .n = 1 , .m = 723 , .expects = 1 },
111+ .{ .x = 1 , .n = 1 , .m = 998244353 , .expects = 1 },
112+ .{ .x = 1 , .n = 1 , .m = i32max , .expects = 1 },
113+
114+ .{ .x = 1 , .n = i64max , .m = 1 , .expects = 0 },
115+ .{ .x = 1 , .n = i64max , .m = 3 , .expects = 1 },
116+ .{ .x = 1 , .n = i64max , .m = 723 , .expects = 1 },
117+ .{ .x = 1 , .n = i64max , .m = 998244353 , .expects = 1 },
118+ .{ .x = 1 , .n = i64max , .m = i32max , .expects = 1 },
119+
120+ .{ .x = i64max , .n = 0 , .m = 1 , .expects = 0 },
121+ .{ .x = i64max , .n = 0 , .m = 3 , .expects = 1 },
122+ .{ .x = i64max , .n = 0 , .m = 723 , .expects = 1 },
123+ .{ .x = i64max , .n = 0 , .m = 998244353 , .expects = 1 },
124+ .{ .x = i64max , .n = 0 , .m = i32max , .expects = 1 },
125+
126+ .{ .x = i64max , .n = i64max , .m = 1 , .expects = 0 },
127+ .{ .x = i64max , .n = i64max , .m = 3 , .expects = 1 },
128+ .{ .x = i64max , .n = i64max , .m = 723 , .expects = 640 },
129+ .{ .x = i64max , .n = i64max , .m = 998244353 , .expects = 683296792 },
130+ .{ .x = i64max , .n = i64max , .m = i32max , .expects = 1 },
131+
132+ .{ .x = 2 , .n = 3 , .m = 1_000_000_007 , .expects = 8 },
133+ .{ .x = 5 , .n = 7 , .m = 1_000_000_007 , .expects = 78125 },
134+ .{ .x = 123 , .n = 456 , .m = 1_000_000_007 , .expects = 565291922 },
135+ };
136+ inline for (tests ) | t | {
137+ try std .testing .expectEqual (t .expects , comptimePowMod (t .x , t .n , t .m ));
138+ }
139+ }
140+
141+ // Reference:
142+ // M. Forisek and J. Jancina,
143+ // Fast Primality Testing for Integers That Fit into a Machine Word
144+ pub fn comptimeIsPrime (n : comptime_int ) bool {
145+ if (n == 2 ) {
146+ return true ;
147+ } else if (n <= 1 or n % 2 == 0 ) {
148+ return false ;
149+ }
150+ const base = std .math .log2 (n );
151+ const upper = (1 << base ) - 1 ;
152+ const magnitude_bits = if (upper >= n ) base else base + 1 ;
153+
154+ // Reference:
155+ // https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
156+ // https://miller-rabin.appspot.com/
157+ // https://oeis.org/A014233
158+ const seq = switch (magnitude_bits ) {
159+ 0... 32 = > [_ ]comptime_int { 2 , 7 , 61 },
160+ 33... 64 = > [_ ]comptime_int { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 },
161+ else = > @compileError ("n is too large" ),
162+ };
163+
164+ inline for (seq ) | v | {
165+ if (n == v ) {
166+ return true ;
167+ }
168+ }
169+
170+ const d = comptime blk : {
171+ var d = n - 1 ;
172+ while (d % 2 == 0 ) {
173+ d /= 2 ;
174+ }
175+ break :blk d ;
176+ };
177+ inline for (seq ) | a | {
178+ const t , const y = comptime blk : {
179+ var t = d ;
180+ var y = comptimePowMod (a , t , n );
181+ while (t != n - 1 and y != 1 and y != n - 1 ) : (t <<= 1 ) {
182+ y = y * y % n ;
183+ }
184+ break :blk .{ t , y };
185+ };
186+ if (y != n - 1 and t % 2 == 0 ) {
187+ return false ;
188+ }
189+ }
190+ return true ;
191+ }
192+
193+ test comptimeIsPrime {
194+ const tests = comptime &[_ ]struct { x : comptime_int , expects : bool }{
195+ .{ .x = 0 , .expects = false },
196+ .{ .x = 1 , .expects = false },
197+ .{ .x = 2 , .expects = true },
198+ .{ .x = 3 , .expects = true },
199+ .{ .x = 4 , .expects = false },
200+ .{ .x = 5 , .expects = true },
201+ .{ .x = 6 , .expects = false },
202+ .{ .x = 7 , .expects = true },
203+ .{ .x = 8 , .expects = false },
204+ .{ .x = 9 , .expects = false },
205+
206+ // .{ .x = 57, .expect = true },
207+ .{ .x = 57 , .expects = false },
208+ .{ .x = 58 , .expects = false },
209+ .{ .x = 59 , .expects = true },
210+ .{ .x = 60 , .expects = false },
211+ .{ .x = 61 , .expects = true },
212+ .{ .x = 62 , .expects = false },
213+
214+ .{ .x = 701928443 , .expects = false },
215+ .{ .x = 998244353 , .expects = true },
216+ .{ .x = 1_000_000_000 , .expects = false },
217+ .{ .x = 1_000_000_007 , .expects = true },
218+
219+ .{ .x = std .math .maxInt (i32 ), .expects = true },
220+ .{ .x = std .math .maxInt (i62 ), .expects = true },
221+ .{ .x = std .math .maxInt (i64 ), .expects = false },
222+ };
223+ inline for (tests ) | t | {
224+ try std .testing .expectEqual (t .expects , comptimeIsPrime (t .x ));
225+ }
226+ }
227+
60228pub fn invGcd (a : i64 , b : i64 ) struct { i64 , i64 } {
61229 const c = @mod (a , b );
62230 if (c == 0 ) {
0 commit comments