-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proper Int64 #171
Proper Int64 #171
Conversation
src/lib/int.ts
Outdated
return Int64.sizeInFields(); | ||
function argToField( | ||
name: string, | ||
x: { value: Field } | number | string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: add range checks (not constraints, just checks) on constant UInt64 / UInt32 / Int64 as you create them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome! Code is clear, and this looks good to me, left a few minor comments
return new Int64(this.value.neg()); | ||
add(y: Int64 | number | string | bigint | UInt64 | UInt32) { | ||
let y_ = argToInt64(y); | ||
return Int64.fromField(this.toField().add(y_.toField())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking that with lookups, it'd be easier to just represent the Int64 as a Field and do range checks after every operation to make sure that 2^64 * res
is in a [0, 2^128]
range. To contrast:
- that would be 2 range checks (because our range is smaller than 2^128) and 1 constraint to shift the value being looked up. Including the addition that's 4 constraints)
- This PR's implementation is:
- 2 direct
toField
(2 constraints) fromField
is 1toField
, 1assertEquals
, 1Witness
- the
Witness
will also callcheck
which will call 1square
and 1assertEquals
, check
also callsUint64.Check
, which is actually quite heavy due to the lack of lookups as well. But we have to imagine that this would be just two lookup constraints at most eventually (with range lookups)- so that's 8-9 constraints
- 2 direct
Not sure if I'm reading this correctly, or if I'm missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think you may have a point.. I might reconsider this when range checks become cheap.
Do you mean shifting with 2^64 + res
and range-checking that? I don't follow how it works with multiplication & negative numbers.
Currently, it's convenient to have Int64 be {magnitude, sign} because that mirrors the Ocaml data structure. If I make it a single Field constrained to 64 bits, I allow a slightly smaller range than the Ocaml type (64 vs 65 bits)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah +
not *
sorry
src/lib/int.ts
Outdated
if (typeof x === 'bigint') return Int64.fromBigInt(x); | ||
if (x instanceof UInt64 || x instanceof UInt32) return Int64.fromUnsigned(x); | ||
return x; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this not implemented on Int64
? Something like Int64.from(x: number | string | bigint | UInt64 | UInt32 | Int64)
.
and same issue here, the fromNumber
and other similar functions don't error if the user tries to input values that are too large (or too small)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great suggestion!
src/lib/int.ts
Outdated
@@ -53,7 +42,7 @@ export class UInt64 extends CircuitValue { | |||
|
|||
static NUM_BITS = 64; | |||
|
|||
divMod(y: UInt64 | number): [UInt64, UInt64] { | |||
divMod(y: UInt64 | number | string): [UInt64, UInt64] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this return type is a bit error prone, why not return something like { quotient, rest }
instead?
Especially when we do things like let [q] =
or let [, r] =
throw 'repr'; | ||
equals(y: Int64 | number | string | bigint | UInt64 | UInt32) { | ||
let y_ = argToInt64(y); | ||
return this.toField().equals(y_.toField()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would this.magnitude.equals(y_.magnitude).and(this.sign.equals(y_.sign)
be better? The two equalities could be optimized by the compiler (using the permutation), and thus you'd only have one constraint (the and
)
actually never mind, you can't optimize these with the permutation as you don't assert. But maybe the assertEquals could use something like this instead
this.magnitude.assertEquals(y_.magnitude);
this.sign.assertEquals(y_.sign);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't do that because I was worried about special-casing for 0, where -0 should equal 0.. The special-casing would also require multiplication constraints I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point :o
@mimoo addressed all feedback, this should be good to go, will merge once the parent PR is merged |
Int64
implementation #66number
,string
, etc as method argumentsCurrency.Amount.Signed.t
This is stacked on top of #164, so that it can:
Since this will use updated bindings from MinaProtocol/mina#11042, which fix an aspect of the
Field
constructor, this also adds a few new tests for the Field constructor.