// /* The MIT License (MIT) Copyright (c) 2016-2020 Maksim Volkau Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // ReSharper disable once InconsistentNaming #if !NET35 && !PCL #define SUPPORTS_SPIN_WAIT #endif namespace ImTools { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Diagnostics; using System.Runtime.CompilerServices; // For [MethodImpl(AggressiveInlining)] /// Helpers for functional composition public static class Fun { /// /// Always a true condition. /// public static bool Always(T _) => true; /// /// Identity function returning passed argument as result. /// public static T Id(T x) => x; /// /// Forward pipe operator (`|>` in F#) /// public static R To(this T x, Func map) => map(x); /// /// Forward pipe operator (`|>` in F#) with the additional state A for two arguments function /// public static R To(this T x, S state, Func map) => map(x, state); /// /// Cast to the R type with the forward pipe operator (`|>` in F#) /// public static R To(this object x) => (R)x; /// /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value /// public static T Do(this T x, Action effect) { effect(x); return x; } /// /// Forward pipe operator (`|>` in F#) but with side effect propagating the original `x` value and the state object /// public static T Do(this T x, S state, Action effect) { effect(x, state); return x; } /// /// Lifts argument to Func without allocations ignoring the first argument. /// For example if you have `Func{T, R} = _ => instance`, /// you may rewrite it without allocations as `instance.ToFunc{A, R}` /// public static R ToFunc(this R result, T ignoredArg) => result; } /// Helpers for lazy instantiations public static class Lazy { /// Provides result type inference for creation of lazy. public static Lazy Of(Func valueFactory) => new Lazy(valueFactory); } /// Replacement for `Void` type which can be used as a type argument and value. /// In traditional functional languages this type is a singleton empty record type, /// e.g. `()` in Haskell https://en.wikipedia.org/wiki/Unit_type public struct Unit : IEquatable { /// Singleton unit value - making it a lower-case so you could import `using static ImTools.Unit;` and write `return unit;` public static readonly Unit unit = new Unit(); /// public override string ToString() => "(unit)"; /// Equals to any other Unit public bool Equals(Unit other) => true; /// public override bool Equals(object obj) => obj is Unit; /// Using type hash code for the value public override int GetHashCode() => typeof(Unit).GetHashCode(); } /// Simple value provider interface - useful for the type pattern matching via `case I{T} x: ...` public interface I { /// The value in this case ;) T Value { get; } } /// Helpers for `Is` and `Union` public static class UnionTools { /// Pretty prints the Union using the type information internal static string ToString(T value, string prefix = "case(", string suffix = ")") { if (typeof(TName) == typeof(Unit)) return prefix + value + suffix; var typeName = typeof(TName).Name; var i = typeName.IndexOf('`'); var name = i == -1 ? typeName : typeName.Substring(0, i); return name + prefix + value + suffix; } } /// Wraps the `T` in a typed `TData` struct value in a one-line declaration, /// so the ]]> /// is different from the ]]> public abstract class Item where TItem : Item { /// Creation method for the consistency with other types public static item Of(T x) => new item(x); /// Nested structure that hosts a value. /// All nested types by convention here are lowercase public readonly struct item : IEquatable, I { /// public T Value { [MethodImpl((MethodImplOptions)256)] get => Item; } /// The value public readonly T Item; /// Constructor public item(T x) => Item = x; /// public bool Equals(item other) => EqualityComparer.Default.Equals(Value, other.Value); /// public override bool Equals(object obj) => obj is item c && Equals(c); /// public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); /// public override string ToString() => UnionTools.ToString(Value); } } /// Item without the data payload public abstract class Item where TItem : Item { /// Single item value public static readonly item Single = new item(); /// Nested structure that hosts a value. /// All nested types by convention here are lowercase public readonly struct item : IEquatable { /// public bool Equals(item other) => true; /// public override bool Equals(object obj) => obj is item; /// public override int GetHashCode() => typeof(TItem).GetHashCode(); /// public override string ToString() => "(" + typeof(TItem).Name + ")"; } } /// Wraps the `T` in a named `TBox` class in a one-line declaration, /// so the ]]> /// is different from the ]]> public abstract class Box : I, IEquatable> where TBox : Box, new() { /// Wraps the value public static TBox Of(T x) => new TBox { Value = x }; /// public T Value { get; private set; } /// public bool Equals(Box other) => other != null && EqualityComparer.Default.Equals(Value, other.Value); /// public override bool Equals(object obj) => obj is Box c && Equals(c); // ReSharper disable once NonReadonlyMemberInGetHashCode /// public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); /// public override string ToString() => UnionTools.ToString(Value, "data("); } /// Unnamed discriminated union (with Empty name), shorter name for simplified inline usage public class U : Union { } /// Discriminated union public abstract class Union { /// To tag the cases with enum value for efficient pattern matching of required - /// otherwise we need to use `is CaseN` pattern or similar which is less efficient public enum Tag : byte { /// Tags Case1 Case1, /// Tags Case2 Case2 } /// The base interface for the cases to operate. /// The naming is selected to start from the lower letter, cause we need to use the nested type. /// It is an unusual case, that's why using the __union__ will be fine to highlight this. // ReSharper disable once InconsistentNaming public interface union { /// The tag Tag Tag { get; } /// Matches the union cases to the R value R Match(Func map1, Func map2); } /// Creates the respective case public static union Of(T1 x) => new case1(x); /// Creates the respective case public static union Of(T2 x) => new case2(x); /// Wraps the respective case public readonly struct case1 : union, IEquatable, I { /// Implicit conversion public static implicit operator case1(T1 x) => new case1(x); /// public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } /// public R Match(Func map1, Func map2) => map1(Case); /// public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } /// The case value public readonly T1 Case; /// Wraps the value public case1(T1 x) => Case = x; /// public bool Equals(case1 other) => EqualityComparer.Default.Equals(Value, other.Value); /// public override bool Equals(object obj) => obj is case1 x && Equals(x); /// public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); /// public override string ToString() => UnionTools.ToString(Value); } /// Wraps the respective case public readonly struct case2 : union, IEquatable, I { /// Conversion public static implicit operator case2(T2 x) => new case2(x); /// public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } /// public R Match(Func map1, Func map2) => map2(Value); /// public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } /// The case value public readonly T2 Case; /// Wraps the value public case2(T2 x) => Case = x; /// public bool Equals(case2 other) => EqualityComparer.Default.Equals(Value, other.Value); /// public override bool Equals(object obj) => obj is case2 x && Equals(x); /// public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Value); /// public override string ToString() => UnionTools.ToString(Value); } } #pragma warning disable 1591 public class U : Union { } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public class U : Union { } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public class U : Union { } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public class U : Union { } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public static union Of(T6 x) => new case6(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case6 : union, IEquatable, I { public static implicit operator case6(T6 x) => new case6(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6) => map6(Case); public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T6 Case; public case6(T6 x) => Case = x; public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case6 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public class U : Union { } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public static union Of(T6 x) => new case6(x); public static union Of(T7 x) => new case7(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case6 : union, IEquatable, I { public static implicit operator case6(T6 x) => new case6(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map6(Case); public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T6 Case; public case6(T6 x) => Case = x; public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case6 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case7 : union, IEquatable, I { public static implicit operator case7(T7 x) => new case7(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7) => map7(Case); public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T7 Case; public case7(T7 x) => Case = x; public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case7 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public static union Of(T6 x) => new case6(x); public static union Of(T7 x) => new case7(x); public static union Of(T8 x) => new case8(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case6 : union, IEquatable, I { public static implicit operator case6(T6 x) => new case6(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map6(Case); public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T6 Case; public case6(T6 x) => Case = x; public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case6 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case7 : union, IEquatable, I { public static implicit operator case7(T7 x) => new case7(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map7(Case); public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T7 Case; public case7(T7 x) => Case = x; public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case7 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case8 : union, IEquatable, I { public static implicit operator case8(T8 x) => new case8(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8) => map8(Case); public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T8 Case; public case8(T8 x) => Case = x; public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case8 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public static union Of(T6 x) => new case6(x); public static union Of(T7 x) => new case7(x); public static union Of(T8 x) => new case8(x); public static union Of(T9 x) => new case9(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case6 : union, IEquatable, I { public static implicit operator case6(T6 x) => new case6(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map6(Case); public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T6 Case; public case6(T6 x) => Case = x; public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case6 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case7 : union, IEquatable, I { public static implicit operator case7(T7 x) => new case7(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map7(Case); public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T7 Case; public case7(T7 x) => Case = x; public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case7 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case8 : union, IEquatable, I { public static implicit operator case8(T8 x) => new case8(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map8(Case); public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T8 Case; public case8(T8 x) => Case = x; public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case8 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case9 : union, IEquatable, I { public static implicit operator case9(T9 x) => new case9(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9) => map9(Case); public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T9 Case; public case9(T9 x) => Case = x; public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case9 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } public abstract class Union { public enum Tag : byte { Case1, Case2, Case3, Case4, Case5, Case6, Case7, Case8, Case9, Case10 } public interface union { Tag Tag { get; } R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10); } public static union Of(T1 x) => new case1(x); public static union Of(T2 x) => new case2(x); public static union Of(T3 x) => new case3(x); public static union Of(T4 x) => new case4(x); public static union Of(T5 x) => new case5(x); public static union Of(T6 x) => new case6(x); public static union Of(T7 x) => new case7(x); public static union Of(T8 x) => new case8(x); public static union Of(T9 x) => new case9(x); public static union Of(T10 x) => new case10(x); public struct case1 : union, IEquatable, I { public static implicit operator case1(T1 x) => new case1(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case1; } [MethodImpl((MethodImplOptions)256)] public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map1(Case); public T1 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T1 Case; public case1(T1 x) => Case = x; public bool Equals(case1 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case1 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case2 : union, IEquatable, I { public static implicit operator case2(T2 x) => new case2(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case2; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map2(Case); public T2 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T2 Case; public case2(T2 x) => Case = x; public bool Equals(case2 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case2 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case3 : union, IEquatable, I { public static implicit operator case3(T3 x) => new case3(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case3; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map3(Case); public T3 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T3 Case; public case3(T3 x) => Case = x; public bool Equals(case3 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case3 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case4 : union, IEquatable, I { public static implicit operator case4(T4 x) => new case4(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case4; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map4(Case); public T4 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T4 Case; public case4(T4 x) => Case = x; public bool Equals(case4 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case4 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case5 : union, IEquatable, I { public static implicit operator case5(T5 x) => new case5(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case5; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map5(Case); public T5 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T5 Case; public case5(T5 x) => Case = x; public bool Equals(case5 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case5 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case6 : union, IEquatable, I { public static implicit operator case6(T6 x) => new case6(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case6; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map6(Case); public T6 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T6 Case; public case6(T6 x) => Case = x; public bool Equals(case6 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case6 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case7 : union, IEquatable, I { public static implicit operator case7(T7 x) => new case7(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case7; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map7(Case); public T7 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T7 Case; public case7(T7 x) => Case = x; public bool Equals(case7 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case7 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case8 : union, IEquatable, I { public static implicit operator case8(T8 x) => new case8(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case8; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map8(Case); public T8 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T8 Case; public case8(T8 x) => Case = x; public bool Equals(case8 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case8 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case9 : union, IEquatable, I { public static implicit operator case9(T9 x) => new case9(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case9; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map9(Case); public T9 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T9 Case; public case9(T9 x) => Case = x; public bool Equals(case9 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case9 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } public struct case10 : union, IEquatable, I { public static implicit operator case10(T10 x) => new case10(x); public Tag Tag { [MethodImpl((MethodImplOptions)256)] get => Tag.Case10; } public R Match(Func map1, Func map2, Func map3, Func map4, Func map5, Func map6, Func map7, Func map8, Func map9, Func map10) => map10(Case); public T10 Value { [MethodImpl((MethodImplOptions)256)] get => Case; } public readonly T10 Case; public case10(T10 x) => Case = x; public bool Equals(case10 other) => EqualityComparer.Default.Equals(Case, other.Case); public override bool Equals(object obj) => obj is case10 x && Equals(x); public override int GetHashCode() => EqualityComparer.Default.GetHashCode(Case); public override string ToString() => UnionTools.ToString(Case); } } #pragma warning restore 1591 /// Methods to work with immutable arrays and some sugar. public static class ArrayTools { private static class EmptyArray { public static readonly T[] Value = new T[0]; } /// Returns singleton empty array of provided type. /// Array item type. Empty array. public static T[] Empty() => EmptyArray.Value; /// Wraps item in array. public static T[] One(this T one) => new[] { one }; /// Returns true if array is null or have no items. Type of array item. /// Source array to check. True if null or has no items, false otherwise. public static bool IsNullOrEmpty(this T[] source) => source == null || source.Length == 0; /// Returns empty array instead of null, or source array otherwise. Type of array item. public static T[] EmptyIfNull(this T[] source) => source ?? Empty(); /// Returns source enumerable if it is array, otherwise converts source to array or an empty array if null. public static T[] ToArrayOrSelf(this IEnumerable source) => source == null ? Empty() : (source as T[] ?? source.ToArray()); /// Returns source enumerable if it is list, otherwise converts source to IList or an empty array if null. public static IList ToListOrSelf(this IEnumerable source) => source == null ? Empty() : source as IList ?? source.ToList(); /// /// Array copy /// public static T[] Copy(this T[] items) { if (items == null) return null; var copy = new T[items.Length]; for (var i = 0; i < copy.Length; i++) copy[i] = items[i]; return copy; } /// Returns new array consisting from all items from source array then all items from added array. /// If source is null or empty, then added array will be returned. /// If added is null or empty, then source will be returned. /// Array item type. /// Array with leading items. /// Array with following items. /// New array with items of source and added arrays. public static T[] Append(this T[] source, params T[] added) { if (added == null || added.Length == 0) return source; if (source == null || source.Length == 0) return added; var result = new T[source.Length + added.Length]; Array.Copy(source, 0, result, 0, source.Length); if (added.Length == 1) result[source.Length] = added[0]; else Array.Copy(added, 0, result, source.Length, added.Length); return result; } /// Performant concat of enumerables in case of arrays. /// But performance will degrade if you use Concat().Where(). /// Type of item. /// goes first. /// appended to source. /// empty array or concat of source and other. public static T[] Append(this IEnumerable source, IEnumerable other) => source.ToArrayOrSelf().Append(other.ToArrayOrSelf()); /// Returns new array with appended, /// or at , if specified. /// If source array could be null or empty, then single value item array will be created despite any index. /// Array item type. /// Array to append value to. /// Value to append. /// (optional) Index of value to update. /// New array with appended or updated value. public static T[] AppendOrUpdate(this T[] source, T value, int index = -1) { if (source == null || source.Length == 0) return new[] { value }; var sourceLength = source.Length; index = index < 0 ? sourceLength : index; var result = new T[index < sourceLength ? sourceLength : sourceLength + 1]; Array.Copy(source, result, sourceLength); result[index] = value; return result; } /// Calls predicate on each item in array until predicate returns true, /// then method will return this item index, or if predicate returns false for each item, method will return -1. /// Type of array items. /// Source array: if null or empty, then method will return -1. /// Delegate to evaluate on each array item until delegate returns true. /// Index of item for which predicate returns true, or -1 otherwise. public static int IndexOf(this T[] source, Func predicate) { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) if (predicate(source[i])) return i; return -1; } /// Minimizes the allocations for closure in predicate lambda with the provided public static int IndexOf(this T[] source, S state, Func predicate) { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) if (predicate(state, source[i])) return i; return -1; } /// Looks up for item in source array equal to provided value, and returns its index, or -1 if not found. /// Type of array items. /// Source array: if null or empty, then method will return -1. /// Value to look up. /// Index of item equal to value, or -1 item is not found. public static int IndexOf(this T[] source, T value) { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) if (Equals(source[i], value)) return i; return -1; } /// The same as `IndexOf` but searching the item by reference public static int IndexOfReference(this T[] source, T reference) where T : class { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) if (ReferenceEquals(source[i], reference)) return i; return -1; } /// Produces new array without item at specified . /// Will return array if index is out of bounds, or source is null/empty. /// Type of array item. /// Input array. Index if item to remove. /// New array with removed item at index, or input source array if index is not in array. public static T[] RemoveAt(this T[] source, int index) { if (source == null || source.Length == 0 || index < 0 || index >= source.Length) return source; if (index == 0 && source.Length == 1) return new T[0]; var result = new T[source.Length - 1]; if (index != 0) Array.Copy(source, 0, result, 0, index); if (index != result.Length) Array.Copy(source, index + 1, result, index, result.Length - index); return result; } /// Looks for item in array using equality comparison, and returns new array with found item remove, or original array if not item found. /// Type of array item. /// Input array. Value to find and remove. /// New array with value removed or original array if value is not found. public static T[] Remove(this T[] source, T value) => source.RemoveAt(source.IndexOf(value)); /// Returns first item matching the , or default item value. /// item type /// items collection to search /// condition to evaluate for each item. /// First item matching condition or default value. public static T FindFirst(this T[] source, Func predicate) { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) { var item = source[i]; if (predicate(item)) return item; } return default(T); } /// Version of FindFirst with the fixed state used by predicate to prevent allocations by predicate lambda closure public static T FindFirst(this T[] source, S state, Func predicate) { if (source != null && source.Length != 0) for (var i = 0; i < source.Length; ++i) { var item = source[i]; if (predicate(state, item)) return item; } return default(T); } /// Returns first item matching the , or default item value. /// item type /// items collection to search /// condition to evaluate for each item. /// First item matching condition or default value. public static T FindFirst(this IEnumerable source, Func predicate) => source is T[] sourceArr ? sourceArr.FindFirst(predicate) : source.FirstOrDefault(predicate); /// Returns element if collection consist on single element, otherwise returns default value. /// It does not throw for collection with many elements public static T SingleOrDefaultIfMany(this IEnumerable source) { if (source is IList list) return list.Count == 1 ? list[0] : default(T); if (source == null) return default(T); using (var e = source.GetEnumerator()) { if (!e.MoveNext()) return default(T); var it = e.Current; return !e.MoveNext() ? it : default(T); } } /// Does for each item public static void ForEach(this T[] source, Action action) { if (!source.IsNullOrEmpty()) for (var i = 0; i < source.Length; i++) action(source[i]); } /// Appends source to results public static T[] AppendTo(T[] source, int sourcePos, int count, T[] results = null) { if (results == null) { var newResults = new T[count]; if (count == 1) newResults[0] = source[sourcePos]; else for (int i = 0, j = sourcePos; i < count; ++i, ++j) newResults[i] = source[j]; return newResults; } var matchCount = results.Length; var appendedResults = new T[matchCount + count]; if (matchCount == 1) appendedResults[0] = results[0]; else Array.Copy(results, 0, appendedResults, 0, matchCount); if (count == 1) appendedResults[matchCount] = source[sourcePos]; else Array.Copy(source, sourcePos, appendedResults, matchCount, count); return appendedResults; } private static R[] AppendTo(T[] source, int sourcePos, int count, Func map, R[] results = null) { if (results == null || results.Length == 0) { var newResults = new R[count]; if (count == 1) newResults[0] = map(source[sourcePos]); else for (int i = 0, j = sourcePos; i < count; ++i, ++j) newResults[i] = map(source[j]); return newResults; } var oldResultsCount = results.Length; var appendedResults = new R[oldResultsCount + count]; if (oldResultsCount == 1) appendedResults[0] = results[0]; else Array.Copy(results, 0, appendedResults, 0, oldResultsCount); if (count == 1) appendedResults[oldResultsCount] = map(source[sourcePos]); else { for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) appendedResults[i] = map(source[j]); } return appendedResults; } private static R[] AppendTo(T[] source, S state, int sourcePos, int count, Func map, R[] results = null) { if (results == null || results.Length == 0) { var newResults = new R[count]; if (count == 1) newResults[0] = map(state, source[sourcePos]); else for (int i = 0, j = sourcePos; i < count; ++i, ++j) newResults[i] = map(state, source[j]); return newResults; } var oldResultsCount = results.Length; var appendedResults = new R[oldResultsCount + count]; if (oldResultsCount == 1) appendedResults[0] = results[0]; else Array.Copy(results, 0, appendedResults, 0, oldResultsCount); if (count == 1) appendedResults[oldResultsCount] = map(state, source[sourcePos]); else { for (int i = oldResultsCount, j = sourcePos; i < appendedResults.Length; ++i, ++j) appendedResults[i] = map(state, source[j]); } return appendedResults; } /// Where method similar to Enumerable.Where but more performant and non necessary allocating. /// It returns source array and does Not create new one if all items match the condition. /// Type of source items. /// If null, the null will be returned. /// Condition to keep items. /// New array if some items are filter out. Empty array if all items are filtered out. Original array otherwise. public static T[] Match(this T[] source, Func condition) { if (source == null || source.Length == 0) return source; if (source.Length == 1) return condition(source[0]) ? source : Empty(); if (source.Length == 2) { var condition0 = condition(source[0]); var condition1 = condition(source[1]); return condition0 && condition1 ? new[] { source[0], source[1] } : condition0 ? new[] { source[0] } : condition1 ? new[] { source[1] } : Empty(); } var matchStart = 0; T[] matches = null; var matchFound = false; var i = 0; for (; i < source.Length; ++i) if (!(matchFound = condition(source[i]))) { // for accumulated matched items if (i != 0 && i > matchStart) matches = AppendTo(source, matchStart, i - matchStart, matches); matchStart = i + 1; // guess the next match start will be after the non-matched item } // when last match was found but not all items are matched (hence matchStart != 0) if (matchFound && matchStart != 0) return AppendTo(source, matchStart, i - matchStart, matches); return matches ?? (matchStart != 0 ? Empty() : source); } /// The same as `Match` but assumes that is not null and not empty public static T[] MatchUnsafe(this T[] source, Func condition) { var matchStart = 0; T[] matches = null; var matchFound = false; var i = 0; for (; i < source.Length; ++i) if (!(matchFound = condition(source[i]))) { // for accumulated matched items if (i != 0 && i > matchStart) matches = AppendTo(source, matchStart, i - matchStart, matches); matchStart = i + 1; // guess the next match start will be after the non-matched item } // when last match was found but not all items are matched (hence matchStart != 0) if (matchFound && matchStart != 0) return AppendTo(source, matchStart, i - matchStart, matches); return matches ?? (matchStart != 0 ? Empty() : source); } /// Match with the additional state to use in to minimize the allocations in lambda closure public static T[] Match(this T[] source, S state, Func condition) { if (source == null || source.Length == 0) return source; if (source.Length == 1) return condition(state, source[0]) ? source : Empty(); if (source.Length == 2) { var condition0 = condition(state, source[0]); var condition1 = condition(state, source[1]); return condition0 && condition1 ? new[] { source[0], source[1] } : condition0 ? new[] { source[0] } : condition1 ? new[] { source[1] } : Empty(); } var matchStart = 0; T[] matches = null; var matchFound = false; var i = 0; for (; i < source.Length; ++i) if (!(matchFound = condition(state, source[i]))) { // for accumulated matched items if (i != 0 && i > matchStart) matches = AppendTo(source, matchStart, i - matchStart, matches); matchStart = i + 1; // guess the next match start will be after the non-matched item } // when last match was found but not all items are matched (hence matchStart != 0) if (matchFound && matchStart != 0) return AppendTo(source, matchStart, i - matchStart, matches); return matches ?? (matchStart != 0 ? Empty() : source); } /// Where method similar to Enumerable.Where but more performant and non necessary allocating. /// It returns source array and does Not create new one if all items match the condition. /// Type of source items. Type of result items. /// If null, the null will be returned. /// Condition to keep items. Converter from source to result item. /// New array of result items. public static R[] Match(this T[] source, Func condition, Func map) { if (source == null) return null; if (source.Length == 0) return Empty(); if (source.Length == 1) { var item = source[0]; return condition(item) ? new[] { map(item) } : Empty(); } if (source.Length == 2) { var condition0 = condition(source[0]); var condition1 = condition(source[1]); return condition0 && condition1 ? new[] { map(source[0]), map(source[1]) } : condition0 ? new[] { map(source[0]) } : condition1 ? new[] { map(source[1]) } : Empty(); } var matchStart = 0; R[] matches = null; var matchFound = false; var i = 0; for (; i < source.Length; ++i) if (!(matchFound = condition(source[i]))) { // for accumulated matched items if (i != 0 && i > matchStart) matches = AppendTo(source, matchStart, i - matchStart, map, matches); matchStart = i + 1; // guess the next match start will be after the non-matched item } // when last match was found but not all items are matched (hence matchStart != 0) if (matchFound && matchStart != 0) return AppendTo(source, matchStart, i - matchStart, map, matches); return matches ?? (matchStart == 0 ? AppendTo(source, 0, source.Length, map) : Empty()); } /// Match with the additional state to use in and to minimize the allocations in lambda closure public static R[] Match(this T[] source, S state, Func condition, Func map) { if (source == null) return null; if (source.Length == 0) return Empty(); if (source.Length == 1) { var item = source[0]; return condition(state, item) ? new[] { map(state, item) } : Empty(); } if (source.Length == 2) { var condition0 = condition(state, source[0]); var condition1 = condition(state, source[1]); return condition0 && condition1 ? new[] { map(state, source[0]), map(state, source[1]) } : condition0 ? new[] { map(state, source[0]) } : condition1 ? new[] { map(state, source[1]) } : Empty(); } var matchStart = 0; R[] matches = null; var matchFound = false; var i = 0; for (; i < source.Length; ++i) if (!(matchFound = condition(state, source[i]))) { // for accumulated matched items if (i != 0 && i > matchStart) matches = AppendTo(source, state, matchStart, i - matchStart, map, matches); matchStart = i + 1; // guess the next match start will be after the non-matched item } // when last match was found but not all items are matched (hence matchStart != 0) if (matchFound && matchStart != 0) return AppendTo(source, state, matchStart, i - matchStart, map, matches); return matches ?? (matchStart == 0 ? AppendTo(source, state, 0, source.Length, map) : Empty()); } /// Maps all items from source to result array. /// Source item type Result item type /// Source items Function to convert item from source to result. /// Converted items public static R[] Map(this T[] source, Func map) { if (source == null) return null; var sourceCount = source.Length; if (sourceCount == 0) return Empty(); if (sourceCount == 1) return new[] { map(source[0]) }; if (sourceCount == 2) return new[] { map(source[0]), map(source[1]) }; var results = new R[sourceCount]; for (var i = 0; i < source.Length; i++) results[i] = map(source[i]); return results; } /// Map with additional state to use in to minimize allocations in lambda closure public static R[] Map(this T[] source, S state, Func map) { if (source == null) return null; var sourceCount = source.Length; if (sourceCount == 0) return Empty(); if (sourceCount == 1) return new[] { map(state, source[0]) }; if (sourceCount == 2) return new[] { map(state, source[0]), map(state, source[1]) }; var results = new R[sourceCount]; for (var i = 0; i < source.Length; i++) results[i] = map(state, source[i]); return results; } /// Maps all items from source to result collection. /// If possible uses fast array Map otherwise Enumerable.Select. /// Source item type Result item type /// Source items Function to convert item from source to result. /// Converted items public static IEnumerable Map(this IEnumerable source, Func map) => source is T[] arr ? arr.Map(map) : source?.Select(map); /// If is array uses more effective Match for array, otherwise just calls Where /// Type of source items. /// If null, the null will be returned. /// Condition to keep items. /// Result items, may be an array. public static IEnumerable Match(this IEnumerable source, Func condition) => source is T[] arr ? arr.Match(condition) : source?.Where(condition); /// If is array uses more effective Match for array, /// otherwise just calls Where, Select /// Type of source items. Type of result items. /// If null, the null will be returned. /// Condition to keep items. Converter from source to result item. /// Result items, may be an array. public static IEnumerable Match(this IEnumerable source, Func condition, Func map) => source is T[] arr ? arr.Match(condition, map) : source?.Where(condition).Select(map); } /// Wrapper that provides optimistic-concurrency Swap operation implemented using . /// Type of object to wrap. public sealed class Ref where T : class { /// Gets the wrapped value. public T Value => _value; private T _value; /// Creates ref to object, optionally with initial value provided. /// (optional) Initial value. public Ref(T initialValue = default) => _value = initialValue; /// Exchanges currently hold object with - see for details. /// Delegate to produce new object value from current one passed as parameter. /// Returns old object value the same way as /// Important: May be called multiple times to retry update with value concurrently changed by other code. public T Swap(Func getNewValue) => Ref.Swap(ref _value, getNewValue); // todo: A good candidate to implement // The same as `Swap` but instead of old known value it returns the new one //public T SwapAndGetNewValue(Func getNewValue) => // Ref.Swap(ref _value, getNewValue); /// Option without allocation for capturing `a` in closure of `getNewValue` public T Swap(A a, Func getNewValue) => Ref.Swap(ref _value, a, getNewValue); /// Option without allocation for capturing `a` and `b` in closure of `getNewValue` public T Swap(A a, B b, Func getNewValue) => Ref.Swap(ref _value, a, b, getNewValue); /// Just sets new value ignoring any intermingled changes and returns the original value /// old value public T Swap(T newValue) => Interlocked.Exchange(ref _value, newValue); /// Directly sets the value and returns the new value public T SetNonAtomic(T newValue) => _value = newValue; /// Compares current Referred value with and if equal replaces current with /// /// True if current value was replaced with new value, and false if current value is outdated (already changed by other party). /// [!CDATA[ /// var value = SomeRef.Value; /// if (!SomeRef.TrySwapIfStillCurrent(value, Update(value)) /// SomeRef.Swap(v => Update(v)); // fallback to normal Swap with delegate allocation /// ]] public bool TrySwapIfStillCurrent(T currentValue, T newValue) => Interlocked.CompareExchange(ref _value, newValue, currentValue) == currentValue; } /// Provides optimistic-concurrency consistent operation. public static class Ref { /// The default max retry count - can be overriden by `Swap` optional parameter public const int RETRY_COUNT_UNTIL_THROW = 50; /// Factory for with type of value inference. /// Type of value to wrap. /// Initial value to wrap. /// New ref. public static Ref Of(T value) where T : class => new Ref(value); /// Creates new ref to the value of original ref. Ref value type. /// Original ref. New ref to original value. public static Ref NewRef(this Ref original) where T : class => Of(original.Value); /// First, it evaluates new value using function. /// Second, it checks that original value is not changed. /// If it is changed it will retry first step, otherwise it assigns new value and returns original (the one used for ). /// Type of value to swap. /// Reference to change to new value /// Delegate to get value from old one. /// (optional) /// Old/original value. By analogy with . /// Important: May be called multiple times to retry update with value concurrently changed by other code. [MethodImpl((MethodImplOptions)256)] public static T Swap(ref T value, Func getNewValue, int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) where T : class { #if SUPPORTS_SPIN_WAIT var spinWait = new SpinWait(); #endif var retryCount = 0; while (true) { var oldValue = value; var newValue = getNewValue(oldValue); if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) return oldValue; if (++retryCount > retryCountUntilThrow) ThrowRetryCountExceeded(retryCountUntilThrow); #if SUPPORTS_SPIN_WAIT spinWait.SpinOnce(); #endif } } private static void ThrowRetryCountExceeded(int retryCountExceeded) => throw new InvalidOperationException( $"Ref retried to Update for {retryCountExceeded} times But there is always someone else intervened."); /// Swap with the additional state required for the delegate . /// May prevent closure creation for the delegate [MethodImpl((MethodImplOptions)256)] public static T Swap(ref T value, A a, Func getNewValue, int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) where T : class { #if SUPPORTS_SPIN_WAIT var spinWait = new SpinWait(); #endif var retryCount = 0; while (true) { var oldValue = value; var newValue = getNewValue(oldValue, a); if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) return oldValue; if (++retryCount > retryCountUntilThrow) ThrowRetryCountExceeded(retryCountUntilThrow); #if SUPPORTS_SPIN_WAIT spinWait.SpinOnce(); #endif } } /// Swap with the additional state , required for the delegate . /// May prevent closure creation for the delegate [MethodImpl((MethodImplOptions)256)] public static T Swap(ref T value, A a, B b, Func getNewValue, int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) where T : class { #if SUPPORTS_SPIN_WAIT var spinWait = new SpinWait(); #endif var retryCount = 0; while (true) { var oldValue = value; var newValue = getNewValue(oldValue, a, b); if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) return oldValue; if (++retryCount > retryCountUntilThrow) ThrowRetryCountExceeded(retryCountUntilThrow); #if SUPPORTS_SPIN_WAIT spinWait.SpinOnce(); #endif } } /// Swap with the additional state , , required for the delegate . /// May prevent closure creation for the delegate [MethodImpl((MethodImplOptions)256)] public static T Swap(ref T value, A a, B b, C c, Func getNewValue, int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) where T : class { #if SUPPORTS_SPIN_WAIT var spinWait = new SpinWait(); #endif var retryCount = 0; while (true) { var oldValue = value; var newValue = getNewValue(oldValue, a, b, c); if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) return oldValue; if (++retryCount > retryCountUntilThrow) ThrowRetryCountExceeded(retryCountUntilThrow); #if SUPPORTS_SPIN_WAIT spinWait.SpinOnce(); #endif } } // todo: Func of 5 args is not available on all plats // /// Option without allocation for capturing `a`, `b`, `c`, `d` in closure of `getNewValue` // [MethodImpl((MethodImplOptions)256)] // public static T Swap(ref T value, A a, B b, C c, D d, Func getNewValue, // int retryCountUntilThrow = RETRY_COUNT_UNTIL_THROW) // where T : class // { //#if SUPPORTS_SPIN_WAIT // var spinWait = new SpinWait(); //#endif // var retryCount = 0; // while (true) // { // var oldValue = value; // var newValue = getNewValue(oldValue, a, b, c, d); // if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue) // return oldValue; // if (++retryCount > retryCountUntilThrow) // ThrowRetryCountExceeded(retryCountUntilThrow); //#if SUPPORTS_SPIN_WAIT // spinWait.SpinOnce(); //#endif // } // } } /// Printable thing via provided printer public interface IPrintable { /// Print to the provided string builder via the provided printer. StringBuilder Print(StringBuilder s, Func printer); } /// Produces good enough hash codes for the fields public static class Hasher { /// Combines hashes of two fields public static int Combine(T1 a, T2 b) => Combine(a?.GetHashCode() ?? 0, b?.GetHashCode() ?? 0); /// Inspired by System.Tuple.CombineHashCodes public static int Combine(int h1, int h2) { if (h1 == 0) return h2; unchecked { return (h1 << 5) + h1 ^ h2; } } } /// Simple unbounded object pool public sealed class StackPool where T : class { /// Give me an object [MethodImpl((MethodImplOptions)256)] public T RentOrDefault() => Interlocked.Exchange(ref _s, _s?.Tail)?.Head; /// Give it back [MethodImpl((MethodImplOptions)256)] public void Return(T x) => Interlocked.Exchange(ref _s, new Stack(x, _s)); private Stack _s; private sealed class Stack { public readonly T Head; public readonly Stack Tail; public Stack(T h, Stack t) { Head = h; Tail = t; } } } /// Immutable Key-Value pair. It is reference type (could be check for null), /// which is different from System value type . /// In addition provides and implementations. /// Type of Key.Type of Value. public class KV : IPrintable { /// Key. public readonly K Key; /// Value. public readonly V Value; /// Creates Key-Value object by providing key and value. Does Not check either one for null. /// key.value. public KV(K key, V value) { Key = key; Value = value; } /// public StringBuilder Print(StringBuilder s, Func printer) => s.Append("(").To(b => Key == null ? b : printer(b, Key)) .Append(", ").To(b => Value == null ? b : printer(b, Value)) .Append(')'); /// Creates nice string view.String representation. public override string ToString() => Print(new StringBuilder(), (s, x) => s.Append(x)).ToString(); /// Returns true if both key and value are equal to corresponding key-value of other object. public override bool Equals(object obj) { var other = obj as KV; return other != null && (ReferenceEquals(other.Key, Key) || Equals(other.Key, Key)) && (ReferenceEquals(other.Value, Value) || Equals(other.Value, Value)); } /// Combines key and value hash code public override int GetHashCode() => Hasher.Combine(Key, Value); } /// Helpers for . public static class KV { /// Creates the key value pair. public static KV Of(K key, V value) => new KV(key, value); /// Creates the pair with the new value public static KV WithValue(this KV kv, V value) => new KV(kv.Key, value); } /// Simple helper for creation of the pair of two parts. public static class KeyValuePair { /// Pairs key with value. public static KeyValuePair Pair(this K key, V value) => new KeyValuePair(key, value); } /// Helper structure which allows to distinguish null value from the default value for optional parameter. public struct Opt { /// Allows to transparently convert parameter argument to opt structure. public static implicit operator Opt(T value) => new Opt(value); /// Argument value. public readonly T Value; /// Indicates that value is provided. public readonly bool HasValue; /// Wraps passed value in structure. Sets the flag that value is present. public Opt(T value) { HasValue = true; Value = value; } /// Helper to get value or default value if value is not present. public T OrDefault(T defaultValue = default) => HasValue ? Value : defaultValue; } /// Ever growing list public struct GrowingList { /// Default initial capacity public const int DefaultInitialCapacity = 2; /// The items array public T[] Items; /// The count public int Count; /// Constructs the thing public GrowingList(T[] items, int count = 0) { Items = items; Count = count; } /// Push the new slot and return the ref to it public ref T PushSlot() { if (Items == null) Items = new T[DefaultInitialCapacity]; else if (Count >= Items.Length) Items = Expand(Items); return ref Items[Count++]; } /// Adds the new item possibly extending the item collection public void PushSlot(T item) { if (Items == null) Items = new T[DefaultInitialCapacity]; else if (Count >= Items.Length) Items = Expand(Items); Items[Count++] = item; } /// Pops the item - just moving the counter back public void Pop() => --Count; /// Expands the items starting with 2 private static T[] Expand(T[] items) { var count = items.Length; var newItems = new T[count << 1]; // count x 2 Array.Copy(items, 0, newItems, 0, count); return newItems; } /// public override string ToString() => $"Count {Count} of {(Count == 0 || Items == null || Items.Length == 0 ? "empty" : "first (" + Items[0] + ") and last (" + Items[Count - 1] + ")")}"; } /// Immutable list - simplest linked list with the Head and the Tail. public sealed class ImList { /// Empty list to Push to. public static readonly ImList Empty = new ImList(); /// True for empty list. public bool IsEmpty => Tail == null; /// First value in a list. public readonly T Head; /// The rest of values or Empty if list has a single value. public readonly ImList Tail; /// Prepends new value and returns new list. public ImList Push(T head) => new ImList(head, this); /// Enumerates the list. public IEnumerable Enumerate() { if (IsEmpty) yield break; for (var list = this; !list.IsEmpty; list = list.Tail) yield return list.Head; } /// String representation for debugging purposes public override string ToString() => IsEmpty ? "[]" : Tail.IsEmpty ? "[" + Head + "]" : Tail.Tail.IsEmpty ? "[" + Head + "," + Tail.Head + "]" : Tail.Tail.Tail.IsEmpty ? "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + "]" : "[" + Head + "," + Tail.Head + "," + Tail.Tail.Head + ", ...]"; private ImList() { } private ImList(T head, ImList tail) { Head = head; Tail = tail; } } /// Extension methods providing basic operations on a list. public static class ImList { /// Split list into (Head, Tail, IsEmpty) tuple public static void Deconstruct(this ImList list, out T head, out ImList tail, out bool isEmpty) { head = list.Head; tail = list.Tail; isEmpty = list.IsEmpty; } /// /// Constructs the reversed list from the parameter array of items /// public static ImList List(params T[] items) { var l = ImList.Empty; if (items != null) for (var i = items.Length - 1; i >= 0; --i) l = l.Push(items[i]); return l; } /// /// Constructs the list as the reversed input list /// public static ImList ToImList(this IList source) { var l = ImList.Empty; if (source != null) for (var i = source.Count - 1; i >= 0; --i) l = l.Push(source[i]); return l; } /// /// Constructs the list as the reversed enumerable /// public static ImList ToImList(this IEnumerable source) { if (source is IList list) return list.ToImList(); var l = ImList.Empty; if (source != null) foreach (var item in source) l = l.Push(item); return l.Reverse(); } /// Constructs list of one element public static ImList List(this T head) => ImList.Empty.Push(head); /// Constructs list from head and tail public static ImList List(this T head, ImList tail) => tail.Push(head); /// Apples some effect action to each element public static void ForEach(this ImList list, Action effect) { for (; !list.IsEmpty; list = list.Tail) effect(list.Head); } /// Fold list to a single value. The respective name for it in LINQ is Aggregate public static S Fold(this ImList list, S state, Func reduce) { if (list.IsEmpty) return state; var result = state; for (; !list.IsEmpty; list = list.Tail) result = reduce(list.Head, result); return result; } /// Fold list to a single value with index of item. The respective name for it in LINQ is Aggregate. public static S Fold(this ImList list, S state, Func reduce) { if (list.IsEmpty) return state; var result = state; for (var i = 0; !list.IsEmpty; list = list.Tail, ++i) result = reduce(list.Head, i, result); return result; } /// Returns new list in reverse order. public static ImList Reverse(this ImList list) { if (list.IsEmpty || list.Tail.IsEmpty) return list; var reversed = ImList.Empty; for (; !list.IsEmpty; list = list.Tail) reversed = reversed.Push(list.Head); return reversed; } /// Maps the items from the first list to the result list. public static ImList Map(this ImList list, Func map) => list.Fold(ImList.Empty, (x, r) => List(map(x), r)).Reverse(); /// Maps with index public static ImList Map(this ImList list, Func map) => list.Fold(ImList.Empty, (x, i, r) => List(map(x, i), r)).Reverse(); /// Copies list to array. public static T[] ToArray(this ImList source) => source.IsEmpty ? ArrayTools.Empty() : source.Tail.IsEmpty ? new[] { source.Head } : source.Enumerate().ToArray(); } /// Zipper is an immutable persistent data structure, to represent collection with single focused (selected, active) element. /// Consist of REVERSED `Left` immutable list, `Focus` element, and the `Right` immutable list. That's why a Zipper name, /// where left and right part are joined / zipped in focus item. public sealed class ImZipper { /// Empty singleton instance to start building your zipper public static readonly ImZipper Empty = new ImZipper(); /// True is zipper does not contain items public bool IsEmpty => Count == 0; /// Index of Focus item, from `0` to `Count-1` public readonly int Index; /// Number of items public readonly int Count; /// Left REVERSED list, so the Head of the list is just prior the Focus item public readonly ImList Left; /// Right list, where Head is just after the Focus item public readonly ImList Right; /// Single focus item public readonly T Focus; /// public override string ToString() => IsEmpty ? "[||]" : Count + ":" + Left.Reverse() + "|" + Index + ":" + Focus + "|" + Right; /// Sets a new focus and pushes the old focus to the Left list. public ImZipper Append(T focus) => PushLeft(focus); /// Sets a new focus and pushes the old focus to the Left list. public ImZipper PushLeft(T focus) => IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) : new ImZipper(Left.Push(Focus), focus, Index + 1, Right, Count + 1); /// Sets a new focus and pushes the old focus to the right list. public ImZipper Insert(T focus) => PushRight(focus); /// Sets a new focus and pushes the old focus to the right list. public ImZipper PushRight(T focus) => IsEmpty ? new ImZipper(ImList.Empty, focus, 0, ImList.Empty, 1) : new ImZipper(Left, focus, Index, Right.Push(Focus), Count + 1); /// Removes a focus, filling the hole with the item from the left list, or from the right if the left is empty public ImZipper PopLeft() => IsEmpty ? this : Left.IsEmpty && Right.IsEmpty ? Empty : !Left.IsEmpty ? new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1) : new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1); /// Removes a focus, filling the hole with the item from the right list, or from the left if the right is empty public ImZipper PopRight() => IsEmpty ? this : Left.IsEmpty && Right.IsEmpty ? Empty : !Right.IsEmpty ? new ImZipper(Left, Right.Head, Index, Right.Tail, Count - 1) : new ImZipper(Left.Tail, Left.Head, Index - 1, Right, Count - 1); /// Shifts focus one element to the left (decrementing its Index). public ImZipper ShiftLeft() => IsEmpty || Left.IsEmpty ? this : new ImZipper(Left.Tail, Left.Head, Index - 1, Right.Push(Focus), Count); /// Shifts focus one element to the right (incrementing its Index). public ImZipper ShiftRight() => IsEmpty || Right.IsEmpty ? this : new ImZipper(Left.Push(Focus), Right.Head, Index + 1, Right.Tail, Count); /// Sets a new focus and returns a new zipper with the left and right lists unchanged public ImZipper WithFocus(T focus) => IsEmpty ? this : new ImZipper(Left, focus, Index, Right, Count); /// Maps over the zipper items producing a new zipper public ImZipper Map(Func map) => IsEmpty ? ImZipper.Empty : new ImZipper(Left.Reverse().Fold(ImList.Empty, (x, r) => r.Push(map(x))), map(Focus), Index, Right.Map(map), Count); /// Maps over the zipper items with item index, producing a new zipper public ImZipper Map(Func map) => IsEmpty ? ImZipper.Empty : new ImZipper( Left.Reverse().Fold(ImList.Empty, (x, i, r) => r.Push(map(x, i))), map(Focus, Index), Index, Right.Map((x, i) => map(x, Index + 1 + i)), Count); private ImZipper() => Index = -1; private ImZipper(ImList left, T focus, int index, ImList right, int count) { Left = left; Focus = focus; Index = index; Right = right; Count = count; } } /// Other ImZipper methods public static class ImZipper { /// Appends array items to zipper public static ImZipper Zip(params T[] items) { if (items.IsNullOrEmpty()) return ImZipper.Empty; var z = ImZipper.Empty; for (var i = 0; i < items.Length; ++i) z = z.PushLeft(items[i]); return z; } /// Converts to array. public static T[] ToArray(this ImZipper z) { if (z.IsEmpty) return ArrayTools.Empty(); var a = new T[z.Count]; z.Fold(a, (x, i, xs) => { xs[i] = x; return xs; }); return a; } /// Shifts focus to a specified index, e.g. a random access public static ImZipper ShiftTo(this ImZipper z, int i) { if (i < 0 || i >= z.Count || i == z.Index) return z; while (i < z.Index) z = z.ShiftLeft(); while (i > z.Index) z = z.ShiftRight(); return z; } /// Updates a focus element if it is present, otherwise does nothing. /// If the focus item is the equal one, then returns the same zipper back. public static ImZipper Update(this ImZipper z, Func update) { if (z.IsEmpty) return z; var result = update(z.Focus); if (ReferenceEquals(z.Focus, result) || result != null && result.Equals(z.Focus)) return z; return z.WithFocus(result); } /// Update the item at random index, by shifting and updating it public static ImZipper UpdateAt(this ImZipper z, int i, Func update) => i < 0 || i >= z.Count ? z : z.ShiftTo(i).Update(update); /// Update the item at random index, by shifting and updating it public static ImZipper RemoveAt(this ImZipper z, int i) => i < 0 || i >= z.Count ? z : z.ShiftTo(i).PopLeft(); /// Folds zipper to a single value public static S Fold(this ImZipper z, S state, Func reduce) => z.IsEmpty ? state : z.Right.Fold(reduce(z.Focus, z.Left.Reverse().Fold(state, reduce)), reduce); /// Folds zipper to a single value by using an item index public static S Fold(this ImZipper z, S state, Func reduce) { if (z.IsEmpty) return state; var focusIndex = z.Index; var reducedLeft = z.Left.Reverse().Fold(state, reduce); return z.Right.Fold(reduce(z.Focus, focusIndex, reducedLeft), (x, i, r) => reduce(x, focusIndex + i + 1, r)); } /// Apply some effect action on each element public static void ForEach(this ImZipper z, Action effect) { if (!z.IsEmpty) { if (!z.Left.IsEmpty) z.Left.Reverse().ForEach(effect); effect(z.Focus); if (!z.Right.IsEmpty) z.Right.ForEach(effect); } } } /// Given the old value should and the new value should return result updated value. public delegate V Update(V oldValue, V newValue); /// Update handler including the key public delegate V Update(K key, V oldValue, V newValue); /// /// Fold reducer. Designed as a alternative to `Func{V, S, S}` but with possibility of inlining on the call side. /// Note: To get the advantage of inlining the can the interface should be implemented and passed as a NON-GENERIC STRUCT /// public interface IFoldReducer { /// Reduce method S Reduce(T x, S state); } /// /// Immutable http://en.wikipedia.org/wiki/AVL_tree with integer keys and values. /// public class ImMap { /// Empty tree to start with. public static readonly ImMap Empty = new ImMap(); /// Returns true if tree is empty. public bool IsEmpty => this == Empty; /// Prevents multiple creation of an empty tree protected ImMap() { } /// Height of the longest sub-tree/branch - 0 for the empty tree public virtual int Height => 0; /// Prints "empty" public override string ToString() => "empty"; } /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory public sealed class ImMapEntry : ImMap { /// public override int Height => 1; /// The Key is basically the hash, or the Height for ImMapTree public readonly int Key; /// The value - may be modified if you need a Ref{V} semantics public V Value; /// Constructs the entry with the default value public ImMapEntry(int key) => Key = key; /// Constructs the entry public ImMapEntry(int key, V value) { Key = key; Value = value; } /// Prints the key value pair public override string ToString() => Key + ":" + Value; } /// /// The two level - two node tree with either left or right /// public sealed class ImMapBranch : ImMap { /// Always two public override int Height => 2; /// Contains the once created data node public readonly ImMapEntry Entry; /// Left sub-tree/branch, or empty. public ImMapEntry RightEntry; /// Constructor public ImMapBranch(ImMapEntry entry, ImMapEntry rightEntry) { Entry = entry; RightEntry = rightEntry; } /// Prints the key value pair public override string ToString() => "h2:" + Entry + "->" + RightEntry; } /// /// The tree always contains Left and Right node, for the missing leaf we have /// public sealed class ImMapTree : ImMap { /// Starts from 2 public override int Height => TreeHeight; /// Starts from 2 - allows to access the field directly when you know it is a Tree public int TreeHeight; /// Contains the once created data node public readonly ImMapEntry Entry; /// Left sub-tree/branch, or empty. public ImMap Left; /// Right sub-tree/branch, or empty.md public ImMap Right; internal ImMapTree(ImMapEntry entry, ImMap left, ImMap right, int height) { Entry = entry; Left = left; Right = right; TreeHeight = height; } internal ImMapTree(ImMapEntry entry, ImMapEntry leftEntry, ImMapEntry rightEntry) { Entry = entry; Left = leftEntry; Right = rightEntry; TreeHeight = 2; } /// Outputs the brief tree info - mostly for debugging purposes public override string ToString() => "h" + Height + ":" + Entry + "->(" + (Left is ImMapTree leftTree ? "h" + leftTree.TreeHeight + ":" + leftTree.Entry : "" + Left) + ", " + (Right is ImMapTree rightTree ? "h" + rightTree.TreeHeight + ":" + rightTree.Entry : "" + Right) + ")"; /// Adds or updates the left or right branch public ImMapTree AddOrUpdateLeftOrRightEntry(int key, ImMapEntry entry) { if (key < Entry.Key) { var left = Left; if (left is ImMapTree leftTree) { if (key == leftTree.Entry.Key) return new ImMapTree(Entry, new ImMapTree(entry, leftTree.Left, leftTree.Right, leftTree.TreeHeight), Right, TreeHeight); var newLeftTree = leftTree.AddOrUpdateLeftOrRightEntry(key, entry); return newLeftTree.TreeHeight == leftTree.TreeHeight ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) : BalanceNewLeftTree(newLeftTree); } if (left is ImMapBranch leftBranch) { if (key < leftBranch.Entry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), Right, TreeHeight); if (key > leftBranch.Entry.Key) { var newLeft = // 5 5 // 2 ? => 3 ? // 3 2 4 // 4 key > leftBranch.RightEntry.Key ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) // 5 5 // 2 ? => 2.5 ? // 3 2 3 // 2.5 : key < leftBranch.RightEntry.Key ? new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry) : (ImMap)new ImMapBranch(leftBranch.Entry, entry); return new ImMapTree(Entry, newLeft, Right, TreeHeight); } return new ImMapTree(Entry, new ImMapBranch(entry, leftBranch.RightEntry), Right, TreeHeight); } var leftLeaf = (ImMapEntry)left; return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) : new ImMapTree(Entry, entry, Right, TreeHeight); } else { var right = Right; if (right is ImMapTree rightTree) { if (key == rightTree.Entry.Key) return new ImMapTree(Entry, Left, new ImMapTree(entry, rightTree.Left, rightTree.Right, rightTree.TreeHeight), TreeHeight); var newRightTree = rightTree.AddOrUpdateLeftOrRightEntry(key, entry); return newRightTree.TreeHeight == rightTree.TreeHeight ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) : BalanceNewRightTree(newRightTree); } if (right is ImMapBranch rightBranch) { if (key > rightBranch.Entry.Key) { var newRight = // 5 5 // ? 6 => ? 8 // 8 6 ! // ! key > rightBranch.RightEntry.Key ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) // 5 5 // ? 6 => ? 7 // 8 6 8 // 7 : key < rightBranch.RightEntry.Key ? new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry) : (ImMap)new ImMapBranch(rightBranch.Entry, entry); return new ImMapTree(Entry, Left, newRight, TreeHeight); } if (key < rightBranch.Entry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), TreeHeight); return new ImMapTree(Entry, Left, new ImMapBranch(entry, rightBranch.RightEntry), TreeHeight); } var rightLeaf = (ImMapEntry)right; return key > rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) : key < rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) : new ImMapTree(Entry, Left, entry, TreeHeight); } } /// Adds the left or right branch public ImMapTree AddUnsafeLeftOrRightEntry(int key, ImMapEntry entry) { if (key < Entry.Key) { var left = Left; if (left is ImMapTree leftTree) { var newLeftTree = leftTree.AddUnsafeLeftOrRightEntry(key, entry); return newLeftTree.TreeHeight == leftTree.TreeHeight ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) : BalanceNewLeftTree(newLeftTree); } if (left is ImMapBranch leftBranch) { if (key < leftBranch.Entry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), Right, TreeHeight); var newLeft = key > leftBranch.RightEntry.Key ? new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry) : new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry); return new ImMapTree(Entry, newLeft, Right, TreeHeight); } var leftLeaf = (ImMapEntry)left; return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) : new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3); } else { var right = Right; if (right is ImMapTree rightTree) { var newRightTree = rightTree.AddUnsafeLeftOrRightEntry(key, entry); return newRightTree.TreeHeight == rightTree.TreeHeight ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) : BalanceNewRightTree(newRightTree); } if (right is ImMapBranch rightBranch) { if (key > rightBranch.Entry.Key) { var newRight = key > rightBranch.RightEntry.Key ? new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry) : new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry); return new ImMapTree(Entry, Left, newRight, TreeHeight); } return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), TreeHeight); } var rightLeaf = (ImMapEntry)right; return key > rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) : new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3); } } /// Adds to the left or right branch, or keeps the un-modified map public ImMapTree AddOrKeepLeftOrRight(int key, V value) { if (key < Entry.Key) { var left = Left; if (left is ImMapTree leftTree) { if (key == leftTree.Entry.Key) return this; var newLeftTree = leftTree.AddOrKeepLeftOrRight(key, value); return newLeftTree == leftTree ? this : newLeftTree.TreeHeight == leftTree.TreeHeight ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) : BalanceNewLeftTree(newLeftTree); } if (left is ImMapBranch leftBranch) { if (key < leftBranch.Entry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.Entry, new ImMapEntry(key, value), leftBranch.RightEntry), Right, TreeHeight); if (key > leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key, value)), Right, TreeHeight); if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(new ImMapEntry(key, value), leftBranch.Entry, leftBranch.RightEntry), Right, TreeHeight); return this; } var leftLeaf = (ImMapEntry)left; return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key, value)), Right, 3) : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key, value), leftLeaf), Right, 3) : this; } else { var right = Right; if (right is ImMapTree rightTree) { if (key == rightTree.Entry.Key) return this; var newRightTree = rightTree.AddOrKeepLeftOrRight(key, value); return newRightTree == rightTree ? this : newRightTree.TreeHeight == rightTree.TreeHeight ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) : BalanceNewRightTree(newRightTree); } if (right is ImMapBranch rightBranch) { if (key > rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key, value)), TreeHeight); if (key < rightBranch.Entry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.Entry, new ImMapEntry(key, value), rightBranch.RightEntry), TreeHeight); if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(new ImMapEntry(key, value), rightBranch.Entry, rightBranch.RightEntry), TreeHeight); return this; } var rightLeaf = (ImMapEntry)right; return key > rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key, value)), 3) : key < rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key, value), rightLeaf), 3) : this; } } /// Adds to the left or right branch, or keeps the un-modified map public ImMapTree AddOrKeepLeftOrRight(int key) { if (key < Entry.Key) { var left = Left; if (left is ImMapTree leftTree) { if (key == leftTree.Entry.Key) return this; var newLeftTree = leftTree.AddOrKeepLeftOrRight(key); return newLeftTree == leftTree ? this : newLeftTree.TreeHeight == leftTree.TreeHeight ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) : BalanceNewLeftTree(newLeftTree); } if (left is ImMapBranch leftBranch) { if (key < leftBranch.Entry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.Entry, new ImMapEntry(key), leftBranch.RightEntry), Right, TreeHeight); if (key > leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, new ImMapEntry(key)), Right, TreeHeight); if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(new ImMapEntry(key), leftBranch.Entry, leftBranch.RightEntry), Right, TreeHeight); return this; } var leftLeaf = (ImMapEntry)left; return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, new ImMapEntry(key)), Right, 3) : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(new ImMapEntry(key), leftLeaf), Right, 3) : this; } else { var right = Right; if (right is ImMapTree rightTree) { if (key == rightTree.Entry.Key) return this; // note: tree always contains left and right (for the missing leaf we have a Branch) var newRightTree = rightTree.AddOrKeepLeftOrRight(key); return newRightTree == rightTree ? this : newRightTree.TreeHeight == rightTree.TreeHeight ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) : BalanceNewRightTree(newRightTree); } if (right is ImMapBranch rightBranch) { if (key > rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, new ImMapEntry(key)), TreeHeight); if (key < rightBranch.Entry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.Entry, new ImMapEntry(key), rightBranch.RightEntry), TreeHeight); if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(new ImMapEntry(key), rightBranch.Entry, rightBranch.RightEntry), TreeHeight); return this; } var rightLeaf = (ImMapEntry)right; return key > rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, new ImMapEntry(key)), 3) : key < rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(new ImMapEntry(key), rightLeaf), 3) : this; } } /// Adds to the left or right branch, or keeps the un-modified map public ImMapTree AddOrKeepLeftOrRightEntry(int key, ImMapEntry entry) { if (key < Entry.Key) { var left = Left; if (left is ImMapTree leftTree) { if (key == leftTree.Entry.Key) return this; var newLeftTree = leftTree.AddOrKeepLeftOrRightEntry(key, entry); return newLeftTree == leftTree ? this : newLeftTree.TreeHeight == leftTree.TreeHeight ? new ImMapTree(Entry, newLeftTree, Right, TreeHeight) : BalanceNewLeftTree(newLeftTree); } if (left is ImMapBranch leftBranch) { if (key < leftBranch.Entry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.Entry, entry, leftBranch.RightEntry), Right, TreeHeight); if (key > leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(leftBranch.RightEntry, leftBranch.Entry, entry), Right, TreeHeight); if (key > leftBranch.Entry.Key && key < leftBranch.RightEntry.Key) return new ImMapTree(Entry, new ImMapTree(entry, leftBranch.Entry, leftBranch.RightEntry), Right, TreeHeight); return this; } var leftLeaf = (ImMapEntry)left; return key > leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(leftLeaf, entry), Right, 3) : key < leftLeaf.Key ? new ImMapTree(Entry, new ImMapBranch(entry, leftLeaf), Right, 3) : this; } else { var right = Right; if (right is ImMapTree rightTree) { if (key == rightTree.Entry.Key) return this; // note: tree always contains left and right (for the missing leaf we have a Branch) var newRightTree = rightTree.AddOrKeepLeftOrRightEntry(key, entry); return newRightTree == rightTree ? this : newRightTree.TreeHeight == rightTree.TreeHeight ? new ImMapTree(Entry, Left, newRightTree, TreeHeight) : BalanceNewRightTree(newRightTree); } if (right is ImMapBranch rightBranch) { if (key > rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.RightEntry, rightBranch.Entry, entry), TreeHeight); if (key < rightBranch.Entry.Key) return new ImMapTree(Entry, Left, new ImMapTree(rightBranch.Entry, entry, rightBranch.RightEntry), TreeHeight); if (key > rightBranch.Entry.Key && key < rightBranch.RightEntry.Key) return new ImMapTree(Entry, Left, new ImMapTree(entry, rightBranch.Entry, rightBranch.RightEntry), TreeHeight); return this; } var rightLeaf = (ImMapEntry)right; return key > rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(rightLeaf, entry), 3) : key < rightLeaf.Key ? new ImMapTree(Entry, Left, new ImMapBranch(entry, rightLeaf), 3) : this; } } private ImMapTree BalanceNewLeftTree(ImMapTree newLeftTree) { var rightHeight = Right.Height; var delta = newLeftTree.TreeHeight - rightHeight; if (delta <= 0) return new ImMapTree(Entry, newLeftTree, Right, TreeHeight); if (delta == 1) return new ImMapTree(Entry, newLeftTree, Right, newLeftTree.TreeHeight + 1); // here is the balancing art comes into place if (rightHeight == 1) { if (newLeftTree.Left is ImMapEntry leftLeftLeaf) { // 30 15 // 10 40 => 10 20 // 5 15 5 12 30 40 // 12 20 if (newLeftTree.Right is ImMapTree leftRightTree) { newLeftTree.Right = leftRightTree.Left; newLeftTree.TreeHeight = 2; return new ImMapTree(leftRightTree.Entry, newLeftTree, new ImMapTree(Entry, leftRightTree.Right, Right, 2), 3); } // we cannot reuse the new left tree here because it is reduced into the branch // 30 15 // 10 40 => 5 20 // 5 15 10 30 40 // 20 var leftRightBranch = (ImMapBranch)newLeftTree.Right; return new ImMapTree(leftRightBranch.Entry, new ImMapBranch(leftLeftLeaf, newLeftTree.Entry), new ImMapTree(Entry, leftRightBranch.RightEntry, Right, 2), 3); } newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, 2); newLeftTree.TreeHeight = 3; return newLeftTree; } var leftLeftHeight = (newLeftTree.Left as ImMapTree)?.TreeHeight ?? 2; var leftRightHeight = (newLeftTree.Right as ImMapTree)?.TreeHeight ?? 2; if (leftLeftHeight < leftRightHeight) { var leftRightTree = (ImMapTree)newLeftTree.Right; newLeftTree.Right = leftRightTree.Left; newLeftTree.TreeHeight = leftLeftHeight + 1; return new ImMapTree(leftRightTree.Entry, newLeftTree, new ImMapTree(Entry, leftRightTree.Right, Right, rightHeight + 1), leftLeftHeight + 2); //return new ImMapTree(leftRightTree.Entry, // new ImMapTree(newLeftTree.Entry, leftLeftHeight, newLeftTree.Left, leftRightTree.Left), // new ImMapTree(Entry, leftRightTree.Right, rightHeight, Right)); } newLeftTree.Right = new ImMapTree(Entry, newLeftTree.Right, Right, leftRightHeight + 1); newLeftTree.TreeHeight = leftRightHeight + 2; return newLeftTree; } private ImMapTree BalanceNewRightTree(ImMapTree newRightTree) { var leftHeight = Left.Height; var delta = newRightTree.Height - leftHeight; if (delta <= 0) return new ImMapTree(Entry, Left, newRightTree, TreeHeight); if (delta == 1) return new ImMapTree(Entry, Left, newRightTree, newRightTree.TreeHeight + 1); if (leftHeight == 1) { // here we need to re-balance by default, because the new right tree is at least 3 level (actually exactly 3 or it would be too unbalanced) // double rotation needed if only the right-right is a leaf if (newRightTree.Right is ImMapEntry == false) { newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, 2); newRightTree.TreeHeight = 3; return newRightTree; } // 20 30 // 10 40 => 20 40 // 30 50 10 25 35 50 // 25 35 if (newRightTree.Left is ImMapTree rightLeftTree) { newRightTree.Left = rightLeftTree.Right; newRightTree.TreeHeight = 2; return new ImMapTree(rightLeftTree.Entry, new ImMapTree(Entry, Left, rightLeftTree.Left, 2), newRightTree, 3); } // 20 30 // 10 40 => 10 40 // 30 50 20 35 50 // 35 var rightLeftBranch = (ImMapBranch)newRightTree.Left; newRightTree.Left = rightLeftBranch.RightEntry; newRightTree.TreeHeight = 2; return new ImMapTree(rightLeftBranch.Entry, new ImMapBranch((ImMapEntry)Left, Entry), newRightTree, 3); } var rightRightHeight = (newRightTree.Right as ImMapTree)?.TreeHeight ?? 2; var rightLeftHeight = (newRightTree.Left as ImMapTree)?.TreeHeight ?? 2; if (rightRightHeight < rightLeftHeight) { var rightLeftTree = (ImMapTree)newRightTree.Left; newRightTree.Left = rightLeftTree.Right; // the height now should be defined by rr - because left now is shorter by 1 newRightTree.TreeHeight = rightRightHeight + 1; // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left return new ImMapTree(rightLeftTree.Entry, // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height new ImMapTree(Entry, Left, rightLeftTree.Left, leftHeight + 1), newRightTree, rightRightHeight + 2); //return new ImMapTree(rightLeftTree.Entry, // new ImMapTree(Entry, leftHeight, Left, rightLeftTree.Left), // new ImMapTree(newRightTree.Entry, rightLeftTree.Right, rightRightHeight, newRightTree.Right)); } Debug.Assert(rightLeftHeight >= leftHeight, "The whole rightHeight > leftHeight by 2, and rightRight >= leftHeight but not more than by 2"); // we may decide on the height because the Left smaller by 2 newRightTree.Left = new ImMapTree(Entry, Left, newRightTree.Left, rightLeftHeight + 1); // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway newRightTree.TreeHeight = rightLeftHeight + 2; return newRightTree; } } /// ImMap methods public static class ImMap { /// Adds or updates the value by key in the map, always returns a modified map [MethodImpl((MethodImplOptions)256)] public static ImMap AddOrUpdateEntry(this ImMap map, ImMapEntry entry) { if (map == ImMap.Empty) return entry; var key = entry.Key; if (map is ImMapEntry leaf) return key > leaf.Key ? new ImMapBranch(leaf, entry) : key < leaf.Key ? new ImMapBranch(entry, leaf) : (ImMap)entry; if (map is ImMapBranch branch) { if (key > branch.Entry.Key) // 5 10 // 10 => 5 11 // 11 return key > branch.RightEntry.Key ? new ImMapTree(branch.RightEntry, branch.Entry, entry) // 5 7 // 10 => 5 10 // 7 : key < branch.RightEntry.Key // rotate if right ? new ImMapTree(entry, branch.Entry, branch.RightEntry) : (ImMap)new ImMapBranch(branch.Entry, entry); return key < branch.Entry.Key ? new ImMapTree(branch.Entry, entry, branch.RightEntry) : (ImMap)new ImMapBranch(entry, branch.RightEntry); } var tree = (ImMapTree)map; return key == tree.Entry.Key ? new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight) : tree.AddOrUpdateLeftOrRightEntry(key, entry); } /// Adds or updates the value by key in the map, always returns a modified map [MethodImpl((MethodImplOptions)256)] public static ImMap AddOrUpdate(this ImMap map, int key, V value) => map.AddOrUpdateEntry(new ImMapEntry(key, value)); /// Adds the value by key in the map - ASSUMES that the key is not in the map, always returns a modified map [MethodImpl((MethodImplOptions)256)] public static ImMap AddEntryUnsafe(this ImMap map, ImMapEntry entry) { if (map == ImMap.Empty) return entry; var key = entry.Key; if (map is ImMapEntry leaf) return key > leaf.Key ? new ImMapBranch(leaf, entry) : new ImMapBranch(entry, leaf); if (map is ImMapBranch branch) return key < branch.Entry.Key ? new ImMapTree(branch.Entry, entry, branch.RightEntry) : key > branch.RightEntry.Key ? new ImMapTree(branch.RightEntry, branch.Entry, entry) : new ImMapTree(entry, branch.Entry, branch.RightEntry); return ((ImMapTree)map).AddUnsafeLeftOrRightEntry(key, entry); } /// Adds the value for the key or returns the un-modified map if key is already present [MethodImpl((MethodImplOptions)256)] public static ImMap AddOrKeep(this ImMap map, int key, V value) { if (map == ImMap.Empty) return new ImMapEntry(key, value); if (map is ImMapEntry leaf) return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key, value)) : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key, value), leaf) : map; if (map is ImMapBranch branch) return key < branch.Entry.Key ? new ImMapTree(branch.Entry, new ImMapEntry(key, value), branch.RightEntry) : key > branch.RightEntry.Key ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key, value)) : key > branch.Entry.Key && key < branch.RightEntry.Key ? new ImMapTree(new ImMapEntry(key, value), branch.Entry, branch.RightEntry) : map; var tree = (ImMapTree)map; return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRight(key, value) : map; } /// Adds the entry with default value for the key or returns the un-modified map if key is already present [MethodImpl((MethodImplOptions)256)] public static ImMap AddOrKeep(this ImMap map, int key) { if (map == ImMap.Empty) return new ImMapEntry(key); if (map is ImMapEntry leaf) return key > leaf.Key ? new ImMapBranch(leaf, new ImMapEntry(key)) : key < leaf.Key ? new ImMapBranch(new ImMapEntry(key), leaf) : map; if (map is ImMapBranch branch) return key < branch.Entry.Key ? new ImMapTree(branch.Entry, new ImMapEntry(key), branch.RightEntry) : key > branch.RightEntry.Key ? new ImMapTree(branch.RightEntry, branch.Entry, new ImMapEntry(key)) : key > branch.Entry.Key && key < branch.RightEntry.Key ? new ImMapTree(new ImMapEntry(key), branch.Entry, branch.RightEntry) : map; var tree = (ImMapTree)map; return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRight(key) : map; } /// Adds the entry for the key or returns the un-modified map if key is already present [MethodImpl((MethodImplOptions)256)] public static ImMap AddOrKeepEntry(this ImMap map, ImMapEntry entry) { if (map == ImMap.Empty) return entry; var key = entry.Key; if (map is ImMapEntry leaf) return key > leaf.Key ? new ImMapBranch(leaf, entry) : key < leaf.Key ? new ImMapBranch(entry, leaf) : map; if (map is ImMapBranch branch) return key < branch.Entry.Key ? new ImMapTree(branch.Entry, entry, branch.RightEntry) : key > branch.RightEntry.Key ? new ImMapTree(branch.RightEntry, branch.Entry, entry) : key > branch.Entry.Key && key < branch.RightEntry.Key ? new ImMapTree(entry, branch.Entry, branch.RightEntry) : map; var tree = (ImMapTree)map; return key != tree.Entry.Key ? tree.AddOrKeepLeftOrRightEntry(key, entry) : map; } ///Returns the new map with the updated value for the key, or the same map if the key was not found. [MethodImpl((MethodImplOptions)256)] public static ImMap Update(this ImMap map, int key, V value) => map.Contains(key) ? map.UpdateImpl(key, new ImMapEntry(key, value)) : map; ///Returns the new map with the updated value for the key, ASSUMES that the key is not in the map. [MethodImpl((MethodImplOptions)256)] public static ImMap UpdateEntryUnsafe(this ImMap map, ImMapEntry entry) => map.UpdateImpl(entry.Key, entry); internal static ImMap UpdateImpl(this ImMap map, int key, ImMapEntry entry) { if (map is ImMapTree tree) return key > tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateImpl(key, entry), tree.TreeHeight) : key < tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left.UpdateImpl(key, entry), tree.Right, tree.TreeHeight) : new ImMapTree(entry, tree.Left, tree.Right, tree.TreeHeight); // the key was found - so it should be either entry or right entry if (map is ImMapBranch branch) return key == branch.Entry.Key ? new ImMapBranch(entry, branch.RightEntry) : new ImMapBranch(branch.Entry, entry); return entry; } ///Returns the new map with the value set to default, or the same map if the key was not found. [MethodImpl((MethodImplOptions)256)] public static ImMap UpdateToDefault(this ImMap map, int key) => map.Contains(key) ? map.UpdateToDefaultImpl(key) : map; internal static ImMap UpdateToDefaultImpl(this ImMap map, int key) { if (map is ImMapTree tree) return key > tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left, tree.Right.UpdateToDefaultImpl(key), tree.TreeHeight) : key < tree.Entry.Key ? new ImMapTree(tree.Entry, tree.Left.UpdateToDefaultImpl(key), tree.Right, tree.TreeHeight) : new ImMapTree(new ImMapEntry(key), tree.Left, tree.Right, tree.TreeHeight); // the key was found - so it should be either entry or right entry if (map is ImMapBranch branch) return key == branch.Entry.Key ? new ImMapBranch(new ImMapEntry(key), branch.RightEntry) : new ImMapBranch(branch.Entry, new ImMapEntry(key)); return new ImMapEntry(key); } /// Returns `true` if key is found or `false` otherwise. [MethodImpl((MethodImplOptions)256)] public static bool Contains(this ImMap map, int key) { ImMapEntry entry; while (map is ImMapTree tree) { entry = tree.Entry; if (key > entry.Key) map = tree.Right; else if (key < entry.Key) map = tree.Left; else return true; } if (map is ImMapBranch branch) return branch.Entry.Key == key || branch.RightEntry.Key == key; entry = map as ImMapEntry; return entry != null && entry.Key == key; } /// Returns the entry if key is found or null otherwise. [MethodImpl((MethodImplOptions)256)] public static ImMapEntry GetEntryOrDefault(this ImMap map, int key) { ImMapEntry entry; while (map is ImMapTree tree) { entry = tree.Entry; if (key > entry.Key) map = tree.Right; else if (key < entry.Key) map = tree.Left; else return entry; } if (map is ImMapBranch branch) return branch.Entry.Key == key ? branch.Entry : branch.RightEntry.Key == key ? branch.RightEntry : null; entry = map as ImMapEntry; return entry != null && entry.Key == key ? entry : null; } /// Returns the value if key is found or default value otherwise. [MethodImpl((MethodImplOptions)256)] public static V GetValueOrDefault(this ImMap map, int key) { ImMapEntry entry; while (map is ImMapTree tree) { entry = tree.Entry; if (key > entry.Key) map = tree.Right; else if (key < entry.Key) map = tree.Left; else return entry.Value; } if (map is ImMapBranch branch) return branch.Entry.Key == key ? branch.Entry.Value : branch.RightEntry.Key == key ? branch.RightEntry.Value : default; entry = map as ImMapEntry; if (entry != null && entry.Key == key) return entry.Value; return default; } /// Returns true if key is found and sets the value. [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImMap map, int key, out V value) { ImMapEntry entry; while (map is ImMapTree tree) { entry = tree.Entry; if (key > entry.Key) map = tree.Right; else if (key < entry.Key) map = tree.Left; else { value = entry.Value; return true; } } if (map is ImMapBranch branch) { if (branch.Entry.Key == key) { value = branch.Entry.Value; return true; } if (branch.RightEntry.Key == key) { value = branch.RightEntry.Value; return true; } value = default; return false; } entry = map as ImMapEntry; if (entry != null && entry.Key == key) { value = entry.Value; return true; } value = default; return false; } /// Returns true if key is found and sets the value. [MethodImpl((MethodImplOptions)256)] public static bool TryFindEntry(this ImMap map, int key, out ImMapEntry result) { ImMapEntry entry; while (map is ImMapTree tree) { entry = tree.Entry; if (key > entry.Key) map = tree.Right; else if (key < entry.Key) map = tree.Left; else { result = entry; return true; } } if (map is ImMapBranch branch) { if (branch.Entry.Key == key) { result = branch.Entry; return true; } if (branch.RightEntry.Key == key) { result = branch.RightEntry; return true; } result = null; return false; } entry = map as ImMapEntry; if (entry != null && entry.Key == key) { result = entry; return true; } result = null; return false; } /// /// Enumerates all the map nodes from the left to the right and from the bottom to top /// You may pass `parentStacks` to reuse the array memory. /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, /// the content of the stack is not important and could be erased. /// public static IEnumerable> Enumerate(this ImMap map, ImMapTree[] parentStack = null) { if (map == ImMap.Empty) yield break; if (map is ImMapEntry leaf) yield return leaf; else if (map is ImMapBranch branch) { yield return branch.Entry; yield return branch.RightEntry; } else if (map is ImMapTree tree) { if (tree.TreeHeight == 2) { yield return (ImMapEntry)tree.Left; yield return tree.Entry; yield return (ImMapEntry)tree.Right; } else { parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; var parentIndex = -1; while (true) { if ((tree = map as ImMapTree) != null) { if (tree.TreeHeight == 2) { yield return (ImMapEntry)tree.Left; yield return tree.Entry; yield return (ImMapEntry)tree.Right; if (parentIndex == -1) break; tree = parentStack[parentIndex--]; yield return tree.Entry; map = tree.Right; } else { parentStack[++parentIndex] = tree; map = tree.Left; } } else if ((branch = map as ImMapBranch) != null) { yield return branch.Entry; yield return branch.RightEntry; if (parentIndex == -1) break; tree = parentStack[parentIndex--]; yield return tree.Entry; map = tree.Right; } else { yield return (ImMapEntry)map; if (parentIndex == -1) break; tree = parentStack[parentIndex--]; yield return tree.Entry; map = tree.Right; } } } } } /// /// Folds all the map nodes with the state from left to right and from the bottom to top /// You may pass `parentStacks` to reuse the array memory. /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, /// the content of the stack is not important and could be erased. /// public static S Fold(this ImMap map, S state, Func, S, S> reduce, ImMapTree[] parentStack = null) { if (map == ImMap.Empty) return state; if (map is ImMapEntry leaf) state = reduce(leaf, state); else if (map is ImMapBranch branch) { state = reduce(branch.Entry, state); state = reduce(branch.RightEntry, state); } else if (map is ImMapTree tree) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state); state = reduce(tree.Entry, state); state = reduce((ImMapEntry)tree.Right, state); } else { parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; var parentIndex = -1; while (true) { if ((tree = map as ImMapTree) != null) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state); state = reduce(tree.Entry, state); state = reduce((ImMapEntry)tree.Right, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } else { parentStack[++parentIndex] = tree; map = tree.Left; } } else if ((branch = map as ImMapBranch) != null) { state = reduce(branch.Entry, state); state = reduce(branch.RightEntry, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } else { state = reduce((ImMapEntry)map, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } } } } return state; } /// /// Folds all the map nodes with the state from left to right and from the bottom to top /// You may pass `parentStacks` to reuse the array memory. /// NOTE: the length of `parentStack` should be at least of map (height - 2) - the stack want be used for 0, 1, 2 height maps, /// the content of the stack is not important and could be erased. /// public static S Fold(this ImMap map, S state, A a, Func, S, A, S> reduce, ImMapTree[] parentStack = null) { if (map == ImMap.Empty) return state; if (map is ImMapEntry leaf) state = reduce(leaf, state, a); else if (map is ImMapBranch branch) { state = reduce(branch.Entry, state, a); state = reduce(branch.RightEntry, state, a); } else if (map is ImMapTree tree) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state, a); state = reduce(tree.Entry, state, a); state = reduce((ImMapEntry)tree.Right, state, a); } else { parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; var parentIndex = -1; while (true) { if ((tree = map as ImMapTree) != null) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state, a); state = reduce(tree.Entry, state, a); state = reduce((ImMapEntry)tree.Right, state, a); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state, a); map = tree.Right; } else { parentStack[++parentIndex] = tree; map = tree.Left; } } else if ((branch = map as ImMapBranch) != null) { state = reduce(branch.Entry, state, a); state = reduce(branch.RightEntry, state, a); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state, a); map = tree.Right; } else { state = reduce((ImMapEntry)map, state, a); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state, a); map = tree.Right; } } } } return state; } /// /// Visits all the map nodes with from the left to the right and from the bottom to the top /// You may pass `parentStacks` to reuse the array memory. /// NOTE: the length of `parentStack` should be at least of map height, content is not important and could be erased. /// public static void Visit(this ImMap map, Action> visit, ImMapTree[] parentStack = null) { if (map == ImMap.Empty) return; if (map is ImMapEntry leaf) visit(leaf); else if (map is ImMapBranch branch) { visit(branch.Entry); visit(branch.RightEntry); } else if (map is ImMapTree tree) { if (tree.TreeHeight == 2) { visit((ImMapEntry)tree.Left); visit(tree.Entry); visit((ImMapEntry)tree.Right); } else { parentStack = parentStack ?? new ImMapTree[tree.TreeHeight - 2]; var parentIndex = -1; while (true) { if ((tree = map as ImMapTree) != null) { if (tree.TreeHeight == 2) { visit((ImMapEntry)tree.Left); visit(tree.Entry); visit((ImMapEntry)tree.Right); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; visit(tree.Entry); map = tree.Right; } else { parentStack[++parentIndex] = tree; map = tree.Left; } } else if ((branch = map as ImMapBranch) != null) { visit(branch.Entry); visit(branch.RightEntry); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; visit(tree.Entry); map = tree.Right; } else { visit((ImMapEntry)map); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; visit(tree.Entry); map = tree.Right; } } } } } /// Wraps Key and Value payload to store inside ImMapEntry public struct KValue { /// The key public K Key; /// The value public object Value; /// Constructs a pair public KValue(K key, object value) { Key = key; Value = value; } } /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value, Update update) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, key, value, update); } private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, ImMapEntry> oldEntry, K key, object value, Update update = null) { if (key.Equals(oldEntry.Value.Key)) { value = update == null ? value : update(key, oldEntry.Value.Value, value); return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); } // add a new conflicting key value ImMapEntry>[] newConflicts; if (oldEntry.Value.Value is ImMapEntry>[] conflicts) { // entry is already containing the conflicted entries var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) --conflictIndex; if (conflictIndex != -1) { // update the existing conflict newConflicts = new ImMapEntry>[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); } else { // add the new conflicting value newConflicts = new ImMapEntry>[conflictCount + 1]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); } } else { newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; } var conflictsEntry = new ImMapEntry>(hash); conflictsEntry.Value.Value = newConflicts; return map.UpdateEntryUnsafe(conflictsEntry); } /// Efficiently creates the new entry [MethodImpl((MethodImplOptions)256)] private static ImMapEntry> CreateKValueEntry(int hash, K key, object value) { var newEntry = new ImMapEntry>(hash); newEntry.Value.Key = key; newEntry.Value.Value = value; return newEntry; } /// Efficiently creates the new entry [MethodImpl((MethodImplOptions)256)] public static ImMapEntry> CreateKValueEntry(int hash, K key) { var newEntry = new ImMapEntry>(hash); newEntry.Value.Key = key; return newEntry; } /// Uses the user provided hash and adds or updates the tree with passed key-value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public static ImMap> AddOrUpdate(this ImMap> map, int hash, K key, object value) => map.AddOrUpdate(hash, CreateKValueEntry(hash, key, value)); /// Uses the provided hash and adds or updates the tree with the passed key-value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public static ImMap> AddOrUpdate(this ImMap> map, int hash, ImMapEntry> entry) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map.AddEntryUnsafe(entry) : UpdateEntryOrAddOrUpdateConflict(map, hash, oldEntry, entry); } private static ImMap> UpdateEntryOrAddOrUpdateConflict(ImMap> map, int hash, ImMapEntry> oldEntry, ImMapEntry> newEntry) { if (newEntry.Value.Key.Equals(oldEntry.Value.Key)) return map.UpdateEntryUnsafe(newEntry); // add a new conflicting key value ImMapEntry>[] newConflicts; if (oldEntry.Value.Value is ImMapEntry>[] conflicts) { // entry is already containing the conflicted entries var key = newEntry.Value.Key; var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) --conflictIndex; if (conflictIndex != -1) { // update the existing conflict newConflicts = new ImMapEntry>[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictIndex] = newEntry; } else { // add the new conflicting value newConflicts = new ImMapEntry>[conflictCount + 1]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictCount] = newEntry; } } else { newConflicts = new[] { oldEntry, newEntry }; } return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); } /// Adds the new entry or keeps the current map if entry key is already present [MethodImpl((MethodImplOptions)256)] public static ImMap> AddOrKeep(this ImMap> map, int hash, K key) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map.AddEntryUnsafe(CreateKValueEntry(hash, key)) : AddOrKeepConflict(map, hash, oldEntry, key); } /// Adds the new entry or keeps the current map if entry key is already present [MethodImpl((MethodImplOptions)256)] public static ImMap> AddOrKeep(this ImMap> map, int hash, K key, object value) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map.AddEntryUnsafe(CreateKValueEntry(hash, key, value)) : AddOrKeepConflict(map, hash, oldEntry, key, value); } private static ImMap> AddOrKeepConflict(ImMap> map, int hash, ImMapEntry> oldEntry, K key, object value = null) { if (key.Equals(oldEntry.Value.Key)) return map; // add a new conflicting key value ImMapEntry>[] newConflicts; if (oldEntry.Value.Value is ImMapEntry>[] conflicts) { // entry is already containing the conflicted entries var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) --conflictIndex; if (conflictIndex != -1) return map; // add the new conflicting value newConflicts = new ImMapEntry>[conflictCount + 1]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictCount] = CreateKValueEntry(hash, key, value); } else { newConflicts = new[] { oldEntry, CreateKValueEntry(hash, key, value) }; } return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); } /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. public static ImMap> Update(this ImMap> map, int hash, K key, object value, Update update = null) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key, value, update); } private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, int hash, ImMapEntry> oldEntry, K key, object value, Update update = null) { if (key.Equals(oldEntry.Value.Key)) { value = update == null ? value : update(key, oldEntry.Value.Value, value); return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key, value)); } // add a new conflicting key value ImMapEntry>[] newConflicts; if (oldEntry.Value.Value is ImMapEntry>[] conflicts) { // entry is already containing the conflicted entries var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) --conflictIndex; if (conflictIndex == -1) return map; // update the existing conflict newConflicts = new ImMapEntry>[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); value = update == null ? value : update(key, conflicts[conflictIndex].Value.Value, value); newConflicts[conflictIndex] = CreateKValueEntry(hash, key, value); } else { return map; } return map.UpdateEntryUnsafe(CreateKValueEntry(hash, default(K), newConflicts)); } /// Updates the map with the default value if the key is found, otherwise returns the same unchanged map. public static ImMap> UpdateToDefault(this ImMap> map, int hash, K key) { var oldEntry = map.GetEntryOrDefault(hash); return oldEntry == null ? map : UpdateEntryOrReturnSelf(map, hash, oldEntry, key); } private static ImMap> UpdateEntryOrReturnSelf(ImMap> map, int hash, ImMapEntry> oldEntry, K key) { if (key.Equals(oldEntry.Value.Key)) return map.UpdateEntryUnsafe(CreateKValueEntry(hash, key)); // add a new conflicting key value ImMapEntry>[] newConflicts; if (oldEntry.Value.Value is ImMapEntry>[] conflicts) { // entry is already containing the conflicted entries var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Value.Key)) --conflictIndex; if (conflictIndex == -1) return map; // update the existing conflict newConflicts = new ImMapEntry>[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictIndex] = CreateKValueEntry(hash, key); } else { return map; } var conflictsEntry = new ImMapEntry>(hash); conflictsEntry.Value.Value = newConflicts; return map.UpdateEntryUnsafe(conflictsEntry); } /// Returns the entry if key is found or default value otherwise. [MethodImpl((MethodImplOptions)256)] public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, K key) { var entry = map.GetEntryOrDefault(hash); return entry != null ? key.Equals(entry.Value.Key) ? entry : GetConflictedEntryOrDefault(entry, key) : null; } /// Returns the value if key is found or default value otherwise. [MethodImpl((MethodImplOptions)256)] public static object GetValueOrDefault(this ImMap> map, int hash, K key) => map.GetEntryOrDefault(hash, key)?.Value.Value; /// Sets the value if key is found or returns false otherwise. [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImMap> map, int hash, K key, out object value) { var entry = map.GetEntryOrDefault(hash, key); if (entry != null) { value = entry.Value.Value; return true; } value = null; return false; } /// Returns the entry if key is found or `null` otherwise. [MethodImpl((MethodImplOptions)256)] public static ImMapEntry> GetEntryOrDefault(this ImMap> map, int hash, Type type) { var entry = map.GetEntryOrDefault(hash); return entry != null ? entry.Value.Key == type ? entry : GetConflictedEntryOrDefault(entry, type) : null; } /// Returns the value if the Type key is found or default value otherwise. [MethodImpl((MethodImplOptions)256)] public static object GetValueOrDefault(this ImMap> map, int hash, Type typeKey) => map.GetEntryOrDefault(hash, typeKey)?.Value.Value; /// Returns the value if the Type key is found or default value otherwise. [MethodImpl((MethodImplOptions)256)] public static object GetValueOrDefault(this ImMap> map, Type typeKey) => map.GetEntryOrDefault(RuntimeHelpers.GetHashCode(typeKey), typeKey)?.Value.Value; internal static ImMapEntry> GetConflictedEntryOrDefault(ImMapEntry> entry, K key) { if (entry.Value.Value is ImMapEntry>[] conflicts) for (var i = 0; i < conflicts.Length; ++i) if (key.Equals(conflicts[i].Value.Key)) return conflicts[i]; return null; } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// public static IEnumerable>> Enumerate(this ImMap> map) { foreach (var entry in map.Enumerate(null)) { if (entry.Value.Value is ImMapEntry>[] conflicts) for (var i = 0; i < conflicts.Length; i++) yield return conflicts[i]; else yield return entry; } } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public static S Fold(this ImMap> map, S state, Func>, S, S> reduce, ImMapTree>[] parentsStack = null) => map.Fold(state, reduce, (entry, s, r) => { if (entry.Value.Value is ImMapEntry>[] conflicts) for (var i = 0; i < conflicts.Length; i++) s = r(conflicts[i], s); else s = r(entry, s); return s; }, parentsStack); /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public static S Visit(this ImMap> map, S state, Action>, S> effect, ImMapTree>[] parentsStack = null) => map.Fold(state, effect, (entry, s, eff) => { if (entry.Value.Value is ImMapEntry>[] conflicts) for (var i = 0; i < conflicts.Length; i++) eff(conflicts[i], s); else eff(entry, s); return s; }, parentsStack); /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public static void Visit(this ImMap> map, Action>> effect, ImMapTree>[] parentsStack = null) => map.Fold(false, effect, (entry, s, eff) => { if (entry.Value.Value is ImMapEntry>[] conflicts) for (var i = 0; i < conflicts.Length; i++) eff(conflicts[i]); else eff(entry); return false; }, parentsStack); } /// /// The array of ImMap slots where the key first bits are used for FAST slot location /// and the slot is the reference to ImMap that can be swapped with its updated value /// public static class ImMapSlots { /// Default number of slots public const int SLOT_COUNT_POWER_OF_TWO = 32; /// The default mask to partition the key to the target slot public const int KEY_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; /// Creates the array with the empty slots [MethodImpl((MethodImplOptions)256)] public static ImMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) { var slots = new ImMap[slotCountPowerOfTwo]; for (var i = 0; i < slots.Length; ++i) slots[i] = ImMap.Empty; return slots; } /// Returns a new tree with added or updated value for specified key. [MethodImpl((MethodImplOptions)256)] public static void AddOrUpdate(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) { ref var slot = ref slots[key & keyMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(key, value), copy) != copy) RefAddOrUpdateSlot(ref slot, key, value); } /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrUpdateSlot(ref ImMap slot, int key, V value) => Ref.Swap(ref slot, key, value, (x, k, v) => x.AddOrUpdate(k, v)); /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public static void AddOrKeep(this ImMap[] slots, int key, V value, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) { ref var slot = ref slots[key & keyMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key, value), copy) != copy) RefAddOrKeepSlot(ref slot, key, value); } /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrKeepSlot(ref ImMap slot, int key, V value) => Ref.Swap(ref slot, key, value, (s, k, v) => s.AddOrKeep(k, v)); /// Adds a default value entry for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public static void AddOrKeep(this ImMap[] slots, int key, int keyMaskToFindSlot = KEY_MASK_TO_FIND_SLOT) { ref var slot = ref slots[key & keyMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(key), copy) != copy) RefAddOrKeepSlot(ref slot, key); } /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrKeepSlot(ref ImMap slot, int key) => Ref.Swap(ref slot, key, (s, k) => s.AddOrKeep(k)); /// Folds all map nodes without the order public static S Fold(this ImMap[] slots, S state, Func, S, S> reduce) { var parentStack = ArrayTools.Empty>(); for (var i = 0; i < slots.Length; ++i) { var map = slots[i]; if (map == ImMap.Empty) continue; if (map is ImMapEntry leaf) state = reduce(leaf, state); else if (map is ImMapBranch branch) { state = reduce(branch.Entry, state); state = reduce(branch.RightEntry, state); } else if (map is ImMapTree tree) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state); state = reduce(tree.Entry, state); state = reduce((ImMapEntry)tree.Right, state); } else { if (parentStack.Length < tree.TreeHeight - 2) parentStack = new ImMapTree[tree.TreeHeight - 2]; var parentIndex = -1; while (true) { if ((tree = map as ImMapTree) != null) { if (tree.TreeHeight == 2) { state = reduce((ImMapEntry)tree.Left, state); state = reduce(tree.Entry, state); state = reduce((ImMapEntry)tree.Right, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } else { parentStack[++parentIndex] = tree; map = tree.Left; } } else if ((branch = map as ImMapBranch) != null) { state = reduce(branch.Entry, state); state = reduce(branch.RightEntry, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } else { state = reduce((ImMapEntry)map, state); if (parentIndex == -1) break; tree = parentStack[parentIndex--]; state = reduce(tree.Entry, state); map = tree.Right; } } } } } return state; } } /// Wraps the stored data with "fixed" reference semantics - when added to the tree it did not change or reconstructed in memory public class ImHashMapEntry { /// Empty thingy public static readonly ImHashMapEntry Empty = new ImHashMapEntry(); /// Key hash public readonly int Hash; /// The key public readonly K Key; /// The value - may be mutated implementing the Ref CAS semantics if needed public V Value; private ImHashMapEntry() { } /// Constructs the data public ImHashMapEntry(int hash, K key, V value) { Hash = hash; Key = key; Value = value; } /// Constructs the data with the default value public ImHashMapEntry(int hash, K key) { Hash = hash; Key = key; } /// Outputs the brief tree info - mostly for debugging purposes public override string ToString() => Key + ": " + Value; } /// Stores ALL the data in `Conflicts` array, the fields except the `hash` are just fillers. /// This way we preserve the once created `ImHashMapData` so that client can hold the reference to it and update the Value if needed. public sealed class ImHashMapConflicts : ImHashMapEntry { /// Conflicted data public readonly ImHashMapEntry[] Conflicts; /// public ImHashMapConflicts(int hash, params ImHashMapEntry[] conflicts) : base(hash, default, default) => Conflicts = conflicts; } /// Immutable http://en.wikipedia.org/wiki/AVL_tree /// where node key is the hash code of public sealed class ImHashMap { /// Empty map to start with. public static readonly ImHashMap Empty = new ImHashMap(); /// Calculated key hash. public int Hash { [MethodImpl((MethodImplOptions)256)] get => Entry.Hash; } /// Key of type K that should support and . public K Key { [MethodImpl((MethodImplOptions)256)] get => Entry.Key; } /// Value of any type V. public V Value { [MethodImpl((MethodImplOptions)256)] get => Entry.Value; } /// In case of conflicts for different keys contains conflicted keys with their values. public ImHashMapEntry[] Conflicts { [MethodImpl((MethodImplOptions)256)] get => (Entry as ImHashMapConflicts)?.Conflicts; } /// Left sub-tree/branch, or empty. public ImHashMap Left; /// Right sub-tree/branch, or empty. public ImHashMap Right; /// Height of longest sub-tree/branch plus 1. It is 0 for empty tree, and 1 for single node tree. public int Height; /// Returns true if tree is empty. public bool IsEmpty => Height == 0; /// The entry which is allocated once and can be used as a "fixed" reference to the Key and Value public readonly ImHashMapEntry Entry; internal ImHashMap() => Entry = ImHashMapEntry.Empty; /// Creates leaf node public ImHashMap(int hash, K key, V value) { Entry = new ImHashMapEntry(hash, key, value); Left = Empty; Right = Empty; Height = 1; } /// Creates a leaf node with default value public ImHashMap(int hash, K key) { Entry = new ImHashMapEntry(hash, key); Left = Empty; Right = Empty; Height = 1; } /// Creates a leaf node public ImHashMap(ImHashMapEntry entry) { Entry = entry; Left = Empty; Right = Empty; Height = 1; } /// Creates the tree and calculates the height for you public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right) { Entry = entry; Left = left; Right = right; Height = 1 + (left.Height > right.Height ? left.Height : right.Height); } /// Creates the tree with the known height public ImHashMap(ImHashMapEntry entry, ImHashMap left, ImHashMap right, int height) { Entry = entry; Left = left; Right = right; Height = height; } /// Outputs the brief tree info - mostly for debugging purposes public override string ToString() => Height == 0 ? "empty" : "(" + Entry + ") -> (" + (Left.Height == 0 ? "empty" : Left.Entry + " of height " + Left.Height) + ", " + (Right.Height == 0 ? "empty" : Right.Entry + " of height " + Right.Height) + ")"; /// Uses the user provided hash and adds and updates the tree with passed key-value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrUpdate(int hash, K key, V value) => Height == 0 ? new ImHashMap(hash, key, value) : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value) : AddOrUpdateLeftOrRight(hash, key, value); /// Adds and updates the tree with passed key-value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrUpdate(K key, V value) => AddOrUpdate(key.GetHashCode(), key, value); private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value) { var conflictsData = Entry as ImHashMapConflicts; return conflictsData == null && (ReferenceEquals(key, Key) || key.Equals(Key)) ? new ImHashMap(new ImHashMapEntry(hash, key, value), Left, Right, Height) : AddOrUpdateConflict(conflictsData, hash, key, value); } internal enum DoAddOrUpdateConflicts { AddOrUpdate, AddOrKeep, Update } private ImHashMap AddOrUpdateConflict(ImHashMapConflicts conflictsData, int hash, K key, V value, Update update = null, DoAddOrUpdateConflicts doWhat = DoAddOrUpdateConflicts.AddOrUpdate) { if (conflictsData == null) return doWhat == DoAddOrUpdateConflicts.Update ? this : new ImHashMap( new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key, value)), Left, Right, Height); var conflicts = conflictsData.Conflicts; var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) --conflictIndex; ImHashMapEntry[] newConflicts; if (conflictIndex != -1) { if (doWhat == DoAddOrUpdateConflicts.AddOrKeep) return this; // update the existing conflicted value newConflicts = new ImHashMapEntry[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); var newValue = update == null ? value : update(key, conflicts[conflictIndex].Value, value); newConflicts[conflictIndex] = new ImHashMapEntry(hash, key, newValue); } else { if (doWhat == DoAddOrUpdateConflicts.Update) return this; // add the new conflicting value newConflicts = new ImHashMapEntry[conflictCount + 1]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictCount] = new ImHashMapEntry(hash, key, value); } return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); } private ImHashMap AddOrUpdateLeftOrRight(int hash, K key, V value) { if (hash < Hash) { if (Left.Height == 0) return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); if (Left.Hash == hash) return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value), Right, Height); if (Right.Height == 0) { if (hash < Left.Hash) return new ImHashMap(Left.Entry, new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); } var left = Left.AddOrUpdateLeftOrRight(hash, key, value); return left.Height > Right.Height + 1 ? BalanceNewLeftTree(left) : new ImHashMap(Entry, left, Right); } else { if (Right.Height == 0) return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); if (Right.Hash == hash) return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value), Height); if (Left.Height == 0) { if (hash < Right.Hash) return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); return new ImHashMap(Right.Entry, new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); } var right = Right.AddOrUpdateLeftOrRight(hash, key, value); return right.Height > Left.Height + 1 ? BalanceNewRightTree(right) : new ImHashMap(Entry, Left, right); } } private ImHashMap BalanceNewLeftTree(ImHashMap newLeftTree) { var leftLeft = newLeftTree.Left; var leftLeftHeight = leftLeft.Height; var leftRight = newLeftTree.Right; var leftRightHeight = leftRight.Height; if (leftRightHeight > leftLeftHeight) { newLeftTree.Right = leftRight.Left; newLeftTree.Height = leftLeftHeight + 1; return new ImHashMap(leftRight.Entry, newLeftTree, new ImHashMap(Entry, leftRight.Right, Right, Right.Height + 1), leftLeftHeight + 2); //return new ImHashMap(leftRight.Entry, // new ImHashMap(newLeftTree.Entry, leftLeft, leftRight.Left), // new ImHashMap(Entry, leftRight.Right, Right)); } newLeftTree.Right = new ImHashMap(Entry, leftRight, Right, leftRightHeight + 1); newLeftTree.Height = leftRightHeight + 2; return newLeftTree; //return new ImHashMap(newLeftTree.Entry, // leftLeft, new ImHashMap(Entry, leftRight, Right)); } // Note that Left is by 2 less deep than `newRightTree` - means that at `newRightTree.Left/Right` is at least of Left height or deeper private ImHashMap BalanceNewRightTree(ImHashMap newRightTree) { var rightLeft = newRightTree.Left; var rightLeftHeight = rightLeft.Height; var rightRight = newRightTree.Right; var rightRightHeight = rightRight.Height; if (rightLeftHeight > rightRightHeight) // 1 greater - not 2 greater because it would be too unbalanced { newRightTree.Left = rightLeft.Right; // the height now should be defined by rr - because left now is shorter by 1 newRightTree.Height = rightRightHeight + 1; // the whole height consequentially can be defined by `newRightTree` (rr+1) because left is consist of short Left and -2 rl.Left return new ImHashMap(rightLeft.Entry, // Left should be >= rightLeft.Left because it maybe rightLeft.Right which defines rl height new ImHashMap(Entry, Left, rightLeft.Left, height: Left.Height + 1), newRightTree, rightRightHeight + 2); //return new ImHashMap(rightLeft.Entry, // new ImHashMap(Entry, Left, rightLeft.Left), // new ImHashMap(newRightTree.Entry, rightLeft.Right, rightRight)); } // we may decide on the height because the Left smaller by 2 newRightTree.Left = new ImHashMap(Entry, Left, rightLeft, rightLeftHeight + 1); // if rr was > rl by 1 than new rl+1 should be equal height to rr now, if rr was == rl than new rl wins anyway newRightTree.Height = rightLeftHeight + 2; return newRightTree; //return new ImHashMap(newRightTree.Entry, new ImHashMap(Entry, Left, rightLeft), rightRight); } /// Uses the user provided hash and adds and updates the tree with passed key-value and the update function for the existing value. Returns a new tree. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrUpdate(int hash, K key, V value, Update update) => Height == 0 ? new ImHashMap(hash, key, value) : hash == Hash ? UpdateValueOrAddOrUpdateConflict(hash, key, value, update) : AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); private ImHashMap UpdateValueOrAddOrUpdateConflict(int hash, K key, V value, Update update) { var conflictsData = Entry as ImHashMapConflicts; return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) ? new ImHashMap(new ImHashMapEntry(hash, key, update(key, Value, value)), Left, Right, Height) : AddOrUpdateConflict(conflictsData, hash, key, value, update); } private ImHashMap AddOrUpdateLeftOrRightWithUpdate(int hash, K key, V value, Update update) { if (hash < Hash) { if (Left.Height == 0) return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); if (Left.Hash == hash) return new ImHashMap(Entry, Left.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Right, Height); if (Right.Height == 0) { if (hash < Left.Hash) return new ImHashMap(Left.Entry, new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); } var left = Left.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); return left.Height > Right.Height + 1 ? BalanceNewLeftTree(left) : new ImHashMap(Entry, left, Right); } else { if (Right.Height == 0) return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); if (Right.Hash == hash) return new ImHashMap(Entry, Left, Right.UpdateValueOrAddOrUpdateConflict(hash, key, value, update), Height); if (Left.Height == 0) { if (hash < Right.Hash) return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); return new ImHashMap(Right.Entry, new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); } var right = Right.AddOrUpdateLeftOrRightWithUpdate(hash, key, value, update); return right.Height > Left.Height + 1 ? BalanceNewRightTree(right) : new ImHashMap(Entry, Left, right); } } /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrUpdate(K key, V value, Update update) => AddOrUpdate(key.GetHashCode(), key, value, update); /// Returns a new tree with added or updated key-value. Uses the provided for updating the existing value. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrUpdate(K key, V value, Update update) => AddOrUpdate(key.GetHashCode(), key, value, update.IgnoreKey); /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrKeep(int hash, K key, V value) => Height == 0 ? new ImHashMap(hash, key, value) : hash == Hash ? KeepValueOrAddConflict(hash, key, value) : AddOrKeepLeftOrRight(hash, key, value); /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrKeep(K key, V value) => AddOrKeep(key.GetHashCode(), key, value); private ImHashMap KeepValueOrAddConflict(int hash, K key, V value) { var conflictsData = Entry as ImHashMapConflicts; return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) ? this : AddOrUpdateConflict(conflictsData, hash, key, value, null, DoAddOrUpdateConflicts.AddOrKeep); } private ImHashMap AddOrKeepLeftOrRight(int hash, K key, V value) { if (hash < Hash) { if (Left.Height == 0) return new ImHashMap(Entry, new ImHashMap(hash, key, value), Right, 2); if (Left.Hash == hash) { var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key, value); return ReferenceEquals(leftWithNewConflict, Left) ? this : new ImHashMap(Entry, leftWithNewConflict, Right, Height); } if (Right.Height == 0) { if (hash < Left.Hash) return new ImHashMap(Left.Entry, new ImHashMap(hash, key, value), new ImHashMap(Entry), 2); return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); } var left = Left.AddOrKeepLeftOrRight(hash, key, value); if (ReferenceEquals(left, Left)) return this; return left.Height > Right.Height + 1 ? BalanceNewLeftTree(left) : new ImHashMap(Entry, left, Right); } else { if (Right.Height == 0) return new ImHashMap(Entry, Left, new ImHashMap(hash, key, value), 2); if (Right.Hash == hash) { var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key, value); return ReferenceEquals(rightWithNewConflict, Right) ? this : new ImHashMap(Entry, Left, rightWithNewConflict, Height); } if (Left.Height == 0) { if (hash < Right.Hash) return new ImHashMap(new ImHashMapEntry(hash, key, value), new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); return new ImHashMap(Right.Entry, new ImHashMap(Entry), new ImHashMap(hash, key, value), 2); } var right = Right.AddOrKeepLeftOrRight(hash, key, value); if (ReferenceEquals(right, Right)) return this; return right.Height > Left.Height + 1 ? BalanceNewRightTree(right) : new ImHashMap(Entry, Left, right); } } /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrKeep(int hash, K key) => Height == 0 ? new ImHashMap(hash, key) : hash == Hash ? KeepValueOrAddConflict(hash, key) : AddOrKeepLeftOrRight(hash, key); /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public ImHashMap AddOrKeep(K key) => AddOrKeep(key.GetHashCode(), key); private ImHashMap KeepValueOrAddConflict(int hash, K key) { var conflictsData = Entry as ImHashMapConflicts; return conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key)) ? this : AddOrKeepConflict(conflictsData, hash, key); } private ImHashMap AddOrKeepConflict(ImHashMapConflicts conflictsData, int hash, K key) { if (conflictsData == null) return new ImHashMap( new ImHashMapConflicts(hash, Entry, new ImHashMapEntry(hash, key)), Left, Right, Height); var conflicts = conflictsData.Conflicts; var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) --conflictIndex; if (conflictIndex != -1) return this; // add the new conflicting value var newConflicts = new ImHashMapEntry[conflictCount + 1]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictCount] = new ImHashMapEntry(hash, key); return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); } private ImHashMap AddOrKeepLeftOrRight(int hash, K key) { if (hash < Hash) { if (Left.Height == 0) return new ImHashMap(Entry, new ImHashMap(hash, key), Right, 2); if (Left.Hash == hash) { var leftWithNewConflict = Left.KeepValueOrAddConflict(hash, key); return ReferenceEquals(leftWithNewConflict, Left) ? this : new ImHashMap(Entry, leftWithNewConflict, Right, Height); } if (Right.Height == 0) { if (hash < Left.Hash) return new ImHashMap(Left.Entry, new ImHashMap(hash, key), new ImHashMap(Entry), 2); return new ImHashMap(new ImHashMapEntry(hash, key), new ImHashMap(Left.Entry), new ImHashMap(Entry), 2); } var left = Left.AddOrKeepLeftOrRight(hash, key); if (ReferenceEquals(left, Left)) return this; return left.Height > Right.Height + 1 ? BalanceNewLeftTree(left) : new ImHashMap(Entry, left, Right); } else { if (Right.Height == 0) return new ImHashMap(Entry, Left, new ImHashMap(hash, key), 2); if (Right.Hash == hash) { var rightWithNewConflict = Right.KeepValueOrAddConflict(hash, key); return ReferenceEquals(rightWithNewConflict, Right) ? this : new ImHashMap(Entry, Left, rightWithNewConflict, Height); } if (Left.Height == 0) { if (hash < Right.Hash) return new ImHashMap(new ImHashMapEntry(hash, key), new ImHashMap(Entry), new ImHashMap(Right.Entry), 2); return new ImHashMap(Right.Entry, new ImHashMap(Entry), new ImHashMap(hash, key), 2); } var right = Right.AddOrKeepLeftOrRight(hash, key); if (ReferenceEquals(right, Right)) return this; return right.Height > Left.Height + 1 ? BalanceNewRightTree(right) : new ImHashMap(Entry, Left, right); } } /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. public ImHashMap Update(int hash, K key, V value, Update update = null) { if (Height == 0) return this; // No need to balance cause we not adding or removing nodes if (hash < Hash) { var left = Left.Update(hash, key, value, update); return ReferenceEquals(left, Left) ? this : new ImHashMap(Entry, left, Right, Height); } if (hash > Hash) { var right = Right.Update(hash, key, value, update); return ReferenceEquals(right, Right) ? this : new ImHashMap(Entry, Left, right, Height); } var conflictsData = Entry as ImHashMapConflicts; if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) return new ImHashMap( new ImHashMapEntry(hash, key, update == null ? value : update(key, Value, value)), Left, Right, Height); return AddOrUpdateConflict(conflictsData, hash, key, value, update, DoAddOrUpdateConflicts.Update); } /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. [MethodImpl((MethodImplOptions)256)] public ImHashMap Update(K key, V value) => Update(key.GetHashCode(), key, value); /// Updates the map with the new value if key is found, otherwise returns the same unchanged map. [MethodImpl((MethodImplOptions)256)] public ImHashMap Update(K key, V value, Update update) => Update(key.GetHashCode(), key, value, update.IgnoreKey); /// Updates the map with the Default (null for reference types) value if key is found, otherwise returns the same unchanged map. [MethodImpl((MethodImplOptions)256)] public ImHashMap UpdateToDefault(int hash, K key) { if (Height == 0) return this; // No need to balance cause we not adding or removing nodes if (hash < Hash) { var left = Left.UpdateToDefault(hash, key); return left == Left ? this : new ImHashMap(Entry, left, Right, Height); } if (hash > Hash) { var right = Right.UpdateToDefault(hash, key); return right == Right ? this : new ImHashMap(Entry, Left, right, Height); } var conflictsData = Entry as ImHashMapConflicts; if (conflictsData == null && (ReferenceEquals(Key, key) || Key.Equals(key))) return new ImHashMap(new ImHashMapEntry(hash, key), Left, Right, Height); return UpdateConflictToDefault(conflictsData, hash, key); } private ImHashMap UpdateConflictToDefault(ImHashMapConflicts conflictsData, int hash, K key) { if (conflictsData == null) return this; var conflicts = conflictsData.Conflicts; var conflictCount = conflicts.Length; var conflictIndex = conflictCount - 1; while (conflictIndex != -1 && !key.Equals(conflicts[conflictIndex].Key)) --conflictIndex; if (conflictIndex == -1) return this; // update the existing conflicted value var newConflicts = new ImHashMapEntry[conflictCount]; Array.Copy(conflicts, 0, newConflicts, 0, conflictCount); newConflicts[conflictIndex] = new ImHashMapEntry(hash, key); return new ImHashMap(new ImHashMapConflicts(hash, newConflicts), Left, Right, Height); } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// public IEnumerable> Enumerate() { if (Height != 0) { var parents = new ImHashMap[Height]; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parents[++parentCount] = node; node = node.Left; } else { node = parents[parentCount--]; if (node.Entry is ImHashMapConflicts conflictsData) { var conflicts = conflictsData.Conflicts; for (var i = 0; i < conflicts.Length; i++) yield return conflicts[i]; } else { yield return node.Entry; } node = node.Right; } } } } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public S Fold(S state, Func, S, S> reduce, ImHashMap[] parentsStack = null) { if (Height == 1 && Entry is ImHashMapConflicts == false) return reduce(Entry, state); if (Height != 0) { parentsStack = parentsStack ?? new ImHashMap[Height]; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parentsStack[++parentCount] = node; node = node.Left; } else { node = parentsStack[parentCount--]; if (!(node.Entry is ImHashMapConflicts conflicts)) state = reduce(node.Entry, state); else { var conflict = conflicts.Conflicts; for (var i = 0; i < conflict.Length; i++) state = reduce(conflict[i], state); } node = node.Right; } } } return state; } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public S Fold(S state, Func, int, S, S> reduce, ImHashMap[] parentsStack = null) { if (Height == 1 && Entry is ImHashMapConflicts == false) return reduce(Entry, 0, state); if (Height != 0) { parentsStack = parentsStack ?? new ImHashMap[Height]; var index = 0; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parentsStack[++parentCount] = node; node = node.Left; } else { node = parentsStack[parentCount--]; if (!(node.Entry is ImHashMapConflicts conflicts)) state = reduce(node.Entry, index++, state); else { var conflictData = conflicts.Conflicts; for (var i = 0; i < conflictData.Length; i++) state = reduce(conflictData[i], index++, state); } node = node.Right; } } } return state; } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public S Visit(S state, Action, S> effect, ImHashMap[] parentsStack = null) { if (Height == 1 && Entry is ImHashMapConflicts == false) { effect(Entry, state); } else if (Height != 0) { parentsStack = parentsStack ?? new ImHashMap[Height]; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parentsStack[++parentCount] = node; node = node.Left; } else { node = parentsStack[parentCount--]; if (!(node.Entry is ImHashMapConflicts conflicts)) effect(node.Entry, state); else { var conflict = conflicts.Conflicts; for (var i = 0; i < conflict.Length; i++) effect(conflict[i], state); } node = node.Right; } } } return state; } /// /// Depth-first in-order traversal as described in http://en.wikipedia.org/wiki/Tree_traversal /// The only difference is using fixed size array instead of stack for speed-up. /// Note: By passing you may reuse the stack array between different method calls, /// but it should be at least length. The contents of array are not important. /// public void Visit(Action> effect, ImHashMap[] parentsStack = null) { if (Height == 1 && Entry is ImHashMapConflicts == false) effect(Entry); else if (Height != 0) { parentsStack = parentsStack ?? new ImHashMap[Height]; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parentsStack[++parentCount] = node; node = node.Left; } else { node = parentsStack[parentCount--]; if (!(node.Entry is ImHashMapConflicts conflicts)) effect(node.Entry); else { var conflict = conflicts.Conflicts; for (var i = 0; i < conflict.Length; i++) effect(conflict[i]); } node = node.Right; } } } } /// Finds the first entry matching the condition, returns `null` if not found public ImHashMapEntry FindFirstOrDefault(Func, bool> condition, ImHashMap[] parentsStack = null) { if (Height == 1 && Entry is ImHashMapConflicts == false) { if (condition(Entry)) return Entry; } else if (Height != 0) { parentsStack = parentsStack ?? new ImHashMap[Height]; var node = this; var parentCount = -1; while (node.Height != 0 || parentCount != -1) { if (node.Height != 0) { parentsStack[++parentCount] = node; node = node.Left; } else { node = parentsStack[parentCount--]; if (!(node.Entry is ImHashMapConflicts conflicts)) { if (condition(node.Entry)) return node.Entry; } else { var conflictedEntries = conflicts.Conflicts; for (var i = 0; i < conflictedEntries.Length; i++) if (condition(conflictedEntries[i])) return conflictedEntries[i]; } node = node.Right; } } } return null; } /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx public ImHashMap Remove(int hash, K key) => RemoveImpl(hash, key); /// Removes or updates value for specified key, or does nothing if the key is not found (returns the unchanged map) /// Based on Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2008/01/21/immutability-in-c-part-nine-academic-plus-my-avl-tree-implementation.aspx [MethodImpl((MethodImplOptions)256)] public ImHashMap Remove(K key) => RemoveImpl(key.GetHashCode(), key); private ImHashMap RemoveImpl(int hash, K key, bool ignoreKey = false) { if (Height == 0) return this; ImHashMap result; if (hash == Hash) // found node { if (ignoreKey || Equals(Key, key)) { if (Height == 1) // remove node return Empty; if (Right.IsEmpty) result = Left; else if (Left.IsEmpty) result = Right; else { // we have two children, so remove the next highest node and replace this node with it. var next = Right; while (!next.Left.IsEmpty) next = next.Left; result = new ImHashMap(next.Entry, Left, Right.RemoveImpl(next.Hash, default, ignoreKey: true)); } } else if (Entry is ImHashMapConflicts conflictsData) return TryRemoveConflicted(conflictsData, hash, key); else return this; // if key is not matching and no conflicts to lookup - just return } else result = hash < Hash ? Balance(Entry, Left.RemoveImpl(hash, key, ignoreKey), Right) : Balance(Entry, Left, Right.RemoveImpl(hash, key, ignoreKey)); return result; } /// Searches for the key in the conflicts and returns true if found public bool ContainsConflictedData(K key) { if (Conflicts != null) { var conflicts = Conflicts; for (var i = 0; i < conflicts.Length; ++i) if (key.Equals(conflicts[i].Key)) return true; } return false; } /// Searches for the key in the node conflicts public ImHashMapEntry GetConflictedEntryOrDefault(K key) { if (Conflicts != null) { var conflicts = Conflicts; for (var i = 0; i < conflicts.Length; ++i) if (key.Equals(conflicts[i].Key)) return conflicts[i]; } return null; } /// Searches for the key in the node conflicts public V GetConflictedValueOrDefault(K key, V defaultValue) { if (Conflicts != null) { var conflicts = Conflicts; for (var i = 0; i < conflicts.Length; ++i) if (key.Equals(conflicts[i].Key)) return conflicts[i].Value; } return defaultValue; } /// Searches for the key in the node conflicts public bool TryFindConflictedValue(K key, out V value) { if (Conflicts != null) { var conflicts = Conflicts; for (var i = 0; i < conflicts.Length; ++i) if (Equals(conflicts[i].Key, key)) { value = conflicts[i].Value; return true; } } value = default; return false; } // todo: implement in terms of BalanceNewLeftTree | BalanceNewRightTree private static ImHashMap Balance(ImHashMapEntry entry, ImHashMap left, ImHashMap right) { var delta = left.Height - right.Height; if (delta > 1) // left is longer by 2, rotate left { var leftLeft = left.Left; var leftRight = left.Right; if (leftRight.Height > leftLeft.Height) { // double rotation: // 5 => 5 => 4 // 2 6 4 6 2 5 // 1 4 2 3 1 3 6 // 3 1 return new ImHashMap(leftRight.Entry, new ImHashMap(left.Entry, leftLeft, leftRight.Left), new ImHashMap(entry, leftRight.Right, right)); } // one rotation: // 5 => 2 // 2 6 1 5 // 1 4 4 6 return new ImHashMap(left.Entry, leftLeft, new ImHashMap(entry, leftRight, right)); } if (delta < -1) { var rightLeft = right.Left; var rightRight = right.Right; return rightLeft.Height > rightRight.Height ? new ImHashMap(rightLeft.Entry, new ImHashMap(entry, left, rightLeft.Left), new ImHashMap(right.Entry, rightLeft.Right, rightRight)) : new ImHashMap(right.Entry, new ImHashMap(entry, left, rightLeft), rightRight); } return new ImHashMap(entry, left, right); } private ImHashMap TryRemoveConflicted(ImHashMapConflicts conflictsData, int hash, K key) { var conflicts = conflictsData.Conflicts; var index = conflicts.Length - 1; while (index != -1 && !conflicts[index].Key.Equals(key)) --index; if (index == -1) // key is not found in conflicts - just return return this; // we removing the one from the 2 items, so we can reference the remaining item directly from the map node if (conflicts.Length == 2) return new ImHashMap(index == 0 ? conflicts[1] : conflicts[0], Left, Right, Height); // copy all except the `index`ed data into shrinked conflicts var shrinkedConflicts = new ImHashMapEntry[conflicts.Length - 1]; var newIndex = 0; for (var i = 0; i < conflicts.Length; ++i) if (i != index) shrinkedConflicts[newIndex++] = conflicts[i]; return new ImHashMap(new ImHashMapConflicts(hash, shrinkedConflicts), Left, Right, Height); } } /// ImHashMap methods for faster performance public static class ImHashMap { internal static V IgnoreKey(this Update update, K _, V oldValue, V newValue) => update(oldValue, newValue); /// Looks for key in a tree and returns `true` if found. [MethodImpl((MethodImplOptions)256)] public static bool Contains(this ImHashMap map, int hash, K key) { while (map.Height != 0 && map.Hash != hash) map = hash < map.Hash ? map.Left : map.Right; return map.Height != 0 && (key.Equals(map.Key) || map.ContainsConflictedData(key)); } /// Looks for key in a tree and returns `true` if found. [MethodImpl((MethodImplOptions)256)] public static bool Contains(this ImHashMap map, K key) => map.Height != 0 && map.Contains(key.GetHashCode(), key); /// Looks for key in a tree and returns the Data object if found or `null` otherwise. [MethodImpl((MethodImplOptions)256)] public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, int hash, K key) { while (map.Height != 0 && map.Hash != hash) map = hash < map.Hash ? map.Left : map.Right; return map.Height == 0 ? null : key.Equals(map.Key) ? map.Entry : map.GetConflictedEntryOrDefault(key); } /// Looks for key in a tree and returns the Data object if found or `null` otherwise. [MethodImpl((MethodImplOptions)256)] public static ImHashMapEntry GetEntryOrDefault(this ImHashMap map, K key) { if (map.Height == 0) return null; var hash = key.GetHashCode(); while (map.Hash != hash) { map = hash < map.Hash ? map.Left : map.Right; if (map.Height == 0) return null; } return key.Equals(map.Key) ? map.Entry : map.GetConflictedEntryOrDefault(key); } /// Looks for key in a tree and returns the key value if found, or otherwise. [MethodImpl((MethodImplOptions)256)] public static V GetValueOrDefault(this ImHashMap map, K key, V defaultValue = default) { if (map.Height == 0) return defaultValue; var hash = key.GetHashCode(); while (map.Hash != hash) { map = hash < map.Hash ? map.Left : map.Right; if (map.Height == 0) return defaultValue; } return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); } /// Looks for key in a tree and returns the key value if found, or otherwise. [MethodImpl((MethodImplOptions)256)] public static V GetValueOrDefault(this ImHashMap map, int hash, K key, V defaultValue = default) { if (map.Height == 0) return defaultValue; while (map.Hash != hash) { map = hash < map.Hash ? map.Left : map.Right; if (map.Height == 0) return defaultValue; } return key.Equals(map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); } /// Looks for key in a tree and returns the key value if found, or otherwise. [MethodImpl((MethodImplOptions)256)] public static V GetValueOrDefault(this ImHashMap map, Type key, V defaultValue = default) { if (map.Height == 0) return defaultValue; var hash = RuntimeHelpers.GetHashCode(key); while (hash != map.Hash) { map = hash < map.Hash ? map.Left : map.Right; if (map.Height == 0) return defaultValue; } // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); } /// Looks for key in a tree and returns the key value if found, or otherwise. [MethodImpl((MethodImplOptions)256)] public static V GetValueOrDefault(this ImHashMap map, int hash, Type key, V defaultValue = default) { if (map.Height == 0) return defaultValue; while (hash != map.Hash) { map = hash < map.Hash ? map.Left : map.Right; if (map.Height == 0) return defaultValue; } // we don't need to check `Height != 0` again cause in that case `key` will be `null` and `ReferenceEquals` will fail return ReferenceEquals(key, map.Key) ? map.Value : map.GetConflictedValueOrDefault(key, defaultValue); } /// Returns true if key is found and sets the value. [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImHashMap map, K key, out V value) { if (map.Height != 0) { var hash = key.GetHashCode(); while (hash != map.Hash && map.Height != 0) map = hash < map.Hash ? map.Left : map.Right; if (map.Height != 0) { if (key.Equals(map.Key)) { value = map.Value; return true; } return map.TryFindConflictedValue(key, out value); } } value = default; return false; } /// Returns true if key is found and sets the value. [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImHashMap map, int hash, K key, out V value) { if (map.Height != 0) { while (hash != map.Hash && map.Height != 0) map = hash < map.Hash ? map.Left : map.Right; if (map.Height != 0) { if (key.Equals(map.Key)) { value = map.Value; return true; } return map.TryFindConflictedValue(key, out value); } } value = default; return false; } /// Returns true if key is found and the result value. [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImHashMap map, Type key, out V value) { if (map.Height != 0) { var hash = RuntimeHelpers.GetHashCode(key); while (hash != map.Hash && map.Height != 0) map = hash < map.Hash ? map.Left : map.Right; if (map.Height != 0) { // assign to `var data = ...` if (ReferenceEquals(key, map.Key)) { value = map.Value; return true; } return map.TryFindConflictedValue(key, out value); } } value = default; return false; } /// Returns true if hash and key are found and the result value, or the false otherwise [MethodImpl((MethodImplOptions)256)] public static bool TryFind(this ImHashMap map, int hash, Type key, out V value) { if (map.Height != 0) { while (hash != map.Hash && map.Height != 0) map = hash < map.Hash ? map.Left : map.Right; if (map.Height != 0) { if (ReferenceEquals(key, map.Key)) { value = map.Value; return true; } return map.TryFindConflictedValue(key, out value); } } value = default; return false; } /// Uses `RuntimeHelpers.GetHashCode()` public static ImHashMap AddOrUpdate(this ImHashMap map, Type key, V value) => map.AddOrUpdate(RuntimeHelpers.GetHashCode(key), key, value); } /// The array of ImHashMap slots where the key first bits are used for FAST slot location /// and the slot is the reference to ImHashMap that can be swapped with its updated value public static class ImHashMapSlots { /// Default number of slots public const int SLOT_COUNT_POWER_OF_TWO = 32; /// The default mask to partition the key to the target slot public const int HASH_MASK_TO_FIND_SLOT = SLOT_COUNT_POWER_OF_TWO - 1; /// Creates the array with the empty slots [MethodImpl((MethodImplOptions)256)] public static ImHashMap[] CreateWithEmpty(int slotCountPowerOfTwo = SLOT_COUNT_POWER_OF_TWO) { var slots = new ImHashMap[slotCountPowerOfTwo]; for (var i = 0; i < slots.Length; ++i) slots[i] = ImHashMap.Empty; return slots; } /// Returns a new tree with added or updated value for specified key. [MethodImpl((MethodImplOptions)256)] public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) { ref var slot = ref slots[hash & hashMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value), copy) != copy) RefAddOrUpdateSlot(ref slot, hash, key, value); } /// Returns a new tree with added or updated value for specified key. [MethodImpl((MethodImplOptions)256)] public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => slots.AddOrUpdate(key.GetHashCode(), key, value, hashMaskToFindSlot); /// Updates the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v)); /// Updates the value with help of `updateValue` function [MethodImpl((MethodImplOptions)256)] public static void AddOrUpdate(this ImHashMap[] slots, int hash, K key, V value, Update update, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) { ref var slot = ref slots[hash & hashMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrUpdate(hash, key, value, update), copy) != copy) RefAddOrUpdateSlot(ref slot, hash, key, value, update); } /// Updates the value with help of `updateValue` function [MethodImpl((MethodImplOptions)256)] public static void AddOrUpdate(this ImHashMap[] slots, K key, V value, Update updateValue, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => slots.AddOrUpdate(key.GetHashCode(), key, value, updateValue, hashMaskToFindSlot); /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrUpdateSlot(ref ImHashMap slot, int hash, K key, V value, Update update) => Ref.Swap(ref slot, hash, key, value, (x, h, k, v) => x.AddOrUpdate(h, k, v, update)); /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public static void AddOrKeep(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) { ref var slot = ref slots[hash & hashMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.AddOrKeep(hash, key, value), copy) != copy) RefAddOrKeepSlot(ref slot, hash, key, value); } /// Adds a new value for the specified key or keeps the existing map if the key is already in the map. [MethodImpl((MethodImplOptions)256)] public static void AddOrKeep(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => slots.AddOrKeep(key.GetHashCode(), key, value, hashMaskToFindSlot); /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefAddOrKeepSlot(ref ImHashMap slot, int hash, K key, V value) => Ref.Swap(ref slot, hash, key, value, (s, h, k, v) => s.AddOrKeep(h, k, v)); /// Updates the specified slot or does not change it [MethodImpl((MethodImplOptions)256)] public static void Update(this ImHashMap[] slots, int hash, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) { ref var slot = ref slots[hash & hashMaskToFindSlot]; var copy = slot; if (Interlocked.CompareExchange(ref slot, copy.Update(hash, key, value), copy) != copy) RefUpdateSlot(ref slot, hash, key, value); } /// Updates the specified slot or does not change it [MethodImpl((MethodImplOptions)256)] public static void Update(this ImHashMap[] slots, K key, V value, int hashMaskToFindSlot = HASH_MASK_TO_FIND_SLOT) => slots.Update(key.GetHashCode(), key, value, hashMaskToFindSlot); /// Update the ref to the slot with the new version - retry if the someone changed the slot in between public static void RefUpdateSlot(ref ImHashMap slot, int hash, K key, V value) => Ref.Swap(ref slot, key, value, (s, k, v) => s.Update(k, v)); /// Returns all map tree nodes without the order public static S Fold(this ImHashMap[] slots, S state, Func, S, S> reduce) { var parentStack = ArrayTools.Empty>(); for (var s = 0; s < slots.Length; s++) { var map = slots[s]; var height = map.Height; if (height != 0) { if (height > 1 && parentStack.Length < height) parentStack = new ImHashMap[height]; state = map.Fold(state, reduce, parentStack); } } return state; } } }