From 07e18043145f3d91c8ea7ec3ad41b3c905585277 Mon Sep 17 00:00:00 2001 From: Ethan Werner Date: Wed, 1 Jul 2026 09:42:39 +0400 Subject: initial commit: ts time series analysis library v0.1.0 - 354 financial and mathematical time-series indicators - Uniform C11 API with consistent function signatures - 349 passing tests with -Werror - Static and shared library builds - pkg-config integration - Python bindings - Fuzzing and sanitizer support - Security hardening flags --- test/edge_cases.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 test/edge_cases.c (limited to 'test/edge_cases.c') diff --git a/test/edge_cases.c b/test/edge_cases.c new file mode 100644 index 0000000..ce47f72 --- /dev/null +++ b/test/edge_cases.c @@ -0,0 +1,324 @@ +#include +#include +#include +#include "ts.h" + +bool ts_test_edge_cases( void ) +{ + bool result = true; + + /* === NULL input === */ + + /* average with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 0 (empty input) === */ + + /* average with n=0 should return early, no crash */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with n=0 should not crash */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_deviation_standard(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_variance(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 1 (single element) === */ + + /* average with n=1, k=1 should produce the element itself */ + { + fp_t const x[] = {5}; + fp_t const e[] = {5}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_average_p); + } + + /* deviation_standard with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === k = 0 (zero period) === */ + + /* average with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_rsi(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_deviation_standard(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_variance(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === k > n (period exceeds data) === */ + + /* average with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_average(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_deviation_standard(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_variance(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === All-zero input === */ + + /* average with all-zero input should produce zeros */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-zero input should not crash (all gains/losses are 0) */ + { + fp_t const x[] = {0, 0, 0, 0, 0, 0, 0}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === All-same input (zero variance) === */ + + /* average with all-same input should produce that value */ + { + fp_t const x[] = {7, 7, 7, 7, 7}; + fp_t const e[] = {7, 7, 7}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-same input should produce 100 (no losses) */ + { + fp_t const x[] = {5, 5, 5, 5, 5, 5, 5}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === n == k boundary === */ + + /* average with n==k should succeed (produces 1 output) */ + { + fp_t const x[] = {1, 2, 3}; + fp_t const e[] = {2}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 3, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with n==k should fail (requires n > k) */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === NaN input === */ + + /* average with NaN input should return -1 (invalid input) */ + { + fp_t x[] = {NAN, 1.0, 2.0, 3.0, 4.0}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === Monotonic input (RSI edge cases) === */ + + /* rsi with strictly ascending input should produce 100 */ + { + fp_t const x[] = {1, 2, 3, 4, 5, 6, 7}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* rsi with strictly descending input should produce 0 */ + { + fp_t const x[] = {7, 6, 5, 4, 3, 2, 1}; + fp_t const e[] = {0, 0, 0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* === rsi with k > n === */ + + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + return result; +} -- cgit v1.3.1