use crate::dangerous_numbers::DangerousNumber;
use num::{Bounded, NumCast};
use num_traits::{WrappingAdd, WrappingSub};
use rand::Rng;
use rand::distr::StandardUniform;
use rand::prelude::Distribution;
use rand::prelude::SliceRandom;
use std::collections::HashMap;
use std::ops::{Add, BitXor, Sub};
#[derive(Debug)]
pub struct Mutator<R: Rng> {
rng: R,
chances: Vec<bool>,
stored_indexes: HashMap<&'static str, usize>,
fake_rng: bool,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone)]
enum MutatorOperation {
BitFlip,
Flip,
Arithmetic,
}
impl Distribution<MutatorOperation> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> MutatorOperation {
match rng.random_range(0..3) {
0 => MutatorOperation::BitFlip,
1 => MutatorOperation::Flip,
2 => MutatorOperation::Arithmetic,
_ => unreachable!(), }
}
}
impl<R: Rng> Mutator<R> {
pub fn new(rng: R) -> Mutator<R> {
Mutator {
rng,
chances: vec![],
stored_indexes: HashMap::new(),
fake_rng: false,
}
}
pub fn mutate<T>(&mut self, num: &mut T)
where
T: BitXor<Output = T>
+ Add<Output = T>
+ Sub<Output = T>
+ NumCast
+ Bounded
+ Copy
+ WrappingAdd<Output = T>
+ WrappingSub<Output = T>
+ DangerousNumber<T>
+ std::fmt::Debug,
{
#[allow(clippy::eq_op)]
if self.fake_rng {
*num = (*num) ^ (*num);
return;
}
if self.gen_chance(0.001) {
*num = T::select_dangerous_number(&mut self.rng);
return;
}
let operation: MutatorOperation = self.rng.random();
match operation {
MutatorOperation::BitFlip => self.bit_flip(num),
MutatorOperation::Flip => self.flip(num),
MutatorOperation::Arithmetic => self.arithmetic(num),
}
}
fn bit_flip<T>(&mut self, num: &mut T)
where
T: BitXor<Output = T> + Add<Output = T> + Sub<Output = T> + NumCast + Copy,
{
#[allow(clippy::cast_possible_truncation)]
let num_bits = (std::mem::size_of::<T>() * 8) as u8;
let idx: u8 = self.rng.random_range(0..num_bits);
if let Some(cast) = num::cast(1u64 << idx) {
*num = (*num) ^ cast;
}
}
fn flip<T>(&mut self, num: &mut T)
where
T: BitXor<Output = T> + Add<Output = T> + Sub<Output = T> + NumCast + Copy,
{
#[allow(clippy::cast_possible_truncation)]
let num_bits = (std::mem::size_of::<T>() * 8) as u8;
assert!(num_bits <= 64);
let mut potential_bit_indices = [0u8; 64];
for i in 0..num_bits {
potential_bit_indices[i as usize] = i;
}
let (bit_indices, _) = potential_bit_indices[0..num_bits as usize]
.partial_shuffle(&mut self.rng, num_bits as usize);
for idx in bit_indices {
if let Some(cast) = num::cast(1u64 << *idx) {
*num = (*num) ^ cast;
}
}
}
fn arithmetic<T>(&mut self, num: &mut T)
where
T: Add<Output = T>
+ Sub<Output = T>
+ NumCast
+ Copy
+ WrappingAdd<Output = T>
+ WrappingSub<Output = T>,
{
let added_num: i64 = self.rng.random_range(1..=0x10);
if self.rng.random::<bool>() {
if let Some(cast) = num::cast(added_num) {
*num = num.wrapping_add(&cast);
}
} else {
if let Some(cast) = num::cast(added_num) {
*num = num.wrapping_sub(&cast);
}
}
}
pub fn gen_range(&mut self, min: usize, max: usize) -> usize {
self.rng.random_range(min..max)
}
pub fn gen_index(&mut self, key: &'static str, max: usize) -> usize {
if self.stored_indexes.contains_key(key) {
let change = self.gen_chance(0.4);
if let Some(index) = self.stored_indexes.get_mut(key) {
if change {
*index = self.rng.random_range(0..max);
}
return *index;
}
}
let index = self.rng.random_range(0..max);
self.stored_indexes.insert(key, index);
index
}
pub fn gen_chance(&mut self, chance_percentage: f64) -> bool {
let chance = {
if chance_percentage <= 0.0 {
false
} else if chance_percentage >= 1.0 {
true
} else {
self.rng.random_bool(chance_percentage)
}
};
self.chances.push(chance);
chance
}
}