Skip to content

Commit

Permalink
osmomath(log/CL): ln(x), log_1.0001(x), log_custom(x) (osmosis-labs#3169
Browse files Browse the repository at this point in the history
)

* natural logarithm

* tick log

* save

* save

* customBase

* logs

Co-authored-by: Ruslan Akhtariev <[email protected]>
  • Loading branch information
2 people authored and Ruslan Akhtariev committed Nov 1, 2022
1 parent 095e5b5 commit 5044503
Show file tree
Hide file tree
Showing 2 changed files with 321 additions and 0 deletions.
42 changes: 42 additions & 0 deletions osmomath/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ var (
oneInt = big.NewInt(1)
tenInt = big.NewInt(10)

// log_2(e)
// From: https://www.wolframalpha.com/input?i=log_2%28e%29+with+37+digits
logOfEbase2 = MustNewDecFromStr("1.442695040888963407359924681001892137")

// log_2(1.0001)
// From: https://www.wolframalpha.com/input?i=log_2%281.0001%29+to+33+digits
tickLogOf2 = MustNewDecFromStr("0.000144262291094554178391070900057480")
// initialized in init() since requires
// precision to be defined.
twoBigDec BigDec
Expand Down Expand Up @@ -917,3 +924,38 @@ func (x BigDec) LogBase2() BigDec {

return y
}

// Natural logarithm of x.
// Formula: ln(x) = log_2(x) / log_2(e)
func (x BigDec) Ln() BigDec {
log2x := x.LogBase2()

y := log2x.Quo(logOfEbase2)

return y
}

// log_1.0001(x) "tick" base logarithm
// Formula: log_1.0001(b) = log_2(b) / log_2(1.0001)
func (x BigDec) TickLog() BigDec {
log2x := x.LogBase2()

y := log2x.Quo(tickLogOf2)

return y
}

// log_a(x) custom base logarithm
// Formula: log_a(b) = log_2(b) / log_2(a)
func (x BigDec) CustomBaseLog(base BigDec) BigDec {
if base.LTE(ZeroDec()) || base.Equal(OneDec()) {
panic(fmt.Sprintf("log is not defined at base <= 0 or base == 1, base given (%s)", base))
}

log2x_argument := x.LogBase2()
log2x_base := base.LogBase2()

y := log2x_argument.Quo(log2x_base)

return y
}
279 changes: 279 additions & 0 deletions osmomath/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,3 +739,282 @@ func (s *decimalTestSuite) TestLog2() {
})
}
}

func (s *decimalTestSuite) TestLn() {
var expectedErrTolerance = MustNewDecFromStr("0.000000000000000000000000000000000100")

tests := map[string]struct {
initialValue BigDec
expected BigDec

expectedPanic bool
}{
"log_e{-1}; invalid; panic": {
initialValue: OneDec().Neg(),
expectedPanic: true,
},
"log_e{0}; invalid; panic": {
initialValue: ZeroDec(),
expectedPanic: true,
},
"log_e{0.001} = -6.90775527898213705205397436405309262": {
initialValue: MustNewDecFromStr("0.001"),
// From: https://www.wolframalpha.com/input?i=log0.001+to+36+digits+with+36+decimals
expected: MustNewDecFromStr("-6.90775527898213705205397436405309262"),
},
"log_e{0.56171821941421412902170941} = -0.576754943768592057376050794884207180": {
initialValue: MustNewDecFromStr("0.56171821941421412902170941"),
// From: https://www.wolframalpha.com/input?i=log0.56171821941421412902170941+to+36+digits
expected: MustNewDecFromStr("-0.576754943768592057376050794884207180"),
},
"log_e{0.999912345} = -0.000087658841924023373535614212850888": {
initialValue: MustNewDecFromStr("0.999912345"),
// From: https://www.wolframalpha.com/input?i=log0.999912345+to+32+digits
expected: MustNewDecFromStr("-0.000087658841924023373535614212850888"),
},
"log_e{1} = 0": {
initialValue: NewBigDec(1),
expected: NewBigDec(0),
},
"log_e{e} = 1": {
initialValue: MustNewDecFromStr("2.718281828459045235360287471352662498"),
// From: https://www.wolframalpha.com/input?i=e+with+36+decimals
expected: NewBigDec(1),
},
"log_e{7} = 1.945910149055313305105352743443179730": {
initialValue: NewBigDec(7),
// From: https://www.wolframalpha.com/input?i=log7+up+to+36+decimals
expected: MustNewDecFromStr("1.945910149055313305105352743443179730"),
},
"log_e{512} = 6.238324625039507784755089093123589113": {
initialValue: NewBigDec(512),
// From: https://www.wolframalpha.com/input?i=log512+up+to+36+decimals
expected: MustNewDecFromStr("6.238324625039507784755089093123589113"),
},
"log_e{580} = 6.36302810354046502061849560850445238": {
initialValue: NewBigDec(580),
// From: https://www.wolframalpha.com/input?i=log580+up+to+36+decimals
expected: MustNewDecFromStr("6.36302810354046502061849560850445238"),
},
"log_e{1024.987654321} = 6.93243584693509415029056534690631614": {
initialValue: NewDecWithPrec(1024987654321, 9),
// From: https://www.wolframalpha.com/input?i=log1024.987654321+to+36+digits
expected: MustNewDecFromStr("6.93243584693509415029056534690631614"),
},
"log_e{912648174127941279170121098210.92821920190204131121} = 68.986147965719214790400745338243805015": {
initialValue: MustNewDecFromStr("912648174127941279170121098210.92821920190204131121"),
// From: https://www.wolframalpha.com/input?i=log912648174127941279170121098210.92821920190204131121+to+38+digits
expected: MustNewDecFromStr("68.986147965719214790400745338243805015"),
},
}

for name, tc := range tests {
s.Run(name, func() {
osmoassert.ConditionalPanic(s.T(), tc.expectedPanic, func() {
// Create a copy to test that the original was not modified.
// That is, that Ln() is non-mutative.
initialCopy := ZeroDec()
initialCopy.i.Set(tc.initialValue.i)

// system under test.
res := tc.initialValue.Ln()
require.True(DecApproxEq(s.T(), tc.expected, res, expectedErrTolerance))
require.Equal(s.T(), initialCopy, tc.initialValue)
})
})
}
}

