1use crate::error::FdarError;
7use crate::matrix::FdMatrix;
8
9pub fn validate_fdata(data: &FdMatrix, argvals: &[f64]) -> Result<(usize, usize), FdarError> {
32 let (n, m) = data.shape();
33 if n == 0 {
34 return Err(FdarError::InvalidDimension {
35 parameter: "data",
36 expected: "n > 0 rows".to_string(),
37 actual: format!("n = {n}"),
38 });
39 }
40 if m == 0 {
41 return Err(FdarError::InvalidDimension {
42 parameter: "data",
43 expected: "m > 0 columns".to_string(),
44 actual: format!("m = {m}"),
45 });
46 }
47 if argvals.len() != m {
48 return Err(FdarError::InvalidDimension {
49 parameter: "argvals",
50 expected: format!("{m} elements"),
51 actual: format!("{} elements", argvals.len()),
52 });
53 }
54 Ok((n, m))
55}
56
57pub fn validate_response(y: &[f64], n: usize) -> Result<(), FdarError> {
72 if y.len() != n {
73 return Err(FdarError::InvalidDimension {
74 parameter: "y",
75 expected: format!("{n} elements"),
76 actual: format!("{} elements", y.len()),
77 });
78 }
79 Ok(())
80}
81
82pub fn validate_labels(y: &[usize], n: usize, min_classes: usize) -> Result<usize, FdarError> {
102 if y.len() != n {
103 return Err(FdarError::InvalidDimension {
104 parameter: "y",
105 expected: format!("{n} elements"),
106 actual: format!("{} elements", y.len()),
107 });
108 }
109 let n_classes = y.iter().copied().max().map_or(0, |m| m + 1);
110 if n_classes < min_classes {
111 return Err(FdarError::InvalidParameter {
112 parameter: "y",
113 message: format!("need at least {min_classes} classes, got {n_classes}"),
114 });
115 }
116 Ok(n_classes)
117}
118
119pub fn validate_dist_mat(
140 dist_mat: &FdMatrix,
141 expected_n: Option<usize>,
142) -> Result<usize, FdarError> {
143 let n = dist_mat.nrows();
144 if dist_mat.ncols() != n {
145 return Err(FdarError::InvalidDimension {
146 parameter: "dist_mat",
147 expected: format!("{n} x {n} (square)"),
148 actual: format!("{} x {}", n, dist_mat.ncols()),
149 });
150 }
151 if let Some(exp) = expected_n {
152 if n != exp {
153 return Err(FdarError::InvalidDimension {
154 parameter: "dist_mat",
155 expected: format!("{exp} x {exp}"),
156 actual: format!("{n} x {n}"),
157 });
158 }
159 }
160 Ok(n)
161}
162
163pub fn validate_ncomp(ncomp: usize, n: usize, m: usize) -> Result<usize, FdarError> {
181 if ncomp == 0 {
182 return Err(FdarError::InvalidParameter {
183 parameter: "ncomp",
184 message: "must be >= 1".to_string(),
185 });
186 }
187 Ok(ncomp.min(n).min(m))
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
197 fn fdata_ok() {
198 let data = FdMatrix::zeros(10, 50);
199 let t: Vec<f64> = (0..50).map(|i| i as f64).collect();
200 let (n, m) = validate_fdata(&data, &t).unwrap();
201 assert_eq!((n, m), (10, 50));
202 }
203
204 #[test]
205 fn fdata_zero_rows() {
206 let data = FdMatrix::zeros(0, 5);
207 let t = vec![0.0; 5];
208 assert!(validate_fdata(&data, &t).is_err());
209 }
210
211 #[test]
212 fn fdata_zero_cols() {
213 let data = FdMatrix::zeros(5, 0);
214 assert!(validate_fdata(&data, &[]).is_err());
215 }
216
217 #[test]
218 fn fdata_argvals_mismatch() {
219 let data = FdMatrix::zeros(5, 10);
220 let t = vec![0.0; 8];
221 assert!(validate_fdata(&data, &t).is_err());
222 }
223
224 #[test]
227 fn response_ok() {
228 validate_response(&[1.0, 2.0, 3.0], 3).unwrap();
229 }
230
231 #[test]
232 fn response_mismatch() {
233 assert!(validate_response(&[1.0, 2.0], 3).is_err());
234 }
235
236 #[test]
239 fn labels_ok() {
240 let nc = validate_labels(&[0, 1, 0, 1], 4, 2).unwrap();
241 assert_eq!(nc, 2);
242 }
243
244 #[test]
245 fn labels_too_few_classes() {
246 assert!(validate_labels(&[0, 0, 0], 3, 2).is_err());
247 }
248
249 #[test]
250 fn labels_length_mismatch() {
251 assert!(validate_labels(&[0, 1], 3, 2).is_err());
252 }
253
254 #[test]
257 fn dist_mat_ok() {
258 let dm = FdMatrix::zeros(5, 5);
259 assert_eq!(validate_dist_mat(&dm, None).unwrap(), 5);
260 assert_eq!(validate_dist_mat(&dm, Some(5)).unwrap(), 5);
261 }
262
263 #[test]
264 fn dist_mat_not_square() {
265 let dm = FdMatrix::zeros(5, 3);
266 assert!(validate_dist_mat(&dm, None).is_err());
267 }
268
269 #[test]
270 fn dist_mat_wrong_size() {
271 let dm = FdMatrix::zeros(5, 5);
272 assert!(validate_dist_mat(&dm, Some(4)).is_err());
273 }
274
275 #[test]
278 fn ncomp_ok() {
279 assert_eq!(validate_ncomp(5, 10, 20).unwrap(), 5);
280 }
281
282 #[test]
283 fn ncomp_clamped() {
284 assert_eq!(validate_ncomp(100, 10, 20).unwrap(), 10);
285 assert_eq!(validate_ncomp(100, 20, 10).unwrap(), 10);
286 }
287
288 #[test]
289 fn ncomp_zero() {
290 assert!(validate_ncomp(0, 10, 20).is_err());
291 }
292}