Merged
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Extensive duplicated logic across malachite and num-bigint implementations
- I replaced duplicated num-bigint/malachite operation bodies with shared generic helpers so both variants now execute one common implementation path.
Or push these changes by commenting:
@cursor push 8142c259a6
Preview (8142c259a6)
diff --git a/src/more_ops.rs b/src/more_ops.rs
--- a/src/more_ops.rs
+++ b/src/more_ops.rs
@@ -608,19 +608,42 @@
}
fn op_div_impl(a: &mut Allocator, input: NodePtr, max_cost: Cost, limit: bool) -> Response {
+ op_div_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ int_atom,
+ |v: &Number| v.sign() == Sign::NoSign,
+ Allocator::new_number,
+ )
+}
+
+fn op_div_impl_inner<T>(
+ a: &mut Allocator,
+ input: NodePtr,
+ max_cost: Cost,
+ limit: bool,
+ int_atom_fn: fn(&Allocator, NodePtr, &str) -> crate::error::Result<(T, usize)>,
+ is_zero: fn(&T) -> bool,
+ new_number_fn: fn(&mut Allocator, T) -> crate::error::Result<NodePtr>,
+) -> Response
+where
+ T: Integer,
+{
let [v0, v1] = get_args::<2>(a, input, "/")?;
- let (a0, a0_len) = int_atom(a, v0, "/")?;
- let (a1, a1_len) = int_atom(a, v1, "/")?;
+ let (a0, a0_len) = int_atom_fn(a, v0, "/")?;
+ let (a1, a1_len) = int_atom_fn(a, v1, "/")?;
if limit && a0_len > 2048 {
return Err(EvalErr::InvalidOpArg(input, "div".to_string()));
}
let cost = DIV_BASE_COST + ((a0_len + a1_len) as Cost) * DIV_COST_PER_BYTE;
check_cost(cost, max_cost)?;
- if a1.sign() == Sign::NoSign {
+ if is_zero(&a1) {
return Err(EvalErr::DivisionByZero(input));
}
let q = a0.div_floor(&a1);
- let q = a.new_number(q)?;
+ let q = new_number_fn(a, q)?;
Ok(malloc_cost(a, cost, q))
}
@@ -630,37 +653,55 @@
max_cost: Cost,
limit: bool,
) -> Response {
- let [v0, v1] = get_args::<2>(a, input, "/")?;
- let (a0, a0_len) = malachite_int_atom(a, v0, "/")?;
- let (a1, a1_len) = malachite_int_atom(a, v1, "/")?;
- if limit && a0_len > 2048 {
- return Err(EvalErr::InvalidOpArg(input, "div".to_string()));
- }
- let cost = DIV_BASE_COST + ((a0_len + a1_len) as Cost) * DIV_COST_PER_BYTE;
- check_cost(cost, max_cost)?;
- if a1.sign() == malachite_bigint::Sign::NoSign {
- return Err(EvalErr::DivisionByZero(input));
- }
- let q = a0.div_floor(&a1);
- let q = a.new_malachite_number(q)?;
- Ok(malloc_cost(a, cost, q))
+ op_div_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ malachite_int_atom,
+ |v: &malachite_bigint::BigInt| v.sign() == malachite_bigint::Sign::NoSign,
+ Allocator::new_malachite_number,
+ )
}
fn op_divmod_impl(a: &mut Allocator, input: NodePtr, max_cost: Cost, limit: bool) -> Response {
+ op_divmod_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ int_atom,
+ |v: &Number| v.sign() == Sign::NoSign,
+ Allocator::new_number,
+ )
+}
+
+fn op_divmod_impl_inner<T>(
+ a: &mut Allocator,
+ input: NodePtr,
+ max_cost: Cost,
+ limit: bool,
+ int_atom_fn: fn(&Allocator, NodePtr, &str) -> crate::error::Result<(T, usize)>,
+ is_zero: fn(&T) -> bool,
+ new_number_fn: fn(&mut Allocator, T) -> crate::error::Result<NodePtr>,
+) -> Response
+where
+ T: Integer,
+{
let [v0, v1] = get_args::<2>(a, input, "divmod")?;
- let (a0, a0_len) = int_atom(a, v0, "divmod")?;
- let (a1, a1_len) = int_atom(a, v1, "divmod")?;
+ let (a0, a0_len) = int_atom_fn(a, v0, "divmod")?;
+ let (a1, a1_len) = int_atom_fn(a, v1, "divmod")?;
if limit && a0_len > 2048 {
return Err(EvalErr::InvalidOpArg(input, "divmod".to_string()));
}
let cost = DIVMOD_BASE_COST + ((a0_len + a1_len) as Cost) * DIVMOD_COST_PER_BYTE;
check_cost(cost, max_cost)?;
- if a1.sign() == Sign::NoSign {
+ if is_zero(&a1) {
return Err(EvalErr::DivisionByZero(input));
}
let (q, r) = a0.div_mod_floor(&a1);
- let q1 = a.new_number(q)?;
- let r1 = a.new_number(r)?;
+ let q1 = new_number_fn(a, q)?;
+ let r1 = new_number_fn(a, r)?;
let c = (a.atom_len(q1) + a.atom_len(r1)) as Cost * MALLOC_COST_PER_BYTE;
let r: NodePtr = a.new_pair(q1, r1)?;
@@ -673,24 +714,15 @@
max_cost: Cost,
limit: bool,
) -> Response {
- let [v0, v1] = get_args::<2>(a, input, "divmod")?;
- let (a0, a0_len) = malachite_int_atom(a, v0, "divmod")?;
- let (a1, a1_len) = malachite_int_atom(a, v1, "divmod")?;
- if limit && a0_len > 2048 {
- return Err(EvalErr::InvalidOpArg(input, "divmod".to_string()));
- }
- let cost = DIVMOD_BASE_COST + ((a0_len + a1_len) as Cost) * DIVMOD_COST_PER_BYTE;
- check_cost(cost, max_cost)?;
- if a1.sign() == malachite_bigint::Sign::NoSign {
- return Err(EvalErr::DivisionByZero(input));
- }
- let (q, r) = a0.div_mod_floor(&a1);
- let q1 = a.new_malachite_number(q)?;
- let r1 = a.new_malachite_number(r)?;
-
- let c = (a.atom_len(q1) + a.atom_len(r1)) as Cost * MALLOC_COST_PER_BYTE;
- let r: NodePtr = a.new_pair(q1, r1)?;
- Ok(Reduction(cost + c, r))
+ op_divmod_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ malachite_int_atom,
+ |v: &malachite_bigint::BigInt| v.sign() == malachite_bigint::Sign::NoSign,
+ Allocator::new_malachite_number,
+ )
}
pub fn op_divmod(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
@@ -710,18 +742,41 @@
}
fn op_mod_impl(a: &mut Allocator, input: NodePtr, max_cost: Cost, limit: bool) -> Response {
+ op_mod_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ int_atom,
+ |v: &Number| v.sign() == Sign::NoSign,
+ Allocator::new_number,
+ )
+}
+
+fn op_mod_impl_inner<T>(
+ a: &mut Allocator,
+ input: NodePtr,
+ max_cost: Cost,
+ limit: bool,
+ int_atom_fn: fn(&Allocator, NodePtr, &str) -> crate::error::Result<(T, usize)>,
+ is_zero: fn(&T) -> bool,
+ new_number_fn: fn(&mut Allocator, T) -> crate::error::Result<NodePtr>,
+) -> Response
+where
+ T: Integer,
+{
let [v0, v1] = get_args::<2>(a, input, "mod")?;
- let (a0, a0_len) = int_atom(a, v0, "mod")?;
- let (a1, a1_len) = int_atom(a, v1, "mod")?;
+ let (a0, a0_len) = int_atom_fn(a, v0, "mod")?;
+ let (a1, a1_len) = int_atom_fn(a, v1, "mod")?;
if limit && a0_len > 2048 {
return Err(EvalErr::InvalidOpArg(input, "mod".to_string()));
}
let cost = DIV_BASE_COST + ((a0_len + a1_len) as Cost) * DIV_COST_PER_BYTE;
check_cost(cost, max_cost)?;
- if a1.sign() == Sign::NoSign {
+ if is_zero(&a1) {
return Err(EvalErr::DivisionByZero(input));
}
- let q = a.new_number(a0.mod_floor(&a1))?;
+ let q = new_number_fn(a, a0.mod_floor(&a1))?;
let c = a.atom_len(q) as Cost * MALLOC_COST_PER_BYTE;
Ok(Reduction(cost + c, q))
}
@@ -732,20 +787,15 @@
max_cost: Cost,
limit: bool,
) -> Response {
- let [v0, v1] = get_args::<2>(a, input, "mod")?;
- let (a0, a0_len) = malachite_int_atom(a, v0, "mod")?;
- let (a1, a1_len) = malachite_int_atom(a, v1, "mod")?;
- if limit && a0_len > 2048 {
- return Err(EvalErr::InvalidOpArg(input, "mod".to_string()));
- }
- let cost = DIV_BASE_COST + ((a0_len + a1_len) as Cost) * DIV_COST_PER_BYTE;
- check_cost(cost, max_cost)?;
- if a1.sign() == malachite_bigint::Sign::NoSign {
- return Err(EvalErr::DivisionByZero(input));
- }
- let q = a.new_malachite_number(a0.mod_floor(&a1))?;
- let c = a.atom_len(q) as Cost * MALLOC_COST_PER_BYTE;
- Ok(Reduction(cost + c, q))
+ op_mod_impl_inner(
+ a,
+ input,
+ max_cost,
+ limit,
+ malachite_int_atom,
+ |v: &malachite_bigint::BigInt| v.sign() == malachite_bigint::Sign::NoSign,
+ Allocator::new_malachite_number,
+ )
}
pub fn op_mod(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
@@ -1151,61 +1201,67 @@
}
pub fn op_modpow(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
+ op_modpow_impl(
+ a,
+ input,
+ max_cost,
+ int_atom,
+ |v: &Number| v.sign() == Sign::Minus,
+ |v: &Number| v.sign() == Sign::NoSign,
+ |base, exponent, modulus| base.modpow(exponent, modulus),
+ Allocator::new_number,
+ )
+}
+
+fn op_modpow_impl<T>(
+ a: &mut Allocator,
+ input: NodePtr,
+ max_cost: Cost,
+ int_atom_fn: fn(&Allocator, NodePtr, &str) -> crate::error::Result<(T, usize)>,
+ is_negative: fn(&T) -> bool,
+ is_zero: fn(&T) -> bool,
+ modpow_fn: fn(&T, &T, &T) -> T,
+ new_number_fn: fn(&mut Allocator, T) -> crate::error::Result<NodePtr>,
+) -> Response {
let [base, exponent, modulus] = get_args::<3>(a, input, "modpow")?;
let mut cost = MODPOW_BASE_COST;
- let (base, bsize) = int_atom(a, base, "modpow")?;
+ let (base, bsize) = int_atom_fn(a, base, "modpow")?;
cost += bsize as Cost * MODPOW_COST_PER_BYTE_BASE_VALUE;
- let (exponent, esize) = int_atom(a, exponent, "modpow")?;
+ let (exponent, esize) = int_atom_fn(a, exponent, "modpow")?;
cost += (esize * esize) as Cost * MODPOW_COST_PER_BYTE_EXPONENT;
check_cost(cost, max_cost)?;
- let (modulus, msize) = int_atom(a, modulus, "modpow")?;
+ let (modulus, msize) = int_atom_fn(a, modulus, "modpow")?;
cost += (msize * msize) as Cost * MODPOW_COST_PER_BYTE_MOD;
check_cost(cost, max_cost)?;
- if exponent.sign() == Sign::Minus {
+ if is_negative(&exponent) {
return Err(EvalErr::InvalidOpArg(
input,
"ModPow with Negative Exponent".to_string(),
));
}
- if modulus.sign() == Sign::NoSign {
+ if is_zero(&modulus) {
return Err(EvalErr::DivisionByZero(input));
}
- let ret = base.modpow(&exponent, &modulus);
- let ret = a.new_number(ret)?;
+ let ret = modpow_fn(&base, &exponent, &modulus);
+ let ret = new_number_fn(a, ret)?;
Ok(malloc_cost(a, cost, ret))
}
pub fn op_modpow_malachite(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response {
- let [base, exponent, modulus] = get_args::<3>(a, input, "modpow")?;
-
- let mut cost = MODPOW_BASE_COST;
- let (base, bsize) = malachite_int_atom(a, base, "modpow")?;
- cost += bsize as Cost * MODPOW_COST_PER_BYTE_BASE_VALUE;
- let (exponent, esize) = malachite_int_atom(a, exponent, "modpow")?;
- cost += (esize * esize) as Cost * MODPOW_COST_PER_BYTE_EXPONENT;
- check_cost(cost, max_cost)?;
- let (modulus, msize) = malachite_int_atom(a, modulus, "modpow")?;
- cost += (msize * msize) as Cost * MODPOW_COST_PER_BYTE_MOD;
- check_cost(cost, max_cost)?;
-
- if exponent.sign() == malachite_bigint::Sign::Minus {
- return Err(EvalErr::InvalidOpArg(
- input,
- "ModPow with Negative Exponent".to_string(),
- ));
- }
-
- if modulus.sign() == malachite_bigint::Sign::NoSign {
- return Err(EvalErr::DivisionByZero(input));
- }
-
- let ret = base.modpow(&exponent, &modulus);
- let ret = a.new_malachite_number(ret)?;
- Ok(malloc_cost(a, cost, ret))
+ op_modpow_impl(
+ a,
+ input,
+ max_cost,
+ malachite_int_atom,
+ |v: &malachite_bigint::BigInt| v.sign() == malachite_bigint::Sign::Minus,
+ |v: &malachite_bigint::BigInt| v.sign() == malachite_bigint::Sign::NoSign,
+ |base, exponent, modulus| base.modpow(exponent, modulus),
+ Allocator::new_malachite_number,
+ )
}
#[cfg(test)]|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. Ignoring alerts on:
|
Pull Request Test Coverage Report for Build 22865941586Details
💛 - Coveralls |
…ere it has more efficient implementations
5a135a5 to
d9ea087
Compare
Rigidity
approved these changes
Mar 9, 2026
Contributor
Author
|
@SocketSecurity ignore-all |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

based on @Rigidity's and @wjblanke's work.
Allocatorto create and retrieve malachite large integersClvmFlagsfeature flag to select the malachite implementations (opt-in)Note
Medium Risk
Touches core arithmetic op implementations and allocator number serialization, which can affect consensus-critical execution if enabled; risk is mitigated by being gated behind
ClvmFlags::MALACHITEand reinforced with expanded fuzzing/tests.Overview
Adds
malachite-bigintas a new bigint backend and wires it into the VM as an opt-in execution mode.This extends
Allocator/op_utilswith Malachite number encoding/decoding helpers, and adds parallel Malachite implementations fordiv,divmod,mod, andmodpow(including the*_limitvariants) while preserving existing cost accounting and error behavior.ChiaDialectgains a newClvmFlags::MALACHITEtoggle to route opcodes 19/20/60/61 to the Malachite versions, and test coverage is expanded (unit tests, op-tests harness, and fuzz targets) to validate equivalence between the original and Malachite implementations.Written by Cursor Bugbot for commit 55284b8. This will update automatically on new commits. Configure here.