using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Numerics; using System.Reflection; using System.Text.RegularExpressions; using float2 = CultMath.float2; using Random = CultMath.Random; public static class Extensions { public static bool IsImplementationOf(this Type baseType, Type interfaceType) { return baseType.GetInterfaces().Any(interfaceType.Equals); } public static T[] WeightedRandomElements(this IEnumerable collection, ref Random random, Func weightFunction, int count) { var elements = collection as T[] ?? collection.ToArray(); var weights = new Dictionary(elements.Length); var totalWeight = 0f; foreach (var x in elements) { weights[x] = weightFunction(x); totalWeight += weights[x]; } var randomElements = new T[count]; for (int i = 0; i < count; i++) { var targetWeight = random.NextFloat(totalWeight); var accumWeight = 0f; foreach (var x in elements) { accumWeight += weights[x]; if (accumWeight > targetWeight) { randomElements[i] = x; break; } } } return randomElements; } // Thanks, https://stackoverflow.com/a/48599119 public static bool ByteArrayCompare(ReadOnlySpan a1, ReadOnlySpan a2) { return a1.SequenceEqual(a2); } public static bool ByteEquals(this byte[] a, byte[] b) => ByteArrayCompare(a, b); public static char Arrow(this ItemRotation rot) { switch (rot) { case ItemRotation.None: return '\u2191'; case ItemRotation.Clockwise: return '\u2192'; case ItemRotation.Reversed: return '\u2193'; case ItemRotation.CounterClockwise: return '\u2190'; default: throw new ArgumentOutOfRangeException(nameof(rot), rot, null); } } public static float2 Direction(this ItemRotation rotation) { switch (rotation) { case ItemRotation.None: return new float2(0, 1); case ItemRotation.CounterClockwise: return new float2(-1, 0); case ItemRotation.Reversed: return new float2(0, -1); case ItemRotation.Clockwise: return new float2(1, 0); default: throw new ArgumentOutOfRangeException(nameof(rotation), rotation, null); } } public static float2 Rotate(this float2 v, ItemRotation rotation) { switch (rotation) { case ItemRotation.None: return v; case ItemRotation.CounterClockwise: return new float2(-v.y, v.x); case ItemRotation.Reversed: return new float2(-v.x, -v.y); case ItemRotation.Clockwise: return new float2(v.y, -v.x); default: throw new ArgumentOutOfRangeException(nameof(rotation), rotation, null); } } private static Random? _random; //private static Random Random => (Random) (_random ??= new Random((uint) (DateTime.Now.Ticks%uint.MaxValue))); // public static T RandomElement(this IEnumerable enumerable) => enumerable.ElementAt(Random.NextInt(0, enumerable.Count())); public static float NextPowerDistribution(this ref Random random, float min, float max, float exp, float randexp) => MathF.Pow((MathF.Pow(max, exp + 1) - MathF.Pow(min, exp + 1)) * MathF.Pow(random.NextFloat(), randexp) + MathF.Pow(min, exp + 1), 1 / (exp + 1)); public static float NextUnbounded(this ref Random random) => 1 / (1 - random.NextFloat()) - 1; public static float NextUnbounded(this ref Random random, float bias, float power, float ceiling) => 1 / (1 - MathF.Pow(MathF.Min(random.NextFloat(), ceiling), 1 - MathF.Pow(Clamp(bias, 0, .99f), 1 / power))) - 1; public static bool IsDefault(this T value) where T : struct { bool isDefault = value.Equals(default(T)); return isDefault; } public static bool IsNull(this KeyValuePair pair) { return pair.Equals(new KeyValuePair()); } public static float Angle(this float2 from, float2 to) { var num = MathF.Sqrt(LengthSquared(from) * LengthSquared(to)); return num < 1.00000000362749E-15 ? 0.0f : MathF.Acos(Clamp(Dot(from, to) / num, -1f, 1f)) * 57.29578f; } private static float Clamp(float value, float min, float max) { return value < min ? min : value > max ? max : value; } private static float Dot(float2 a, float2 b) { return a.x * b.x + a.y * b.y; } private static float LengthSquared(float2 value) { return Dot(value, value); } } // https://github.com/Burtsev-Alexey/net-object-deep-copy public static class ObjectExtensions { private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); public static bool IsPrimitive(this Type type) { if (type == typeof(String)) return true; return (type.IsValueType & type.IsPrimitive); } public static Object Copy(this Object originalObject) { return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer())); } private static Object InternalCopy(Object originalObject, IDictionary visited) { if (originalObject == null) return null; var typeToReflect = originalObject.GetType(); if (IsPrimitive(typeToReflect)) return originalObject; if (visited.ContainsKey(originalObject)) return visited[originalObject]; if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null; var cloneObject = CloneMethod.Invoke(originalObject, null); if (typeToReflect.IsArray) { var arrayType = typeToReflect.GetElementType(); if (IsPrimitive(arrayType) == false) { Array clonedArray = (Array)cloneObject; clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices)); } } visited.Add(originalObject, cloneObject); CopyFields(originalObject, visited, cloneObject, typeToReflect); RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect); return cloneObject; } private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect) { if (typeToReflect.BaseType != null) { RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType); CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate); } } private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) { foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags)) { if (filter != null && filter(fieldInfo) == false) continue; if (IsPrimitive(fieldInfo.FieldType)) continue; var originalFieldValue = fieldInfo.GetValue(originalObject); var clonedFieldValue = InternalCopy(originalFieldValue, visited); fieldInfo.SetValue(cloneObject, clonedFieldValue); } } public static T Copy(this T original) { return (T)Copy((Object)original); } } public class ReferenceEqualityComparer : EqualityComparer { public override bool Equals(object x, object y) { return ReferenceEquals(x, y); } public override int GetHashCode(object obj) { if (obj == null) return 0; return obj.GetHashCode(); } } public static class ArrayExtensions { public static void ForEach(this Array array, Action action) { if (array.LongLength == 0) return; ArrayTraverse walker = new ArrayTraverse(array); do action(array, walker.Position); while (walker.Step()); } } internal class ArrayTraverse { public int[] Position; private int[] maxLengths; public ArrayTraverse(Array array) { maxLengths = new int[array.Rank]; for (int i = 0; i < array.Rank; ++i) { maxLengths[i] = array.GetLength(i) - 1; } Position = new int[array.Rank]; } public bool Step() { for (int i = 0; i < Position.Length; ++i) { if (Position[i] < maxLengths[i]) { Position[i]++; for (int j = 0; j < i; j++) { Position[j] = 0; } return true; } } return false; } }