summaryrefslogtreecommitdiff
path: root/ms
diff options
context:
space:
mode:
authorWill Hawkins <[email protected]>2023-05-23 17:58:14 -0400
committerWill Hawkins <[email protected]>2023-06-21 09:12:22 -0400
commitec2ccf69d8b08abb03fa3bdb3e7e95ae1862d619 (patch)
tree6b636bdbda82db40da89a2bde213c684542850dc /ms
parent5558f0347baaf6db066314f0eaf82d7fb552b2f7 (diff)
Major Update/Refactor to Support IETF 02
Beginning of a release candidate for support for IETF 02 tag of the responsiveness spec.
Diffstat (limited to 'ms')
-rw-r--r--ms/ms.go29
-rw-r--r--ms/ms_test.go93
2 files changed, 86 insertions, 36 deletions
diff --git a/ms/ms.go b/ms/ms.go
index a13762a..06c7340 100644
--- a/ms/ms.go
+++ b/ms/ms.go
@@ -33,7 +33,7 @@ type MathematicalSeries[T constraints.Float | constraints.Integer] interface {
Len() int
Values() []T
Percentile(int) T
- DoubleSidedTrim(uint32) MathematicalSeries[T]
+ DoubleSidedTrim(uint) MathematicalSeries[T]
Less(int, int) bool
Swap(int, int)
}
@@ -77,7 +77,7 @@ func (ims *InfiniteMathematicalSeries[T]) Less(i, j int) bool {
return ims.elements[i] < ims.elements[j]
}
-func (ims *InfiniteMathematicalSeries[T]) DoubleSidedTrim(percent uint32) MathematicalSeries[T] {
+func (ims *InfiniteMathematicalSeries[T]) DoubleSidedTrim(percent uint) MathematicalSeries[T] {
if percent >= 100 {
panic(
fmt.Sprintf("Cannot perform double-sided trim for an invalid percentage: %d", percent),
@@ -137,7 +137,6 @@ func (ims *InfiniteMathematicalSeries[T]) AllSequentialIncreasesLessThan(
* N.B.: Overflow is possible -- use at your discretion!
*/
func (ims *InfiniteMathematicalSeries[T]) StandardDeviation() (bool, T) {
-
// From https://www.mathsisfun.com/data/standard-deviation-calculator.html
// Yes, for real!
@@ -165,7 +164,7 @@ func (ims *InfiniteMathematicalSeries[T]) StandardDeviation() (bool, T) {
// Finally, the standard deviation is the square root
// of the variance.
sd := T(math.Sqrt(variance))
- //sd := T(variance)
+ // sd := T(variance)
return true, sd
}
@@ -187,14 +186,14 @@ func (ims *InfiniteMathematicalSeries[T]) Percentile(p int) T {
}
type CappedMathematicalSeries[T constraints.Float | constraints.Integer] struct {
- elements_count uint64
+ elements_count uint
elements []T
- index uint64
- divisor *saturating.Saturating[uint64]
+ index uint
+ divisor *saturating.Saturating[uint]
}
func NewCappedMathematicalSeries[T constraints.Float | constraints.Integer](
- instants_count uint64,
+ instants_count uint,
) MathematicalSeries[T] {
return &CappedMathematicalSeries[T]{
elements: make([]T, instants_count),
@@ -221,7 +220,6 @@ func (ma *CappedMathematicalSeries[T]) CalculateAverage() float64 {
func (ma *CappedMathematicalSeries[T]) AllSequentialIncreasesLessThan(
limit float64,
) (_ bool, maximumSequentialIncrease float64) {
-
// If we have not yet accumulated a complete set of intervals,
// this is false.
if ma.divisor.Value() != ma.elements_count {
@@ -233,7 +231,7 @@ func (ma *CappedMathematicalSeries[T]) AllSequentialIncreasesLessThan(
oldestIndex := ma.index
previous := ma.elements[oldestIndex]
maximumSequentialIncrease = 0
- for i := uint64(1); i < ma.elements_count; i++ {
+ for i := uint(1); i < ma.elements_count; i++ {
currentIndex := (oldestIndex + i) % ma.elements_count
current := ma.elements[currentIndex]
percentChange := utilities.SignedPercentDifference(current, previous)
@@ -249,7 +247,6 @@ func (ma *CappedMathematicalSeries[T]) AllSequentialIncreasesLessThan(
* N.B.: Overflow is possible -- use at your discretion!
*/
func (ma *CappedMathematicalSeries[T]) StandardDeviation() (bool, T) {
-
// If we have not yet accumulated a complete set of intervals,
// we are always false.
if ma.divisor.Value() != ma.elements_count {
@@ -283,7 +280,7 @@ func (ma *CappedMathematicalSeries[T]) StandardDeviation() (bool, T) {
// Finally, the standard deviation is the square root
// of the variance.
sd := T(math.Sqrt(variance))
- //sd := T(variance)
+ // sd := T(variance)
return true, sd
}
@@ -312,7 +309,7 @@ func (ma *CappedMathematicalSeries[T]) Values() []T {
}
func (ma *CappedMathematicalSeries[T]) Len() int {
- if uint64(len(ma.elements)) != ma.elements_count {
+ if uint(len(ma.elements)) != ma.elements_count {
panic(
fmt.Sprintf(
"Error: A capped mathematical series' metadata is invalid: the length of its element array/slice does not match element_count! (%v vs %v)",
@@ -346,19 +343,19 @@ func (ims *CappedMathematicalSeries[T]) Less(i, j int) bool {
return ims.elements[i] < ims.elements[j]
}
-func (ims *CappedMathematicalSeries[T]) DoubleSidedTrim(percent uint32) MathematicalSeries[T] {
+func (ims *CappedMathematicalSeries[T]) DoubleSidedTrim(percent uint) MathematicalSeries[T] {
if percent >= 100 {
panic(
fmt.Sprintf("Cannot perform double-sided trim for an invalid percentage: %d", percent),
)
}
- trimmed := &CappedMathematicalSeries[T]{elements_count: uint64(ims.Len())}
+ trimmed := &CappedMathematicalSeries[T]{elements_count: uint(ims.Len())}
trimmed.elements = make([]T, ims.Len())
copy(trimmed.elements, ims.elements)
sort.Sort(trimmed)
- elementsToTrim := uint64(float32(ims.Len()) * ((float32(percent)) / float32(100.0)))
+ elementsToTrim := uint(float32(ims.Len()) * ((float32(percent)) / float32(100.0)))
trimmed.elements = trimmed.elements[elementsToTrim : len(trimmed.elements)-int(elementsToTrim)]
trimmed.elements_count -= (elementsToTrim * 2)
diff --git a/ms/ms_test.go b/ms/ms_test.go
index 533cc7e..34817d0 100644
--- a/ms/ms_test.go
+++ b/ms/ms_test.go
@@ -11,7 +11,7 @@ func Test_InfiniteValues(test *testing.T) {
series := NewInfiniteMathematicalSeries[float64]()
shouldMatch := make([]float64, 0)
previous := float64(1.0)
- for _ = range utilities.Iota(1, 80) {
+ for range utilities.Iota(1, 80) {
previous *= 1.059
series.AddElement(float64(previous))
shouldMatch = append(shouldMatch, previous)
@@ -21,10 +21,11 @@ func Test_InfiniteValues(test *testing.T) {
test.Fatalf("Values() on infinite mathematical series does not work.")
}
}
+
func Test_InfiniteSequentialIncreasesAlwaysLessThan(test *testing.T) {
series := NewInfiniteMathematicalSeries[float64]()
previous := float64(1.0)
- for _ = range utilities.Iota(1, 80) {
+ for range utilities.Iota(1, 80) {
previous *= 1.059
series.AddElement(float64(previous))
}
@@ -35,6 +36,7 @@ func Test_InfiniteSequentialIncreasesAlwaysLessThan(test *testing.T) {
)
}
}
+
func Test_CappedTooFewInstantsSequentialIncreasesLessThanAlwaysFalse(test *testing.T) {
series := NewCappedMathematicalSeries[float64](500)
series.AddElement(0.0)
@@ -51,14 +53,17 @@ func Test_Infinite_degenerate_percentile_too_high(test *testing.T) {
test.Fatalf("(infinite) Series percentile of 101 failed.")
}
}
+
func Test_Infinite_degenerate_percentile_too_low(test *testing.T) {
series := NewInfiniteMathematicalSeries[int]()
if series.Percentile(-1) != 0 {
test.Fatalf("(infinite) Series percentile of -1 failed.")
}
}
+
func Test_Infinite90_percentile(test *testing.T) {
- series := NewInfiniteMathematicalSeries[int]()
+ var expected int64 = 10
+ series := NewInfiniteMathematicalSeries[int64]()
series.AddElement(10)
series.AddElement(9)
series.AddElement(8)
@@ -70,15 +75,16 @@ func Test_Infinite90_percentile(test *testing.T) {
series.AddElement(2)
series.AddElement(1)
- if series.Percentile(90) != 10 {
+ if series.Percentile(90) != expected {
test.Fatalf(
- "(infinite) Series 90th percentile of 0 ... 10 failed: Expected 10 got %v.",
+ "(infinite) Series 90th percentile of 0 ... 10 failed: Expected: %v; Actual: %v.", expected,
series.Percentile(90),
)
}
}
func Test_Infinite90_percentile_reversed(test *testing.T) {
+ var expected int64 = 10
series := NewInfiniteMathematicalSeries[int64]()
series.AddElement(1)
series.AddElement(2)
@@ -91,15 +97,16 @@ func Test_Infinite90_percentile_reversed(test *testing.T) {
series.AddElement(9)
series.AddElement(10)
- if series.Percentile(90) != 10 {
+ if series.Percentile(90) != expected {
test.Fatalf(
- "(infinite) Series 90th percentile of 0 ... 10 failed: Expected 10 got %v.",
+ "(infinite) Series 90th percentile of 0 ... 10 failed: Expected %v; Actual: %v.", expected,
series.Percentile(90),
)
}
}
func Test_Infinite50_percentile_jumbled(test *testing.T) {
+ var expected int64 = 15
series := NewInfiniteMathematicalSeries[int64]()
series.AddElement(7)
series.AddElement(2)
@@ -112,15 +119,16 @@ func Test_Infinite50_percentile_jumbled(test *testing.T) {
series.AddElement(11)
series.AddElement(12)
- if series.Percentile(50) != 15 {
+ if series.Percentile(50) != expected {
test.Fatalf(
- "(infinite) Series 50 percentile of a jumble of numbers failed: Expected 15 got %v.",
+ "(infinite) Series 50 percentile of a jumble of numbers failed: Expected %v; Actual: %v.", expected,
series.Percentile(50),
)
}
}
func Test_InfiniteDoubleSidedTrimmedMean_jumbled(test *testing.T) {
+ expected := 16
series := NewInfiniteMathematicalSeries[int64]()
series.AddElement(7)
series.AddElement(2)
@@ -145,10 +153,10 @@ func Test_InfiniteDoubleSidedTrimmedMean_jumbled(test *testing.T) {
trimmed := series.DoubleSidedTrim(10)
- if trimmed.Len() != 16 {
+ if trimmed.Len() != expected {
test.Fatalf(
"Capped series is not of the proper size. Expected %v and got %v",
- 16,
+ expected,
trimmed.Len(),
)
}
@@ -165,7 +173,7 @@ func Test_InfiniteDoubleSidedTrimmedMean_jumbled(test *testing.T) {
func Test_CappedSequentialIncreasesAlwaysLessThan(test *testing.T) {
series := NewCappedMathematicalSeries[float64](40)
previous := float64(1.0)
- for _ = range utilities.Iota(1, 80) {
+ for range utilities.Iota(1, 80) {
previous *= 1.059
series.AddElement(float64(previous))
}
@@ -221,16 +229,41 @@ func Test_CappedSequentialIncreasesAlwaysLessThanWithWraparoundInverse(test *tes
}
func Test_CappedStandardDeviationCalculation(test *testing.T) {
+ expected := 2.93
series := NewCappedMathematicalSeries[float64](5)
// 5.7, 1.0, 8.6, 7.4, 2.2
series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
+ series.AddElement(5.7)
series.AddElement(1.0)
series.AddElement(8.6)
series.AddElement(7.4)
series.AddElement(2.2)
- if _, sd := series.StandardDeviation(); !utilities.ApproximatelyEqual(2.93, sd, 0.01) {
- test.Fatalf("Standard deviation max calculation failed: %v.", sd)
+ if _, sd := series.StandardDeviation(); !utilities.ApproximatelyEqual(sd, expected, 0.01) {
+ test.Fatalf("Standard deviation max calculation failed: Expected: %v; Actual: %v.", expected, sd)
+ } else {
+ test.Logf("Standard deviation calculation result: %v", sd)
+ }
+}
+
+func Test_CappedStandardDeviationCalculation2(test *testing.T) {
+ expected := 1.41
+ series := NewCappedMathematicalSeries[float64](5)
+ series.AddElement(8)
+ series.AddElement(9)
+ series.AddElement(10)
+ series.AddElement(11)
+ series.AddElement(12)
+
+ if _, sd := series.StandardDeviation(); !utilities.ApproximatelyEqual(sd, expected, 0.01) {
+ test.Fatalf("Standard deviation max calculation failed: Expected: %v; Actual: %v.", expected, sd)
} else {
test.Logf("Standard deviation calculation result: %v", sd)
}
@@ -252,6 +285,7 @@ func Test_CappedRotatingValues(test *testing.T) {
test.Fatalf("Adding values does not properly erase earlier values.")
}
}
+
func Test_CappedLen(test *testing.T) {
series := NewCappedMathematicalSeries[int](5)
@@ -275,13 +309,16 @@ func Test_Capped_degenerate_percentile_too_high(test *testing.T) {
test.Fatalf("Series percentile of 101 failed.")
}
}
+
func Test_Capped_degenerate_percentile_too_low(test *testing.T) {
series := NewCappedMathematicalSeries[int](21)
if series.Percentile(-1) != 0 {
test.Fatalf("Series percentile of -1 failed.")
}
}
+
func Test_Capped90_percentile(test *testing.T) {
+ var expected int = 10
series := NewCappedMathematicalSeries[int](10)
series.AddElement(10)
series.AddElement(9)
@@ -294,9 +331,9 @@ func Test_Capped90_percentile(test *testing.T) {
series.AddElement(2)
series.AddElement(1)
- if series.Percentile(90) != 10 {
+ if series.Percentile(90) != expected {
test.Fatalf(
- "Series 90th percentile of 0 ... 10 failed: Expected 10 got %v.",
+ "Series 90th percentile of 0 ... 10 failed: Expected %v got %v.", expected,
series.Percentile(90),
)
}
@@ -324,6 +361,7 @@ func Test_Capped90_percentile_reversed(test *testing.T) {
}
func Test_Capped50_percentile_jumbled(test *testing.T) {
+ var expected int64 = 15
series := NewCappedMathematicalSeries[int64](10)
series.AddElement(7)
series.AddElement(2)
@@ -336,15 +374,16 @@ func Test_Capped50_percentile_jumbled(test *testing.T) {
series.AddElement(11)
series.AddElement(12)
- if series.Percentile(50) != 15 {
+ if series.Percentile(50) != expected {
test.Fatalf(
- "Series 50 percentile of a jumble of numbers failed: Expected 15 got %v.",
+ "Series 50 percentile of a jumble of numbers failed: Expected %v got %v.", expected,
series.Percentile(50),
)
}
}
func Test_CappedDoubleSidedTrimmedMean_jumbled(test *testing.T) {
+ expected := 8
series := NewCappedMathematicalSeries[int64](10)
series.AddElement(7)
series.AddElement(2)
@@ -360,10 +399,10 @@ func Test_CappedDoubleSidedTrimmedMean_jumbled(test *testing.T) {
trimmed := series.DoubleSidedTrim(10)
- if trimmed.Len() != 8 {
+ if trimmed.Len() != expected {
test.Fatalf(
"Capped series is not of the proper size. Expected %v and got %v",
- 8,
+ expected,
trimmed.Len(),
)
}
@@ -376,3 +415,17 @@ func Test_CappedDoubleSidedTrimmedMean_jumbled(test *testing.T) {
prev = v
}
}
+
+func Test_CappedAverage(test *testing.T) {
+ expected := 1.0082230220488836e+08
+ series := NewCappedMathematicalSeries[float64](4)
+ series.AddElement(9.94747772516195e+07)
+ series.AddElement(9.991286984703423e+07)
+ series.AddElement(1.0285437111086299e+08)
+ series.AddElement(1.0104719061003672e+08)
+ if average := series.CalculateAverage(); !utilities.ApproximatelyEqual(average, 0.01, expected) {
+ test.Fatalf(
+ "Expected: %v; Actual: %v.", average, expected,
+ )
+ }
+}