| ... | @@ -53,8 +53,10 @@ pub const FieldElementError = error{NonCanonical}; | ... | @@ -53,8 +53,10 @@ pub const FieldElementError = error{NonCanonical}; |
| 53 | /// Invalid representation (Montgomery vs non-Montgomery domain.) | 53 | /// Invalid representation (Montgomery vs non-Montgomery domain.) |
| 54 | pub const RepresentationError = error{UnexpectedRepresentation}; | 54 | pub const RepresentationError = error{UnexpectedRepresentation}; |
| 55 | | 55 | |
| | 56 | pub const DivisionByZeroError = error{DivisionByZero}; |
| | 57 | |
| 56 | /// The set of all possible errors `std.crypto.ff` functions can return. | 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 | /// An unsigned big integer with a fixed maximum size (`max_bits`), suitable for cryptographic operations. | 61 | /// An unsigned big integer with a fixed maximum size (`max_bits`), suitable for cryptographic operations. |
| 60 | /// Storage rounds up to whole limbs and can hold up to `capacity_bits` bits. | 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,6 +300,54 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 298 | @memset(x_limbs[active..], 0); | 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 | fn expandTo(x: *Self, new_len: usize) void { | 351 | fn expandTo(x: *Self, new_len: usize) void { |
| 302 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); | 352 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); |
| 303 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); | 353 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); |
| ... | @@ -1371,6 +1421,34 @@ test "Uint bit measurement and shifts" { | ... | @@ -1371,6 +1421,34 @@ test "Uint bit measurement and shifts" { |
| 1371 | try testing.expectEqual(1, x.limbs_len); | 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 | test "Uint addition and multiply-add" { | 1452 | test "Uint addition and multiply-add" { |
| 1375 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 1453 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1376 | | 1454 | |