| ... | @@ -751,6 +751,26 @@ pub fn Modulus(comptime max_bits: comptime_int) type { | ... | @@ -751,6 +751,26 @@ pub fn Modulus(comptime max_bits: comptime_int) type { |
| 751 | return out; | 751 | return out; |
| 752 | } | 752 | } |
| 753 | | 753 | |
| | 754 | /// Creates an odd modulus from the product of two integers. |
| | 755 | pub fn fromProduct(a: FeUint, b: FeUint) (InvalidModulusError || OverflowError)!Self { |
| | 756 | return fromProductWithOptions(a, b, .{}); |
| | 757 | } |
| | 758 | |
| | 759 | /// Creates a modulus from the product of two integers, with options (set `allow_even` to `true` to allow the product to be even) |
| | 760 | pub fn fromProductWithOptions(a: FeUint, b: FeUint, options: Options) (InvalidModulusError || OverflowError)!Self { |
| | 761 | const product = fullProduct(a.limbsConst(), b.limbsConst()); |
| | 762 | |
| | 763 | if (orLimbs(product.limbs_buffer[FeUint.max_limbs_count..]) != 0) { |
| | 764 | return error.Overflow; |
| | 765 | } |
| | 766 | |
| | 767 | const v = FeUint{ |
| | 768 | .limbs_buffer = product.limbs_buffer[0..FeUint.max_limbs_count].*, |
| | 769 | .limbs_len = FeUint.max_limbs_count, |
| | 770 | }; |
| | 771 | return try Self.fromUintWithOptions(v, options); |
| | 772 | } |
| | 773 | |
| 754 | /// Serializes the modulus to a byte string. | 774 | /// Serializes the modulus to a byte string. |
| 755 | pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void { | 775 | pub fn toBytes(self: Self, bytes: []u8, comptime endian: Endian) OverflowError!void { |
| 756 | return self.v.toBytes(bytes, endian); | 776 | return self.v.toBytes(bytes, endian); |
| ... | @@ -1664,6 +1684,32 @@ test "field element constants and subOne" { | ... | @@ -1664,6 +1684,32 @@ test "field element constants and subOne" { |
| 1664 | } | 1684 | } |
| 1665 | } | 1685 | } |
| 1666 | | 1686 | |
| | 1687 | test "modulus from a product" { |
| | 1688 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| | 1689 | |
| | 1690 | const M = Modulus(256); |
| | 1691 | const U = Uint(256); |
| | 1692 | const one = try U.fromPrimitive(u8, 1); |
| | 1693 | const a = (try U.fromPrimitive(u32, 65537)).normalize(); |
| | 1694 | const b = try U.fromPrimitive(u256, (1 << 200) + 12345); |
| | 1695 | const m = try M.fromProduct(a, b); |
| | 1696 | try testing.expectEqual(65537 * ((1 << 200) + 12345), try m.toUint().toPrimitive(u256)); |
| | 1697 | try testing.expect((try M.fromProduct(b, a)).v.eql(m.v)); |
| | 1698 | try testing.expectError(error.EvenModulus, M.fromProduct(U.zero, a)); |
| | 1699 | try testing.expectError(error.ModulusTooSmall, M.fromProduct(one, one)); |
| | 1700 | const two = try U.fromPrimitive(u8, 2); |
| | 1701 | try testing.expectError(error.EvenModulus, M.fromProduct(a, two)); |
| | 1702 | try testing.expectEqual(2 * 65537, try (try M.fromProductWithOptions(a, two, .{ .allow_even = true })).toUint().toPrimitive(u32)); |
| | 1703 | try testing.expectError(error.ModulusTooSmall, M.fromProductWithOptions(U.zero, a, .{ .allow_even = true })); |
| | 1704 | |
| | 1705 | const full = try U.fromBytes(&maxUintBytes(U), .big); |
| | 1706 | try testing.expect((try M.fromProduct(full, one)).toUint().eql(full)); |
| | 1707 | try testing.expectError(error.Overflow, M.fromProduct(full, a)); |
| | 1708 | try testing.expectError(error.Overflow, M.fromProduct(a, full)); |
| | 1709 | const power = try U.fromPrimitive(u256, 1 << 160); |
| | 1710 | try testing.expectError(error.Overflow, M.fromProductWithOptions(power, power, .{ .allow_even = true })); |
| | 1711 | } |
| | 1712 | |
| 1667 | test "even modulus arithmetic" { | 1713 | test "even modulus arithmetic" { |
| 1668 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 1714 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1669 | | 1715 | |