Skip to content

Commit

Permalink
feat(stdlibs): add math/overflow (gnolang#1698)
Browse files Browse the repository at this point in the history
## Description

This PR ports over the overflow stdlib to Gno. The addition of this
library will enable adding Coin functionality already existing in tm2's
[coin.go](https://github.com/gnolang/gno/blob/master/tm2/pkg/std/coin.go)
the
[Coin/Coins](https://github.com/gnolang/gno/blob/master/gnovm/stdlibs/std/coins.gno)
in Gno.

The tests pass fully, and the library was simply copy-pasted.

As per the conversation below, this PR also deprecates/removes the
`examples/` package for `overflow`, as it makes more sense to have
`overflow` be a part of stdlibs.

This will unblock gnolang#1696.

cc @thehowl

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>

---------

Co-authored-by: Hariom Verma <[email protected]>
  • Loading branch information
leohhhn and harry-hov authored Apr 24, 2024
1 parent b4bf514 commit 688edf5
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 210 deletions.
2 changes: 1 addition & 1 deletion examples/gno.land/p/demo/groups/gno.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module gno.land/p/demo/groups

require (
gno.land/p/demo/maths v0.0.0-latest
gno.land/p/demo/rat v0.0.0-latest
gno.land/r/demo/boards v0.0.0-latest
)
6 changes: 3 additions & 3 deletions examples/gno.land/p/demo/groups/vote_set.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"std"
"time"

"gno.land/p/demo/maths"
"gno.land/p/demo/rat"
)

//----------------------------------------
Expand Down Expand Up @@ -71,8 +71,8 @@ func (vlist *VoteList) CountVotes(target string) int {
// Committee

type Committee struct {
Quorum maths.Rat
Threshold maths.Rat
Quorum rat.Rat
Threshold rat.Rat
Addresses std.AddressSet
}

Expand Down
1 change: 0 additions & 1 deletion examples/gno.land/p/demo/maths/gno.mod

This file was deleted.

1 change: 1 addition & 0 deletions examples/gno.land/p/demo/rat/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/demo/rat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package maths
package rat

const (
intSize = 32 << (^uint(0) >> 63) // 32 or 64
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package maths
package rat

//----------------------------------------
// Rat fractions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// NOTE: there was a bug with the original Quotient* functions, and
// testing method. These have been fixed here, and tests ported to
// tests/files/maths_int*.go respectively.
// TODO: make PR upstream.
package maths
// Note: moved over from p/demo/maths.

/*
Package overflow offers overflow-checked integer arithmetic operations
Expand All @@ -24,12 +23,16 @@ overflow.Add(math.MaxInt64,1) -> (0, false)
Add, Sub, Mul, Div are for int. Add64, Add32, etc. are specifically sized.
If anybody wishes an unsigned version, submit a pull request for code
and new tests. */
and new tests.
*/
package overflow

import "math"

//go:generate ./overflow_template.sh

func _is64Bit() bool {
maxU32 := uint(MaxUint32)
maxU32 := uint(math.MaxUint32)
return ((maxU32 << 1) >> 1) == maxU32
}

Expand Down Expand Up @@ -220,7 +223,7 @@ func Div8p(a, b int8) int8 {
func Quo8(a, b int8) (int8, int8, bool) {
if b == 0 {
return 0, 0, false
} else if b == -1 && a == MinInt8 {
} else if b == -1 && a == math.MinInt8 {
return 0, 0, false
}
c := a / b
Expand Down Expand Up @@ -310,7 +313,7 @@ func Div16p(a, b int16) int16 {
func Quo16(a, b int16) (int16, int16, bool) {
if b == 0 {
return 0, 0, false
} else if b == -1 && a == MinInt16 {
} else if b == -1 && a == math.MinInt16 {
return 0, 0, false
}
c := a / b
Expand Down Expand Up @@ -400,7 +403,7 @@ func Div32p(a, b int32) int32 {
func Quo32(a, b int32) (int32, int32, bool) {
if b == 0 {
return 0, 0, false
} else if b == -1 && a == MinInt32 {
} else if b == -1 && a == math.MinInt32 {
return 0, 0, false
}
c := a / b
Expand Down Expand Up @@ -490,7 +493,7 @@ func Div64p(a, b int64) int64 {
func Quo64(a, b int64) (int64, int64, bool) {
if b == 0 {
return 0, 0, false
} else if b == -1 && a == MinInt64 {
} else if b == -1 && a == math.MinInt64 {
return 0, 0, false
}
c := a / b
Expand Down
200 changes: 200 additions & 0 deletions gnovm/stdlibs/math/overflow/overflow_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package overflow

import (
"math"
"testing"
)

// sample all possibilities of 8 bit numbers
// by checking against 64 bit numbers

func TestAlgorithms(t *testing.T) {
errors := 0

for a64 := int64(math.MinInt8); a64 <= int64(math.MaxInt8); a64++ {
for b64 := int64(math.MinInt8); b64 <= int64(math.MaxInt8) && errors < 10; b64++ {

a8 := int8(a64)
b8 := int8(b64)

if int64(a8) != a64 || int64(b8) != b64 {
t.Fatal("LOGIC FAILURE IN TEST")
}

// ADDITION
{
r64 := a64 + b64

// now the verification
result, ok := Add8(a8, b8)
if ok && int64(result) != r64 {
t.Errorf("failed to fail on %v + %v = %v instead of %v\n",
a8, b8, result, r64)
errors++
}
if !ok && int64(result) == r64 {
t.Fail()
errors++
}
}

// SUBTRACTION
{
r64 := a64 - b64

// now the verification
result, ok := Sub8(a8, b8)
if ok && int64(result) != r64 {
t.Errorf("failed to fail on %v - %v = %v instead of %v\n",
a8, b8, result, r64)
}
if !ok && int64(result) == r64 {
t.Fail()
errors++
}
}

// MULTIPLICATION
{
r64 := a64 * b64

// now the verification
result, ok := Mul8(a8, b8)
if ok && int64(result) != r64 {
t.Errorf("failed to fail on %v * %v = %v instead of %v\n",
a8, b8, result, r64)
errors++
}
if !ok && int64(result) == r64 {
t.Fail()
errors++
}
}

// DIVISION
if b8 != 0 {
r64 := a64 / b64
rem64 := a64 % b64

// now the verification
result, rem, ok := Quo8(a8, b8)
if ok && int64(result) != r64 {
t.Errorf("failed to fail on %v / %v = %v instead of %v\n",
a8, b8, result, r64)
errors++
}
if ok && int64(rem) != rem64 {
t.Errorf("failed to fail on %v %% %v = %v instead of %v\n",
a8, b8, rem, rem64)
errors++
}
}
}
}
}

func TestQuotient(t *testing.T) {
q, r, ok := Quo(100, 3)
if r != 1 || q != 33 || !ok {
t.Errorf("expected 100/3 => 33, r=1")
}
if _, _, ok = Quo(1, 0); ok {
t.Error("unexpected lack of failure")
}
}

func TestLong(t *testing.T) {
if testing.Short() {
t.Skip()
}

ctr := int64(0)

for a64 := int64(math.MinInt16); a64 <= int64(math.MaxInt16); a64++ {
for b64 := int64(math.MinInt16); b64 <= int64(math.MaxInt16); b64++ {
a16 := int16(a64)
b16 := int16(b64)
if int64(a16) != a64 || int64(b16) != b64 {
panic("LOGIC FAILURE IN TEST")
}
ctr++

// ADDITION
{
r64 := a64 + b64

// now the verification
result, ok := Add16(a16, b16)
if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) {
if !ok || int64(result) != r64 {
println("add", a16, b16, result, r64)
panic("incorrect result for non-overflow")
}
} else {
if ok {
println("add", a16, b16, result, r64)
panic("incorrect ok result")
}
}
}

// SUBTRACTION
{
r64 := a64 - b64

// now the verification
result, ok := Sub16(a16, b16)
if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) {
if !ok || int64(result) != r64 {
println("sub", a16, b16, result, r64)
panic("incorrect result for non-overflow")
}
} else {
if ok {
println("sub", a16, b16, result, r64)
panic("incorrect ok result")
}
}
}

// MULTIPLICATION
{
r64 := a64 * b64

// now the verification
result, ok := Mul16(a16, b16)
if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) {
if !ok || int64(result) != r64 {
println("mul", a16, b16, result, r64)
panic("incorrect result for non-overflow")
}
} else {
if ok {
println("mul", a16, b16, result, r64)
panic("incorrect ok result")
}
}
}

// DIVISION
if b16 != 0 {
r64 := a64 / b64

// now the verification
result, _, ok := Quo16(a16, b16)
if int64(math.MinInt16) <= r64 && r64 <= int64(math.MaxInt16) {
if !ok || int64(result) != r64 {
println("quo", a16, b16, result, r64)
panic("incorrect result for non-overflow")
}
} else {
if ok {
println("quo", a16, b16, result, r64)
panic("incorrect ok result")
}
}
}
}
}
println("done", ctr)
}
Loading

0 comments on commit 688edf5

Please sign in to comment.