use core::{fmt, iter, num::NonZero};
use crate::{
iter::{Alternating, Apply, Indices},
side::Side,
};
pub trait StrategyObj: fmt::Display + fmt::Debug {}
impl<S: ?Sized + Strategy> StrategyObj for S {}
pub trait Strategy: fmt::Display + fmt::Debug {
type Iter<'a>: Iterator<Item = Side>
where
Self: 'a;
fn strategy(&self) -> Self::Iter<'_>;
#[inline]
fn apply<I: DoubleEndedIterator>(&self, iter: I) -> Apply<I, Self::Iter<'_>> {
Apply::new(iter, self.strategy())
}
#[inline]
fn indexed_strategy(&self) -> Indices<Self::Iter<'_>> {
Indices::new(self.strategy())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Todo;
impl fmt::Display for Todo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("unimplemented")
}
}
impl Strategy for Todo {
type Iter<'a> = crate::iter::Todo;
fn strategy(&self) -> crate::iter::Todo {
crate::iter::Todo
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Always(pub Side);
impl fmt::Display for Always {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "always {}", self.0)
}
}
impl Strategy for Always {
type Iter<'a> = iter::Repeat<Side>;
#[inline]
fn strategy(&self) -> iter::Repeat<Side> {
iter::repeat(self.0)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Alternate {
pub start: Side,
pub period: NonZero<usize>,
}
impl Alternate {
#[inline]
pub fn swap(self) -> Alternate {
Alternate {
start: self.start.swap(),
period: self.period,
}
}
#[inline]
pub fn shrink(self) -> Option<Alternate> {
let period = self.period.get() - 1;
Some(Alternate {
start: self.start,
period: NonZero::new(period)?,
})
}
}
impl fmt::Display for Alternate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"alternating {} to {} every {} iterations",
self.start,
self.start.swap(),
self.period
)
}
}
impl Strategy for Alternate {
type Iter<'a> = Alternating;
#[inline]
fn strategy(&self) -> Self::Iter<'_> {
Alternating::new(self.start, self.period)
}
}
#[cfg_attr(feature = "nightly", doc(cfg(feature = "rand")))]
#[cfg(any(doc, feature = "rand"))]
mod rand {
use core::fmt;
use rand::SeedableRng;
use crate::{iter::Randomly, strategy::Strategy};
pub struct Random<
#[cfg(feature = "rand")] R: rand::SeedableRng,
#[cfg(not(feature = "rand"))] R,
> {
#[cfg(feature = "rand")]
seed: R::Seed,
#[cfg(not(feature = "rand"))]
seed: R,
forward_prob: f64,
}
impl<#[cfg(feature = "rand")] R: rand::SeedableRng, #[cfg(not(feature = "rand"))] R> Random<R> {
pub fn seeded(
#[cfg(feature = "rand")] seed: R::Seed,
#[cfg(not(feature = "rand"))] seed: R,
forward_prob: f64,
) -> Random<R> {
Random { seed, forward_prob }
}
#[allow(clippy::self_named_constructors)]
#[cfg(any(doc, feature = "std"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "std")))]
pub fn random(forward_prob: f64) -> Random<R> {
#[cfg(feature = "rand")]
{
let mut seed = R::Seed::default();
rand::Rng::fill(&mut rand::rng(), seed.as_mut());
Random { seed, forward_prob }
}
#[cfg(not(feature = "rand"))]
{
unimplemented!()
}
}
}
impl<#[cfg(feature = "rand")] R: rand::SeedableRng, #[cfg(not(feature = "rand"))] R> fmt::Debug
for Random<R>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Random")
.field("seed", &self.seed.as_ref())
.field("forward_prob", &self.forward_prob)
.finish()
}
}
impl<#[cfg(feature = "rand")] R: rand::SeedableRng, #[cfg(not(feature = "rand"))] R> Clone
for Random<R>
{
#[inline]
fn clone(&self) -> Self {
#[cfg(feature = "rand")]
{
let mut seed = R::Seed::default();
seed.as_mut().copy_from_slice(self.seed.as_ref());
Random {
seed,
forward_prob: self.forward_prob,
}
}
#[cfg(not(feature = "rand"))]
{
unimplemented!()
}
}
}
impl<R: SeedableRng> fmt::Display for Random<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "random with seed 0x")?;
for b in self.seed.as_ref() {
write!(f, "{b:02x}")?;
}
Ok(())
}
}
impl<
#[cfg(feature = "rand")] R: rand::SeedableRng + rand::RngCore,
#[cfg(not(feature = "rand"))] R,
> Strategy for Random<R>
{
type Iter<'a>
= Randomly<R>
where
R: 'a;
#[inline]
fn strategy(&self) -> Self::Iter<'_> {
#[cfg(feature = "rand")]
{
Randomly::new(self.seed.clone(), self.forward_prob)
}
#[cfg(not(feature = "rand"))]
{
unimplemented!()
}
}
}
}
#[cfg_attr(feature = "nightly", doc(cfg(feature = "rand")))]
#[cfg(any(doc, feature = "rand"))]
pub use self::rand::Random;