From b889cc1eb8094db3ebf68e3dc4a8e48ea8c43319 Mon Sep 17 00:00:00 2001
From: Roman <ackhtariev@gmail.com>
Date: Thu, 8 Dec 2022 23:34:27 -0500
Subject: [PATCH 1/5] feat(osmomath): BigDec power function with integer
 exponent, overflow tests at max spot price

---
 osmomath/decimal.go      |  20 +++++++
 osmomath/decimal_test.go | 121 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 141 insertions(+)

diff --git a/osmomath/decimal.go b/osmomath/decimal.go
index 821b0f5b4e1..71a9b9517e1 100644
--- a/osmomath/decimal.go
+++ b/osmomath/decimal.go
@@ -972,3 +972,23 @@ func (x BigDec) CustomBaseLog(base BigDec) BigDec {
 
 	return y
 }
+
+// PowerInteger takes a given decimal to an integer power
+// and returns the result. Non-mutative. Uses square and multiply
+// algorithm for performing the calculation.
+func (d BigDec) PowerInteger(power uint64) BigDec {
+	if power == 0 {
+		return OneDec()
+	}
+	tmp := OneDec()
+
+	for i := power; i > 1; {
+		if i%2 != 0 {
+			tmp = tmp.Mul(d)
+		}
+		i /= 2
+		d = d.Mul(d)
+	}
+
+	return d.Mul(tmp)
+}
diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index b50e9150b6d..a611f67942d 100644
--- a/osmomath/decimal_test.go
+++ b/osmomath/decimal_test.go
@@ -13,6 +13,7 @@ import (
 	"gopkg.in/yaml.v2"
 
 	"github.com/osmosis-labs/osmosis/v13/app/apptesting/osmoassert"
+	gammtypes "github.com/osmosis-labs/osmosis/v13/x/gamm/types"
 )
 
 type decimalTestSuite struct {
@@ -1018,3 +1019,123 @@ func (s *decimalTestSuite) TestCustomBaseLog() {
 		})
 	}
 }
+
+func (s *decimalTestSuite) TestPowerInteger() {
+	var expectedErrTolerance = MustNewDecFromStr("0.000000000000000000000000000000100000")
+
+	tests := map[string]struct {
+		base           BigDec
+		exponent       uint64
+		expectedResult BigDec
+
+		expectedToleranceOverwrite BigDec
+	}{
+		"0^2": {
+			base:     ZeroDec(),
+			exponent: 2,
+
+			expectedResult: ZeroDec(),
+		},
+		"1^2": {
+			base:     OneDec(),
+			exponent: 2,
+
+			expectedResult: OneDec(),
+		},
+		"4^4": {
+			base:     MustNewDecFromStr("4"),
+			exponent: 4,
+
+			expectedResult: MustNewDecFromStr("256"),
+		},
+		"5^3": {
+			base:     MustNewDecFromStr("5"),
+			exponent: 4,
+
+			expectedResult: MustNewDecFromStr("625"),
+		},
+		"e^10": {
+			base:     eulersNumber,
+			exponent: 10,
+
+			// https://www.wolframalpha.com/input?i=e%5E10+41+digits
+			expectedResult: MustNewDecFromStr("22026.465794806716516957900645284244366354"),
+		},
+		"geom twap overflow: 2^log_2{max spot price + 1}": {
+			base: twoBigDec,
+			// add 1 for simplicity of calculation to isolate overflow.
+			exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice).Add(OneDec()).LogBase2().TruncateInt().Uint64()),
+
+			// https://www.wolframalpha.com/input?i=2%5E%28floor%28+log+base+2+%282%5E128%29%29%29+++39+digits
+			expectedResult: MustNewDecFromStr("340282366920938463463374607431768211456"),
+		},
+		"geom twap overflow: 2^log_2{max spot price}": {
+			base:     twoBigDec,
+			exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice).LogBase2().TruncateInt().Uint64()),
+
+			// https://www.wolframalpha.com/input?i=2%5E%28floor%28+log+base+2+%282%5E128+-+1%29%29%29+++39+digits
+			expectedResult: MustNewDecFromStr("170141183460469231731687303715884105728"),
+		},
+		"geom twap overflow: 2^log_2{max spot price / 2 - 2017}": { // 2017 is prime.
+			base:     twoBigDec,
+			exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice.Quo(sdk.NewDec(2)).Sub(sdk.NewDec(2017))).LogBase2().TruncateInt().Uint64()),
+
+			// https://www.wolframalpha.com/input?i=e%5E10+41+digits
+			expectedResult: MustNewDecFromStr("85070591730234615865843651857942052864"),
+		},
+
+		// sdk.Dec test vectors copied from osmosis-labs/cosmos-sdk:
+
+		"1.0 ^ (10) => 1.0": {
+			base:     OneDec(),
+			exponent: 10,
+
+			expectedResult: OneDec(),
+		},
+		"0.5 ^ 2 => 0.25": {
+			base:     NewDecWithPrec(5, 1),
+			exponent: 2,
+
+			expectedResult: NewDecWithPrec(25, 2),
+		},
+		"0.2 ^ 2 => 0.04": {
+			base:     NewDecWithPrec(2, 1),
+			exponent: 2,
+
+			expectedResult: NewDecWithPrec(4, 2),
+		},
+		"3 ^ 3 => 27": {
+			base:     NewBigDec(3),
+			exponent: 3,
+
+			expectedResult: NewBigDec(27),
+		},
+		"-3 ^ 4 = 81": {
+			base:     NewBigDec(-3),
+			exponent: 4,
+
+			expectedResult: NewBigDec(81),
+		},
+		"1.414213562373095049 ^ 2 = 2": {
+			base:     NewDecWithPrec(1414213562373095049, 18),
+			exponent: 2,
+
+			expectedResult:             NewBigDec(2),
+			expectedToleranceOverwrite: MustNewDecFromStr("0.0000000000000000006"),
+		},
+	}
+
+	for name, tc := range tests {
+		tc := tc
+		s.Run(name, func() {
+
+			tolerance := expectedErrTolerance
+			if !tc.expectedToleranceOverwrite.IsNil() {
+				tolerance = tc.expectedToleranceOverwrite
+			}
+
+			actualResult := tc.base.PowerInteger(tc.exponent)
+			require.True(DecApproxEq(s.T(), tc.expectedResult, actualResult, tolerance))
+		})
+	}
+}

