use core::convert::TryFrom;
use std::f64::EPSILON;
use derive_more::{Display, Into};
use derive_num_bounded::{
derive_from_str_from_try_into, derive_into_inner, derive_new_from_bounded_float,
derive_new_from_lower_bounded, derive_try_from_from_new,
};
use num_traits::bounds::{LowerBounded, UpperBounded};
use rand::distributions::Bernoulli;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, PartialOrd, Ord, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "usize"))]
#[cfg_attr(feature = "serde", serde(try_from = "usize"))]
pub struct NumSamples(usize);
impl Default for NumSamples {
fn default() -> Self {
Self(20)
}
}
impl LowerBounded for NumSamples {
fn min_value() -> Self {
Self(2)
}
}
derive_new_from_lower_bounded!(NumSamples(usize));
derive_into_inner!(NumSamples(usize));
derive_try_from_from_new!(NumSamples(usize));
derive_from_str_from_try_into!(NumSamples(usize));
#[derive(Clone, Copy, Debug, Display, PartialEq, PartialOrd, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "f64"))]
#[cfg_attr(feature = "serde", serde(try_from = "f64"))]
pub struct AdjustRate(f64);
impl Default for AdjustRate {
fn default() -> Self {
Self(0.1)
}
}
impl LowerBounded for AdjustRate {
fn min_value() -> Self {
Self(EPSILON)
}
}
impl UpperBounded for AdjustRate {
fn max_value() -> Self {
Self(1.)
}
}
derive_new_from_bounded_float!(AdjustRate(f64));
derive_into_inner!(AdjustRate(f64));
derive_try_from_from_new!(AdjustRate(f64));
derive_from_str_from_try_into!(AdjustRate(f64));
impl Eq for AdjustRate {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for AdjustRate {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { self.partial_cmp(other).unwrap_unchecked() }
}
}
#[derive(Clone, Copy, Debug, Display, PartialEq, PartialOrd, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "f64"))]
#[cfg_attr(feature = "serde", serde(try_from = "f64"))]
pub struct MutationChance(f64);
impl MutationChance {
pub fn default_for(num_bits: usize) -> Self {
if num_bits == 0 {
Self(1.)
} else {
Self(1. / num_bits as f64)
}
}
}
impl LowerBounded for MutationChance {
fn min_value() -> Self {
Self(0.)
}
}
impl UpperBounded for MutationChance {
fn max_value() -> Self {
Self(1.)
}
}
impl From<MutationChance> for Bernoulli {
fn from(x: MutationChance) -> Self {
Bernoulli::new(x.into()).unwrap()
}
}
derive_new_from_bounded_float!(MutationChance(f64));
derive_into_inner!(MutationChance(f64));
derive_try_from_from_new!(MutationChance(f64));
derive_from_str_from_try_into!(MutationChance(f64));
impl Eq for MutationChance {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for MutationChance {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { self.partial_cmp(other).unwrap_unchecked() }
}
}
impl MutationChance {
pub fn is_zero(&self) -> bool {
self.0 == 0.0
}
}
#[derive(Clone, Copy, Debug, Display, PartialEq, PartialOrd, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "f64"))]
#[cfg_attr(feature = "serde", serde(try_from = "f64"))]
pub struct MutationAdjustRate(f64);
impl Default for MutationAdjustRate {
fn default() -> Self {
Self(0.05)
}
}
impl LowerBounded for MutationAdjustRate {
fn min_value() -> Self {
Self(EPSILON)
}
}
impl UpperBounded for MutationAdjustRate {
fn max_value() -> Self {
Self(1.)
}
}
derive_new_from_bounded_float!(MutationAdjustRate(f64));
derive_into_inner!(MutationAdjustRate(f64));
derive_try_from_from_new!(MutationAdjustRate(f64));
derive_from_str_from_try_into!(MutationAdjustRate(f64));
impl Eq for MutationAdjustRate {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for MutationAdjustRate {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { self.partial_cmp(other).unwrap_unchecked() }
}
}
#[derive(Clone, Copy, Debug, Display, PartialEq, PartialOrd, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "f64"))]
#[cfg_attr(feature = "serde", serde(try_from = "f64"))]
pub struct Probability(f64);
impl Probability {
pub const unsafe fn new_unchecked(x: f64) -> Self {
Self(x)
}
}
impl Default for Probability {
fn default() -> Self {
Self(0.5)
}
}
impl LowerBounded for Probability {
fn min_value() -> Self {
Self(0.)
}
}
impl UpperBounded for Probability {
fn max_value() -> Self {
Self(1.)
}
}
derive_new_from_bounded_float!(Probability(f64));
derive_into_inner!(Probability(f64));
derive_try_from_from_new!(Probability(f64));
derive_from_str_from_try_into!(Probability(f64));
impl Eq for Probability {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Probability {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { self.partial_cmp(other).unwrap_unchecked() }
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "Probability"))]
#[cfg_attr(feature = "serde", serde(try_from = "Probability"))]
pub struct ProbabilityThreshold {
ub: Probability,
lb: Probability,
}
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)]
pub enum InvalidProbabilityThresholdError {
TooLow,
TooHigh,
}
impl ProbabilityThreshold {
pub fn new(value: Probability) -> Result<Self, InvalidProbabilityThresholdError> {
if value < Self::min_value().into() {
Err(InvalidProbabilityThresholdError::TooLow)
} else if value > Self::max_value().into() {
Err(InvalidProbabilityThresholdError::TooHigh)
} else {
Ok(Self {
ub: value,
lb: Probability(1. - f64::from(value)),
})
}
}
pub fn into_inner(self) -> Probability {
self.ub
}
pub fn upper_bound(&self) -> Probability {
self.ub
}
pub fn lower_bound(&self) -> Probability {
self.lb
}
}
impl LowerBounded for ProbabilityThreshold {
fn min_value() -> Self {
Self {
ub: Probability(0.5 + EPSILON),
lb: Probability(0.5 - EPSILON),
}
}
}
impl UpperBounded for ProbabilityThreshold {
fn max_value() -> Self {
Self {
ub: Probability(1. - EPSILON),
lb: Probability(EPSILON),
}
}
}
impl Default for ProbabilityThreshold {
fn default() -> Self {
Self {
ub: Probability(0.75),
lb: Probability(0.25),
}
}
}
impl PartialEq for ProbabilityThreshold {
fn eq(&self, other: &Self) -> bool {
self.ub.eq(&other.ub)
}
}
impl Eq for ProbabilityThreshold {}
impl PartialOrd for ProbabilityThreshold {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.ub.partial_cmp(&other.ub)
}
}
impl Ord for ProbabilityThreshold {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { self.partial_cmp(other).unwrap_unchecked() }
}
}
impl From<ProbabilityThreshold> for Probability {
fn from(x: ProbabilityThreshold) -> Self {
x.ub
}
}
impl TryFrom<Probability> for ProbabilityThreshold {
type Error = InvalidProbabilityThresholdError;
fn try_from(value: Probability) -> Result<Self, Self::Error> {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn num_samples_from_str_returns_correct_result() {
assert_eq!("10".parse::<NumSamples>().unwrap(), NumSamples(10));
}
#[test]
fn adjust_rate_from_str_returns_correct_result() {
assert_eq!("0.2".parse::<AdjustRate>().unwrap(), AdjustRate(0.2));
}
#[test]
fn mutation_chance_from_str_returns_correct_result() {
assert_eq!(
"0.2".parse::<MutationChance>().unwrap(),
MutationChance(0.2)
);
}
#[test]
fn mutation_adjust_rate_from_str_returns_correct_result() {
assert_eq!(
"0.2".parse::<MutationAdjustRate>().unwrap(),
MutationAdjustRate(0.2)
);
}
}