// C# で解く「なぜ関数プログラミングは重要か」
// H27.1/15 SUZUKI Hisao
// cf. http://www.sampou.org/haskell/article/whyfp.html

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

// 3. 関数の貼り合せ
static class WhyFp3
{
    static int sum(IEnumerable<int> seq) {
        return seq.Aggregate(0, (x, y)=> x + y);
    }

    static int product(IEnumerable<int> seq) {
        return seq.Aggregate(1, (x, y)=> x * y);
    }

    static bool anytrue(IEnumerable<bool> seq) {
        return seq.Aggregate(false, (x, y)=> x || y);
    }

    static bool alltrue(IEnumerable<bool> seq) {
        return seq.Aggregate(true, (x, y)=> x && y);
    }


    static IEnumerable<int> doubleall(IEnumerable<int> seq) {
        return seq.Select(n => 2 * n);
    }

    static int summatrix(IEnumerable<IEnumerable<int>> mat) {
        return sum(mat.Select(sum));
    }


    // ラベル付き順序ツリー
    class Tree<T> {
        public readonly T Label;
        public readonly IEnumerable<Tree<T>> Subtrees;

        public Tree(T x, IEnumerable<Tree<T>> y) {
            Label = x;
            Subtrees = y;
            // Console.WriteLine(" -- Tree({0}, {1})", x,
            //                   (y == null) ? 0 : y.GetHashCode()); 
        }

        public R Reduce<U, R>
            (Func<T, U, R> f, Func<IEnumerable<R>, U> g, U a) {
            if (Subtrees == null) {
                return f(Label, a);
            } else {
                IEnumerable<R> yy = Subtrees.Select(t => t.Reduce(f, g, a));
                U b = g(yy);
                return f(Label, b);
            }
        }
    }


    static int sumtree(Tree<int> tree) {
        return tree.Reduce((x, y)=> x + y, 
                           yy => yy.Aggregate(0, (x, y)=> x + y),
                           0);
    }

    static IEnumerable<T> cons<T>(T a, IEnumerable<T> x) {
        yield return a;
        if (x != null)
            foreach (T e in x)
                yield return e;
    }

    static IEnumerable<T> labels<T>(Tree<T> tree) {
        return tree.Reduce(cons,
                           yy => yy.SelectMany(y => y),
                           (IEnumerable<T>) null);
    }

    static Tree<R> maptree<T, R>(Func<T, R> f, Tree<T> tree) {
        return tree.Reduce((x, yy) => new Tree<R>(f(x), yy),
                           yy => yy,
                           (IEnumerable<Tree<R>>) null);
    }

    static void Main() {
        Console.WriteLine(sum(new int[]{1, 2, 3, 4})); // => 10
        Console.WriteLine(product(new int[]{1, 2, 3, 4})); // => 24
        Console.WriteLine(anytrue(new bool[]{false, true, false})); // => True
        Console.WriteLine(alltrue(new bool[]{false, true, false})); // => False

        Console.WriteLine(string.Join(", ",
                                      doubleall(new int[]{1, 2, 3, 4})));
        // => 2, 4, 6, 8

        Console.WriteLine(summatrix (new int[][]{
                    new int[]{1, 2, 3},
                    new int[]{4, 5, 6}})); // => 21

        var theTree = new Tree<int>(1, new Tree<int>[] {
                new Tree<int>(2, null),
                new Tree<int>(3, new Tree<int>[]{
                        new Tree<int>(4, null)
                    })
            });
        Console.WriteLine(sumtree(theTree)); // => 10
        Console.WriteLine(string.Join(", ", labels(theTree)));
        // => 1, 2, 3, 4
        Console.WriteLine(string.Join(", ", labels(maptree(x => x + 100,
                                                           theTree))));
        // => 101, 102, 103, 104
    }
}