|
| 1 | +using System.Numerics; |
1 | 2 | using ManagedCode.TimeSeries.Abstractions; |
2 | 3 | using ManagedCode.TimeSeries.Accumulators; |
3 | 4 | using ManagedCode.TimeSeries.Summers; |
@@ -303,6 +304,26 @@ public void NumberTimeSeriesSummerResamplePreservesTotals() |
303 | 304 | summer.Sum().ShouldBe(12); |
304 | 305 | } |
305 | 306 |
|
| 307 | + [Fact] |
| 308 | + public void NumberTimeSeriesSummerStrategiesCoverSupportedNumericTypes() |
| 309 | + { |
| 310 | + AssertStrategies<int>(2, 3, 4); |
| 311 | + AssertStrategies<long>(2, 3, 4); |
| 312 | + AssertStrategies<float>(2, 3, 4); |
| 313 | + AssertStrategies<double>(2, 3, 4); |
| 314 | + AssertStrategies<decimal>(2, 3, 4); |
| 315 | + } |
| 316 | + |
| 317 | + [Fact] |
| 318 | + public void NumberTimeSeriesSummerMergeAndResamplePreserveStrategiesAcrossNumericTypes() |
| 319 | + { |
| 320 | + AssertMergeAndResample<int>(); |
| 321 | + AssertMergeAndResample<long>(); |
| 322 | + AssertMergeAndResample<float>(); |
| 323 | + AssertMergeAndResample<double>(); |
| 324 | + AssertMergeAndResample<decimal>(); |
| 325 | + } |
| 326 | + |
306 | 327 | [Fact] |
307 | 328 | public void DoubleTimeSeriesSummerConstructorsRespectStrategy() |
308 | 329 | { |
@@ -330,6 +351,90 @@ public void FloatTimeSeriesSummerAggregatesValues() |
330 | 351 | summer.Max().ShouldNotBeNull(); |
331 | 352 | } |
332 | 353 |
|
| 354 | + private static void AssertStrategies<TNumber>(TNumber first, TNumber second, TNumber newBucketValue) |
| 355 | + where TNumber : struct, INumber<TNumber> |
| 356 | + { |
| 357 | + foreach (var strategy in Enum.GetValues<Strategy>()) |
| 358 | + { |
| 359 | + var summer = new NumberTimeSeriesSummer<TNumber>(TimeSpan.FromSeconds(1), 0, strategy); |
| 360 | + var start = summer.Start; |
| 361 | + summer.AddNewData(start, first); |
| 362 | + summer.AddNewData(start, second); |
| 363 | + summer.Samples[start].ShouldBe(strategy switch |
| 364 | + { |
| 365 | + Strategy.Sum => first + second, |
| 366 | + Strategy.Min => TNumber.Min(first, second), |
| 367 | + Strategy.Max => TNumber.Max(first, second), |
| 368 | + Strategy.Replace => second, |
| 369 | + _ => throw new InvalidOperationException("Unsupported strategy.") |
| 370 | + }); |
| 371 | + |
| 372 | + var next = start.AddSeconds(1); |
| 373 | + summer.AddNewData(next, newBucketValue); |
| 374 | + summer.Samples[next].ShouldBe(newBucketValue); |
| 375 | + summer.DataCount.ShouldBe(3ul); |
| 376 | + summer.Start.ShouldBe(start); |
| 377 | + summer.End.ShouldBe(next); |
| 378 | + } |
| 379 | + } |
| 380 | + |
| 381 | + private static void AssertMergeAndResample<TNumber>() |
| 382 | + where TNumber : struct, INumber<TNumber> |
| 383 | + { |
| 384 | + foreach (var strategy in Enum.GetValues<Strategy>()) |
| 385 | + { |
| 386 | + var summer = new NumberTimeSeriesSummer<TNumber>(TimeSpan.FromSeconds(1), 0, strategy); |
| 387 | + var incoming = new NumberTimeSeriesSummer<TNumber>(TimeSpan.FromSeconds(1), 0, strategy); |
| 388 | + var interval = TimeSpan.FromSeconds(3); |
| 389 | + var currentStartTicks = summer.Start.UtcDateTime.Ticks; |
| 390 | + var alignedStartTicks = currentStartTicks - currentStartTicks % interval.Ticks; |
| 391 | + var start = new DateTimeOffset(alignedStartTicks, TimeSpan.Zero); |
| 392 | + var first = TNumber.CreateChecked(2); |
| 393 | + var second = TNumber.CreateChecked(10); |
| 394 | + var merged = TNumber.CreateChecked(3); |
| 395 | + var newBucket = TNumber.CreateChecked(20); |
| 396 | + |
| 397 | + summer.AddNewData(start, first); |
| 398 | + summer.AddNewData(start.AddSeconds(1), second); |
| 399 | + incoming.AddNewData(start, merged); |
| 400 | + incoming.AddNewData(start.AddSeconds(3), newBucket); |
| 401 | + |
| 402 | + summer.Merge(incoming); |
| 403 | + summer.DataCount.ShouldBe(4ul); |
| 404 | + summer.Start.ShouldBe(start); |
| 405 | + summer.End.ShouldBe(start.AddSeconds(3)); |
| 406 | + summer.Samples[start].ShouldBe(strategy switch |
| 407 | + { |
| 408 | + Strategy.Sum => first + merged, |
| 409 | + Strategy.Min => TNumber.Min(first, merged), |
| 410 | + Strategy.Max => TNumber.Max(first, merged), |
| 411 | + Strategy.Replace => merged, |
| 412 | + _ => throw new InvalidOperationException("Unsupported strategy.") |
| 413 | + }); |
| 414 | + |
| 415 | + summer.Resample(interval, 0); |
| 416 | + |
| 417 | + if (strategy == Strategy.Replace) |
| 418 | + { |
| 419 | + // The source snapshot is unordered, so Replace keeps one of these inputs. |
| 420 | + new[] { merged, second }.ShouldContain(summer.Samples[start]); |
| 421 | + } |
| 422 | + else |
| 423 | + { |
| 424 | + summer.Samples[start].ShouldBe(strategy switch |
| 425 | + { |
| 426 | + Strategy.Sum => first + merged + second, |
| 427 | + Strategy.Min => TNumber.Min(TNumber.Min(first, merged), second), |
| 428 | + Strategy.Max => TNumber.Max(TNumber.Max(first, merged), second), |
| 429 | + _ => throw new InvalidOperationException("Unsupported strategy.") |
| 430 | + }); |
| 431 | + } |
| 432 | + |
| 433 | + summer.Samples[start.AddSeconds(3)].ShouldBe(newBucket); |
| 434 | + summer.DataCount.ShouldBe(4ul); |
| 435 | + } |
| 436 | + } |
| 437 | + |
333 | 438 | // [Fact] |
334 | 439 | // public async Task SummerGroup() |
335 | 440 | // { |
|
0 commit comments