use std::marker::PhantomData;
use std::mem;
use crate::core::shared::{Abs, DataValue, NdVector, Vector};
use super::{FinalMetadata, PredictionTransform};
use super::geom::*;
pub struct OrthogonalTransform<Data> {
out: Vec<NdVector<2,f64>>,
metadata: Vec<bool>,
_marker: PhantomData<Data>,
}
impl<Data> OrthogonalTransform<Data>
where
Data: Vector,
Data::Component: DataValue
{
pub fn new() -> Self {
Self {
out: Vec::new(),
metadata: Vec::new(),
_marker: PhantomData,
}
}
}
impl<Data> PredictionTransform for OrthogonalTransform<Data>
where Data: Vector,
Data::Component: DataValue
{
const ID: usize = 5;
type Data = Data;
type Correction = NdVector<2,f64>;
type Metadata = bool;
fn map(_orig: Self::Data, _pred: Self::Data, _metadata: Self::Metadata) -> Self::Correction {
unimplemented!()
}
fn map_with_tentative_metadata(&mut self, orig: Self::Data, pred: Self::Data) {
let one = Data::Component::one();
let zero = Data::Component::zero();
let pred_norm_squared = pred.dot(pred);
let ref_on_pred_perp = if unsafe{ pred.get_unchecked(1).abs() } > one/Data::Component::from_u64(10) {
self.metadata.push(true);
unsafe {
let mut out = pred * (*pred.get_unchecked(0) / pred_norm_squared);
*out.get_unchecked_mut(0) += one;
out
}
} else {
self.metadata.push(false);
unsafe {
let mut out = pred * (*pred.get_unchecked(1) / pred_norm_squared);
*out.get_unchecked_mut(1) += one;
out
}
};
let pred_norm_squared = pred_norm_squared.to_f64();
let pred_cross_orig = pred.cross(orig);
debug_assert!(pred_cross_orig.dot(pred).abs() < one/Data::Component::from_u64(1_000_000));
debug_assert!(ref_on_pred_perp.dot(pred).abs() < one/Data::Component::from_u64(1_000_000));
let ref_on_pred_perp_norm_squared = ref_on_pred_perp.dot(ref_on_pred_perp).to_f64();
let difference = ref_on_pred_perp-pred_cross_orig;
let difference_norm_squared = difference.dot(difference).to_f64();
let sign = if pred.dot(ref_on_pred_perp.cross(pred_cross_orig)) > zero { 1_f64 } else { -1_f64 };
let first_angle = sign * (1_f64+ref_on_pred_perp_norm_squared-difference_norm_squared/2_f64/ref_on_pred_perp_norm_squared.sqrt()).acos();
let orig_norm_squared = orig.dot(orig).to_f64();
let difference = pred - orig;
let difference_norm_squared = difference.dot(difference).to_f64();
let second_angle = (pred_norm_squared+orig_norm_squared-difference_norm_squared/(2_f64*pred_norm_squared.sqrt()*orig_norm_squared.sqrt())).acos();
self.out.push(NdVector::from(
[first_angle, second_angle]
));
}
fn inverse(&mut self, pred: Self::Data, crr: Self::Correction, metadata: Self::Metadata) -> Self::Data {
let one = Data::Component::one();
let pred_norm_squared = pred.dot(pred);
let ref_on_pred_perp = if metadata {
unsafe {
let mut out = pred * (*pred.get_unchecked(0) / pred_norm_squared);
*out.get_unchecked_mut(0) += one;
out
}
} else {
unsafe {
let mut out = pred * (*pred.get_unchecked(1) / pred_norm_squared);
*out.get_unchecked_mut(1) += one;
out
}
};
let mut pred_cross_orig = Data::zero();
let rotation = rotation_matrix_from(pred, unsafe{ *crr.get_unchecked(0) });
unsafe {
*pred_cross_orig.get_unchecked_mut(0) = rotation.get_unchecked(0).dot(ref_on_pred_perp);
*pred_cross_orig.get_unchecked_mut(1) = rotation.get_unchecked(1).dot(ref_on_pred_perp);
*pred_cross_orig.get_unchecked_mut(2) = rotation.get_unchecked(2).dot(ref_on_pred_perp);
};
let rotation = rotation_matrix_from(pred_cross_orig, unsafe{ *crr.get_unchecked(1) });
let mut orig = Data::zero();
unsafe {
*orig.get_unchecked_mut(0) = rotation.get_unchecked(0).dot(pred);
*orig.get_unchecked_mut(1) = rotation.get_unchecked(1).dot(pred);
*orig.get_unchecked_mut(2) = rotation.get_unchecked(2).dot(pred);
};
orig
}
fn squeeze(&mut self) -> (FinalMetadata<Self::Metadata>, Vec<Self::Correction>) {
if self.metadata.iter().all(|&v|v) {
(
FinalMetadata::Global(self.metadata.pop().unwrap()),
mem::take(&mut self.out)
)
} else {
(
FinalMetadata::Local(std::mem::take(&mut self.metadata)),
mem::take(&mut self.out)
)
}
}
}