use crate::points::internal_norm;
use crate::points::rotation_2d::rotate_tuple2;
use crate::points::Points;
use integer_sqrt::IntegerSquareRoot;
use rayon::prelude::*;
pub trait PointArrays2<const N: usize> {
type Output;
fn x(&self) -> [f64; N];
fn y(&self) -> [f64; N];
fn with_x(&self, x: [f64; N]) -> Self::Output;
fn with_y(&self, y: [f64; N]) -> Self::Output;
fn magnitude_squared(&self) -> [f64; N];
fn magnitude(&self) -> [f64; N];
fn dot(&self, other: &Self) -> [f64; N];
fn rotate(&self, alpha: &f64) -> Self::Output;
fn unit(&self) -> Self::Output;
fn zero() -> Self::Output;
fn identity() -> Self::Output;
fn i_hat() -> Self::Output;
fn j_hat() -> Self::Output;
}
use std::convert::TryInto;
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
fn vec_to_array<T, const N: usize>(v: Vec<T>) -> [T; N] {
v.try_into()
.unwrap_or_else(|v: Vec<T>| panic!("Expected a Vec of length {} but it was {}", N, v.len()))
}
#[derive(Debug, PartialEq)]
pub struct PointArray2<const N: usize> {
pub x: [f64; N],
pub y: [f64; N],
}
impl<const N: usize> Default for PointArray2<N> {
fn default() -> Self {
Self {
x: [f64::default(); N],
y: [f64::default(); N],
}
}
}
impl<const N: usize> PointArray2<N> {
pub fn new(x: [f64; N], y: [f64; N]) -> Self {
Self { x: x, y: y }
}
}
impl<const N: usize> Points for PointArray2<N> {
type Output = Self;
fn add_p(&self, other: &Self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i] + other.x[i];
op_y[i] = self.y[i] + other.y[i];
}
PointArray2 { x: op_x, y: op_y }
}
fn sub_p(&self, other: &Self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i] - other.x[i];
op_y[i] = self.y[i] - other.y[i];
}
PointArray2 { x: op_x, y: op_y }
}
fn mul_p(&self, other: &Self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i] * other.x[i];
op_y[i] = self.y[i] * other.y[i];
}
PointArray2 { x: op_x, y: op_y }
}
fn div_p(&self, other: &Self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i] / other.x[i];
op_y[i] = self.y[i] / other.y[i];
}
PointArray2 { x: op_x, y: op_y }
}
fn neg_p(&self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = -self.x[i];
op_y[i] = -self.y[i];
}
PointArray2 { x: op_x, y: op_y }
}
fn scale(&self, s: f64) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i] * s;
op_y[i] = self.y[i] * s;
}
PointArray2 { x: op_x, y: op_y }
}
fn round(&self) -> Self {
let mut op_x: [f64; N] = [0.0; N];
let mut op_y: [f64; N] = [0.0; N];
for i in 0..N {
op_x[i] = self.x[i].round();
op_y[i] = self.y[i].round();
}
PointArray2 { x: op_x, y: op_y }
}
}
impl<const N: usize> Add for PointArray2<N> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
self.add_p(&other)
}
}
impl<const N: usize> AddAssign for PointArray2<N> {
fn add_assign(&mut self, other: Self) {
for i in 0..N {
self.x[i] += other.x[i];
self.y[i] += other.y[i];
}
}
}
impl<const N: usize> Sub for PointArray2<N> {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
self.sub_p(&other)
}
}
impl<const N: usize> Mul for PointArray2<N> {
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
self.mul_p(&other)
}
}
impl<const N: usize> Div for PointArray2<N> {
type Output = Self;
fn div(self, other: Self) -> Self::Output {
self.div_p(&other)
}
}
impl<const N: usize> Neg for PointArray2<N> {
type Output = Self;
fn neg(self) -> Self::Output {
self.neg_p()
}
}
impl<const N: usize> PointArrays2<N> for PointArray2<N> {
type Output = Self;
fn x(&self) -> [f64; N] {
self.x
}
fn y(&self) -> [f64; N] {
self.y
}
fn with_x(&self, x: [f64; N]) -> Self::Output {
assert_eq!(x.len(), self.y.len());
PointArray2::new(x, self.y)
}
fn with_y(&self, y: [f64; N]) -> Self::Output {
assert_eq!(y.len(), self.x.len());
PointArray2::new(self.x, y)
}
fn magnitude_squared(&self) -> [f64; N] {
vec_to_array(
self.x
.par_iter()
.zip(self.y.par_iter())
.map(|(x, y)| x * x + y * y)
.collect::<Vec<f64>>(),
)
}
fn magnitude(&self) -> [f64; N] {
vec_to_array(
self.x
.par_iter()
.zip(self.y.par_iter())
.map(|(x, y)| (x * x + y * y).sqrt())
.collect::<Vec<f64>>(),
)
}
fn dot(&self, other: &Self) -> [f64; N] {
vec_to_array(
self.x
.par_iter()
.zip(self.y.par_iter())
.zip(other.x.par_iter())
.zip(other.y.par_iter())
.map(|(((x1, y1), x2), y2)| (x1 * x2 + y1 * y2))
.collect::<Vec<f64>>(),
)
}
fn rotate(&self, alpha: &f64) -> Self::Output {
let (x_rot, y_rot) = self
.x
.par_iter()
.zip(self.y.par_iter())
.map(|(x, y)| rotate_tuple2(&(*x, *y), alpha))
.collect::<(Vec<f64>, Vec<f64>)>();
PointArray2::new(vec_to_array(x_rot), vec_to_array(y_rot))
}
fn unit(&self) -> Self::Output {
let (x_local, y_local) = self
.x
.par_iter()
.zip(self.y.par_iter())
.map(|(x, y)| internal_norm(x, y))
.collect::<(Vec<f64>, Vec<f64>)>();
PointArray2::new(vec_to_array(x_local), vec_to_array(y_local))
}
fn zero() -> Self::Output {
Self {
x: [0.0; N],
y: [0.0; N],
}
}
fn identity() -> Self::Output {
Self {
x: [1.0; N],
y: [1.0; N],
}
}
fn i_hat() -> Self::Output {
Self {
x: [1.0; N],
y: [0.0; N],
}
}
fn j_hat() -> Self::Output {
Self {
x: [0.0; N],
y: [1.0; N],
}
}
}
fn hard_function(x: &f64, y: &f64) -> (f64, f64) {
(x.powi(3).sqrt(), y.powi(3).sqrt())
}
impl<const N: usize> PointArray2<N> {
pub fn gen_point_closure(&self) -> PointArray2<N> {
let (x_local, y_local) = self
.x
.par_iter()
.zip(self.y.par_iter())
.map(|(x, y)| hard_function(x, y))
.collect::<(Vec<f64>, Vec<f64>)>();
PointArray2::new(vec_to_array(x_local), vec_to_array(y_local))
}
}
impl<const N: usize> PointArray2<N> {
pub fn grid2d(x_min: &f64, x_max: &f64, y_min: &f64, y_max: &f64) -> PointArray2<N> {
let num_points: usize = N.integer_sqrt();
let mut x = [0.0; N];
let mut y = [0.0; N];
let mut k: usize = 0;
let step_x = (x_max - x_min) / (num_points - 1) as f64;
let step_y = (y_max - y_min) / (num_points - 1) as f64;
for i in 0..num_points {
for j in 0..num_points {
x[k] = x_min + i as f64 * step_x;
y[k] = y_min + j as f64 * step_y;
k += 1;
}
}
PointArray2::new(x, y)
}
}
pub fn cart_prod_2d<const N: usize>(
x_min: &f64,
x_max: &f64,
y_min: &f64,
y_max: &f64,
) -> (Vec<f64>, Vec<f64>) {
let xs = (0_usize..N).into_par_iter();
let ys = (0_usize..N).into_par_iter();
let num_points: usize = N;
let step_x = (x_max - x_min) / (num_points - 1) as f64;
let step_y = (y_max - y_min) / (num_points - 1) as f64;
let (xx, yy): (Vec<f64>, Vec<f64>) = xs
.clone()
.flat_map(|x| {
ys.clone()
.map(move |y| (x_min + (x as f64) * step_x, y_min + y as f64 * step_y))
})
.unzip();
(xx, yy)
}
pub fn cart_prod_3d<const N: usize>() -> (Vec<f64>, Vec<f64>, Vec<f64>) {
let xs = (0..N).into_par_iter();
let ys = (0..N).into_par_iter();
let zs = (0..N).into_par_iter();
let ys = &ys;
let zs = &zs;
let (xx, (yy, zz)): (Vec<f64>, (Vec<f64>, Vec<f64>)) = xs
.flat_map(move |x| {
ys.clone()
.flat_map(move |y| zs.clone().map(move |z| (x as f64, (y as f64, z as f64))))
})
.unzip();
(xx, yy, zz)
}
#[cfg(test)]
mod tests {
use super::{cart_prod_2d, cart_prod_3d, hard_function, PointArray2, PointArrays2};
use crate::STACK_MAX;
const NUM_ELEM: usize = 100;
#[test]
fn test_default() {
let array = PointArray2::<2>::default();
let const_array: [f64; 2] = [0.0, 0.0];
assert_eq!(array.x, const_array, "Test x");
assert_eq!(array.y, const_array, "Test y");
}
#[test]
fn test_zeros() {
let array = PointArray2::<NUM_ELEM>::default();
let const_array: [f64; NUM_ELEM] = [0.0; NUM_ELEM];
assert_eq!(array.x, const_array, "Test x");
assert_eq!(array.y, const_array, "Test y");
}
#[test]
fn test_new() {
let array = PointArray2::<4>::new([1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]);
let array_x: [f64; 4] = [1.0, 2.0, 3.0, 4.0];
let array_y: [f64; 4] = [5.0, 6.0, 7.0, 8.0];
assert_eq!(array.x, array_x, "Test x");
assert_eq!(array.y, array_y, "Test y");
}
#[test]
fn test_add() {
let first_array = PointArray2::<2>::new([1.0, 2.0], [3.0, 4.0]);
let second_array = PointArray2::<2>::new([5.0, 6.0], [7.0, 8.0]);
let sum_array = first_array + second_array;
let array_x: [f64; 2] = [6.0, 8.0];
let array_y: [f64; 2] = [10.0, 12.0];
assert_eq!(sum_array.x, array_x, "Test x");
assert_eq!(sum_array.y, array_y, "Test y");
}
#[test]
fn test_magnitude_squared() {
let array = PointArray2::<2>::new([1.0, 2.0], [3.0, 4.0]).magnitude_squared();
let mag_array: [f64; 2] = [10.0, 20.0];
assert_eq!(array, mag_array);
}
#[test]
fn test_magnitude() {
let array = PointArray2::<2>::new([1.0, 2.0], [3.0, 4.0]).magnitude();
let mag_array: [f64; 2] = [10.0_f64.sqrt(), 20.0_f64.sqrt()];
assert_eq!(array, mag_array);
}
#[test]
fn test_magnitude_1e4() {
assert!(
NUM_ELEM <= STACK_MAX,
"Warning: Possible stack overflow. Maximum number of elements is {}, {} were allocated",
STACK_MAX,
NUM_ELEM
);
let array = PointArray2::<NUM_ELEM>::new([1.0; NUM_ELEM], [2.0; NUM_ELEM]).magnitude();
let mag_array: [f64; NUM_ELEM] = [5.0_f64.sqrt(); NUM_ELEM];
assert_eq!(array, mag_array);
}
#[test]
fn test_unit() {
assert!(
NUM_ELEM <= STACK_MAX,
"Warning: Possible stack overflow. Maximum number of elements is {}, {} were allocated",
STACK_MAX,
NUM_ELEM
);
let array = PointArray2::<NUM_ELEM>::new([1.0; NUM_ELEM], [2.0; NUM_ELEM]).unit();
let mag_array = PointArray2::<NUM_ELEM>::new(
[1.0 / 5.0_f64.sqrt(); NUM_ELEM],
[2.0 / 5.0_f64.sqrt(); NUM_ELEM],
);
assert_eq!(array, mag_array);
}
#[test]
fn test_dot_product() {
assert!(
NUM_ELEM <= STACK_MAX,
"Warning: Possible stack overflow. Maximum number of elements is {}, {} were allocated",
STACK_MAX,
NUM_ELEM
);
let array_1 = PointArray2::<NUM_ELEM>::new([1.0; NUM_ELEM], [2.0; NUM_ELEM]);
let array_2 = PointArray2::<NUM_ELEM>::new([3.0; NUM_ELEM], [4.0; NUM_ELEM]);
let result = array_1.dot(&array_2);
let mag_array: [f64; NUM_ELEM] = [11.0; NUM_ELEM];
assert_eq!(result, mag_array);
}
#[test]
fn test_rotate_90_small() {
let x = [1.0, -2.0, -3.0, -1.0];
let y = [1.0, 3.0, -2.0, -4.0];
let array = PointArray2::new(x, y).rotate(&90.0_f64.to_radians());
let comp_array = PointArray2::new(
[-0.9999999999999999, -3.0, 1.9999999999999998, 4.0],
[0.9999999999999999, -2.0, -3.0, -0.9999999999999998],
);
assert_eq!(array, comp_array);
}
#[test]
fn test_rotate_90_full() {
const NUM_ELEM: usize = 1000;
assert!(
NUM_ELEM <= STACK_MAX,
"Warning: Possible stack overflow. Maximum number of elements is {}, {} were allocated",
STACK_MAX,
NUM_ELEM
);
let array = PointArray2::<NUM_ELEM>::new([1.0; NUM_ELEM], [1.0; NUM_ELEM])
.rotate(&90.0_f64.to_radians());
let comp_array = PointArray2::<NUM_ELEM>::new(
[-0.9999999999999999; NUM_ELEM],
[0.9999999999999999; NUM_ELEM],
);
assert_eq!(array, comp_array);
}
#[test]
fn test_closure() {
assert!(
NUM_ELEM <= STACK_MAX,
"Warning: Possible stack overflow. Maximum number of elements is {}, {} were allocated",
STACK_MAX,
NUM_ELEM
);
let input_x = 3.0;
let input_y = 2.0;
let (output_x, output_y) = hard_function(&input_x, &input_y);
let closure_array = PointArray2::<NUM_ELEM>::new([input_x; NUM_ELEM], [input_y; NUM_ELEM])
.gen_point_closure();
let mag_array = PointArray2::new([output_x; NUM_ELEM], [output_y; NUM_ELEM]);
assert_eq!(closure_array, mag_array);
}
#[test]
fn test_grid2d() {
let x_min = -1.0;
let x_max = 1.0;
let y_min = -2.0;
let y_max = 2.0;
let points = PointArray2::<9>::grid2d(&x_min, &x_max, &y_min, &y_max);
let comp_points = PointArray2 {
x: [-1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
y: [-2.0, 0.0, 2.0, -2.0, 0.0, 2.0, -2.0, 0.0, 2.0],
};
assert_eq!(points, comp_points);
}
#[test]
fn test_grid2d_large() {
let x_min = -3.0;
let x_max = 3.0;
let y_min = -6.0;
let y_max = 6.0;
let points = PointArray2::<81>::grid2d(&x_min, &x_max, &y_min, &y_max);
let comp_points = PointArray2 {
x: [
-3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.25, -2.25, -2.25, -2.25,
-2.25, -2.25, -2.25, -2.25, -2.25, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5,
-1.5, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75,
1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25,
2.25, 2.25, 2.25, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0,
],
y: [
-6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5,
3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0,
-1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0,
-6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5,
3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0,
-1.5, 0.0, 1.5, 3.0, 4.5, 6.0,
],
};
assert_eq!(points, comp_points);
}
#[test]
fn test_cart_prod_3() {
let x_min = -1.0;
let x_max = 1.0;
let y_min = -2.0;
let y_max = 2.0;
let (x_vec, y_vec) = cart_prod_2d::<3>(&x_min, &x_max, &y_min, &y_max);
let x_comp = vec![-1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0];
let y_comp = vec![-2.0, 0.0, 2.0, -2.0, 0.0, 2.0, -2.0, 0.0, 2.0];
assert_eq!(x_vec, x_comp);
assert_eq!(y_vec, y_comp);
}
#[test]
fn test_cart_prod_9() {
let x_min = -3.0;
let x_max = 3.0;
let y_min = -6.0;
let y_max = 6.0;
let (x_vec, y_vec) = cart_prod_2d::<9>(&x_min, &x_max, &y_min, &y_max);
let x_comp = vec![
-3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -2.25, -2.25, -2.25, -2.25,
-2.25, -2.25, -2.25, -2.25, -2.25, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5,
-1.5, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, -0.75, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75,
1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25,
2.25, 2.25, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0,
];
let y_comp = vec![
-6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0,
4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0,
1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0,
-1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0,
-4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5, 6.0, -6.0, -4.5, -3.0, -1.5, 0.0, 1.5, 3.0, 4.5,
6.0,
];
assert_eq!(x_vec, x_comp);
assert_eq!(y_vec, y_comp);
}
#[test]
fn test_card_3d() {
let (x, y, z) = cart_prod_3d::<3>();
let x_comp = vec![
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
];
let y_comp = [
0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0,
2.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0,
];
let z_comp = [
0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0,
2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0,
];
assert_eq!(x, x_comp);
assert_eq!(y, y_comp);
assert_eq!(z, z_comp);
}
}