use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, U1};
use rand::{Rng, RngCore};
use crate::binary_string::BinaryString;
pub trait Bounded<D> {
type Item;
fn is_in_bounds(&self, obj: &Self::Item) -> bool;
fn bound(&self, obj: Self::Item) -> Self::Item;
fn next_random(&self, size: D, rand: &mut dyn RngCore) -> Self::Item;
}
pub struct BoundedBinaryString<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
min: Option<BinaryString<D>>,
max: Option<BinaryString<D>>,
}
impl<D> BoundedBinaryString<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
pub fn unbounded() -> Self {
Self {
min: None,
max: None,
}
}
}
impl<D> Bounded<D> for BoundedBinaryString<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
type Item = BinaryString<D>;
fn is_in_bounds(&self, obj: &Self::Item) -> bool {
if let Some(min) = &self.min {
if obj < min {
return false;
}
}
if let Some(max) = &self.max {
if obj > max {
return false;
}
}
return true;
}
fn bound(&self, obj: Self::Item) -> Self::Item {
if let Some(min) = &self.min {
if &obj < min {
return min.clone();
}
}
if let Some(max) = &self.max {
if &obj > max {
return max.clone();
}
}
obj
}
fn next_random(&self, size: D, rand: &mut dyn RngCore) -> Self::Item {
// TODO... this is very suboptimal, favoring the max, min.
self.bound(
BinaryString::from_ovector(
OVector::<i8, D>::from_fn_generic(size, U1, |_, _| rand.random_range(0..=1))
))
}
}
pub struct BoundedOVector<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
min_max: OVector<(f64, f64), D>
}
impl<D> BoundedOVector<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
pub fn new(min: OVector<f64, D>, max: OVector<f64, D>) -> Self {
Self {
min_max: min.zip_map(&max, |min, max| (min, max))
}
}
}
impl<D> Bounded<D> for BoundedOVector<D>
where
D: Dim,
DefaultAllocator: Allocator<D>
{
type Item = OVector<f64, D>;
fn is_in_bounds(&self, obj: &Self::Item) -> bool {
obj.iter()
.zip(self.min_max.iter())
.all(|(&c, &(min, max))| c <= max && c >= min)
}
fn bound(&self, mut obj: Self::Item) -> Self::Item {
obj
.zip_apply(&self.min_max, |c, (min, max)| *c = c.clamp(min, max));
obj
}
fn next_random(&self, _: D, rand: &mut dyn RngCore) -> Self::Item {
self.min_max.map(|(min, max)| rand.random_range(min..=max))
}
}