#![cfg(not(debug_assertions))]
mod common;
use std::sync::Arc;
use bigoish::{Log as LogModel, N, assert_best_fit, growing_inputs};
use common::Sha256Hasher;
use cpu_time::ThreadTime;
use eml::{Log, MemoryStorage};
fn make_log(n: usize) -> Arc<Log<MemoryStorage>> {
let mut log = Log::new(MemoryStorage::new());
log.add_algorithm(0, Box::new(Sha256Hasher)).unwrap();
for i in 0..n {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
Arc::new(log)
}
#[test]
fn complexity_inclusion_proof_log_n() {
assert_best_fit(
LogModel(N),
|log: Arc<Log<MemoryStorage>>| {
let ts = log.tree_size(0).unwrap();
let mut proof = None;
for _ in 0..100 {
proof = Some(log.inclusion_proof(0, ts / 2).unwrap());
}
proof.unwrap()
},
growing_inputs(100, make_log, 25),
);
}
#[test]
fn complexity_consistency_proof_log_n() {
assert_best_fit(
LogModel(N),
|log: Arc<Log<MemoryStorage>>| {
let ts = log.tree_size(0).unwrap();
let mut proof = None;
for _ in 0..100 {
proof = Some(log.consistency_proof(0, ts / 2).unwrap());
}
proof.unwrap()
},
growing_inputs(100, make_log, 25),
);
}
#[test]
fn complexity_root_extraction_log_n() {
assert_best_fit(
LogModel(N),
|log: Arc<Log<MemoryStorage>>| {
let mut root = None;
for _ in 0..100 {
root = Some(log.root(0).unwrap());
}
root.unwrap()
},
growing_inputs(100, make_log, 25),
);
}
fn median(v: &mut [u128]) -> u128 {
v.sort_unstable();
v[v.len() / 2]
}
fn assert_rank_at_most(data: Vec<(f64, f64)>, max_rank: u32, label: &str, expected_notation: &str) {
let (best, _all) = big_o::infer_complexity(data).unwrap();
assert!(
best.rank <= max_rank,
"{label} should be {expected_notation}, but best fit is {} (rank {}, max allowed \
{max_rank})",
best.notation,
best.rank,
);
}
#[test]
fn complexity_append_amortized_constant() {
let sizes: &[usize] = &[500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000];
let batch = 1_000;
let trials = 21;
let mut data: Vec<(f64, f64)> = Vec::with_capacity(sizes.len());
for &n in sizes {
let mut times = Vec::with_capacity(trials);
for _ in 0..trials {
let mut log = Log::new(MemoryStorage::new());
log.add_algorithm(0, Box::new(Sha256Hasher)).unwrap();
for i in 0..n {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
let start = ThreadTime::now();
for i in n..(n + batch) {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
times.push(start.elapsed().as_nanos());
}
let per_append = median(&mut times) as f64 / batch as f64;
data.push((n as f64, per_append));
}
assert_rank_at_most(data, 1200, "append", "O(1) amortized");
}
#[test]
fn complexity_add_algorithm_log_k() {
let sizes: &[usize] = &[1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000];
let trials = 21;
let mut data: Vec<(f64, f64)> = Vec::with_capacity(sizes.len());
for &k in sizes {
let mut times = Vec::with_capacity(trials);
for _ in 0..trials {
let mut log = Log::new(MemoryStorage::new());
log.add_algorithm(0, Box::new(Sha256Hasher)).unwrap();
for i in 0..k {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
let start = ThreadTime::now();
log.add_algorithm(1, Box::new(Sha256Hasher)).unwrap();
times.push(start.elapsed().as_nanos());
}
data.push((k as f64, median(&mut times) as f64));
}
assert_rank_at_most(data, 999, "add_algorithm", "O(log K)");
}
#[test]
fn complexity_resume_algorithm_linear_gap() {
let gaps: &[usize] = &[100, 500, 1_000, 2_000, 5_000, 10_000, 20_000];
let base_size = 100;
let trials = 21;
let mut data: Vec<(f64, f64)> = Vec::with_capacity(gaps.len());
for &g in gaps {
let mut times = Vec::with_capacity(trials);
for _ in 0..trials {
let mut log = Log::new(MemoryStorage::new());
log.add_algorithm(0, Box::new(Sha256Hasher)).unwrap();
log.add_algorithm(1, Box::new(Sha256Hasher)).unwrap();
for i in 0..base_size {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
log.remove_algorithm(1).unwrap();
for i in base_size..(base_size + g) {
log.append(&(i as u64).to_le_bytes()).unwrap();
}
let start = ThreadTime::now();
log.resume_algorithm(1).unwrap();
times.push(start.elapsed().as_nanos());
}
data.push((g as f64, median(&mut times) as f64));
}
assert_rank_at_most(data, 1200, "resume_algorithm", "O(G)");
}