From 7d14b8fbf13d81fc490199b5c55b5dfe83c8c30d Mon Sep 17 00:00:00 2001
From: Roman <ackhtariev@gmail.com>
Date: Thu, 8 Dec 2022 23:55:42 -0500
Subject: [PATCH 2/5] euler's number

---
 osmomath/math.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/osmomath/math.go b/osmomath/math.go
index d8f341bb605..59b6268d26f 100644
--- a/osmomath/math.go
+++ b/osmomath/math.go
@@ -18,6 +18,9 @@ var (
 	one_half sdk.Dec = sdk.MustNewDecFromStr("0.5")
 	one      sdk.Dec = sdk.OneDec()
 	two      sdk.Dec = sdk.MustNewDecFromStr("2")
+
+	// https://www.wolframalpha.com/input?i=2.718281828459045235360287471352662498&assumption=%22ClashPrefs%22+-%3E+%7B%22Math%22%7D
+	eulersNumber = MustNewDecFromStr("2.718281828459045235360287471352662498")
 )
 
 // Returns the internal "power precision".

From bc8e81de05c6641ecfd7300d6260ec314d96b7a4 Mon Sep 17 00:00:00 2001
From: Roman <ackhtariev@gmail.com>
Date: Fri, 9 Dec 2022 00:07:45 -0500
Subject: [PATCH 3/5] nolint

---
 osmomath/math.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/osmomath/math.go b/osmomath/math.go
index 59b6268d26f..0e07cac8216 100644
--- a/osmomath/math.go
+++ b/osmomath/math.go
@@ -20,6 +20,7 @@ var (
 	two      sdk.Dec = sdk.MustNewDecFromStr("2")
 
 	// https://www.wolframalpha.com/input?i=2.718281828459045235360287471352662498&assumption=%22ClashPrefs%22+-%3E+%7B%22Math%22%7D
+	// nolint: unused
 	eulersNumber = MustNewDecFromStr("2.718281828459045235360287471352662498")
 )
 

From 8f75b70ca668a8fb903c198d660a33205dffac70 Mon Sep 17 00:00:00 2001
From: Roman <ackhtariev@gmail.com>
Date: Fri, 9 Dec 2022 11:58:41 -0500
Subject: [PATCH 4/5] more tests

---
 osmomath/decimal_test.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index a611f67942d..74146463065 100644
--- a/osmomath/decimal_test.go
+++ b/osmomath/decimal_test.go
@@ -1116,6 +1116,18 @@ func (s *decimalTestSuite) TestPowerInteger() {
 
 			expectedResult: NewBigDec(81),
 		},
+		"-3 ^ 50 = 717897987691852588770249": {
+			base:     NewBigDec(-3),
+			exponent: 50,
+
+			expectedResult: MustNewDecFromStr("717897987691852588770249"),
+		},
+		"-3 ^ 51 = -2153693963075557766310747": {
+			base:     NewBigDec(-3),
+			exponent: 51,
+
+			expectedResult: MustNewDecFromStr("-2153693963075557766310747"),
+		},
 		"1.414213562373095049 ^ 2 = 2": {
 			base:     NewDecWithPrec(1414213562373095049, 18),
 			exponent: 2,

From bb09c2866cae4bc08affbd1f1b357b513ba18bf9 Mon Sep 17 00:00:00 2001
From: Roman <ackhtariev@gmail.com>
Date: Fri, 9 Dec 2022 12:00:31 -0500
Subject: [PATCH 5/5] changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c550a45be9b..09e8e133d6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   - The v1beta1 queries actually have base asset and quote asset reversed, so you were always getting 1/correct spot price. People fixed this by reordering the arguments.
   - This PR adds v2 queries for doing the correct thing, and giving people time to migrate from v1beta1 queries to v2.
   - It also changes cosmwasm to only allow the v2 queries, as no contracts on Osmosis mainnet uses the v1beta1 queries.
+* [#3676](https://github.com/osmosis-labs/osmosis/pull/3676) implement `PowerInteger` function on `osmomath.BigDec` 
 
 
 ### Bug fixes