using System; using System.Collections; using System.Collections.Generic; namespace Engine.Core; /// /// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution /// /// /// public class FastListOrdered : IList, IReadOnlyList, IEnumerable where TItem : notnull where TIndex : IComparable { private readonly SortedDictionary> items = null!; private readonly Func getIndexFunc = null!; private readonly IComparer sortBy = null!; private int count = 0; public int Count => count; public bool IsReadOnly { get; set; } = false; public TItem this[int index] { get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; } set { if (IsReadOnly) throw new System.Data.ReadOnlyException(); (TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value; } } private (TIndex TIndex, int i) GetAt(Index index) { int actualIndex = index.IsFromEnd ? count - index.Value : index.Value; if (actualIndex < 0 || actualIndex >= count) throw new IndexOutOfRangeException(); int leftIndex = actualIndex; foreach ((TIndex i, FastList list) in items) { if (leftIndex < list.Count) return (i, leftIndex); leftIndex -= list.Count; } throw new IndexOutOfRangeException(); } public int IndexOf(TItem item) { int indexCounter = 0; foreach ((TIndex index, FastList list) in items) { int i = list.IndexOf(item); if (i != -1) return indexCounter + i; indexCounter += list.Count; } return -1; } public void Add(TItem item) { if (IsReadOnly) throw new System.Data.ReadOnlyException(); TIndex key = getIndexFunc(item); if (!items.TryGetValue(key, out FastList? list)) items[key] = list = []; list.Add(item); count++; } public void Insert(int index, TItem item) { if (IsReadOnly) throw new System.Data.ReadOnlyException(); TIndex tIndex = getIndexFunc(item); if (!items.TryGetValue(tIndex, out FastList? list)) items[tIndex] = list = []; list.Insert(index, item); count++; } public bool Remove(TItem item) { if (IsReadOnly) throw new System.Data.ReadOnlyException(); TIndex index = getIndexFunc(item); if (!items.TryGetValue(index, out FastList? list)) throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector"); if (!list.Remove(item)) return false; count--; return true; } public void RemoveAt(int index) { if (IsReadOnly) throw new System.Data.ReadOnlyException(); (TIndex tIndex, int i) = GetAt(index); items[tIndex].RemoveAt(i); count--; } public void Clear() { if (IsReadOnly) throw new System.Data.ReadOnlyException(); foreach ((TIndex index, FastList list) in items) list.Clear(); count = 0; } public bool Contains(TItem item) { foreach ((TIndex index, FastList list) in items) if (list.Contains(item)) return true; return false; } public void CopyTo(TItem[] array, int arrayIndex) { int indexCounter = 0; foreach ((TIndex index, FastList list) in items) { list.CopyTo(array, indexCounter); indexCounter += list.Count; } } public IEnumerator GetEnumerator() { foreach ((TIndex index, FastList list) in items) foreach (TItem item in list) yield return item; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public FastListOrdered(Func getIndexFunc, Comparison sortBy) { this.getIndexFunc = getIndexFunc; this.sortBy = Comparer.Create(sortBy); items = new(this.sortBy); } public FastListOrdered(Func getIndexFunc, IComparer sortBy) { this.getIndexFunc = getIndexFunc; this.sortBy = sortBy; items = new(sortBy); } }