#![cfg(not(tarpaulin_include))]
use crate::data::{
Direction_, LineSoS_, Line_, Point, PointId, Polygon, PolygonConvex, Triangle, Vector,
};
use crate::PolygonScalar;
use array_init::{array_init, try_array_init};
use core::ops::Range;
use num::BigRational;
use num_bigint::BigInt;
use num_traits::*;
use ordered_float::NotNan;
use proptest::arbitrary::*;
use proptest::collection::*;
use proptest::prelude::*;
use proptest::strategy::*;
use proptest::test_runner::*;
use rand::distributions::uniform::SampleUniform;
use rand::SeedableRng;
use std::collections::BTreeSet;
use std::convert::TryInto;
use std::fmt::Debug;
use std::ops::Index;
use std::ops::IndexMut;
type Mapped<I, O> = Map<StrategyFor<I>, fn(_: I) -> O>;
type FilterMapped<I, O> = FilterMap<StrategyFor<I>, fn(_: I) -> Option<O>>;
pub struct ShrinkablePolygon<T: ValueTree> {
points: Vec<ShrinkablePoint<T, 2>>,
cut: Vec<PointId>,
cut_prev: Option<PointId>,
uncut: Vec<PointId>,
prev_shrink: Option<usize>,
next_shrink: usize,
done: bool,
}
impl<T: ValueTree> ShrinkablePolygon<T>
where
<T as ValueTree>::Value: Clone + PolygonScalar,
{
fn new(points: Vec<ShrinkablePoint<T, 2>>) -> Self {
ShrinkablePolygon {
points: points,
cut: Vec::new(),
cut_prev: None,
uncut: Vec::new(),
prev_shrink: None,
next_shrink: 0,
done: false,
}
}
fn polygon(&self) -> Polygon<<T as ValueTree>::Value> {
let mut poly = Polygon::new_unchecked(self.points.iter().map(|pt| pt.current()).collect());
poly.rings[0].retain(|pid| !self.cut.contains(pid));
poly
}
}
impl<T: ValueTree> ValueTree for ShrinkablePolygon<T>
where
<T as ValueTree>::Value: Clone + PolygonScalar,
{
type Value = Polygon<<T as ValueTree>::Value>;
fn current(&self) -> Self::Value {
let mut poly = Polygon::new_unchecked(self.points.iter().map(|pt| pt.current()).collect());
poly.rings[0].retain(|pid| !self.cut.contains(pid));
Polygon::new_unchecked(
poly.rings[0]
.iter()
.map(|pid| poly.points[pid.usize()].clone())
.collect(),
)
}
fn simplify(&mut self) -> bool {
if self.cut_prev.is_some() {
self.uncut.clear();
self.cut_prev = None;
}
let poly = self.polygon();
if poly.rings[0].len() > 3 {
for pt in poly.iter_boundary() {
if !self.uncut.contains(&pt.point_id()) && pt.is_ear() {
self.cut.push(pt.point_id());
self.cut_prev = Some(pt.point_id());
return true;
}
}
}
let shrink_points = false;
if shrink_points {
while self.next_shrink < poly.rings[0].len()
&& !self.points[poly.rings[0][self.next_shrink].usize()].simplify()
{
self.next_shrink += 1;
}
if self.next_shrink < poly.rings[0].len() {
while self.polygon().validate().is_err() {
if !self.points[poly.rings[0][self.next_shrink].usize()].complicate() {
self.next_shrink = usize::MAX;
return true;
}
}
self.prev_shrink = Some(self.next_shrink);
return true;
} else {
self.done = true;
return false;
}
} else {
self.done = true;
return false;
}
}
fn complicate(&mut self) -> bool {
if self.done {
return false;
}
if let Some(cut) = self.cut_prev {
self.cut.pop();
self.cut_prev = None;
self.uncut.push(cut);
return true;
}
if let Some(idx) = self.prev_shrink {
let key = self.polygon().rings[0][idx].usize();
self.points[key].complicate();
while self.polygon().validate().is_err() {
if !self.points[key].complicate() {
self.done = true;
return true;
}
}
self.prev_shrink = None;
}
return false;
}
}
#[derive(Debug, Clone)]
pub struct PolygonStrat<T>(T, Range<usize>);
impl<T> Strategy for PolygonStrat<T>
where
T: Clone + std::fmt::Debug + Strategy,
T::Value: Clone + PolygonScalar,
T::Tree: Clone,
{
type Tree = ShrinkablePolygon<T::Tree>;
type Value = Polygon<T::Value>;
fn new_tree(&self, runner: &mut TestRunner) -> Result<Self::Tree, Reason> {
let n = runner.rng().gen_range(self.1.clone()).max(3);
loop {
let mut points = Vec::with_capacity(n);
let mut set = BTreeSet::new();
let mut actual = Vec::new();
while actual.len() < n {
let pt = Point::new([self.0.clone(), self.0.clone()]).new_tree(runner)?;
let current = pt.current();
if set.insert(current.clone()) {
points.push(pt);
actual.push(current)
}
}
let rng = &mut rand::rngs::SmallRng::seed_from_u64(0);
match crate::algorithms::polygonization::two_opt_moves(actual, rng)
.map_err(|err| err.to_string())
{
Err(_err) => continue,
Ok(poly) => {
assert_eq!(poly.rings[0].len(), points.len());
let mut new_points = Vec::new();
for &pid in poly.rings[0].iter() {
new_points.push(points[pid.usize()].clone());
}
return Ok(ShrinkablePolygon::new(new_points));
}
}
}
}
}
impl<T: Arbitrary> Arbitrary for Polygon<T>
where
T::Strategy: Clone,
<<T as Arbitrary>::Strategy as Strategy>::Tree: Clone,
T: PolygonScalar,
{
type Strategy = PolygonStrat<T::Strategy>;
type Parameters = (Range<usize>, T::Parameters);
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
let (size_range, t_params) = params;
if size_range.is_empty() {
PolygonStrat(T::arbitrary_with(t_params), 3..50)
} else {
PolygonStrat(T::arbitrary_with(t_params), size_range)
}
}
}
pub fn polygon_nn() -> impl Strategy<Value = Polygon<NotNan<f64>>> {
PolygonStrat(
any::<f64>().prop_filter_map("Check for NaN", |pt| rem_float(pt).try_into().ok()),
3..50,
)
}
pub fn polygon_big() -> impl Strategy<Value = Polygon<BigRational>> {
PolygonStrat(
any::<f64>().prop_filter_map("Check for NaN", |pt| BigRational::from_float(pt)),
3..50,
)
}
impl<T> Arbitrary for PolygonConvex<T>
where
T: Bounded + PolygonScalar + SampleUniform + Copy + Into<BigInt>,
{
type Strategy = Map<(Range<usize>, StrategyFor<u64>), fn(_: (usize, u64)) -> PolygonConvex<T>>;
type Parameters = Range<usize>;
fn arbitrary_with(mut range: Self::Parameters) -> Self::Strategy {
if range.is_empty() {
range = 3usize..100;
}
(range, any::<u64>()).prop_map(|(n, seed)| {
let rng = &mut rand::rngs::SmallRng::seed_from_u64(seed);
let p = PolygonConvex::random(n.max(3), rng);
p
})
}
}
#[derive(Clone)]
pub struct ShrinkablePoint<T, const N: usize> {
point: Point<T, N>,
shrink: usize,
prev_shrink: Option<usize>,
}
impl<T, const N: usize> ValueTree for ShrinkablePoint<T, N>
where
T: ValueTree,
{
type Value = Point<<T as ValueTree>::Value, N>;
fn current(&self) -> Point<T::Value, N> {
Point {
array: array_init(|i| self.point.array.index(i).current()),
}
}
fn simplify(&mut self) -> bool {
for ix in self.shrink..N {
if !self.point.array.index_mut(ix).simplify() {
self.shrink = ix + 1;
} else {
self.prev_shrink = Some(ix);
return true;
}
}
false
}
fn complicate(&mut self) -> bool {
match self.prev_shrink {
None => false,
Some(ix) => {
if self.point.array.index_mut(ix).complicate() {
true
} else {
self.prev_shrink = None;
false
}
}
}
}
}
impl<T, const N: usize> Strategy for Point<T, N>
where
T: Clone + Debug + Strategy,
{
type Tree = ShrinkablePoint<T::Tree, N>;
type Value = Point<<T as Strategy>::Value, N>;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
let tree = ShrinkablePoint {
point: Point {
array: try_array_init(|i| self.array.index(i).new_tree(runner))?,
},
shrink: 0,
prev_shrink: None,
};
Ok(tree)
}
}
impl<T: Arbitrary, const N: usize> Arbitrary for Point<T, N>
where
T::Strategy: Clone,
T::Parameters: Clone,
T: Clone,
{
type Strategy = Mapped<Vec<T>, Point<T, N>>;
type Parameters = T::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
vec(any_with::<T>(params), N).prop_map(|vec: Vec<T>| Point {
array: vec.try_into().unwrap(),
})
}
}
impl<T: Arbitrary, const N: usize> Arbitrary for Vector<T, N>
where
T::Strategy: Clone,
T::Parameters: Clone,
T: Clone,
{
type Strategy = Mapped<Point<T, N>, Vector<T, N>>;
type Parameters = T::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
Point::<T, N>::arbitrary_with(params).prop_map(|pt| pt.into())
}
}
impl<T: Arbitrary, const N: usize> Arbitrary for Direction_<T, N>
where
T::Strategy: Clone,
T::Parameters: Clone,
T: Clone,
{
type Strategy = Mapped<(bool, Point<T, N>), Direction_<T, N>>;
type Parameters = T::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
(any::<bool>(), Point::<T, N>::arbitrary_with(params)).prop_map(|(is_pt, pt)| {
if is_pt {
Direction_::Through(pt)
} else {
Direction_::Vector(pt.into())
}
})
}
}
impl<T: Arbitrary, const N: usize> Arbitrary for Line_<T, N>
where
T::Strategy: Clone,
T::Parameters: Clone,
T: Clone,
{
type Strategy = Mapped<(Point<T, N>, Direction_<T, N>), Line_<T, N>>;
type Parameters = T::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
any_with::<(Point<T, N>, Direction_<T, N>)>((params.clone(), params))
.prop_map(|(origin, direction)| Line_ { origin, direction })
}
}
impl<T: Arbitrary, const N: usize> Arbitrary for LineSoS_<T, N>
where
T::Strategy: Clone,
T::Parameters: Clone,
T: Clone,
{
type Strategy = Mapped<Line_<T, N>, LineSoS_<T, N>>;
type Parameters = T::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
any_with::<Line_<T, N>>(params).prop_map(|line| line.into())
}
}
pub fn any_nn<const N: usize>() -> impl Strategy<Value = Point<NotNan<f64>, N>> {
any::<Point<f64, N>>().prop_filter_map("Check for NaN", |pt| pt.map(rem_float).try_into().ok())
}
fn rem_float(f: f64) -> f64 {
let (mantissa, exponent, sign) = f.integer_decode();
((mantissa as f64) * 2f64.powi(exponent as i32 % 250)).copysign(sign as f64)
}
pub fn any_r<const N: usize>() -> impl Strategy<Value = Point<BigInt, N>> {
any::<Point<isize, N>>().prop_map(|pt| pt.cast())
}
impl<T> Arbitrary for Triangle<T>
where
T: Arbitrary + Clone + PolygonScalar,
<T as Arbitrary>::Strategy: Clone,
<T as Arbitrary>::Parameters: Clone,
{
type Strategy = FilterMapped<[Point<T, 2>; 3], Triangle<T>>;
type Parameters = <Point<T, 2> as Arbitrary>::Parameters;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
any_with::<[Point<T, 2>; 3]>(params)
.prop_filter_map("Ensure CCW", |pts| Triangle::new(pts).ok())
}
}