#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct WeightSnapshot {
pub weights: Vec<f32>,
pub timestamp: u64,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MorphHistory {
pub snapshots: Vec<WeightSnapshot>,
pub cursor: usize,
pub max_depth: usize,
}
#[allow(dead_code)]
pub fn new_morph_history(max_depth: usize) -> MorphHistory {
MorphHistory {
snapshots: Vec::new(),
cursor: 0,
max_depth: max_depth.max(1),
}
}
#[allow(dead_code)]
pub fn push_snapshot(hist: &mut MorphHistory, weights: Vec<f32>, ts: u64) {
if hist.cursor > 0 {
let keep = hist.snapshots.len() - hist.cursor;
hist.snapshots.truncate(keep);
hist.cursor = 0;
}
hist.snapshots.push(WeightSnapshot { weights, timestamp: ts });
while hist.snapshots.len() > hist.max_depth {
hist.snapshots.remove(0);
}
}
#[allow(dead_code)]
pub fn undo(hist: &mut MorphHistory) -> Option<&[f32]> {
if !can_undo(hist) {
return None;
}
hist.cursor += 1;
let idx = hist.snapshots.len() - 1 - hist.cursor;
Some(&hist.snapshots[idx].weights)
}
#[allow(dead_code)]
pub fn redo(hist: &mut MorphHistory) -> Option<&[f32]> {
if !can_redo(hist) {
return None;
}
hist.cursor -= 1;
let idx = hist.snapshots.len() - 1 - hist.cursor;
Some(&hist.snapshots[idx].weights)
}
#[allow(dead_code)]
pub fn can_undo(hist: &MorphHistory) -> bool {
hist.cursor + 1 < hist.snapshots.len()
}
#[allow(dead_code)]
pub fn can_redo(hist: &MorphHistory) -> bool {
hist.cursor > 0
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HistoryEntry {
pub target: String,
pub old_value: f32,
pub new_value: f32,
}
#[allow(dead_code)]
pub fn record_change(hist: &mut MorphHistory, target: &str, old_value: f32, new_value: f32) {
let entry = HistoryEntry {
target: target.to_string(),
old_value,
new_value,
};
if hist.cursor > 0 {
let keep = hist.snapshots.len() - hist.cursor;
hist.snapshots.truncate(keep);
hist.cursor = 0;
}
push_snapshot(hist, vec![entry.old_value, entry.new_value], hist.snapshots.len() as u64);
}
#[allow(dead_code)]
pub fn undo_morph(hist: &mut MorphHistory) -> Option<&[f32]> {
undo(hist)
}
#[allow(dead_code)]
pub fn redo_morph(hist: &mut MorphHistory) -> Option<&[f32]> {
redo(hist)
}
#[allow(dead_code)]
pub fn history_count(hist: &MorphHistory) -> usize {
hist.snapshots.len()
}
#[allow(dead_code)]
pub fn history_clear(hist: &mut MorphHistory) {
hist.snapshots.clear();
hist.cursor = 0;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_history_is_empty() {
let h = new_morph_history(10);
assert!(!can_undo(&h));
assert!(!can_redo(&h));
}
#[test]
fn push_enables_undo() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.0], 1);
push_snapshot(&mut h, vec![1.0], 2);
assert!(can_undo(&h));
}
#[test]
fn undo_returns_previous_weights() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.2], 1);
push_snapshot(&mut h, vec![0.8], 2);
let prev = undo(&mut h).expect("should succeed");
assert!((prev[0] - 0.2).abs() < 1e-6);
}
#[test]
fn redo_after_undo() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
push_snapshot(&mut h, vec![0.9], 2);
undo(&mut h);
assert!(can_redo(&h));
let next = redo(&mut h).expect("should succeed");
assert!((next[0] - 0.9).abs() < 1e-6);
}
#[test]
fn undo_when_empty_returns_none() {
let mut h = new_morph_history(10);
assert!(undo(&mut h).is_none());
}
#[test]
fn redo_without_undo_returns_none() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.5], 1);
assert!(redo(&mut h).is_none());
}
#[test]
fn push_clears_redo() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
push_snapshot(&mut h, vec![0.5], 2);
undo(&mut h);
push_snapshot(&mut h, vec![0.3], 3);
assert!(!can_redo(&h));
}
#[test]
fn max_depth_enforced() {
let mut h = new_morph_history(3);
for i in 0..6u64 {
push_snapshot(&mut h, vec![i as f32 * 0.1], i);
}
assert!(h.snapshots.len() <= 3);
}
#[test]
fn multiple_undos() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
push_snapshot(&mut h, vec![0.2], 2);
push_snapshot(&mut h, vec![0.3], 3);
undo(&mut h);
undo(&mut h);
let w = undo(&mut h);
assert!(w.is_none()); }
#[test]
fn record_change_adds_snapshot() {
let mut h = new_morph_history(10);
record_change(&mut h, "smile", 0.0, 0.5);
assert_eq!(history_count(&h), 1);
}
#[test]
fn undo_morph_works() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
push_snapshot(&mut h, vec![0.9], 2);
let w = undo_morph(&mut h).expect("should succeed");
assert!((w[0] - 0.1).abs() < 1e-6);
}
#[test]
fn redo_morph_works() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
push_snapshot(&mut h, vec![0.9], 2);
undo_morph(&mut h);
let w = redo_morph(&mut h).expect("should succeed");
assert!((w[0] - 0.9).abs() < 1e-6);
}
#[test]
fn history_clear_works() {
let mut h = new_morph_history(10);
push_snapshot(&mut h, vec![0.1], 1);
history_clear(&mut h);
assert_eq!(history_count(&h), 0);
}
}