Skip to content

Commit

Permalink
Move SliceMedianValue to math
Browse files Browse the repository at this point in the history
  • Loading branch information
swift1337 committed Jul 22, 2024
1 parent ae124bb commit aab53c8
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 26 deletions.
53 changes: 53 additions & 0 deletions pkg/math/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package math

import "slices"

type number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

// SliceMedianValue returns the median value of the given slice.
// Returns 0 for an empty slice. If inPlace is true, the input slice will be sorted in place.
// Otherwise, a copy of the input slice will be sorted.
func SliceMedianValue[T number](items []T, inPlace bool) T {
if inPlace {
return sliceMedianValue(items)
}

copied := make([]T, len(items))
copy(copied, items)

out := sliceMedianValue(copied)

// We don't need the copy anymore
//nolint:ineffassign // let's help the garbage collector :)
copied = nil

return out
}

func sliceMedianValue[T number](items []T) T {
switch len(items) {
case 0:
return 0
case 1:
return items[0]
}

slices.Sort(items)

// note that int division is used here e.g. 5/2 => 2

// []int{1 2 3 4 5} => items[(5/2)] => items[2] => 3
if len(items)%2 == 1 {
return items[len(items)/2]
}

// odd number of items
rightIndex := len(items) / 2
leftIndex := rightIndex - 1

// []int{1 2 3 4} => (items[1] + items[2]) / 2 => 5/2 => 2
return (items[leftIndex] + items[rightIndex]) / 2
}
75 changes: 75 additions & 0 deletions pkg/math/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package math

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSliceMedianValue(t *testing.T) {
for _, tt := range []struct {
name string
input []int
expected int
inPlace bool
}{
{
name: "empty",
input: nil,
expected: 0,
inPlace: false,
},
{
name: "single",
input: []int{10},
expected: 10,
},
{
name: "two",
input: []int{10, 20},
expected: 15,
},
{
name: "even",
input: []int{30, 20, 10, 20},
expected: 20,
},
{
name: "even in-place",
input: []int{30, 20, 10, 20},
expected: 20,
inPlace: true,
},
{
name: "odd",
input: []int{5, 5, 6, 1, 1, 1, 4},
expected: 4,
},
{
name: "odd in-place",
input: []int{1, 1, 1, 1, 7, 7, 7, 7},
expected: 4,
},
} {
t.Run(tt.name, func(t *testing.T) {
// ASSERT
// Given a copy of the input slice
var snapshot []int
for _, v := range tt.input {
snapshot = append(snapshot, v)
}

// ACT
out := SliceMedianValue(tt.input, tt.inPlace)

// ASSERT
assert.Equal(t, tt.expected, out)

// Check that elements of the input slice are unchanged
if !tt.inPlace {
assert.Equal(t, snapshot, tt.input)
}
})
}

}
28 changes: 2 additions & 26 deletions x/crosschain/keeper/gas_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"golang.org/x/exp/slices"

slicemath "github.com/zeta-chain/zetacore/pkg/math"
"github.com/zeta-chain/zetacore/x/crosschain/types"
)

Expand Down Expand Up @@ -44,36 +44,12 @@ func (k Keeper) GetMedianGasPriceInUint(ctx sdk.Context, chainID int64) (math.Ui

var (
gasPrice = math.NewUint(entity.Prices[entity.MedianIndex])
priorityFee = math.NewUint(medianValue(entity.PriorityFees))
priorityFee = math.NewUint(slicemath.SliceMedianValue(entity.PriorityFees, false))
)

return gasPrice, priorityFee, true
}

// medianValue returns the median value of a slice
// example: [ 1 7 5 2 3 6 4 ] => [ 1 2 3 4 5 6 7 ] => 4
func medianValue(items []uint64) uint64 {
switch len(items) {
case 0:
return 0
case 1:
return items[0]
}

// We don't want to modify the original slice
copiedItems := make([]uint64, len(items))
copy(copiedItems, items)

slices.Sort(copiedItems)
mv := copiedItems[len(copiedItems)/2]

// We don't need the copy anymore
//nolint:ineffassign // let's help garbage collector :)
copiedItems = nil

return mv
}

// RemoveGasPrice removes a gasPrice from the store
func (k Keeper) RemoveGasPrice(ctx sdk.Context, index string) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.GasPriceKey))
Expand Down

0 comments on commit aab53c8

Please sign in to comment.