openvm_ecc_circuit/weierstrass_chip/
double.rs

1use std::{cell::RefCell, rc::Rc};
2
3use num_bigint::BigUint;
4use num_traits::One;
5use openvm_circuit_primitives::var_range::VariableRangeCheckerBus;
6use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr, FieldVariable};
7
8pub fn ec_double_ne_expr(
9    config: ExprBuilderConfig, // The coordinate field.
10    range_bus: VariableRangeCheckerBus,
11    a_biguint: BigUint,
12) -> FieldExpr {
13    config.check_valid();
14    let builder = ExprBuilder::new(config, range_bus.range_max_bits);
15    let builder = Rc::new(RefCell::new(builder));
16
17    let mut x1 = ExprBuilder::new_input(builder.clone());
18    let mut y1 = ExprBuilder::new_input(builder.clone());
19    let a = ExprBuilder::new_const(builder.clone(), a_biguint.clone());
20    let is_double_flag = builder.borrow_mut().new_flag();
21    // We need to prevent divide by zero when not double flag
22    // (equivalently, when it is the setup opcode)
23    let lambda_denom = FieldVariable::select(
24        is_double_flag,
25        &y1.int_mul(2),
26        &ExprBuilder::new_const(builder.clone(), BigUint::one()),
27    );
28    let mut lambda = (x1.square().int_mul(3) + a) / lambda_denom;
29    let mut x3 = lambda.square() - x1.int_mul(2);
30    x3.save_output();
31    let mut y3 = lambda * (x1 - x3.clone()) - y1;
32    y3.save_output();
33
34    let builder = builder.borrow().clone();
35    FieldExpr::new_with_setup_values(builder, range_bus, true, vec![a_biguint])
36}