Skip to content

Conversation

Copy link

Copilot AI commented Dec 15, 2025

The M31::new() function performed single-step reduction (val - P), which only handles values in [0, 2*P). Values >= 2*P were incorrectly reduced:

M31::new(2*P)      // 4294967294 → returned P (2147483647), expected 0
M31::new(u32::MAX) // 4294967295 → returned P+1 (2147483648), expected 1

Changes

  • Applied double reduction to handle full u32 range:

    let reduced = if val >= P { val - P } else { val };
    let reduced = if reduced >= P { reduced - P } else { reduced };
  • Added boundary tests covering P, 2*P-1, 2*P, and u32::MAX

The fix preserves existing behavior for values < 2*P while correctly reducing the upper boundary cases.

Original prompt

This section details on the original issue you should resolve

<issue_title>Bug: M31::new() function performs incorrect modular reduction</issue_title>
<issue_description>We have a function named new in the field, which tries to compute u32 values to a value mod P.

    #[inline]
    pub const fn new(val: u32) -> Self {
        let reduced = if val >= P { val - P } else { val };
        Self(reduced)
    }

Where P = 2^31 -1.

Here's the problem:

When val >= P, the code does val - P, which works only if val < 2*P (i.e., val < 2^32 - 2). For values ≥ 2*P (≥2^32 - 2 ), this will not properly reduce them modulo P:

  • Input val = 2*P = 2^32 - 2 → Should output 0, but the code outputs: 2p - p = p (which is P, not 0)

  • Input val = u32::MAX = 2^32 - 1 → Should output 1, but the code outputs: 2^32 - 1 - (2^31 - 1) = 2^31 (which is P+1, not 1)

</issue_description>

<agent_instructions>Is the reported issue is valid or invalid in current design ? Can check that specific code also whats the impact and why its currently leads to bug ? create end to end poc to check the bug is valid or not.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@this-vishalsingh > We have a function named `new` in the [field](https://github.com/ZippelLabs/ZP1/blob/2141f45836728bbc90a9ee9e191e2f11d69f6a66/crates/primitives/src/field.rs#L35), which tries to compute `u32` values to a value mod `P`. > > ``` > #[inline] > pub const fn new(val: u32) -> Self { > let reduced = if val >= P { val - P } else { val }; > Self(reduced) > } > ``` > > Where `P = 2^31 -1`. > > Here's the problem: > > When `val >= P`, the code does `val - P`, which works only if `val < 2*P` (i.e., `val < 2^32 - 2`). For values `≥ 2*P` (`≥2^32 - 2` ), this will not properly reduce them modulo `P`: > > * Input `val = 2*P = 2^32 - 2` → Should output 0, but the code outputs: `2p - p = p` (which is P, not 0) > * Input `val = u32::MAX = 2^32 - 1` → Should output 1, but the code outputs: `2^32 - 1 - (2^31 - 1)` = 2^31 (which is `P+1`, not 1)

@0x6980 Can you create poc that will make easy?</comment_new>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits December 15, 2025 03:39
Co-authored-by: this-vishalsingh <93567955+this-vishalsingh@users.noreply.github.com>
Co-authored-by: this-vishalsingh <93567955+this-vishalsingh@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix incorrect modular reduction in M31::new() function Fix M31::new() modular reduction for values >= 2*P Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: M31::new() function performs incorrect modular reduction

2 participants