diff --git a/frontend/api.go b/frontend/api.go index c2b9d05a5..b3f218c52 100644 --- a/frontend/api.go +++ b/frontend/api.go @@ -51,18 +51,20 @@ type API interface { // Mul returns res = i1 * i2 * ... in Mul(i1, i2 Variable, in ...Variable) Variable - // DivUnchecked returns i1 / i2 . if i1 == i2 == 0, returns 0 + // DivUnchecked returns i1 / i2 + // If i1 == i2 == 0, the return value (0) is unconstrained. DivUnchecked(i1, i2 Variable) Variable // Div returns i1 / i2 + // If i2 == 0 the constraint will not be satisfied. Div(i1, i2 Variable) Variable // Inverse returns res = 1 / i1 + // If i1 == 0 the constraint will not be satisfied. Inverse(i1 Variable) Variable // --------------------------------------------------------------------------------------------- // Bit operations - // TODO @gbotrel move bit operations in std/math/bits // ToBinary unpacks a Variable in binary, // n is the number of bits to select (starting from lsb) @@ -72,29 +74,32 @@ type API interface { ToBinary(i1 Variable, n ...int) []Variable // FromBinary packs b, seen as a fr.Element in little endian + // This function constrain the bits b... to be boolean (0 or 1) FromBinary(b ...Variable) Variable // Xor returns a ^ b - // a and b must be 0 or 1 + // This function constrain a and b to be boolean (0 or 1) Xor(a, b Variable) Variable // Or returns a | b - // a and b must be 0 or 1 + // This function constrain a and b to be boolean (0 or 1) Or(a, b Variable) Variable - // Or returns a & b - // a and b must be 0 or 1 + // And returns a & b + // This function constrain a and b to be boolean (0 or 1) And(a, b Variable) Variable // --------------------------------------------------------------------------------------------- // Conditionals // Select if b is true, yields i1 else yields i2 + // This function constrain b to be boolean (0 or 1) Select(b Variable, i1, i2 Variable) Variable // Lookup2 performs a 2-bit lookup between i1, i2, i3, i4 based on bits b0 // and b1. Returns i0 if b0=b1=0, i1 if b0=1 and b1=0, i2 if b0=0 and b1=1 // and i3 if b0=b1=1. + // This function constrain b0 and b1 to be boolean (0 or 1) Lookup2(b0, b1 Variable, i0, i1, i2, i3 Variable) Variable // IsZero returns 1 if a is zero, 0 otherwise @@ -119,8 +124,9 @@ type API interface { // AssertIsDifferent fails if i1 == i2 AssertIsDifferent(i1, i2 Variable) - // AssertIsBoolean fails if v != 0 and v != 1 + // AssertIsBoolean fails if v ∉ {0,1} AssertIsBoolean(i1 Variable) + // AssertIsCrumb fails if v ∉ {0,1,2,3} (crumb is a 2-bit variable; see https://en.wikipedia.org/wiki/Units_of_information) AssertIsCrumb(i1 Variable) diff --git a/frontend/cs/scs/api.go b/frontend/cs/scs/api.go index fb2397cde..58ed49cfc 100644 --- a/frontend/cs/scs/api.go +++ b/frontend/cs/scs/api.go @@ -35,7 +35,6 @@ import ( "github.com/consensys/gnark/std/math/bits" ) -// Add returns res = i1+i2+...in func (builder *builder) Add(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { // separate the constant part from the variables vars, k := builder.filterConstantSum(append([]frontend.Variable{i1, i2}, in...)) @@ -105,7 +104,6 @@ func (builder *builder) mulAccFastTrack(a, b, c frontend.Variable) frontend.Vari return res } -// neg returns -in func (builder *builder) neg(in []frontend.Variable) []frontend.Variable { res := make([]frontend.Variable, len(in)) @@ -115,13 +113,11 @@ func (builder *builder) neg(in []frontend.Variable) []frontend.Variable { return res } -// Sub returns res = i1 - i2 - ...in func (builder *builder) Sub(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { r := builder.neg(append([]frontend.Variable{i2}, in...)) return builder.Add(i1, r[0], r[1:]...) } -// Neg returns -i func (builder *builder) Neg(i1 frontend.Variable) frontend.Variable { if n, ok := builder.constantValue(i1); ok { n = builder.cs.Neg(n) @@ -132,7 +128,6 @@ func (builder *builder) Neg(i1 frontend.Variable) frontend.Variable { return v } -// Mul returns res = i1 * i2 * ... in func (builder *builder) Mul(i1, i2 frontend.Variable, in ...frontend.Variable) frontend.Variable { vars, k := builder.filterConstantProd(append([]frontend.Variable{i1, i2}, in...)) if len(vars) == 0 { @@ -157,7 +152,6 @@ func (builder *builder) mulConstant(t expr.Term, m constraint.Element) expr.Term return t } -// DivUnchecked returns i1 / i2 . if i1 == i2 == 0, returns 0 func (builder *builder) DivUnchecked(i1, i2 frontend.Variable) frontend.Variable { c1, i1Constant := builder.constantValue(i1) c2, i2Constant := builder.constantValue(i2) @@ -195,7 +189,6 @@ func (builder *builder) DivUnchecked(i1, i2 frontend.Variable) frontend.Variable return res } -// Div returns i1 / i2 func (builder *builder) Div(i1, i2 frontend.Variable) frontend.Variable { // note that here we ensure that v2 can't be 0, but it costs us one extra constraint builder.Inverse(i2) @@ -203,7 +196,6 @@ func (builder *builder) Div(i1, i2 frontend.Variable) frontend.Variable { return builder.DivUnchecked(i1, i2) } -// Inverse returns res = 1 / i1 func (builder *builder) Inverse(i1 frontend.Variable) frontend.Variable { if c, ok := builder.constantValue(i1); ok { if c.IsZero() { @@ -236,11 +228,6 @@ func (builder *builder) Inverse(i1 frontend.Variable) frontend.Variable { // --------------------------------------------------------------------------------------------- // Bit operations -// ToBinary unpacks a frontend.Variable in binary, -// n is the number of bits to select (starting from lsb) -// n default value is fr.Bits the number of bits needed to represent a field element -// -// The result is in little endian (first bit= lsb) func (builder *builder) ToBinary(i1 frontend.Variable, n ...int) []frontend.Variable { // nbBits nbBits := builder.cs.FieldBitLen() @@ -254,13 +241,10 @@ func (builder *builder) ToBinary(i1 frontend.Variable, n ...int) []frontend.Vari return bits.ToBinary(builder, i1, bits.WithNbDigits(nbBits)) } -// FromBinary packs b, seen as a fr.Element in little endian func (builder *builder) FromBinary(b ...frontend.Variable) frontend.Variable { return bits.FromBinary(builder, b) } -// Xor returns a ^ b -// a and b must be 0 or 1 func (builder *builder) Xor(a, b frontend.Variable) frontend.Variable { // pre condition: a, b must be booleans builder.AssertIsBoolean(a) @@ -335,8 +319,6 @@ func (builder *builder) Xor(a, b frontend.Variable) frontend.Variable { return res } -// Or returns a | b -// a and b must be 0 or 1 func (builder *builder) Or(a, b frontend.Variable) frontend.Variable { builder.AssertIsBoolean(a) builder.AssertIsBoolean(b) @@ -388,8 +370,6 @@ func (builder *builder) Or(a, b frontend.Variable) frontend.Variable { return res } -// Or returns a & b -// a and b must be 0 or 1 func (builder *builder) And(a, b frontend.Variable) frontend.Variable { builder.AssertIsBoolean(a) builder.AssertIsBoolean(b) @@ -401,7 +381,6 @@ func (builder *builder) And(a, b frontend.Variable) frontend.Variable { // --------------------------------------------------------------------------------------------- // Conditionals -// Select if b is true, yields i1 else yields i2 func (builder *builder) Select(b frontend.Variable, i1, i2 frontend.Variable) frontend.Variable { _b, bConstant := builder.constantValue(b) @@ -424,9 +403,6 @@ func (builder *builder) Select(b frontend.Variable, i1, i2 frontend.Variable) fr return builder.Add(l, i2) } -// Lookup2 performs a 2-bit lookup between i1, i2, i3, i4 based on bits b0 -// and b1. Returns i0 if b0=b1=0, i1 if b0=1 and b1=0, i2 if b0=0 and b1=1 -// and i3 if b0=b1=1. func (builder *builder) Lookup2(b0, b1 frontend.Variable, i0, i1, i2, i3 frontend.Variable) frontend.Variable { // ensure that bits are actually bits. Adds no constraints if the variables // are already constrained. @@ -473,7 +449,6 @@ func (builder *builder) Lookup2(b0, b1 frontend.Variable, i0, i1, i2, i3 fronten } -// IsZero returns 1 if a is zero, 0 otherwise func (builder *builder) IsZero(i1 frontend.Variable) frontend.Variable { if a, ok := builder.constantValue(i1); ok { if a.IsZero() { @@ -519,7 +494,6 @@ func (builder *builder) IsZero(i1 frontend.Variable) frontend.Variable { return m } -// Cmp returns 1 if i1>i2, 0 if i1=i2, -1 if i1