feat(statistics): add Variance indicator with O(1) calculation and usage example

This commit is contained in:
Miha Kralj
2025-12-25 17:18:41 -08:00
parent 9ba89812cd
commit 4ff6dc0ad9
61 changed files with 6069 additions and 99 deletions
+4 -4
View File
@@ -28,7 +28,7 @@ public sealed class Bilateral : AbstractBase
private readonly RingBuffer _buffer;
private readonly double[] _spatialWeights;
private record struct State(double SumSq, double LastInput, double LastValidValue);
private record struct State(double SumSq, double LastValidValue);
private State _state;
private State _p_state;
@@ -104,11 +104,12 @@ public sealed class Bilateral : AbstractBase
{
_state.SumSq -= (removed * removed);
}
_state.LastInput = val;
}
double result = CalculateBilateral();
Last = new TValue(DateTime.MinValue, result);
// Use DateTime.UtcNow as Prime(ReadOnlySpan<double>) does not provide timestamps.
// This represents an initial/primed reading rather than a real source timestamp.
Last = new TValue(DateTime.UtcNow, result);
_p_state = _state;
}
@@ -151,7 +152,6 @@ public sealed class Bilateral : AbstractBase
{
_state.SumSq -= (removed * removed);
}
_state.LastInput = val;
}
else
{
+19 -3
View File
@@ -87,7 +87,10 @@ public sealed class Blma : AbstractBase
else
{
// Full period, use pre-calculated weights
result = CalculateWeightedSum(_buffer, _weights) / _weightSum;
// Fallback for cases where weights sum to zero (e.g. N=2)
result = Math.Abs(_weightSum) < double.Epsilon
? _buffer.Average()
: CalculateWeightedSum(_buffer, _weights) / _weightSum;
}
var tValue = new TValue(input.Time, result);
@@ -223,8 +226,21 @@ public sealed class Blma : AbstractBase
else
{
// Full period
double sum = source.Slice(i - period + 1, period).DotProduct(weights);
destination[i] = sum / weightSum;
if (Math.Abs(weightSum) < double.Epsilon)
{
// Fallback for zero sum weights (e.g. N=2)
double sum = 0;
for (int j = 0; j < period; j++)
{
sum += source[i - period + 1 + j];
}
destination[i] = sum / period;
}
else
{
double sum = source.Slice(i - period + 1, period).DotProduct(weights);
destination[i] = sum / weightSum;
}
}
}
}
+10 -5
View File
@@ -86,11 +86,6 @@ public sealed class Butter : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override TValue Update(TValue input, bool isNew = true)
{
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
return Last;
}
if (isNew)
{
_p_state = _state;
@@ -100,6 +95,11 @@ public sealed class Butter : AbstractBase
_state = _p_state;
}
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
return Last;
}
double x = input.Value;
double y = _state.Count < 2
? x
@@ -181,6 +181,11 @@ public sealed class Butter : AbstractBase
for (int i = 0; i < source.Length; i++)
{
double x = source[i];
if (double.IsNaN(x) || double.IsInfinity(x))
{
destination[i] = i > 0 ? destination[i - 1] : 0;
continue;
}
double y = i < 2
? x
: (b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * invA0;
+9 -3
View File
@@ -240,7 +240,11 @@ public sealed class Ema : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double Compute(double input, double alpha, double decay, ref State state)
{
state.Ema += alpha * (input - state.Ema);
// state.Ema += alpha * (input - state.Ema)
// state.Ema = state.Ema + alpha * input - alpha * state.Ema
// state.Ema = state.Ema * (1 - alpha) + alpha * input
// state.Ema = state.Ema * decay + alpha * input
state.Ema = Math.FusedMultiplyAdd(state.Ema, decay, alpha * input);
double result;
if (!state.IsCompensated)
@@ -285,7 +289,8 @@ public sealed class Ema : AbstractBase
else
val = lastValidValue;
state.Ema += alpha * (val - state.Ema);
state.Ema = Math.FusedMultiplyAdd(state.Ema, decay, alpha * val);
state.E *= decay;
if (!state.IsHot && state.E <= COVERAGE_THRESHOLD)
@@ -305,7 +310,8 @@ public sealed class Ema : AbstractBase
else
val = lastValidValue;
state.Ema += alpha * (val - state.Ema);
// state.Ema += alpha * (val - state.Ema); // skipcq: S125
state.Ema = Math.FusedMultiplyAdd(state.Ema, decay, alpha * val);
output[i] = state.Ema;
}
}
+8 -4
View File
@@ -164,7 +164,8 @@ public sealed class Kama : AbstractBase
// Cap ER at 1.0 just in case floating point errors push it slightly over
if (er > 1.0) er = 1.0;
double sc = er * (_fastAlpha - _slowAlpha) + _slowAlpha;
// double sc = er * (_fastAlpha - _slowAlpha) + _slowAlpha; // skipcq: S125
double sc = Math.FusedMultiplyAdd(er, _fastAlpha - _slowAlpha, _slowAlpha);
sc *= sc;
double prevKama = _p_state.Kama;
@@ -173,7 +174,8 @@ public sealed class Kama : AbstractBase
prevKama = _state.Kama;
}
_state.Kama = prevKama + sc * (val - prevKama);
// _state.Kama = prevKama + sc * (val - prevKama); // skipcq: S125
_state.Kama = Math.FusedMultiplyAdd(sc, val - prevKama, prevKama);
}
Last = new TValue(input.Time, _state.Kama);
@@ -314,10 +316,12 @@ public sealed class Kama : AbstractBase
double er = (volatilitySum > 1e-10) ? change / volatilitySum : 0.0;
if (er > 1.0) er = 1.0;
double sc = er * (fastAlpha - slowAlpha) + slowAlpha;
// double sc = er * (fastAlpha - slowAlpha) + slowAlpha; // skipcq: S125
double sc = Math.FusedMultiplyAdd(er, fastAlpha - slowAlpha, slowAlpha);
sc *= sc;
kama += sc * (val - kama);
// kama += sc * (val - kama); // skipcq: S125
kama = Math.FusedMultiplyAdd(sc, val - kama, kama);
output[i] = kama;
}
}
@@ -1,6 +1,6 @@
---
description: Configuration for AI behavior when interacting with Codacy's MCP Server
applyTo: '**'
description: Configuration for AI behavior when interacting with Codacy's MCP Server
applyTo: '**'
---
---
# Codacy Rules
+14 -14
View File
@@ -97,7 +97,7 @@ public sealed class Lsma : AbstractBase
// O(1) update for sum_xy
// sum_xy_new = sum_xy_old + sum_y_prev - n * oldest
_state.SumXY = _state.SumXY + prev_sum_y - _period * oldest;
_state.SumXY = Math.FusedMultiplyAdd(-_period, oldest, _state.SumXY + prev_sum_y);
// O(1) update for sum_y
_state.SumY = _state.SumY - oldest + val;
@@ -119,7 +119,7 @@ public sealed class Lsma : AbstractBase
// index j in buffer corresponds to x = count - 1 - j
// sum_xy = sum(x * y)
int x = span.Length - 1 - i;
_state.SumXY += x * span[i];
_state.SumXY = Math.FusedMultiplyAdd(x, span[i], _state.SumXY);
}
}
@@ -139,7 +139,7 @@ public sealed class Lsma : AbstractBase
for (int i = 0; i < span.Length; i++)
{
int x = span.Length - 1 - i;
_state.SumXY += x * span[i];
_state.SumXY = Math.FusedMultiplyAdd(x, span[i], _state.SumXY);
}
}
@@ -197,11 +197,11 @@ public sealed class Lsma : AbstractBase
}
else
{
double m = (n * _state.SumXY - sx * _state.SumY) / denom;
double b = (_state.SumY - m * sx) / n;
double m = Math.FusedMultiplyAdd(n, _state.SumXY, -sx * _state.SumY) / denom;
double b = Math.FusedMultiplyAdd(-m, sx, _state.SumY) / n;
// LSMA = b - m * offset
result = b - m * _offset;
result = Math.FusedMultiplyAdd(-m, _offset, b);
}
}
@@ -335,7 +335,7 @@ public sealed class Lsma : AbstractBase
{
// buffer[j] is at index j
// x = count - 1 - j
sum_xy += (count - 1 - j) * buffer[j];
sum_xy = Math.FusedMultiplyAdd(count - 1 - j, buffer[j], sum_xy);
}
if (count <= 1)
@@ -355,9 +355,9 @@ public sealed class Lsma : AbstractBase
}
else
{
double m = (n * sum_xy - sx * sum_y) / denom;
double b = (sum_y - m * sx) / n;
output[i] = b - m * offset;
double m = Math.FusedMultiplyAdd(n, sum_xy, -sx * sum_y) / denom;
double b = Math.FusedMultiplyAdd(-m, sx, sum_y) / n;
output[i] = Math.FusedMultiplyAdd(-m, offset, b);
}
}
@@ -373,7 +373,7 @@ public sealed class Lsma : AbstractBase
double prev_sum_y = sum_y;
// sum_xy_new = sum_xy_old + sum_y_prev - n * oldest
sum_xy = sum_xy + prev_sum_y - period * oldest;
sum_xy = Math.FusedMultiplyAdd(-period, oldest, sum_xy + prev_sum_y);
sum_y = sum_y - oldest + val;
buffer[bufferIndex] = val;
@@ -382,9 +382,9 @@ public sealed class Lsma : AbstractBase
if (bufferIndex >= period)
bufferIndex = 0;
double m = (period * sum_xy - full_sum_x * sum_y) / full_denom;
double b = (sum_y - m * full_sum_x) / period;
output[i] = b - m * offset;
double m = Math.FusedMultiplyAdd(period, sum_xy, -full_sum_x * sum_y) / full_denom;
double b = Math.FusedMultiplyAdd(-m, full_sum_x, sum_y) / period;
output[i] = Math.FusedMultiplyAdd(-m, offset, b);
}
}
}
+14 -14
View File
@@ -80,15 +80,15 @@ public sealed class Pwma : AbstractBase
double oldest = _buffer.Oldest;
_state.Sum = _state.Sum - oldest + val;
_state.WSum = _state.WSum - oldSum + (_period * val);
_state.PSum = _state.PSum - 2 * oldWSum + oldSum + ((double)_period * _period * val);
_state.WSum = Math.FusedMultiplyAdd(_period, val, _state.WSum - oldSum);
_state.PSum = Math.FusedMultiplyAdd((double)_period * _period, val, _state.PSum - 2 * oldWSum + oldSum);
}
else
{
int count = _buffer.Count + 1;
_state.Sum += val;
_state.WSum += count * val;
_state.PSum += (double)count * count * val;
_state.WSum = Math.FusedMultiplyAdd(count, val, _state.WSum);
_state.PSum = Math.FusedMultiplyAdd((double)count * count, val, _state.PSum);
}
_buffer.Add(val);
@@ -104,8 +104,8 @@ public sealed class Pwma : AbstractBase
foreach (double item in _buffer)
{
recalcSum += item;
recalcWsum += i * item;
recalcPsum += (double)i * i * item;
recalcWsum = Math.FusedMultiplyAdd(i, item, recalcWsum);
recalcPsum = Math.FusedMultiplyAdd((double)i * i, item, recalcPsum);
i++;
}
_state.Sum = recalcSum;
@@ -143,8 +143,8 @@ public sealed class Pwma : AbstractBase
double diff = val - _state.LastInput;
_state.Sum += diff;
_state.WSum += n * diff;
_state.PSum += (double)n * n * diff;
_state.WSum = Math.FusedMultiplyAdd(n, diff, _state.WSum);
_state.PSum = Math.FusedMultiplyAdd((double)n * n, diff, _state.PSum);
_buffer.UpdateNewest(val);
}
@@ -264,8 +264,8 @@ public sealed class Pwma : AbstractBase
val = lastValid;
sum += val;
wsum += (i + 1) * val;
psum += (double)(i + 1) * (i + 1) * val;
wsum = Math.FusedMultiplyAdd(i + 1, val, wsum);
psum = Math.FusedMultiplyAdd((double)(i + 1) * (i + 1), val, psum);
buffer[i] = val;
double currentDivisor = (double)(i + 1) * (i + 2) * (2 * (i + 1) + 1) / 6.0;
@@ -286,8 +286,8 @@ public sealed class Pwma : AbstractBase
double oldest = buffer[bufferIdx];
sum = sum - oldest + val;
wsum = wsum - oldSum + (period * val);
psum = psum - 2 * oldWSum + oldSum + ((double)period * period * val);
wsum = Math.FusedMultiplyAdd(period, val, wsum - oldSum);
psum = Math.FusedMultiplyAdd((double)period * period, val, psum - 2 * oldWSum + oldSum);
buffer[bufferIdx] = val;
bufferIdx++;
@@ -309,8 +309,8 @@ public sealed class Pwma : AbstractBase
double v = buffer[idx];
recalcSum += v;
recalcWsum += (k + 1) * v;
recalcPsum += (double)(k + 1) * (k + 1) * v;
recalcWsum = Math.FusedMultiplyAdd(k + 1, v, recalcWsum);
recalcPsum = Math.FusedMultiplyAdd((double)(k + 1) * (k + 1), v, recalcPsum);
}
sum = recalcSum;
wsum = recalcWsum;