| ... | ... | @@ -53,8 +53,10 @@ pub const FieldElementError = error{NonCanonical}; |
| 53 | 53 | /// Invalid representation (Montgomery vs non-Montgomery domain.) |
| 54 | 54 | pub const RepresentationError = error{UnexpectedRepresentation}; |
| 55 | 55 | |
| 56 | pub const DivisionByZeroError = error{DivisionByZero}; |
| 57 | |
| 56 | 58 | /// The set of all possible errors `std.crypto.ff` functions can return. |
| 57 | | pub const Error = OverflowError || InvalidModulusError || NullExponentError || FieldElementError || RepresentationError; |
| 59 | pub const Error = OverflowError || InvalidModulusError || NullExponentError || FieldElementError || RepresentationError || DivisionByZeroError; |
| 58 | 60 | |
| 59 | 61 | /// An unsigned big integer with a fixed maximum size (`max_bits`), suitable for cryptographic operations. |
| 60 | 62 | /// Storage rounds up to whole limbs and can hold up to `capacity_bits` bits. |
| ... | ... | @@ -298,6 +300,54 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 298 | 300 | @memset(x_limbs[active..], 0); |
| 299 | 301 | } |
| 300 | 302 | |
| 303 | /// Divides by `divisor` in place and returns the remainder. |
| 304 | /// Returns `error.DivisionByZero` without changing the integer if `divisor` is zero. |
| 305 | pub fn divRemPublic(x: *Self, divisor: usize) DivisionByZeroError!usize { |
| 306 | if (divisor == 0) return error.DivisionByZero; |
| 307 | const Wide = @Int(.unsigned, 2 * @bitSizeOf(Limb)); |
| 308 | const x_limbs = x.limbs(); |
| 309 | var rem: Limb = 0; |
| 310 | var i = x.limbs_len; |
| 311 | while (i != 0) { |
| 312 | i -= 1; |
| 313 | const num = (@as(Wide, rem) << t_bits) | x_limbs[i]; |
| 314 | x_limbs[i] = @intCast(num / divisor); |
| 315 | rem = @intCast(num % divisor); |
| 316 | } |
| 317 | return rem; |
| 318 | } |
| 319 | |
| 320 | /// Returns the greatest common divisor, with gcd(x, 0) = x. |
| 321 | /// Returns `error.DivisionByZero` if both operands are zero. |
| 322 | pub fn gcdPublic(x: Self, y: Self) DivisionByZeroError!Self { |
| 323 | if (x.isZero() and y.isZero()) return error.DivisionByZero; |
| 324 | if (x.isZero()) return y; |
| 325 | if (y.isZero()) return x; |
| 326 | |
| 327 | var a = x; |
| 328 | var b = y; |
| 329 | const len = @max(a.limbs_len, b.limbs_len); |
| 330 | a.expandTo(len); |
| 331 | b.expandTo(len); |
| 332 | |
| 333 | const a_shift = a.trailingZeroBitsPublic(); |
| 334 | const b_shift = b.trailingZeroBitsPublic(); |
| 335 | a.shiftRightPublic(a_shift); |
| 336 | b.shiftRightPublic(b_shift); |
| 337 | while (true) { |
| 338 | switch (a.compare(b)) { |
| 339 | .eq => break, |
| 340 | .lt => mem.swap(Self, &a, &b), |
| 341 | .gt => {}, |
| 342 | } |
| 343 | _ = a.subWithOverflow(b); |
| 344 | a.shiftRightPublic(a.trailingZeroBitsPublic()); |
| 345 | } |
| 346 | |
| 347 | a.shiftLeft(@min(a_shift, b_shift)); // GCD always fits in either input, overflow is never an issue |
| 348 | return a; |
| 349 | } |
| 350 | |
| 301 | 351 | fn expandTo(x: *Self, new_len: usize) void { |
| 302 | 352 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); |
| 303 | 353 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); |
| ... | ... | @@ -1371,6 +1421,34 @@ test "Uint bit measurement and shifts" { |
| 1371 | 1421 | try testing.expectEqual(1, x.limbs_len); |
| 1372 | 1422 | } |
| 1373 | 1423 | |
| 1424 | test "Uint division and gcd" { |
| 1425 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1426 | |
| 1427 | const U = Uint(256); |
| 1428 | const v: u256 = (1 << 255) + (1 << t_bits) + 13; |
| 1429 | for ([_]usize{ 1, 7, math.maxInt(TLimb), (1 << t_bits) + 5, math.maxInt(usize) }) |divisor| { |
| 1430 | var x = try U.fromPrimitive(u256, v); |
| 1431 | try testing.expectEqual(v % divisor, try x.divRemPublic(divisor)); |
| 1432 | try testing.expectEqual(v / divisor, try x.toPrimitive(u256)); |
| 1433 | try expectWellFormedLimbs(x); |
| 1434 | } |
| 1435 | var x = (try U.fromPrimitive(u16, 1000)).normalize(); |
| 1436 | try testing.expectError(error.DivisionByZero, x.divRemPublic(0)); |
| 1437 | try testing.expectEqual(6, try x.divRemPublic(7)); |
| 1438 | try testing.expectEqual(142, try x.toPrimitive(u16)); |
| 1439 | try testing.expectEqual(1, x.limbs_len); |
| 1440 | try testing.expectEqual(142, try x.divRemPublic(1000)); |
| 1441 | try testing.expectEqual(0, try x.divRemPublic(7)); |
| 1442 | |
| 1443 | for ([_][3]u256{ .{ 0, 5, 5 }, .{ 6, 6, 6 }, .{ 17, 4, 1 }, .{ 15 << 200, 21 << 100, 3 << 100 } }) |c| { |
| 1444 | const a = (try U.fromPrimitive(u256, c[0])).normalize(); |
| 1445 | const b = try U.fromPrimitive(u256, c[1]); |
| 1446 | try testing.expectEqual(c[2], try (try a.gcdPublic(b)).toPrimitive(u256)); |
| 1447 | try testing.expectEqual(c[2], try (try b.gcdPublic(a)).toPrimitive(u256)); |
| 1448 | } |
| 1449 | try testing.expectError(error.DivisionByZero, U.zero.gcdPublic(U.zero)); |
| 1450 | } |
| 1451 | |
| 1374 | 1452 | test "Uint addition and multiply-add" { |
| 1375 | 1453 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1376 | 1454 | |