/// <summary>
        /// Returns a copy of the given <see cref="IEnumerable{T}"/> that is shuffled using the Fisher-Yates algorithm.
        /// </summary>
        /// <typeparam name="T">The type of the items in the <see cref="IEnumerable{T}"/>.</typeparam>
        /// <param name="source">The <see cref="IEnumerable{T}"/> to copy.</param>
        /// <param name="rand">The <see cref="Random"/> to use for number generation. Uses <see cref="TSRandom"/> if <see langword="null"/>.</param>
        public static IEnumerable <T> Shuffle <T>(this IEnumerable <T> source, Random rand = null)
        {
            GenericExtensions.ThrowIfNull(source, nameof(source));

            rand ??= TSRandom.Instance; // Thread-safe random
            return(source.ShuffleIterator(rand));
        }
        /// <summary>
        /// Returns the given array with the specified object added to the end.
        /// </summary>
        /// <typeparam name="T">The type of objects contained within the array.</typeparam>
        /// <param name="array">The array to add to.</param>
        /// <param name="item">The object to be added to the end of the array. The value can be null for reference types.</param>
        /// <returns></returns>
        public static T[] Add <T>(this T[] array, T item)
        {
            GenericExtensions.ThrowIfNull(array, nameof(array));

            int oldLen = array.Length;

            Array.Resize(ref array, oldLen + 1);
            array[oldLen] = item;

            return(array);
        }
        /// <summary>
        /// Returns a new, combined array from the given arrays.
        /// </summary>
        /// <typeparam name="T">The type of the array.</typeparam>
        /// <param name="array">The first array to concatenate.</param>
        /// <param name="arrays">The additional arrays to concatenate.</param>
        /// <returns></returns>
        public static T[] Concat <T>(this T[] array, params T[][] arrays)
        {
            GenericExtensions.ThrowIfNull(array, nameof(array));
            GenericExtensions.ThrowIfNull(arrays, nameof(arrays));

            int offset = array.Length;

            Array.Resize(ref array, array.Length + arrays.Sum(a => a.Length));

            foreach (T[] arr in arrays)
            {
                int len = arr.Length;
                Array.Copy(arr, 0, array, offset, len);
                offset += len;
            }

            return(array);
        }
        /// <summary>
        /// Returns the given array with the first occurence of the specified object removed.
        /// </summary>
        /// <typeparam name="T">The type of items contained within the array.</typeparam>
        /// <param name="array">The array to remove from.</param>
        /// <param name="item">The object to remove from the array. The value can be null for reference types.</param>
        /// <returns></returns>
        public static T[] Remove <T>(this T[] array, T item)
        {
            GenericExtensions.ThrowIfNull(array, nameof(array));

            int removeIdx = Array.IndexOf(array, item);

            if (removeIdx > array.GetLowerBound(0) - 1)
            {
                T[] newArray = new T[array.Length - 1];

                Array.Copy(array, 0, newArray, 0, removeIdx + 1);
                Array.Copy(array, removeIdx + 1, newArray, removeIdx + 1, array.Length - removeIdx);

                return(newArray);
            }

            return(array);
        }