use crate::allocator::Allocator;
use crate::{Const, DefaultAllocator, Dim, DimName, Dyn, OMatrix, Scalar, U1};
use proptest::arbitrary::Arbitrary;
use proptest::collection::vec;
use proptest::strategy::{BoxedStrategy, Just, NewTree, Strategy, ValueTree};
use proptest::test_runner::TestRunner;
use std::ops::RangeInclusive;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct MatrixParameters<NParameters, R, C> {
pub rows: DimRange<R>,
pub cols: DimRange<C>,
pub value_parameters: NParameters,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DimRange<D = Dyn>(RangeInclusive<D>);
impl<D: Dim> DimRange<D> {
pub fn lower_bound(&self) -> D {
*self.0.start()
}
pub fn upper_bound(&self) -> D {
*self.0.end()
}
}
impl<D: Dim> From<D> for DimRange<D> {
fn from(dim: D) -> Self {
DimRange(dim..=dim)
}
}
impl<D: Dim> From<RangeInclusive<D>> for DimRange<D> {
fn from(range: RangeInclusive<D>) -> Self {
DimRange(range)
}
}
impl From<RangeInclusive<usize>> for DimRange<Dyn> {
fn from(range: RangeInclusive<usize>) -> Self {
DimRange::from(Dyn(*range.start())..=Dyn(*range.end()))
}
}
impl<D: Dim> DimRange<D> {
pub fn to_range_inclusive(&self) -> RangeInclusive<usize> {
self.lower_bound().value()..=self.upper_bound().value()
}
}
impl From<usize> for DimRange<Dyn> {
fn from(dim: usize) -> Self {
DimRange::from(Dyn(dim))
}
}
fn dynamic_dim_range() -> DimRange<Dyn> {
DimRange::from(0..=6)
}
pub fn matrix<R, C, ScalarStrategy>(
value_strategy: ScalarStrategy,
rows: impl Into<DimRange<R>>,
cols: impl Into<DimRange<C>>,
) -> MatrixStrategy<ScalarStrategy, R, C>
where
ScalarStrategy: Strategy + Clone + 'static,
ScalarStrategy::Value: Scalar,
R: Dim,
C: Dim,
DefaultAllocator: Allocator<R, C>,
{
matrix_(value_strategy, rows.into(), cols.into())
}
fn matrix_<R, C, ScalarStrategy>(
value_strategy: ScalarStrategy,
rows: DimRange<R>,
cols: DimRange<C>,
) -> MatrixStrategy<ScalarStrategy, R, C>
where
ScalarStrategy: Strategy + Clone + 'static,
ScalarStrategy::Value: Scalar,
R: Dim,
C: Dim,
DefaultAllocator: Allocator<R, C>,
{
let nrows = rows.lower_bound().value()..=rows.upper_bound().value();
let ncols = cols.lower_bound().value()..=cols.upper_bound().value();
let strategy = nrows
.prop_flat_map(move |nrows| (Just(nrows), ncols.clone()))
.prop_flat_map(move |(nrows, ncols)| {
(
Just(nrows),
Just(ncols),
vec(value_strategy.clone(), nrows * ncols),
)
})
.prop_map(|(nrows, ncols, values)| {
OMatrix::from_iterator_generic(R::from_usize(nrows), C::from_usize(ncols), values)
})
.boxed();
MatrixStrategy { strategy }
}
pub fn vector<D, ScalarStrategy>(
value_strategy: ScalarStrategy,
length: impl Into<DimRange<D>>,
) -> MatrixStrategy<ScalarStrategy, D, U1>
where
ScalarStrategy: Strategy + Clone + 'static,
ScalarStrategy::Value: Scalar,
D: Dim,
DefaultAllocator: Allocator<D>,
{
matrix_(value_strategy, length.into(), Const::<1>.into())
}
impl<NParameters, R, C> Default for MatrixParameters<NParameters, R, C>
where
NParameters: Default,
R: DimName,
C: DimName,
{
fn default() -> Self {
Self {
rows: DimRange::from(R::name()),
cols: DimRange::from(C::name()),
value_parameters: NParameters::default(),
}
}
}
impl<NParameters, R> Default for MatrixParameters<NParameters, R, Dyn>
where
NParameters: Default,
R: DimName,
{
fn default() -> Self {
Self {
rows: DimRange::from(R::name()),
cols: dynamic_dim_range(),
value_parameters: NParameters::default(),
}
}
}
impl<NParameters, C> Default for MatrixParameters<NParameters, Dyn, C>
where
NParameters: Default,
C: DimName,
{
fn default() -> Self {
Self {
rows: dynamic_dim_range(),
cols: DimRange::from(C::name()),
value_parameters: NParameters::default(),
}
}
}
impl<NParameters> Default for MatrixParameters<NParameters, Dyn, Dyn>
where
NParameters: Default,
{
fn default() -> Self {
Self {
rows: dynamic_dim_range(),
cols: dynamic_dim_range(),
value_parameters: NParameters::default(),
}
}
}
impl<T, R, C> Arbitrary for OMatrix<T, R, C>
where
T: Scalar + Arbitrary,
<T as Arbitrary>::Strategy: Clone,
R: Dim,
C: Dim,
MatrixParameters<T::Parameters, R, C>: Default,
DefaultAllocator: Allocator<R, C>,
{
type Parameters = MatrixParameters<T::Parameters, R, C>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
let value_strategy = T::arbitrary_with(args.value_parameters);
matrix(value_strategy, args.rows, args.cols)
}
type Strategy = MatrixStrategy<T::Strategy, R, C>;
}
#[derive(Debug, Clone)]
pub struct MatrixStrategy<NStrategy, R: Dim, C: Dim>
where
NStrategy: Strategy,
NStrategy::Value: Scalar,
DefaultAllocator: Allocator<R, C>,
{
strategy: BoxedStrategy<OMatrix<NStrategy::Value, R, C>>,
}
impl<NStrategy, R, C> Strategy for MatrixStrategy<NStrategy, R, C>
where
NStrategy: Strategy,
NStrategy::Value: Scalar,
R: Dim,
C: Dim,
DefaultAllocator: Allocator<R, C>,
{
type Tree = MatrixValueTree<NStrategy::Value, R, C>;
type Value = OMatrix<NStrategy::Value, R, C>;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
let underlying_tree = self.strategy.new_tree(runner)?;
Ok(MatrixValueTree {
value_tree: underlying_tree,
})
}
}
pub struct MatrixValueTree<T, R, C>
where
T: Scalar,
R: Dim,
C: Dim,
DefaultAllocator: Allocator<R, C>,
{
value_tree: Box<dyn ValueTree<Value = OMatrix<T, R, C>>>,
}
impl<T, R, C> ValueTree for MatrixValueTree<T, R, C>
where
T: Scalar,
R: Dim,
C: Dim,
DefaultAllocator: Allocator<R, C>,
{
type Value = OMatrix<T, R, C>;
fn current(&self) -> Self::Value {
self.value_tree.current()
}
fn simplify(&mut self) -> bool {
self.value_tree.simplify()
}
fn complicate(&mut self) -> bool {
self.value_tree.complicate()
}
}