diff --git a/specs/tri/math/statistics.t27 b/specs/tri/math/statistics.t27 index f796ce4f15..d32d3133a0 100644 --- a/specs/tri/math/statistics.t27 +++ b/specs/tri/math/statistics.t27 @@ -10,67 +10,238 @@ module TriStatistics; // 3. Core Functions // ═══════════════════════════════════════════════════════════ - // mean(values: []f64) → void - fn mean(values: []f64) -> void { - // TODO: Implement from .tri spec + // mean(values: []f64) → f64 + fn mean(values: []f64) -> f64 { + var sum: f64 = 0.0; + for (i in 0..values.len) { + sum += values[i]; + } + return sum / f64(values.len); } - // variance(values: []f64) → void - fn variance(values: []f64) -> void { - // TODO: Implement from .tri spec + // variance(values: []f64) → f64 + fn variance(values: []f64) -> f64 { + if (values.len == 0) return 0.0; + + var mean_val = mean(values); + var sum_sq: f64 = 0.0; + for (i in 0..values.len) { + sum_sq += (values[i] - mean_val) * (values[i] - mean_val); + } + return sum_sq / f64(values.len); } - // std_dev(values: []f64) → void - fn std_dev(values: []f64) -> void { - // TODO: Implement from .tri spec + // std_dev(values: []f64) → f64 + fn std_dev(values: []f64) -> f64 { + return math.sqrt(variance(values)); } - // median(allocator: std.mem.Allocator) → void - fn median(allocator: std.mem.Allocator) -> void { - // TODO: Implement from .tri spec + // median(allocator: std.mem.Allocator, values: []f64) → f64 + fn median(allocator: std.mem.Allocator, values: []f64) -> f64 { + if (values.len == 0) return 0.0; + + var sorted_values: []f64 = values; + + // Simple bubble sort for small datasets + for (i in 0..sorted_values.len) { + for (j in i+1..sorted_values.len) { + if (sorted_values[i] > sorted_values[j]) { + var temp = sorted_values[i]; + sorted_values[i] = sorted_values[j]; + sorted_values[j] = temp; + } + } + } + + var mid = sorted_values.len / 2; + if (sorted_values.len % 2 == 1) { + return sorted_values[mid]; + } else { + return (sorted_values[mid - 1] + sorted_values[mid]) / 2.0; + } } - // percentile(allocator: std.mem.Allocator) → void - fn percentile(allocator: std.mem.Allocator) -> void { - // TODO: Implement from .tri spec + // percentile(allocator: std.mem.Allocator, values: []f64, p: f64) → f64 + fn percentile(allocator: std.mem.Allocator, values: []f64, p: f64) -> f64 { + if (values.len == 0) return 0.0; + if (p < 0.0 or p > 100.0) return 0.0; + + var sorted_values: []f64 = values; + + // Simple bubble sort for small datasets + for (i in 0..sorted_values.len) { + for (j in i+1..sorted_values.len) { + if (sorted_values[i] > sorted_values[j]) { + var temp = sorted_values[i]; + sorted_values[i] = sorted_values[j]; + sorted_values[j] = temp; + } + } + } + + var index = (p / 100.0) * f64(sorted_values.len - 1); + var lower_idx = math.floor(index); + var upper_idx = math.ceil(index); + var weight = index - lower_idx; + + if (lower_idx == upper_idx) { + return sorted_values[lower_idx]; + } else { + return sorted_values[lower_idx] * (1.0 - weight) + sorted_values[upper_idx] * weight; + } } - // correlation(x: []f64) → void - fn correlation(x: []f64) -> void { - // TODO: Implement from .tri spec + // correlation(x: []f64, y: []f64) → f64 + fn correlation(x: []f64, y: []f64) -> f64 { + if (x.len != y.len or x.len == 0) return 0.0; + + var mean_x = mean(x); + var mean_y = mean(y); + + var numerator: f64 = 0.0; + var sum_sq_x: f64 = 0.0; + var sum_sq_y: f64 = 0.0; + + for (i in 0..x.len) { + var diff_x = x[i] - mean_x; + var diff_y = y[i] - mean_y; + numerator += diff_x * diff_y; + sum_sq_x += diff_x * diff_x; + sum_sq_y += diff_y * diff_y; + } + + var denominator = math.sqrt(sum_sq_x * sum_sq_y); + if (denominator == 0.0) return 0.0; + + return numerator / denominator; } // ═══════════════════════════════════════════════════════════ // TDD: Tests (from .tri behaviors) // ═══════════════════════════════════════════════════════════ - test mean_basic_case - given input = default_input() - when result = mean(input) - then result != undefined + test "mean_basic_case" { + var input = default_input(); + var result = mean(input); + assert(result != undefined); + } + + test "variance_basic_case" { + var input = default_input(); + var result = variance(input); + assert(result != undefined); + } + + test "std_dev_basic_case" { + var input = default_input(); + var result = std_dev(input); + assert(result != undefined); + } + + test "median_basic_case" { + var input = default_input(); + var result = median(default_allocator(), input); + assert(result != undefined); + } + + test "percentile_basic_case" { + var input = default_input(); + var result = percentile(default_allocator(), input, 50.0); + assert(result != undefined); + } + + test "correlation_basic_case" { + var x = default_input(); + var y = default_input(); + var result = correlation(x, y); + assert(result != undefined); + } + + test "mean_calculates_correctly" { + var values = [1.0, 2.0, 3.0, 4.0, 5.0]; + var result = mean(values); + assert(result == 3.0); + } + + test "variance_calculates_correctly" { + var values = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]; + var result = variance(values); + assert(result == 4.0); + } + + test "std_dev_calculates_correctly" { + var values = [1.0, 2.0, 3.0, 4.0, 5.0]; + var result = std_dev(values); + assert(result == math.sqrt(2.0)); + } + + test "median_odd_number" { + var values = [1.0, 3.0, 2.0, 5.0, 4.0]; + var result = median(default_allocator(), values); + assert(result == 3.0); + } + + test "median_even_number" { + var values = [1.0, 2.0, 3.0, 4.0]; + var result = median(default_allocator(), values); + assert(result == 2.5); + } + + test "median_empty" { + var values: []f64 = []; + var result = median(default_allocator(), values); + assert(result == 0.0); + } + + test "percentile_50th" { + var values = [1.0, 2.0, 3.0, 4.0, 5.0]; + var result = percentile(default_allocator(), values, 50.0); + assert(result == 3.0); + } + + test "percentile_25th" { + var values = [1.0, 2.0, 3.0, 4.0, 5.0]; + var result = percentile(default_allocator(), values, 25.0); + assert(result == 2.0); + } - test variance_basic_case - given input = default_input() - when result = variance(input) - then result != undefined + test "percentile_empty" { + var values: []f64 = []; + var result = percentile(default_allocator(), values, 50.0); + assert(result == 0.0); + } - test std_dev_basic_case - given input = default_input() - when result = std_dev(input) - then result != undefined + test "correlation_perfect_positive" { + var x = [1.0, 2.0, 3.0, 4.0, 5.0]; + var y = [2.0, 4.0, 6.0, 8.0, 10.0]; + var result = correlation(x, y); + assert(result == 1.0); + } - test median_basic_case - given input = default_input() - when result = median(input) - then result != undefined + test "correlation_perfect_negative" { + var x = [1.0, 2.0, 3.0, 4.0, 5.0]; + var y = [10.0, 8.0, 6.0, 4.0, 2.0]; + var result = correlation(x, y); + assert(result == -1.0); + } - test percentile_basic_case - given input = default_input() - when result = percentile(input) - then result != undefined + test "correlation_no_correlation" { + var x = [1.0, 2.0, 3.0, 4.0, 5.0]; + var y = [3.0, 1.0, 4.0, 2.0, 5.0]; + var result = correlation(x, y); + assert(result >= -1.0 and result <= 1.0); + } - test correlation_basic_case - given input = default_input() - when result = correlation(input) - then result != undefined + test "correlation_different_lengths" { + var x = [1.0, 2.0, 3.0]; + var y = [1.0, 2.0]; + var result = correlation(x, y); + assert(result == 0.0); + } + test "correlation_empty" { + var x: []f64 = []; + var y: []f64 = []; + var result = correlation(x, y); + assert(result == 0.0); + } \ No newline at end of file