Skip to content

Commit 656245d

Browse files
author
ffranr
authored
Merge pull request #1135 from lightninglabs/fixed-point-within-tolerance
Add fixed-point `WithinTolerance` method and greater-than operators
2 parents 4c47b07 + b078457 commit 656245d

File tree

4 files changed

+450
-0
lines changed

4 files changed

+450
-0
lines changed

rfq/negotiator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,8 @@ func expiryWithinBounds(expiryUnixTimestamp uint64,
518518

519519
// priceWithinBounds returns true if the difference between the first price and
520520
// the second price is within the given tolerance (in parts per million (PPM)).
521+
//
522+
// TODO(ffranr): Replace with FixedPoint[T].WithinTolerance.
521523
func pricesWithinBounds(firstPrice lnwire.MilliSatoshi,
522524
secondPrice lnwire.MilliSatoshi, tolerancePpm uint64) bool {
523525

rfqmath/arithmetic.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ type Int[N any] interface {
3333
// Equals returns true if the two integers are equal.
3434
Equals(other N) bool
3535

36+
// Gt returns true if the integer is greater than the other integer.
37+
Gt(other N) bool
38+
39+
// Gte returns true if the integer is greater than or equal to the other
40+
// integer.
41+
Gte(other N) bool
42+
3643
// ToFloat converts the integer to a float.
3744
ToFloat() float64
3845

@@ -121,6 +128,17 @@ func (b GoInt[T]) Equals(other GoInt[T]) bool {
121128
return b.value == other.value
122129
}
123130

131+
// Gt returns true if the integer is greater than the other integer.
132+
func (b GoInt[T]) Gt(other GoInt[T]) bool {
133+
return b.value > other.value
134+
}
135+
136+
// Gte returns true if the integer is greater than or equal to the other
137+
// integer.
138+
func (b GoInt[T]) Gte(other GoInt[T]) bool {
139+
return b.value >= other.value
140+
}
141+
124142
// A compile-time constraint to ensure that the GoInt type implements the Int
125143
// interface.
126144
var _ Int[GoInt[uint]] = GoInt[uint]{}
@@ -208,6 +226,17 @@ func (b BigInt) Equals(other BigInt) bool {
208226
return b.value.Cmp(other.value) == 0
209227
}
210228

229+
// Gt returns true if the integer is greater than the other integer.
230+
func (b BigInt) Gt(other BigInt) bool {
231+
return b.value.Cmp(other.value) == 1
232+
}
233+
234+
// Gte returns true if the integer is greater than or equal to the other
235+
// integer.
236+
func (b BigInt) Gte(other BigInt) bool {
237+
return b.Equals(other) || b.Gt(other)
238+
}
239+
211240
// A compile-time constraint to ensure that the BigInt type implements the Int
212241
// interface.
213242
var _ Int[BigInt] = BigInt{}

rfqmath/fixed_point.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,53 @@ func (f FixedPoint[T]) Equals(other FixedPoint[T]) bool {
109109
return f.Coefficient.Equals(other.Coefficient) && f.Scale == other.Scale
110110
}
111111

112+
// WithinTolerance returns true if the two FixedPoint values are within the
113+
// given tolerance (in parts per million (PPM)).
114+
func (f FixedPoint[T]) WithinTolerance(
115+
other FixedPoint[T], tolerancePpm T) bool {
116+
117+
// Determine the larger scale between the two fixed-point numbers.
118+
// Both values will be scaled to this larger scale to ensure a
119+
// consistent comparison.
120+
var largerScale uint8
121+
if f.Scale > other.Scale {
122+
largerScale = f.Scale
123+
} else {
124+
largerScale = other.Scale
125+
}
126+
127+
subjectFp := f.ScaleTo(largerScale)
128+
otherFp := other.ScaleTo(largerScale)
129+
130+
var (
131+
// delta will be the absolute difference between the two
132+
// coefficients.
133+
delta T
134+
135+
// maxCoefficient is the larger of the two coefficients.
136+
maxCoefficient T
137+
)
138+
if subjectFp.Coefficient.Gt(otherFp.Coefficient) {
139+
delta = subjectFp.Coefficient.Sub(otherFp.Coefficient)
140+
maxCoefficient = subjectFp.Coefficient
141+
} else {
142+
delta = otherFp.Coefficient.Sub(subjectFp.Coefficient)
143+
maxCoefficient = otherFp.Coefficient
144+
}
145+
146+
// Calculate the tolerance in absolute terms based on the largest
147+
// coefficient.
148+
//
149+
// tolerancePpm is parts per million, therefore we multiply the delta by
150+
// 1,000,000 instead of dividing the tolerance.
151+
scaledDelta := delta.Mul(NewInt[T]().FromUint64(1_000_000))
152+
153+
// Compare the scaled delta to the product of the maximum coefficient
154+
// and the tolerance.
155+
toleranceCoefficient := maxCoefficient.Mul(tolerancePpm)
156+
return toleranceCoefficient.Gte(scaledDelta)
157+
}
158+
112159
// FixedPointFromUint64 creates a new FixedPoint from the given integer and
113160
// scale. Note that the input here should be *unscaled*.
114161
func FixedPointFromUint64[N Int[N]](value uint64, scale uint8) FixedPoint[N] {

0 commit comments

Comments
 (0)