# 128-bit TEAL operations

**URL:** <https://forum.algorand.co/t/128-bit-teal-operations/2739>\
**Category:** General\
**Created:** [March 4, 2021, 7:03am UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739 "2021-03-04T07:03:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![krotkiewicz](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/krotkiewicz/32/624_2.png) [@krotkiewicz](https://forum.algorand.co/u/krotkiewicz)\
**Post date:** [March 4, 2021, 7:03am UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/1 "2021-03-04T07:03:22Z")

</div>

We are trying to implement AMM (Automated Market Maker) DEX in TEAL.

Not sure how much into the details should I go but the problem that we are facing right now is that, we need to calculate proportion of assets that are already in the pool to the asset being added to the pool and from that we can calculate how much new pool tokens we should mint.

Lets assume that we have 100 pieces of asset X in the pool and this is represented by 1000 pool tokens (PT). When a user adds 10 more token X to the pool, we mint 100 new PT tokens and give it to the user. Simple.

This can be easily calculated from following formula pt = (newX/totalX) \* totalPT.

From here comes harder things. Below are my assumptions that could be wrong, so please feel free to correct me.

All 3 variables in this equation could be either small numbers or large one from 1 up to 2^64.

The problem is that to calculate it, we need to either do multiplication or division first. If we do do division first, we could lose precision (1/2=0), we could multiple it by some power of two first (and then divide it back). If we do multiplication first we could face integer overflow.  
In both cases we face either integer overflow or loosing lots of precision.

This means that there is no other way than to use mulw operation here? Or maybe I am exaggerating?

Also, after mulw we need to divide two values (low and high) from stack and convert it back to 64-bit? I can imagine this is not super hard and require just few operations.

---

<div class="post-metadata">

**Author:** ![jannotti](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/jannotti/32/688_2.png) [@jannotti](https://forum.algorand.co/u/jannotti)\
**Post date:** [March 4, 2021, 2:18pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/2 "2021-03-04T14:18:09Z")

</div>

I think you would avoid losing precision by also talking the modulo when performing that initial division, and use that fractional part in the later multiplication.

---

<div class="post-metadata">

**Author:** ![krotkiewicz](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/krotkiewicz/32/624_2.png) [@krotkiewicz](https://forum.algorand.co/u/krotkiewicz)\
**Post date:** [March 4, 2021, 4:13pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/3 "2021-03-04T16:13:14Z")

</div>

Ok, that make sense but what in case we have such numbers? I am getting either 0 or overflow.

 ![IMG_6743](https://us1.discourse-cdn.com/flex016/uploads/algorand/original/1X/cd394febebeb510edd73aac82ee280cf0657ef85.jpeg)

---

<div class="post-metadata">

**Author:** ![jannotti](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/jannotti/32/688_2.png) [@jannotti](https://forum.algorand.co/u/jannotti)\
**Post date:** [March 5, 2021, 9:23pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/4 "2021-03-05T21:23:28Z")

</div>

Let’s pretend TEAL only supported u16, so the numbers are small.  
I think you’re asking how you should calculate  
(2^8/2^9)(2^8+1). Which is (256/513)(258) = 128r384

How can we get 128r384 using only what TEAL has?

I changed the calculation slightly to be a better illustration.  
Dividing by 513 makes it more challenging - simple bit shifting won’t  
work for the division. I used 258 as the second multiplicand instead  
of 257 because otherwise the result is 128r128 which would be  
confusing.

First, use `mulw` for 256\*258 to get 66048, which would be returned as:  
1 in the high word, and 512 in the low word. How can we divide this  
two word representation of 66048 by 513?

Note that if you got a number in the high word that is bigger than  
your divisor, you’ve got a problem - your final result doesn’t even  
fit in the word size. I suppose something like that could happen in  
an AMM if the token values were extremely divergent. Not sure what  
you would do. So let’s assume otherwise for now.

The strategy will be to scale the small number in the high word up,  
divide it by 513, and then scale it back down in such a way that  
during the scale down, we figure out how it contributes tp the low word,  
which is the final result.

Pick the largest scale factor `k` that won’t make the high word  
overflow, and is a power of two. We can use `k`=2^15 because 1 is so  
small. Scale up and divide: 1 \* 2^15 / 513 = 63r449

So we now have three numbers. 512 in the low word from our original  
multiply, 63 after the division by 513 in the high word, and 449 as  
the remainder. Now we combine all of those.

The 63 needs to be scaled scaled “down” from the high word, into the  
low word, by 15 bits. So it’s going to contribute to our final answer  
as 126 (shifting down 15 bits from high into low is the same as  
shifting the high up 1 bit and treating it as the low). The 449 also  
needs to be scaled into the low word (as 898) but then combined with  
the 512 that’s already there (898+512=1410) and divided by  
513. 1410/513 = 2r384.

Add the 126 to 2 to get 128. And since the remainder was 384, we have  
calculated exactly 128r384. You’ll probably be making a rounding  
decision based on the 384 remainder compared to the 513 divisor.

I think perhaps the hardest part of this with TEAL today is the bit  
shifting for the scaling by `k`. You can “shift” by multiplying or  
dividing by integer constants. But I can’t think of a good way to  
obtain those constants given a calculated integer at run time. That is, if you  
want to shift up by 5, how do you calculate that you must multiply by  
32? Soon you’ll be able to make the right integer by using a new  
opcode `bitset`. In the mean time, I think you could hack up a kind of  
lookup table in a byte-array.

All that being said, I’m sure Algorand is amenable to making this  
easier, the question is what is the best way? Should we add `divw`  
which takes its argument as two stack words? That seems easy, and a  
smaller conceptual leaps than adding a u128 type (for which people  
would probably ask for mulw to get two u128s and we’d have the same  
problem!)

I suppose the “good news” is that, at the moment, if you tried to do  
this the “simple way” by multiplying first, with `*` instead of `mulw`, the  
overflow would panic your TEAL, so that txn just wouldn’t happen.

---

<div class="post-metadata">

**Author:** ![jannotti](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/jannotti/32/688_2.png) [@jannotti](https://forum.algorand.co/u/jannotti)\
**Post date:** [March 5, 2021, 11:37pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/5 "2021-03-05T23:37:56Z")

</div>

I’m not all convinced this always works. The low word could be nearly too big, so when you bring the bits down from the high word, you get addition overflow.

---

<div class="post-metadata">

**Author:** ![krotkiewicz](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/krotkiewicz/32/624_2.png) [@krotkiewicz](https://forum.algorand.co/u/krotkiewicz)\
**Post date:** [March 8, 2021, 1:55pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/6 "2021-03-08T13:55:29Z")

</div>

Thank you.

This looks like a right way to go, I will give it a try and think what to do with that one overflow. Still it looks like a quite a work to common math/finance problem that eventually most of more advanced smart contracts would end up.

Regarding extra opcodes. Adding divw would definitely help for that one case but I am not sure if it has any broader use cases.

So looks like I would need to build divw “subroutine” and wait for that opcode together with bitset.

---

<div class="post-metadata">

**Author:** ![jannotti](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.algorand.co/jannotti/32/688_2.png) [@jannotti](https://forum.algorand.co/u/jannotti)\
**Post date:** [June 17, 2021, 2:43pm UTC](https://forum.algorand.co/t/128-bit-teal-operations/2739/7 "2021-06-17T14:43:13Z")

</div>

With divmodw and callsub/retsub released to betanet…

```auto
func TestMulDiv(t *testing.T) {
	// Demonstrate a "function" that expects three u64s on stack,
	// and calculates B*C/A. (Following opcode documentaion
	// convention, C is top-of-stack, B is below it, and A is
	// below B.

	muldiv := `
muldiv:
mulw // multiply B*C. puts TWO u64s on stack
int 0 // high word of C as a double-word
dig 3 // pull C to TOS
divmodw
pop // pop unneeded remainder low word
pop // pop unneeded remainder high word
swap
int 0
==
assert // ensure high word of quotient was 0
swap // bring C to surface
pop // in order to get rid of it
retsub
`
	testAccepts(t, "int 5; int 8; int 10; callsub muldiv; int 16; ==; return;"+muldiv, 4)

	testRejects(t, "int 5; int 8; int 10; callsub muldiv; int 15; ==; return;"+muldiv, 4)

	testAccepts(t, "int 500000000000; int 80000000000; int 100000000000; callsub muldiv; int 16000000000; ==; return;"+muldiv, 4)
}

```
