use std::str::FromStr;
#[cfg(test)]
use crate::test_infra::{load_test_file, DataArrOfReals};
use rand::Rng;
#[derive(Debug, Clone, PartialEq)]
pub struct Bounds {
min: f64,
max: f64,
}
impl Bounds {
pub fn new(min: f64, max: f64) -> Self {
Bounds {
min,
max
}
}
pub fn min(&self) -> f64 {
self.min
}
pub fn max(&self) -> f64 {
self.max
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BinaryString {
pub vec: Vec<i8>
}
#[derive(Debug, Clone, PartialEq)]
pub enum BinaryStringConversionError {
DimensionMismatch,
NoBounds
}
impl BinaryString {
pub fn new(vec: Vec<i8>) -> BinaryString {
BinaryString {
vec
}
}
pub fn vec(&self) -> &Vec<i8> {
&self.vec
}
pub fn perturb<TRng>(self: &Self, rng: &mut TRng, p: f64) -> Self
where TRng : Rng
{
BinaryString::new(
self.into_iter()
.map(|c| if rng.random::<f64>() <= p { 1 - *c } else { *c })
.collect::<Vec<i8>>()
)
}
fn to_real_internal<'a, T: DoubleEndedIterator<Item = &'a i8>>(vec: T, len: usize, min: f64, max: f64) -> f64
{
let diff = max - min;
let len = len as i32;
let max_represent_num = 2f64.powi(len) - 1.0;
let represented_num = vec
.rev()
.enumerate()
.map(|(bit, c)| diff * (*c as f64) * 2f64.powi(bit as i32))
.sum::<f64>();
min + (represented_num / max_represent_num)
}
pub fn to_real_single(self: &Self, min: f64, max: f64) -> f64 {
BinaryString::to_real_internal(self.vec.iter(), self.vec.len(), min, max)
}
pub fn to_real(self: &Self, bounds: &Vec<Bounds>) -> Result<Vec<f64>, BinaryStringConversionError> {
if bounds.len() == 0 {
return Err(BinaryStringConversionError::NoBounds);
}
let chunk_size = self.vec.len() / bounds.len();
if self.vec.len() % bounds.len() != 0 {
return Err(BinaryStringConversionError::DimensionMismatch);
}
Ok(self.vec.chunks(chunk_size)
.zip(bounds)
.map(|(chunk, bound)| BinaryString::to_real_internal(chunk.iter(), chunk_size, bound.min(), bound.max()))
.collect::<Vec<f64>>())
}
}
impl FromStr for BinaryString {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let binary_vec: Vec<i8> = s
.chars()
// skip spaces
.filter(|c| *c != ' ')
// Map ones and zeros
.map(|c| match c {
'0' => Ok(0),
'1' => Ok(1),
_ => Err(format!("Invalid binary character: {}", c)),
})
.collect::<Result<Vec<i8>, Self::Err>>()?;
Ok(BinaryString::new(binary_vec))
}
}
impl<'a> IntoIterator for &'a BinaryString {
type Item = &'a i8;
type IntoIter = std::slice::Iter<'a, i8>;
fn into_iter(self) -> Self::IntoIter {
self.vec.iter()
}
}
#[test]
fn test_binary_string_to_real_single() {
assert_eq!(
BinaryString::new(vec![1])
.to_real_single(0.0, 32.0),
32.0
);
assert_eq!(
BinaryString::new(vec![1, 1])
.to_real_single(0.0, 32.0),
32.0
);
assert_eq!(
BinaryString::new(vec![0, 1])
.to_real_single(0.0, 32.0),
32.0 / 3.0
);
assert_eq!(
BinaryString::new(vec![0, 0])
.to_real_single(-16.0, 16.0),
-16.0
);
assert_eq!(
BinaryString::new(vec![0, 0, 0, 0, 1])
.to_real_single(0.0, 31.0),
1.0
);
assert_eq!(
BinaryString::new(vec![1, 1, 1, 1, 1])
.to_real_single(0.0, 31.0),
31.0
);
assert_eq!(
BinaryString::new(vec![0, 0, 0, 1, 0])
.to_real_single(0.0, 31.0),
2.0
);
assert_eq!(
BinaryString::new(vec![1; 512])
.to_real_single(0.0, 31.0),
31.0
);
}
#[cfg(test)]
fn test_binary_string_to_real(file_name: &str, bounds: Vec<Bounds>) {
let data = load_test_file::<i8, DataArrOfReals>(file_name);
for test in data {
let res = BinaryString::new(test.inp)
.to_real(&bounds);
if !test.out.valid {
assert_eq!(
res,
Err(BinaryStringConversionError::DimensionMismatch)
);
}
else {
assert_eq!(
res.unwrap(),
test.out.vec
);
}
}
}
#[test]
fn test_binary_string_to_real_1d_1() {
test_binary_string_to_real(
"tests/Bin2Real_1D_1.txt",
vec![Bounds::new(0.0, 1.0)]
);
}
#[test]
fn test_binary_string_to_real_1d_2() {
test_binary_string_to_real(
"tests/Bin2Real_1D_2.txt",
vec![Bounds::new(0.0, 4095.0)]
);
}
#[test]
fn test_binary_string_to_real_1d_3() {
test_binary_string_to_real(
"tests/Bin2Real_1D_3.txt",
vec![Bounds::new(-5.0, 5.0)]
);
}
#[test]
fn test_binary_string_to_real_2d_1() {
test_binary_string_to_real(
"tests/Bin2Real_2D_1.txt",
vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
);
}
#[test]
fn test_binary_string_to_real_2d_2() {
test_binary_string_to_real(
"tests/Bin2Real_2D_2.txt",
vec![Bounds::new(0.0, 63.0), Bounds::new(-32.0, 31.0)]
);
}
#[test]
fn test_binary_string_to_real_2d_3() {
test_binary_string_to_real(
"tests/Bin2Real_2D_3.txt",
vec![Bounds::new(-5.0, 5.0), Bounds::new(0.0, 10.0)]
);
}
#[test]
fn test_binary_string_to_real_3d_1() {
test_binary_string_to_real(
"tests/Bin2Real_3D_1.txt",
vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
);
}
#[test]
fn test_binary_string_to_real_3d_2() {
test_binary_string_to_real(
"tests/Bin2Real_3D_2.txt",
vec![Bounds::new(0.0, 15.0), Bounds::new(-8.0, 7.0), Bounds::new(-8.0, 8.0)]
);
}
#[test]
fn test_binary_string_to_real_4d_1() {
test_binary_string_to_real(
"tests/Bin2Real_4D_1.txt",
vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
);
}
#[test]
fn test_binary_string_to_real_4d_2() {
test_binary_string_to_real(
"tests/Bin2Real_4D_2.txt",
vec![Bounds::new(0.0, 7.0), Bounds::new(-4.0, 3.0), Bounds::new(-4.0, 4.0), Bounds::new(-8.0, 0.0)]
);
}
#[test]
fn test_binary_string_to_real_6d_1() {
test_binary_string_to_real(
"tests/Bin2Real_6D_1.txt",
vec![Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0), Bounds::new(0.0, 1.0)]
);
}
#[test]
fn test_binary_string_to_real_6d_2() {
test_binary_string_to_real(
"tests/Bin2Real_6D_2.txt",
(0..=5).map(|x| Bounds::new(x as f64, (2 * (x + 1)) as f64)).collect::<Vec<_>>()
);
}