use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::statistics::{mean, median, quartiles, standard_deviation};
type ParamFunction = Rc<RefCell<dyn Fn()>>;
type ParamFunctionTuple = (&'static str, ParamFunction);
pub fn timeit<F>(method: F) -> Duration
where
F: FnOnce(),
{
let now: Instant = Instant::now();
method();
now.elapsed()
}
struct CustomRng {
state: u64,
}
impl CustomRng {
fn default() -> CustomRng {
CustomRng {
state: CustomRng::get_current_time(),
}
}
fn new() -> CustomRng {
CustomRng::default()
}
fn seed(mut self, seed: u64) -> Self {
self.state = seed;
self
}
fn next(&mut self) -> u64 {
let mut x = self.state;
let current_time = CustomRng::get_current_time();
let time_string = current_time.to_string();
let digits = time_string
.chars()
.filter_map(|c| c.to_digit(10))
.collect::<Vec<u32>>();
for chunk in digits.chunks(2) {
let r = chunk.iter().sum::<u32>();
if r < 12 {
x ^= x << r
} else {
x ^= x >> r
}
}
self.state = x;
x
}
fn get_current_time() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Clock may have gone backwards")
.as_secs()
/ 3600
}
fn random_f32(&mut self, min: f32, max: f32) -> f32 {
min + (self.next() as f32 / u32::MAX as f32) * (max - min)
}
fn random_number<T>(&mut self, min: T, max: T) -> T
where
T: std::ops::Sub<Output = T>
+ std::ops::Mul<Output = T>
+ std::ops::Add<Output = T>
+ std::ops::Rem<Output = T>
+ std::ops::Div<Output = T>
+ Copy
+ From<u64>,
{
min + (max - min * T::from(self.next()) % T::from(u64::MAX)) / T::from(u64::MAX)
}
}
pub fn create_vector(num_elements: usize, min_value: f32, max_value: f32) -> Vec<f32> {
let mut vec: Vec<f32> = Vec::with_capacity(num_elements);
let mut rng: CustomRng = CustomRng::new();
for _ in 0..num_elements {
vec.push(rng.random_f32(min_value, max_value));
}
vec
}
pub fn create_matrix(
num_rows: usize,
num_cols: usize,
min_value: f32,
max_value: f32,
) -> Vec<Vec<f32>> {
let mut vec: Vec<Vec<f32>> = Vec::with_capacity(num_rows);
for _ in 0..num_rows {
vec.push(create_vector(num_cols, min_value, max_value));
}
vec
}
pub fn compare_execution_times(n: u64, functions: Vec<ParamFunctionTuple>) {
let mut results: HashMap<String, Vec<Duration>> = HashMap::new();
for (name, function) in functions {
let mut execution_times: Vec<Duration> = Vec::with_capacity(n as usize);
for _ in 0..n {
let execution_time = timeit(|| function.borrow()());
execution_times.push(execution_time);
}
results.insert(name.to_string(), execution_times);
}
analyze_execution_results(results);
}
struct FunctionStatistics {
name: String,
mean: f32,
median: f32,
std_deviation: f32,
percentile_25: f32,
percentile_75: f32,
speedup: Option<f32>,
}
fn analyze_execution_results(results: HashMap<String, Vec<Duration>>) {
let mut function_stats: Vec<FunctionStatistics> = results
.iter()
.map(|(name, duration_times)| {
let mut execution_times: Vec<f32> = durations_to_f32s(duration_times);
let (percentile_25, percentile_75) = quartiles(&mut execution_times);
FunctionStatistics {
name: name.clone(),
mean: mean(&execution_times),
median: median(&execution_times),
std_deviation: standard_deviation(&execution_times),
percentile_25,
percentile_75,
speedup: None,
}
})
.collect();
function_stats.sort_by(|stats1, stats2| {
stats1
.mean
.partial_cmp(&stats2.mean)
.unwrap_or(std::cmp::Ordering::Equal)
});
let reference_mean = function_stats
.first()
.map(|stats| stats.mean)
.unwrap_or(0.0);
for stats in &mut function_stats {
if reference_mean != 0.0 {
let speedup_factor = reference_mean / stats.mean;
stats.speedup = Some(speedup_factor);
}
}
for (rank, stats) in function_stats.iter().enumerate() {
let rank = rank + 1;
println!("Rank {}: Function: {}", rank, stats.name);
println!("Mean: {:.6} ms", stats.mean * 1000.0);
println!("Median: {:.6} ms", stats.median * 1000.0);
println!("25th Percentile: {:.6} ms", stats.percentile_25 * 1000.0);
println!("75th Percentile: {:.6} ms", stats.percentile_75 * 1000.0);
println!("---------------------------------");
}
}
fn durations_to_f32s(durations: &[Duration]) -> Vec<f32> {
durations
.iter()
.map(|duration| duration.as_secs_f32())
.collect()
}
pub fn test_implementation() {
}
struct NewCustomRng {
state: u64,
}
impl NewCustomRng {
fn new() -> NewCustomRng {
NewCustomRng {
state: CustomRng::new().state,
}
}
fn next(&mut self) -> u32 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
self.state = x;
(x & 0xFFFFFFFF) as u32
}
fn random_f32(&mut self, min: f32, max: f32) -> f32 {
let rand_val = self.next() as f32 / u32::MAX as f32;
min + (max - min) * rand_val
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_current_time() {
let mut current_time = CustomRng::new();
println!(
"number: {:?}",
current_time.next() as f64 / u64::MAX as f64 * 10e5
);
}
#[test]
fn random_f() {
let mut rng = NewCustomRng::new();
let result = rng.random_f32(-1.0, 1.0);
println!("Random f32: {}", result);
println!("number: {:?}", CustomRng::new().random_f32(-1.0, 1.0));
}
}