Source: Itertools.js

/**
 * @file
 * https://docs.python.org/3/library/itertools.html
 */

/**
 * 
 * 
 * https://docs.python.org/3/library/itertools.html
 */
class Itertools {

    /**
     * 指定した値から開始する等差数列のIterableオブジェクトを返却する。
     * 
     * @param {number} start 
     * @param {number} step [Optional] 刻み幅を指定する。デフォルトは1.
     * @returns {Iterable}
     */
    static count(start, step = 1) {
        return ({
            [Symbol.iterator]: function* () {
                let currentValue = start - step;
                while (true) {
                    currentValue += step;
                    yield currentValue;
                }
            }
        });
    }

    /**
     * [start,end)
     * 
     * @param {number} start 
     * @param {number} end 
     * @param {number} step 
     * @returns {Iterable}
     */
    static range(start, end, step = 1) {
        return ({
            [Symbol.iterator]: function* () {
                let currentValue = start - step;
                while (true) {
                    currentValue += step;
                    if (currentValue < end) {
                        yield currentValue;
                    } else {
                        return;
                    }
                }
            }
        });
    }

    /**
     * 引数のIterableオブジェクトを繰り返すIterableオブジェクトを返却する。
     * 
     * @param {array} elements 配列やIterableオブジェクト
    * @returns {Iterable}
        */
    static cycle(elements) {
        return ({
            [Symbol.iterator]: function* () {
                while (true) {
                    for (let elem of elements) yield elem;
                }
            }
        });
    }

    /**
         * 引数の値を繰り返すIterableオブジェクトを返却する。
         * 
         * @param {*} value Iterableオブジェクトが生成する値
         * @returns {Iterable}
         */
    static repeat(value) {
        return ({
            [Symbol.iterator]: function* () {
                while (true) yield value;
            }
        });
    }

    /**
     * <p>
     * 左側からの畳み込みを行った結果をIterableオブジェクトとして返却する。
     * </p>
     * 
     * <p>
     * Haskellのscanlに近いが、初期値としては配列の最初の要素が用いられる。
     * したがって、関数は型 a -> a -> aを持つ必要がある。
     * </p>
     * 
     * @param {Iterable} iterable 
     * @param {function} func
     * @example
     * Itertools.accumulate([1,2,3,4,5]) // [1,3,6,10,15]
     * Itertools.accumulate([1,2,3,4,5], (a,b) => a*b) // [1,2,6,24,120]
    */
    static accumulate(iterable, func = (a, b) => a + b) {
        return ({
            [Symbol.iterator]: function* () {
                const it = iterable[Symbol.iterator]();
                let acc;
                while (true) {
                    let { value, done } = it.next();
                    if (done) return;
                    if (acc === undefined) {
                        acc = value;
                    } else {
                        acc = func(acc, value);
                    }
                    yield acc;
                }
            }
        });
    }
}

module.exports = Itertools;