1#![allow(clippy::unwrap_used, clippy::disallowed_methods)]
2use crate::{Point, Rect};
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25#[repr(C)]
26pub struct Vec4 {
27 pub x: f32,
28 pub y: f32,
29 pub z: f32,
30 pub w: f32,
31}
32
33impl Vec4 {
34 #[inline]
36 #[must_use]
37 pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
38 Self { x, y, z, w }
39 }
40
41 #[inline]
43 #[must_use]
44 pub const fn zero() -> Self {
45 Self::new(0.0, 0.0, 0.0, 0.0)
46 }
47
48 #[inline]
50 #[must_use]
51 pub const fn from_point(p: Point) -> Self {
52 Self::new(p.x, p.y, 0.0, 1.0)
53 }
54
55 #[inline]
57 #[must_use]
58 pub const fn to_point(self) -> Point {
59 Point {
60 x: self.x,
61 y: self.y,
62 }
63 }
64
65 #[inline]
67 #[must_use]
68 pub fn dot(self, other: Self) -> f32 {
69 self.w.mul_add(
70 other.w,
71 self.z
72 .mul_add(other.z, self.x.mul_add(other.x, self.y * other.y)),
73 )
74 }
75
76 #[inline]
78 #[must_use]
79 pub fn add(self, other: Self) -> Self {
80 Self::new(
81 self.x + other.x,
82 self.y + other.y,
83 self.z + other.z,
84 self.w + other.w,
85 )
86 }
87
88 #[inline]
90 #[must_use]
91 pub fn sub(self, other: Self) -> Self {
92 Self::new(
93 self.x - other.x,
94 self.y - other.y,
95 self.z - other.z,
96 self.w - other.w,
97 )
98 }
99
100 #[inline]
102 #[must_use]
103 pub fn scale(self, s: f32) -> Self {
104 Self::new(self.x * s, self.y * s, self.z * s, self.w * s)
105 }
106
107 #[inline]
109 #[must_use]
110 pub fn mul(self, other: Self) -> Self {
111 Self::new(
112 self.x * other.x,
113 self.y * other.y,
114 self.z * other.z,
115 self.w * other.w,
116 )
117 }
118
119 #[inline]
121 #[must_use]
122 pub fn lerp(self, other: Self, t: f32) -> Self {
123 self.add(other.sub(self).scale(t))
124 }
125
126 #[inline]
128 #[must_use]
129 pub fn length(self) -> f32 {
130 self.dot(self).sqrt()
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn normalize(self) -> Self {
137 let len = self.length();
138 if len > 0.0 {
139 self.scale(1.0 / len)
140 } else {
141 self
142 }
143 }
144}
145
146impl Default for Vec4 {
147 fn default() -> Self {
148 Self::zero()
149 }
150}
151
152impl From<Point> for Vec4 {
153 fn from(p: Point) -> Self {
154 Self::from_point(p)
155 }
156}
157
158impl From<Vec4> for Point {
159 fn from(v: Vec4) -> Self {
160 v.to_point()
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq)]
166#[repr(C)]
167pub struct Mat4 {
168 pub data: [[f32; 4]; 4],
170}
171
172impl Mat4 {
173 #[inline]
175 #[must_use]
176 pub const fn from_data(data: [[f32; 4]; 4]) -> Self {
177 Self { data }
178 }
179
180 #[inline]
182 #[must_use]
183 pub const fn identity() -> Self {
184 Self::from_data([
185 [1.0, 0.0, 0.0, 0.0],
186 [0.0, 1.0, 0.0, 0.0],
187 [0.0, 0.0, 1.0, 0.0],
188 [0.0, 0.0, 0.0, 1.0],
189 ])
190 }
191
192 #[inline]
194 #[must_use]
195 pub const fn zero() -> Self {
196 Self::from_data([
197 [0.0, 0.0, 0.0, 0.0],
198 [0.0, 0.0, 0.0, 0.0],
199 [0.0, 0.0, 0.0, 0.0],
200 [0.0, 0.0, 0.0, 0.0],
201 ])
202 }
203
204 #[inline]
206 #[must_use]
207 pub const fn translation(x: f32, y: f32, z: f32) -> Self {
208 Self::from_data([
209 [1.0, 0.0, 0.0, x],
210 [0.0, 1.0, 0.0, y],
211 [0.0, 0.0, 1.0, z],
212 [0.0, 0.0, 0.0, 1.0],
213 ])
214 }
215
216 #[inline]
218 #[must_use]
219 pub const fn translation_2d(x: f32, y: f32) -> Self {
220 Self::translation(x, y, 0.0)
221 }
222
223 #[inline]
225 #[must_use]
226 pub const fn scale(x: f32, y: f32, z: f32) -> Self {
227 Self::from_data([
228 [x, 0.0, 0.0, 0.0],
229 [0.0, y, 0.0, 0.0],
230 [0.0, 0.0, z, 0.0],
231 [0.0, 0.0, 0.0, 1.0],
232 ])
233 }
234
235 #[inline]
237 #[must_use]
238 pub const fn scale_2d(x: f32, y: f32) -> Self {
239 Self::scale(x, y, 1.0)
240 }
241
242 #[inline]
244 #[must_use]
245 pub const fn scale_uniform(s: f32) -> Self {
246 Self::scale(s, s, s)
247 }
248
249 #[inline]
251 #[must_use]
252 pub fn rotation_z(angle_rad: f32) -> Self {
253 let (sin, cos) = angle_rad.sin_cos();
254 Self::from_data([
255 [cos, -sin, 0.0, 0.0],
256 [sin, cos, 0.0, 0.0],
257 [0.0, 0.0, 1.0, 0.0],
258 [0.0, 0.0, 0.0, 1.0],
259 ])
260 }
261
262 #[inline]
264 #[must_use]
265 pub fn ortho(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Self {
266 let width = right - left;
267 let height = top - bottom;
268 let depth = far - near;
269
270 Self::from_data([
271 [2.0 / width, 0.0, 0.0, -(right + left) / width],
272 [0.0, 2.0 / height, 0.0, -(top + bottom) / height],
273 [0.0, 0.0, -2.0 / depth, -(far + near) / depth],
274 [0.0, 0.0, 0.0, 1.0],
275 ])
276 }
277
278 #[inline]
280 #[must_use]
281 pub fn ortho_screen(width: f32, height: f32) -> Self {
282 Self::ortho(0.0, width, height, 0.0, -1.0, 1.0)
283 }
284
285 #[inline]
287 #[must_use]
288 pub fn mul(&self, other: &Self) -> Self {
289 let mut result = Self::zero();
290 for i in 0..4 {
291 for j in 0..4 {
292 for k in 0..4 {
293 result.data[i][j] += self.data[i][k] * other.data[k][j];
294 }
295 }
296 }
297 result
298 }
299
300 #[inline]
302 #[must_use]
303 pub fn transform_vec4(&self, v: Vec4) -> Vec4 {
304 Vec4::new(
305 self.data[0][3].mul_add(
306 v.w,
307 self.data[0][2].mul_add(v.z, self.data[0][0].mul_add(v.x, self.data[0][1] * v.y)),
308 ),
309 self.data[1][3].mul_add(
310 v.w,
311 self.data[1][2].mul_add(v.z, self.data[1][0].mul_add(v.x, self.data[1][1] * v.y)),
312 ),
313 self.data[2][3].mul_add(
314 v.w,
315 self.data[2][2].mul_add(v.z, self.data[2][0].mul_add(v.x, self.data[2][1] * v.y)),
316 ),
317 self.data[3][3].mul_add(
318 v.w,
319 self.data[3][2].mul_add(v.z, self.data[3][0].mul_add(v.x, self.data[3][1] * v.y)),
320 ),
321 )
322 }
323
324 #[inline]
326 #[must_use]
327 pub fn transform_point(&self, p: Point) -> Point {
328 let v = self.transform_vec4(Vec4::from_point(p));
329 Point::new(v.x, v.y)
330 }
331
332 #[inline]
334 #[must_use]
335 pub fn transform_rect(&self, rect: &Rect) -> Rect {
336 let corners = [
337 Point::new(rect.x, rect.y),
338 Point::new(rect.x + rect.width, rect.y),
339 Point::new(rect.x + rect.width, rect.y + rect.height),
340 Point::new(rect.x, rect.y + rect.height),
341 ];
342
343 let transformed: Vec<Point> = corners.iter().map(|&p| self.transform_point(p)).collect();
344
345 let min_x = transformed
346 .iter()
347 .map(|p| p.x)
348 .fold(f32::INFINITY, f32::min);
349 let max_x = transformed
350 .iter()
351 .map(|p| p.x)
352 .fold(f32::NEG_INFINITY, f32::max);
353 let min_y = transformed
354 .iter()
355 .map(|p| p.y)
356 .fold(f32::INFINITY, f32::min);
357 let max_y = transformed
358 .iter()
359 .map(|p| p.y)
360 .fold(f32::NEG_INFINITY, f32::max);
361
362 Rect::new(min_x, min_y, max_x - min_x, max_y - min_y)
363 }
364
365 #[inline]
367 #[must_use]
368 pub const fn column(&self, idx: usize) -> Vec4 {
369 Vec4::new(
370 self.data[0][idx],
371 self.data[1][idx],
372 self.data[2][idx],
373 self.data[3][idx],
374 )
375 }
376
377 #[inline]
379 #[must_use]
380 pub const fn row(&self, idx: usize) -> Vec4 {
381 Vec4::new(
382 self.data[idx][0],
383 self.data[idx][1],
384 self.data[idx][2],
385 self.data[idx][3],
386 )
387 }
388
389 #[inline]
391 #[must_use]
392 pub const fn transpose(&self) -> Self {
393 Self::from_data([
394 [
395 self.data[0][0],
396 self.data[1][0],
397 self.data[2][0],
398 self.data[3][0],
399 ],
400 [
401 self.data[0][1],
402 self.data[1][1],
403 self.data[2][1],
404 self.data[3][1],
405 ],
406 [
407 self.data[0][2],
408 self.data[1][2],
409 self.data[2][2],
410 self.data[3][2],
411 ],
412 [
413 self.data[0][3],
414 self.data[1][3],
415 self.data[2][3],
416 self.data[3][3],
417 ],
418 ])
419 }
420}
421
422impl Default for Mat4 {
423 fn default() -> Self {
424 Self::identity()
425 }
426}
427
428impl std::ops::Mul for Mat4 {
429 type Output = Self;
430 fn mul(self, rhs: Self) -> Self {
431 Self::mul(&self, &rhs)
432 }
433}
434
435impl std::ops::Mul<Vec4> for Mat4 {
436 type Output = Vec4;
437 fn mul(self, rhs: Vec4) -> Vec4 {
438 self.transform_vec4(rhs)
439 }
440}
441
442#[inline]
446#[must_use]
447pub fn batch_transform_points(points: &[Point], transform: &Mat4) -> Vec<Point> {
448 points
449 .iter()
450 .map(|&p| transform.transform_point(p))
451 .collect()
452}
453
454#[inline]
456#[must_use]
457pub fn batch_transform_vec4(vecs: &[Vec4], transform: &Mat4) -> Vec<Vec4> {
458 vecs.iter().map(|&v| transform.transform_vec4(v)).collect()
459}
460
461#[inline]
463#[must_use]
464pub fn batch_lerp_points(from: &[Point], to: &[Point], t: f32) -> Vec<Point> {
465 debug_assert_eq!(from.len(), to.len());
466 from.iter()
467 .zip(to.iter())
468 .map(|(a, b)| a.lerp(b, t))
469 .collect()
470}
471
472#[inline]
474#[must_use]
475pub fn bounding_box(points: &[Point]) -> Option<Rect> {
476 if points.is_empty() {
477 return None;
478 }
479
480 let mut min_x = f32::INFINITY;
481 let mut max_x = f32::NEG_INFINITY;
482 let mut min_y = f32::INFINITY;
483 let mut max_y = f32::NEG_INFINITY;
484
485 for p in points {
486 min_x = min_x.min(p.x);
487 max_x = max_x.max(p.x);
488 min_y = min_y.min(p.y);
489 max_y = max_y.max(p.y);
490 }
491
492 Some(Rect::new(min_x, min_y, max_x - min_x, max_y - min_y))
493}
494
495#[inline]
497#[must_use]
498pub fn centroid(points: &[Point]) -> Option<Point> {
499 if points.is_empty() {
500 return None;
501 }
502
503 let sum: (f32, f32) = points
504 .iter()
505 .fold((0.0, 0.0), |acc, p| (acc.0 + p.x, acc.1 + p.y));
506 let n = points.len() as f32;
507 Some(Point::new(sum.0 / n, sum.1 / n))
508}
509
510#[must_use]
512pub fn point_in_convex_polygon(point: Point, polygon: &[Point]) -> bool {
513 if polygon.len() < 3 {
514 return false;
515 }
516
517 let mut positive = false;
518 let mut negative = false;
519
520 for i in 0..polygon.len() {
521 let a = polygon[i];
522 let b = polygon[(i + 1) % polygon.len()];
523
524 let cross = (point.x - a.x).mul_add(b.y - a.y, -((point.y - a.y) * (b.x - a.x)));
525
526 if cross > 0.0 {
527 positive = true;
528 } else if cross < 0.0 {
529 negative = true;
530 }
531
532 if positive && negative {
533 return false;
534 }
535 }
536
537 true
538}
539
540#[must_use]
542pub fn polygon_area(polygon: &[Point]) -> f32 {
543 if polygon.len() < 3 {
544 return 0.0;
545 }
546
547 let mut area = 0.0;
548 for i in 0..polygon.len() {
549 let j = (i + 1) % polygon.len();
550 area += polygon[i].x * polygon[j].y;
551 area -= polygon[j].x * polygon[i].y;
552 }
553
554 (area / 2.0).abs()
555}
556
557#[inline]
592#[must_use]
593pub fn batch_sum_f64(values: &[f64]) -> f64 {
594 if values.len() < 4 {
595 return values.iter().sum();
596 }
597
598 let (chunks, remainder) = values.as_chunks::<4>();
603
604 let mut acc = [0.0f64; 4];
605 for chunk in chunks {
606 acc[0] += chunk[0];
607 acc[1] += chunk[1];
608 acc[2] += chunk[2];
609 acc[3] += chunk[3];
610 }
611
612 let mut sum = acc[0] + acc[1] + acc[2] + acc[3];
613 for &v in remainder {
614 sum += v;
615 }
616 sum
617}
618
619#[inline]
630#[must_use]
631pub fn batch_mean_f64(values: &[f64]) -> f64 {
632 if values.is_empty() {
633 return 0.0;
634 }
635 batch_sum_f64(values) / values.len() as f64
636}
637
638#[inline]
652#[must_use]
653pub fn batch_min_max_f64(values: &[f64]) -> Option<(f64, f64)> {
654 if values.is_empty() {
655 return None;
656 }
657
658 if values.len() < 4 {
659 let mut min = values[0];
660 let mut max = values[0];
661 for &v in &values[1..] {
662 min = min.min(v);
663 max = max.max(v);
664 }
665 return Some((min, max));
666 }
667
668 let (chunks, remainder) = values.as_chunks::<4>();
670
671 let mut min_acc = [f64::INFINITY; 4];
672 let mut max_acc = [f64::NEG_INFINITY; 4];
673
674 for chunk in chunks {
675 min_acc[0] = min_acc[0].min(chunk[0]);
676 min_acc[1] = min_acc[1].min(chunk[1]);
677 min_acc[2] = min_acc[2].min(chunk[2]);
678 min_acc[3] = min_acc[3].min(chunk[3]);
679
680 max_acc[0] = max_acc[0].max(chunk[0]);
681 max_acc[1] = max_acc[1].max(chunk[1]);
682 max_acc[2] = max_acc[2].max(chunk[2]);
683 max_acc[3] = max_acc[3].max(chunk[3]);
684 }
685
686 let mut min = min_acc[0].min(min_acc[1]).min(min_acc[2]).min(min_acc[3]);
687 let mut max = max_acc[0].max(max_acc[1]).max(max_acc[2]).max(max_acc[3]);
688
689 for &v in remainder {
690 min = min.min(v);
691 max = max.max(v);
692 }
693
694 Some((min, max))
695}
696
697#[inline]
710#[must_use]
711pub fn normalize_f64(values: &[f64]) -> Vec<f64> {
712 if values.is_empty() {
713 return Vec::new();
714 }
715
716 let Some((min, max)) = batch_min_max_f64(values) else {
717 return Vec::new();
718 };
719
720 let range = max - min;
721 if range == 0.0 {
722 return vec![0.0; values.len()];
723 }
724
725 let inv_range = 1.0 / range;
726 values.iter().map(|&v| (v - min) * inv_range).collect()
727}
728
729#[inline]
742#[must_use]
743pub fn normalize_with_range_f64(values: &[f64], min: f64, max: f64) -> Vec<f64> {
744 let range = max - min;
745 if range == 0.0 {
746 return vec![0.0; values.len()];
747 }
748
749 let inv_range = 1.0 / range;
750 values.iter().map(|&v| (v - min) * inv_range).collect()
751}
752
753#[inline]
765#[must_use]
766pub fn batch_scale_f64(values: &[f64], scale: f64) -> Vec<f64> {
767 values.iter().map(|&v| v * scale).collect()
768}
769
770#[inline]
784#[must_use]
785pub fn batch_scale_offset_f64(values: &[f64], scale: f64, offset: f64) -> Vec<f64> {
786 values.iter().map(|&v| v.mul_add(scale, offset)).collect()
787}
788
789#[inline]
802#[must_use]
803pub fn batch_variance_f64(values: &[f64]) -> f64 {
804 if values.len() < 2 {
805 return 0.0;
806 }
807
808 let mean = batch_mean_f64(values);
809 let sum_sq: f64 = values.iter().map(|&v| (v - mean) * (v - mean)).sum();
810 sum_sq / values.len() as f64
811}
812
813#[inline]
825#[must_use]
826pub fn batch_stddev_f64(values: &[f64]) -> f64 {
827 batch_variance_f64(values).sqrt()
828}
829
830#[inline]
844#[must_use]
845pub fn weighted_sum_f64(values: &[f64], weights: &[f64]) -> f64 {
846 debug_assert_eq!(values.len(), weights.len());
847
848 if values.len() < 4 {
849 return values
850 .iter()
851 .zip(weights.iter())
852 .map(|(&v, &w)| v * w)
853 .sum();
854 }
855
856 let (v_chunks, v_rem) = values.as_chunks::<4>();
858 let (w_chunks, w_rem) = weights.as_chunks::<4>();
859
860 let mut acc = [0.0f64; 4];
861 for (vc, wc) in v_chunks.iter().zip(w_chunks.iter()) {
862 acc[0] = vc[0].mul_add(wc[0], acc[0]);
863 acc[1] = vc[1].mul_add(wc[1], acc[1]);
864 acc[2] = vc[2].mul_add(wc[2], acc[2]);
865 acc[3] = vc[3].mul_add(wc[3], acc[3]);
866 }
867
868 let mut sum = acc[0] + acc[1] + acc[2] + acc[3];
869 for (&v, &w) in v_rem.iter().zip(w_rem.iter()) {
870 sum = v.mul_add(w, sum);
871 }
872 sum
873}
874
875#[inline]
888#[must_use]
889pub fn percentile_sorted_f64(sorted_values: &[f64], p: f64) -> f64 {
890 if sorted_values.is_empty() {
891 return 0.0;
892 }
893 if sorted_values.len() == 1 {
894 return sorted_values[0];
895 }
896
897 let p = p.clamp(0.0, 1.0);
898 let n = sorted_values.len() as f64;
899 let idx = p * (n - 1.0);
900 let lower = idx.floor() as usize;
901 let upper = idx.ceil() as usize;
902
903 if lower == upper {
904 sorted_values[lower]
905 } else {
906 let frac = idx - lower as f64;
907 sorted_values[lower].mul_add(1.0 - frac, sorted_values[upper] * frac)
908 }
909}
910
911#[inline]
924#[must_use]
925pub fn histogram_f64(values: &[f64], min: f64, max: f64, num_bins: usize) -> Vec<usize> {
926 if num_bins == 0 || values.is_empty() {
927 return vec![0; num_bins];
928 }
929
930 let range = max - min;
931 if range <= 0.0 {
932 return {
934 let mut counts = vec![0; num_bins];
935 counts[0] = values.len();
936 counts
937 };
938 }
939
940 let bin_width = range / num_bins as f64;
941 let mut counts = vec![0usize; num_bins];
942
943 for &v in values {
944 let bin = ((v - min) / bin_width) as usize;
945 let bin = bin.min(num_bins - 1); counts[bin] += 1;
947 }
948
949 counts
950}
951
952#[cfg(test)]
953mod tests {
954 use super::*;
955
956 #[test]
961 fn test_vec4_new() {
962 let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
963 assert_eq!(v.x, 1.0);
964 assert_eq!(v.y, 2.0);
965 assert_eq!(v.z, 3.0);
966 assert_eq!(v.w, 4.0);
967 }
968
969 #[test]
970 fn test_vec4_zero() {
971 let v = Vec4::zero();
972 assert_eq!(v, Vec4::new(0.0, 0.0, 0.0, 0.0));
973 }
974
975 #[test]
976 fn test_vec4_default() {
977 let v = Vec4::default();
978 assert_eq!(v, Vec4::zero());
979 }
980
981 #[test]
982 fn test_vec4_from_point() {
983 let p = Point::new(10.0, 20.0);
984 let v = Vec4::from_point(p);
985 assert_eq!(v, Vec4::new(10.0, 20.0, 0.0, 1.0));
986 }
987
988 #[test]
989 fn test_vec4_to_point() {
990 let v = Vec4::new(5.0, 15.0, 25.0, 35.0);
991 let p = v.to_point();
992 assert_eq!(p, Point::new(5.0, 15.0));
993 }
994
995 #[test]
996 fn test_vec4_dot() {
997 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
998 let b = Vec4::new(2.0, 3.0, 4.0, 5.0);
999 assert_eq!(
1000 a.dot(b),
1001 4.0f32.mul_add(5.0, 3.0f32.mul_add(4.0, 1.0f32.mul_add(2.0, 2.0 * 3.0)))
1002 );
1003 }
1004
1005 #[test]
1006 fn test_vec4_add() {
1007 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
1008 let b = Vec4::new(5.0, 6.0, 7.0, 8.0);
1009 let c = a.add(b);
1010 assert_eq!(c, Vec4::new(6.0, 8.0, 10.0, 12.0));
1011 }
1012
1013 #[test]
1014 fn test_vec4_sub() {
1015 let a = Vec4::new(5.0, 6.0, 7.0, 8.0);
1016 let b = Vec4::new(1.0, 2.0, 3.0, 4.0);
1017 let c = a.sub(b);
1018 assert_eq!(c, Vec4::new(4.0, 4.0, 4.0, 4.0));
1019 }
1020
1021 #[test]
1022 fn test_vec4_scale() {
1023 let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
1024 let s = v.scale(2.0);
1025 assert_eq!(s, Vec4::new(2.0, 4.0, 6.0, 8.0));
1026 }
1027
1028 #[test]
1029 fn test_vec4_mul() {
1030 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
1031 let b = Vec4::new(2.0, 2.0, 2.0, 2.0);
1032 let c = a.mul(b);
1033 assert_eq!(c, Vec4::new(2.0, 4.0, 6.0, 8.0));
1034 }
1035
1036 #[test]
1037 fn test_vec4_lerp() {
1038 let a = Vec4::new(0.0, 0.0, 0.0, 0.0);
1039 let b = Vec4::new(10.0, 10.0, 10.0, 10.0);
1040 let c = a.lerp(b, 0.5);
1041 assert_eq!(c, Vec4::new(5.0, 5.0, 5.0, 5.0));
1042 }
1043
1044 #[test]
1045 fn test_vec4_length() {
1046 let v = Vec4::new(3.0, 4.0, 0.0, 0.0);
1047 assert!((v.length() - 5.0).abs() < 0.0001);
1048 }
1049
1050 #[test]
1051 fn test_vec4_normalize() {
1052 let v = Vec4::new(3.0, 4.0, 0.0, 0.0);
1053 let n = v.normalize();
1054 assert!((n.length() - 1.0).abs() < 0.0001);
1055 }
1056
1057 #[test]
1058 fn test_vec4_from_impl() {
1059 let p = Point::new(1.0, 2.0);
1060 let v: Vec4 = p.into();
1061 assert_eq!(v, Vec4::new(1.0, 2.0, 0.0, 1.0));
1062 }
1063
1064 #[test]
1069 fn test_mat4_identity() {
1070 let m = Mat4::identity();
1071 assert_eq!(m.data[0][0], 1.0);
1072 assert_eq!(m.data[1][1], 1.0);
1073 assert_eq!(m.data[2][2], 1.0);
1074 assert_eq!(m.data[3][3], 1.0);
1075 assert_eq!(m.data[0][1], 0.0);
1076 }
1077
1078 #[test]
1079 fn test_mat4_zero() {
1080 let m = Mat4::zero();
1081 for i in 0..4 {
1082 for j in 0..4 {
1083 assert_eq!(m.data[i][j], 0.0);
1084 }
1085 }
1086 }
1087
1088 #[test]
1089 fn test_mat4_default() {
1090 let m = Mat4::default();
1091 assert_eq!(m, Mat4::identity());
1092 }
1093
1094 #[test]
1095 fn test_mat4_translation() {
1096 let m = Mat4::translation(10.0, 20.0, 30.0);
1097 let p = Point::new(0.0, 0.0);
1098 let t = m.transform_point(p);
1099 assert_eq!(t, Point::new(10.0, 20.0));
1100 }
1101
1102 #[test]
1103 fn test_mat4_translation_2d() {
1104 let m = Mat4::translation_2d(5.0, 15.0);
1105 let p = Point::new(10.0, 10.0);
1106 let t = m.transform_point(p);
1107 assert_eq!(t, Point::new(15.0, 25.0));
1108 }
1109
1110 #[test]
1111 fn test_mat4_scale() {
1112 let m = Mat4::scale(2.0, 3.0, 4.0);
1113 let p = Point::new(10.0, 10.0);
1114 let t = m.transform_point(p);
1115 assert_eq!(t, Point::new(20.0, 30.0));
1116 }
1117
1118 #[test]
1119 fn test_mat4_scale_2d() {
1120 let m = Mat4::scale_2d(0.5, 2.0);
1121 let p = Point::new(10.0, 10.0);
1122 let t = m.transform_point(p);
1123 assert_eq!(t, Point::new(5.0, 20.0));
1124 }
1125
1126 #[test]
1127 fn test_mat4_scale_uniform() {
1128 let m = Mat4::scale_uniform(2.0);
1129 let p = Point::new(5.0, 5.0);
1130 let t = m.transform_point(p);
1131 assert_eq!(t, Point::new(10.0, 10.0));
1132 }
1133
1134 #[test]
1135 fn test_mat4_rotation_z() {
1136 use std::f32::consts::PI;
1137 let m = Mat4::rotation_z(PI / 2.0); let p = Point::new(1.0, 0.0);
1139 let t = m.transform_point(p);
1140 assert!((t.x - 0.0).abs() < 0.0001);
1142 assert!((t.y - 1.0).abs() < 0.0001);
1143 }
1144
1145 #[test]
1146 fn test_mat4_mul_identity() {
1147 let a = Mat4::identity();
1148 let b = Mat4::identity();
1149 let c = a.mul(&b);
1150 assert_eq!(c, Mat4::identity());
1151 }
1152
1153 #[test]
1154 fn test_mat4_mul_combined_transform() {
1155 let translate = Mat4::translation_2d(10.0, 0.0);
1156 let scale = Mat4::scale_2d(2.0, 2.0);
1157 let combined = translate.mul(&scale);
1158
1159 let p = Point::new(5.0, 5.0);
1160 let t = combined.transform_point(p);
1161 assert_eq!(t, Point::new(20.0, 10.0));
1163 }
1164
1165 #[test]
1166 fn test_mat4_transform_rect() {
1167 let m = Mat4::scale_2d(2.0, 2.0);
1168 let rect = Rect::new(10.0, 10.0, 20.0, 30.0);
1169 let t = m.transform_rect(&rect);
1170 assert_eq!(t.x, 20.0);
1171 assert_eq!(t.y, 20.0);
1172 assert_eq!(t.width, 40.0);
1173 assert_eq!(t.height, 60.0);
1174 }
1175
1176 #[test]
1177 fn test_mat4_transpose() {
1178 let m = Mat4::from_data([
1179 [1.0, 2.0, 3.0, 4.0],
1180 [5.0, 6.0, 7.0, 8.0],
1181 [9.0, 10.0, 11.0, 12.0],
1182 [13.0, 14.0, 15.0, 16.0],
1183 ]);
1184 let t = m.transpose();
1185 assert_eq!(t.data[0][1], 5.0);
1186 assert_eq!(t.data[1][0], 2.0);
1187 }
1188
1189 #[test]
1190 fn test_mat4_column() {
1191 let m = Mat4::identity();
1192 let col = m.column(0);
1193 assert_eq!(col, Vec4::new(1.0, 0.0, 0.0, 0.0));
1194 }
1195
1196 #[test]
1197 fn test_mat4_row() {
1198 let m = Mat4::identity();
1199 let row = m.row(0);
1200 assert_eq!(row, Vec4::new(1.0, 0.0, 0.0, 0.0));
1201 }
1202
1203 #[test]
1204 fn test_mat4_ortho_screen() {
1205 let m = Mat4::ortho_screen(800.0, 600.0);
1206 let p = m.transform_vec4(Vec4::new(0.0, 0.0, 0.0, 1.0));
1208 assert!((p.x - (-1.0)).abs() < 0.001);
1209 assert!((p.y - 1.0).abs() < 0.001);
1210 }
1211
1212 #[test]
1213 fn test_mat4_mul_operator() {
1214 let a = Mat4::translation_2d(10.0, 20.0);
1215 let b = Mat4::scale_2d(2.0, 2.0);
1216 let c = a * b;
1217 assert_eq!(c, a.mul(&b));
1218 }
1219
1220 #[test]
1221 fn test_mat4_mul_vec4_operator() {
1222 let m = Mat4::translation_2d(10.0, 20.0);
1223 let v = Vec4::new(0.0, 0.0, 0.0, 1.0);
1224 let r = m * v;
1225 assert_eq!(r, Vec4::new(10.0, 20.0, 0.0, 1.0));
1226 }
1227
1228 #[test]
1233 fn test_batch_transform_points() {
1234 let m = Mat4::translation_2d(10.0, 10.0);
1235 let points = vec![Point::new(0.0, 0.0), Point::new(5.0, 5.0)];
1236 let result = batch_transform_points(&points, &m);
1237 assert_eq!(result[0], Point::new(10.0, 10.0));
1238 assert_eq!(result[1], Point::new(15.0, 15.0));
1239 }
1240
1241 #[test]
1242 fn test_batch_transform_vec4() {
1243 let m = Mat4::scale_uniform(2.0);
1244 let vecs = vec![Vec4::new(1.0, 1.0, 1.0, 0.0), Vec4::new(2.0, 2.0, 2.0, 0.0)];
1245 let result = batch_transform_vec4(&vecs, &m);
1246 assert_eq!(result[0], Vec4::new(2.0, 2.0, 2.0, 0.0));
1247 assert_eq!(result[1], Vec4::new(4.0, 4.0, 4.0, 0.0));
1248 }
1249
1250 #[test]
1251 fn test_batch_lerp_points() {
1252 let from = vec![Point::new(0.0, 0.0), Point::new(10.0, 10.0)];
1253 let to = vec![Point::new(10.0, 10.0), Point::new(20.0, 20.0)];
1254 let result = batch_lerp_points(&from, &to, 0.5);
1255 assert_eq!(result[0], Point::new(5.0, 5.0));
1256 assert_eq!(result[1], Point::new(15.0, 15.0));
1257 }
1258
1259 #[test]
1264 fn test_bounding_box() {
1265 let points = vec![
1266 Point::new(0.0, 0.0),
1267 Point::new(10.0, 5.0),
1268 Point::new(5.0, 15.0),
1269 ];
1270 let bbox = bounding_box(&points).unwrap();
1271 assert_eq!(bbox.x, 0.0);
1272 assert_eq!(bbox.y, 0.0);
1273 assert_eq!(bbox.width, 10.0);
1274 assert_eq!(bbox.height, 15.0);
1275 }
1276
1277 #[test]
1278 fn test_bounding_box_empty() {
1279 let points: Vec<Point> = vec![];
1280 assert!(bounding_box(&points).is_none());
1281 }
1282
1283 #[test]
1284 fn test_centroid() {
1285 let points = vec![
1286 Point::new(0.0, 0.0),
1287 Point::new(10.0, 0.0),
1288 Point::new(10.0, 10.0),
1289 Point::new(0.0, 10.0),
1290 ];
1291 let c = centroid(&points).unwrap();
1292 assert_eq!(c, Point::new(5.0, 5.0));
1293 }
1294
1295 #[test]
1296 fn test_centroid_empty() {
1297 let points: Vec<Point> = vec![];
1298 assert!(centroid(&points).is_none());
1299 }
1300
1301 #[test]
1302 fn test_point_in_convex_polygon() {
1303 let square = vec![
1304 Point::new(0.0, 0.0),
1305 Point::new(10.0, 0.0),
1306 Point::new(10.0, 10.0),
1307 Point::new(0.0, 10.0),
1308 ];
1309
1310 assert!(point_in_convex_polygon(Point::new(5.0, 5.0), &square));
1311 assert!(!point_in_convex_polygon(Point::new(15.0, 5.0), &square));
1312 }
1313
1314 #[test]
1315 fn test_point_in_convex_polygon_edge() {
1316 let triangle = vec![
1317 Point::new(0.0, 0.0),
1318 Point::new(10.0, 0.0),
1319 Point::new(5.0, 10.0),
1320 ];
1321
1322 assert!(point_in_convex_polygon(Point::new(5.0, 0.0), &triangle));
1324 }
1325
1326 #[test]
1327 fn test_polygon_area_square() {
1328 let square = vec![
1329 Point::new(0.0, 0.0),
1330 Point::new(10.0, 0.0),
1331 Point::new(10.0, 10.0),
1332 Point::new(0.0, 10.0),
1333 ];
1334 let area = polygon_area(&square);
1335 assert!((area - 100.0).abs() < 0.0001);
1336 }
1337
1338 #[test]
1339 fn test_polygon_area_triangle() {
1340 let triangle = vec![
1341 Point::new(0.0, 0.0),
1342 Point::new(10.0, 0.0),
1343 Point::new(5.0, 10.0),
1344 ];
1345 let area = polygon_area(&triangle);
1346 assert!((area - 50.0).abs() < 0.0001);
1347 }
1348
1349 #[test]
1350 fn test_polygon_area_too_few_points() {
1351 assert_eq!(polygon_area(&[]), 0.0);
1352 assert_eq!(polygon_area(&[Point::new(0.0, 0.0)]), 0.0);
1353 assert_eq!(
1354 polygon_area(&[Point::new(0.0, 0.0), Point::new(1.0, 1.0)]),
1355 0.0
1356 );
1357 }
1358
1359 mod compute_block_tests {
1368 use super::*;
1369
1370 #[test]
1371 fn test_batch_sum_f64_empty() {
1372 assert_eq!(batch_sum_f64(&[]), 0.0);
1373 }
1374
1375 #[test]
1376 fn test_batch_sum_f64_small() {
1377 assert_eq!(batch_sum_f64(&[1.0, 2.0, 3.0]), 6.0);
1378 }
1379
1380 #[test]
1381 fn test_batch_sum_f64_large() {
1382 let values: Vec<f64> = (1..=100).map(f64::from).collect();
1383 assert_eq!(batch_sum_f64(&values), 5050.0);
1384 }
1385
1386 #[test]
1387 fn test_batch_sum_f64_exact_chunks() {
1388 let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
1390 assert_eq!(batch_sum_f64(&values), 36.0);
1391 }
1392
1393 #[test]
1394 fn test_batch_mean_f64_empty() {
1395 assert_eq!(batch_mean_f64(&[]), 0.0);
1396 }
1397
1398 #[test]
1399 fn test_batch_mean_f64() {
1400 assert_eq!(batch_mean_f64(&[2.0, 4.0, 6.0, 8.0]), 5.0);
1401 }
1402
1403 #[test]
1404 fn test_batch_min_max_f64_empty() {
1405 assert!(batch_min_max_f64(&[]).is_none());
1406 }
1407
1408 #[test]
1409 fn test_batch_min_max_f64_single() {
1410 assert_eq!(batch_min_max_f64(&[42.0]), Some((42.0, 42.0)));
1411 }
1412
1413 #[test]
1414 fn test_batch_min_max_f64_small() {
1415 assert_eq!(batch_min_max_f64(&[3.0, 1.0, 2.0]), Some((1.0, 3.0)));
1416 }
1417
1418 #[test]
1419 fn test_batch_min_max_f64_large() {
1420 let values: Vec<f64> = (1..=256).map(f64::from).collect();
1421 assert_eq!(batch_min_max_f64(&values), Some((1.0, 256.0)));
1422 }
1423
1424 #[test]
1425 fn test_normalize_f64_empty() {
1426 assert!(normalize_f64(&[]).is_empty());
1427 }
1428
1429 #[test]
1430 fn test_normalize_f64_constant() {
1431 let result = normalize_f64(&[5.0, 5.0, 5.0]);
1432 assert_eq!(result, vec![0.0, 0.0, 0.0]);
1433 }
1434
1435 #[test]
1436 fn test_normalize_f64() {
1437 let result = normalize_f64(&[0.0, 50.0, 100.0]);
1438 assert_eq!(result, vec![0.0, 0.5, 1.0]);
1439 }
1440
1441 #[test]
1442 fn test_normalize_with_range_f64() {
1443 let result = normalize_with_range_f64(&[25.0, 50.0, 75.0], 0.0, 100.0);
1444 assert_eq!(result, vec![0.25, 0.5, 0.75]);
1445 }
1446
1447 #[test]
1448 fn test_batch_scale_f64() {
1449 let result = batch_scale_f64(&[1.0, 2.0, 3.0], 2.0);
1450 assert_eq!(result, vec![2.0, 4.0, 6.0]);
1451 }
1452
1453 #[test]
1454 fn test_batch_scale_offset_f64() {
1455 let result = batch_scale_offset_f64(&[0.0, 0.5, 1.0], 100.0, 100.0);
1456 assert_eq!(result, vec![100.0, 150.0, 200.0]);
1457 }
1458
1459 #[test]
1460 fn test_batch_variance_f64_empty() {
1461 assert_eq!(batch_variance_f64(&[]), 0.0);
1462 }
1463
1464 #[test]
1465 fn test_batch_variance_f64_single() {
1466 assert_eq!(batch_variance_f64(&[42.0]), 0.0);
1467 }
1468
1469 #[test]
1470 fn test_batch_variance_f64() {
1471 let values = vec![2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
1472 let variance = batch_variance_f64(&values);
1473 assert!((variance - 4.0).abs() < 0.001);
1474 }
1475
1476 #[test]
1477 fn test_batch_stddev_f64() {
1478 let values = vec![2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
1479 let stddev = batch_stddev_f64(&values);
1480 assert!((stddev - 2.0).abs() < 0.001);
1481 }
1482
1483 #[test]
1484 fn test_weighted_sum_f64_small() {
1485 let values = vec![1.0, 2.0];
1486 let weights = vec![0.5, 0.5];
1487 assert_eq!(weighted_sum_f64(&values, &weights), 1.5);
1488 }
1489
1490 #[test]
1491 fn test_weighted_sum_f64_large() {
1492 let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
1493 let weights = vec![1.0; 8];
1494 assert_eq!(weighted_sum_f64(&values, &weights), 36.0);
1495 }
1496
1497 #[test]
1498 fn test_percentile_sorted_f64_empty() {
1499 assert_eq!(percentile_sorted_f64(&[], 0.5), 0.0);
1500 }
1501
1502 #[test]
1503 fn test_percentile_sorted_f64_single() {
1504 assert_eq!(percentile_sorted_f64(&[42.0], 0.5), 42.0);
1505 }
1506
1507 #[test]
1508 fn test_percentile_sorted_f64_median() {
1509 let sorted = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1510 assert_eq!(percentile_sorted_f64(&sorted, 0.5), 3.0);
1511 }
1512
1513 #[test]
1514 fn test_percentile_sorted_f64_quartiles() {
1515 let sorted = vec![1.0, 2.0, 3.0, 4.0, 5.0];
1516 assert_eq!(percentile_sorted_f64(&sorted, 0.0), 1.0);
1517 assert_eq!(percentile_sorted_f64(&sorted, 1.0), 5.0);
1518 assert_eq!(percentile_sorted_f64(&sorted, 0.25), 2.0);
1519 assert_eq!(percentile_sorted_f64(&sorted, 0.75), 4.0);
1520 }
1521
1522 #[test]
1523 fn test_histogram_f64_empty() {
1524 assert_eq!(histogram_f64(&[], 0.0, 10.0, 5), vec![0; 5]);
1525 }
1526
1527 #[test]
1528 fn test_histogram_f64() {
1529 let values = vec![0.1, 0.5, 0.9, 1.5, 2.5, 3.5, 4.5];
1530 let counts = histogram_f64(&values, 0.0, 5.0, 5);
1531 assert_eq!(counts, vec![3, 1, 1, 1, 1]);
1532 }
1533
1534 #[test]
1535 fn test_histogram_f64_edge_values() {
1536 let values = vec![0.0, 1.0, 2.0, 3.0, 4.0, 4.999];
1538 let counts = histogram_f64(&values, 0.0, 5.0, 5);
1539 assert_eq!(counts, vec![1, 1, 1, 1, 2]); }
1541
1542 #[test]
1543 fn test_histogram_f64_uniform() {
1544 let values: Vec<f64> = (0..100).map(f64::from).collect();
1546 let counts = histogram_f64(&values, 0.0, 100.0, 10);
1547 assert!(counts.iter().all(|&c| c == 10));
1549 }
1550 }
1551}