|
| 1 | +use anyhow::Result; |
| 2 | +use byteorder::{LittleEndian, WriteBytesExt}; |
| 3 | +use crc64fast::Digest; |
| 4 | +use std::path::Path; |
| 5 | +use valori_kernel::ValoriKernel; |
| 6 | +use valori_persistence::{snapshot, wal}; |
| 7 | +use valori_persistence::snapshot::SnapshotHeader; |
| 8 | + |
| 9 | +fn main() -> Result<()> { |
| 10 | + let out_dir = Path::new("demo_db"); |
| 11 | + if out_dir.exists() { |
| 12 | + std::fs::remove_dir_all(out_dir)?; |
| 13 | + } |
| 14 | + std::fs::create_dir_all(&out_dir)?; |
| 15 | + |
| 16 | + let mut kernel = ValoriKernel::new(); |
| 17 | + |
| 18 | + println!("🎬 Generating 'Semantic Drift' Dataset..."); |
| 19 | + |
| 20 | + // --- SCENE 1: The "Red" Cluster (ID 1-100) --- |
| 21 | + // Center: [1000, 1000] |
| 22 | + println!("1. Ingesting 100 'Red' vectors..."); |
| 23 | + for i in 1..=100 { |
| 24 | + let x = 1000 + rand_diff(i); |
| 25 | + let y = 1000 + rand_diff(i + 1000); // Different seed |
| 26 | + let payload = create_insert_payload(i, vec![x, y]); |
| 27 | + kernel.apply_event(&payload)?; |
| 28 | + } |
| 29 | + |
| 30 | + // --- CHECKPOINT 1: SNAPSHOT --- |
| 31 | + println!("2. Taking Snapshot (Checkpoint 1)..."); |
| 32 | + let snap_body = kernel.save_snapshot()?; |
| 33 | + |
| 34 | + // Compute Hash |
| 35 | + let mut digest = Digest::new(); |
| 36 | + digest.write(&snap_body); |
| 37 | + let checksum = digest.sum64(); |
| 38 | + let mut state_hash = [0u8; 16]; |
| 39 | + state_hash[0..8].copy_from_slice(&checksum.to_le_bytes()); |
| 40 | + |
| 41 | + let header = SnapshotHeader::new(100, 1700000000, state_hash); |
| 42 | + |
| 43 | + snapshot::write_to(out_dir.join("snapshot.val"), header, &snap_body)?; |
| 44 | + |
| 45 | + // --- SCENE 2: The "Blue" Intrusion (ID 101-200) --- |
| 46 | + // Center: [3000, 3000] (Far away from Red) |
| 47 | + println!("3. Ingesting 100 'Blue' vectors (Drift)..."); |
| 48 | + |
| 49 | + let wal_path = out_dir.join("events.log"); |
| 50 | + |
| 51 | + // We will append to WAL directly. |
| 52 | + for i in 101..=200 { |
| 53 | + let x = 3000 + rand_diff(i); |
| 54 | + let y = 3000 + rand_diff(i + 2000); |
| 55 | + let payload = create_insert_payload(i, vec![x, y]); |
| 56 | + |
| 57 | + // Append to WAL |
| 58 | + wal::append_entry(&wal_path, i, &payload)?; |
| 59 | + } |
| 60 | + |
| 61 | + // --- SCENE 3: The "Purple" Influencer (ID 201) --- |
| 62 | + // Position: [1500, 1500] (Exact match for the query) |
| 63 | + println!("4. Ingesting 1 'Purple' Influencer..."); |
| 64 | + let x = 1500; |
| 65 | + let y = 1500; |
| 66 | + let payload = create_insert_payload(201, vec![x, y]); |
| 67 | + wal::append_entry(&wal_path, 201, &payload)?; |
| 68 | + |
| 69 | + // Create a dummy metadata index |
| 70 | + let idx_path = out_dir.join("metadata.idx"); |
| 71 | + valori_persistence::idx::append_metadata(&idx_path, 100, None, "snapshot".to_string())?; |
| 72 | + valori_persistence::idx::append_metadata(&idx_path, 150, None, "blue_wave".to_string())?; |
| 73 | + valori_persistence::idx::append_metadata(&idx_path, 201, None, "purple_event".to_string())?; |
| 74 | + |
| 75 | + println!("✅ Demo Database generated at: {:?}", out_dir.canonicalize()?); |
| 76 | + println!("📊 Story: Red (0-100) -> Blue (101-200) -> Purple (201)"); |
| 77 | + println!(" Try: valori diff --dir demo_db --from 100 --to 201 --query '[1500, 1500]'"); |
| 78 | + |
| 79 | + Ok(()) |
| 80 | +} |
| 81 | + |
| 82 | +fn create_insert_payload(id: u64, values: Vec<i32>) -> Vec<u8> { |
| 83 | + let dim = values.len() as u16; |
| 84 | + let mut wtr = Vec::new(); |
| 85 | + wtr.write_u8(1).unwrap(); // CMD_INSERT |
| 86 | + wtr.write_u64::<LittleEndian>(id).unwrap(); |
| 87 | + wtr.write_u16::<LittleEndian>(dim).unwrap(); |
| 88 | + for v in values { |
| 89 | + wtr.write_i32::<LittleEndian>(v).unwrap(); |
| 90 | + } |
| 91 | + wtr |
| 92 | +} |
| 93 | + |
| 94 | +// Deterministic Pseudo-Random |
| 95 | +fn rand_diff(seed: u64) -> i32 { |
| 96 | + let mut x = seed; |
| 97 | + x ^= x << 13; |
| 98 | + x ^= x >> 7; |
| 99 | + x ^= x << 17; |
| 100 | + // Range -100 to 100 |
| 101 | + ((x % 200) as i32) - 100 |
| 102 | +} |
0 commit comments