func (s *decimalTestSuite) TestTickLog() {
tests := map[string]struct {
initialValue BigDec
expected BigDec

expectedErrTolerance BigDec
expectedPanic bool
}{
"log_1.0001{-1}; invalid; panic": {
initialValue: OneDec().Neg(),
expectedPanic: true,
},
"log_1.0001{0}; invalid; panic": {
initialValue: ZeroDec(),
expectedPanic: true,
},
"log_1.0001{0.001} = -69081.006609899112313305835611219486392199": {
initialValue: MustNewDecFromStr("0.001"),
// From: https://www.wolframalpha.com/input?i=log_1.0001%280.001%29+to+41+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000143031879"),
expected: MustNewDecFromStr("-69081.006609899112313305835611219486392199"),
},
"log_1.0001{0.999912345} = -0.876632247930741919880461740717176538": {
initialValue: MustNewDecFromStr("0.999912345"),
// From: https://www.wolframalpha.com/input?i=log_1.0001%280.999912345%29+to+36+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000138702"),
expected: MustNewDecFromStr("-0.876632247930741919880461740717176538"),
},
"log_1.0001{1} = 0": {
initialValue: NewBigDec(1),

expectedErrTolerance: ZeroDec(),
expected: NewBigDec(0),
},
"log_1.0001{1.0001} = 1": {
initialValue: MustNewDecFromStr("1.0001"),

expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000152500"),
expected: OneDec(),
},
"log_1.0001{512} = 62386.365360724158196763710649998441051753": {
initialValue: NewBigDec(512),
// From: https://www.wolframalpha.com/input?i=log_1.0001%28512%29+to+41+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000129292137"),
expected: MustNewDecFromStr("62386.365360724158196763710649998441051753"),
},
"log_1.0001{1024.987654321} = 69327.824629506998657531621822514042777198": {
initialValue: NewDecWithPrec(1024987654321, 9),
// From: https://www.wolframalpha.com/input?i=log_1.0001%281024.987654321%29+to+41+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000143836264"),
expected: MustNewDecFromStr("69327.824629506998657531621822514042777198"),
},
"log_1.0001{912648174127941279170121098210.92821920190204131121} = 689895.972156319183538389792485913311778672": {
initialValue: MustNewDecFromStr("912648174127941279170121098210.92821920190204131121"),
// From: https://www.wolframalpha.com/input?i=log_1.0001%28912648174127941279170121098210.92821920190204131121%29+to+42+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000001429936067"),
expected: MustNewDecFromStr("689895.972156319183538389792485913311778672"),
},
}

for name, tc := range tests {
s.Run(name, func() {
osmoassert.ConditionalPanic(s.T(), tc.expectedPanic, func() {
// Create a copy to test that the original was not modified.
// That is, that Ln() is non-mutative.
initialCopy := ZeroDec()
initialCopy.i.Set(tc.initialValue.i)

// system under test.
res := tc.initialValue.TickLog()
fmt.Println(name, res.Sub(tc.expected).Abs())
require.True(DecApproxEq(s.T(), tc.expected, res, tc.expectedErrTolerance))
require.Equal(s.T(), initialCopy, tc.initialValue)
})
})
}
}

