1use crate::strategy::{CITest, CITestDataType, TestResult};
2use crate::utils::power_divergence::power_divergence;
3use ndarray::{Array1, Array2};
4
5const LOG_LIKELIHOOD_LAMBDA: f64 = 0.0;
6
7#[derive(Debug, Clone, PartialEq)]
12pub struct LogLikelihood {
13 pub boolean: bool,
14 pub significance_level: f64,
15}
16
17impl LogLikelihood {
18 #[must_use]
19 pub fn new(boolean: bool, significance_level: f64) -> Self {
20 Self {
21 boolean,
22 significance_level,
23 }
24 }
25}
26
27impl CITest for LogLikelihood {
28 fn run_test(
29 &self,
30 x_values: Array1<f64>,
31 y_values: Array1<f64>,
32 z: Array2<f64>,
33 ) -> anyhow::Result<TestResult> {
34 power_divergence(
35 &x_values,
36 &y_values,
37 &z,
38 self.boolean,
39 self.significance_level,
40 LOG_LIKELIHOOD_LAMBDA,
41 )
42 }
43
44 fn data_types(&self) -> &'static [CITestDataType] {
45 &[CITestDataType::Discrete]
46 }
47}
48
49#[cfg(test)]
50#[allow(clippy::many_single_char_names)]
51mod tests {
52 use super::*;
53 use crate::utils::EPS;
54 use ndarray::{array, Array2};
55
56 fn unwrap_correlated(r: &TestResult) -> (f64, f64, usize) {
57 match r {
58 TestResult::Statistic(a, b, c) => (*a, *b, *c),
59 _ => panic!("expected Correlated2"),
60 }
61 }
62
63 #[test]
64 fn uncond_independent_data_accepted() {
65 let t = LogLikelihood {
66 boolean: false,
67 significance_level: 0.05,
68 };
69 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
70 let y = array![1., 2., 1., 2., 1., 2., 1., 2.];
71 let empty = Array2::<f64>::zeros((0, 0));
72
73 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, empty).unwrap());
74 assert!(stat.abs() < EPS);
75 assert!(p > 0.99);
76 assert_eq!(dof, 1);
77 }
78
79 #[test]
80 fn cond_independent_data_accepted() {
81 let t = LogLikelihood {
82 boolean: false,
83 significance_level: 0.05,
84 };
85 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
86 let y = array![1., 2., 1., 2., 1., 2., 1., 2.];
87 let z = array![[1.], [1.], [1.], [1.], [2.], [2.], [2.], [2.]];
88
89 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, z).unwrap());
90 assert!((stat).abs() < EPS, " got {stat}");
91 assert!(p > 0.99);
92 assert_eq!(dof, 2);
93 }
94
95 #[test]
96 fn uncond_dependent_data_rejected() {
97 let t = LogLikelihood {
98 boolean: false,
99 significance_level: 0.05,
100 };
101 let x = array![1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2.];
102 let y = array![1., 1., 1., 1., 1., 2., 1., 2., 2., 2., 2., 2.];
103 let empty = Array2::<f64>::zeros((0, 0));
104
105 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, empty).unwrap());
106 assert!((stat - 5.822_063_320_647_374).abs() < EPS, "got {stat}");
107 assert!((p - 0.015_826_368_796_540_195).abs() < EPS, "got {p}");
108 assert_eq!(dof, 1);
109 }
110
111 #[test]
112 fn cond_dependent_data_rejected() {
113 let t = LogLikelihood {
114 boolean: false,
115 significance_level: 0.05,
116 };
117 let x = array![1., 1., 2., 2., 1., 1., 2., 2.];
118 let y = array![1., 1., 2., 2., 1., 1., 2., 2.];
119 let z = array![[1.], [1.], [1.], [1.], [2.], [2.], [2.], [2.]];
120
121 let (p, stat, dof) = unwrap_correlated(&t.run_test(x, y, z).unwrap());
122 assert!(
123 (stat - 11.090_354_888_959_125).abs() < EPS,
124 "for stat got {stat}"
125 );
126 assert!((p - 0.003_906_249_999_999_994).abs() < EPS, "for p got {p}");
127 assert_eq!(dof, 2);
128 }
129
130 #[test]
131 fn uncond_bool_rejects_dependent() {
132 let t = LogLikelihood {
133 boolean: true,
134 significance_level: 0.05,
135 };
136 let x = array![1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2.];
137 let y = array![1., 1., 1., 1., 1., 2., 1., 2., 2., 2., 2., 2.];
138 let empty = Array2::<f64>::zeros((0, 0));
139 let r = t.run_test(x, y, empty).unwrap();
140 assert!(matches!(r, TestResult::Boolean(false)));
141 }
142
143 #[test]
144 fn cond_bool_rejects_dependent() {
145 let t = LogLikelihood {
146 boolean: true,
147 significance_level: 0.05,
148 };
149 let x = array![1., 1., 1., 2., 2., 2., 1., 1., 1., 2., 2., 2.];
150 let y = array![1., 1., 2., 2., 2., 2., 1., 1., 2., 2., 2., 2.];
151 let z = array![
152 [1.],
153 [1.],
154 [1.],
155 [1.],
156 [1.],
157 [1.],
158 [2.],
159 [2.],
160 [2.],
161 [2.],
162 [2.],
163 [2.]
164 ];
165
166 let r = t.run_test(x, y, z).unwrap();
167 assert!(matches!(r, TestResult::Boolean(false)));
168 }
169}