Skip to content

Commit

Permalink
rfq: implement fixed point based arithmetic
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef authored and guggero committed Sep 4, 2024
1 parent ac783f0 commit 583b60e
Show file tree
Hide file tree
Showing 51 changed files with 2,305 additions and 0 deletions.
213 changes: 213 additions & 0 deletions rfq/arithmetic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package rfq

import (
"math/big"

"golang.org/x/exp/constraints"
)

// Arithmetic defines the basic arithmetic operations. The structure of the
// interfaces allows for chaining the arithmetic operations.
type Arithmetic[N any] interface {
// Add returns the sum of the two numbers.
Add(N) N

// Mul returns the product of the two numbers.
Mul(N) N

// Sub returns the difference of the two numbers.
Sub(N) N

// Div returns the division of the two numbers.
Div(N) N
}

// Int is an interface that represents an integer types and the operations we
// care about w.r.t that type.
type Int[N any] interface {
// Arithmetic asserts that the target type of this interface satisfies
// the Arithmetic interface. This lets us get around limitations
// regarding recursive types in Go.
Arithmetic[N]

// Equals returns true if the two integers are equal.
Equals(other N) bool

// ToFloat converts the integer to a float.
ToFloat() float64

// FromFloat converts a float to the integer type.
FromFloat(float64) N

// ToUint64 converts the integer to a uint64.
ToUint64() uint64

// FromUint64 converts a uint64 to the integer type.
FromUint64(uint64) N
}

// NewInt creates a new integer of the target type.
func NewInt[N Int[N]]() N {
var n N
return n
}

// GoInt is a concrete implementation of the Int interface for the set of
// built-in integer types. It ends up mapping the integers to a uint64
// internally for operations.
type GoInt[T constraints.Unsigned] struct {
value T
}

// NewGoInt creates a new GoInt from the given integer.
func NewGoInt[T constraints.Unsigned](value T) GoInt[T] {
return GoInt[T]{
value: value,
}
}

// Add returns the sum of the two integers.
func (b GoInt[T]) Add(other GoInt[T]) GoInt[T] {
return GoInt[T]{
value: b.value + other.value,
}
}

// Mul returns the product of the two integers.
func (b GoInt[T]) Mul(other GoInt[T]) GoInt[T] {
return GoInt[T]{
value: b.value * other.value,
}
}

// Sub returns the difference of the two integers.
func (b GoInt[T]) Sub(other GoInt[T]) GoInt[T] {
return GoInt[T]{
value: b.value - other.value,
}
}

// Div returns the division of the two integers.
func (b GoInt[T]) Div(other GoInt[T]) GoInt[T] {
return GoInt[T]{
value: b.value / other.value,
}
}

// ToFloat converts the integer to a float.
func (b GoInt[T]) ToFloat() float64 {
return float64(b.value)
}

// FromFloat converts a float to the integer type.
func (b GoInt[T]) FromFloat(f float64) GoInt[T] {
b.value = T(f)
return b
}

// ToUint64 converts the integer to a uint64.
func (b GoInt[T]) ToUint64() uint64 {
return uint64(b.value)
}

// FromUint64 converts a uint64 to the integer type.
func (b GoInt[T]) FromUint64(u uint64) GoInt[T] {
b.value = T(u)
return b
}

// Equals returns true if the two integers are equal.
func (b GoInt[T]) Equals(other GoInt[T]) bool {
return b.value == other.value
}

// A compile-time constraint to ensure that the GoInt type implements the Int
// interface.
var _ Int[GoInt[uint]] = GoInt[uint]{}

// BigInt is a concrete implementation of the Int interface using Go's bit
// integer type.
type BigInt struct {
value *big.Int
}

// NewBigInt creates a new BigInt from the given integer.
func NewBigInt(value *big.Int) BigInt {
return BigInt{
value: value,
}
}

// copyInt returns a copy of the internal big.Int. This is used to ensure we
// don't mutate the underlying bit.Int during arithmetic operations.
func (b BigInt) copyInt() *big.Int {
return new(big.Int).Set(b.value)
}

// Add returns the sum of the two integers.
func (b BigInt) Add(other BigInt) BigInt {
return BigInt{
value: b.copyInt().Add(b.value, other.value),
}
}

// Mul returns the product of the two integers.
func (b BigInt) Mul(other BigInt) BigInt {
return BigInt{
value: b.copyInt().Mul(b.value, other.value),
}
}

// Sub returns the difference of the two integers.
func (b BigInt) Sub(other BigInt) BigInt {
return BigInt{
value: b.copyInt().Sub(b.value, other.value),
}
}

// Div returns the division of the two integers.
func (b BigInt) Div(other BigInt) BigInt {
return BigInt{
value: b.copyInt().Div(b.value, other.value),
}
}

// ToFloat converts the integer to a float.
func (b BigInt) ToFloat() float64 {
floatVal, _ := b.value.Float64()
return floatVal
}

// FromFloat converts a float to the integer type.
func (b BigInt) FromFloat(f float64) BigInt {
if b.value == nil {
b.value = new(big.Int)
}

b.value.SetInt64(int64(f))
return b
}

// FromUint64 converts a uint64 to the integer type.
func (b BigInt) FromUint64(u uint64) BigInt {
if b.value == nil {
b.value = new(big.Int)
}

b.value.SetUint64(u)
return b
}

// ToUint64 converts the integer to a uint64.
func (b BigInt) ToUint64() uint64 {
return b.value.Uint64()
}

// Equals returns true if the two integers are equal.
func (b BigInt) Equals(other BigInt) bool {
return b.value.Cmp(other.value) == 0
}

// A compile-time constraint to ensure that the BigInt type implements the Int
// interface.
var _ Int[BigInt] = BigInt{}
Loading

0 comments on commit 583b60e

Please sign in to comment.