@@ -8,30 +8,42 @@ const math = std.math;
88const assert = std .debug .assert ;
99const testing = std .testing ;
1010
11+ const UnsignedError = error {Overflow };
12+ const SignedError = error {
13+ Overflow ,
14+ Underflow ,
15+ DivisionByZero ,
16+ };
17+
1118/// Returns the power of x raised by the integer y (x^y).
1219///
1320/// Errors:
14- /// - Overflow: Integer overflow or Infinity
21+ /// - Overflow: Integer overflow
1522/// - Underflow: Absolute value of result smaller than 1
23+ /// - DivisionByZero: Undefined power.
1624///
1725/// Edge case rules ordered by precedence:
1826/// - powi(T, x, 0) = 1 unless T is i1, i0, u0
1927/// - powi(T, 0, x) = 0 when x > 0
20- /// - powi(T, 0, x) = Overflow
28+ /// - powi(T, 0, x) = DivisionByZero
2129/// - powi(T, 1, y) = 1
2230/// - powi(T, -1, y) = -1 for y an odd integer
2331/// - powi(T, -1, y) = 1 unless T is i1, i0, u0
2432/// - powi(T, -1, y) = Overflow
2533/// - powi(T, x, y) = Overflow when y >= @bitSizeOf(x)
2634/// - powi(T, x, y) = Underflow when y < 0
27- pub fn powi (comptime T : type , x : T , y : T ) (error {
28- Overflow ,
29- Underflow ,
30- }! T ) {
31- const bit_size = @typeInfo (T ).int .bits ;
35+ pub fn powi (comptime T : type , x : T , y : T ) (if (@typeInfo (T ) == .int and @typeInfo (T ).int .signedness == .unsigned )
36+ UnsignedError
37+ else
38+ SignedError )! T {
39+ const info = @typeInfo (T );
40+ if (info != .int and info != .comptime_int )
41+ @compileError ("powi not implemented for " ++ @typeName (T ));
42+
43+ const is_unsigned = info == .int and info .int .signedness == .unsigned ;
3244
3345 // `y & 1 == 0` won't compile when `does_one_overflow`.
34- const does_one_overflow = math .maxInt (T ) < 1 ;
46+ const does_one_overflow = info == .int and math .maxInt (T ) < 1 ;
3547 const is_y_even = ! does_one_overflow and y & 1 == 0 ;
3648
3749 if (x == 1 or y == 0 or (x == -1 and is_y_even )) {
@@ -50,15 +62,17 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
5062 if (y > 0 ) {
5163 return 0 ;
5264 } else {
53- // Infinity/NaN, not overflow in strict sense
54- return error .Overflow ;
65+ if ( is_unsigned ) unreachable ;
66+ return error .DivisionByZero ;
5567 }
5668 }
69+
5770 // x >= 2 or x <= -2 from this point
58- if (y >= bit_size ) {
71+ if (info == .int and y >= info . int . bits ) {
5972 return error .Overflow ;
6073 }
6174 if (y < 0 ) {
75+ if (is_unsigned ) unreachable ;
6276 return error .Underflow ;
6377 }
6478
@@ -71,27 +85,32 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
7185
7286 while (exp > 1 ) {
7387 if (exp & 1 == 1 ) {
74- const ov = @mulWithOverflow (acc , base );
75- if (ov [1 ] != 0 ) return error .Overflow ;
76- acc = ov [0 ];
88+ acc = try mul (T , acc , base );
7789 }
7890
7991 exp >>= 1 ;
8092
81- const ov = @mulWithOverflow (base , base );
82- if (ov [1 ] != 0 ) return error .Overflow ;
83- base = ov [0 ];
93+ base = try mul (T , base , base );
8494 }
8595
8696 if (exp == 1 ) {
87- const ov = @mulWithOverflow (acc , base );
88- if (ov [1 ] != 0 ) return error .Overflow ;
89- acc = ov [0 ];
97+ acc = try mul (T , acc , base );
9098 }
9199
92100 return acc ;
93101}
94102
103+ inline fn mul (comptime T : type , x : T , y : T ) error {Overflow }! T {
104+ return switch (@typeInfo (T )) {
105+ .int = > {
106+ const prod , const overflow = @mulWithOverflow (x , y );
107+ return if (overflow != 0 ) error .Overflow else prod ;
108+ },
109+ .comptime_int = > x * y ,
110+ else = > unreachable ,
111+ };
112+ }
113+
95114test powi {
96115 try testing .expectError (error .Overflow , powi (i8 , -66 , 6 ));
97116 try testing .expectError (error .Overflow , powi (i16 , -13 , 13 ));
@@ -106,13 +125,17 @@ test powi {
106125 try testing .expect ((try powi (i64 , -36 , 6 )) == 2176782336 );
107126 try testing .expect ((try powi (i17 , -2 , 15 )) == -32768 );
108127 try testing .expect ((try powi (i42 , -5 , 7 )) == -78125 );
128+ try testing .expect ((try powi (comptime_int , -12345 , 11 )) == -1014850422703912515858714960329315071728515625 );
129+ try comptime testing .expect ((try powi (comptime_int , 13 , 5 )) == 371293 );
109130
110131 try testing .expect ((try powi (u8 , 6 , 2 )) == 36 );
111132 try testing .expect ((try powi (u16 , 5 , 4 )) == 625 );
112133 try testing .expect ((try powi (u32 , 12 , 6 )) == 2985984 );
113134 try testing .expect ((try powi (u64 , 34 , 2 )) == 1156 );
114135 try testing .expect ((try powi (u17 , 16 , 3 )) == 4096 );
115136 try testing .expect ((try powi (u42 , 34 , 6 )) == 1544804416 );
137+ try testing .expect ((try powi (comptime_int , 54321 , 9 )) == 4118222497610732111054528594901610509007281 );
138+ try comptime testing .expect ((try powi (comptime_int , 51 , 3 )) == 132651 );
116139
117140 try testing .expectError (error .Overflow , powi (i8 , 120 , 7 ));
118141 try testing .expectError (error .Overflow , powi (i16 , 73 , 15 ));
@@ -157,6 +180,8 @@ test "powi.special" {
157180 try testing .expect ((try powi (i64 , -1 , 6 )) == 1 );
158181 try testing .expect ((try powi (i17 , -1 , 15 )) == -1 );
159182 try testing .expect ((try powi (i42 , -1 , 7 )) == -1 );
183+ try testing .expect ((try powi (comptime_int , -1 , 5 )) == -1 );
184+ try comptime testing .expect ((try powi (comptime_int , -1 , 3 )) == -1 );
160185
161186 try testing .expect ((try powi (u8 , 1 , 2 )) == 1 );
162187 try testing .expect ((try powi (u16 , 1 , 4 )) == 1 );
@@ -185,13 +210,15 @@ test "powi.special" {
185210 try testing .expect ((try powi (u64 , 34 , 0 )) == 1 );
186211 try testing .expect ((try powi (u17 , 16 , 0 )) == 1 );
187212 try testing .expect ((try powi (u42 , 34 , 0 )) == 1 );
213+ try testing .expect ((try powi (comptime_int , 41 , 0 )) == 1 );
214+ try comptime testing .expect ((try powi (comptime_int , 43 , 0 )) == 1 );
188215}
189216
190217test "powi.narrow" {
191218 try testing .expectError (error .Overflow , powi (u0 , 0 , 0 ));
192219 try testing .expectError (error .Overflow , powi (i0 , 0 , 0 ));
193220 try testing .expectError (error .Overflow , powi (i1 , 0 , 0 ));
194221 try testing .expectError (error .Overflow , powi (i1 , -1 , 0 ));
195- try testing .expectError (error .Overflow , powi (i1 , 0 , -1 ));
222+ try testing .expectError (error .DivisionByZero , powi (i1 , 0 , -1 ));
196223 try testing .expect ((try powi (i1 , -1 , -1 )) == -1 );
197224}
0 commit comments