/// <summary>
        /// Groups element into sub-lists of adjacent that share the same property, true or false, from the predicate.
        /// Basically performs edge detection in order to operate.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="elements"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static IEnumerable <Tuple <IEnumerable <T>, bool> > ClusteredByPredicate <T>(this IEnumerable <T> elements, Func <T, bool> predicate)
        {
            // We'll build a list assigning numeric, so we can have true, false AND "added at the front" for edge detection purposes.
            var withTrueFalseAsOneZero = Alg.ReadPoint(elements.Select(x => Tuple.Create(x, predicate(x) ? 1 : 0))).TakenToEnd();

            return(Alg.Map(
                       (c, prior) =>
            {
                var cf = c.First();
                return Alg.When(
                    cf.Item2 != prior.Item2,
                    () => Tuple.Create(c.TakeWhile(x => x.Item2 == cf.Item2).Select(x => x.Item1), (cf.Item2 == 1)));
            },
                       withTrueFalseAsOneZero.Tails().Where(x => x.Any()), new[] { Tuple.Create(default(T), -1) }.Concat(withTrueFalseAsOneZero)).SelectMany(x => x.ToEnumerable()));
        }
        public static IEnumerable <IEnumerable <T> > Transpose <T>(this IEnumerable <IEnumerable <T> > original)
        {
            var orp = Alg.ReadPoint(original);

            if (!orp.AtEnd)
            {
                int numHorizontalInOriginal = orp.Value.Count();
                // We cache this
                var originalsAsArrays = Alg.ReadPoint(original.Select(row => { var r = row.ToArray(); if (r.Length != numHorizontalInOriginal)
                                                                               {
                                                                                   throw new Exception("Row size mismatch");
                                                                               }
                                                                               return(r); })).TakenToEnd();
                return(Enumerable.Range(0, numHorizontalInOriginal).Select(ordinal => originalsAsArrays.Select(row => row[ordinal])));
            }

            return(new IEnumerable <T>[] { });
        }
        public static IEnumerable <string> CharsToLines(IEnumerable <char> chars)
        {
            List <char>      accumulator = new List <char>();
            ReadPoint <char> currentChar = Alg.ReadPoint(chars);

            while (!currentChar.AtEnd)
            {
                char c = currentChar.Value;
                if (c == '\n')
                {
                    yield return(Alg.MergedChars(accumulator));

                    accumulator = new List <char>();
                }
                else
                if (c == '\r')
                {       // have to handle '\r'-only case.
                    if (!currentChar.Next.AtEnd)
                    {
                        if (currentChar.Next.Value != '\n')
                        {
                            yield return(Alg.MergedChars(accumulator));

                            accumulator = new List <char>();
                        }
                    }
                }
                else
                {
                    accumulator.Add(c);
                }
                currentChar = currentChar.Next;
            }

            if (accumulator.Count != 0)
            {
                yield return(Alg.MergedChars(accumulator));

                accumulator = new List <char>();
            }
        }
        public static IEnumerable <string> LineToCSFEntries(string line, char delimiter, bool quotesHaveMeaning)
        {
            ReadPoint <char> currentChar        = Alg.ReadPoint(line.ToCharArray());
            List <char>      accumulatedElement = new List <char>();
            bool             inQuote            = false;
            bool             haveSeenAComma     = false;

            while (!currentChar.AtEnd)
            {
                char c = currentChar.Value;
                if (inQuote)
                {
                    if (c == '\"')
                    {   // may be a double-quote
                        if (IsAtCSVQuote(currentChar))
                        {
                            accumulatedElement.Add('\"');
                            currentChar = currentChar.Next; // next add will be done at end of this loop.
                        }
                        else
                        {
                            accumulatedElement.Add(c);
                            inQuote = false;
                        }
                    }
                    else
                    {
                        accumulatedElement.Add(c);
                    }
                }
                else
                {
                    if (c == delimiter)
                    {
                        haveSeenAComma = true;
                        yield return(Alg.MergedChars(accumulatedElement).Trim());

                        accumulatedElement = new List <char>();
                    }
                    else
                    {
                        if (quotesHaveMeaning && (c == '\"'))
                        {
                            inQuote = true;
                            accumulatedElement.Add(c);
                        }
                        else
                        {
                            accumulatedElement.Add(c);
                        }
                    }
                }
                currentChar = currentChar.Next;
            }
            if ((accumulatedElement.Count != 0) || haveSeenAComma)
            {
                yield return(Alg.MergedChars(accumulatedElement).Trim());   // deliberately return "last"

                accumulatedElement = new List <char>();
            }
        }
 public static IEnumerableReenumerable <T> Memoized <T>(this IEnumerable <T> source)
 {
     return(Alg.ReadPoint(source).TakenToEnd());
 }
 public static IEnumerableReenumerable <T> ShuffledWithWeights <T>(this IEnumerable <T> source, Func <T, int> weightAccess, Func <int, int> random)
 {
     return(Alg.ReadPoint(ShuffledAssist(source, weightAccess, random)).TakenToEnd());
 }
 // Memoizes/caches its result, subsequent scans of the same result will give the same sequence.
 public static IEnumerableReenumerable <T> Shuffled <T>(this IEnumerable <T> source, Func <int, int> random)
 {
     // standard shuffle: everything has a weight of 1
     return(Alg.ReadPoint(ShuffledAssist(source, item => 1, random)).TakenToEnd());
 }
 public static IEnumerable <IEnumerable <T> > Tails <T>(this IEnumerable <T> original)
 {
     return(Alg.ReadPoint(original).AllReadPointsDownIncludingEnd().Select(x => x.TakenToEnd()));
 }