From d1d177229dc072b2caa90fb2f9f536a4760b799b Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Fri, 16 Feb 2024 18:13:04 -0800 Subject: [PATCH 1/5] field extension support initial commit --- core/src/air/extension.rs | 46 +++++++++++++++++++ core/src/air/mod.rs | 1 + .../precompiles/sha256/compress/air.rs | 4 +- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 core/src/air/extension.rs diff --git a/core/src/air/extension.rs b/core/src/air/extension.rs new file mode 100644 index 0000000000..62c64f8313 --- /dev/null +++ b/core/src/air/extension.rs @@ -0,0 +1,46 @@ +use std::{ + mem::size_of, + ops::{Add, Mul}, +}; + +use core::borrow::{Borrow, BorrowMut}; +use itertools::Itertools; +use p3_field::AbstractField; +use sp1_derive::AlignedBorrow; + +use super::SP1AirBuilder; + +#[derive(AlignedBorrow, Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +#[repr(C)] +pub struct Extension(pub [T; 4]); // Extension 4 is hard coded for now. TODO: Change to a const generic + +impl Extension { + // Converts a field element to extension element + pub fn from>(x: V) -> Extension + where + AB::Expr: From, + { + let zero = AB::Expr::zero(); + let x_expr = x.into(); + Extension([zero.clone(), zero.clone(), zero, x_expr]) + } + + // Negates an extension field Element + pub fn neg>(self) -> Extension { + Extension(self.0.map(|x| AB::Expr::zero() - x)) + } + + // Adds an extension field element + pub fn add>(self, rhs: &Self) -> Extension + where + V: Add, + { + let mut elements = Vec::new(); + + for (e1, e2) in self.0.iter().zip_eq(rhs.0.iter()) { + elements.push(*e1 + *e2); + } + + Extension(elements.into()) + } +} diff --git a/core/src/air/mod.rs b/core/src/air/mod.rs index d23ef2031f..1b9b9c4c16 100644 --- a/core/src/air/mod.rs +++ b/core/src/air/mod.rs @@ -1,4 +1,5 @@ mod builder; +mod extension; mod interaction; mod machine; mod polynomial; diff --git a/core/src/syscall/precompiles/sha256/compress/air.rs b/core/src/syscall/precompiles/sha256/compress/air.rs index 9170a7040b..0445cfdff8 100644 --- a/core/src/syscall/precompiles/sha256/compress/air.rs +++ b/core/src/syscall/precompiles/sha256/compress/air.rs @@ -26,7 +26,7 @@ where let local: &ShaCompressCols = main.row_slice(0).borrow(); let next: &ShaCompressCols = main.row_slice(1).borrow(); - self.contrain_control_flow_flags(builder, local, next); + self.constrain_control_flow_flags(builder, local, next); self.constrain_memory(builder, local); @@ -37,7 +37,7 @@ where } impl ShaCompressChip { - fn contrain_control_flow_flags( + fn constrain_control_flow_flags( &self, builder: &mut AB, local: &ShaCompressCols, From ea29c1051a8a2589e7e6e96e9afce69303f8c952 Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Fri, 16 Feb 2024 18:25:42 -0800 Subject: [PATCH 2/5] added mul --- core/src/air/extension.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/air/extension.rs b/core/src/air/extension.rs index 62c64f8313..6830346a04 100644 --- a/core/src/air/extension.rs +++ b/core/src/air/extension.rs @@ -33,14 +33,28 @@ impl Extension { // Adds an extension field element pub fn add>(self, rhs: &Self) -> Extension where - V: Add, + V: Add + Copy, { let mut elements = Vec::new(); - for (e1, e2) in self.0.iter().zip_eq(rhs.0.iter()) { - elements.push(*e1 + *e2); + for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.into_iter()) { + elements.push(e1 + e2); } - Extension(elements.into()) + Extension(elements.try_into().unwrap()) + } + + // Multiplies an extension field element + pub fn mul>(self, rhs: &Self) -> Extension + where + V: Mul + Copy, + { + let mut elements = Vec::new(); + + for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.into_iter()) { + elements.push(e1 * e2); + } + + Extension(elements.try_into().unwrap()) } } From 423ed0cc820e2499c9f7bcdb2516750b40914363 Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Sat, 17 Feb 2024 08:56:20 -0800 Subject: [PATCH 3/5] in progress div --- core/src/air/extension.rs | 32 +++++++++++-- core/src/air/mod.rs | 1 + core/src/operations/div_extension.rs | 70 ++++++++++++++++++++++++++++ core/src/operations/mod.rs | 1 + 4 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 core/src/operations/div_extension.rs diff --git a/core/src/air/extension.rs b/core/src/air/extension.rs index 6830346a04..95a2461a69 100644 --- a/core/src/air/extension.rs +++ b/core/src/air/extension.rs @@ -5,24 +5,34 @@ use std::{ use core::borrow::{Borrow, BorrowMut}; use itertools::Itertools; -use p3_field::AbstractField; +use p3_field::{ + extension::{BinomialExtensionField, BinomiallyExtendable}, + field_to_array, AbstractExtensionField, AbstractField, Field, +}; use sp1_derive::AlignedBorrow; use super::SP1AirBuilder; #[derive(AlignedBorrow, Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] #[repr(C)] -pub struct Extension(pub [T; 4]); // Extension 4 is hard coded for now. TODO: Change to a const generic +pub struct Extension(pub [T; 4]); // Degree 4 is hard coded for now. TODO: Change to a const generic impl Extension { + // Returns the one element of the extension field + pub fn one>() -> Extension + where + AB::Expr: AbstractField, + { + let one = AB::Expr::one(); + Extension(field_to_array(one)) + } + // Converts a field element to extension element pub fn from>(x: V) -> Extension where AB::Expr: From, { - let zero = AB::Expr::zero(); - let x_expr = x.into(); - Extension([zero.clone(), zero.clone(), zero, x_expr]) + Extension(field_to_array(x.into())) } // Negates an extension field Element @@ -58,3 +68,15 @@ impl Extension { Extension(elements.try_into().unwrap()) } } + +impl From> for Extension +where + F: Field, + F::F: BinomiallyExtendable<4>, +{ + fn from(value: BinomialExtensionField) -> Self { + let base_slice = value.as_base_slice(); + + Self(base_slice.try_into().unwrap()) + } +} diff --git a/core/src/air/mod.rs b/core/src/air/mod.rs index 1b9b9c4c16..b91cb28769 100644 --- a/core/src/air/mod.rs +++ b/core/src/air/mod.rs @@ -7,6 +7,7 @@ mod sub_builder; mod word; pub use builder::*; +pub use extension::*; pub use interaction::*; pub use machine::*; pub use polynomial::*; diff --git a/core/src/operations/div_extension.rs b/core/src/operations/div_extension.rs new file mode 100644 index 0000000000..25d69ce437 --- /dev/null +++ b/core/src/operations/div_extension.rs @@ -0,0 +1,70 @@ +//! An operation to performce div on the inputs. +//! +use core::borrow::Borrow; +use core::borrow::BorrowMut; +use p3_air::AirBuilder; +use p3_field::extension::BinomialExtensionField; +use p3_field::extension::BinomiallyExtendable; +use p3_field::AbstractField; +use p3_field::Field; +use sp1_derive::AlignedBorrow; +use std::mem::size_of; + +use crate::air::Extension; +use crate::air::SP1AirBuilder; + +/// A set of columns needed to compute whether the given word is 0. +#[derive(AlignedBorrow, Default, Debug, Clone, Copy)] +#[repr(C)] +pub struct DivExtOperation { + /// The inverse of the b input + pub b_inverse: Extension, + + /// Result is the quotient + pub result: Extension, +} + +impl> DivExtOperation { + pub fn populate( + &mut self, + a: BinomialExtensionField, + b: BinomialExtensionField, + ) -> BinomialExtensionField { + self.b_inverse = b.inverse().into(); + let result = a / b; + self.result = result.into(); + result + } + + // pub fn eval( + // builder: &mut AB, + // a: AB::Expr, + // cols: DivExtOperation, + // is_real: AB::Expr, + // ) { + // builder.assert_bool(is_real.clone()); + // let one: AB::Expr = AB::F::one().into(); + + // // 1. Input == 0 => is_zero = 1 regardless of the inverse. + // // 2. Input != 0 + // // 2.1. inverse is correctly set => is_zero = 0. + // // 2.2. inverse is incorrect + // // 2.2.1 inverse is nonzero => is_zero isn't bool, it fails. + // // 2.2.2 inverse is 0 => is_zero is 1. But then we would assert that a = 0. And that + // // assert fails. + + // // If the input is 0, then any product involving it is 0. If it is nonzero and its inverse + // // is correctly set, then the product is 1. + // let is_zero = one.clone() - cols.inverse * a.clone(); + // builder + // .when(is_real.clone()) + // .assert_eq(is_zero, cols.result); + // builder.when(is_real.clone()).assert_bool(cols.result); + + // // If the result is 1, then the input is 0. + // builder + // .when(is_real.clone()) + // .when(cols.result) + // .assert_zero(a.clone()); + // } +} diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 242c9100b1..9876b0df66 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -8,6 +8,7 @@ mod add; mod add4; mod add5; mod and; +mod div_extension; pub mod field; mod fixed_rotate_right; mod fixed_shift_right; From e0ddee64b1fdd1a2d8fb7e40f1accda69a7ae6e3 Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Sun, 18 Feb 2024 06:16:44 -0800 Subject: [PATCH 4/5] added is_equal --- core/src/air/extension.rs | 8 +- core/src/operations/div_extension.rs | 62 ++++++--------- core/src/operations/is_equal_extension.rs | 54 +++++++++++++ core/src/operations/is_zero_extension.rs | 93 +++++++++++++++++++++++ core/src/operations/mod.rs | 4 + 5 files changed, 181 insertions(+), 40 deletions(-) create mode 100644 core/src/operations/is_equal_extension.rs create mode 100644 core/src/operations/is_zero_extension.rs diff --git a/core/src/air/extension.rs b/core/src/air/extension.rs index 95a2461a69..1a546c8b16 100644 --- a/core/src/air/extension.rs +++ b/core/src/air/extension.rs @@ -13,9 +13,11 @@ use sp1_derive::AlignedBorrow; use super::SP1AirBuilder; +pub const DEGREE: usize = 4; + #[derive(AlignedBorrow, Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] #[repr(C)] -pub struct Extension(pub [T; 4]); // Degree 4 is hard coded for now. TODO: Change to a const generic +pub struct Extension(pub [T; DEGREE]); // Degree 4 is hard coded for now. TODO: Change to a const generic impl Extension { // Returns the one element of the extension field @@ -67,6 +69,10 @@ impl Extension { Extension(elements.try_into().unwrap()) } + + pub fn as_base_slice(&self) -> &[V] { + &self.0 + } } impl From> for Extension diff --git a/core/src/operations/div_extension.rs b/core/src/operations/div_extension.rs index 25d69ce437..a05fd65650 100644 --- a/core/src/operations/div_extension.rs +++ b/core/src/operations/div_extension.rs @@ -12,59 +12,43 @@ use std::mem::size_of; use crate::air::Extension; use crate::air::SP1AirBuilder; +use crate::air::DEGREE; /// A set of columns needed to compute whether the given word is 0. #[derive(AlignedBorrow, Default, Debug, Clone, Copy)] #[repr(C)] pub struct DivExtOperation { - /// The inverse of the b input - pub b_inverse: Extension, - /// Result is the quotient pub result: Extension, } -impl> DivExtOperation { +impl> DivExtOperation { pub fn populate( &mut self, - a: BinomialExtensionField, - b: BinomialExtensionField, - ) -> BinomialExtensionField { - self.b_inverse = b.inverse().into(); + a: BinomialExtensionField, + b: BinomialExtensionField, + ) -> BinomialExtensionField { let result = a / b; self.result = result.into(); result } - // pub fn eval( - // builder: &mut AB, - // a: AB::Expr, - // cols: DivExtOperation, - // is_real: AB::Expr, - // ) { - // builder.assert_bool(is_real.clone()); - // let one: AB::Expr = AB::F::one().into(); - - // // 1. Input == 0 => is_zero = 1 regardless of the inverse. - // // 2. Input != 0 - // // 2.1. inverse is correctly set => is_zero = 0. - // // 2.2. inverse is incorrect - // // 2.2.1 inverse is nonzero => is_zero isn't bool, it fails. - // // 2.2.2 inverse is 0 => is_zero is 1. But then we would assert that a = 0. And that - // // assert fails. - - // // If the input is 0, then any product involving it is 0. If it is nonzero and its inverse - // // is correctly set, then the product is 1. - // let is_zero = one.clone() - cols.inverse * a.clone(); - // builder - // .when(is_real.clone()) - // .assert_eq(is_zero, cols.result); - // builder.when(is_real.clone()).assert_bool(cols.result); - - // // If the result is 1, then the input is 0. - // builder - // .when(is_real.clone()) - // .when(cols.result) - // .assert_zero(a.clone()); - // } + pub fn eval( + builder: &mut AB, + a: Extension, + b: Extension, + cols: DivExtOperation, + is_real: AB::Expr, + ) { + builder.assert_bool(is_real.clone()); + + let product = b.mul(&cols.result); + builder.when(is_real.clone()).assert_eq(product, a); + + // If the result is 1, then the input is 0. + builder + .when(is_real.clone()) + .when(cols.result) + .assert_zero(a.clone()); + } } diff --git a/core/src/operations/is_equal_extension.rs b/core/src/operations/is_equal_extension.rs new file mode 100644 index 0000000000..9f95f59de3 --- /dev/null +++ b/core/src/operations/is_equal_extension.rs @@ -0,0 +1,54 @@ +use core::borrow::Borrow; +use core::borrow::BorrowMut; +use p3_field::extension::BinomialExtensionField; +use p3_field::extension::BinomiallyExtendable; +use sp1_derive::AlignedBorrow; +use std::mem::size_of; + +use crate::air::Extension; +use crate::air::SP1AirBuilder; +use crate::air::DEGREE; + +use super::IsZeroExtOperation; + +/// A set of columns needed to compute the equality of two field extension elements. +#[derive(AlignedBorrow, Default, Debug, Clone, Copy)] +#[repr(C)] +pub struct IsEqualExtOperation { + /// An operation to check whether the differences in field extension elements is zero. + pub is_diff_zero: IsZeroExtOperation, +} + +impl> IsEqualExtOperation { + pub fn populate( + &mut self, + a: BinomialExtensionField, + b: BinomialExtensionField, + ) -> u32 { + let diff = a - b; + self.is_diff_zero.populate(diff); + (a == b) as u32 + } + + pub fn eval( + builder: &mut AB, + a: Extension, + b: Extension, + cols: IsEqualExtOperation, + is_real: AB::Expr, + ) { + builder.assert_bool(is_real.clone()); + + // Calculate differences. + let diff = a.sub(b); + + // Check if the difference is 0. + IsZeroExtOperation::::eval(builder, diff, cols.is_diff_zero, is_real.clone()); + + // Degree 3 constraint to avoid "OodEvaluationMismatch". + builder.assert_zero( + is_real.clone() * is_real.clone() * is_real.clone() + - is_real.clone() * is_real.clone() * is_real.clone(), + ); + } +} diff --git a/core/src/operations/is_zero_extension.rs b/core/src/operations/is_zero_extension.rs new file mode 100644 index 0000000000..0664f24fcc --- /dev/null +++ b/core/src/operations/is_zero_extension.rs @@ -0,0 +1,93 @@ +//! An operation to check if the input word is 0. +//! +//! This is bijective (i.e., returns 1 if and only if the input is 0). It is also worth noting that +//! this operation doesn't do a range check. +use core::borrow::Borrow; +use core::borrow::BorrowMut; +use p3_air::AirBuilder; +use p3_field::extension::BinomialExtensionField; +use p3_field::extension::BinomiallyExtendable; +use p3_field::AbstractExtensionField; +use sp1_derive::AlignedBorrow; +use std::mem::size_of; + +use crate::air::Extension; +use crate::air::SP1AirBuilder; +use crate::air::DEGREE; + +use super::IsZeroOperation; + +/// A set of columns needed to compute whether the given field ext element is 0. +#[derive(AlignedBorrow, Default, Debug, Clone, Copy)] +#[repr(C)] +pub struct IsZeroExtOperation { + /// `IsZeroOperation` to check if each base field element in the input field ext element is zero. + pub is_zero_base_element: [IsZeroOperation; DEGREE], + + /// A boolean flag indicating whether the first and second base field elements are 0. + /// This equals `is_zero_byte[0] * is_zero_byte[1]`. + pub is_lower_half_zero: T, + + /// A boolean flag indicating whether the third and fourth base field elements are 0. + pub is_upper_half_zero: T, + + /// A boolean flag indicating whether the field ext element is zero. This equals `is_zero_byte[0] * ... * + /// is_zero_byte[DEGREE - 1]`. + pub result: T, +} + +impl> IsZeroExtOperation { + pub fn populate(&mut self, a: BinomialExtensionField) -> u32 { + let mut is_zero = true; + let base_slice = a.as_base_slice(); + for i in 0..DEGREE { + is_zero &= self.is_zero_base_element[i].populate_from_field_element(base_slice[i]) == 1; + } + self.is_lower_half_zero = + self.is_zero_base_element[0].result * self.is_zero_base_element[1].result; + self.is_upper_half_zero = + self.is_zero_base_element[2].result * self.is_zero_base_element[3].result; + self.result = F::from_bool(is_zero); + is_zero as u32 + } + + pub fn eval( + builder: &mut AB, + a: Extension, + cols: IsZeroExtOperation, + is_real: AB::Expr, + ) { + let base_slice = a.as_base_slice(); + + // Calculate whether each byte is 0. + for i in 0..DEGREE { + IsZeroOperation::::eval( + builder, + base_slice[i].clone(), + cols.is_zero_base_element[i], + is_real.clone(), + ); + } + + // From here, we only assert when is_real is true. + builder.assert_bool(is_real.clone()); + let mut builder_is_real = builder.when(is_real.clone()); + + // Calculate is_upper_half_zero and is_lower_half_zero and finally the result. + builder_is_real.assert_bool(cols.is_lower_half_zero); + builder_is_real.assert_bool(cols.is_upper_half_zero); + builder_is_real.assert_bool(cols.result); + builder_is_real.assert_eq( + cols.is_lower_half_zero, + cols.is_zero_base_element[0].result * cols.is_zero_base_element[1].result, + ); + builder_is_real.assert_eq( + cols.is_upper_half_zero, + cols.is_zero_base_element[2].result * cols.is_zero_base_element[3].result, + ); + builder_is_real.assert_eq( + cols.result, + cols.is_lower_half_zero * cols.is_upper_half_zero, + ); + } +} diff --git a/core/src/operations/mod.rs b/core/src/operations/mod.rs index 9876b0df66..359320412d 100644 --- a/core/src/operations/mod.rs +++ b/core/src/operations/mod.rs @@ -12,8 +12,10 @@ mod div_extension; pub mod field; mod fixed_rotate_right; mod fixed_shift_right; +mod is_equal_extension; mod is_equal_word; mod is_zero; +mod is_zero_extension; mod is_zero_word; mod not; mod or; @@ -25,8 +27,10 @@ pub use add5::*; pub use and::*; pub use fixed_rotate_right::*; pub use fixed_shift_right::*; +pub use is_equal_extension::*; pub use is_equal_word::*; pub use is_zero::*; +pub use is_zero_extension::*; pub use is_zero_word::*; pub use not::*; pub use or::*; From e4c93c5cede1f323a38dd6150e19151d4d37cad0 Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Sun, 18 Feb 2024 07:00:28 -0800 Subject: [PATCH 5/5] fixed compilation errors --- core/src/air/extension.rs | 51 ++++++++++++++++------- core/src/operations/div_extension.rs | 25 +++++------ core/src/operations/is_equal_extension.rs | 6 ++- 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/core/src/air/extension.rs b/core/src/air/extension.rs index 1a546c8b16..6e8401bbdc 100644 --- a/core/src/air/extension.rs +++ b/core/src/air/extension.rs @@ -19,9 +19,9 @@ pub const DEGREE: usize = 4; #[repr(C)] pub struct Extension(pub [T; DEGREE]); // Degree 4 is hard coded for now. TODO: Change to a const generic -impl Extension { +impl Extension { // Returns the one element of the extension field - pub fn one>() -> Extension + pub fn one>() -> Extension where AB::Expr: AbstractField, { @@ -30,51 +30,72 @@ impl Extension { } // Converts a field element to extension element - pub fn from>(x: V) -> Extension - where - AB::Expr: From, - { - Extension(field_to_array(x.into())) + pub fn from>(x: E) -> Extension { + Extension(field_to_array(x)) } // Negates an extension field Element - pub fn neg>(self) -> Extension { + pub fn neg>(self) -> Extension { Extension(self.0.map(|x| AB::Expr::zero() - x)) } // Adds an extension field element - pub fn add>(self, rhs: &Self) -> Extension + pub fn add>(self, rhs: &Self) -> Extension where - V: Add + Copy, + E: Add, { let mut elements = Vec::new(); - for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.into_iter()) { + for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.clone().into_iter()) { elements.push(e1 + e2); } Extension(elements.try_into().unwrap()) } + // Subtracts an extension field element + pub fn sub>(self, rhs: &Self) -> Extension + where + E: Add, + { + let mut elements = Vec::new(); + + for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.clone().into_iter()) { + elements.push(e1 - e2); + } + + Extension(elements.try_into().unwrap()) + } + // Multiplies an extension field element - pub fn mul>(self, rhs: &Self) -> Extension + pub fn mul>(self, rhs: &Self) -> Extension where - V: Mul + Copy, + E: Mul, { let mut elements = Vec::new(); - for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.into_iter()) { + for (e1, e2) in self.0.into_iter().zip_eq(rhs.0.clone().into_iter()) { elements.push(e1 * e2); } Extension(elements.try_into().unwrap()) } - pub fn as_base_slice(&self) -> &[V] { + pub fn as_base_slice(&self) -> &[E] { &self.0 } } +impl Extension { + // Converts a field element with var base elements to one with expr base elements. + pub fn from_var>(self) -> Extension + where + V: Into, + { + Extension(self.0.map(|x| x.into())) + } +} + impl From> for Extension where F: Field, diff --git a/core/src/operations/div_extension.rs b/core/src/operations/div_extension.rs index a05fd65650..40abaeb340 100644 --- a/core/src/operations/div_extension.rs +++ b/core/src/operations/div_extension.rs @@ -2,11 +2,8 @@ //! use core::borrow::Borrow; use core::borrow::BorrowMut; -use p3_air::AirBuilder; use p3_field::extension::BinomialExtensionField; use p3_field::extension::BinomiallyExtendable; -use p3_field::AbstractField; -use p3_field::Field; use sp1_derive::AlignedBorrow; use std::mem::size_of; @@ -14,10 +11,14 @@ use crate::air::Extension; use crate::air::SP1AirBuilder; use crate::air::DEGREE; +use super::IsEqualExtOperation; + /// A set of columns needed to compute whether the given word is 0. #[derive(AlignedBorrow, Default, Debug, Clone, Copy)] #[repr(C)] pub struct DivExtOperation { + pub is_equal: IsEqualExtOperation, + /// Result is the quotient pub result: Extension, } @@ -30,6 +31,10 @@ impl> DivExtOperation { ) -> BinomialExtensionField { let result = a / b; self.result = result.into(); + + let product = b * result; + self.is_equal.populate(a, product); + result } @@ -39,16 +44,12 @@ impl> DivExtOperation { b: Extension, cols: DivExtOperation, is_real: AB::Expr, - ) { + ) where + AB::F: BinomiallyExtendable, + { builder.assert_bool(is_real.clone()); - let product = b.mul(&cols.result); - builder.when(is_real.clone()).assert_eq(product, a); - - // If the result is 1, then the input is 0. - builder - .when(is_real.clone()) - .when(cols.result) - .assert_zero(a.clone()); + let product = b.mul::(&cols.result.from_var::()); + IsEqualExtOperation::::eval(builder, a, product, cols.is_equal, is_real.clone()); } } diff --git a/core/src/operations/is_equal_extension.rs b/core/src/operations/is_equal_extension.rs index 9f95f59de3..bd2361959c 100644 --- a/core/src/operations/is_equal_extension.rs +++ b/core/src/operations/is_equal_extension.rs @@ -36,11 +36,13 @@ impl> IsEqualExtOperation { b: Extension, cols: IsEqualExtOperation, is_real: AB::Expr, - ) { + ) where + AB::F: BinomiallyExtendable, + { builder.assert_bool(is_real.clone()); // Calculate differences. - let diff = a.sub(b); + let diff = a.sub::(&b); // Check if the difference is 0. IsZeroExtOperation::::eval(builder, diff, cols.is_diff_zero, is_real.clone());