|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.ML.Runtime;
namespace Microsoft.ML.Internal.Utilities
{
[BestFriend]
internal static partial class Utils
{
public const int ArrayMaxSize = ArrayUtils.ArrayMaxSize;
public static bool StartsWithInvariantCultureIgnoreCase(this string str, string startsWith)
{
return str.StartsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
}
public static bool StartsWithInvariantCulture(this string str, string startsWith)
{
return str.StartsWith(startsWith, StringComparison.InvariantCulture);
}
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
public static void Reverse<T>(T[] a, int iMin, int iLim)
{
while (iMin < --iLim)
Swap(ref a[iMin++], ref a[iLim]);
}
// Getting the size of a collection, when the collection may be null.
public static int Size(string x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Length;
}
public static int Size(StringBuilder x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Length;
}
public static int Size(Array x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Length;
}
public static int Size<T>(T[] x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Length;
}
public static int Size<T>(List<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<T>(IList<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<T>(IReadOnlyList<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<T>(Stack<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<T>(HashSet<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<T>(SortedSet<T> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size<TKey, TValue>(Dictionary<TKey, TValue> x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Count;
}
public static int Size(BitArray x)
{
Contracts.AssertValueOrNull(x);
return x == null ? 0 : x.Length;
}
// Getting items from a collection when the collection may be null.
public static bool TryGetValue<TKey, TValue>(Dictionary<TKey, TValue> map, TKey key, out TValue value)
{
Contracts.AssertValueOrNull(map);
if (map == null)
{
value = default(TValue);
return false;
}
return map.TryGetValue(key, out value);
}
public static T[] ToArray<T>(List<T> list)
{
Contracts.AssertValueOrNull(list);
return list == null ? null : list.ToArray();
}
// Adding items to a collection when the collection may be null.
public static void Add<T>(ref List<T> list, T item)
{
Contracts.AssertValueOrNull(list);
if (list == null)
list = new List<T>();
list.Add(item);
}
public static bool Add<T>(ref HashSet<T> set, T item)
{
Contracts.AssertValueOrNull(set);
if (set == null)
set = new HashSet<T>();
return set.Add(item);
}
public static void Add<TKey, TValue>(ref Dictionary<TKey, TValue> map, TKey key, TValue value)
{
Contracts.AssertValueOrNull(map);
if (map == null)
map = new Dictionary<TKey, TValue>();
map.Add(key, value);
}
public static void Set<TKey, TValue>(ref Dictionary<TKey, TValue> map, TKey key, TValue value)
{
Contracts.AssertValueOrNull(map);
if (map == null)
map = new Dictionary<TKey, TValue>();
map[key] = value;
}
public static void Push<T>(ref Stack<T> stack, T item)
{
Contracts.AssertValueOrNull(stack);
if (stack == null)
stack = new Stack<T>();
stack.Push(item);
}
/// <summary>
/// Copies the values from src to dst.
/// </summary>
/// <remarks>
/// This can be removed once we have the APIs from https://github.com/dotnet/corefx/issues/33006.
/// </remarks>
public static void CopyTo<T>(this List<T> src, Span<T> dst, int? count = null)
{
Contracts.Assert(src != null);
Contracts.Assert(!count.HasValue || (0 <= count && count <= src.Count));
Contracts.Assert(src.Count <= dst.Length);
count = count ?? src.Count;
for (int i = 0; i < count; i++)
{
dst[i] = src[i];
}
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this IList<int> input, int value)
{
Contracts.AssertValue(input);
return FindIndexSorted(input, 0, input.Count, value);
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this IList<float> input, float value)
{
Contracts.AssertValue(input);
return FindIndexSorted(input, 0, input.Count, value);
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this Double[] input, Double value)
{
Contracts.AssertValue(input);
return FindIndexSorted(input, 0, input.Length, value);
}
/// <summary>
/// Akin to <c>FindIndexSorted</c>, except stores the found index in the output
/// <c>index</c> parameter, and returns whether that index is a valid index
/// pointing to a value equal to the input parameter <c>value</c>.
/// </summary>
public static bool TryFindIndexSorted(this int[] input, int min, int lim, int value, out int index)
{
return ArrayUtils.TryFindIndexSorted(input, min, lim, value, out index);
}
/// <summary>
/// Akin to <c>FindIndexSorted</c>, except stores the found index in the output
/// <c>index</c> parameter, and returns whether that index is a valid index
/// pointing to a value equal to the input parameter <c>value</c>.
/// </summary>
public static bool TryFindIndexSorted(ReadOnlySpan<int> input, int min, int lim, int value, out int index)
{
index = FindIndexSorted(input, min, lim, value);
return index < lim && input[index] == value;
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this int[] input, int min, int lim, int value)
{
return FindIndexSorted(input.AsSpan(), min, lim, value);
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this ReadOnlySpan<int> input, int min, int lim, int value)
{
return ArrayUtils.FindIndexSorted(input, min, lim, value);
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this IList<int> input, int min, int lim, int value)
{
Contracts.AssertValue(input);
Contracts.Assert(0 <= min && min <= lim && lim <= input.Count);
int minCur = min;
int limCur = lim;
while (minCur < limCur)
{
int mid = (int)(((uint)minCur + (uint)limCur) / 2);
Contracts.Assert(minCur <= mid && mid < limCur);
if (input[mid] >= value)
limCur = mid;
else
minCur = mid + 1;
Contracts.Assert(min <= minCur && minCur <= limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
}
Contracts.Assert(min <= minCur && minCur == limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
return minCur;
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this IList<float> input, int min, int lim, float value)
{
Contracts.AssertValue(input);
Contracts.Assert(0 <= min && min <= lim && lim <= input.Count);
Contracts.Assert(!float.IsNaN(value));
int minCur = min;
int limCur = lim;
while (minCur < limCur)
{
int mid = (int)(((uint)minCur + (uint)limCur) / 2);
Contracts.Assert(minCur <= mid && mid < limCur);
Contracts.Assert(!float.IsNaN(input[mid]));
if (input[mid] >= value)
limCur = mid;
else
minCur = mid + 1;
Contracts.Assert(min <= minCur && minCur <= limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
}
Contracts.Assert(min <= minCur && minCur == limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
return minCur;
}
/// <summary>
/// Assumes input is sorted and finds value using BinarySearch.
/// If value is not found, returns the logical index of 'value' in the sorted list i.e index of the first element greater than value.
/// In case of duplicates it returns the index of the first one.
/// It guarantees that items before the returned index are < value, while those at and after the returned index are >= value.
/// </summary>
public static int FindIndexSorted(this Double[] input, int min, int lim, Double value)
{
Contracts.AssertValue(input);
Contracts.Assert(0 <= min && min <= lim && lim <= input.Length);
Contracts.Assert(!Double.IsNaN(value));
int minCur = min;
int limCur = lim;
while (minCur < limCur)
{
int mid = (int)(((uint)minCur + (uint)limCur) / 2);
Contracts.Assert(minCur <= mid && mid < limCur);
Contracts.Assert(!Double.IsNaN(input[mid]));
if (input[mid] >= value)
limCur = mid;
else
minCur = mid + 1;
Contracts.Assert(min <= minCur && minCur <= limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
}
Contracts.Assert(min <= minCur && minCur == limCur && limCur <= lim);
Contracts.Assert(minCur == min || input[minCur - 1] < value);
Contracts.Assert(limCur == lim || input[limCur] >= value);
return minCur;
}
/// <summary>
/// Finds the unique index for which func(input[i]) == false whenever i < index and
/// func(input[i]) == true whenever i >= index.
/// Callers should guarantee that there is such an index. Uses binary search.
/// </summary>
public static int FindIndexSorted<T>(this T[] input, int min, int lim, Func<T, bool> func)
{
Contracts.AssertValue(input);
Contracts.Assert(0 <= min && min <= lim && lim <= input.Length);
int minCur = min;
int limCur = lim;
while (minCur < limCur)
{
int mid = (int)(((uint)minCur + (uint)limCur) / 2);
Contracts.Assert(minCur <= mid && mid < limCur);
if (func(input[mid]))
limCur = mid;
else
minCur = mid + 1;
Contracts.Assert(min <= minCur && minCur <= limCur && limCur <= lim);
Contracts.Assert(minCur == min || !func(input[minCur - 1]));
Contracts.Assert(limCur == lim || func(input[limCur]));
}
Contracts.Assert(min <= minCur && minCur == limCur && limCur <= lim);
Contracts.Assert(minCur == min || !func(input[minCur - 1]));
Contracts.Assert(limCur == lim || func(input[limCur]));
return minCur;
}
/// <summary>
/// Finds the unique index for which func(input[i], value) == false whenever i < index and
/// func(input[i], value) == true whenever i >= index.
/// Callers should guarantee that there is such an index. Uses binary search.
/// </summary>
public static int FindIndexSorted<T, TValue>(this T[] input, int min, int lim, Func<T, TValue, bool> func, TValue value)
{
Contracts.AssertValue(input);
Contracts.Assert(0 <= min && min <= lim && lim <= input.Length);
int minCur = min;
int limCur = lim;
while (minCur < limCur)
{
int mid = (int)(((uint)minCur + (uint)limCur) / 2);
Contracts.Assert(minCur <= mid && mid < limCur);
if (func(input[mid], value))
limCur = mid;
else
minCur = mid + 1;
Contracts.Assert(min <= minCur && minCur <= limCur && limCur <= lim);
Contracts.Assert(minCur == min || !func(input[minCur - 1], value));
Contracts.Assert(limCur == lim || func(input[limCur], value));
}
Contracts.Assert(min <= minCur && minCur == limCur && limCur <= lim);
Contracts.Assert(minCur == min || !func(input[minCur - 1], value));
Contracts.Assert(limCur == lim || func(input[limCur], value));
return minCur;
}
public static int[] GetIdentityPermutation(int size)
{
Contracts.Assert(size >= 0);
var res = new int[size];
for (int i = 0; i < size; i++)
res[i] = i;
return res;
}
public static void FillIdentity(Span<int> a, int lim)
{
Contracts.Assert(0 <= lim && lim <= a.Length);
for (int i = 0; i < lim; ++i)
a[i] = i;
}
// REVIEW: Maybe remove this in future?
public static void InterlockedAdd(ref double target, double v)
{
double snapshotOfTargetBefore;
double snapshotOfTargetDuring = target;
double targetPlusV;
do
{
snapshotOfTargetBefore = snapshotOfTargetDuring;
targetPlusV = snapshotOfTargetBefore + v;
snapshotOfTargetDuring = Interlocked.CompareExchange(ref target, targetPlusV, snapshotOfTargetBefore);
} while (snapshotOfTargetDuring != snapshotOfTargetBefore);
}
public static int[] InvertPermutation(int[] perm)
{
Contracts.AssertValue(perm);
var res = new int[perm.Length];
for (int i = 0; i < perm.Length; i++)
{
int j = perm[i];
Contracts.Assert(0 <= j && j < perm.Length);
Contracts.Assert(res[j] == 0 && (j != perm[0] || i == 0));
res[j] = i;
}
return res;
}
public static int[] GetRandomPermutation(Random rand, int size)
{
Contracts.AssertValue(rand);
Contracts.Assert(size >= 0);
var res = GetIdentityPermutation(size);
Shuffle<int>(rand, res);
return res;
}
public static bool AreEqual(float[] arr1, float[] arr2)
{
if (arr1 == arr2)
return true;
if (arr1 == null || arr2 == null)
return false;
if (arr1.Length != arr2.Length)
return false;
for (int i = 0; i < arr1.Length; i++)
{
if (arr1[i] != arr2[i])
return false;
}
return true;
}
public static bool AreEqual(double[] arr1, double[] arr2)
{
if (arr1 == arr2)
return true;
if (arr1 == null || arr2 == null)
return false;
if (arr1.Length != arr2.Length)
return false;
for (int i = 0; i < arr1.Length; i++)
{
if (arr1[i] != arr2[i])
return false;
}
return true;
}
public static void Shuffle<T>(Random rand, Span<T> rgv)
{
Contracts.AssertValue(rand);
for (int iv = 0; iv < rgv.Length; iv++)
Swap(ref rgv[iv], ref rgv[iv + rand.Next(rgv.Length - iv)]);
}
public static bool AreEqual(int[] arr1, int[] arr2)
{
if (arr1 == arr2)
return true;
if (arr1 == null || arr2 == null)
return false;
if (arr1.Length != arr2.Length)
return false;
for (int i = 0; i < arr1.Length; i++)
{
if (arr1[i] != arr2[i])
return false;
}
return true;
}
public static bool AreEqual(bool[] arr1, bool[] arr2)
{
if (arr1 == arr2)
return true;
if (arr1 == null || arr2 == null)
return false;
if (arr1.Length != arr2.Length)
return false;
for (int i = 0; i < arr1.Length; i++)
{
if (arr1[i] != arr2[i])
return false;
}
return true;
}
public static string ExtractLettersAndNumbers(string value)
{
return Regex.Replace(value, "[^A-Za-z0-9]", "");
}
/// <summary>
/// Checks that an input IList is monotonically increasing.
/// </summary>
/// <param name="values">An array of values</param>
/// <returns>True if the array is monotonically increasing (if each element is greater
/// than or equal to previous elements); false otherwise. ILists containing NaN values
/// are considered to be not monotonically increasing.</returns>
public static bool IsMonotonicallyIncreasing(IList<float> values)
{
if (Utils.Size(values) <= 1)
return true;
var previousValue = values[0];
var listLength = values.Count;
for (int i = 1; i < listLength; i++)
{
var currentValue = values[i];
// Inverted check for NaNs
if (!(currentValue >= previousValue))
return false;
previousValue = currentValue;
}
return true;
}
/// <summary>
/// Checks that an input array is monotonically increasing.
/// </summary>
/// <param name="values">An array of values</param>
/// <returns>True if the array is monotonically increasing (if each element is greater
/// than or equal to previous elements); false otherwise.</returns>
public static bool IsMonotonicallyIncreasing(IList<int> values)
{
if (Utils.Size(values) <= 1)
return true;
var previousValue = values[0];
var listLength = values.Count;
for (int i = 1; i < listLength; i++)
{
var currentValue = values[i];
if (currentValue < previousValue)
return false;
previousValue = currentValue;
}
return true;
}
/// <summary>
/// Checks that an input array is monotonically increasing.
/// </summary>
/// <param name="values">An array of values</param>
/// <returns>True if the array is monotonically increasing (if each element is greater
/// than or equal to previous elements); false otherwise. Arrays containing NaN values
/// are considered to be not monotonically increasing.</returns>
public static bool IsMonotonicallyIncreasing(IList<double> values)
{
if (Utils.Size(values) <= 1)
return true;
var previousValue = values[0];
var listLength = values.Count;
for (int i = 1; i < listLength; i++)
{
var currentValue = values[i];
// Inverted check for NaNs
if (!(currentValue >= previousValue))
return false;
previousValue = currentValue;
}
return true;
}
/// <summary>
/// Returns whether an input integer vector is sorted and unique,
/// and between an inclusive lower and exclusive upper bound for
/// the first and last items, respectively.
/// </summary>
public static bool IsIncreasing(int min, ReadOnlySpan<int> values, int lim)
{
if (values.Length < 1)
return true;
var prev = values[0];
if (prev < min)
return false;
for (int i = 1; i < values.Length; i++)
{
if (values[i] <= prev)
return false;
prev = values[i];
}
return prev < lim;
}
/// <summary>
/// Returns whether an input integer vector up to <paramref name="len"/>
/// is sorted and unique, and between an inclusive lower and exclusive
/// upper bound for the first and last items, respectively.
/// </summary>
public static bool IsIncreasing(int min, ReadOnlySpan<int> values, int len, int lim)
{
Contracts.Check(values.Length >= len);
if (len < 1)
return true;
var prev = values[0];
if (prev < min)
return false;
for (int i = 1; i < len; i++)
{
if (values[i] <= prev)
return false;
prev = values[i];
}
return prev < lim;
}
/// <summary>
/// Create an array of specified length, filled with a specified value
/// </summary>
public static T[] CreateArray<T>(int length, T value)
{
Contracts.Assert(length >= 0, "Length can't be negative");
var result = new T[length];
for (int i = 0; i < length; i++)
result[i] = value;
return result;
}
public static bool[] BuildArray(int length, IEnumerable<DataViewSchema.Column> columnsNeeded)
{
Contracts.CheckParam(length >= 0, nameof(length));
var result = new bool[length];
foreach (var col in columnsNeeded)
{
if (col.Index < result.Length)
result[col.Index] = true;
}
return result;
}
public static T[] BuildArray<T>(int length, Func<int, T> func)
{
Contracts.CheckParam(length >= 0, nameof(length));
Contracts.CheckValue(func, nameof(func));
var result = new T[length];
for (int i = 0; i < result.Length; i++)
result[i] = func(i);
return result;
}
/// <summary>
/// Given a predicate, over a range of values defined by a limit calculate
/// first the values for which that predicate was true, and second an inverse
/// map.
/// </summary>
/// <param name="schema">The input schema where the predicate can check if columns are active.</param>
/// <param name="pred">The predicate to test for various value</param>
/// <param name="map">An ascending array of values from 0 inclusive
/// to <paramref name="schema.Count"/> exclusive, holding all values for which
/// <paramref name="pred"/> is true</param>
/// <param name="invMap">Forms an inverse mapping of <paramref name="map"/>,
/// so that <c><paramref name="invMap"/>[<paramref name="map"/>[i]] == i</c>,
/// and for other entries not appearing in <paramref name="map"/>,
/// <c><paramref name="invMap"/>[i] == -1</c></param>
public static void BuildSubsetMaps(DataViewSchema schema, Func<DataViewSchema.Column, bool> pred, out int[] map, out int[] invMap)
{
Contracts.CheckValue(schema, nameof(schema));
Contracts.Check(schema.Count > 0, nameof(schema));
Contracts.CheckValue(pred, nameof(pred));
// REVIEW: Better names?
List<int> mapList = new List<int>();
invMap = new int[schema.Count];
for (int c = 0; c < schema.Count; ++c)
{
if (!pred(schema[c]))
{
invMap[c] = -1;
continue;
}
invMap[c] = mapList.Count;
mapList.Add(c);
}
map = mapList.ToArray();
}
/// <summary>
/// Given a predicate, over a range of values defined by a limit calculate
/// first the values for which that predicate was true, and second an inverse
/// map.
/// </summary>
/// <param name="lim">Indicates the exclusive upper bound on the tested values</param>
/// <param name="pred">The predicate to test for various value</param>
/// <param name="map">An ascending array of values from 0 inclusive
/// to <paramref name="lim"/> exclusive, holding all values for which
/// <paramref name="pred"/> is true</param>
/// <param name="invMap">Forms an inverse mapping of <paramref name="map"/>,
/// so that <c><paramref name="invMap"/>[<paramref name="map"/>[i]] == i</c>,
/// and for other entries not appearing in <paramref name="map"/>,
/// <c><paramref name="invMap"/>[i] == -1</c></param>
public static void BuildSubsetMaps(int lim, Func<int, bool> pred, out int[] map, out int[] invMap)
{
Contracts.CheckParam(lim >= 0, nameof(lim));
Contracts.CheckValue(pred, nameof(pred));
// REVIEW: Better names?
List<int> mapList = new List<int>();
invMap = new int[lim];
for (int c = 0; c < lim; ++c)
{
if (!pred(c))
{
invMap[c] = -1;
continue;
}
invMap[c] = mapList.Count;
mapList.Add(c);
}
map = mapList.ToArray();
}
/// <summary>
/// Given the columns needed, over a range of values defined by a limit calculate
/// first the values for which the column is present was true, and second an inverse
/// map.
/// </summary>
/// <param name="lim">Indicates the exclusive upper bound on the tested values</param>
/// <param name="columnsNeeded">The set of columns the calling component operates on.</param>
/// <param name="map">An ascending array of values from 0 inclusive
/// to <paramref name="lim"/> exclusive, holding all values for which
/// <paramref name="columnsNeeded"/> are present.
/// (The respective index appears in the <paramref name="columnsNeeded"/> collection).</param>
/// <param name="invMap">Forms an inverse mapping of <paramref name="map"/>,
/// so that <c><paramref name="invMap"/>[<paramref name="map"/>[i]] == i</c>,
/// and for other entries not appearing in <paramref name="map"/>,
/// <c><paramref name="invMap"/>[i] == -1</c></param>
public static void BuildSubsetMaps(int lim, IEnumerable<DataViewSchema.Column> columnsNeeded, out int[] map, out int[] invMap)
{
Contracts.CheckParam(lim >= 0, nameof(lim));
Contracts.CheckValue(columnsNeeded, nameof(columnsNeeded));
// REVIEW: Better names?
List<int> mapList = new List<int>();
invMap = Enumerable.Repeat(-1, lim).ToArray<int>();
foreach (var col in columnsNeeded)
{
Contracts.Check(col.Index < lim);
invMap[col.Index] = mapList.Count;
mapList.Add(col.Index);
}
map = mapList.ToArray();
}
public static T[] Concat<T>(T[] a, T[] b)
{
if (a == null)
return b;
if (b == null)
return a;
if (a.Length == 0)
return b;
if (b.Length == 0)
return a;
var res = new T[a.Length + b.Length];
Array.Copy(a, res, a.Length);
Array.Copy(b, 0, res, a.Length, b.Length);
return res;
}
public static T[] Concat<T>(params T[][] arrays)
{
// Total size.
int size = 0;
// First non-null.
T[] nn = null;
// First non-empty.
T[] ne = null;
foreach (var a in arrays)
{
if (a == null)
continue;
checked { size += a.Length; }
if (nn == null)
nn = a;
if (ne == null && size > 0)
ne = a;
}
Contracts.Assert(nn != null || size == 0);
Contracts.Assert((ne == null) == (size == 0));
// If the size is zero, return the first non-null.
if (size == 0)
return nn;
// If there is only one non-empty, return it.
if (size == ne.Length)
return ne;
var res = new T[size];
int ivDst = 0;
foreach (var a in arrays)
{
int cv = Utils.Size(a);
if (cv == 0)
continue;
Array.Copy(a, 0, res, ivDst, cv);
ivDst += cv;
}
Contracts.Assert(ivDst == size);
return res;
}
/// <summary>
/// Resizes the array if necessary, to ensure that it has at least <paramref name="min"/> elements.
/// </summary>
/// <param name="array">The array to resize. Can be null.</param>
/// <param name="min">The minimum number of items the new array must have.</param>
/// <param name="keepOld">True means that the old array is preserved, if possible (Array.Resize is called). False
/// means that a new array will be allocated.
/// </param>
/// <returns>The new size, that is no less than <paramref name="min"/>.</returns>
public static int EnsureSize<T>(ref T[] array, int min, bool keepOld = true)
{
return EnsureSize(ref array, min, Utils.ArrayMaxSize, keepOld);
}
/// <summary>
/// Resizes the array if necessary, to ensure that it has at least <paramref name="min"/> and at most <paramref name="max"/> elements.
/// </summary>
/// <param name="array">The array to resize. Can be null.</param>
/// <param name="min">The minimum number of items the new array must have.</param>
/// <param name="max">The maximum number of items the new array can have.</param>
/// <param name="keepOld">True means that the old array is preserved, if possible (Array.Resize is called). False
/// means that a new array will be allocated.
/// </param>
/// <returns>The new size, that is no less than <paramref name="min"/> and no more that <paramref name="max"/>.</returns>
public static int EnsureSize<T>(ref T[] array, int min, int max, bool keepOld = true)
=> EnsureSize(ref array, min, max, keepOld, out bool _);
public static int EnsureSize<T>(ref T[] array, int min, int max, bool keepOld, out bool resized)
{
return ArrayUtils.EnsureSize(ref array, min, max, keepOld, out resized);
}
/// <summary>
/// Returns the number of set bits in a bit array.
/// </summary>
public static int GetCardinality(BitArray bitArray)
{
Contracts.CheckValue(bitArray, nameof(bitArray));
int cnt = 0;
foreach (bool b in bitArray)
{
if (b)
cnt++;
}
return cnt;
}
private static MethodInfo MarshalInvokeCheckAndCreate<TRet>(Type genArg, Delegate func)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, func);
if (meth.ReturnType != typeof(TRet))
throw Contracts.ExceptParam(nameof(func), "Cannot be generic on return type");
return meth;
}
// REVIEW: n-argument versions? The multi-column re-application problem?
// Think about how to address these.
/// <summary>
/// Given a generic method with a single type parameter, re-create the generic method on a new type,
/// then reinvoke the method and return the result. A common pattern throughout the code base is to
/// have some sort of generic method, whose parameters and return value are, as defined, non-generic,
/// but whose code depends on some sort of generic type parameter. This utility method exists to make
/// this common pattern more convenient, and also safer so that the arguments, if any, can be type
/// checked at compile time instead of at runtime.
///
/// Because it is strongly typed, this can only be applied to methods whose return type
/// is known at compile time, that is, that do not depend on the type parameter of the method itself.
/// </summary>
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
/// <param name="func">A delegate that should be a generic method with a single type parameter.
/// The generic method definition will be extracted, then a new method will be created with the
/// given type parameter, then the method will be invoked.</param>
/// <param name="target">The target of the invocation.</param>
/// <param name="genArg">The new type parameter for the generic method</param>
/// <returns>The return value of the invoked function</returns>
public static TResult MarshalInvoke<TTarget, TResult>(FuncInstanceMethodInfo1<TTarget, TResult> func, TTarget target, Type genArg)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(target, null);
}
/// <summary>
/// A static version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TResult>(FuncStaticMethodInfo1<TResult> func, Type genArg)
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(null, null);
}
/// <summary>
/// A one-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TTarget, TArg1, TResult>(FuncInstanceMethodInfo1<TTarget, TArg1, TResult> func, TTarget target, Type genArg, TArg1 arg1)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(target, new object[] { arg1 });
}
/// <summary>
/// A one-argument version of <see cref="MarshalInvoke{TResult}(FuncStaticMethodInfo1{TResult}, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TArg1, TResult>(FuncStaticMethodInfo1<TArg1, TResult> func, Type genArg, TArg1 arg1)
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(null, new object[] { arg1 });
}
/// <summary>
/// A one-argument, three-type-parameter version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TTarget, TArg1, TResult>(FuncInstanceMethodInfo3<TTarget, TArg1, TResult> func, TTarget target, Type genArg1, Type genArg2, Type genArg3, TArg1 arg1)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg1, genArg2, genArg3);
return (TResult)meth.Invoke(target, new object[] { arg1 });
}
/// <summary>
/// A one-argument, three-type-parameter version of <see cref="MarshalInvoke{TResult}(FuncStaticMethodInfo1{TResult}, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TArg1, TResult>(FuncStaticMethodInfo3<TArg1, TResult> func, Type genArg1, Type genArg2, Type genArg3, TArg1 arg1)
{
var meth = func.MakeGenericMethod(genArg1, genArg2, genArg3);
return (TResult)meth.Invoke(null, new object[] { arg1 });
}
/// <summary>
/// A two-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TTarget, TArg1, TArg2, TResult>(FuncInstanceMethodInfo1<TTarget, TArg1, TArg2, TResult> func, TTarget target, Type genArg, TArg1 arg1, TArg2 arg2)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(target, new object[] { arg1, arg2 });
}
/// <summary>
/// A two-argument version of <see cref="MarshalInvoke{TResult}(FuncStaticMethodInfo1{TResult}, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TArg1, TArg2, TResult>(FuncStaticMethodInfo1<TArg1, TArg2, TResult> func, Type genArg, TArg1 arg1, TArg2 arg2)
{
var meth = func.MakeGenericMethod(genArg);
return (TResult)meth.Invoke(null, new object[] { arg1, arg2 });
}
/// <summary>
/// A two-argument, two-type-parameter version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TTarget, TArg1, TArg2, TResult>(FuncInstanceMethodInfo2<TTarget, TArg1, TArg2, TResult> func, TTarget target, Type genArg1, Type genArg2, TArg1 arg1, TArg2 arg2)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg1, genArg2);
return (TResult)meth.Invoke(target, new object[] { arg1, arg2 });
}
/// <summary>
/// A two-argument, two-type-parameter version of <see cref="MarshalInvoke{TResult}(FuncStaticMethodInfo1{TResult}, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TArg1, TArg2, TResult>(FuncStaticMethodInfo2<TArg1, TArg2, TResult> func, Type genArg1, Type genArg2, TArg1 arg1, TArg2 arg2)
{
var meth = func.MakeGenericMethod(genArg1, genArg2);
return (TResult)meth.Invoke(null, new object[] { arg1, arg2 });
}
/// <summary>
/// A two-argument, three-type-parameter version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TTarget, TArg1, TArg2, TResult>(FuncInstanceMethodInfo3<TTarget, TArg1, TArg2, TResult> func, TTarget target, Type genArg1, Type genArg2, Type genArg3, TArg1 arg1, TArg2 arg2)
where TTarget : class
{
var meth = func.MakeGenericMethod(genArg1, genArg2, genArg3);
return (TResult)meth.Invoke(target, new object[] { arg1, arg2 });
}
/// <summary>
/// A two-argument, three-type-parameter version of <see cref="MarshalInvoke{TResult}(FuncStaticMethodInfo1{TResult}, Type)"/>.
/// </summary>
public static TResult MarshalInvoke<TArg1, TArg2, TResult>(FuncStaticMethodInfo3<TArg1, TArg2, TResult> func, Type genArg1, Type genArg2, Type genArg3, TArg1 arg1, TArg2 arg2)
{
var meth = func.MakeGenericMethod(genArg1, genArg2, genArg3);
return (TResult)meth.Invoke(null, new object[] { arg1, arg2 });
}
/// <summary>
/// A three-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TRet>(Func<TArg1, TArg2, TArg3, TRet> func, Type genArg,
TArg1 arg1, TArg2 arg2, TArg3 arg3)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3 });
}
/// <summary>
/// A four-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4 });
}
/// <summary>
/// A five-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5 });
}
/// <summary>
/// A six-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 });
}
/// <summary>
/// A seven-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 });
}
/// <summary>
/// An eight-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TRet>(Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 });
}
/// <summary>
/// A nine-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TRet>(
Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8, TArg9 arg9)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 });
}
/// <summary>
/// A ten-argument version of <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>.
/// </summary>
public static TRet MarshalInvoke<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TRet>(
Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TRet> func,
Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, TArg8 arg8, TArg9 arg9, TArg10 arg10)
{
var meth = MarshalInvokeCheckAndCreate<TRet>(genArg, func);
return (TRet)meth.Invoke(func.Target, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 });
}
private static MethodInfo MarshalActionInvokeCheckAndCreate(Type genArg, Delegate func)
{
Contracts.CheckValue(genArg, nameof(genArg));
Contracts.CheckValue(func, nameof(func));
var meth = func.GetMethodInfo();
Contracts.CheckParam(meth.IsGenericMethod, nameof(func), "Should be generic but is not");
Contracts.CheckParam(meth.GetGenericArguments().Length == 1, nameof(func),
"Should have exactly one generic type parameter but does not");
meth = meth.GetGenericMethodDefinition().MakeGenericMethod(genArg);
return meth;
}
/// <summary>
/// This is akin to <see cref="MarshalInvoke{TTarget, TResult}(FuncInstanceMethodInfo1{TTarget, TResult}, TTarget, Type)"/>, except applied to
/// <see cref="Action"/> instead of <see cref="Func{TRet}"/>.
/// </summary>
/// <param name="act">A delegate that should be a generic method with a single type parameter.
/// The generic method definition will be extracted, then a new method will be created with the
/// given type parameter, then the method will be invoked.</param>
/// <param name="genArg">The new type parameter for the generic method</param>
public static void MarshalActionInvoke(Action act, Type genArg)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, act);
meth.Invoke(act.Target, null);
}
/// <summary>
/// A one-argument version of <see cref="MarshalActionInvoke(Action, Type)"/>.
/// </summary>
public static void MarshalActionInvoke<TArg1>(Action<TArg1> act, Type genArg, TArg1 arg1)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, act);
meth.Invoke(act.Target, new object[] { arg1 });
}
/// <summary>
/// A two-argument version of <see cref="MarshalActionInvoke(Action, Type)"/>.
/// </summary>
public static void MarshalActionInvoke<TArg1, TArg2>(Action<TArg1, TArg2> act, Type genArg, TArg1 arg1, TArg2 arg2)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, act);
meth.Invoke(act.Target, new object[] { arg1, arg2 });
}
/// <summary>
/// A three-argument version of <see cref="MarshalActionInvoke(Action, Type)"/>.
/// </summary>
public static void MarshalActionInvoke<TArg1, TArg2, TArg3>(Action<TArg1, TArg2, TArg3> act, Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, act);
meth.Invoke(act.Target, new object[] { arg1, arg2, arg3 });
}
/// <summary>
/// A four-argument version of <see cref="MarshalActionInvoke(Action, Type)"/>.
/// </summary>
public static void MarshalActionInvoke<TArg1, TArg2, TArg3, TArg4>(Action<TArg1, TArg2, TArg3, TArg4> act, Type genArg, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
{
var meth = MarshalActionInvokeCheckAndCreate(genArg, act);
meth.Invoke(act.Target, new object[] { arg1, arg2, arg3, arg4 });
}
public static string GetDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
public static int Count<TSource>(this ReadOnlySpan<TSource> source, Func<TSource, bool> predicate)
{
Contracts.CheckValue(predicate, nameof(predicate));
int result = 0;
for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
result++;
}
return result;
}
public static bool All<TSource>(this ReadOnlySpan<TSource> source, Func<TSource, bool> predicate)
{
Contracts.CheckValue(predicate, nameof(predicate));
for (int i = 0; i < source.Length; i++)
{
if (!predicate(source[i]))
return false;
}
return true;
}
}
}
|