func (s *decimalTestSuite) TestCustomBaseLog() {
tests := map[string]struct {
initialValue BigDec
base BigDec

expected BigDec
expectedErrTolerance BigDec

expectedPanic bool
}{
"log_2{-1}: normal base, invalid argument - panics": {
initialValue: NewBigDec(-1),
base: NewBigDec(2),
expectedPanic: true,
},
"log_2{0}: normal base, invalid argument - panics": {
initialValue: NewBigDec(0),
base: NewBigDec(2),
expectedPanic: true,
},
"log_(-1)(2): invalid base, normal argument - panics": {
initialValue: NewBigDec(2),
base: NewBigDec(-1),
expectedPanic: true,
},
"log_1(2): base cannot equal to 1 - panics": {
initialValue: NewBigDec(2),
base: NewBigDec(1),
expectedPanic: true,
},
"log_30(100) = 1.353984985057691049642502891262784015": {
initialValue: NewBigDec(100),
base: NewBigDec(30),
// From: https://www.wolframalpha.com/input?i=log_30%28100%29+to+37+digits
expectedErrTolerance: ZeroDec(),
expected: MustNewDecFromStr("1.353984985057691049642502891262784015"),
},
"log_0.2(0.99) = 0.006244624769837438271878639001855450": {
initialValue: MustNewDecFromStr("0.99"),
base: MustNewDecFromStr("0.2"),
// From: https://www.wolframalpha.com/input?i=log_0.2%280.99%29+to+34+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000000013"),
expected: MustNewDecFromStr("0.006244624769837438271878639001855450"),
},

"log_0.0001(500000) = -1.424742501084004701196565276318876743": {
initialValue: NewBigDec(500000),
base: NewDecWithPrec(1, 4),
// From: https://www.wolframalpha.com/input?i=log_0.0001%28500000%29+to+37+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000000003"),
expected: MustNewDecFromStr("-1.424742501084004701196565276318876743"),
},

"log_500000(0.0001) = -0.701881216598197542030218906945601429": {
initialValue: NewDecWithPrec(1, 4),
base: NewBigDec(500000),
// From: https://www.wolframalpha.com/input?i=log_500000%280.0001%29+to+36+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000000001"),
expected: MustNewDecFromStr("-0.701881216598197542030218906945601429"),
},

"log_10000(5000000) = 1.674742501084004701196565276318876743": {
initialValue: NewBigDec(5000000),
base: NewBigDec(10000),
// From: https://www.wolframalpha.com/input?i=log_10000%285000000%29+to+37+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000000002"),
expected: MustNewDecFromStr("1.674742501084004701196565276318876743"),
},
"log_0.123456789(1) = 0": {
initialValue: OneDec(),
base: MustNewDecFromStr("0.123456789"),

expectedErrTolerance: ZeroDec(),
expected: ZeroDec(),
},
"log_1111(1111) = 1": {
initialValue: NewBigDec(1111),
base: NewBigDec(1111),

expectedErrTolerance: ZeroDec(),
expected: OneDec(),
},

"log_1.123{1024.987654321} = 59.760484327223888489694630378785099461": {
initialValue: NewDecWithPrec(1024987654321, 9),
base: NewDecWithPrec(1123, 3),
// From: https://www.wolframalpha.com/input?i=log_1.123%281024.987654321%29+to+38+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000007686"),
expected: MustNewDecFromStr("59.760484327223888489694630378785099461"),
},

"log_1.123{912648174127941279170121098210.92821920190204131121} = 594.689327867863079177915648832621538986": {
initialValue: MustNewDecFromStr("912648174127941279170121098210.92821920190204131121"),
base: NewDecWithPrec(1123, 3),
// From: https://www.wolframalpha.com/input?i=log_1.123%28912648174127941279170121098210.92821920190204131121%29+to+39+digits
expectedErrTolerance: MustNewDecFromStr("0.000000000000000000000000000000077705"),
expected: MustNewDecFromStr("594.689327867863079177915648832621538986"),
},
}
for name, tc := range tests {
s.Run(name, func() {
osmoassert.ConditionalPanic(s.T(), tc.expectedPanic, func() {
// Create a copy to test that the original was not modified.
// That is, that Ln() is non-mutative.
initialCopy := ZeroDec()
initialCopy.i.Set(tc.initialValue.i)

// system under test.
res := tc.initialValue.CustomBaseLog(tc.base)
require.True(DecApproxEq(s.T(), tc.expected, res, tc.expectedErrTolerance))
require.Equal(s.T(), initialCopy, tc.initialValue)
})
})
}
}

0 comments on commit 5044503

Please sign in to comment.