-
Notifications
You must be signed in to change notification settings - Fork 131
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
WIP: Wide int ergonomic #544
base: master
Are you sure you want to change the base?
Conversation
Too much constructor overloadingIn the last commit a11490a, I created a class However, when moving to the next task, where I want My solution was to use some Should I use multiple dispatch |
I found some ugly solution with |
I agree on |
I'm sorry that this WIP PR is progressing slowly due to the inactive support. Our team decided to pause here.
If we regard |
Just chiming in to say this is really important work, there are many scenarios in which you cannot safely multiply two integers on Algorand since e.g. base units are capped at the entire 64 bits, so the lack of proper 128 bit support shoe-horns you into using byte arrays for everything (or writing raw |
@zejoeh Thanks for your appreciation, although this is not the priority of the team right now (see #543 (comment)). I tried to achieve the the purpose you said with this PR (didn't finish it due to the time limitation). Speaking of class CachedSeq:
cached: bool
@overload
def __init__(self, *exprs: Expr) -> None: # overload_1
pass
@overload
def __init__(self, exprs: list[Expr]) -> None: # overload_2
pass
def __init__(self, *exprs): # type: ignore : type copied from seq.py
if len(exprs) == 1 and isinstance(exprs[0], list):
exprs = exprs[0]
self.expr = Seq(*exprs)
self.cache_scratch = ScratchVar(self.expr.type_of())
self.cached = False
def load(self):
if not self.cached:
self.cached = True
return Seq(self.cache_scratch.store(self.expr), self.cache_scratch.load())
return self.cache_scratch.load() |
@PabloLION Since you are in control of decimals, maybe you have a similar situation as I did. I use the following much-less-general solution, but given the amount of work you put into this I'm sure you already have something similar or better for what you need: class MulwDivw(pt.Expr):
"""Wide multiplication of two 64 bit uints a and b followed by wide division by a 64 bit uint c."""
def __init__(self, a: pt.Expr, b: pt.Expr, c: pt.Expr):
super().__init__()
self.a = a
self.b = b
self.c = c
def __teal__(self, options: "CompileOptions"):
# mulw of a and b
mulw_start, mulw_end = pt.TealSimpleBlock.FromOp(options, pt.TealOp(self, pt.Op.mulw), self.a, self.b)
# divw of result and c
divw_start, divw_end = pt.TealSimpleBlock.FromOp(options, pt.TealOp(self, pt.Op.divw), self.c)
mulw_end.setNextBlock(divw_start)
return mulw_start, divw_end
def __str__(self):
return f"(MulwDivw ({self.a},{self.b},{self.c}))"
def type_of(self):
return pt.TealType.uint64
def has_return(self) -> bool:
return False For me this was enough to replace a lot of byte array operations in intermediary computations after verifying that the results all still fit in 64 bits, without having to handle the multiple return values problem. Using Of course with only this I still need to make sure my domain is otherwise bounded by 64 bits (or use byte arrays where it can't be). It would be great to have native 16 byte (128 bit)- and 32 byte (256 bit) arrays instead - a man can dream. 😄 |
@zejoeh I see this is correct and useful because I was implementing With my plan, to achieve the same calculation, with Also FYI, it's already possible to do 512-bit calculation with Byteslice (docs at https://pyteal.readthedocs.io/en/stable/arithmetic_expression.html#byteslice-arithmetic) |
Moved from PR #541. For issue #246
Context
addw
,expw
,mulw
,divmodw
should be implemented with MultiValue.WideSummation
andWideRatio
. (names TBD)Tasks
WideInt
similar toMultiValue
and use it in "wideexpr" to substituteMultiValue
. For a variablew:WideInt
,w.high()
load the high part of the wide integer from scratch space,w.low()
the low part.WideInt
has 3 constructor overloads (see next chapter)WideExpr
to return aWideInt
(in scratch space)+
(__pos__
) to convert aInt
to aWideInt
(with high word = 0)WideInt
toInt
. Fail when it has a non-zero high word. This is a shortcut forw.low.load() if w.high.load()==0 else ERROR
Int OP WideInt
andWideInt OP WideInt
, where OP can be+
,-
,*
,etc. This was done here and I'll update them(TBD) if allowed.ModW
,MinusW
,WideAdd
,WideMul
,WideSummation
andWideRatio
to process calculation betweenWideInt
sWideInt
constructorsWideInt(num: int) -> WideInt
WideInt(expr_high: Expr, expr_low: Expr) -> WideInt
WideInt(expr_high: ScratchSlot, expr_low: ScratchSlot) -> WideInt
Create a new WideInt.
Pass the int as the only argument. For example,
WideInt((1<<64)+1)
.Pass two
Expr
s. They must be of typeTealType.uint64
For example,WideInt(Int(1),Int(1))
.Pass two
ScratchSlot
s. For example,WideInt(ScratchSlot(), ScratchSlot())
.To be decided
WideInt
,WideExpr
,WideAdd
,WideMul
are good? Alternatives are:WideUint64
,MultiExpr
,AddWW
,MulWW
respectively+
(__pos__
) to convert aInt
to aWideInt
(high word = 0) is a good practice?DivW
so it accept params ofWideInt
(I think not, but add aWideDiv
instead)pyteal/pyteal/ast/expr.py
Lines 72 to 75 in 53c8e00