use clay_codes::ClayCode;
use std::collections::HashMap;
#[test]
fn test_full_repair_flow_with_bandwidth_check() {
let clay = ClayCode::new(10, 4, 13).unwrap();
assert_eq!(clay.n, 14);
assert_eq!(clay.k, 10);
assert_eq!(clay.m, 4);
assert_eq!(clay.d, 13);
assert_eq!(clay.q, 4);
assert_eq!(clay.sub_chunk_no, 256); assert_eq!(clay.beta, 64);
let data_size = clay.k * clay.sub_chunk_no;
let data: Vec<u8> = (0..data_size).map(|i| ((i * 17 + 31) % 256) as u8).collect();
let chunks = clay.encode(&data);
assert_eq!(chunks.len(), 14);
let chunk_size = chunks[0].len();
let sub_chunk_size = chunk_size / clay.sub_chunk_no;
let available: Vec<usize> = (1..clay.n).collect();
let helper_info = clay.minimum_to_repair(0, &available).unwrap();
assert_eq!(helper_info.len(), clay.d);
let repair_bytes: usize = helper_info
.iter()
.map(|(_, indices)| indices.len() * sub_chunk_size)
.sum();
let full_decode_bytes = clay.k * chunk_size;
let repair_ratio = repair_bytes as f64 / full_decode_bytes as f64;
println!("(14, 10, 13) repair bandwidth ratio: {:.3}", repair_ratio);
assert!(repair_ratio < 0.35, "Repair should use < 35% of full decode bandwidth");
let mut partial_data: HashMap<usize, Vec<u8>> = HashMap::new();
for (helper_idx, indices) in &helper_info {
let mut helper_partial = Vec::new();
for &sc_idx in indices {
let start_byte = sc_idx * sub_chunk_size;
let end_byte = (sc_idx + 1) * sub_chunk_size;
helper_partial.extend_from_slice(&chunks[*helper_idx][start_byte..end_byte]);
}
partial_data.insert(*helper_idx, helper_partial);
}
let recovered = clay.repair(0, &partial_data, chunk_size).unwrap();
assert_eq!(recovered, chunks[0], "Repair failed to recover correct data");
}
#[test]
fn test_multi_erasure_decode() {
let clay = ClayCode::new(4, 2, 5).unwrap();
let data: Vec<u8> = (0..512).map(|i| (i % 256) as u8).collect();
let chunks = clay.encode(&data);
let erasure_patterns = vec![
vec![0],
vec![5],
vec![0, 5],
vec![0, 1],
vec![4, 5],
vec![1, 3],
];
for erasures in erasure_patterns {
let mut available: HashMap<usize, Vec<u8>> = HashMap::new();
for (i, chunk) in chunks.iter().enumerate() {
if !erasures.contains(&i) {
available.insert(i, chunk.clone());
}
}
let decoded = clay.decode(&available, &erasures).unwrap();
assert_eq!(
&decoded[..data.len()],
&data[..],
"Failed to decode with erasures {:?}",
erasures
);
}
}
#[test]
fn test_repair_bandwidth_advantage() {
let test_params = vec![
(4, 2, 5), (9, 3, 11), (10, 4, 13), ];
for (k, m, d) in test_params {
let clay = ClayCode::new(k, m, d).unwrap();
let data_size = k * clay.sub_chunk_no;
let data: Vec<u8> = (0..data_size).map(|i| (i % 256) as u8).collect();
let chunks = clay.encode(&data);
let chunk_size = chunks[0].len();
let sub_chunk_size = chunk_size / clay.sub_chunk_no;
for lost_node in 0..clay.n {
let available: Vec<usize> = (0..clay.n).filter(|&i| i != lost_node).collect();
let helper_info = clay.minimum_to_repair(lost_node, &available).unwrap();
let repair_bytes: usize = helper_info
.iter()
.map(|(_, indices)| indices.len() * sub_chunk_size)
.sum();
let full_decode_bytes = k * chunk_size;
assert!(
repair_bytes < full_decode_bytes,
"Repair bandwidth {} >= full decode {} for node {} with ({}, {}, {})",
repair_bytes, full_decode_bytes, lost_node, k, m, d
);
}
}
}