From 0f1588128f38aadc626c3f56fa9b83e9b162b9a8 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 7 Mar 2022 15:37:42 -0500 Subject: [PATCH 01/20] update on u128 add/mul arithmetic --- pyteal/ast/widemath.py | 175 +++++++++++++++++++++++++++++++++-------- 1 file changed, 141 insertions(+), 34 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 5b7fd756a..3a8f9d886 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -1,14 +1,51 @@ from typing import List, Tuple, TYPE_CHECKING -from ..types import TealType, require_type -from ..errors import TealInputError, TealInternalError, TealCompileError -from ..ir import TealOp, Op, TealSimpleBlock, TealBlock +from pyteal.ast.multi import MultiValue +from pyteal.ir.tealblock import TealBlock + +from ..types import TealType +from ..errors import TealInternalError, TealCompileError +from ..ir import TealOp, Op, TealSimpleBlock from .expr import Expr if TYPE_CHECKING: from ..compiler import CompileOptions +def multU128U64( + expr: Expr, factor: Expr, options: "CompileOptions" +) -> Tuple[TealBlock, TealSimpleBlock]: + facSrt, facEnd = factor.__teal__(options) + # stack is [..., A, B, C], where C is current factor + # need to pop all A,B,C from stack and push X,Y, where X and Y are: + # X * 2**64 + Y = (A * 2**64 + B) * C + # <=> X * 2**64 + Y = A * C * 2**64 + B * C + # <=> X = A * C + highword(B * C) + # Y = lowword(B * C) + multiply = TealSimpleBlock( + [ + TealOp(expr, Op.uncover, 2), # stack: [..., B, C, A] + TealOp(expr, Op.dig, 1), # stack: [..., B, C, A, C] + TealOp(expr, Op.mul), # stack: [..., B, C, A*C] + TealOp(expr, Op.cover, 2), # stack: [..., A*C, B, C] + TealOp( + expr, Op.mulw + ), # stack: [..., A*C, highword(B*C), lowword(B*C)] + TealOp( + expr, Op.cover, 2 + ), # stack: [..., lowword(B*C), A*C, highword(B*C)] + TealOp( + expr, Op.add + ), # stack: [..., lowword(B*C), A*C+highword(B*C)] + TealOp( + expr, Op.swap + ), # stack: [..., A*C+highword(B*C), lowword(B*C)] + ] + ) + facEnd.setNextBlock(multiply) + return facSrt, multiply + + def multiplyFactors( expr: Expr, factors: List[Expr], options: "CompileOptions" ) -> Tuple[TealSimpleBlock, TealSimpleBlock]: @@ -38,42 +75,70 @@ def multiplyFactors( end = multiplyFirst2 for factor in factors[2:]: - facXStart, facXEnd = factor.__teal__(options) - end.setNextBlock(facXStart) - - # stack is [..., A, B, C], where C is current factor - # need to pop all A,B,C from stack and push X,Y, where X and Y are: - # X * 2**64 + Y = (A * 2**64 + B) * C - # <=> X * 2**64 + Y = A * C * 2**64 + B * C - # <=> X = A * C + highword(B * C) - # Y = lowword(B * C) - multiply = TealSimpleBlock( - [ - TealOp(expr, Op.uncover, 2), # stack: [..., B, C, A] - TealOp(expr, Op.dig, 1), # stack: [..., B, C, A, C] - TealOp(expr, Op.mul), # stack: [..., B, C, A*C] - TealOp(expr, Op.cover, 2), # stack: [..., A*C, B, C] - TealOp( - expr, Op.mulw - ), # stack: [..., A*C, highword(B*C), lowword(B*C)] - TealOp( - expr, Op.cover, 2 - ), # stack: [..., lowword(B*C), A*C, highword(B*C)] - TealOp( - expr, Op.add - ), # stack: [..., lowword(B*C), A*C+highword(B*C)] - TealOp( - expr, Op.swap - ), # stack: [..., A*C+highword(B*C), lowword(B*C)] - ] - ) + facSrt, multed = multU128U64(expr, factor, options) + end.setNextBlock(facSrt) + end = multed + + return start, end + + +def addU128U64( + expr: Expr, term: Expr, options: "CompileOptions" +) -> Tuple[TealBlock, TealSimpleBlock]: + termSrt, termEnd = term.__teal__(options) + addition = TealSimpleBlock( + [ + TealOp(expr, Op.addw), + TealOp(expr, Op.cover, 2), + TealOp(expr, Op.add), + TealOp(expr, Op.uncover, 1), + ] + ) + termEnd.setNextBlock(addition) + return termSrt, addition + + +def addTerms( + expr: Expr, terms: List[Expr], options: "CompileOptions" +) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + if len(terms) == 0: + raise TealInternalError("Received 0 terms") + + start = TealSimpleBlock([]) + + term0srt, term0end = terms[0].__teal__(options) + + if len(terms) == 1: + highword = TealSimpleBlock([TealOp(expr, Op.int, 0)]) + + start.setNextBlock(highword) + highword.setNextBlock(term0srt) + end = term0end + else: + start.setNextBlock(term0srt) + + term1srt, term1end = terms[1].__teal__(options) + term0end.setNextBlock(term1srt) + addFirst2 = TealSimpleBlock([TealOp(expr, Op.addw)]) + term1end.setNextBlock(addFirst2) + end = addFirst2 - facXEnd.setNextBlock(multiply) - end = multiply + for term in terms[2:]: + termSrt, added = addU128U64(expr, term, options) + end.setNextBlock(termSrt) + end = added return start, end +""" +def substractU128U64( + expr: Expr, term: Expr, options: "CompileOptions" +) -> Tuple[TealBlock, TealSimpleBlock]: + pass +""" + + class WideRatio(Expr): """A class used to calculate expressions of the form :code:`(N_1 * N_2 * N_3 * ...) / (D_1 * D_2 * D_3 * ...)` @@ -157,3 +222,45 @@ def has_return(self): WideRatio.__module__ = "pyteal" + + +class WideUint128(MultiValue): + @staticmethod + def addw(*terms: Expr): + if len(terms) < 2: + pass + return WideUint128( + Op.addw, [TealType.uint64, TealType.uint64], args=list(terms) + ) + + @staticmethod + def mulw(*factors: Expr): + if len(factors) < 2: + pass + return WideUint128( + Op.addw, [TealType.uint64, TealType.uint64], args=list(factors) + ) + + @staticmethod + def expw(base: Expr, _pow: Expr): + return WideUint128( + Op.expw, [TealType.uint64, TealType.uint64], args=[base, _pow] + ) + + def __init__(self, op: Op, types: List[TealType], *, args: List[Expr]): + super().__init__(op, types, args=args) + + def __add__(self, other: Expr): + return super().__add__(other) + + def __sub__(self, other: Expr): + return super().__sub__(other) + + def __divmod__(self, other: "WideUint128"): + pass + + def __truediv__(self, other: Expr): + return super().__truediv__(other) + + def __teal__(self, options: "CompileOptions"): + return super().__teal__(options) From 9b0c8430d201f8639ad416d4c424a7a3906198df Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 7 Mar 2022 15:44:50 -0500 Subject: [PATCH 02/20] tame format --- pyteal/ast/widemath.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 3a8f9d886..a46b96e79 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -28,18 +28,10 @@ def multU128U64( TealOp(expr, Op.dig, 1), # stack: [..., B, C, A, C] TealOp(expr, Op.mul), # stack: [..., B, C, A*C] TealOp(expr, Op.cover, 2), # stack: [..., A*C, B, C] - TealOp( - expr, Op.mulw - ), # stack: [..., A*C, highword(B*C), lowword(B*C)] - TealOp( - expr, Op.cover, 2 - ), # stack: [..., lowword(B*C), A*C, highword(B*C)] - TealOp( - expr, Op.add - ), # stack: [..., lowword(B*C), A*C+highword(B*C)] - TealOp( - expr, Op.swap - ), # stack: [..., A*C+highword(B*C), lowword(B*C)] + TealOp(expr, Op.mulw), # stack: [..., A*C, highword(B*C), lowword(B*C)] + TealOp(expr, Op.cover, 2), # stack: [..., lowword(B*C), A*C, highword(B*C)] + TealOp(expr, Op.add), # stack: [..., lowword(B*C), A*C+highword(B*C)] + TealOp(expr, Op.swap), # stack: [..., A*C+highword(B*C), lowword(B*C)] ] ) facEnd.setNextBlock(multiply) From cfef23f6542fd25a1c93b348ad62055fd047a641 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Mar 2022 13:45:44 -0500 Subject: [PATCH 03/20] push what I have local --- pyteal/ast/widemath.py | 210 +++++++++++++++++++++++++++++++++-------- 1 file changed, 171 insertions(+), 39 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index a46b96e79..118edc90e 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -1,12 +1,12 @@ +from abc import ABCMeta, abstractmethod from typing import List, Tuple, TYPE_CHECKING -from pyteal.ast.multi import MultiValue -from pyteal.ir.tealblock import TealBlock - -from ..types import TealType +from ..types import TealType, require_type from ..errors import TealInternalError, TealCompileError -from ..ir import TealOp, Op, TealSimpleBlock +from ..ir import TealOp, Op, TealSimpleBlock, TealBlock from .expr import Expr +from .leafexpr import LeafExpr +from .multi import MultiValue if TYPE_CHECKING: from ..compiler import CompileOptions @@ -24,14 +24,22 @@ def multU128U64( # Y = lowword(B * C) multiply = TealSimpleBlock( [ - TealOp(expr, Op.uncover, 2), # stack: [..., B, C, A] - TealOp(expr, Op.dig, 1), # stack: [..., B, C, A, C] - TealOp(expr, Op.mul), # stack: [..., B, C, A*C] - TealOp(expr, Op.cover, 2), # stack: [..., A*C, B, C] - TealOp(expr, Op.mulw), # stack: [..., A*C, highword(B*C), lowword(B*C)] - TealOp(expr, Op.cover, 2), # stack: [..., lowword(B*C), A*C, highword(B*C)] - TealOp(expr, Op.add), # stack: [..., lowword(B*C), A*C+highword(B*C)] - TealOp(expr, Op.swap), # stack: [..., A*C+highword(B*C), lowword(B*C)] + # stack: [..., B, C, A] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, C, A, C] + TealOp(expr, Op.dig, 1), + # stack: [..., B, C, A*C] + TealOp(expr, Op.mul), + # stack: [..., A*C, B, C] + TealOp(expr, Op.cover, 2), + # stack: [..., A*C, highword(B*C), lowword(B*C)] + TealOp(expr, Op.mulw), + # stack: [..., lowword(B*C), A*C, highword(B*C)] + TealOp(expr, Op.cover, 2), + # stack: [..., lowword(B*C), A*C+highword(B*C)] + TealOp(expr, Op.add), + # stack: [..., A*C+highword(B*C), lowword(B*C)] + TealOp(expr, Op.swap), ] ) facEnd.setNextBlock(multiply) @@ -44,6 +52,9 @@ def multiplyFactors( if len(factors) == 0: raise TealInternalError("Received 0 factors") + for factor in factors: + require_type(factor, TealType.uint64) + start = TealSimpleBlock([]) fac0Start, fac0End = factors[0].__teal__(options) @@ -78,11 +89,21 @@ def addU128U64( expr: Expr, term: Expr, options: "CompileOptions" ) -> Tuple[TealBlock, TealSimpleBlock]: termSrt, termEnd = term.__teal__(options) + # stack is [..., A, B, C], where C is current term + # need to pop all A, B, C from stack and push X, Y, where X and Y are: + # X * 2 ** 64 + Y = (A * 2 ** 64) + (B + C) + # <=> X * 2 ** 64 + Y = (A + highword(B + C)) * 2 ** 64 + lowword(B + C) + # <=> X = A + highword(B + C) + # Y = lowword(B + C) addition = TealSimpleBlock( [ + # stack: [..., A, highword(B + C), lowword(B + C)] TealOp(expr, Op.addw), + # stack: [..., lowword(B + C), A, highword(B + C)] TealOp(expr, Op.cover, 2), + # stack: [..., lowword(B + C), A + highword(B + C)] TealOp(expr, Op.add), + # stack: [..., A + highword(B + C), lowword(B + C)] TealOp(expr, Op.uncover, 1), ] ) @@ -96,6 +117,9 @@ def addTerms( if len(terms) == 0: raise TealInternalError("Received 0 terms") + for term in terms: + require_type(term, TealType.uint64) + start = TealSimpleBlock([]) term0srt, term0end = terms[0].__teal__(options) @@ -123,12 +147,31 @@ def addTerms( return start, end -""" -def substractU128U64( +def substractU128( expr: Expr, term: Expr, options: "CompileOptions" ) -> Tuple[TealBlock, TealSimpleBlock]: - pass -""" + termSrt, termEnd = term.__teal__(options) + substract = TealSimpleBlock( + [ + TealOp(expr, Op.uncover, 3), + TealOp(expr, Op.uncover, 2), + TealOp(expr, Op.minus), + TealOp(expr, Op.neq), + TealOp(expr, Op.assert_), + TealOp(expr, Op.minus), + ] + ) + termEnd.setNextBlock(substract) + return termSrt, substract + + """ +uncover 3 // [B, C, D, A] +uncover 2 // [B, D, A, C] +- // [B, D, A-C] +! // [B, D, A-C == 0] +assert // [B, D] +- // [B-D], aka [X] + """ class WideRatio(Expr): @@ -216,43 +259,132 @@ def has_return(self): WideRatio.__module__ = "pyteal" -class WideUint128(MultiValue): +class WideUint128(LeafExpr, metaclass=ABCMeta): @staticmethod - def addw(*terms: Expr): + def sumW(*terms: Expr): if len(terms) < 2: - pass - return WideUint128( - Op.addw, [TealType.uint64, TealType.uint64], args=list(terms) - ) + raise TealInternalError("received term number less than 2") + + for term in terms: + require_type(term, TealType.uint64) + + class WideUint128Sum(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.addw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.terms = list(args) + + def __teal__(self, options: "CompileOptions"): + return addTerms(self, self.terms, options) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Sum(*terms) @staticmethod - def mulw(*factors: Expr): + def prodW(*factors: Expr): if len(factors) < 2: - pass - return WideUint128( - Op.addw, [TealType.uint64, TealType.uint64], args=list(factors) - ) + raise TealInternalError("received factor number less than 2") + + for factor in factors: + require_type(factor, TealType.uint64) + + class WideUint128Prod(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.mulw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.factors = list(args) + + def __teal__(self, options: "CompileOptions"): + return multiplyFactors(self, self.factors, options) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Prod(*factors) @staticmethod def expw(base: Expr, _pow: Expr): - return WideUint128( - Op.expw, [TealType.uint64, TealType.uint64], args=[base, _pow] - ) - - def __init__(self, op: Op, types: List[TealType], *, args: List[Expr]): - super().__init__(op, types, args=args) + require_type(base, TealType.uint64) + require_type(_pow, TealType.uint64) + + class WideUint128Exp(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.expw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.base = args[0] + self.power = args[1] + + def __teal__(self, options: "CompileOptions"): + return TealBlock.FromOp( + options, TealOp(self, Op.expw), self.base, self.power + ) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Exp(base, _pow) + + @abstractmethod + def __init__(self, *args: Expr): + pass def __add__(self, other: Expr): - return super().__add__(other) + if isinstance(other, WideUint128): + return self.add128w(other) + else: + require_type(other, TealType.uint64) + return self.add(other) + + def add128w(self, other: "WideUint128"): + pass + + def add(self, other: Expr): + pass def __sub__(self, other: Expr): - return super().__sub__(other) + if isinstance(other, WideUint128): + return self.minus128w(other) + else: + require_type(other, TealType.uint64) + return self.minus(other) + + def minus128w(self, other: "WideUint128"): + pass + + def minus(self, other: Expr): + pass + + def __div__(self, other: Expr): + pass + + def div128w(self, other: "WideUint128"): + if isinstance(other, WideUint128): + return self.div128w(other) + else: + require_type(other, TealType.uint64) + return self.div(other) + + def div(self, other: Expr): + # returns u64 + pass def __divmod__(self, other: "WideUint128"): pass - def __truediv__(self, other: Expr): - return super().__truediv__(other) + def __mod__(self, other: "WideUint128"): + pass + @abstractmethod def __teal__(self, options: "CompileOptions"): - return super().__teal__(options) + pass + + def type_of(self) -> TealType: + return TealType.none From c3f74786cccae7409373a7b3bfd02425598b5abd Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Mar 2022 14:58:32 -0500 Subject: [PATCH 04/20] update 128b minus --- pyteal/ast/widemath.py | 99 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 118edc90e..5286f8524 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -3,7 +3,7 @@ from ..types import TealType, require_type from ..errors import TealInternalError, TealCompileError -from ..ir import TealOp, Op, TealSimpleBlock, TealBlock +from ..ir import TealOp, Op, TealSimpleBlock, TealBlock, TealConditionalBlock from .expr import Expr from .leafexpr import LeafExpr from .multi import MultiValue @@ -147,7 +147,7 @@ def addTerms( return start, end -def substractU128( +def substractU128toU64( expr: Expr, term: Expr, options: "CompileOptions" ) -> Tuple[TealBlock, TealSimpleBlock]: termSrt, termEnd = term.__teal__(options) @@ -174,6 +174,99 @@ def substractU128( """ + +def __substrctU128U64(expr: Expr) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + # stack: [..., A, B, C], where A * 2 ** 64 + B is 128 bit uint, C is uint64 + substractPrep = TealSimpleBlock([ + # stack: [..., A, B, C, B, C] + TealOp(expr, Op.dup2), + # stack: [..., A, B, C, B >= C] + TealOp(expr, Op.gt), + ]) + substractCond = TealConditionalBlock([]) + substractTrueBlock = TealSimpleBlock([ + # stack: [..., A, B - C] + TealOp(expr, Op.minus) + ]) + + substractFalseBlock = TealSimpleBlock([ + # stack: [..., B, C, A] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, C, A, A] + TealOp(expr, Op.dup), + # stack: [..., B, C, A, A, 1] + TealOp(expr, Op.int, 1), + # stack: [..., B, C, A, A >= 1], + TealOp(expr, Op.gt), + # stack: [..., B, C, A] + TealOp(expr, Op.assert_), + # stack: [..., B, C, A, 1] + TealOp(expr, Op.int, 1), + # stack: [..., B, C, A - 1] + TealOp(expr, Op.minus), + # stack: [..., A - 1, B, C] + TealOp(expr, Op.cover, 2), + # stack: [..., A - 1, C, B] + TealOp(expr, Op.cover, 1), + # stack: [..., A - 1, C - B] + TealOp(expr, Op.minus), + # stack: [..., A - 1, 2^64 - 1 - (C - B)] + TealOp(expr, Op.bitwise_not), + # stack: [..., A - 1, 2^64 - (C - B), 1] + TealOp(expr, Op.int, 1), + # stack: [..., A - 1, 2^64 - (C - B)] + TealOp(expr, Op.add) + ]) + substractPrep.setNextBlock(substractCond) + substractCond.setTrueBlock(substractTrueBlock) + substractCond.setFalseBlock(substractFalseBlock) + + end = TealSimpleBlock([]) + substractTrueBlock.setNextBlock(end) + substractFalseBlock.setNextBlock(end) + return substractPrep, end + + +def substractU128U64( + expr: Expr, rhsTerm: Expr, options: "CompileOptions" +) -> Tuple[TealBlock, TealSimpleBlock]: + termSrt, termEnd = rhsTerm.__teal__(options) + subsSrt, subsEnd = __substrctU128U64(expr) + termEnd.setNextBlock(subsSrt) + return termSrt, subsEnd + + +def substractU128( + expr: Expr, lhsTerm: Expr, rhsTerm: Expr, options: "CompileOptions" +) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + start = TealSimpleBlock([]) + lhsSrt, lhsEnd = lhsTerm.__teal__(options) + rhsSrt, rhsEnd = rhsTerm.__teal__(options) + start.setNextBlock(lhsSrt) + lhsEnd.setNextBlock(rhsSrt) + # stack: [..., A, B, C, D] + highwordPrep = TealSimpleBlock([ + # stack: [..., B, C, D, A] + TealOp(expr, Op.uncover, 3), + # stack: [..., B, D, A, C] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, D, A, C, A, C] + TealOp(expr, Op.dup2), + # stack: [..., B, D, A, C, A >= C] + TealOp(expr, Op.gt), + # stack: [..., B, D, A, C] + TealOp(expr, Op.assert_), + # stack: [..., B, D, A - C] + TealOp(expr, Op.minus), + # stack: [..., A - C, B, D] + TealOp(expr, Op.cover, 2) + ]) + rhsEnd.setNextBlock(highwordPrep) + subsSrt, subsEnd = __substrctU128U64(expr) + highwordPrep.setNextBlock(subsSrt) + return start, subsEnd + + class WideRatio(Expr): """A class used to calculate expressions of the form :code:`(N_1 * N_2 * N_3 * ...) / (D_1 * D_2 * D_3 * ...)` @@ -362,7 +455,7 @@ def minus128w(self, other: "WideUint128"): def minus(self, other: Expr): pass - def __div__(self, other: Expr): + def __truediv__(self, other: Expr): pass def div128w(self, other: "WideUint128"): From 30fe7db33af05feb608b8c01e2ec24abdee99f95 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Mar 2022 14:58:46 -0500 Subject: [PATCH 05/20] fmt --- pyteal/ast/widemath.py | 119 ++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 5286f8524..bfddf4128 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -174,49 +174,54 @@ def substractU128toU64( """ - def __substrctU128U64(expr: Expr) -> Tuple[TealSimpleBlock, TealSimpleBlock]: # stack: [..., A, B, C], where A * 2 ** 64 + B is 128 bit uint, C is uint64 - substractPrep = TealSimpleBlock([ - # stack: [..., A, B, C, B, C] - TealOp(expr, Op.dup2), - # stack: [..., A, B, C, B >= C] - TealOp(expr, Op.gt), - ]) + substractPrep = TealSimpleBlock( + [ + # stack: [..., A, B, C, B, C] + TealOp(expr, Op.dup2), + # stack: [..., A, B, C, B >= C] + TealOp(expr, Op.gt), + ] + ) substractCond = TealConditionalBlock([]) - substractTrueBlock = TealSimpleBlock([ - # stack: [..., A, B - C] - TealOp(expr, Op.minus) - ]) - - substractFalseBlock = TealSimpleBlock([ - # stack: [..., B, C, A] - TealOp(expr, Op.uncover, 2), - # stack: [..., B, C, A, A] - TealOp(expr, Op.dup), - # stack: [..., B, C, A, A, 1] - TealOp(expr, Op.int, 1), - # stack: [..., B, C, A, A >= 1], - TealOp(expr, Op.gt), - # stack: [..., B, C, A] - TealOp(expr, Op.assert_), - # stack: [..., B, C, A, 1] - TealOp(expr, Op.int, 1), - # stack: [..., B, C, A - 1] - TealOp(expr, Op.minus), - # stack: [..., A - 1, B, C] - TealOp(expr, Op.cover, 2), - # stack: [..., A - 1, C, B] - TealOp(expr, Op.cover, 1), - # stack: [..., A - 1, C - B] - TealOp(expr, Op.minus), - # stack: [..., A - 1, 2^64 - 1 - (C - B)] - TealOp(expr, Op.bitwise_not), - # stack: [..., A - 1, 2^64 - (C - B), 1] - TealOp(expr, Op.int, 1), - # stack: [..., A - 1, 2^64 - (C - B)] - TealOp(expr, Op.add) - ]) + substractTrueBlock = TealSimpleBlock( + [ + # stack: [..., A, B - C] + TealOp(expr, Op.minus) + ] + ) + + substractFalseBlock = TealSimpleBlock( + [ + # stack: [..., B, C, A] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, C, A, A] + TealOp(expr, Op.dup), + # stack: [..., B, C, A, A, 1] + TealOp(expr, Op.int, 1), + # stack: [..., B, C, A, A >= 1], + TealOp(expr, Op.gt), + # stack: [..., B, C, A] + TealOp(expr, Op.assert_), + # stack: [..., B, C, A, 1] + TealOp(expr, Op.int, 1), + # stack: [..., B, C, A - 1] + TealOp(expr, Op.minus), + # stack: [..., A - 1, B, C] + TealOp(expr, Op.cover, 2), + # stack: [..., A - 1, C, B] + TealOp(expr, Op.cover, 1), + # stack: [..., A - 1, C - B] + TealOp(expr, Op.minus), + # stack: [..., A - 1, 2^64 - 1 - (C - B)] + TealOp(expr, Op.bitwise_not), + # stack: [..., A - 1, 2^64 - (C - B), 1] + TealOp(expr, Op.int, 1), + # stack: [..., A - 1, 2^64 - (C - B)] + TealOp(expr, Op.add), + ] + ) substractPrep.setNextBlock(substractCond) substractCond.setTrueBlock(substractTrueBlock) substractCond.setFalseBlock(substractFalseBlock) @@ -245,22 +250,24 @@ def substractU128( start.setNextBlock(lhsSrt) lhsEnd.setNextBlock(rhsSrt) # stack: [..., A, B, C, D] - highwordPrep = TealSimpleBlock([ - # stack: [..., B, C, D, A] - TealOp(expr, Op.uncover, 3), - # stack: [..., B, D, A, C] - TealOp(expr, Op.uncover, 2), - # stack: [..., B, D, A, C, A, C] - TealOp(expr, Op.dup2), - # stack: [..., B, D, A, C, A >= C] - TealOp(expr, Op.gt), - # stack: [..., B, D, A, C] - TealOp(expr, Op.assert_), - # stack: [..., B, D, A - C] - TealOp(expr, Op.minus), - # stack: [..., A - C, B, D] - TealOp(expr, Op.cover, 2) - ]) + highwordPrep = TealSimpleBlock( + [ + # stack: [..., B, C, D, A] + TealOp(expr, Op.uncover, 3), + # stack: [..., B, D, A, C] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, D, A, C, A, C] + TealOp(expr, Op.dup2), + # stack: [..., B, D, A, C, A >= C] + TealOp(expr, Op.gt), + # stack: [..., B, D, A, C] + TealOp(expr, Op.assert_), + # stack: [..., B, D, A - C] + TealOp(expr, Op.minus), + # stack: [..., A - C, B, D] + TealOp(expr, Op.cover, 2), + ] + ) rhsEnd.setNextBlock(highwordPrep) subsSrt, subsEnd = __substrctU128U64(expr) highwordPrep.setNextBlock(subsSrt) From 84a6969f8468a4c7591d07844f929601259f7fbf Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Mar 2022 15:09:08 -0500 Subject: [PATCH 06/20] minor --- pyteal/ast/widemath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index bfddf4128..bbf434350 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -216,7 +216,7 @@ def __substrctU128U64(expr: Expr) -> Tuple[TealSimpleBlock, TealSimpleBlock]: TealOp(expr, Op.minus), # stack: [..., A - 1, 2^64 - 1 - (C - B)] TealOp(expr, Op.bitwise_not), - # stack: [..., A - 1, 2^64 - (C - B), 1] + # stack: [..., A - 1, 2^64 - 1 - (C - B), 1] TealOp(expr, Op.int, 1), # stack: [..., A - 1, 2^64 - (C - B)] TealOp(expr, Op.add), From 094653c333475aa2a0def8017bbb4d6f060fd8b6 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 9 Mar 2022 16:24:24 -0500 Subject: [PATCH 07/20] per Jason comments --- pyteal/ast/widemath.py | 152 ++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index bbf434350..b6298a6d8 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -360,78 +360,6 @@ def has_return(self): class WideUint128(LeafExpr, metaclass=ABCMeta): - @staticmethod - def sumW(*terms: Expr): - if len(terms) < 2: - raise TealInternalError("received term number less than 2") - - for term in terms: - require_type(term, TealType.uint64) - - class WideUint128Sum(MultiValue, WideUint128): - def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.addw, [TealType.uint64, TealType.uint64], args=list(args) - ) - self.terms = list(args) - - def __teal__(self, options: "CompileOptions"): - return addTerms(self, self.terms, options) - - def __str__(self) -> str: - return MultiValue.__str__(self) - - return WideUint128Sum(*terms) - - @staticmethod - def prodW(*factors: Expr): - if len(factors) < 2: - raise TealInternalError("received factor number less than 2") - - for factor in factors: - require_type(factor, TealType.uint64) - - class WideUint128Prod(MultiValue, WideUint128): - def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.mulw, [TealType.uint64, TealType.uint64], args=list(args) - ) - self.factors = list(args) - - def __teal__(self, options: "CompileOptions"): - return multiplyFactors(self, self.factors, options) - - def __str__(self) -> str: - return MultiValue.__str__(self) - - return WideUint128Prod(*factors) - - @staticmethod - def expw(base: Expr, _pow: Expr): - require_type(base, TealType.uint64) - require_type(_pow, TealType.uint64) - - class WideUint128Exp(MultiValue, WideUint128): - def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.expw, [TealType.uint64, TealType.uint64], args=list(args) - ) - self.base = args[0] - self.power = args[1] - - def __teal__(self, options: "CompileOptions"): - return TealBlock.FromOp( - options, TealOp(self, Op.expw), self.base, self.power - ) - - def __str__(self) -> str: - return MultiValue.__str__(self) - - return WideUint128Exp(base, _pow) - @abstractmethod def __init__(self, *args: Expr): pass @@ -488,3 +416,83 @@ def __teal__(self, options: "CompileOptions"): def type_of(self) -> TealType: return TealType.none + + +# (A + B - C) * D +# sumW(A, B).minus(C).mul(D).reduce(lambda high, low: Seq(Assert(Not(high)), low)) # alias as .to64Bits() +# sumW(A, B).minus(C).mul(D).reduce(lambda high, low: Concat(Itob(high), Itob(low))) # alias as .toBinary() + +# (A + B) - (C * D) +# sumW(A, B).minus(mulW(C, D)).to64Bits() + + +def sumW(*terms: Expr): + if len(terms) < 2: + raise TealInternalError("received term number less than 2") + + for term in terms: + require_type(term, TealType.uint64) + + class WideUint128Sum(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.addw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.terms = list(args) + + def __teal__(self, options: "CompileOptions"): + return addTerms(self, self.terms, options) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Sum(*terms) + + +def prodW(*factors: Expr): + if len(factors) < 2: + raise TealInternalError("received factor number less than 2") + + for factor in factors: + require_type(factor, TealType.uint64) + + class WideUint128Prod(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.mulw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.factors = list(args) + + def __teal__(self, options: "CompileOptions"): + return multiplyFactors(self, self.factors, options) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Prod(*factors) + + +def expW(base: Expr, _pow: Expr): + require_type(base, TealType.uint64) + require_type(_pow, TealType.uint64) + + class WideUint128Exp(MultiValue, WideUint128): + def __init__(self, *args: Expr): + WideUint128.__init__(self, *args) + MultiValue.__init__( + self, Op.expw, [TealType.uint64, TealType.uint64], args=list(args) + ) + self.base = args[0] + self.power = args[1] + + def __teal__(self, options: "CompileOptions"): + return TealBlock.FromOp( + options, TealOp(self, Op.expw), self.base, self.power + ) + + def __str__(self) -> str: + return MultiValue.__str__(self) + + return WideUint128Exp(base, _pow) From 96dc76949852d96ad6d5d273234926ab01063f74 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 13:40:53 -0500 Subject: [PATCH 08/20] update full arith support --- pyteal/ast/widemath.py | 636 +++++++++++++++++++++++++++++++++-------- 1 file changed, 515 insertions(+), 121 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index b6298a6d8..5cae91a9c 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -2,7 +2,7 @@ from typing import List, Tuple, TYPE_CHECKING from ..types import TealType, require_type -from ..errors import TealInternalError, TealCompileError +from ..errors import TealInputError, TealInternalError, TealCompileError from ..ir import TealOp, Op, TealSimpleBlock, TealBlock, TealConditionalBlock from .expr import Expr from .leafexpr import LeafExpr @@ -12,79 +12,6 @@ from ..compiler import CompileOptions -def multU128U64( - expr: Expr, factor: Expr, options: "CompileOptions" -) -> Tuple[TealBlock, TealSimpleBlock]: - facSrt, facEnd = factor.__teal__(options) - # stack is [..., A, B, C], where C is current factor - # need to pop all A,B,C from stack and push X,Y, where X and Y are: - # X * 2**64 + Y = (A * 2**64 + B) * C - # <=> X * 2**64 + Y = A * C * 2**64 + B * C - # <=> X = A * C + highword(B * C) - # Y = lowword(B * C) - multiply = TealSimpleBlock( - [ - # stack: [..., B, C, A] - TealOp(expr, Op.uncover, 2), - # stack: [..., B, C, A, C] - TealOp(expr, Op.dig, 1), - # stack: [..., B, C, A*C] - TealOp(expr, Op.mul), - # stack: [..., A*C, B, C] - TealOp(expr, Op.cover, 2), - # stack: [..., A*C, highword(B*C), lowword(B*C)] - TealOp(expr, Op.mulw), - # stack: [..., lowword(B*C), A*C, highword(B*C)] - TealOp(expr, Op.cover, 2), - # stack: [..., lowword(B*C), A*C+highword(B*C)] - TealOp(expr, Op.add), - # stack: [..., A*C+highword(B*C), lowword(B*C)] - TealOp(expr, Op.swap), - ] - ) - facEnd.setNextBlock(multiply) - return facSrt, multiply - - -def multiplyFactors( - expr: Expr, factors: List[Expr], options: "CompileOptions" -) -> Tuple[TealSimpleBlock, TealSimpleBlock]: - if len(factors) == 0: - raise TealInternalError("Received 0 factors") - - for factor in factors: - require_type(factor, TealType.uint64) - - start = TealSimpleBlock([]) - - fac0Start, fac0End = factors[0].__teal__(options) - - if len(factors) == 1: - # need to use 0 as high word - highword = TealSimpleBlock([TealOp(expr, Op.int, 0)]) - - start.setNextBlock(highword) - highword.setNextBlock(fac0Start) - - end = fac0End - else: - start.setNextBlock(fac0Start) - - fac1Start, fac1End = factors[1].__teal__(options) - fac0End.setNextBlock(fac1Start) - - multiplyFirst2 = TealSimpleBlock([TealOp(expr, Op.mulw)]) - fac1End.setNextBlock(multiplyFirst2) - - end = multiplyFirst2 - for factor in factors[2:]: - facSrt, multed = multU128U64(expr, factor, options) - end.setNextBlock(facSrt) - end = multed - - return start, end - - def addU128U64( expr: Expr, term: Expr, options: "CompileOptions" ) -> Tuple[TealBlock, TealSimpleBlock]: @@ -147,31 +74,35 @@ def addTerms( return start, end -def substractU128toU64( +def addU128( expr: Expr, term: Expr, options: "CompileOptions" -) -> Tuple[TealBlock, TealSimpleBlock]: +) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + start = TealSimpleBlock([]) termSrt, termEnd = term.__teal__(options) - substract = TealSimpleBlock( + # stack: [..., A, B, C, D] + addition = TealSimpleBlock( [ - TealOp(expr, Op.uncover, 3), + # stack: [..., A, C, D, B] TealOp(expr, Op.uncover, 2), - TealOp(expr, Op.minus), - TealOp(expr, Op.neq), - TealOp(expr, Op.assert_), - TealOp(expr, Op.minus), + # stack: [..., A, C, B, D] + TealOp(expr, Op.swap), + # stack: [..., A, C, highword(B + D), lowword(B + D)] + TealOp(expr, Op.addw), + # stack: [..., lowword(B + D), A, C, highword(B + D)] + TealOp(expr, Op.cover, 3), + # stack: [..., lowword(B + D), highword(B + D), A, C] + TealOp(expr, Op.cover, 2), + # stack: [..., lowword(B + D), highword(B + D), A + C] + TealOp(expr, Op.add), + # stack: [..., lowword(B + D), highword(B + D) + A + C] + TealOp(expr, Op.add), + # stack: [..., highword(B + D) + A + C, lowword(B + D)] + TealOp(expr, Op.swap), ] ) - termEnd.setNextBlock(substract) - return termSrt, substract - - """ -uncover 3 // [B, C, D, A] -uncover 2 // [B, D, A, C] -- // [B, D, A-C] -! // [B, D, A-C == 0] -assert // [B, D] -- // [B-D], aka [X] - """ + start.setNextBlock(termSrt) + termEnd.setNextBlock(addition) + return start, addition def __substrctU128U64(expr: Expr) -> Tuple[TealSimpleBlock, TealSimpleBlock]: @@ -242,13 +173,12 @@ def substractU128U64( def substractU128( - expr: Expr, lhsTerm: Expr, rhsTerm: Expr, options: "CompileOptions" + expr: Expr, rhsTerm: Expr, options: "CompileOptions" ) -> Tuple[TealSimpleBlock, TealSimpleBlock]: start = TealSimpleBlock([]) - lhsSrt, lhsEnd = lhsTerm.__teal__(options) rhsSrt, rhsEnd = rhsTerm.__teal__(options) - start.setNextBlock(lhsSrt) - lhsEnd.setNextBlock(rhsSrt) + start.setNextBlock(rhsSrt) + rhsEnd.setNextBlock(rhsSrt) # stack: [..., A, B, C, D] highwordPrep = TealSimpleBlock( [ @@ -274,6 +204,158 @@ def substractU128( return start, subsEnd +def __multU128U64(expr: Expr) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + srt = TealSimpleBlock([]) + # stack: [..., A, B, C] + multiply = TealSimpleBlock( + [ + # stack: [..., B, C, A] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, C, A, C] + TealOp(expr, Op.dig, 1), + # stack: [..., B, C, A*C] + TealOp(expr, Op.mul), + # stack: [..., A*C, B, C] + TealOp(expr, Op.cover, 2), + # stack: [..., A*C, highword(B*C), lowword(B*C)] + TealOp(expr, Op.mulw), + # stack: [..., lowword(B*C), A*C, highword(B*C)] + TealOp(expr, Op.cover, 2), + # stack: [..., lowword(B*C), A*C+highword(B*C)] + TealOp(expr, Op.add), + # stack: [..., A*C+highword(B*C), lowword(B*C)] + TealOp(expr, Op.swap), + ] + ) + end = TealSimpleBlock([]) + srt.setNextBlock(multiply) + multiply.setNextBlock(end) + return srt, end + + +def multU128U64( + expr: Expr, factor: Expr, options: "CompileOptions" +) -> Tuple[TealBlock, TealSimpleBlock]: + facSrt, facEnd = factor.__teal__(options) + # stack is [..., A, B, C], where C is current factor + # need to pop all A,B,C from stack and push X,Y, where X and Y are: + # X * 2**64 + Y = (A * 2**64 + B) * C + # <=> X * 2**64 + Y = A * C * 2**64 + B * C + # <=> X = A * C + highword(B * C) + # Y = lowword(B * C) + multSrt, multEnd = __multU128U64(expr) + facEnd.setNextBlock(multSrt) + return facSrt, multEnd + + +def multiplyFactors( + expr: Expr, factors: List[Expr], options: "CompileOptions" +) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + if len(factors) == 0: + raise TealInternalError("Received 0 factors") + + for factor in factors: + require_type(factor, TealType.uint64) + + start = TealSimpleBlock([]) + + fac0Start, fac0End = factors[0].__teal__(options) + + if len(factors) == 1: + # need to use 0 as high word + highword = TealSimpleBlock([TealOp(expr, Op.int, 0)]) + + start.setNextBlock(highword) + highword.setNextBlock(fac0Start) + + end = fac0End + else: + start.setNextBlock(fac0Start) + + fac1Start, fac1End = factors[1].__teal__(options) + fac0End.setNextBlock(fac1Start) + + multiplyFirst2 = TealSimpleBlock([TealOp(expr, Op.mulw)]) + fac1End.setNextBlock(multiplyFirst2) + + end = multiplyFirst2 + for factor in factors[2:]: + facSrt, multed = multU128U64(expr, factor, options) + end.setNextBlock(facSrt) + end = multed + + return start, end + + +def multU128( + expr: Expr, rhsFactor: Expr, options: "CompileOptions" +) -> Tuple[TealSimpleBlock, TealSimpleBlock]: + start = TealSimpleBlock([]) + rhsSrt, rhsEnd = rhsFactor.__teal__(options) + start.setNextBlock(rhsSrt) + # stack: [..., A, B, C, D] + multPrep = TealSimpleBlock( + [ + # stack; [..., B, C, D, A] + TealOp(expr, Op.uncover, 3), + # stack: [..., B, D, A, C] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, D, A, C, A, C] + TealOp(expr, Op.dup2), + # stack: [..., B, D, A, C, A * C], if mul overflow, then u128 mult will also overflow + TealOp(expr, Op.mul), + # stack: [..., B, D, A, C, A * C != 0] + TealOp(expr, Op.logic_not), + # stack: [..., B, D, A, C], at least one of A and C is 0 + TealOp(expr, Op.assert_), + # stack: [..., B, D, A, C, C] + TealOp(expr, Op.dup), + # stack: [..., B, D, C, C, A] + TealOp(expr, Op.uncover, 2), + # stack: [..., B, D, C, C + A] + TealOp(expr, Op.add), + # stack: [..., B, D, C, C + A, C + A] + TealOp(expr, Op.dup), + # stack: [..., B, D, C + A, C, C + A] + TealOp(expr, Op.cover, 2), + # stack: [..., B, D, C + A, C == C + A] decide C + A should be swapped to before B or D + TealOp(expr, Op.eq), + ] + ) + rhsEnd.setNextBlock(multPrep) + + multCond = TealConditionalBlock([]) + multPrep.setNextBlock(multCond) + + # stack: [..., B, D, C] + multCondTrue = TealSimpleBlock( + [ + # stack: [..., B, C, D] + TealOp(expr, Op.swap), + # stack: [..., D, B, C] + TealOp(expr, Op.cover, 2), + # stack: [..., C, D, B] + TealOp(expr, Op.cover, 2), + ] + ) + # stack: [..., B, D, A] + multCondFalse = TealSimpleBlock( + [ + # stack: [..., A, B, D] + TealOp(expr, Op.cover, 2) + ] + ) + multCond.setTrueBlock(multCondTrue) + multCond.setFalseBlock(multCondFalse) + + # stack: [..., C, D, B] or [..., A, B, D] + multSrt, multEnd = __multU128U64(expr) + multCondTrue.setNextBlock(multSrt) + multCondFalse.setNextBlock(multSrt) + + return start, multEnd + + class WideRatio(Expr): """A class used to calculate expressions of the form :code:`(N_1 * N_2 * N_3 * ...) / (D_1 * D_2 * D_3 * ...)` @@ -366,57 +448,369 @@ def __init__(self, *args: Expr): def __add__(self, other: Expr): if isinstance(other, WideUint128): - return self.add128w(other) + return self.addU128(other) else: require_type(other, TealType.uint64) - return self.add(other) + return self.addU64(other) - def add128w(self, other: "WideUint128"): - pass + def addU128(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for addU128") - def add(self, other: Expr): - pass + class WideUint128AddU128(WideUint128): + def __init__(self, lhs: WideUint128, arg: WideUint128): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + addSrt, addEnd = addU128(self, self.term, options) + lhsEnd.setNextBlock(addSrt) + return lhsSrt, addEnd + + def __str__(self) -> str: + return "(addw {} {})".format(self.lhs, self.term) + + return WideUint128AddU128(self, other) + + def addU64(self, other: Expr): + require_type(other, TealType.uint64) + + class WideUint128AddU64(WideUint128): + def __init__(self, lhs: WideUint128, arg: Expr): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + addSrt, addEnd = addU128U64(self, self.term, options) + lhsEnd.setNextBlock(addSrt) + return lhsSrt, addEnd + + def __str__(self) -> str: + return "(addw {} {})".format(self.lhs, self.term) + + return WideUint128AddU64(self, other) def __sub__(self, other: Expr): if isinstance(other, WideUint128): - return self.minus128w(other) + return self.minusU128(other) else: require_type(other, TealType.uint64) - return self.minus(other) + return self.minusU64(other) - def minus128w(self, other: "WideUint128"): - pass + def minusU128(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for minusU128") - def minus(self, other: Expr): - pass + class WideUint128MinusU128(WideUint128): + def __init__(self, lhs: WideUint128, arg: WideUint128): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs - def __truediv__(self, other: Expr): - pass + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + subSrt, subEnd = substractU128(self, self.term, options) + lhsEnd.setNextBlock(subSrt) + return lhsSrt, subEnd + + def __str__(self) -> str: + return "(minusW {} {})".format(self.lhs, self.term) + + return WideUint128MinusU128(self, other) + + def minusU64(self, other: Expr): + require_type(other, TealType.uint64) + + class WideUint128MinusU64(WideUint128): + def __init__(self, lhs: WideUint128, arg: Expr): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs - def div128w(self, other: "WideUint128"): + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + subSrt, subEnd = substractU128U64(self, self.term, options) + lhsEnd.setNextBlock(subSrt) + return lhsSrt, subEnd + + def __str__(self) -> str: + return "(minusW {} {})".format(self.lhs, self.term) + + return WideUint128MinusU64(self, other) + + def __mul__(self, other): if isinstance(other, WideUint128): - return self.div128w(other) + return self.mulU128(other) else: require_type(other, TealType.uint64) - return self.div(other) + return self.mulU64(other) - def div(self, other: Expr): - # returns u64 - pass + def mulU128(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for mulU128") - def __divmod__(self, other: "WideUint128"): - pass + class WideUint128MulU128(WideUint128): + def __init__(self, lhs: WideUint128, arg: WideUint128): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + mulSrt, mulEnd = multU128(self, self.term, options) + lhsEnd.setNextBlock(mulSrt) + return lhsSrt, mulEnd + + def __str__(self) -> str: + return "(mulw {} {})".format(self.lhs, self.term) + + return WideUint128MulU128(self, other) + + def mulU64(self, other: Expr): + require_type(other, TealType.uint64) + + class WideUint128MulU64(WideUint128): + def __init__(self, lhs: WideUint128, arg: Expr): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + mulSrt, mulEnd = multU128U64(self, self.term, options) + lhsEnd.setNextBlock(mulSrt) + return lhsSrt, mulEnd + + def __str__(self) -> str: + return "(mulw {} {})".format(self.lhs, self.term) + + return WideUint128MulU64(self, other) + + def __truediv__(self, other: Expr): + if isinstance(other, WideUint128): + return self.divU128(other) + else: + require_type(other, TealType.uint64) + return self.divU64(other) + + def divU128(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for divU128") + + class WideUint128DivU128(WideUint128): + def __init__(self, lhs: WideUint128, arg: WideUint128): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + termSrt, termEnd = self.term.__teal__(options) + lhsEnd.setNextBlock(termSrt) + divFromDivmodW = TealSimpleBlock( + [ + TealOp(self, Op.divmodw), + TealOp(self, Op.pop), + TealOp(self, Op.pop), + ] + ) + termEnd.setNextBlock(divFromDivmodW) + return lhsSrt, divFromDivmodW + + def __str__(self) -> str: + return "(divW {} {})".format(self.lhs, self.term) + + return WideUint128DivU128(self, other) + + def divU64(self, other: Expr): + require_type(other, TealType.uint64) + + class WideUint128DivU64(WideUint128): + def __init__(self, lhs: WideUint128, arg: Expr): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + termSrt, termEnd = self.term.__teal__(options) + lhsEnd.setNextBlock(termSrt) + divFromDivW = TealSimpleBlock( + [ + TealOp(self, Op.divw), + ] + ) + termEnd.setNextBlock(divFromDivW) + return lhsSrt, divFromDivW + + def __str__(self) -> str: + return "(divW {} {})".format(self.lhs, self.term) + + def type_of(self) -> TealType: + return TealType.uint64 + + return WideUint128DivU64(self, other) def __mod__(self, other: "WideUint128"): - pass + if isinstance(other, WideUint128): + return self.modU128(other) + else: + require_type(other, TealType.uint64) + return self.modU64(other) + + def modU128(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for modU128") + + class WideUint128ModU128(WideUint128): + def __init__(self, lhs: WideUint128, arg: WideUint128): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + termSrt, termEnd = self.term.__teal__(options) + lhsEnd.setNextBlock(termSrt) + modFromDivModW = TealSimpleBlock( + [ + # stack: [..., divH, divL, modH, modL] + TealOp(self, Op.divmodw), + # stack: [..., divL, modH, modL, divH] + TealOp(self, Op.uncover, 3), + # stack: [..., modH, modL, divH, divL] + TealOp(self, Op.uncover, 3), + TealOp(self, Op.pop), + TealOp(self, Op.pop), + ] + ) + termEnd.setNextBlock(modFromDivModW) + return lhsSrt, modFromDivModW + + def __str__(self) -> str: + return "(modW {} {})".format(self.lhs, self.term) + + return WideUint128ModU128(self, other) + + def modU64(self, other: Expr): + require_type(other, TealType.uint64) + # returns u64 + class WideUint128ModU64(WideUint128): + def __init__(self, lhs: WideUint128, arg: Expr): + WideUint128.__init__(self, arg) + self.term = arg + self.lhs = lhs + + def __teal__(self, options: "CompileOptions"): + lhsSrt, lhsEnd = self.lhs.__teal__(options) + termSrt, termEnd = self.term.__teal__(options) + lhsEnd.setNextBlock(termSrt) + divFromDivW = TealSimpleBlock( + [ + # stack: [..., A, B, C, 0] + TealOp(self, Op.int, 0), + # stack: [..., A, B, 0, C] + TealOp(self, Op.swap), + # stack: [..., divH, divL, modH, modL] + TealOp(self, Op.divmodw), + # stack: [..., divL, modH, modL, divH] + TealOp(self, Op.uncover, 3), + # stack: [..., modH, modL, divH, divL] + TealOp(self, Op.uncover, 3), + TealOp(self, Op.pop), + TealOp(self, Op.pop), + ] + ) + termEnd.setNextBlock(divFromDivW) + return lhsSrt, divFromDivW + + def __str__(self) -> str: + return "(divW {} {})".format(self.lhs, self.term) + + def type_of(self) -> TealType: + return TealType.uint64 + + return WideUint128ModU64(self, other) @abstractmethod - def __teal__(self, options: "CompileOptions"): + def __teal__( + self, options: "CompileOptions" + ) -> Tuple[TealSimpleBlock, TealSimpleBlock]: pass def type_of(self) -> TealType: return TealType.none + def toUint64(self) -> Expr: + if self.type_of() == TealType.uint64: + raise TealInternalError("expression is already evaluated to uint64") + + class WideUint128ToUint64(Expr): + def __init__(self, wideArith: WideUint128): + super().__init__() + self.wideArith = wideArith + + def type_of(self) -> TealType: + return TealType.uint64 + + def has_return(self) -> bool: + return False + + def __teal__( + self, options: "CompileOptions" + ) -> Tuple[TealBlock, TealSimpleBlock]: + arithSrt, arithEnd = self.wideArith.__teal__(options) + reducer = TealSimpleBlock([TealOp(self, Op.swap), TealOp(self, Op.pop)]) + arithEnd.setNextBlock(reducer) + return arithSrt, reducer + + def __str__(self) -> str: + return "(toUint64 {})".format(self.wideArith) + + return WideUint128ToUint64(self) + + def toBinary(self) -> Expr: + if self.type_of() == TealType.uint64: + raise TealInternalError("expression is already evaluated to uint64") + + class WideUint128ToBinary(Expr): + def __init__(self, wideArith: WideUint128): + super().__init__() + self.wideArith = wideArith + + def type_of(self) -> TealType: + return TealType.uint64 + + def has_return(self) -> bool: + return False + + def __teal__( + self, options: "CompileOptions" + ) -> Tuple[TealBlock, TealSimpleBlock]: + arithSrt, arithEnd = self.wideArith.__teal__(options) + reducer = TealSimpleBlock( + [ + TealOp(self, Op.itob), + TealOp(self, Op.swap), + TealOp(self, Op.itob), + TealOp(self, Op.swap), + TealOp(self, Op.concat), + ] + ) + arithEnd.setNextBlock(reducer) + return arithSrt, reducer + + def __str__(self) -> str: + return "(toBinary {})".format(self.wideArith) + + return WideUint128ToBinary(self) + + +WideUint128.__module__ = "pyteal" # (A + B - C) * D # sumW(A, B).minus(C).mul(D).reduce(lambda high, low: Seq(Assert(Not(high)), low)) # alias as .to64Bits() From 182d3d9ca0630081bf65f098b4287121bc770e7a Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 13:48:52 -0500 Subject: [PATCH 09/20] minor --- pyteal/ast/widemath.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 5cae91a9c..92fba545a 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -6,7 +6,6 @@ from ..ir import TealOp, Op, TealSimpleBlock, TealBlock, TealConditionalBlock from .expr import Expr from .leafexpr import LeafExpr -from .multi import MultiValue if TYPE_CHECKING: from ..compiler import CompileOptions @@ -827,19 +826,16 @@ def sumW(*terms: Expr): for term in terms: require_type(term, TealType.uint64) - class WideUint128Sum(MultiValue, WideUint128): + class WideUint128Sum(WideUint128): def __init__(self, *args: Expr): WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.addw, [TealType.uint64, TealType.uint64], args=list(args) - ) self.terms = list(args) def __teal__(self, options: "CompileOptions"): return addTerms(self, self.terms, options) def __str__(self) -> str: - return MultiValue.__str__(self) + return "(addw {})".format(" ".join([term.__str__() for term in self.terms])) return WideUint128Sum(*terms) @@ -851,19 +847,18 @@ def prodW(*factors: Expr): for factor in factors: require_type(factor, TealType.uint64) - class WideUint128Prod(MultiValue, WideUint128): + class WideUint128Prod(WideUint128): def __init__(self, *args: Expr): WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.mulw, [TealType.uint64, TealType.uint64], args=list(args) - ) self.factors = list(args) def __teal__(self, options: "CompileOptions"): return multiplyFactors(self, self.factors, options) def __str__(self) -> str: - return MultiValue.__str__(self) + return "(mulw {})".format( + " ".join([factor.__str__() for factor in self.factors]) + ) return WideUint128Prod(*factors) @@ -872,12 +867,9 @@ def expW(base: Expr, _pow: Expr): require_type(base, TealType.uint64) require_type(_pow, TealType.uint64) - class WideUint128Exp(MultiValue, WideUint128): + class WideUint128Exp(WideUint128): def __init__(self, *args: Expr): WideUint128.__init__(self, *args) - MultiValue.__init__( - self, Op.expw, [TealType.uint64, TealType.uint64], args=list(args) - ) self.base = args[0] self.power = args[1] @@ -887,6 +879,6 @@ def __teal__(self, options: "CompileOptions"): ) def __str__(self) -> str: - return MultiValue.__str__(self) + return "(expw {} {})".format(self.base.__str__(), self.power.__str__()) return WideUint128Exp(base, _pow) From 8a39b00712b03c03100f2a74059f670ab19f68a1 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 14:13:44 -0500 Subject: [PATCH 10/20] minor --- pyteal/__init__.pyi | 4 ++++ pyteal/ast/__init__.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pyteal/__init__.pyi b/pyteal/__init__.pyi index 1591faeea..ebf049914 100644 --- a/pyteal/__init__.pyi +++ b/pyteal/__init__.pyi @@ -170,5 +170,9 @@ __all__ = [ "UnaryExpr", "While", "WideRatio", + "WideUint128", "compileTeal", + "expW", + "prodW", + "sumW", ] diff --git a/pyteal/ast/__init__.py b/pyteal/ast/__init__.py index f554daad6..e372922a2 100644 --- a/pyteal/ast/__init__.py +++ b/pyteal/ast/__init__.py @@ -97,7 +97,7 @@ # more ops from .naryexpr import NaryExpr, And, Or, Concat -from .widemath import WideRatio +from .widemath import WideRatio, sumW, prodW, expW, WideUint128 # control flow from .if_ import If @@ -218,6 +218,10 @@ "Or", "Concat", "WideRatio", + "sumW", + "prodW", + "expW", + "WideUint128", "If", "Cond", "Seq", From 59201906bd14eabde509284d53f0923f840e4f29 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 14:49:22 -0500 Subject: [PATCH 11/20] minor --- pyteal/ast/widemath.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 92fba545a..deb4eb679 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -697,7 +697,7 @@ def __str__(self) -> str: def modU64(self, other: Expr): require_type(other, TealType.uint64) - # returns u64 + class WideUint128ModU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): WideUint128.__init__(self, arg) @@ -733,7 +733,7 @@ def __str__(self) -> str: def type_of(self) -> TealType: return TealType.uint64 - return WideUint128ModU64(self, other) + return WideUint128ModU64(self, other).toUint64() @abstractmethod def __teal__( From 323f775898b1f885b02c4035005579d072227665 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 15:56:34 -0500 Subject: [PATCH 12/20] comply with ide hints: --- pyteal/ast/widemath.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index deb4eb679..a9c477f09 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -443,7 +443,7 @@ def has_return(self): class WideUint128(LeafExpr, metaclass=ABCMeta): @abstractmethod def __init__(self, *args: Expr): - pass + super().__init__() def __add__(self, other: Expr): if isinstance(other, WideUint128): @@ -835,7 +835,7 @@ def __teal__(self, options: "CompileOptions"): return addTerms(self, self.terms, options) def __str__(self) -> str: - return "(addw {})".format(" ".join([term.__str__() for term in self.terms])) + return "(addw {})".format(" ".join([t.__str__() for t in self.terms])) return WideUint128Sum(*terms) @@ -857,7 +857,7 @@ def __teal__(self, options: "CompileOptions"): def __str__(self) -> str: return "(mulw {})".format( - " ".join([factor.__str__() for factor in self.factors]) + " ".join([f.__str__() for f in self.factors]) ) return WideUint128Prod(*factors) From bea15600c4b66300f5be6de86427ff2553e86fe3 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 10 Mar 2022 16:56:38 -0500 Subject: [PATCH 13/20] fmt --- pyteal/ast/widemath.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index a9c477f09..32585dcc8 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -856,9 +856,7 @@ def __teal__(self, options: "CompileOptions"): return multiplyFactors(self, self.factors, options) def __str__(self) -> str: - return "(mulw {})".format( - " ".join([f.__str__() for f in self.factors]) - ) + return "(mulw {})".format(" ".join([f.__str__() for f in self.factors])) return WideUint128Prod(*factors) From 954d09241409ac540f6aa6fbf40fed6db152bbe5 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 11 Mar 2022 10:57:23 -0500 Subject: [PATCH 14/20] minor fixes here n there --- pyteal/ast/widemath.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 32585dcc8..063264b54 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -469,7 +469,7 @@ def __teal__(self, options: "CompileOptions"): return lhsSrt, addEnd def __str__(self) -> str: - return "(addw {} {})".format(self.lhs, self.term) + return "(addW {} {})".format(self.lhs, self.term) return WideUint128AddU128(self, other) @@ -489,7 +489,7 @@ def __teal__(self, options: "CompileOptions"): return lhsSrt, addEnd def __str__(self) -> str: - return "(addw {} {})".format(self.lhs, self.term) + return "(addW {} {})".format(self.lhs, self.term) return WideUint128AddU64(self, other) @@ -541,7 +541,7 @@ def __str__(self) -> str: return WideUint128MinusU64(self, other) - def __mul__(self, other): + def __mul__(self, other: Expr): if isinstance(other, WideUint128): return self.mulU128(other) else: @@ -565,7 +565,7 @@ def __teal__(self, options: "CompileOptions"): return lhsSrt, mulEnd def __str__(self) -> str: - return "(mulw {} {})".format(self.lhs, self.term) + return "(mulW {} {})".format(self.lhs, self.term) return WideUint128MulU128(self, other) @@ -585,7 +585,7 @@ def __teal__(self, options: "CompileOptions"): return lhsSrt, mulEnd def __str__(self) -> str: - return "(mulw {} {})".format(self.lhs, self.term) + return "(mulW {} {})".format(self.lhs, self.term) return WideUint128MulU64(self, other) @@ -654,7 +654,7 @@ def type_of(self) -> TealType: return WideUint128DivU64(self, other) - def __mod__(self, other: "WideUint128"): + def __mod__(self, other: Expr): if isinstance(other, WideUint128): return self.modU128(other) else: @@ -720,7 +720,13 @@ def __teal__(self, options: "CompileOptions"): TealOp(self, Op.uncover, 3), # stack: [..., modH, modL, divH, divL] TealOp(self, Op.uncover, 3), + # stack: [..., modH, modL, divH] TealOp(self, Op.pop), + # stack: [..., modH, modL] + TealOp(self, Op.pop), + # stack: [..., modL, modH] + TealOp(self, Op.swap), + # stack: [..., modL] TealOp(self, Op.pop), ] ) @@ -728,7 +734,7 @@ def __teal__(self, options: "CompileOptions"): return lhsSrt, divFromDivW def __str__(self) -> str: - return "(divW {} {})".format(self.lhs, self.term) + return "(modW {} {})".format(self.lhs, self.term) def type_of(self) -> TealType: return TealType.uint64 @@ -782,7 +788,7 @@ def __init__(self, wideArith: WideUint128): self.wideArith = wideArith def type_of(self) -> TealType: - return TealType.uint64 + return TealType.bytes def has_return(self) -> bool: return False From f7034decd5342c47c1eec9f80a72ad85366cba27 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 11 Mar 2022 13:43:26 -0500 Subject: [PATCH 15/20] update reduce To method with lowest scratch use --- pyteal/ast/widemath.py | 83 +++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 063264b54..0cc68c80d 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -1,6 +1,7 @@ from abc import ABCMeta, abstractmethod -from typing import List, Tuple, TYPE_CHECKING +from typing import Callable, List, Tuple, TYPE_CHECKING +from .scratch import ScratchSlot from ..types import TealType, require_type from ..errors import TealInputError, TealInternalError, TealCompileError from ..ir import TealOp, Op, TealSimpleBlock, TealBlock, TealConditionalBlock @@ -442,8 +443,9 @@ def has_return(self): class WideUint128(LeafExpr, metaclass=ABCMeta): @abstractmethod - def __init__(self, *args: Expr): + def __init__(self, outputNum: int): super().__init__() + self.output_slots = [ScratchSlot() for _ in range(outputNum)] def __add__(self, other: Expr): if isinstance(other, WideUint128): @@ -458,7 +460,7 @@ def addU128(self, other: "WideUint128"): class WideUint128AddU128(WideUint128): def __init__(self, lhs: WideUint128, arg: WideUint128): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -478,7 +480,7 @@ def addU64(self, other: Expr): class WideUint128AddU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -506,7 +508,7 @@ def minusU128(self, other: "WideUint128"): class WideUint128MinusU128(WideUint128): def __init__(self, lhs: WideUint128, arg: WideUint128): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -526,7 +528,7 @@ def minusU64(self, other: Expr): class WideUint128MinusU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -554,7 +556,7 @@ def mulU128(self, other: "WideUint128"): class WideUint128MulU128(WideUint128): def __init__(self, lhs: WideUint128, arg: WideUint128): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -574,7 +576,7 @@ def mulU64(self, other: Expr): class WideUint128MulU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -602,7 +604,7 @@ def divU128(self, other: "WideUint128"): class WideUint128DivU128(WideUint128): def __init__(self, lhs: WideUint128, arg: WideUint128): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -630,7 +632,7 @@ def divU64(self, other: Expr): class WideUint128DivU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 1) self.term = arg self.lhs = lhs @@ -649,9 +651,6 @@ def __teal__(self, options: "CompileOptions"): def __str__(self) -> str: return "(divW {} {})".format(self.lhs, self.term) - def type_of(self) -> TealType: - return TealType.uint64 - return WideUint128DivU64(self, other) def __mod__(self, other: Expr): @@ -667,7 +666,7 @@ def modU128(self, other: "WideUint128"): class WideUint128ModU128(WideUint128): def __init__(self, lhs: WideUint128, arg: WideUint128): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -700,7 +699,7 @@ def modU64(self, other: Expr): class WideUint128ModU64(WideUint128): def __init__(self, lhs: WideUint128, arg: Expr): - WideUint128.__init__(self, arg) + WideUint128.__init__(self, 2) self.term = arg self.lhs = lhs @@ -736,9 +735,6 @@ def __teal__(self, options: "CompileOptions"): def __str__(self) -> str: return "(modW {} {})".format(self.lhs, self.term) - def type_of(self) -> TealType: - return TealType.uint64 - return WideUint128ModU64(self, other).toUint64() @abstractmethod @@ -748,11 +744,13 @@ def __teal__( pass def type_of(self) -> TealType: - return TealType.none + return TealType.uint64 if len(self.output_slots) == 1 else TealType.none def toUint64(self) -> Expr: if self.type_of() == TealType.uint64: raise TealInternalError("expression is already evaluated to uint64") + elif len(self.output_slots) > 2: + raise TealInternalError("expression is only appliable for uint128") class WideUint128ToUint64(Expr): def __init__(self, wideArith: WideUint128): @@ -781,6 +779,8 @@ def __str__(self) -> str: def toBinary(self) -> Expr: if self.type_of() == TealType.uint64: raise TealInternalError("expression is already evaluated to uint64") + elif len(self.output_slots) > 2: + raise TealInternalError("expression is only appliable for uint128") class WideUint128ToBinary(Expr): def __init__(self, wideArith: WideUint128): @@ -814,6 +814,45 @@ def __str__(self) -> str: return WideUint128ToBinary(self) + def reduceTo(self, reducer: Callable[..., Expr]): + if self.type_of() == TealType.uint64: + raise TealInternalError("expression is already evaluated to uint64") + + class WideUint128Reduced(Expr): + def __init__(self, wideArith: WideUint128): + super().__init__() + self.wideArith = wideArith + + argsLoaded = [ + slot.load(TealType.uint64) for slot in self.wideArith.output_slots + ] + self.reduceExpr = reducer(argsLoaded) + + def __str__(self) -> str: + return "(reduced {})".format(self.wideArith) + + def __teal__( + self, options: "CompileOptions" + ) -> Tuple[TealBlock, TealSimpleBlock]: + srt = TealSimpleBlock([]) + curEnd = srt + for slot in reversed(self.wideArith.output_slots): + store = slot.store() + storeSrt, storeEnd = store.__teal__(options) + curEnd.setNextBlock(storeSrt) + curEnd = storeEnd + reduceSrt, reduceEnd = self.reduceExpr.__teal__(options) + curEnd.setNextBlock(reduceSrt) + return srt, reduceEnd + + def type_of(self) -> TealType: + return self.reduceExpr.type_of() + + def has_return(self) -> bool: + return False + + return WideUint128Reduced(self) + WideUint128.__module__ = "pyteal" @@ -834,7 +873,7 @@ def sumW(*terms: Expr): class WideUint128Sum(WideUint128): def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) + WideUint128.__init__(self, 2) self.terms = list(args) def __teal__(self, options: "CompileOptions"): @@ -855,7 +894,7 @@ def prodW(*factors: Expr): class WideUint128Prod(WideUint128): def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) + WideUint128.__init__(self, 2) self.factors = list(args) def __teal__(self, options: "CompileOptions"): @@ -873,7 +912,7 @@ def expW(base: Expr, _pow: Expr): class WideUint128Exp(WideUint128): def __init__(self, *args: Expr): - WideUint128.__init__(self, *args) + WideUint128.__init__(self, 2) self.base = args[0] self.power = args[1] From 356b61df6d390f976d5340de88ffc20a81bf51e6 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 11 Mar 2022 13:47:14 -0500 Subject: [PATCH 16/20] remove redundant abstract teal method --- pyteal/ast/widemath.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 0cc68c80d..8897bb4f4 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -737,12 +737,6 @@ def __str__(self) -> str: return WideUint128ModU64(self, other).toUint64() - @abstractmethod - def __teal__( - self, options: "CompileOptions" - ) -> Tuple[TealSimpleBlock, TealSimpleBlock]: - pass - def type_of(self) -> TealType: return TealType.uint64 if len(self.output_slots) == 1 else TealType.none From 1f9812b0cd8267f2f46cb84b3d08dcb33fcb1ccc Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 11 Mar 2022 13:55:25 -0500 Subject: [PATCH 17/20] update divmodw support --- pyteal/ast/widemath.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 8897bb4f4..98f46e2cd 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -737,6 +737,31 @@ def __str__(self) -> str: return WideUint128ModU64(self, other).toUint64() + def __divmod__(self, other: "WideUint128"): + if not isinstance(other, WideUint128): + raise TealInputError("expected WideUint128 input for divmodW") + + class WideUint128DivmodW(WideUint128): + def __init__(self, lhs: WideUint128, rhs: WideUint128): + WideUint128.__init__(self, 4) + self.lhs = lhs + self.term = rhs + + def __teal__( + self, options: "CompileOptions" + ) -> Tuple[TealBlock, TealSimpleBlock]: + lhsSrt, lhsEnd = self.lhs.__teal__(options) + termSrt, termEnd = self.term.__teal__(options) + lhsEnd.setNextBlock(termSrt) + divmodW = TealSimpleBlock([TealOp(self, Op.divmodw)]) + termEnd.setNextBlock(divmodW) + return lhsSrt, divmodW + + def __str__(self) -> str: + return "(divmodW {} {})".format(self.lhs, self.term) + + return WideUint128DivmodW(self, other) + def type_of(self) -> TealType: return TealType.uint64 if len(self.output_slots) == 1 else TealType.none From ac394b0b4168f854b78cd0e51936a172e06c0601 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 11 Mar 2022 14:11:57 -0500 Subject: [PATCH 18/20] update toUint64 check highword == 0 --- pyteal/ast/widemath.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 98f46e2cd..998d09564 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -786,7 +786,13 @@ def __teal__( self, options: "CompileOptions" ) -> Tuple[TealBlock, TealSimpleBlock]: arithSrt, arithEnd = self.wideArith.__teal__(options) - reducer = TealSimpleBlock([TealOp(self, Op.swap), TealOp(self, Op.pop)]) + reducer = TealSimpleBlock( + [ + TealOp(self, Op.swap), + TealOp(self, Op.logic_not), + TealOp(self, Op.assert_), + ] + ) arithEnd.setNextBlock(reducer) return arithSrt, reducer @@ -880,7 +886,8 @@ def has_return(self) -> bool: # sumW(A, B).minus(C).mul(D).reduce(lambda high, low: Concat(Itob(high), Itob(low))) # alias as .toBinary() # (A + B) - (C * D) -# sumW(A, B).minus(mulW(C, D)).to64Bits() +# sumW(A, B).minus(mulW(C, D)).toUint64() +# (sumW(A, B) - prodW(C, D)).toUint64() def sumW(*terms: Expr): From b3e02c8f59133f2941bf431df3092d0e90023266 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Mon, 14 Mar 2022 13:24:08 -0400 Subject: [PATCH 19/20] minor --- pyteal/ast/widemath.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 998d09564..3ae30027e 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -445,6 +445,8 @@ class WideUint128(LeafExpr, metaclass=ABCMeta): @abstractmethod def __init__(self, outputNum: int): super().__init__() + if outputNum <= 0: + raise TealInputError("number of output slot should be positive") self.output_slots = [ScratchSlot() for _ in range(outputNum)] def __add__(self, other: Expr): @@ -455,7 +457,7 @@ def __add__(self, other: Expr): return self.addU64(other) def addU128(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for addU128") class WideUint128AddU128(WideUint128): @@ -503,7 +505,7 @@ def __sub__(self, other: Expr): return self.minusU64(other) def minusU128(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for minusU128") class WideUint128MinusU128(WideUint128): @@ -551,7 +553,7 @@ def __mul__(self, other: Expr): return self.mulU64(other) def mulU128(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for mulU128") class WideUint128MulU128(WideUint128): @@ -599,7 +601,7 @@ def __truediv__(self, other: Expr): return self.divU64(other) def divU128(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for divU128") class WideUint128DivU128(WideUint128): @@ -661,7 +663,7 @@ def __mod__(self, other: Expr): return self.modU64(other) def modU128(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for modU128") class WideUint128ModU128(WideUint128): @@ -738,7 +740,7 @@ def __str__(self) -> str: return WideUint128ModU64(self, other).toUint64() def __divmod__(self, other: "WideUint128"): - if not isinstance(other, WideUint128): + if not isinstance(other, WideUint128) or len(other.output_slots) != 2: raise TealInputError("expected WideUint128 input for divmodW") class WideUint128DivmodW(WideUint128): From 28c3cea364976de6c726610736b85976613f7c16 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 20 Jan 2023 14:55:32 -0500 Subject: [PATCH 20/20] remove unnecessary lines --- pyteal/ast/widemath.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyteal/ast/widemath.py b/pyteal/ast/widemath.py index 80f6aa2db..e662bf871 100644 --- a/pyteal/ast/widemath.py +++ b/pyteal/ast/widemath.py @@ -84,8 +84,6 @@ def addU128( [ # stack: [..., A, C, D, B] TealOp(expr, Op.uncover, 2), - # stack: [..., A, C, B, D] - TealOp(expr, Op.swap), # stack: [..., A, C, highword(B + D), lowword(B + D)] TealOp(expr, Op.addw), # stack: [..., lowword(B + D), A, C, highword(B + D)]