using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("PrioritySet")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Indexed set with optional per-slot priority, conflict resolution, and queue-style operations.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+978d443a8b6a1f288913a375cd36f7569deece8b")] [assembly: AssemblyProduct("PrioritySet")] [assembly: AssemblyTitle("PrioritySet")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace PrioritySet { [Flags] public enum CleanFlags { None = 0, NullValues = 1, NullPriorities = 2, NullValuesAndPriorities = 3 } public enum ConflictResolution { KeepIndex, KeepPriority, ThrowOnConflict } public enum DuplicateValuePolicy { ThrowOnDuplicate, Reject, ReplaceExisting } public enum PlacementIndexBias { HighestValidIndex, LowestValidIndex } [Flags] public enum PrioritySetChangeKind { None = 0, Add = 1, Remove = 2, Reorder = 4, PriorityChange = 8, Clear = 0x10, Clean = 0x20, Sort = 0x40, BulkSetOperation = 0x80, Update = 0x100 } public sealed class PrioritySetChangedEventArgs : EventArgs { public PrioritySetChangeKind ChangeKind { get; } public int? Index { get; } public object? Value { get; } public int? Priority { get; } public PrioritySetChangedEventArgs(PrioritySetChangeKind changeKind, int? index = null, object? value = null, int? priority = null) { ChangeKind = changeKind; Index = index; Value = value; Priority = priority; } } public class PrioritySet : IList, ICollection, IEnumerable, IEnumerable, IReadOnlyList, IReadOnlyCollection, ISet { private sealed class ChangedScope : IDisposable { private readonly PrioritySet _owner; public ChangedScope(PrioritySet owner) { _owner = owner; } public void Dispose() { _owner._changedScopeDepth--; if (_owner._changedScopeDepth == 0) { _owner.FlushPendingChanged(); } } } private sealed record FilteredChangedSubscription(PrioritySetChangeKind ChangeKinds, EventHandler Handler); private sealed class FilteredChangedSubscriptionToken : IDisposable { private PrioritySet? _owner; private readonly FilteredChangedSubscription _subscription; public FilteredChangedSubscriptionToken(PrioritySet owner, FilteredChangedSubscription subscription) { _owner = owner; _subscription = subscription; } public void Dispose() { if (_owner != null) { _owner.RemoveFilteredSubscription(_subscription); _owner = null; } } } private sealed class EntriesReadOnlyView : IReadOnlyList>, IEnumerable>, IEnumerable, IReadOnlyCollection> { private readonly PrioritySet _owner; public PrioritySetEntry this[int index] { get { _owner.ValidateIndex(index); Entry entry = _owner._entries[index]; return new PrioritySetEntry(entry.Value, entry.Priority, index); } } public int Count => _owner.Count; public EntriesReadOnlyView(PrioritySet owner) { _owner = owner; } public IEnumerator> GetEnumerator() { for (int i = 0; i < _owner.Count; i++) { yield return this[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private sealed class ItemsReadOnlyView : IReadOnlyList, IEnumerable, IEnumerable, IReadOnlyCollection { private readonly PrioritySet _owner; public T this[int index] => _owner[index]; public int Count => _owner.Count; public ItemsReadOnlyView(PrioritySet owner) { _owner = owner; } public IEnumerator GetEnumerator() { return _owner.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } private readonly List _filteredChangedSubscriptions = new List(); private int _changedScopeDepth; private PrioritySetChangeKind _pendingChangeKind; private int? _pendingIndex; private object? _pendingValue; private int? _pendingPriority; private readonly List> _entries = new List>(); private readonly Dictionary _indexByValue; private readonly Dictionary _priorityToValue = new Dictionary(); private readonly IEqualityComparer _comparer; private readonly EntriesReadOnlyView _entriesView; private readonly ItemsReadOnlyView _itemsView; public ConflictResolution DefaultConflictResolution { get; set; } public DuplicateValuePolicy DefaultDuplicateValuePolicy { get; set; } = DuplicateValuePolicy.Reject; public PlacementIndexBias DefaultPlacementIndexBias { get; set; } public int Count => _entries.Count; public bool IsReadOnly => false; public IReadOnlyList> Entries => _entriesView; public IReadOnlyList Items => _itemsView; public T this[int index] { get { ValidateIndex(index); return _entries[index].Value; } set { ValidateIndex(index); T value2 = _entries[index].Value; if (!_comparer.Equals(value2, value)) { TrySetValueAt(index, value); } } } public event EventHandler? Changed; private void NotifyChanged(PrioritySetChangeKind changeKind, int? index = null, object? value = null, int? priority = null) { if (changeKind != PrioritySetChangeKind.None) { if (_changedScopeDepth > 0) { _pendingChangeKind |= changeKind; _pendingIndex = index; _pendingValue = value; _pendingPriority = priority; } else { RaiseChanged(new PrioritySetChangedEventArgs(changeKind, index, value, priority)); } } } private IDisposable BeginChangedScope() { _changedScopeDepth++; return new ChangedScope(this); } private void FlushPendingChanged() { if (_pendingChangeKind != PrioritySetChangeKind.None) { RaiseChanged(new PrioritySetChangedEventArgs(_pendingChangeKind, _pendingIndex, _pendingValue, _pendingPriority)); _pendingChangeKind = PrioritySetChangeKind.None; _pendingIndex = null; _pendingValue = null; _pendingPriority = null; } } public IDisposable Subscribe(PrioritySetChangeKind changeKinds, EventHandler handler) { ArgumentNullException.ThrowIfNull(handler, "handler"); if (changeKinds == PrioritySetChangeKind.None) { throw new ArgumentException("At least one change kind must be specified.", "changeKinds"); } FilteredChangedSubscription filteredChangedSubscription = new FilteredChangedSubscription(changeKinds, handler); _filteredChangedSubscriptions.Add(filteredChangedSubscription); return new FilteredChangedSubscriptionToken(this, filteredChangedSubscription); } private void RaiseChanged(PrioritySetChangedEventArgs args) { this.Changed?.Invoke(this, args); if (_filteredChangedSubscriptions.Count == 0) { return; } FilteredChangedSubscription[] array = _filteredChangedSubscriptions.ToArray(); foreach (FilteredChangedSubscription filteredChangedSubscription in array) { if ((args.ChangeKind & filteredChangedSubscription.ChangeKinds) != PrioritySetChangeKind.None) { filteredChangedSubscription.Handler(this, args); } } } private void RemoveFilteredSubscription(FilteredChangedSubscription subscription) { _filteredChangedSubscriptions.Remove(subscription); } public int Clean(CleanFlags flags = CleanFlags.NullValues) { if (flags == CleanFlags.None) { return 0; } bool flag = flags.HasFlag(CleanFlags.NullValues); bool flag2 = flags.HasFlag(CleanFlags.NullPriorities); int num = 0; for (int num2 = _entries.Count - 1; num2 >= 0; num2--) { Entry entry = _entries[num2]; bool num3 = flag && entry.Value == null; bool flag3 = flag2 && !entry.Priority.HasValue; if (num3 || flag3) { int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); UnregisterPriority(valueOrDefault, entry.Value); } RemoveIndexForValue(entry.Value); _entries.RemoveAt(num2); num++; } } if (num > 0) { RebuildIndexMap(); RebuildPriorityMapFromEntries(); EnsurePriorityOrdering(); NotifyChanged(PrioritySetChangeKind.Clean); } return num; } public PrioritySet() : this((IEqualityComparer?)null) { } public PrioritySet(IEnumerable collection) : this(collection, (IEqualityComparer?)null, DuplicateValuePolicy.Reject) { } public PrioritySet(IEnumerable collection, IEqualityComparer? comparer, DuplicateValuePolicy duplicateValuePolicy = DuplicateValuePolicy.Reject) : this(comparer) { ArgumentNullException.ThrowIfNull(collection, "collection"); DefaultDuplicateValuePolicy = duplicateValuePolicy; foreach (T item in collection) { if (item != null && _indexByValue.ContainsKey(item)) { switch (duplicateValuePolicy) { case DuplicateValuePolicy.ThrowOnDuplicate: throw new InvalidOperationException("Duplicate non-null value in collection."); case DuplicateValuePolicy.ReplaceExisting: Remove(item); break; case DuplicateValuePolicy.Reject: continue; } } Add(item); } } public PrioritySet(IEqualityComparer? comparer) { _comparer = comparer ?? EqualityComparer.Default; _indexByValue = new Dictionary(_comparer); _entriesView = new EntriesReadOnlyView(this); _itemsView = new ItemsReadOnlyView(this); } public void Clear() { _entries.Clear(); _indexByValue.Clear(); _priorityToValue.Clear(); NotifyChanged(PrioritySetChangeKind.Clear); } public bool Contains([AllowNull] T value) { if (value == null) { return IndexOfNullReference() >= 0; } return _indexByValue.ContainsKey(value); } public void CopyTo(T[] array, int arrayIndex) { ArgumentNullException.ThrowIfNull(array, "array"); if (arrayIndex < 0) { throw new ArgumentOutOfRangeException("arrayIndex"); } if (array.Length - arrayIndex < Count) { throw new ArgumentException("Destination array is not long enough."); } for (int i = 0; i < Count; i++) { array[arrayIndex + i] = _entries[i].Value; } } public IEnumerator GetEnumerator() { for (int i = 0; i < _entries.Count; i++) { yield return _entries[i].Value; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int IndexOf([AllowNull] T value) { if (value == null) { return IndexOfNullReference(); } if (!_indexByValue.TryGetValue(value, out var value2)) { return -1; } return value2; } public int? GetPriority([AllowNull] T value) { if (!TryGetIndexForLookup(value, out var index)) { return null; } return _entries[index].Priority; } public PrioritySetEntry? GetEntryByValue([AllowNull] T value) { if (!TryGetIndexForLookup(value, out var index)) { return null; } Entry entry = _entries[index]; return new PrioritySetEntry(entry.Value, entry.Priority, index); } public T? GetByPriority(int priority) { if (!_priorityToValue.TryGetValue(priority, out var value)) { return default(T); } return value; } public bool TryGetByPriority(int priority, out T? value) { if (_priorityToValue.TryGetValue(priority, out var value2)) { value = value2; return true; } value = default(T); return false; } public PrioritySetEntry? GetEntryByPriority(int priority) { if (!_priorityToValue.TryGetValue(priority, out var value)) { return null; } if (value != null && _indexByValue.TryGetValue(value, out var value2)) { return new PrioritySetEntry(_entries[value2].Value, priority, value2); } for (int i = 0; i < _entries.Count; i++) { int? priority2 = _entries[i].Priority; if (priority2.HasValue) { int valueOrDefault = priority2.GetValueOrDefault(); if (valueOrDefault == priority) { return new PrioritySetEntry(_entries[i].Value, priority, i); } } } return null; } public IEnumerable ByPriority() { for (int i = 0; i < _entries.Count; i++) { if (_entries[i].Priority.HasValue) { yield return _entries[i].Value; } } } public void Validate() { int num = 0; for (int i = 0; i < _entries.Count; i++) { if (_entries[i].Value != null) { num++; } } if (num != _indexByValue.Count) { throw new InvalidOperationException("Non-null entry count does not match value map."); } _priorityToValue.Clear(); RebuildPriorityMapFromEntries(); for (int j = 0; j < _entries.Count; j++) { T value = _entries[j].Value; if (value != null) { if (!_indexByValue.TryGetValue(value, out var value2) || value2 != j) { throw new InvalidOperationException("Value map index mismatch."); } int? priority = _entries[j].Priority; if (priority.HasValue && PrioritySetInvariant.HasConflict(_entries, j, priority)) { throw new InvalidOperationException($"Ordering conflict at index {j}."); } } } } private ConflictResolution ResolvePolicy(ConflictResolution? resolution) { return resolution ?? DefaultConflictResolution; } private DuplicateValuePolicy ResolveDuplicatePolicy(DuplicateValuePolicy? policy) { return policy ?? DefaultDuplicateValuePolicy; } private PlacementIndexBias ResolvePlacementIndexBias(PlacementIndexBias? placementIndexBias) { return placementIndexBias ?? DefaultPlacementIndexBias; } private PlacementIndexBias ResolveEnqueuePlacementIndexBias(PlacementIndexBias? placementIndexBias) { return placementIndexBias ?? PlacementIndexBias.LowestValidIndex; } private void ValidateIndex(int index) { if ((uint)index >= (uint)_entries.Count) { throw new ArgumentOutOfRangeException("index"); } } private void RebuildIndexMap() { _indexByValue.Clear(); for (int i = 0; i < _entries.Count; i++) { T value = _entries[i].Value; if (value != null) { _indexByValue[value] = i; } } } private int IndexOfNullReference() { for (int i = 0; i < _entries.Count; i++) { if (_entries[i].Value == null) { return i; } } return -1; } private bool TryGetIndexForLookup([AllowNull] T value, out int index) { if (value == null) { index = IndexOfNullReference(); return index >= 0; } return _indexByValue.TryGetValue(value, out index); } private bool TryGetIndexForPriority(int priority, out int index) { index = -1; if (!_priorityToValue.TryGetValue(priority, out var value)) { return false; } if (value != null && _indexByValue.TryGetValue(value, out var value2)) { index = value2; return true; } for (int i = 0; i < _entries.Count; i++) { int? priority2 = _entries[i].Priority; if (priority2.HasValue) { int valueOrDefault = priority2.GetValueOrDefault(); if (valueOrDefault == priority) { index = i; return true; } } } return false; } private bool TryResolveSlotIndex(int? index, int? priority, [AllowNull] T value, bool byValue, out int resolvedIndex) { resolvedIndex = -1; if (byValue) { if (index.HasValue || priority.HasValue) { throw new ArgumentException("Specify only one slot locator (value)."); } return TryGetIndexForLookup(value, out resolvedIndex); } if (index.HasValue) { int valueOrDefault = index.GetValueOrDefault(); if (priority.HasValue) { throw new ArgumentException("Specify only one slot locator (index or priority)."); } if ((uint)valueOrDefault >= (uint)_entries.Count) { return false; } resolvedIndex = valueOrDefault; return true; } if (priority.HasValue) { int valueOrDefault2 = priority.GetValueOrDefault(); return TryGetIndexForPriority(valueOrDefault2, out resolvedIndex); } throw new ArgumentException("Specify one slot locator: index, priority, or value."); } private static int ComputeRelativeInsertIndex(int anchorIndex, bool insertAfter) { if (!insertAfter) { return anchorIndex; } return anchorIndex + 1; } private bool IsAlreadyInSet([AllowNull] T value) { if (value != null) { return _indexByValue.ContainsKey(value); } return false; } private void SetIndexForValue([AllowNull] T value, int index) { if (value != null) { _indexByValue[value] = index; } } private void RemoveIndexForValue([AllowNull] T value) { if (value != null) { _indexByValue.Remove(value); } } private bool ContainsValueIn(IEnumerable values, [AllowNull] T value) { foreach (T value2 in values) { if (_comparer.Equals(value2, value)) { return true; } } return false; } private void RebuildPriorityMapFromEntries() { _priorityToValue.Clear(); for (int i = 0; i < _entries.Count; i++) { int? priority = _entries[i].Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); _priorityToValue[valueOrDefault] = _entries[i].Value; } } } private void RegisterPriority(int priority, [AllowNull] T value) { if (_priorityToValue.TryGetValue(priority, out var value2) && !_comparer.Equals(value2, value)) { throw new InvalidOperationException($"Duplicate priority {priority}."); } _priorityToValue[priority] = value; } private void UnregisterPriority(int? priority, [AllowNull] T value) { if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); if (_priorityToValue.TryGetValue(valueOrDefault, out var value2) && _comparer.Equals(value2, value)) { _priorityToValue.Remove(valueOrDefault); } } } private void UpdateEntryPriority(Entry entry, int? newPriority) { UnregisterPriority(entry.Priority, entry.Value); entry.Priority = newPriority; if (newPriority.HasValue) { int valueOrDefault = newPriority.GetValueOrDefault(); RegisterPriority(valueOrDefault, entry.Value); } } public T[] ToArray() { T[] array = new T[_entries.Count]; for (int i = 0; i < _entries.Count; i++) { array[i] = _entries[i].Value; } return array; } public List ToList() { List list = new List(_entries.Count); for (int i = 0; i < _entries.Count; i++) { list.Add(_entries[i].Value); } return list; } public HashSet ToHashSet() { HashSet hashSet = new HashSet(_comparer); for (int i = 0; i < _entries.Count; i++) { T value = _entries[i].Value; if (value != null) { hashSet.Add(value); } } return hashSet; } public ISet ToSet() { return ToHashSet(); } public static implicit operator T[](PrioritySet set) { return set.ToArray(); } public static implicit operator List(PrioritySet set) { return set.ToList(); } public static explicit operator HashSet(PrioritySet set) { return set.ToHashSet(); } public bool TryAdd([AllowNull] T value, int? index = null, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null, PlacementIndexBias? placementIndexBias = null) { bool hasValue = index.HasValue; bool hasValue2 = priority.HasValue; if (IsAlreadyInSet(value)) { if (!hasValue && !hasValue2) { return HandleDuplicateAdd(value, duplicateValuePolicy); } ApplyAddToExistingSlot(value, index, priority, resolution); int? index2 = null; int value2; if (value == null) { int num = IndexOfNullReference(); if (num >= 0) { index2 = num; } } else if (_indexByValue.TryGetValue(value, out value2)) { index2 = value2; } int? obj; if (index2.HasValue) { int valueOrDefault = index2.GetValueOrDefault(); obj = _entries[valueOrDefault].Priority; } else { obj = null; } int? priority2 = obj; PrioritySetChangeKind prioritySetChangeKind = PrioritySetChangeKind.None; if (hasValue) { prioritySetChangeKind |= PrioritySetChangeKind.Reorder; } if (hasValue2) { prioritySetChangeKind |= PrioritySetChangeKind.PriorityChange; } NotifyChanged(prioritySetChangeKind, index2, value, priority2); return true; } PrioritySetChangeKind prioritySetChangeKind2 = PrioritySetChangeKind.None; prioritySetChangeKind2 |= PrioritySetChangeKind.Add; if (hasValue) { prioritySetChangeKind2 |= PrioritySetChangeKind.Reorder; } if (hasValue2) { prioritySetChangeKind2 |= PrioritySetChangeKind.PriorityChange; } if (!hasValue && !hasValue2) { InsertCore(_entries.Count, value, null); int value3; int? index3 = ((value == null) ? new int?(IndexOfNullReference()) : (_indexByValue.TryGetValue(value, out value3) ? new int?(value3) : ((int?)null))); int? obj2; if (index3.HasValue) { int valueOrDefault2 = index3.GetValueOrDefault(); obj2 = _entries[valueOrDefault2].Priority; } else { obj2 = null; } int? priority3 = obj2; NotifyChanged(prioritySetChangeKind2, index3, value, priority3); return true; } InsertWithPlacement(value, index, priority, resolution, ResolvePlacementIndexBias(placementIndexBias)); int? index4 = null; int value4; if (value == null) { int num2 = IndexOfNullReference(); if (num2 >= 0) { index4 = num2; } } else if (_indexByValue.TryGetValue(value, out value4)) { index4 = value4; } int? obj3; if (index4.HasValue) { int valueOrDefault3 = index4.GetValueOrDefault(); obj3 = _entries[valueOrDefault3].Priority; } else { obj3 = null; } int? priority4 = obj3; NotifyChanged(prioritySetChangeKind2, index4, value, priority4); return true; } public bool Add([AllowNull] T value, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null, PlacementIndexBias? placementIndexBias = null) { return TryAdd(value, null, priority, resolution, duplicateValuePolicy, placementIndexBias); } public bool AddAt([AllowNull] T value, int index, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { return TryAdd(value, index, null, resolution, duplicateValuePolicy); } public bool AddAt([AllowNull] T value, int index, int priority, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { return TryAdd(value, index, priority, resolution, duplicateValuePolicy); } void ICollection.Add(T value) { Add(value); } bool ISet.Add(T value) { return Add(value); } public void Insert(int index, [AllowNull] T value) { TryInsert(index, value); } public void Insert(int index, [AllowNull] T value, int? priority, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { TryInsert(index, value, priority, resolution, duplicateValuePolicy); } public bool TryInsert(int index, [AllowNull] T value) { return TryInsert(index, value, null); } public bool TryInsert(int index, [AllowNull] T value, int? priority, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (IsAlreadyInSet(value) && !TryPrepareDuplicateForInsert(value, duplicateValuePolicy)) { return false; } if (!priority.HasValue) { InsertCore(index, value, null); int value2; int? index2 = ((value == null) ? new int?(IndexOfNullReference()) : (_indexByValue.TryGetValue(value, out value2) ? new int?(value2) : ((int?)null))); int? obj; if (index2.HasValue) { int valueOrDefault = index2.GetValueOrDefault(); obj = _entries[valueOrDefault].Priority; } else { obj = null; } int? priority2 = obj; NotifyChanged(PrioritySetChangeKind.Add, index2, value, priority2); return true; } InsertWithPlacement(value, index, priority, resolution, ResolvePlacementIndexBias(null)); SyncPriorityMap(); int? index3 = null; int value3; if (value == null) { int num = IndexOfNullReference(); if (num >= 0) { index3 = num; } } else if (_indexByValue.TryGetValue(value, out value3)) { index3 = value3; } int? obj2; if (index3.HasValue) { int valueOrDefault2 = index3.GetValueOrDefault(); obj2 = _entries[valueOrDefault2].Priority; } else { obj2 = null; } int? priority3 = obj2; PrioritySetChangeKind prioritySetChangeKind = PrioritySetChangeKind.Add; if (priority.HasValue) { prioritySetChangeKind |= PrioritySetChangeKind.PriorityChange; } NotifyChanged(prioritySetChangeKind, index3, value, priority3); return true; } void IList.Insert(int index, T item) { Insert(index, item); } public void InsertRelativeAt([AllowNull] T value, int anchorIndex, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { TryInsertRelativeAt(value, anchorIndex, insertAfter, priority, resolution, duplicateValuePolicy); } public void InsertRelativeByPriority([AllowNull] T value, int anchorPriority, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { TryInsertRelativeByPriority(value, anchorPriority, insertAfter, priority, resolution, duplicateValuePolicy); } public void InsertRelative([AllowNull] T value, [AllowNull] T anchorValue, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { TryInsertRelative(value, anchorValue, insertAfter, priority, resolution, duplicateValuePolicy); } public bool TryInsertRelativeAt([AllowNull] T value, int anchorIndex, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (!TryResolveSlotIndex(anchorIndex, null, default(T), byValue: false, out var resolvedIndex)) { return false; } int index = ComputeRelativeInsertIndex(resolvedIndex, insertAfter); return TryInsert(index, value, priority, resolution, duplicateValuePolicy); } public bool TryInsertRelativeByPriority([AllowNull] T value, int anchorPriority, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (!TryResolveSlotIndex(null, anchorPriority, default(T), byValue: false, out var resolvedIndex)) { return false; } int index = ComputeRelativeInsertIndex(resolvedIndex, insertAfter); return TryInsert(index, value, priority, resolution, duplicateValuePolicy); } public bool TryInsertRelative([AllowNull] T value, [AllowNull] T anchorValue, bool insertAfter = true, int? priority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (!TryResolveSlotIndex(null, null, anchorValue, byValue: true, out var resolvedIndex)) { return false; } int index = ComputeRelativeInsertIndex(resolvedIndex, insertAfter); return TryInsert(index, value, priority, resolution, duplicateValuePolicy); } public bool Remove([AllowNull] T value) { if (!TryGetIndexForLookup(value, out var index)) { return false; } RemoveAt(index); return true; } public bool TryRemoveAt(int index) { if (index < 0 || index >= _entries.Count) { return false; } Entry entry = _entries[index]; UnregisterPriority(entry.Priority, entry.Value); _entries.RemoveAt(index); RemoveIndexForValue(entry.Value); RebuildIndexMap(); EnsurePriorityOrdering(); NotifyChanged(PrioritySetChangeKind.Remove, index, entry.Value, entry.Priority); return true; } public void RemoveAt(int index) { if (!TryRemoveAt(index)) { throw new ArgumentOutOfRangeException("index"); } } public bool RemoveByPriority(int priority) { return TryRemoveByPriority(priority); } public bool TryRemoveByPriority(int priority) { if (!TryResolveSlotIndex(null, priority, default(T), byValue: false, out var resolvedIndex)) { return false; } return TryRemoveAt(resolvedIndex); } public void SetPriority([AllowNull] T value, int newPriority, ConflictResolution? resolution = null) { TrySetPriority(value, newPriority, resolution); } public bool TrySetPriority([AllowNull] T value, int newPriority, ConflictResolution? resolution = null) { if (!TryGetIndexForLookup(value, out var index)) { return false; } return TrySetPriorityAt(index, newPriority, resolution); } public void SetPriorityAt(int index, int newPriority, ConflictResolution? resolution = null) { if (!TrySetPriorityAt(index, newPriority, resolution)) { throw new ArgumentOutOfRangeException("index"); } } public bool TrySetPriorityAt(int index, int newPriority, ConflictResolution? resolution = null) { if ((uint)index >= (uint)_entries.Count) { return false; } T value = _entries[index].Value; SetPriorityAtCore(index, newPriority, resolution); int? index2 = null; int value2; if (value == null) { int num = IndexOfNullReference(); if (num >= 0) { index2 = num; } } else if (_indexByValue.TryGetValue(value, out value2)) { index2 = value2; } int? num2; if (index2.HasValue) { int valueOrDefault = index2.GetValueOrDefault(); num2 = _entries[valueOrDefault].Priority; } else { num2 = newPriority; } int? priority = num2; NotifyChanged(PrioritySetChangeKind.PriorityChange, index2, value, priority); return true; } public void SetPriorityByPriority(int currentPriority, int newPriority, ConflictResolution? resolution = null) { TrySetPriorityByPriority(currentPriority, newPriority, resolution); } public bool TrySetPriorityByPriority(int currentPriority, int newPriority, ConflictResolution? resolution = null) { if (!_priorityToValue.TryGetValue(currentPriority, out var value)) { return false; } return TrySetPriority(value, newPriority, resolution); } public void ClearPriority([AllowNull] T value) { TryClearPriority(value); } public bool TryClearPriority([AllowNull] T value) { if (!TryGetIndexForLookup(value, out var index)) { return false; } return TryClearPriorityAt(index); } public void ClearPriorityAt(int index) { ValidateIndex(index); TryClearPriorityAt(index); } public bool TryClearPriorityAt(int index) { if ((uint)index >= (uint)_entries.Count) { return false; } Entry entry = _entries[index]; if (!entry.Priority.HasValue) { return false; } UpdateEntryPriority(entry, null); NotifyChanged(PrioritySetChangeKind.PriorityChange, index, entry.Value); return true; } public void ClearPriorityByPriority(int priority) { TryClearPriorityByPriority(priority); } public bool TryClearPriorityByPriority(int priority) { if (!TryResolveSlotIndex(null, priority, default(T), byValue: false, out var resolvedIndex)) { return false; } return TryClearPriorityAt(resolvedIndex); } public int ReplaceValuesWhere(Func shouldReplace, [AllowNull] T replacement, DuplicateValuePolicy? duplicateValuePolicy = null) { ArgumentNullException.ThrowIfNull(shouldReplace, "shouldReplace"); int num = 0; for (int i = 0; i < _entries.Count; i++) { T value = _entries[i].Value; if (shouldReplace(value) && !_comparer.Equals(value, replacement) && TryAssignValueAt(i, replacement, null, duplicateValuePolicy)) { num++; } } if (num > 0) { NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } return num; } public void SetValueAt(int index, [AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy = null, ConflictResolution? resolution = null) { TrySetValueAt(index, value, duplicateValuePolicy, resolution); } public bool TrySetValueAt(int index, [AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy = null, ConflictResolution? resolution = null) { ValidateIndex(index); T value2 = _entries[index].Value; if (_comparer.Equals(value2, value)) { return true; } if (!TryAssignValueAt(index, value, resolution, duplicateValuePolicy)) { return false; } int? index2; if (value != null) { index2 = ((!_indexByValue.TryGetValue(value, out var value3)) ? ((int?)null) : new int?(value3)); } else { int num = IndexOfNullReference(); index2 = ((num >= 0) ? new int?(num) : ((int?)null)); } int? obj; if (index2.HasValue) { int valueOrDefault = index2.GetValueOrDefault(); obj = _entries[valueOrDefault].Priority; } else { obj = null; } int? priority = obj; NotifyChanged(PrioritySetChangeKind.Update, index2, value, priority); return true; } public void SetValueByPriority(int priority, [AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy = null, ConflictResolution? resolution = null) { TrySetValueByPriority(priority, value, duplicateValuePolicy, resolution); } public bool TrySetValueByPriority(int priority, [AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy = null, ConflictResolution? resolution = null) { if (!TryGetIndexForPriority(priority, out var index)) { return false; } return TrySetValueAt(index, value, duplicateValuePolicy, resolution); } private void ReplaceEntryValueAt(int index, [AllowNull] T newValue) { ValidateIndex(index); Entry entry = _entries[index]; T value = entry.Value; if (!_comparer.Equals(value, newValue)) { RemoveIndexForValue(value); int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); UnregisterPriority(valueOrDefault, value); } entry.Value = newValue; SetIndexForValue(newValue, index); priority = entry.Priority; if (priority.HasValue) { int valueOrDefault2 = priority.GetValueOrDefault(); RegisterPriority(valueOrDefault2, newValue); } } } private bool TryAssignValueAt(int index, [AllowNull] T value, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { ValidateIndex(index); if (value != null && _indexByValue.TryGetValue(value, out var value2) && value2 != index) { switch (ResolveDuplicatePolicy(duplicateValuePolicy)) { case DuplicateValuePolicy.ThrowOnDuplicate: throw new InvalidOperationException("Value already exists at another index."); case DuplicateValuePolicy.Reject: return false; case DuplicateValuePolicy.ReplaceExisting: { int? priority = _entries[value2].Priority; RemoveAt(value2); if (value2 < index) { index--; } if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); PlaceAt(index, value, valueOrDefault, ResolvePolicy(resolution)); } else { InsertCore(index, value, null); ResolvePlacement(index, indexSpecified: true, prioritySpecified: false, ResolvePolicy(resolution)); } SyncPriorityMap(); return true; } } } if (_comparer.Equals(_entries[index].Value, value)) { return true; } ReplaceEntryValueAt(index, value); if (_entries[index].Priority.HasValue) { ResolvePlacement(index, indexSpecified: true, prioritySpecified: true, ResolvePolicy(resolution)); } SyncPriorityMap(); return true; } private bool HandleDuplicateAdd([AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy) { switch (ResolveDuplicatePolicy(duplicateValuePolicy)) { case DuplicateValuePolicy.ThrowOnDuplicate: throw new InvalidOperationException("Value already exists in the set."); case DuplicateValuePolicy.Reject: return false; case DuplicateValuePolicy.ReplaceExisting: { Remove(value); InsertCore(_entries.Count, value, null); int value2; int? index = ((value == null) ? new int?(IndexOfNullReference()) : (_indexByValue.TryGetValue(value, out value2) ? new int?(value2) : ((int?)null))); int? obj; if (index.HasValue) { int valueOrDefault = index.GetValueOrDefault(); obj = _entries[valueOrDefault].Priority; } else { obj = null; } int? priority = obj; NotifyChanged(PrioritySetChangeKind.Add, index, value, priority); return true; } default: return false; } } private bool TryPrepareDuplicateForInsert([AllowNull] T value, DuplicateValuePolicy? duplicateValuePolicy) { switch (ResolveDuplicatePolicy(duplicateValuePolicy)) { case DuplicateValuePolicy.ThrowOnDuplicate: throw new InvalidOperationException("Value already exists in the set."); case DuplicateValuePolicy.Reject: return false; case DuplicateValuePolicy.ReplaceExisting: Remove(value); return true; default: return false; } } private void SetPriorityAtCore(int index, int newPriority, ConflictResolution? resolution) { Entry entry = _entries[index]; if (entry.Priority == newPriority && !PrioritySetInvariant.HasConflict(_entries, index, newPriority)) { return; } int? priority = entry.Priority; UnregisterPriority(entry.Priority, entry.Value); entry.Priority = newPriority; if (!PrioritySetInvariant.HasConflict(_entries, index, newPriority)) { RegisterPriority(newPriority, entry.Value); return; } ConflictResolution conflictResolution = ResolvePolicy(resolution); if (conflictResolution == ConflictResolution.ThrowOnConflict) { entry.Priority = priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); RegisterPriority(valueOrDefault, entry.Value); } throw new InvalidOperationException("Priority conflict."); } ResolvePlacement(index, indexSpecified: false, prioritySpecified: true, conflictResolution); SyncPriorityMap(); } private void InsertWithPlacement([AllowNull] T value, int? index, int? priority, ConflictResolution? resolution, PlacementIndexBias placementIndexBias) { ConflictResolution policy = ResolvePolicy(resolution); bool hasValue = index.HasValue; bool hasValue2 = priority.HasValue; if (!hasValue && hasValue2) { int value2 = priority.Value; int index2 = ResolveTargetIndexForPriorityOnly(value, value2, null, placementIndexBias); InsertCore(index2, value, value2, syncPriorityMap: false); ResolvePlacement(index2, indexSpecified: false, prioritySpecified: true, policy); SyncPriorityMap(); } else if (hasValue && !hasValue2) { int value3 = index.Value; value3 = Math.Clamp(value3, 0, _entries.Count); InsertCore(value3, value, null); ResolvePlacement(value3, indexSpecified: true, prioritySpecified: false, policy); SyncPriorityMap(); } else if (hasValue && hasValue2) { int index3 = Math.Clamp(index.Value, 0, _entries.Count); int value4 = priority.Value; if (!PrioritySetInvariant.TryFindHighestValidIndex(_entries, value4, value, out var _)) { ShiftPrioritiesAtOrAbove(value4); } PlaceAt(index3, value, value4, policy); } else { InsertCore(_entries.Count, value, null); } } private void PlaceAt(int index, [AllowNull] T value, int priority, ConflictResolution policy) { if (IsAlreadyInSet(value)) { throw new InvalidOperationException("Value already exists in the set."); } InsertCore(index, value, priority, syncPriorityMap: false); if (policy == ConflictResolution.ThrowOnConflict && PrioritySetInvariant.HasConflict(_entries, index, priority)) { RemoveAt(index); throw new InvalidOperationException("Index and priority conflict."); } ResolvePlacement(index, indexSpecified: true, prioritySpecified: true, policy); SyncPriorityMap(); } private void ResolvePlacement(int index, bool indexSpecified, bool prioritySpecified, ConflictResolution policy) { ValidateIndex(index); Entry entry = _entries[index]; T value = entry.Value; int? priority = entry.Priority; if (!priority.HasValue || !PrioritySetInvariant.HasConflict(_entries, index, priority)) { return; } if (indexSpecified && prioritySpecified) { switch (policy) { case ConflictResolution.ThrowOnConflict: throw new InvalidOperationException("Index and priority conflict."); case ConflictResolution.KeepIndex: ApplyPriorityAtIndex(index, priority, value, allowMoveFallback: true); return; case ConflictResolution.KeepPriority: if (!TryMoveToClosestValidIndex(index, priority.Value, value, policy)) { ApplyPriorityAtIndex(index, priority, value, allowMoveFallback: false); } return; } } if (indexSpecified) { ApplyPriorityAtIndex(index, priority, value, allowMoveFallback: true); } else if (prioritySpecified && !TryMoveToClosestValidIndex(index, priority.Value, value, policy)) { ApplyPriorityAtIndex(index, priority, value, allowMoveFallback: false); } } private void ApplyPriorityAtIndex(int index, int? priority, T value, bool allowMoveFallback) { try { _entries[index].Priority = PrioritySetInvariant.FindPriorityForIndex(_entries, index, priority); } catch (InvalidOperationException) when (allowMoveFallback) { int? num = priority ?? _entries[index].Priority; if (num.HasValue) { int valueOrDefault = num.GetValueOrDefault(); if (TryMoveToClosestValidIndex(index, valueOrDefault, value, ConflictResolution.KeepPriority)) { return; } } UpdateEntryPriority(_entries[index], null); } } private bool TryMoveToClosestValidIndex(int index, int priority, T value, ConflictResolution policy) { if (policy == ConflictResolution.KeepIndex) { return false; } try { int num = PrioritySetInvariant.FindClosestValidIndex(_entries, index, priority, value, index); if (num != index) { MoveEntry(index, num); } return true; } catch (InvalidOperationException) { return false; } } private Entry InsertCore(int index, [AllowNull] T value, int? priority, bool syncPriorityMap = true) { index = Math.Clamp(index, 0, _entries.Count); Entry entry = new Entry(value, priority); _entries.Insert(index, entry); RebuildIndexMap(); if (syncPriorityMap && priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); RegisterPriority(valueOrDefault, value); } return entry; } private void SyncPriorityMap() { RebuildPriorityMapFromEntries(); } private int ResolveTargetIndexForPriorityOnly([AllowNull] T value, int priority, int? requestedIndex, PlacementIndexBias placementIndexBias) { if (requestedIndex.HasValue) { int valueOrDefault = requestedIndex.GetValueOrDefault(); valueOrDefault = Math.Clamp(valueOrDefault, 0, _entries.Count); if (!PrioritySetInvariant.TryFindHighestValidIndex(_entries, priority, value, out var _)) { ShiftPrioritiesAtOrAbove(priority); } return valueOrDefault; } if (placementIndexBias == PlacementIndexBias.LowestValidIndex) { if (PrioritySetInvariant.TryFindLowestValidIndex(_entries, priority, value, out var index2)) { return index2; } ShiftPrioritiesAtOrAbove(priority); if (PrioritySetInvariant.TryFindLowestValidIndex(_entries, priority, value, out index2)) { return index2; } return _entries.Count; } if (PrioritySetInvariant.TryFindHighestValidIndex(_entries, priority, value, out var index3)) { return index3; } ShiftPrioritiesAtOrAbove(priority); if (PrioritySetInvariant.TryFindHighestValidIndex(_entries, priority, value, out index3)) { return index3; } return _entries.Count; } private void ShiftPrioritiesAtOrAbove(int threshold) { List> list = new List>(); foreach (Entry entry in _entries) { int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); if (valueOrDefault >= threshold) { list.Add(entry); } } } list.Sort((Entry a, Entry b) => b.Priority.Value.CompareTo(a.Priority.Value)); foreach (Entry item in list) { int value = item.Priority.Value; if (value == int.MaxValue) { throw new InvalidOperationException("Cannot shift priority at int.MaxValue."); } UnregisterPriority(value, item.Value); int num = value + 1; item.Priority = num; RegisterPriority(num, item.Value); } } private void ApplyAddToExistingSlot([AllowNull] T value, int? index, int? priority, ConflictResolution? resolution) { if (!TryGetIndexForLookup(value, out var index2)) { return; } if (index.HasValue) { int valueOrDefault = index.GetValueOrDefault(); valueOrDefault = Math.Clamp(valueOrDefault, 0, _entries.Count - 1); if (valueOrDefault != index2) { MoveEntry(index2, valueOrDefault); index2 = valueOrDefault; } } if (priority.HasValue) { int valueOrDefault2 = priority.GetValueOrDefault(); if (_priorityToValue.TryGetValue(valueOrDefault2, out var value2) && !_comparer.Equals(value2, value)) { ShiftPrioritiesAtOrAbove(valueOrDefault2); } SetPriorityAtCore(index2, valueOrDefault2, resolution); } RepairOrderingAfterIndexChange(resolution); } public void Enqueue([AllowNull] T value) { TryEnqueue(value); } public void Enqueue([AllowNull] T value, int priority, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null, PlacementIndexBias? placementIndexBias = null) { TryEnqueue(value, priority, resolution, duplicateValuePolicy, placementIndexBias); } public bool TryEnqueue([AllowNull] T value) { return Add(value); } public bool TryEnqueue([AllowNull] T value, int priority, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null, PlacementIndexBias? placementIndexBias = null) { try { return Add(value, priority, resolution, duplicateValuePolicy, ResolveEnqueuePlacementIndexBias(placementIndexBias)); } catch (InvalidOperationException) { return false; } } public T Dequeue() { if (!TryDequeue(out var result)) { throw new InvalidOperationException("The set is empty."); } return result; } public bool TryDequeue(out T result) { if (_entries.Count == 0) { result = default(T); return false; } T value = _entries[0].Value; RemoveAt(0); result = value; return true; } public T Peek() { if (!TryPeek(out var result)) { throw new InvalidOperationException("The set is empty."); } return result; } public bool TryPeek(out T result) { if (_entries.Count == 0) { result = default(T); return false; } result = _entries[0].Value; return true; } public IEnumerable ByPriorityRange(int minInclusive, int maxInclusive) { for (int i = 0; i < _entries.Count; i++) { int? priority = _entries[i].Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); if (valueOrDefault >= minInclusive && valueOrDefault <= maxInclusive) { yield return _entries[i].Value; } } } } public IEnumerable> EntriesByPriorityRange(int minInclusive, int maxInclusive) { for (int i = 0; i < _entries.Count; i++) { int? priority = _entries[i].Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); if (valueOrDefault >= minInclusive && valueOrDefault <= maxInclusive) { yield return new PrioritySetEntry(_entries[i].Value, valueOrDefault, i); } } } } internal void ApplyIndexPermutation(ReadOnlySpan newOrder) { if (newOrder.Length != _entries.Count) { throw new ArgumentException("Permutation length must match Count.", "newOrder"); } bool[] array = new bool[_entries.Count]; ReadOnlySpan readOnlySpan = newOrder; for (int i = 0; i < readOnlySpan.Length; i++) { int num = readOnlySpan[i]; if ((uint)num >= (uint)_entries.Count) { throw new ArgumentOutOfRangeException("newOrder"); } if (array[num]) { throw new ArgumentException("Permutation must be unique.", "newOrder"); } array[num] = true; } Entry[] array2 = new Entry[_entries.Count]; for (int j = 0; j < newOrder.Length; j++) { array2[j] = _entries[newOrder[j]]; } for (int k = 0; k < array2.Length; k++) { _entries[k] = array2[k]; } RebuildIndexMap(); RebuildPriorityMapFromEntries(); } internal void RepairOrderingAfterIndexChange(ConflictResolution? resolution = null) { ConflictResolution conflictResolution = ResolvePolicy(resolution); if (conflictResolution == ConflictResolution.ThrowOnConflict) { conflictResolution = ConflictResolution.KeepIndex; } int num = Math.Max(_entries.Count * 2, 1); for (int i = 0; i < num; i++) { bool flag = false; for (int j = 0; j < _entries.Count; j++) { Entry entry = _entries[j]; if (!entry.Priority.HasValue || !PrioritySetInvariant.HasConflict(_entries, j, entry.Priority)) { continue; } if (conflictResolution == ConflictResolution.KeepPriority) { try { int num2 = PrioritySetInvariant.FindClosestValidIndex(_entries, j, entry.Priority.Value, entry.Value, j); if (num2 != j) { MoveEntry(j, num2); flag = true; break; } } catch (InvalidOperationException) { } } try { int num3 = PrioritySetInvariant.FindPriorityForIndex(_entries, j, entry.Priority); if (num3 != entry.Priority) { UpdateEntryPriority(entry, num3); flag = true; continue; } } catch (InvalidOperationException) { RepairPrioritiesAfterSort(); return; } if (PrioritySetInvariant.HasConflict(_entries, j, entry.Priority) && conflictResolution != ConflictResolution.KeepIndex && TryMoveToClosestValidIndex(j, entry.Priority.Value, entry.Value, conflictResolution)) { flag = true; break; } } if (!flag) { break; } } for (int k = 0; k < _entries.Count; k++) { Entry entry2 = _entries[k]; if (entry2.Priority.HasValue && PrioritySetInvariant.HasConflict(_entries, k, entry2.Priority)) { RepairPrioritiesAfterSort(); break; } } SyncPriorityMap(); } internal void EnsurePriorityOrdering() { for (int i = 0; i < _entries.Count; i++) { Entry entry = _entries[i]; if (entry.Priority.HasValue && PrioritySetInvariant.HasConflict(_entries, i, entry.Priority)) { RepairPrioritiesAfterSort(); break; } } } internal void RepairPrioritiesAfterSort() { if (!HasAnyPriorityOrderingConflict()) { return; } List<(Entry, int)> list = new List<(Entry, int)>(); for (int i = 0; i < _entries.Count; i++) { Entry entry = _entries[i]; int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); list.Add((entry, valueOrDefault)); UnregisterPriority(entry.Priority, entry.Value); entry.Priority = null; } } HashSet hashSet = new HashSet(); int? num = null; foreach (var item in list) { var (entry2, num2) = item; if (num.HasValue) { int valueOrDefault2 = num.GetValueOrDefault(); if (num2 > valueOrDefault2) { num2 = valueOrDefault2; } } while (hashSet.Contains(num2)) { if (num2 == int.MinValue) { throw new InvalidOperationException("Cannot assign unique priorities after sort."); } num2--; } UpdateEntryPriority(entry2, num2); hashSet.Add(num2); num = num2; } SyncPriorityMap(); } private bool HasAnyPriorityOrderingConflict() { for (int i = 0; i < _entries.Count; i++) { Entry entry = _entries[i]; if (entry.Priority.HasValue && PrioritySetInvariant.HasConflict(_entries, i, entry.Priority)) { return true; } } return false; } private void MoveEntry(int fromIndex, int toIndex) { if (fromIndex != toIndex) { Entry item = _entries[fromIndex]; _entries.RemoveAt(fromIndex); toIndex = Math.Clamp(toIndex, 0, _entries.Count); _entries.Insert(toIndex, item); RebuildIndexMap(); SyncPriorityMap(); } } public void UnionWith(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); if (other == this) { return; } using (BeginChangedScope()) { if (other is PrioritySet prioritySet) { foreach (Entry entry in prioritySet._entries) { if (entry.Value == null || !Contains(entry.Value)) { int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); Add(entry.Value, valueOrDefault); } else { TryAdd(entry.Value); } } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); return; } foreach (T item in other) { if (item == null || !Contains(item)) { TryAdd(item); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public void UnionWith(IEnumerable other, ConflictResolution? resolution) { ArgumentNullException.ThrowIfNull(other, "other"); if (other == this) { return; } using (BeginChangedScope()) { if (other is PrioritySet prioritySet) { foreach (Entry entry in prioritySet._entries) { if (entry.Value == null || !Contains(entry.Value)) { int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); Add(entry.Value, valueOrDefault, resolution); } else { TryAdd(entry.Value); } } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); return; } foreach (T item in other) { if (item == null || !Contains(item)) { TryAdd(item); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public void IntersectWith(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); using (BeginChangedScope()) { CollectUniqueMembers(other, out HashSet nonNullMembers, out bool hasNull); for (int num = Count - 1; num >= 0; num--) { T value = _entries[num].Value; if (value == null) { if (!hasNull) { RemoveAt(num); } } else if (!nonNullMembers.Contains(value)) { RemoveAt(num); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public void ExceptWith(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); using (BeginChangedScope()) { if (other == this) { Clear(); NotifyChanged(PrioritySetChangeKind.BulkSetOperation); return; } foreach (T item in other) { Remove(item); } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public void SymmetricExceptWith(IEnumerable other, ConflictResolution? resolution) { ArgumentNullException.ThrowIfNull(other, "other"); using (BeginChangedScope()) { CollectUniqueMembers(other, out HashSet nonNullMembers, out bool hasNull); if (other is PrioritySet prioritySet) { bool flag = false; foreach (Entry entry in prioritySet._entries) { if (entry.Value == null) { flag = true; } else if (!Remove(entry.Value)) { int? priority = entry.Priority; if (priority.HasValue) { int valueOrDefault = priority.GetValueOrDefault(); Add(entry.Value, valueOrDefault, resolution); } else { TryAdd(entry.Value); } } } if (flag) { if (IndexOfNullReference() >= 0) { while (Remove(default(T))) { } } else { TryAdd(default(T)); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); return; } foreach (T item in nonNullMembers) { if (!Remove(item)) { TryAdd(item); } } if (hasNull) { if (IndexOfNullReference() >= 0) { while (Remove(default(T))) { } } else { TryAdd(default(T)); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public void SymmetricExceptWith(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); using (BeginChangedScope()) { CollectUniqueMembers(other, out HashSet nonNullMembers, out bool hasNull); foreach (T item in nonNullMembers) { if (!Remove(item)) { TryAdd(item); } } if (hasNull) { if (IndexOfNullReference() >= 0) { while (Remove(default(T))) { } } else { TryAdd(default(T)); } } NotifyChanged(PrioritySetChangeKind.BulkSetOperation); } } public bool IsSubsetOf(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(this, out HashSet nonNullMembers, out bool hasNull); CollectUniqueMembers(other, out HashSet nonNullMembers2, out bool hasNull2); foreach (T item in nonNullMembers) { if (!nonNullMembers2.Contains(item)) { return false; } } if (hasNull && !hasNull2) { return false; } return true; } public bool IsProperSubsetOf(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(this, out HashSet nonNullMembers, out bool hasNull); CollectUniqueMembers(other, out HashSet nonNullMembers2, out bool hasNull2); int num = nonNullMembers.Count + (hasNull ? 1 : 0); int num2 = nonNullMembers2.Count + (hasNull2 ? 1 : 0); if (IsSubsetOf(other)) { return num < num2; } return false; } public bool IsSupersetOf(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(other, out HashSet nonNullMembers, out bool hasNull); foreach (T item in nonNullMembers) { if (!Contains(item)) { return false; } } if (hasNull && IndexOfNullReference() < 0) { return false; } return true; } public bool IsProperSupersetOf(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(other, out HashSet nonNullMembers, out bool hasNull); int num = nonNullMembers.Count + (hasNull ? 1 : 0); int uniqueMemberCount = GetUniqueMemberCount(); if (IsSupersetOf(other)) { return uniqueMemberCount > num; } return false; } public bool Overlaps(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(this, out HashSet nonNullMembers, out bool hasNull); CollectUniqueMembers(other, out HashSet nonNullMembers2, out bool hasNull2); if (hasNull && hasNull2) { return true; } foreach (T item in nonNullMembers) { if (nonNullMembers2.Contains(item)) { return true; } } return false; } public bool SetEquals(IEnumerable other) { ArgumentNullException.ThrowIfNull(other, "other"); CollectUniqueMembers(this, out HashSet nonNullMembers, out bool hasNull); CollectUniqueMembers(other, out HashSet nonNullMembers2, out bool hasNull2); if (hasNull != hasNull2) { return false; } return nonNullMembers.SetEquals(nonNullMembers2); } private int GetUniqueMemberCount() { CollectUniqueMembers(this, out HashSet nonNullMembers, out bool hasNull); return nonNullMembers.Count + (hasNull ? 1 : 0); } private void CollectUniqueMembers(IEnumerable source, out HashSet nonNullMembers, out bool hasNull) { nonNullMembers = new HashSet(_comparer); hasNull = false; foreach (T item in source) { if (item == null) { hasNull = true; } else { nonNullMembers.Add(item); } } } public PrioritySet(PrioritySet source) : this(source?._comparer) { ArgumentNullException.ThrowIfNull(source, "source"); DefaultConflictResolution = source.DefaultConflictResolution; DefaultDuplicateValuePolicy = source.DefaultDuplicateValuePolicy; DefaultPlacementIndexBias = source.DefaultPlacementIndexBias; _entries.Clear(); for (int i = 0; i < source._entries.Count; i++) { Entry entry = source._entries[i]; _entries.Add(new Entry(entry.Value, entry.Priority)); } RebuildIndexMap(); SyncPriorityMap(); } public PrioritySetSnapshot CreateSnapshot() { PrioritySetEntry[] array = new PrioritySetEntry[_entries.Count]; for (int i = 0; i < _entries.Count; i++) { array[i] = new PrioritySetEntry(_entries[i].Value, _entries[i].Priority, i); } return new PrioritySetSnapshot(array); } public PrioritySet Clone() { return new PrioritySet(this); } public void Sort(Comparison comparison) { ArgumentNullException.ThrowIfNull(comparison, "comparison"); if (_entries.Count > 1) { int[] array = (from x in (from i in Enumerable.Range(0, _entries.Count) select (Index: i, Value: _entries[i].Value)).OrderBy(((int Index, T Value) x) => x.Value, Comparer.Create(comparison)).ThenBy(((int Index, T Value) x) => x.Index) select x.Index).ToArray(); ApplyIndexPermutation(array); RepairPrioritiesAfterSort(); NotifyChanged(PrioritySetChangeKind.Sort); } } public void Sort(Func keySelector, IComparer? comparer = null) { ArgumentNullException.ThrowIfNull(keySelector, "keySelector"); if (comparer == null) { comparer = Comparer.Default; } Sort((T a, T b) => comparer.Compare(keySelector(a), keySelector(b))); } public void SetIndexAt(int index, int newIndex, ConflictResolution? resolution = null) { TrySetIndexAt(index, newIndex, resolution); } public bool TrySetIndexAt(int index, int newIndex, ConflictResolution? resolution = null) { if ((uint)index >= (uint)_entries.Count) { return false; } int num = Math.Clamp(newIndex, 0, _entries.Count - 1); if (num == index) { return true; } T value = _entries[index].Value; int? priority = _entries[index].Priority; MoveEntry(index, num); RepairOrderingAfterIndexChange(resolution); int? index2 = num; if (value != null && _indexByValue.TryGetValue(value, out var value2)) { index2 = value2; } else if (value == null) { int num2 = IndexOfNullReference(); if (num2 >= 0) { index2 = num2; } } int? num3; if (index2.HasValue) { int valueOrDefault = index2.GetValueOrDefault(); num3 = _entries[valueOrDefault].Priority; } else { num3 = priority; } int? priority2 = num3; NotifyChanged(PrioritySetChangeKind.Reorder, index2, value, priority2); return true; } public void SetIndexByValue([AllowNull] T value, int newIndex, ConflictResolution? resolution = null) { TrySetIndexByValue(value, newIndex, resolution); } public bool TrySetIndexByValue([AllowNull] T value, int newIndex, ConflictResolution? resolution = null) { if (!TryResolveSlotIndex(null, null, value, byValue: true, out var resolvedIndex)) { return false; } return TrySetIndexAt(resolvedIndex, newIndex, resolution); } public void SetIndexByPriority(int currentPriority, int newIndex, ConflictResolution? resolution = null) { TrySetIndexByPriority(currentPriority, newIndex, resolution); } public bool TrySetIndexByPriority(int currentPriority, int newIndex, ConflictResolution? resolution = null) { if (!TryResolveSlotIndex(null, currentPriority, default(T), byValue: false, out var resolvedIndex)) { return false; } return TrySetIndexAt(resolvedIndex, newIndex, resolution); } public void SetSlotAt(int index, int newIndex, int newPriority, ConflictResolution? resolution = null) { TrySetSlotAt(index, newIndex, newPriority, resolution); } public bool TrySetSlotAt(int index, int newIndex, int newPriority, ConflictResolution? resolution = null) { return TrySetSlotAtCore(index, updateValue: false, default(T), newIndex, newPriority, resolution); } public void SetSlotAt(int index, [AllowNull] T newValue, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { TrySetSlotAt(index, newValue, newIndex, newPriority, resolution, duplicateValuePolicy); } public bool TrySetSlotAt(int index, [AllowNull] T newValue, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (1 + (newIndex.HasValue ? 1 : 0) + (newPriority.HasValue ? 1 : 0) < 2) { throw new ArgumentException("Changing only the value requires SetValueAt/TrySetValueAt. Changing only index or priority requires TrySetIndexAt or SetPriorityAt.", "newValue"); } return TrySetSlotAtCore(index, updateValue: true, newValue, newIndex, newPriority, resolution, duplicateValuePolicy); } public bool TrySetSlotByValue([AllowNull] T currentValue, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null) { if (!TryGetIndexForLookup(currentValue, out var index)) { return false; } if (!newIndex.HasValue && !newPriority.HasValue) { return true; } if (newIndex.HasValue) { int valueOrDefault = newIndex.GetValueOrDefault(); if (newPriority.HasValue) { int valueOrDefault2 = newPriority.GetValueOrDefault(); return TrySetSlotAt(index, valueOrDefault, valueOrDefault2, resolution); } } if (newIndex.HasValue) { int valueOrDefault3 = newIndex.GetValueOrDefault(); return TrySetIndexAt(index, valueOrDefault3, resolution); } return TrySetPriorityAt(index, newPriority.Value, resolution); } public bool TrySetSlotByValue([AllowNull] T currentValue, [AllowNull] T newValue, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (!TryGetIndexForLookup(currentValue, out var index)) { return false; } if (!newIndex.HasValue && !newPriority.HasValue) { return TrySetValueAt(index, newValue, duplicateValuePolicy, resolution); } return TrySetSlotAt(index, newValue, newIndex, newPriority, resolution, duplicateValuePolicy); } public bool TrySetSlotByPriority(int currentPriority, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null) { if (!TryGetIndexForPriority(currentPriority, out var index)) { return false; } if (!newIndex.HasValue && !newPriority.HasValue) { return true; } if (newIndex.HasValue) { int valueOrDefault = newIndex.GetValueOrDefault(); if (newPriority.HasValue) { int valueOrDefault2 = newPriority.GetValueOrDefault(); return TrySetSlotAt(index, valueOrDefault, valueOrDefault2, resolution); } } if (newIndex.HasValue) { int valueOrDefault3 = newIndex.GetValueOrDefault(); return TrySetIndexAt(index, valueOrDefault3, resolution); } return TrySetPriorityAt(index, newPriority.Value, resolution); } public bool TrySetSlotByPriority(int currentPriority, [AllowNull] T newValue, int? newIndex = null, int? newPriority = null, ConflictResolution? resolution = null, DuplicateValuePolicy? duplicateValuePolicy = null) { if (!TryGetIndexForPriority(currentPriority, out var index)) { return false; } if (!newIndex.HasValue && !newPriority.HasValue) { return TrySetValueAt(index, newValue, duplicateValuePolicy, resolution); } return TrySetSlotAt(index, newValue, newIndex, newPriority, resolution, duplicateValuePolicy); } private bool TrySetSlotAtCore(int index, bool updateValue, [AllowNull] T newValue, int? newIndex, int? newPriority, ConflictResolution? resolution, DuplicateValuePolicy? duplicateValuePolicy = null) { if ((uint)index >= (uint)_entries.Count) { return false; } if (!updateValue) { if (newIndex.HasValue) { newIndex.GetValueOrDefault(); if (newPriority.HasValue) { newPriority.GetValueOrDefault(); goto IL_009c; } } if (newIndex.HasValue) { int valueOrDefault = newIndex.GetValueOrDefault(); return TrySetIndexAt(index, valueOrDefault, resolution); } if (newPriority.HasValue) { int valueOrDefault2 = newPriority.GetValueOrDefault(); return TrySetPriorityAt(index, valueOrDefault2, resolution); } return false; } if (1 + (newIndex.HasValue ? 1 : 0) + (newPriority.HasValue ? 1 : 0) < 2) { return TrySetValueAt(index, newValue, duplicateValuePolicy, resolution); } goto IL_009c; IL_009c: T value = _entries[index].Value; T val = (T)(updateValue ? ((object)newValue) : ((object)value)); PrioritySetChangeKind prioritySetChangeKind = PrioritySetChangeKind.None; if (newIndex.HasValue) { int valueOrDefault3 = newIndex.GetValueOrDefault(); int num = Math.Clamp(valueOrDefault3, 0, _entries.Count - 1); if (num != index) { MoveEntry(index, num); index = num; prioritySetChangeKind |= PrioritySetChangeKind.Reorder; } } if (updateValue && !_comparer.Equals(_entries[index].Value, newValue)) { if (!TryAssignValueAt(index, newValue, resolution, duplicateValuePolicy)) { return false; } prioritySetChangeKind |= PrioritySetChangeKind.Update; } if (newPriority.HasValue) { int valueOrDefault4 = newPriority.GetValueOrDefault(); SetPriorityAtCore(index, valueOrDefault4, resolution); prioritySetChangeKind |= PrioritySetChangeKind.PriorityChange; } RepairOrderingAfterIndexChange(resolution); if (prioritySetChangeKind == PrioritySetChangeKind.None) { prioritySetChangeKind = PrioritySetChangeKind.Update; } int? index2 = null; if (val != null && _indexByValue.TryGetValue(val, out var value2)) { index2 = value2; } else if (val == null) { int num2 = IndexOfNullReference(); if (num2 >= 0) { index2 = num2; } } int? obj; if (index2.HasValue) { int valueOrDefault5 = index2.GetValueOrDefault(); obj = _entries[valueOrDefault5].Priority; } else { obj = null; } int? priority = obj; NotifyChanged(prioritySetChangeKind, index2, val, priority); return true; } } public sealed class PrioritySetSnapshot : IReadOnlyList>, IEnumerable>, IEnumerable, IReadOnlyCollection> { private readonly PrioritySetEntry[] _entries; public PrioritySetEntry this[int index] => _entries[index]; public int Count => _entries.Length; internal PrioritySetSnapshot(PrioritySetEntry[] entries) { _entries = entries; } public IEnumerator> GetEnumerator() { return ((IEnumerable>)_entries).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public readonly struct PrioritySetEntry { public T Value { get; } public int? Priority { get; } public int Index { get; } public PrioritySetEntry([AllowNull] T value, int? priority, int index) { Value = value; Priority = priority; Index = index; } } internal static class PrioritySetInvariant { public static int FindPreviousPrioritizedIndex(IReadOnlyList> entries, int index) { for (int num = index - 1; num >= 0; num--) { if (entries[num].Priority.HasValue) { return num; } } return -1; } public static int FindNextPrioritizedIndex(IReadOnlyList> entries, int index) { for (int i = index + 1; i < entries.Count; i++) { if (entries[i].Priority.HasValue) { return i; } } return -1; } public static bool HasDuplicatePriority(IReadOnlyList> entries, int index, int priority) { for (int i = 0; i < entries.Count; i++) { if (i != index && entries[i].Priority == priority) { return true; } } return false; } public static bool HasConflict(IReadOnlyList> entries, int index, int? priority) { if (!priority.HasValue) { return false; } int value = priority.Value; if (HasDuplicatePriority(entries, index, value)) { return true; } int num = FindPreviousPrioritizedIndex(entries, index); if (num >= 0 && entries[num].Priority.Value < value) { return true; } int num2 = FindNextPrioritizedIndex(entries, index); if (num2 >= 0 && value < entries[num2].Priority.Value) { return true; } return false; } public static bool IsValidPlacement(IReadOnlyList> entries, int index, int? priority) { return !HasConflict(entries, index, priority); } public static (int Min, int Max)? GetAllowedPriorityRange(IReadOnlyList> entries, int index) { int num = FindPreviousPrioritizedIndex(entries, index); int num2 = FindNextPrioritizedIndex(entries, index); if (num < 0 && num2 < 0) { return null; } int num3 = ((num >= 0) ? entries[num].Priority.Value : int.MaxValue); int num4 = ((num2 >= 0) ? entries[num2].Priority.Value : int.MinValue); if (num4 > num3) { return null; } return (num4, num3); } public static int FindPriorityForIndex(IReadOnlyList> entries, int index, int? requestedPriority) { if (requestedPriority.HasValue) { int valueOrDefault = requestedPriority.GetValueOrDefault(); if (IsValidPlacement(entries, index, valueOrDefault)) { return valueOrDefault; } } (int, int)? allowedPriorityRange = GetAllowedPriorityRange(entries, index); if (!allowedPriorityRange.HasValue) { if (requestedPriority.HasValue) { int valueOrDefault2 = requestedPriority.GetValueOrDefault(); if (!HasDuplicatePriority(entries, index, valueOrDefault2)) { return valueOrDefault2; } } for (int num = int.MaxValue; num >= int.MinValue; num--) { if (!HasDuplicatePriority(entries, index, num)) { return num; } } throw new InvalidOperationException("No valid priority exists for the requested index."); } var (num2, num3) = allowedPriorityRange.Value; if (requestedPriority.HasValue) { int valueOrDefault3 = requestedPriority.GetValueOrDefault(); int num4 = Math.Clamp(valueOrDefault3, num2, num3); if (!HasDuplicatePriority(entries, index, num4)) { return num4; } } for (int num5 = num3; num5 >= num2; num5--) { if (!HasDuplicatePriority(entries, index, num5)) { return num5; } } throw new InvalidOperationException("No valid priority exists for the requested index."); } public static int FindClosestValidIndex(IReadOnlyList> entries, int desiredIndex, int priority, T value, int sourceIndex = -1) { desiredIndex = Math.Clamp(desiredIndex, 0, entries.Count); int num = -1; int num2 = int.MaxValue; if (sourceIndex >= 0) { for (int i = 0; i < entries.Count; i++) { if (TrySimulateMove(entries, sourceIndex, i, priority)) { int num3 = Math.Abs(i - desiredIndex); if (num3 < num2 || (num3 == num2 && i < num)) { num2 = num3; num = i; } } } } else { for (int j = 0; j <= entries.Count; j++) { if (TrySimulatePlacement(entries, j, priority, value)) { int num4 = Math.Abs(j - desiredIndex); if (num4 < num2 || (num4 == num2 && j < num)) { num2 = num4; num = j; } } } } if (num < 0) { throw new InvalidOperationException("No valid index exists for the requested priority."); } return num; } public static bool TryFindHighestValidIndex(IReadOnlyList> entries, int priority, T value, out int index) { for (int num = entries.Count; num >= 0; num--) { if (TrySimulatePlacement(entries, num, priority, value)) { index = num; return true; } } index = 0; return false; } public static bool TryFindLowestValidIndex(IReadOnlyList> entries, int priority, T value, out int index) { for (int i = 0; i <= entries.Count; i++) { if (TrySimulatePlacement(entries, i, priority, value)) { index = i; return true; } } index = 0; return false; } private static bool TrySimulatePlacement(IReadOnlyList> entries, int index, int priority, T value) { List> list = new List>(entries); if (index > list.Count) { return false; } if (index == list.Count) { list.Add(new Entry(value, priority)); } else { list.Insert(index, new Entry(value, priority)); } return IsValidPlacement(list, index, priority); } private static bool TrySimulateMove(IReadOnlyList> entries, int fromIndex, int toIndex, int priority) { if ((uint)fromIndex >= (uint)entries.Count) { return false; } T value = entries[fromIndex].Value; List> list = new List>(); for (int i = 0; i < entries.Count; i++) { if (i != fromIndex) { list.Add(entries[i]); } } int index = Math.Clamp(toIndex, 0, list.Count); list.Insert(index, new Entry(value, priority)); return IsValidPlacement(list, index, priority); } } internal sealed class Entry { public T Value { get; [param: AllowNull] set; } public int? Priority { get; set; } public Entry([AllowNull] T value, int? priority) { Value = value; Priority = priority; } } }