cobt 0.1.1

A Cache-Oblivious B-Tree implementation in Rust
Documentation
use cobt::CacheObliviousBTree;

#[cfg(test)]
mod tests {
    use super::*;

    // ========== Basic Functionality Tests ==========

    #[test]
    fn test_new() {
        let cobt = CacheObliviousBTree::<i32, i32>::new();
        assert_eq!(cobt.len(), 0);
    }

    #[test]
    fn test_insert_single() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        tree.insert(42, 100);
        assert_eq!(tree.len(), 1);
        assert_eq!(tree.search(&42), Some(&100));
    }

    #[test]
    fn test_insert_multiple() {
        let mut tree = CacheObliviousBTree::<i32, &str>::new();
        tree.insert(1, "one");
        tree.insert(2, "two");
        tree.insert(3, "three");

        assert_eq!(tree.len(), 3);
        assert_eq!(tree.search(&1), Some(&"one"));
        assert_eq!(tree.search(&2), Some(&"two"));
        assert_eq!(tree.search(&3), Some(&"three"));
    }

    #[test]
    fn test_search_existing_keys() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        for i in 0..10 {
            tree.insert(i, i * 10);
        }

        for i in 0..10 {
            assert_eq!(tree.search(&i), Some(&(i * 10)));
        }
    }

    #[test]
    fn test_search_missing_key() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        tree.insert(1, 10);
        tree.insert(2, 20);
        tree.insert(3, 30);

        assert_eq!(tree.search(&0), None);
        assert_eq!(tree.search(&4), None);
        assert_eq!(tree.search(&100), None);
    }

    #[test]
    fn test_search_empty_tree() {
        let tree = CacheObliviousBTree::<i32, i32>::new();
        assert_eq!(tree.search(&1), None);
        assert_eq!(tree.search(&0), None);
    }

    // ========== Sequential Insertion Tests ==========

    #[test]
    fn test_insert_sequential_ascending() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        for i in 0..100 {
            tree.insert(i, i * 2);
        }

        assert_eq!(tree.len(), 100);

        // Verify all keys are searchable
        for i in 0..100 {
            assert_eq!(tree.search(&i), Some(&(i * 2)));
        }
    }

    #[test]
    fn test_insert_sequential_descending() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        for i in (0..100).rev() {
            tree.insert(i, i * 2);
        }

        assert_eq!(tree.len(), 100);

        // Verify all keys are searchable
        for i in 0..100 {
            assert_eq!(tree.search(&i), Some(&(i * 2)));
        }
    }

    // ========== Random/Mixed Order Tests ==========

    #[test]
    fn test_insert_mixed_order() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        let keys = vec![50, 25, 75, 10, 30, 60, 90, 5, 15, 35];

        for &key in &keys {
            tree.insert(key, key * 10);
        }

        assert_eq!(tree.len(), keys.len());

        // Verify all inserted keys are found
        for &key in &keys {
            assert_eq!(tree.search(&key), Some(&(key * 10)));
        }
    }

    // ========== Edge Cases ==========

    #[test]
    fn test_insert_duplicate_keys() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        tree.insert(1, 10);
        tree.insert(1, 20); // Insert duplicate key
        tree.insert(1, 30); // Insert another duplicate

        // Note: Current implementation doesn't replace values,
        // so size increases but first value should still be there
        assert_eq!(tree.len(), 3);
        // The first inserted value should be found
        assert_eq!(tree.search(&1), Some(&10));
    }

    #[test]
    fn test_negative_keys() {
        let mut tree = CacheObliviousBTree::<i32, &str>::new();
        tree.insert(-10, "negative ten");
        tree.insert(-5, "negative five");
        tree.insert(0, "zero");
        tree.insert(5, "five");

        assert_eq!(tree.search(&-10), Some(&"negative ten"));
        assert_eq!(tree.search(&-5), Some(&"negative five"));
        assert_eq!(tree.search(&0), Some(&"zero"));
        assert_eq!(tree.search(&5), Some(&"five"));
    }

    #[test]
    fn test_string_keys() {
        let mut tree = CacheObliviousBTree::<String, i32>::new();
        tree.insert("apple".to_string(), 1);
        tree.insert("banana".to_string(), 2);
        tree.insert("cherry".to_string(), 3);

        assert_eq!(tree.search(&"apple".to_string()), Some(&1));
        assert_eq!(tree.search(&"banana".to_string()), Some(&2));
        assert_eq!(tree.search(&"cherry".to_string()), Some(&3));
        assert_eq!(tree.search(&"date".to_string()), None);
    }

    // ========== Stress Tests ==========

    #[test]
    fn test_large_dataset_sequential() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        let size = 1000;

        for i in 0..size {
            tree.insert(i, i * 2);
        }

        assert_eq!(tree.len(), size as usize);

        // Spot check various keys
        assert_eq!(tree.search(&0), Some(&0));
        assert_eq!(tree.search(&500), Some(&1000));
        assert_eq!(tree.search(&999), Some(&1998));
        assert_eq!(tree.search(&1000), None);
    }

    #[test]
    fn test_large_dataset_reverse() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        let size = 1000;

        for i in (0..size).rev() {
            tree.insert(i, i * 3);
        }

        assert_eq!(tree.len(), size as usize);

        // Verify random access
        assert_eq!(tree.search(&0), Some(&0));
        assert_eq!(tree.search(&250), Some(&750));
        assert_eq!(tree.search(&999), Some(&2997));
    }

    #[test]
    fn test_interleaved_operations() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();

        // Insert some values
        for i in 0..50 {
            tree.insert(i, i * 10);
        }

        // Search for some values
        for i in (0..50).step_by(5) {
            assert_eq!(tree.search(&i), Some(&(i * 10)));
        }

        // Insert more values
        for i in 50..100 {
            tree.insert(i, i * 10);
        }

        // Verify all values
        assert_eq!(tree.len(), 100);
        for i in 0..100 {
            assert_eq!(tree.search(&i), Some(&(i * 10)));
        }
    }

    // ========== Node Splitting Tests ==========

    #[test]
    fn test_triggers_node_splits() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();

        // Insert enough elements to trigger multiple splits
        // The split threshold is dynamic based on sqrt(keys.len())
        for i in 0..200 {
            tree.insert(i, i * 5);
        }

        assert_eq!(tree.len(), 200);

        // Verify all elements are still searchable after splits
        for i in 0..200 {
            assert_eq!(tree.search(&i), Some(&(i * 5)));
        }
    }

    // ========== Type Variety Tests ==========

    #[test]
    fn test_with_tuples() {
        let mut tree = CacheObliviousBTree::<i32, (i32, &str)>::new();
        tree.insert(1, (100, "first"));
        tree.insert(2, (200, "second"));
        tree.insert(3, (300, "third"));

        assert_eq!(tree.search(&1), Some(&(100, "first")));
        assert_eq!(tree.search(&2), Some(&(200, "second")));
        assert_eq!(tree.search(&3), Some(&(300, "third")));
    }

    #[test]
    fn test_with_optional_values() {
        let mut tree = CacheObliviousBTree::<i32, Option<&str>>::new();
        tree.insert(1, Some("exists"));
        tree.insert(2, None);
        tree.insert(3, Some("also exists"));

        assert_eq!(tree.search(&1), Some(&Some("exists")));
        assert_eq!(tree.search(&2), Some(&None));
        assert_eq!(tree.search(&3), Some(&Some("also exists")));
    }

    // ========== Boundary Tests ==========

    #[test]
    fn test_extreme_values() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        tree.insert(i32::MIN, -1);
        tree.insert(i32::MAX, 1);
        tree.insert(0, 0);

        assert_eq!(tree.search(&i32::MIN), Some(&-1));
        assert_eq!(tree.search(&i32::MAX), Some(&1));
        assert_eq!(tree.search(&0), Some(&0));
    }

    #[test]
    fn test_len_after_operations() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();
        assert_eq!(tree.len(), 0);

        tree.insert(1, 10);
        assert_eq!(tree.len(), 1);

        tree.insert(2, 20);
        assert_eq!(tree.len(), 2);

        tree.insert(3, 30);
        assert_eq!(tree.len(), 3);

        // Search doesn't change length
        tree.search(&1);
        assert_eq!(tree.len(), 3);
    }

    // ========== Pattern Tests ==========

    #[test]
    fn test_even_odd_pattern() {
        let mut tree = CacheObliviousBTree::<i32, &str>::new();

        // Insert even numbers
        for i in (0..100).step_by(2) {
            tree.insert(i, "even");
        }

        // Verify evens exist and odds don't
        for i in 0..100 {
            if i % 2 == 0 {
                assert_eq!(tree.search(&i), Some(&"even"));
            } else {
                assert_eq!(tree.search(&i), None);
            }
        }
    }

    #[test]
    fn test_powers_of_two() {
        let mut tree = CacheObliviousBTree::<i32, i32>::new();

        let powers: Vec<i32> = (0..10).map(|i| 2_i32.pow(i)).collect();
        for &power in &powers {
            tree.insert(power, power);
        }

        assert_eq!(tree.len(), powers.len());

        for &power in &powers {
            assert_eq!(tree.search(&power), Some(&power));
        }

        // Non-powers should not exist
        assert_eq!(tree.search(&3), None);
        assert_eq!(tree.search(&100), None);
    }
}