1use array_init::{array_init, try_array_init};
2use num_rational::BigRational;
3use num_traits::NumOps;
4use rand::distributions::{Distribution, Standard};
5use rand::Rng;
6use std::cmp::Ordering;
7use std::convert::TryFrom;
8use std::iter::Sum;
9use std::ops::AddAssign;
10use std::ops::Index;
11use std::ops::Mul;
12use std::ops::Neg;
13use std::ops::Sub;
14
15use crate::data::Point;
16use crate::{Orientation, PolygonScalar};
17
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19#[repr(transparent)]
20pub struct Vector<T, const N: usize>(pub [T; N]);
21
22#[derive(Debug, Clone, Copy)]
23pub struct VectorView<'a, T, const N: usize>(pub &'a [T; N]);
24
25impl<T, const N: usize> Distribution<Vector<T, N>> for Standard
26where
27 Standard: Distribution<T>,
28{
29 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vector<T, N> {
30 Vector(array_init(|_| rng.gen()))
31 }
32}
33
34impl<T, const N: usize> Vector<T, N>
35where
36 T: Clone,
37{
38 pub fn qda(&self) -> T {
39 unimplemented!();
40 }
41
42 pub fn squared_magnitude(&self) -> T
43 where
44 T: Sum + AddAssign,
45 T: Sub<T, Output = T> + Mul<Output = T>,
46 {
47 self.0.iter().map(|elt| elt.clone() * elt.clone()).sum()
48 }
49
50 pub fn map<U, F>(&self, f: F) -> Vector<U, N>
51 where
52 T: Clone,
53 F: Fn(T) -> U,
54 {
55 Vector(array_init(|i| f(self.0[i].clone())))
56 }
57
58 pub fn cast<U>(&self) -> Vector<U, N>
59 where
60 T: Clone + Into<U>,
61 {
62 Vector(array_init(|i| self.0[i].clone().into()))
63 }
64}
65
66impl<T, const N: usize> Index<usize> for Vector<T, N> {
67 type Output = T;
68 fn index(&self, index: usize) -> &T {
69 self.0.index(index)
70 }
71}
72
73impl<T, const N: usize> From<Point<T, N>> for Vector<T, N> {
74 fn from(point: Point<T, N>) -> Vector<T, N> {
75 Vector(point.array)
76 }
77}
78
79impl<'a, T, const N: usize> From<&'a Point<T, N>> for &'a Vector<T, N> {
80 fn from(point: &Point<T, N>) -> &Vector<T, N> {
81 unsafe { &*(point as *const Point<T, N> as *const Vector<T, N>) }
82 }
83}
84
85impl<'a, T, const N: usize> From<&'a Point<T, N>> for VectorView<'a, T, N> {
86 fn from(point: &Point<T, N>) -> VectorView<'_, T, N> {
87 VectorView(&point.array)
88 }
89}
90
91impl<const N: usize> TryFrom<Vector<f64, N>> for Vector<BigRational, N> {
92 type Error = ();
93 fn try_from(point: Vector<f64, N>) -> Result<Vector<BigRational, N>, ()> {
94 Ok(Vector(try_array_init(|i| {
95 BigRational::from_float(point[i]).ok_or(())
96 })?))
97 }
98}
99
100impl<T> Vector<T, 2> {
101 pub fn unit_right() -> Vector<T, 2>
103 where
104 T: PolygonScalar,
105 {
106 Vector([T::from_constant(1), T::from_constant(0)])
107 }
108
109 pub fn ccw_cmp_around(&self, p: &Vector<T, 2>, q: &Vector<T, 2>) -> Ordering
110 where
111 T: PolygonScalar,
112 {
113 self.ccw_cmp_around_with(&Vector([T::from_constant(1), T::from_constant(0)]), p, q)
114 }
115 pub fn ccw_cmp_around_with(
116 &self,
117 z: &Vector<T, 2>,
118 p: &Vector<T, 2>,
119 q: &Vector<T, 2>,
120 ) -> Ordering
121 where
122 T: PolygonScalar,
123 {
124 Orientation::ccw_cmp_around_with(z, &self.0, &p.0, &q.0)
125 }
126
127 pub fn sort_around(pts: &mut [Vector<T, 2>])
130 where
131 T: PolygonScalar,
132 {
133 let origin = [T::from_constant(0), T::from_constant(0)];
134 pts.sort_unstable_by(|a, b| {
135 Orientation::ccw_cmp_around_with(
136 &Vector([T::from_constant(1), T::from_constant(0)]),
137 &origin,
138 &a.0,
139 &b.0,
140 )
141 })
142 }
144
145 pub fn cmp_along(&self, p: &Point<T, 2>, q: &Point<T, 2>) -> Ordering
146 where
147 T: PolygonScalar,
148 {
149 match Orientation::along_perp_vector(&p.array, self, &q.array) {
151 Orientation::CounterClockWise => Ordering::Greater,
152 Orientation::ClockWise => Ordering::Less,
153 Orientation::CoLinear => Ordering::Equal,
154 }
155 }
156}
157
158mod add;
159mod div;
160mod mul;
161mod sub;
162
163impl<T, const N: usize> Sum for Vector<T, N>
164where
165 T: NumOps + AddAssign + Clone + Sum,
166{
167 fn sum<I>(iter: I) -> Vector<T, N>
168 where
169 I: Iterator<Item = Vector<T, N>>,
170 {
171 let mut acc = Vector(array_init(|_| std::iter::empty().sum()));
172 for vec in iter {
173 acc += vec;
174 }
175 acc
176 }
177}
178
179impl<T, const N: usize> Neg for Vector<T, N>
180where
181 T: NumOps + Neg<Output = T> + Clone,
182{
183 type Output = Self;
184 fn neg(self) -> Self {
185 Vector(array_init(|i| self.0.index(i).clone().neg()))
186 }
187}
188
189#[cfg(test)]
190#[cfg(not(tarpaulin_include))]
191mod tests {
192 use super::*;
193
194 use proptest::prelude::*;
195 use test_strategy::proptest;
196
197 #[test]
198 fn unit_1() {
199 let v = Vector([72i8, -113]);
200 let p1 = Point { array: [-84, 93] };
201 let p2 = Point { array: [114, -71] };
202 v.cmp_along(&p1, &p2);
203 }
204
205 #[proptest]
206 fn cmp_along_fuzz(v: Vector<i8, 2>, p1: Point<i8, 2>, p2: Point<i8, 2>) {
207 v.cmp_along(&p1, &p2);
208 }
209
210 #[proptest]
211 fn cmp_along_prop_x(p1: Point<i8, 2>, p2: Point<i8, 2>) {
212 let v = Vector([1, 0]);
213 prop_assert_eq!(v.cmp_along(&p1, &p2), p1.x_coord().cmp(p2.x_coord()));
214 }
215
216 #[proptest]
217 fn cmp_along_prop_y(p1: Point<i8, 2>, p2: Point<i8, 2>) {
218 let v = Vector([0, 1]);
219 prop_assert_eq!(v.cmp_along(&p1, &p2), p1.y_coord().cmp(p2.y_coord()));
220 }
221}