probability_test.ts
1 // SPDX-License-Identifier: AGPL-3.0-or-later 2 // SPDX-FileCopyrightText: 2025 hyperpolymath 3 // 4 // Tests for BetLang Probability Module 5 6 import { assertEquals, assertThrows, assertAlmostEquals } from '@std/assert'; 7 import { 8 probability, 9 complement, 10 joint, 11 unionExclusive, 12 conditional, 13 expectedValue, 14 variance, 15 standardDeviation, 16 bayes, 17 } from '../src/probability.ts'; 18 19 // Probability creation tests 20 Deno.test('probability - creates valid probability', () => { 21 const p = probability(0.5); 22 assertEquals(p.value, 0.5); 23 }); 24 25 Deno.test('probability - accepts 0', () => { 26 const p = probability(0); 27 assertEquals(p.value, 0); 28 }); 29 30 Deno.test('probability - accepts 1', () => { 31 const p = probability(1); 32 assertEquals(p.value, 1); 33 }); 34 35 Deno.test('probability - throws on negative', () => { 36 assertThrows( 37 () => probability(-0.1), 38 Error, 39 'Invalid probability' 40 ); 41 }); 42 43 Deno.test('probability - throws on > 1', () => { 44 assertThrows( 45 () => probability(1.1), 46 Error, 47 'Invalid probability' 48 ); 49 }); 50 51 // Complement tests 52 Deno.test('complement - P(not A) = 1 - P(A)', () => { 53 const p = probability(0.3); 54 const c = complement(p); 55 assertAlmostEquals(c.value, 0.7, 1e-10); 56 }); 57 58 // Joint probability tests 59 Deno.test('joint - P(A and B) = P(A) * P(B)', () => { 60 const pA = probability(0.5); 61 const pB = probability(0.4); 62 const pAB = joint(pA, pB); 63 assertAlmostEquals(pAB.value, 0.2, 1e-10); 64 }); 65 66 // Union tests 67 Deno.test('unionExclusive - P(A or B) = P(A) + P(B)', () => { 68 const pA = probability(0.3); 69 const pB = probability(0.4); 70 const pAorB = unionExclusive(pA, pB); 71 assertAlmostEquals(pAorB.value, 0.7, 1e-10); 72 }); 73 74 Deno.test('unionExclusive - throws if sum > 1', () => { 75 const pA = probability(0.6); 76 const pB = probability(0.5); 77 assertThrows( 78 () => unionExclusive(pA, pB), 79 Error, 80 'Union of mutually exclusive events exceeds 1' 81 ); 82 }); 83 84 // Conditional probability tests 85 Deno.test('conditional - P(A|B) = P(A and B) / P(B)', () => { 86 const pAandB = probability(0.2); 87 const pB = probability(0.5); 88 const pAgivenB = conditional(pAandB, pB); 89 assertAlmostEquals(pAgivenB.value, 0.4, 1e-10); 90 }); 91 92 Deno.test('conditional - throws when P(B) = 0', () => { 93 const pAandB = probability(0); 94 const pB = probability(0); 95 assertThrows( 96 () => conditional(pAandB, pB), 97 Error, 98 'Cannot compute conditional probability when P(B) = 0' 99 ); 100 }); 101 102 // Statistical tests 103 Deno.test('expectedValue - calculates mean correctly', () => { 104 const data = [1, 2, 3, 4, 5]; 105 assertEquals(expectedValue(data), 3); 106 }); 107 108 Deno.test('expectedValue - throws on empty array', () => { 109 assertThrows( 110 () => expectedValue([]), 111 Error, 112 'Cannot compute expected value of empty array' 113 ); 114 }); 115 116 Deno.test('variance - calculates correctly', () => { 117 const data = [1, 2, 3, 4, 5]; 118 // Mean = 3, variance = ((1-3)^2 + (2-3)^2 + (3-3)^2 + (4-3)^2 + (5-3)^2) / 5 119 // = (4 + 1 + 0 + 1 + 4) / 5 = 2 120 assertEquals(variance(data), 2); 121 }); 122 123 Deno.test('standardDeviation - is sqrt of variance', () => { 124 const data = [1, 2, 3, 4, 5]; 125 assertAlmostEquals(standardDeviation(data), Math.sqrt(2), 1e-10); 126 }); 127 128 // Bayes theorem test 129 Deno.test('bayes - P(A|B) = P(B|A) * P(A) / P(B)', () => { 130 // Example: disease testing 131 // P(positive|disease) = 0.99 (sensitivity) 132 // P(disease) = 0.01 (prior - rare disease) 133 // P(positive) = 0.01 * 0.99 + 0.99 * 0.05 = 0.0594 (total positive rate) 134 // P(disease|positive) = 0.99 * 0.01 / 0.0594 = 0.1667 135 136 const pPositiveGivenDisease = probability(0.99); 137 const pDisease = probability(0.01); 138 const pPositive = probability(0.0594); 139 140 const pDiseaseGivenPositive = bayes(pPositiveGivenDisease, pDisease, pPositive); 141 assertAlmostEquals(pDiseaseGivenPositive.value, 0.1667, 0.001); 142 });