wasSharp – Rev 51

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;

namespace wasSharp.Linq
{
    public static class Extensions
    {
        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     A functional implementation of a switch clause.
        /// </summary>
        /// <typeparam name="T">the type of items in the query</typeparam>
        /// <param name="query">the selector query</param>
        /// <param name="default">the function to execute when no case matches</param>
        /// <param name="case">a list of predicates representing the switch cases,
        /// where each predicate has to return True or False indicating whether
        /// fallthrough should occur.
        /// </param>
        /// <remarks>when used, the default action must be explicitly specified
        /// due to language restrictions
        /// </remarks>
        public static void Switch<T>(this IEnumerable<T> query,
            // default
            Action<T> @default,
            // case
            // case
            // ...
            params Predicate<T>[] @case)
        {
            if (@case.Length % 2 != 0)
                throw new ArgumentException("Pairs of predicates expected.");

            var enumerable = query as IList<T> ?? query.ToList();
            using (var iter = enumerable.GetEnumerator())
            {
                while (iter.MoveNext())
                {
                    var match = false;
                    for (var i = 0; i < @case.Length; i += 2)
                    {
                        if (!@case[i].Invoke(iter.Current))
                            continue;

                        if (@case[i + 1].Invoke(iter.Current))
                            return;

                        match = true;
                    }

                    if (!match)
                        @default.Invoke(iter.Current);
                }
            }
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     A functional implementation of a switch clause.
        /// </summary>
        /// <typeparam name="T">the type of the item to query</typeparam>
        /// <param name="query">the selector query</param>
        /// <param name="default">the function to execute when no case matches</param>
        /// <param name="case">a list of predicates representing the switch cases,
        /// where each predicate has to return True or False indicating whether
        /// fallthrough should occur.
        /// </param>
        /// <remarks>when used, the default action must be explicitly specified
        /// due to language restrictions
        /// </remarks>
        public static void Switch<T>(this T query,
            // default
            Action<T> @default,
            // case
            // case
            // ...
            params Predicate<T>[] @case)
        {
            if (@case.Length % 2 != 0)
                throw new ArgumentException("Pairs of predicates expected.");

            var match = false;
            for (var i = 0; i < @case.Length; i += 2)
            {
                if (!@case[i].Invoke(query))
                    continue;

                if (@case[i + 1].Invoke(query))
                    return;

                match = true;
            }

            if (!match)
                @default.Invoke(query);

        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pf in case the predicate p resolves or qf in case the predicate q resolves, or ef otherwise.
        /// </summary>
        /// <typeparam name="T">the type of items in the query</typeparam>
        /// <param name="query">the selector query</param>
        /// <param name="p">the condition for invoking pf</param>
        /// <param name="pf">the action to invoke in case p resolves</param>
        /// <param name="q">the condition for invoking qf</param>
        /// <param name="qf">the action to invoke in case q resolves</param>
        /// <param name="ef">the action to invoke otherwise</param>
        public static void ForAll<T>(this ParallelQuery<T> query, Predicate<T> p, Action<T> pf, Predicate<T> q, Action<T> qf, Action<T> ef)
        {
            query.ForAll(o =>
            {
                if (p.Invoke(o))
                {
                    pf.Invoke(o);
                }
                else if (q.Invoke(o))
                {
                    qf.Invoke(o);
                }
                else
                {
                    ef.Invoke(o);
                }
            });
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pf in case the predicate p resolves or qf in case the predicate q resolves.
        /// </summary>
        /// <typeparam name="T">the type of items in the query</typeparam>
        /// <param name="query">the selector query</param>
        /// <param name="p">the condition for invoking pf</param>
        /// <param name="pf">the action to invoke in case p resolves</param>
        /// <param name="q">the condition for invoking qf</param>
        /// <param name="qf">the action to invoke in case q resolves</param>
        public static void ForAll<T>(this ParallelQuery<T> query, Predicate<T> p, Action<T> pf, Predicate<T> q, Action<T> qf)
        {
            query.ForAll(o =>
            {
                if (p.Invoke(o))
                {
                    pf.Invoke(o);
                    return;
                }

                if (q.Invoke(o))
                {
                    qf.Invoke(o);
                    return;
                }
            });
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pass if and only if predicate resovles or fail otherwise.
        /// </summary>
        /// <typeparam name="T">the type of items in the query</typeparam>
        /// <param name="query">the selector query</param>
        /// <param name="condition">the condition for invoking pf</param>
        /// <param name="pass">the function to invoke in case the predicate resolves</param>
        /// <param name="fail">the function to invoke otherwise</param>
        public static void ForAll<T>(this ParallelQuery<T> query, Predicate<T> condition, Action<T> pass, Action<T> fail)
        {
            query.ForAll(o =>
            {
                switch (condition.Invoke(o))
                {
                    case true:
                        pass.Invoke(o);
                        return;

                    default:
                        fail.Invoke(o);
                        return;
                }
            });
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pass if and only if condition holds or fail otherwise.
        /// </summary>
        /// <typeparam name="T">the return type of the pass and fail functions</typeparam>
        /// <param name="condition">the branch condition</param>
        /// <param name="pass">function with no parameters and return type T in case condition passes</param>
        /// <param name="fail">function with no parameters and return type T in case condition fails</param>
        /// <returns>the result of pass in case condition holds or the result of fail otherwise</returns>
        public static T IfElse<T>(this bool condition, Func<T> pass, Func<T> fail)
        {
            return condition ? pass.Invoke() : fail.Invoke();
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pass if and only if condition holds or fail otherwise.
        /// </summary>
        /// <typeparam name="U">the type of the argument to pass and fail</typeparam>
        /// <typeparam name="V">the return type of pass and fail</typeparam>
        /// <param name="condition">the branch condition</param>
        /// <param name="pass">function that takes argument arg and returns type V in case condition holds</param>
        /// <param name="fail">function that takes argument arg and returns type V in case condition fails</param>
        /// <param name="arg">the argument passed to pass or fail functions</param>
        /// <returns>the result of pass in case condition holds or the result of fail otherwise</returns>
        public static V IfElse<U, V>(this bool condition, Func<U, V> pass, Func<U, V> fail, U arg = default(U))
        {
            return condition ? pass.Invoke(arg) : fail.Invoke(arg);
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pass if and only if condition holds or fail otherwise.
        /// </summary>
        /// <typeparam name="T">the type of the argument to pass and fail</typeparam>
        /// <param name="condition">the branch condition</param>
        /// <param name="pass">function that takes argument arg and returns nothing in case condition holds</param>
        /// <param name="fail">function that takes argument arg and returns nothing in case condition fails</param>
        /// <param name="arg">the optional argument passed to pass or fail functions</param>
        public static void IfElse<T>(this bool condition, Action<T> pass, Action<T> fail, T arg = default(T))
        {
            switch (condition)
            {
                case true:
                    pass.Invoke(arg);
                    return;

                default:
                    fail.Invoke(arg);
                    return;
            }
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Invokes pass if and only if condition holds or fail otherwise.
        /// </summary>
        /// <typeparam name="U">the type of the first argument to the pass or fail functions</typeparam>
        /// <typeparam name="V">the type of the second argument to the pass or fail functions</typeparam>
        /// <param name="condition">the branch condition</param>
        /// <param name="pass">function that takes argument arg and returns nothing in case condition holds</param>
        /// <param name="fail">function that takes argument arg and returns nothing in case condition fails</param>
        /// <param name="arga">first optional argument passed to pass or fail functions</param>
        /// <param name="argb">second optional argument passed to pass or fail functions</param>
        public static void IfElse<U, V>(this bool condition, Action<U, V> pass, Action<U, V> fail, U arga = default(U), V argb = default(V))
        {
            switch (condition)
            {
                case true:
                    pass.Invoke(arga, argb);
                    return;

                default:
                    fail.Invoke(arga, argb);
                    return;
            }
        }

        ///////////////////////////////////////////////////////////////////////////
        //    Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3    //
        ///////////////////////////////////////////////////////////////////////////
        /// <summary>
        ///     Returns true of an enumerable contains more than one element.
        /// </summary>
        /// <typeparam name="T">the type of the enumeration</typeparam>
        /// <param name="e">the enumeration</param>
        /// <returns>true if enumeration contains more than one element</returns>
        /// <remarks>O(2) worst case</remarks>
        public static bool Some<T>(this IEnumerable<T> e)
        {
            var i = 0;
            using (var iter = e.GetEnumerator())
            {
                while (iter.MoveNext())
                {
                    if (++i > 1)
                        return true;
                }
                return false;
            }
        }

        /// <summary>
        ///     Sequentially removes all the elements from the first sequence that are in the second sequence
        ///     or all the elements from the second sequence that are in the first sequence.
        /// </summary>
        /// <typeparam name="T">the type o the collection</typeparam>
        /// <param name="o">the first sequence to remove from</param>
        /// <param name="p">the second sequence to remove</param>
        /// <returns>the first sequence excluding the second sequence or the second sequence excluding the first sequence</returns>
        public static IEnumerable<T> SequenceExceptAny<T>(this IEnumerable<T> o, IEnumerable<T> p) where T : IEquatable<T>
        {
            var l = new List<T>(o);
            var r = new List<T>(p);
            return l.Count > r.Count
                ? l.Zip(r, (x, y) => x.Equals(y) ? default(T) : y)
                    .Concat(l.Skip(r.Count))
                    .Where(q => q != null && !q.Equals(default(T)))
                : r.Zip(l, (x, y) => x.Equals(y) ? default(T) : y)
                    .Concat(r.Skip(l.Count))
                    .Where(q => q != null && !q.Equals(default(T)));
        }

        /// <summary>
        ///     Sequentially removes all the elements from the first sequence that are in the second sequence.
        /// </summary>
        /// <typeparam name="T">the type o the collection</typeparam>
        /// <param name="o">the first sequence to remove from</param>
        /// <param name="p">the second sequence to remove</param>
        /// <returns>the first sequence excluding the second sequence</returns>
        public static IEnumerable<T> SequenceExcept<T>(this IEnumerable<T> a, IEnumerable<T> b) where T : IEquatable<T>
        {
            using (var ea = a.GetEnumerator())
            {
                using (var eb = b.GetEnumerator())
                {
                    while (ea.MoveNext())
                    {
                        if (eb.MoveNext() && ea.Current.Equals(eb.Current))
                            continue;
                        yield return ea.Current;
                    }
                }
            }
        }
    }
}