UNPKG

mina-attestations

Version:
169 lines 6.79 kB
import { Field, Int64, Provable, Sign, UInt32, UInt64, UInt8, } from 'o1js'; import { assert } from "../util.js"; export { Numeric, numericMaximumType }; // each of these types is naturally included in the next one const numericTypeOrder = [UInt8, UInt32, UInt64, Int64, Field]; /** * Arithmetic and comparison gadgets that work on all pairs of numeric types: * Field, Int64, UInt64, UInt32, and UInt8. * * Example: check if a UInt64 is less than a Field, or compute the sum of a UInt32 and an Int64 (as an Int64). * * The general strategy is to convert both inputs to the "bigger" type, and then perform the operation there. * We also add int64 comparisons which are missing from o1js. */ const Numeric = { /** * Add two numeric values. * The returned type is the larger of the two input types, and follows the overflow rules of that type. * * Example: * ```ts * const sum: UInt32 = Numeric.add(UInt8.from(5), UInt32.from(10)); * ``` */ add(left, right) { let [a, b] = convertToMaximum(left, right); return a.add(b); }, /** * Subtract two numeric values. * The returned type is the larger of the two input types, and follows the overflow rules of that type. * * Example: * ```ts * const difference: Field = Numeric.subtract(Int64.from(10), Field.from(15)); * ``` */ subtract(left, right) { let [a, b] = convertToMaximum(left, right); return a.sub(b); }, /** * Multiply two numeric values. * The returned type is the larger of the two input types, and follows the overflow rules of that type. */ multiply(left, right) { let [a, b] = convertToMaximum(left, right); return a.mul(b); }, /** * Divide two numeric values. * The returned type is the larger of the two input types, and follows the overflow rules of that type. */ divide(left, right) { let [a, b] = convertToMaximum(left, right); return a.div(b); }, /** * Compares two numeric values and returns true if `left < right`. * * **Warning**: when comparing an `Int64` and a `Field`, negative `Int64` are treated as large numbers close to the field size. * This is to be consistent with how order of field elements is understood in general. */ lessThan(left, right) { let [a, b] = convertToMaximum(left, right); if (a instanceof Int64) { return lessThanInt64(a, b); } return a.lessThan(b); }, /** * See {@link Numeric.lessThan}. */ lessThanOrEqual(left, right) { let [a, b] = convertToMaximum(left, right); if (a instanceof Int64) { return lessThanOrEqualInt64(a, b); } return a.lessThanOrEqual(b); }, /** * See {@link Numeric.lessThan}. */ greaterThan(left, right) { return Numeric.lessThan(right, left); }, /** * See {@link Numeric.lessThanOrEqual}. */ greaterThanOrEqual(left, right) { return Numeric.lessThanOrEqual(right, left); }, }; function numericMaximumType(left, right) { let leftTypeIndex = numericTypeOrder.findIndex((t) => left === t); let rightTypeIndex = numericTypeOrder.findIndex((t) => right === t); assert(leftTypeIndex !== -1, 'left is not a numeric type'); assert(rightTypeIndex !== -1, 'right is not a numeric type'); return numericTypeOrder[Math.max(leftTypeIndex, rightTypeIndex)]; } function convertToMaximum(left, right) { const leftTypeIndex = numericTypeOrder.findIndex((type) => left instanceof type); const rightTypeIndex = numericTypeOrder.findIndex((type) => right instanceof type); assert(leftTypeIndex !== -1, 'left is not a numeric type'); assert(rightTypeIndex !== -1, 'right is not a numeric type'); let leftType = numericTypeOrder[leftTypeIndex]; let rightType = numericTypeOrder[rightTypeIndex]; let resultType = numericTypeOrder[Math.max(leftTypeIndex, rightTypeIndex)]; let leftConverted = leftTypeIndex < rightTypeIndex ? resultType === Field ? leftType === Int64 ? left.toField() : leftType.toFields(left)[0] : resultType === Int64 ? leftType === UInt64 ? Int64.fromUnsigned(left) : Int64.fromUnsigned(left.toUInt64()) : resultType === UInt64 ? left.toUInt64() : left.toUInt32() : left; let rightConverted = leftTypeIndex > rightTypeIndex ? resultType === Field ? rightType === Int64 ? right.toField() : rightType.toFields(right)[0] : resultType === Int64 ? rightType === UInt64 ? Int64.fromUnsigned(right) : Int64.fromUnsigned(right.toUInt64()) : resultType === UInt64 ? right.toUInt64() : right.toUInt32() : right; return [leftConverted, rightConverted]; } // Int64 comparisons function lessThanInt64(left, right) { let magnitudeLessThan = left.magnitude.lessThan(right.magnitude); let magnitudeEqual = left.magnitude.equals(right.magnitude); let magnitudeGreaterThan = magnitudeLessThan.not().and(magnitudeEqual.not()); let unequalSign = left.sgn.mul(right.sgn).equals(Sign.minusOne); let leftNegative = left.sgn.equals(Sign.minusOne); return Provable.if(unequalSign, // if the signs are unequal, left < right <=> left < 0 // (this works because sign == -1 guarantees magnitude != 0) leftNegative, // if the signs are equal, and // negative: left < right <=> left.magnitude > right.magnitude // positive: left < right <=> left.magnitude < right.magnitude Provable.if(leftNegative, magnitudeGreaterThan, magnitudeLessThan)); } function lessThanOrEqualInt64(left, right) { let magnitudeLessThanOrEqual = left.magnitude.lessThanOrEqual(right.magnitude); let magnitudeEqual = left.magnitude.equals(right.magnitude); let magnitudeGreaterThanOrEqual = magnitudeLessThanOrEqual .not() .or(magnitudeEqual); let unequalSign = left.sgn.mul(right.sgn).equals(Sign.minusOne); let leftNegative = left.sgn.equals(Sign.minusOne); return Provable.if(unequalSign, // if the signs are unequal, left <= right <=> left < 0 leftNegative, // if the signs are equal, and // negative: left <= right <=> left.magnitude >= right.magnitude // positive: left <= right <=> left.magnitude <= right.magnitude Provable.if(leftNegative, magnitudeGreaterThanOrEqual, magnitudeLessThanOrEqual)); } //# sourceMappingURL=gadgets-numeric.js.map