using System.Collections; namespace PersistentOrderedMap; public abstract class BaseOrderedMap : IEnumerable> where TStrategy : IKeyStrategy { internal Node _root; internal readonly TStrategy _strategy; public int Count { get; protected set; } protected BaseOrderedMap(Node root, TStrategy strategy, int count) { _root = root ?? throw new ArgumentNullException(nameof(root)); _strategy = strategy ?? throw new ArgumentNullException(nameof(strategy)); Count = count; } // --------------------------------------------------------- // Read Operations (Shared) // --------------------------------------------------------- public bool TryGetValue(K key, out V value) { return BTreeFunctions.TryGetValue(_root, key, _strategy, out value); } public bool ContainsKey(K key) { return BTreeFunctions.TryGetValue(_root, key, _strategy, out _); } // --------------------------------------------------------- // Bootstrap / Factory Helpers // --------------------------------------------------------- public static PersistentOrderedMap Create(TStrategy strategy) { // Start with an empty leaf owned by None so the first write triggers CoW. var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); return new PersistentOrderedMap(emptyRoot, strategy, 0); } public static TransientOrderedMap CreateTransient(TStrategy strategy) { var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); return new TransientOrderedMap(emptyRoot, strategy,0); } public BTreeEnumerator GetEnumerator() { return AsEnumerable().GetEnumerator(); } IEnumerator> IEnumerable>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } // 1. Full Scan public BTreeEnumerable AsEnumerable() => new(_root, _strategy, false, default, false, default); // 2. Exact Range public BTreeEnumerable Range(K min, K max) => new(_root, _strategy, true, min, true, max); // 3. Start From (Open Ended) public BTreeEnumerable From(K min) => new(_root, _strategy, true, min, false, default); // 4. Until (Start at beginning) public BTreeEnumerable Until(K max) => new(_root, _strategy, false, default, true, max); // --------------------------------------------------------- // Navigation Operations // --------------------------------------------------------- public bool TryGetMin(out K key, out V value) => BTreeFunctions.TryGetMin(_root, out key, out value); public bool TryGetMax(out K key, out V value) => BTreeFunctions.TryGetMax(_root, out key, out value); public bool TryGetSuccessor(K key, out K nextKey, out V nextValue) => BTreeFunctions.TryGetSuccessor(_root, key, _strategy, out nextKey, out nextValue); public bool TryGetPredecessor(K key, out K prevKey, out V prevValue) => BTreeFunctions.TryGetPredecessor(_root, key, _strategy, out prevKey, out prevValue); // --------------------------------------------------------- // Set Operations (Linear Merge O(N+M)) // --------------------------------------------------------- public IEnumerable> Intersect(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); bool has1 = enum1.MoveNext(); bool has2 = enum2.MoveNext(); while (has1 && has2) { int cmp = _strategy.Compare(enum1.Current.Key, enum2.Current.Key); if (cmp == 0) { yield return enum1.Current; has1 = enum1.MoveNext(); has2 = enum2.MoveNext(); } else if (cmp < 0) has1 = enum1.MoveNext(); else has2 = enum2.MoveNext(); } } public IEnumerable> Except(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); bool has1 = enum1.MoveNext(); bool has2 = enum2.MoveNext(); while (has1 && has2) { int cmp = _strategy.Compare(enum1.Current.Key, enum2.Current.Key); if (cmp == 0) { has1 = enum1.MoveNext(); has2 = enum2.MoveNext(); } else if (cmp < 0) { yield return enum1.Current; has1 = enum1.MoveNext(); } else { has2 = enum2.MoveNext(); } } while (has1) { yield return enum1.Current; has1 = enum1.MoveNext(); } } public IEnumerable> SymmetricExcept(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); bool has1 = enum1.MoveNext(); bool has2 = enum2.MoveNext(); while (has1 && has2) { int cmp = _strategy.Compare(enum1.Current.Key, enum2.Current.Key); if (cmp == 0) { has1 = enum1.MoveNext(); has2 = enum2.MoveNext(); } else if (cmp < 0) { yield return enum1.Current; has1 = enum1.MoveNext(); } else { yield return enum2.Current; has2 = enum2.MoveNext(); } } while (has1) { yield return enum1.Current; has1 = enum1.MoveNext(); } while (has2) { yield return enum2.Current; has2 = enum2.MoveNext(); } } }