use rand::prelude::SmallRng;
use jumprope::JumpRope;
use rand::Rng;
use smallvec::smallvec;
use rle::MergeableIterator;
use rle::zip::{rle_zip, rle_zip3};
use crate::{AgentId, Time};
use crate::list::{Branch, ListCRDT, OpLog};
const USE_UNICODE: bool = true;
const UCHARS: [char; 23] = [
'a', 'b', 'c', '1', '2', '3', ' ', '\n', '©', '¥', '½', 'Ύ', 'Δ', 'δ', 'Ϡ', '←', '↯', '↻', '⇈', '𐆐', '𐆔', '𐆘', '𐆚', ];
pub(crate) fn random_str(len: usize, rng: &mut SmallRng) -> String {
let mut str = String::new();
let alphabet: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".chars().collect();
for _ in 0..len {
let charset = if USE_UNICODE { &UCHARS[..] } else { &alphabet };
str.push(charset[rng.gen_range(0..charset.len())]);
}
str
}
pub(crate) fn make_random_change_raw(oplog: &mut OpLog, branch: &Branch, mut rope: Option<&mut JumpRope>, agent: AgentId, rng: &mut SmallRng) -> Time {
let doc_len = branch.len();
let insert_weight = if doc_len < 100 { 0.55 } else { 0.45 };
let v = if doc_len == 0 || rng.gen_bool(insert_weight) {
let pos = rng.gen_range(0..=doc_len);
let len: usize = rng.gen_range(1..3); let content = random_str(len as usize, rng);
let fwd = len == 1 || rng.gen_bool(0.5);
if let Some(rope) = rope {
rope.insert(pos, content.as_str());
}
if fwd {
oplog.add_insert_at(agent, &branch.version, pos, &content)
} else {
let mut frontier = branch.version.clone();
for c in content.chars().rev() {
let mut buf = [0u8; 8]; let str = c.encode_utf8(&mut buf);
let v = oplog.add_insert_at(agent, &frontier, pos, str);
frontier = smallvec![v];
}
frontier[0]
}
} else {
let pos = rng.gen_range(0..doc_len);
let span = rng.gen_range(1..=usize::min(10, doc_len - pos));
let fwd = span == 1 || rng.gen_bool(0.5);
let del_loc = pos..pos+span;
if let Some(ref mut rope) = rope {
rope.remove(del_loc.clone());
}
if fwd {
let op = branch.make_delete_op(del_loc);
oplog.add_operations_at(agent, &branch.version, &[op])
} else {
let mut frontier = branch.version.clone(); for i in del_loc.rev() {
let op = branch.make_delete_op(i .. i + 1);
let v = oplog.add_operations_at(agent, &frontier, &[op]);
frontier = smallvec![v];
}
frontier[0]
}
};
oplog.dbg_check(false);
v
}
pub(crate) fn make_random_change(doc: &mut ListCRDT, rope: Option<&mut JumpRope>, agent: AgentId, rng: &mut SmallRng) {
let v = make_random_change_raw(&mut doc.oplog, &doc.branch, rope, agent, rng);
doc.branch.merge(&doc.oplog, &[v]);
}
pub(crate) fn choose_2<'a, T>(arr: &'a mut [T], rng: &mut SmallRng) -> (usize, &'a mut T, usize, &'a mut T) {
loop {
let a_idx = rng.gen_range(0..arr.len());
let b_idx = rng.gen_range(0..arr.len());
if a_idx != b_idx {
let (a_idx, b_idx) = if a_idx < b_idx { (a_idx, b_idx) } else { (b_idx, a_idx) };
let (start, end) = arr[..].split_at_mut(b_idx);
let a = &mut start[a_idx];
let b = &mut end[0];
return (a_idx, a, b_idx, b);
}
}
}
impl OpLog {
#[allow(unused)]
pub fn dbg_print_all(&self) {
for x in rle_zip(
self.iter_history(),
self.iter()
) {
println!("{:?}", x);
}
}
#[allow(unused)]
pub(crate) fn dbg_print_ops(&self) {
for (time, op) in rle_zip(
self.iter_history().map(|h| h.span).merge_spans(),
self.iter()
) {
println!("{:?} Op: {:?}", time, op);
}
}
#[allow(unused)]
pub(crate) fn dbg_print_assignments_and_ops(&self) {
for (time, map, op) in rle_zip3(
self.iter_history().map(|h| h.span).merge_spans(),
self.iter_mappings(),
self.iter()
) {
println!("{:?} M: {:?} Op: {:?}", time, map, op);
}
}
}