"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensuring = exports.count = exports.fromFunction = exports.fixed = exports.fibonacci = exports.exponential = exports.elapsed = exports.unionWith_ = exports.unionWith = exports.union_ = exports.union = exports.durations = exports.duration = exports.dimap_ = exports.dimap = exports.delayedM_ = exports.delayedM = exports.delayed_ = exports.delayedFrom = exports.delayed = exports.contramap_ = exports.contramap = exports.compose_ = exports.compose = exports.collectAllIdentity = exports.collectAll = exports.chooseMerge_ = exports.chooseMerge = exports.choose_ = exports.choose = exports.andThenEither_ = exports.andThenEither = exports.checkM_ = exports.checkM = exports.check_ = exports.check = exports.intersection_ = exports.intersection = exports.bothInOut_ = exports.bothInOut = exports.as = exports.andThen_ = exports.andThen = exports.addDelayM_ = exports.addDelayM = exports.addDelay_ = exports.addDelay = exports.repeat = exports.driver = exports.Schedule = void 0;
exports.stop = exports.spaced = exports.second = exports.run_ = exports.run = exports.right = exports.recurs = exports.recurUntilEquals = exports.recurUntilM = exports.recurUntil = exports.recurWhileEquals = exports.recurWhileM = exports.recurWhile = exports.resetWhen_ = exports.resetWhen = exports.resetAfter = exports.repetitions = exports.reconsiderM_ = exports.reconsiderM = exports.reconsider_ = exports.reconsider = exports.provideSome_ = exports.provideSome = exports.provideAll_ = exports.provideAll = exports.onDecision = exports.onDecision_ = exports.modifyDelay_ = exports.modifyDelay = exports.modifyDelayM_ = exports.modifyDelayM = exports.mapM_ = exports.mapM = exports.map_ = exports.map = exports.left = exports.once = exports.linear = exports.jittered_ = exports.jittered = exports.intersectWith = exports.intersectWith_ = exports.identity = exports.forever = exports.foldM_ = exports.foldM = exports.fold_ = exports.fold = exports.first = exports.ensuring_ = void 0;
exports.zipWith_ = exports.zipWith = exports.zipRight_ = exports.zipRight = exports.zipLeft_ = exports.zipLeft = exports.zip = exports.zip_ = exports.unfoldM_ = exports.unfoldM = exports.unfold_ = exports.unfold = exports.windowed = exports.whileOutputM_ = exports.whileOutputM = exports.whileOutput_ = exports.whileOutput = exports.whileInputM_ = exports.whileInputM = exports.whileInput_ = exports.whileInput = exports.untilOutputM_ = exports.untilOutputM = exports.untilOutput_ = exports.untilOutput = exports.untilInputM_ = exports.untilInputM = exports.untilInput_ = exports.untilInput = exports.unit = exports.tapOutput_ = exports.tapOutput = exports.tapInput = exports.tapInput_ = exports.succeed = void 0;
// ets_tracing: off
const Clock = __importStar(require("../Clock"));
const A = __importStar(require("../Collections/Immutable/Array"));
const L = __importStar(require("../Collections/Immutable/List"));
const NA = __importStar(require("../Collections/Immutable/NonEmptyArray"));
const Tp = __importStar(require("../Collections/Immutable/Tuple"));
const E = __importStar(require("../Either"));
const Function_1 = require("../Function");
const NoSuchElementException = __importStar(require("../GlobalExceptions"));
const O = __importStar(require("../Option"));
const Random = __importStar(require("../Random"));
const R = __importStar(require("../Ref"));
const Decision = __importStar(require("./Decision"));
const Driver = __importStar(require("./Driver"));
const T = __importStar(require("./effect"));
/**
 * A `Schedule< Env, In, Out>` defines a recurring schedule, which consumes values of type `In`, and
 * which returns values of type `Out`.
 *
 * Schedules are defined as a possibly infinite set of intervals spread out over time. Each
 * interval defines a window in which recurrence is possible.
 *
 * When schedules are used to repeat or retry effects, the starting boundary of each interval
 * produced by a schedule is used as the moment when the effect will be executed again.
 *
 * Schedules compose in the following primary ways:
 *
 *  * Union. This performs the union of the intervals of two schedules.
 *  * Intersection. This performs the intersection of the intervals of two schedules.
 *  * Sequence. This concatenates the intervals of one schedule onto another.
 *
 * In addition, schedule inputs and outputs can be transformed, filtered (to terminate a
 * schedule early in response to some input or output), and so forth.
 *
 * A variety of other operators exist for transforming and combining schedules, and the companion
 * object for `Schedule` contains all common types of schedules, both for performing retrying, as
 * well as performing repetition.
 */
class Schedule {
    constructor(step) {
        this.step = step;
        /**
         * Returns a new schedule that performs a geometric intersection on the intervals defined
         * by both schedules.
         */
        this["&&"] = (that) => intersection_(this, that);
        /**
         * The same as `&&`, but ignores the left output.
         */
        this["***"] = (that) => bothInOut_(this, that);
        /**
         * The same as `&&`, but ignores the left output.
         */
        this["*>"] = (that) => map_(this["&&"](that), (_) => _.get(1));
        /**
         * Returns a new schedule that allows choosing between feeding inputs to this schedule, or
         * feeding inputs to the specified schedule.
         */
        this["+++"] = (that) => chooseMerge_(this, that);
        /**
         * A symbolic alias for `andThen`.
         */
        this["++"] = (that) => andThen_(this, that);
        /**
         * The same as `&&`, but ignores the right output.
         */
        this["<*"] = (that) => map_(this["&&"](that), (_) => _.get(0));
        /**
         * An operator alias for `zip`.
         */
        this["<*>"] = (that) => zip_(this, that);
        /**
         * Returns the composition of this schedule and the specified schedule, by piping the output of
         * this one into the input of the other. Effects described by this schedule will always be
         * executed before the effects described by the second schedule.
         */
        this["<<<"] = (that) => compose_(that, this);
        /**
         * Returns the composition of this schedule and the specified schedule, by piping the output of
         * this one into the input of the other. Effects described by this schedule will always be
         * executed before the effects described by the second schedule.
         */
        this[">>>"] = (that) => compose_(this, that);
        /**
         * Returns a new schedule that performs a geometric union on the intervals defined
         * by both schedules.
         */
        this["||"] = (that) => union_(this, that);
        /**
         * Returns a new schedule that chooses between two schedules with a common output.
         */
        this["|||"] = (that) => chooseMerge_(this, that);
        /**
         * Operator alias for `andThenEither`.
         */
        this["<||>"] = (that) => andThenEither_(this, that);
    }
}
exports.Schedule = Schedule;
/**
 * Returns a driver that can be used to step the schedule, appropriately handling sleeping.
 */
function driver(self) {
    return (T.map_(R.makeRef([
        O.none,
        self.step
    ]), (ref) => {
        const reset = ref.set([O.none, self.step]);
        const last = (T.chain_(ref.get, ([o, _]) => O.fold_(o, () => T.fail(new NoSuchElementException.NoSuchElementException()), (b) => T.succeed(b))));
        const next = (i) => (T.map_(T.bind_(T.bind_(T.bind_(T.bind_(T.do, "step", () => T.map_(ref.get, ([_, o]) => o)), "now", () => Clock.currentTime), "dec", ({ now, step }) => step(now, i)), "v", ({ dec, now }) => {
            switch (dec._tag) {
                case "Done": {
                    return (T.chain_(ref.set([O.some(dec.out), Decision.done(dec.out)]), () => T.fail(O.none)));
                }
                case "Continue": {
                    return (T.map_(T.chain_(T.map_(ref.set([O.some(dec.out), dec.next]), () => dec.interval - now), (s) => (s > 0 ? T.sleep(s) : T.unit)), () => dec.out));
                }
            }
        }), ({ v }) => v));
        return new Driver.Driver(next, last, reset);
    }));
}
exports.driver = driver;
function repeatLoop(init, self = init) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return repeatLoop(init, self)(now, i);
            }
            case "Continue": {
                return T.succeed(Decision.makeContinue(d.out, d.interval, repeatLoop(init, d.next)));
            }
        }
    });
}
/**
 * Returns a new schedule that loops this one continuously, resetting the state
 * when this schedule is done.
 */
function repeat(self) {
    return new Schedule(repeatLoop(self.step));
}
exports.repeat = repeat;
/**
 * Returns a new schedule with the given delay added to every update.
 */
function addDelay(f) {
    return (self) => addDelayM_(self, (b) => T.succeed(f(b)));
}
exports.addDelay = addDelay;
/**
 * Returns a new schedule with the given delay added to every update.
 */
function addDelay_(self, f) {
    return addDelayM_(self, (b) => T.succeed(f(b)));
}
exports.addDelay_ = addDelay_;
/**
 * Returns a new schedule with the effectfully calculated delay added to every update.
 */
function addDelayM(f) {
    return (self) => addDelayM_(self, f);
}
exports.addDelayM = addDelayM;
/**
 * Returns a new schedule with the effectfully calculated delay added to every update.
 */
function addDelayM_(self, f) {
    return modifyDelayM_(self, (o, d) => T.map_(f(o), (i) => i + d));
}
exports.addDelayM_ = addDelayM_;
/**
 * The same as `andThenEither`, but merges the output.
 */
function andThen(that) {
    return (self) => andThen_(self, that);
}
exports.andThen = andThen;
/**
 * The same as `andThenEither`, but merges the output.
 */
function andThen_(self, that) {
    return map_(andThenEither_(self, that), (a) => (a._tag === "Left" ? a.left : a.right));
}
exports.andThen_ = andThen_;
/**
 * Returns a new schedule that maps this schedule to a constant output.
 */
function as(o) {
    return (self) => map_(self, () => o);
}
exports.as = as;
function bothLoop(self, that) {
    return (now, t) => {
        const { tuple: [in1, in2] } = t;
        return T.zipWith_(self(now, in1), that(now, in2), (d1, d2) => {
            switch (d1._tag) {
                case "Done": {
                    switch (d2._tag) {
                        case "Done": {
                            return Decision.makeDone(Tp.tuple(d1.out, d2.out));
                        }
                        case "Continue": {
                            return Decision.makeDone(Tp.tuple(d1.out, d2.out));
                        }
                    }
                }
                case "Continue": {
                    switch (d2._tag) {
                        case "Done": {
                            return Decision.makeDone(Tp.tuple(d1.out, d2.out));
                        }
                        case "Continue": {
                            return Decision.makeContinue(Tp.tuple(d1.out, d2.out), Math.min(d1.interval, d2.interval), bothLoop(d1.next, d2.next));
                        }
                    }
                }
            }
        });
    };
}
/**
 * Returns a new schedule that has both the inputs and outputs of this and the specified
 * schedule.
 */
function bothInOut(that) {
    return (self) => new Schedule(bothLoop(self.step, that.step));
}
exports.bothInOut = bothInOut;
/**
 * Returns a new schedule that has both the inputs and outputs of this and the specified
 * schedule.
 */
function bothInOut_(self, that) {
    return new Schedule(bothLoop(self.step, that.step));
}
exports.bothInOut_ = bothInOut_;
/**
 * Returns a new schedule that has both the inputs and outputs of this and the specified
 * schedule.
 */
function intersection(that) {
    return (self) => intersection_(self, that);
}
exports.intersection = intersection;
/**
 * Returns a new schedule that performs a geometric intersection on the intervals defined
 * by both schedules.
 */
function intersection_(self, that) {
    return intersectWith_(self, that, (l, r) => Math.max(l, r));
}
exports.intersection_ = intersection_;
/**
 * Returns a new schedule that passes each input and output of this schedule to the spefcified
 * function, and then determines whether or not to continue based on the return value of the
 * function.
 */
function check(f) {
    return (self) => check_(self, f);
}
exports.check = check;
/**
 * Returns a new schedule that passes each input and output of this schedule to the spefcified
 * function, and then determines whether or not to continue based on the return value of the
 * function.
 */
function check_(self, f) {
    return checkM_(self, (i, o) => T.succeed(f(i, o)));
}
exports.check_ = check_;
function checkMLoop(self, test) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.succeed(Decision.makeDone(d.out));
            }
            case "Continue": {
                return T.map_(test(i, d.out), (b) => b
                    ? Decision.makeContinue(d.out, d.interval, checkMLoop(d.next, test))
                    : Decision.makeDone(d.out));
            }
        }
    });
}
/**
 * Returns a new schedule that passes each input and output of this schedule to the specified
 * function, and then determines whether or not to continue based on the return value of the
 * function.
 */
function checkM(test) {
    return (self) => new Schedule(checkMLoop(self.step, test));
}
exports.checkM = checkM;
/**
 * Returns a new schedule that passes each input and output of this schedule to the specified
 * function, and then determines whether or not to continue based on the return value of the
 * function.
 */
function checkM_(self, test) {
    return new Schedule(checkMLoop(self.step, test));
}
exports.checkM_ = checkM_;
/**
 * Returns a new schedule that first executes this schedule to completion, and then executes the
 * specified schedule to completion.
 */
function andThenEither(that) {
    return (self) => andThenEither_(self, that);
}
exports.andThenEither = andThenEither;
function andThenEitherLoop(self, that, onLeft) {
    return (now, i) => {
        if (onLeft) {
            return T.chain_(self(now, i), (d) => {
                switch (d._tag) {
                    case "Continue": {
                        return T.succeed(Decision.makeContinue(E.left(d.out), d.interval, andThenEitherLoop(d.next, that, true)));
                    }
                    case "Done": {
                        return andThenEitherLoop(self, that, false)(now, i);
                    }
                }
            });
        }
        else {
            return T.map_(that(now, i), (d) => {
                switch (d._tag) {
                    case "Done": {
                        return Decision.makeDone(E.right(d.out));
                    }
                    case "Continue": {
                        return Decision.makeContinue(E.right(d.out), d.interval, andThenEitherLoop(self, d.next, false));
                    }
                }
            });
        }
    };
}
/**
 * Returns a new schedule that first executes this schedule to completion, and then executes the
 * specified schedule to completion.
 */
function andThenEither_(self, that) {
    return new Schedule(andThenEitherLoop(self.step, that.step, true));
}
exports.andThenEither_ = andThenEither_;
function chooseLoop(self, that) {
    return (now, either) => E.fold_(either, (i) => T.map_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return Decision.makeDone(E.left(d.out));
            }
            case "Continue": {
                return Decision.makeContinue(E.left(d.out), d.interval, chooseLoop(d.next, that));
            }
        }
    }), (i2) => T.map_(that(now, i2), (d) => {
        switch (d._tag) {
            case "Done": {
                return Decision.makeDone(E.right(d.out));
            }
            case "Continue": {
                return Decision.makeContinue(E.right(d.out), d.interval, chooseLoop(self, d.next));
            }
        }
    }));
}
/**
 * Returns a new schedule that allows choosing between feeding inputs to this schedule, or
 * feeding inputs to the specified schedule.
 */
function choose(that) {
    return (self) => choose_(self, that);
}
exports.choose = choose;
/**
 * Returns a new schedule that allows choosing between feeding inputs to this schedule, or
 * feeding inputs to the specified schedule.
 */
function choose_(self, that) {
    return new Schedule(chooseLoop(self.step, that.step));
}
exports.choose_ = choose_;
/**
 * Returns a new schedule that allows choosing between feeding inputs to this schedule, or
 * feeding inputs to the specified schedule.
 */
function chooseMerge(that) {
    return (self) => chooseMerge_(self, that);
}
exports.chooseMerge = chooseMerge;
/**
 * Returns a new schedule that allows choosing between feeding inputs to this schedule, or
 * feeding inputs to the specified schedule.
 */
function chooseMerge_(self, that) {
    return map_(choose_(self, that), E.merge);
}
exports.chooseMerge_ = chooseMerge_;
/**
 * Returns a new schedule that collects the outputs of this one into an array.
 */
function collectAll(self) {
    return map_(fold_(self, L.empty(), (xs, x) => L.append_(xs, x)), L.toArray);
}
exports.collectAll = collectAll;
/**
 * A schedule that recurs anywhere, collecting all inputs into a list.
 */
function collectAllIdentity() {
    return collectAll(identity());
}
exports.collectAllIdentity = collectAllIdentity;
function composeLoop(self, that) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.map_(that(now, d.out), Decision.toDone);
            }
            case "Continue": {
                return T.map_(that(now, d.out), (d2) => {
                    switch (d2._tag) {
                        case "Done": {
                            return Decision.makeDone(d2.out);
                        }
                        case "Continue": {
                            return Decision.makeContinue(d2.out, Math.max(d.interval, d2.interval), composeLoop(d.next, d2.next));
                        }
                    }
                });
            }
        }
    });
}
/**
 * Returns the composition of this schedule and the specified schedule, by piping the output of
 * this one into the input of the other. Effects described by this schedule will always be
 * executed before the effects described by the second schedule.
 */
function compose(that) {
    return (self) => compose_(self, that);
}
exports.compose = compose;
/**
 * Returns the composition of this schedule and the specified schedule, by piping the output of
 * this one into the input of the other. Effects described by this schedule will always be
 * executed before the effects described by the second schedule.
 */
function compose_(self, that) {
    return new Schedule(composeLoop(self.step, that.step));
}
exports.compose_ = compose_;
function intersectWithLoop(self, that, f) {
    return (now, i) => {
        const left = self(now, i);
        const right = that(now, i);
        return T.zipWith_(left, right, (l, r) => {
            switch (l._tag) {
                case "Done": {
                    switch (r._tag) {
                        case "Done": {
                            return Decision.makeDone(Tp.tuple(l.out, r.out));
                        }
                        case "Continue": {
                            return Decision.makeDone(Tp.tuple(l.out, r.out));
                        }
                    }
                }
                case "Continue": {
                    switch (r._tag) {
                        case "Done": {
                            return Decision.makeDone(Tp.tuple(l.out, r.out));
                        }
                        case "Continue": {
                            return Decision.makeContinue(Tp.tuple(l.out, r.out), f(l.interval, r.interval), intersectWithLoop(l.next, r.next, f));
                        }
                    }
                }
            }
        });
    };
}
/**
 * Returns a new schedule that deals with a narrower class of inputs than this schedule.
 */
function contramap(f) {
    return (self) => contramap_(self, f);
}
exports.contramap = contramap;
/**
 * Returns a new schedule that deals with a narrower class of inputs than this schedule.
 */
function contramap_(self, f) {
    return new Schedule((now, i) => T.map_(self.step(now, f(i)), Decision.contramap(f)));
}
exports.contramap_ = contramap_;
/**
 * Returns a new schedule with the specified computed delay added before the start
 * of each interval produced by this schedule.
 */
function delayed(f) {
    return (self) => delayed_(self, f);
}
exports.delayed = delayed;
/**
 * Returns a new schedule with the specified computed delay added before the start
 * of each interval produced by this schedule.
 */
function delayedFrom(schedule) {
    return addDelay_(schedule, (x) => x);
}
exports.delayedFrom = delayedFrom;
/**
 * Returns a new schedule with the specified computed delay added before the start
 * of each interval produced by this schedule.
 */
function delayed_(self, f) {
    return delayedM_(self, (d) => T.succeed(f(d)));
}
exports.delayed_ = delayed_;
/**
 * Returns a new schedule with the specified effectfully computed delay added before the start
 * of each interval produced by this schedule.
 */
function delayedM(f) {
    return (self) => delayedM_(self, f);
}
exports.delayedM = delayedM;
/**
 * Returns a new schedule with the specified effectfully computed delay added before the start
 * of each interval produced by this schedule.
 */
function delayedM_(self, f) {
    return modifyDelayM_(self, (o, d) => f(d));
}
exports.delayedM_ = delayedM_;
/**
 * Returns a new schedule that contramaps the input and maps the output.
 */
function dimap(f) {
    return (g) => (self) => dimap_(self, f, g);
}
exports.dimap = dimap;
/**
 * Returns a new schedule that contramaps the input and maps the output.
 */
function dimap_(self, f, g) {
    return map_(contramap_(self, f), g);
}
exports.dimap_ = dimap_;
/**
 * A schedule that can recur one time, the specified amount of time into the future.
 */
function duration(n) {
    return new Schedule((now, _) => T.succeed(Decision.makeContinue(0, now + n, () => T.succeed(Decision.makeDone(n)))));
}
exports.duration = duration;
/**
 * A schedule that can recur one time, the specified amount of time into the future.
 */
function durations(n, ...rest) {
    return A.reduce_(rest, duration(n), (acc, d) => andThen_(acc, duration(d)));
}
exports.durations = durations;
/**
 * Returns a new schedule that performs a geometric union on the intervals defined
 * by both schedules.
 */
function union(that) {
    return (self) => union_(self, that);
}
exports.union = union;
/**
 * Returns a new schedule that combines this schedule with the specified
 * schedule, continuing as long as either schedule wants to continue and
 * merging the next intervals according to the specified merge function.
 */
function union_(self, that) {
    return unionWith_(self, that, (d1, d2) => Math.min(d1, d2));
}
exports.union_ = union_;
/**
 * Returns a new schedule that combines this schedule with the specified
 * schedule, continuing as long as either schedule wants to continue and
 * merging the next intervals according to the specified merge function.
 */
function unionWith(that, f) {
    return (self) => unionWith_(self, that, f);
}
exports.unionWith = unionWith;
function unionWithLoop(self, that, f) {
    return (now, inp) => {
        const left = self(now, inp);
        const right = that(now, inp);
        return T.zipWith_(left, right, (l, r) => {
            switch (l._tag) {
                case "Done": {
                    switch (r._tag) {
                        case "Done": {
                            return Decision.makeDone(Tp.tuple(l.out, r.out));
                        }
                        case "Continue": {
                            return Decision.makeContinue(Tp.tuple(l.out, r.out), r.interval, unionWithLoop(() => T.succeed(l), r.next, f));
                        }
                    }
                }
                case "Continue": {
                    switch (r._tag) {
                        case "Done": {
                            return Decision.makeContinue(Tp.tuple(l.out, r.out), l.interval, unionWithLoop(l.next, () => T.succeed(r), f));
                        }
                        case "Continue": {
                            return Decision.makeContinue(Tp.tuple(l.out, r.out), f(l.interval, r.interval), unionWithLoop(l.next, r.next, f));
                        }
                    }
                }
            }
        });
    };
}
/**
 * Returns a new schedule that combines this schedule with the specified
 * schedule, continuing as long as either schedule wants to continue and
 * merging the next intervals according to the specified merge function.
 */
function unionWith_(self, that, f) {
    return new Schedule(unionWithLoop(self.step, that.step, f));
}
exports.unionWith_ = unionWith_;
function elapsedLoop(o) {
    return (now, _) => T.succeed(O.fold_(o, () => Decision.makeContinue(0, now, elapsedLoop(O.some(now))), (start) => Decision.makeContinue(now - start, now, elapsedLoop(O.some(start)))));
}
/**
 * A schedule that occurs everywhere, which returns the total elapsed duration since the
 * first step.
 */
exports.elapsed = new Schedule(elapsedLoop(O.none));
/**
 * A schedule that always recurs, but will wait a certain amount between
 * repetitions, given by `base * factor.pow(n)`, where `n` is the number of
 * repetitions so far. Returns the current duration between recurrences.
 */
function exponential(base, factor = 2.0) {
    return delayedFrom(map_(exports.forever, (i) => base * Math.pow(factor, i)));
}
exports.exponential = exponential;
/**
 * A schedule that always recurs, increasing delays by summing the
 * preceding two delays (similar to the fibonacci sequence). Returns the
 * current duration between recurrences.
 */
function fibonacci(one) {
    return delayedFrom(map_(unfold_((0, Function_1.tuple)(one, one), ([a1, a2]) => (0, Function_1.tuple)(a1, a1 + a2)), ([_]) => _));
}
exports.fibonacci = fibonacci;
/**
 * A schedule that recurs on a fixed interval. Returns the number of
 * repetitions of the schedule so far.
 *
 * If the action run between updates takes longer than the interval, then the
 * action will be run immediately, but re-runs will not "pile up".
 *
 * <pre>
 * |-----interval-----|-----interval-----|-----interval-----|
 * |---------action--------||action|-----|action|-----------|
 * </pre>
 */
function fixed(interval) {
    function loop(startMillis, n) {
        return (now, _) => T.succeed(O.fold_(startMillis, () => Decision.makeContinue(n + 1, now + interval, loop(O.some({ startMillis: now, lastRun: now + interval }), n + 1)), ({ lastRun, startMillis }) => {
            const runningBehind = now > lastRun + interval;
            const boundary = interval === 0 ? interval : interval - ((now - startMillis) % interval);
            const sleepTime = boundary === 0 ? interval : boundary;
            const nextRun = runningBehind ? now : now + sleepTime;
            return Decision.makeContinue(n + 1, nextRun, loop(O.some({ startMillis, lastRun: nextRun }), n + 1));
        }));
    }
    return new Schedule(loop(O.none, 0));
}
exports.fixed = fixed;
/**
 * A schedule that always recurs, mapping input values through the
 * specified function.
 */
function fromFunction(f) {
    return map_(identity(), f);
}
exports.fromFunction = fromFunction;
/**
 * A schedule that always recurs, which counts the number of recurrances.
 */
exports.count = unfold_(0, (n) => n + 1);
function ensuringLoop(finalizer, self) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.as_(finalizer, Decision.makeDone(d.out));
            }
            case "Continue": {
                return T.succeed(Decision.makeContinue(d.out, d.interval, ensuringLoop(finalizer, d.next)));
            }
        }
    });
}
/**
 * Returns a new schedule that will run the specified finalizer as soon as the schedule is
 * complete. Note that unlike `Effect#ensuring`, this method does not guarantee the finalizer
 * will be run. The `Schedule` may not initialize or the driver of the schedule may not run
 * to completion. However, if the `Schedule` ever decides not to continue, then the
 * finalizer will be run.
 */
function ensuring(finalizer) {
    return (self) => new Schedule(ensuringLoop(finalizer, self.step));
}
exports.ensuring = ensuring;
/**
 * Returns a new schedule that will run the specified finalizer as soon as the schedule is
 * complete. Note that unlike `Effect#ensuring`, this method does not guarantee the finalizer
 * will be run. The `Schedule` may not initialize or the driver of the schedule may not run
 * to completion. However, if the `Schedule` ever decides not to continue, then the
 * finalizer will be run.
 */
function ensuring_(self, finalizer) {
    return new Schedule(ensuringLoop(finalizer, self.step));
}
exports.ensuring_ = ensuring_;
/**
 * Returns a new schedule that packs the input and output of this schedule into the first
 * element of a tuple. This allows carrying information through this schedule.
 */
function first() {
    return (self) => bothInOut_(self, identity());
}
exports.first = first;
function foldMLoop(z, f, self) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.succeed(Decision.makeDone(z));
            }
            case "Continue": {
                return T.map_(f(z, d.out), (z2) => Decision.makeContinue(z2, d.interval, foldMLoop(z2, f, d.next)));
            }
        }
    });
}
/**
 * Returns a new schedule that effectfully folds over the outputs of this one.
 */
function fold(z) {
    return (f) => (self) => fold_(self, z, f);
}
exports.fold = fold;
/**
 * Returns a new schedule that effectfully folds over the outputs of this one.
 */
function fold_(self, z, f) {
    return foldM_(self, z, (z, o) => T.succeed(f(z, o)));
}
exports.fold_ = fold_;
/**
 * Returns a new schedule that effectfully folds over the outputs of this one.
 */
function foldM(z) {
    return (f) => (self) => foldM_(self, z, f);
}
exports.foldM = foldM;
/**
 * Returns a new schedule that effectfully folds over the outputs of this one.
 */
function foldM_(self, z, f) {
    return new Schedule(foldMLoop(z, f, self.step));
}
exports.foldM_ = foldM_;
/**
 * A schedule that recurs forever, producing a count of repeats: 0, 1, 2, ...
 */
exports.forever = unfold_(0, (n) => n + 1);
function identityLoop() {
    return (now, i) => T.succeed(Decision.makeContinue(i, now, identityLoop()));
}
/**
 * A schedule that always recurs, which returns inputs as outputs.
 */
function identity() {
    return new Schedule(identityLoop());
}
exports.identity = identity;
/**
 * Returns a new schedule that combines this schedule with the specified
 * schedule, continuing as long as both schedules want to continue and
 * merging the next intervals according to the specified merge function.
 */
function intersectWith_(self, that, f) {
    return new Schedule(intersectWithLoop(self.step, that.step, f));
}
exports.intersectWith_ = intersectWith_;
/**
 * Returns a new schedule that combines this schedule with the specified
 * schedule, continuing as long as both schedules want to continue and
 * merging the next intervals according to the specified merge function.
 */
function intersectWith(that, f) {
    return (self) => intersectWith_(self, that, f);
}
exports.intersectWith = intersectWith;
/**
 * Returns a new schedule that randomly modifies the size of the intervals of this schedule.
 */
function jittered({ max = 0.1, min = 0 } = {}) {
    return (self) => jittered_(self, { min, max });
}
exports.jittered = jittered;
/**
 * Returns a new schedule that randomly modifies the size of the intervals of this schedule.
 */
function jittered_(self, { max = 0.1, min = 0 } = {}) {
    return delayedM_(self, (d) => T.map_(Random.next, (random) => d * min * (1 - random) + d * max * random));
}
exports.jittered_ = jittered_;
/**
 * A schedule that always recurs, but will repeat on a linear time
 * interval, given by `base * n` where `n` is the number of
 * repetitions so far. Returns the current duration between recurrences.
 */
function linear(base) {
    return delayedFrom(map_(exports.forever, (i) => base * (i + 1)));
}
exports.linear = linear;
/**
 * A schedule that recurs one time.
 */
exports.once = unit(recurs(1));
function mapMLoop(f, self) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.map_(f(d.out), (o) => Decision.makeDone(o));
            }
            case "Continue": {
                return T.map_(f(d.out), (o) => Decision.makeContinue(o, d.interval, mapMLoop(f, d.next)));
            }
        }
    });
}
/**
 * Returns a new schedule that makes this schedule available on the `Left` side of an `Either`
 * input, allowing propagating some type `X` through this channel on demand.
 */
function left() {
    return (self) => choose_(self, identity());
}
exports.left = left;
/**
 * Returns a new schedule that maps the output of this schedule through the specified
 * effectful function.
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Returns a new schedule that maps the output of this schedule through the specified
 * effectful function.
 */
function map_(self, f) {
    return mapM_(self, (o) => T.succeed(f(o)));
}
exports.map_ = map_;
/**
 * Returns a new schedule that maps the output of this schedule through the specified function.
 */
function mapM(f) {
    return (self) => new Schedule(mapMLoop(f, self.step));
}
exports.mapM = mapM;
/**
 * Returns a new schedule that maps the output of this schedule through the specified function.
 */
function mapM_(self, f) {
    return new Schedule(mapMLoop(f, self.step));
}
exports.mapM_ = mapM_;
function modifyDelayMLoop(f, self) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.succeed(Decision.makeDone(d.out));
            }
            case "Continue": {
                const delay = d.interval - now;
                return T.map_(f(d.out, delay), (n) => Decision.makeContinue(d.out, d.interval + n, modifyDelayMLoop(f, d.next)));
            }
        }
    });
}
/**
 * Returns a new schedule that modifies the delay using the specified
 * effectual function.
 */
function modifyDelayM(f) {
    return (self) => modifyDelayM_(self, f);
}
exports.modifyDelayM = modifyDelayM;
/**
 * Returns a new schedule that modifies the delay using the specified
 * effectual function.
 */
function modifyDelayM_(self, f) {
    return new Schedule(modifyDelayMLoop(f, self.step));
}
exports.modifyDelayM_ = modifyDelayM_;
/**
 * Returns a new schedule that modifies the delay using the specified
 * function.
 */
function modifyDelay(f) {
    return (self) => modifyDelay_(self, f);
}
exports.modifyDelay = modifyDelay;
/**
 * Returns a new schedule that modifies the delay using the specified
 * function.
 */
function modifyDelay_(self, f) {
    return modifyDelayM_(self, (o, d) => T.succeed(f(o, d)));
}
exports.modifyDelay_ = modifyDelay_;
function onDecisionLoop(self, f) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.as_(f(d), Decision.makeDone(d.out));
            }
            case "Continue": {
                return T.as_(f(d), Decision.makeContinue(d.out, d.interval, onDecisionLoop(d.next, f)));
            }
        }
    });
}
/**
 * Returns a new schedule that applies the current one but runs the specified effect
 * for every decision of this schedule. This can be used to create schedules
 * that log failures, decisions, or computed values.
 */
function onDecision_(self, f) {
    return new Schedule(onDecisionLoop(self.step, f));
}
exports.onDecision_ = onDecision_;
/**
 * Returns a new schedule that applies the current one but runs the specified effect
 * for every decision of this schedule. This can be used to create schedules
 * that log failures, decisions, or computed values.
 */
function onDecision(f) {
    return (self) => new Schedule(onDecisionLoop(self.step, f));
}
exports.onDecision = onDecision;
function provideAllLoop(env, self) {
    return (now, i) => T.provideAll_(T.map_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return Decision.makeDone(d.out);
            }
            case "Continue": {
                return Decision.makeContinue(d.out, d.interval, provideAllLoop(env, d.next));
            }
        }
    }), env);
}
/**
 * Returns a new schedule with its environment provided to it, so the resulting
 * schedule does not require any environment.
 */
function provideAll(env) {
    return (self) => provideAll_(self, env);
}
exports.provideAll = provideAll;
/**
 * Returns a new schedule with its environment provided to it, so the resulting
 * schedule does not require any environment.
 */
function provideAll_(self, env) {
    return new Schedule(provideAllLoop(env, self.step));
}
exports.provideAll_ = provideAll_;
function provideSomeLoop(env, self) {
    return (now, i) => T.provideSome_(T.map_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return Decision.makeDone(d.out);
            }
            case "Continue": {
                return Decision.makeContinue(d.out, d.interval, provideSomeLoop(env, d.next));
            }
        }
    }), env);
}
/**
 * Returns a new schedule with part of its environment provided to it, so the
 * resulting schedule does not require any environment.
 */
function provideSome(env) {
    return (self) => new Schedule(provideSomeLoop(env, self.step));
}
exports.provideSome = provideSome;
/**
 * Returns a new schedule with part of its environment provided to it, so the
 * resulting schedule does not require any environment.
 */
function provideSome_(self, env) {
    return new Schedule(provideSomeLoop(env, self.step));
}
exports.provideSome_ = provideSome_;
/**
 * Returns a new schedule that effectfully reconsiders every decision made by this schedule,
 * possibly modifying the next interval and the output type in the process.
 */
function reconsider(f) {
    return (self) => reconsider_(self, f);
}
exports.reconsider = reconsider;
/**
 * Returns a new schedule that effectfully reconsiders every decision made by this schedule,
 * possibly modifying the next interval and the output type in the process.
 */
function reconsider_(self, f) {
    return reconsiderM_(self, (d) => T.succeed(f(d)));
}
exports.reconsider_ = reconsider_;
function reconsiderMLoop(self, f) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.map_(f(d), E.fold((o2) => Decision.makeDone(o2), ([o2]) => Decision.makeDone(o2)));
            }
            case "Continue": {
                return T.map_(f(d), E.fold((o2) => Decision.makeDone(o2), ([o2, int]) => Decision.makeContinue(o2, int, reconsiderMLoop(d.next, f))));
            }
        }
    });
}
/**
 * Returns a new schedule that effectfully reconsiders every decision made by this schedule,
 * possibly modifying the next interval and the output type in the process.
 */
function reconsiderM(f) {
    return (self) => reconsiderM_(self, f);
}
exports.reconsiderM = reconsiderM;
/**
 * Returns a new schedule that effectfully reconsiders every decision made by this schedule,
 * possibly modifying the next interval and the output type in the process.
 */
function reconsiderM_(self, f) {
    return new Schedule(reconsiderMLoop(self.step, f));
}
exports.reconsiderM_ = reconsiderM_;
/**
 * Returns a new schedule that outputs the number of repetitions of this one.
 */
function repetitions(self) {
    return fold_(self, 0, (n) => n + 1);
}
exports.repetitions = repetitions;
/**
 * Return a new schedule that automatically resets the schedule to its initial state
 * after some time of inactivity defined by `duration`.
 */
function resetAfter(duration) {
    return (self) => map_(resetWhen_(zip_(self, exports.elapsed), ({ tuple: [_, d] }) => d >= duration), ({ tuple: [o] }) => o);
}
exports.resetAfter = resetAfter;
function resetWhenLoop(self, step, f) {
    return (now, i) => T.chain_(step(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return f(d.out) ? self.step(now, i) : T.succeed(Decision.makeDone(d.out));
            }
            case "Continue": {
                return f(d.out)
                    ? self.step(now, i)
                    : T.succeed(Decision.makeContinue(d.out, d.interval, resetWhenLoop(self, d.next, f)));
            }
        }
    });
}
/**
 * Resets the schedule when the specified predicate on the schedule output evaluates to true.
 */
function resetWhen(f) {
    return (self) => resetWhen_(self, f);
}
exports.resetWhen = resetWhen;
/**
 * Resets the schedule when the specified predicate on the schedule output evaluates to true.
 */
function resetWhen_(self, f) {
    return new Schedule(resetWhenLoop(self, self.step, f));
}
exports.resetWhen_ = resetWhen_;
/**
 * A schedule that recurs for as long as the predicate evaluates to true.
 */
function recurWhile(f) {
    return whileInput_(identity(), f);
}
exports.recurWhile = recurWhile;
/**
 * A schedule that recurs for as long as the effectful predicate evaluates to true.
 */
function recurWhileM(f) {
    return whileInputM_(identity(), f);
}
exports.recurWhileM = recurWhileM;
/**
 * A schedule that recurs for as long as the predicate evaluates to true.
 */
function recurWhileEquals(a) {
    return whileInput_(identity(), (x) => a === x);
}
exports.recurWhileEquals = recurWhileEquals;
/**
 * A schedule that recurs for as long as the predicate evaluates to true.
 */
function recurUntil(f) {
    return untilInput_(identity(), f);
}
exports.recurUntil = recurUntil;
/**
 * A schedule that recurs for as long as the effectful predicate evaluates to true.
 */
function recurUntilM(f) {
    return untilInputM_(identity(), f);
}
exports.recurUntilM = recurUntilM;
/**
 * A schedule that recurs for as long as the predicate evaluates to true.
 */
function recurUntilEquals(a) {
    return untilInput_(identity(), (x) => x === a);
}
exports.recurUntilEquals = recurUntilEquals;
/**
 * A schedule spanning all time, which can be stepped only the specified number of times before
 * it terminates.
 */
function recurs(n) {
    return whileOutput_(exports.forever, (x) => x < n);
}
exports.recurs = recurs;
/**
 * Returns a new schedule that makes this schedule available on the `Right` side of an `Either`
 * input, allowing propagating some type `X` through this channel on demand.
 */
function right() {
    return (self) => choose_(identity(), self);
}
exports.right = right;
function runLoop(now, xs, self, acc) {
    if (A.isNonEmpty(xs)) {
        return T.chain_(self(now, NA.head(xs)), (d) => {
            switch (d._tag) {
                case "Done": {
                    return T.succeed([...acc, d.out]);
                }
                case "Continue": {
                    return runLoop(d.interval, xs, d.next, [...acc, d.out]);
                }
            }
        });
    }
    else {
        return T.succeed(acc);
    }
}
/**
 * Runs a schedule using the provided inputs, and collects all outputs.
 */
function run(now, i) {
    return (self) => run_(self, now, i);
}
exports.run = run;
/**
 * Runs a schedule using the provided inputs, and collects all outputs.
 */
function run_(self, now, i) {
    return runLoop(now, Array.from(i), self.step, []);
}
exports.run_ = run_;
/**
 * Returns a new schedule that packs the input and output of this schedule into the second
 * element of a tuple. This allows carrying information through this schedule.
 */
function second() {
    return (self) => bothInOut_(identity(), self);
}
exports.second = second;
function tapInputLoop(self, f) {
    return (now, i) => T.chain_(f(i), () => T.map_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return Decision.makeDone(d.out);
            }
            case "Continue": {
                return Decision.makeContinue(d.out, d.interval, tapInputLoop(d.next, f));
            }
        }
    }));
}
/**
 * Returns a schedule that recurs continuously, each repetition spaced the specified duration
 * from the last run.
 */
function spaced(duration) {
    return addDelay_(exports.forever, () => duration);
}
exports.spaced = spaced;
/**
 * A schedule that does not recur, it just stops.
 */
exports.stop = unit(recurs(0));
/**
 * Returns a schedule that repeats one time, producing the specified constant value.
 */
function succeed(a) {
    return as(a)(exports.forever);
}
exports.succeed = succeed;
/**
 * Returns a new schedule that effectfully processes every input to this schedule.
 */
function tapInput_(self, f) {
    return new Schedule(tapInputLoop(self.step, f));
}
exports.tapInput_ = tapInput_;
/**
 * Returns a new schedule that effectfully processes every input to this schedule.
 */
function tapInput(f) {
    return (self) => new Schedule(tapInputLoop(self.step, f));
}
exports.tapInput = tapInput;
function tapOutputLoop(self, f) {
    return (now, i) => T.chain_(self(now, i), (d) => {
        switch (d._tag) {
            case "Done": {
                return T.as_(f(d.out), Decision.makeDone(d.out));
            }
            case "Continue": {
                return T.as_(f(d.out), Decision.makeContinue(d.out, d.interval, tapOutputLoop(d.next, f)));
            }
        }
    });
}
/**
 * Returns a new schedule that effectfully processes every output from this schedule.
 */
function tapOutput(f) {
    return (self) => tapOutput_(self, f);
}
exports.tapOutput = tapOutput;
/**
 * Returns a new schedule that effectfully processes every output from this schedule.
 */
function tapOutput_(self, f) {
    return new Schedule(tapOutputLoop(self.step, f));
}
exports.tapOutput_ = tapOutput_;
/**
 * Returns a new schedule that maps the output of this schedule to unit.
 */
function unit(self) {
    return as(undefined)(self);
}
exports.unit = unit;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilInput(f) {
    return (self) => untilInput_(self, f);
}
exports.untilInput = untilInput;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilInput_(self, f) {
    return check_(self, (i) => !f(i));
}
exports.untilInput_ = untilInput_;
/**
 * Returns a new schedule that continues until the specified effectful predicate on the input
 * evaluates to true.
 */
function untilInputM(f) {
    return (self) => untilInputM_(self, f);
}
exports.untilInputM = untilInputM;
/**
 * Returns a new schedule that continues until the specified effectful predicate on the input
 * evaluates to true.
 */
function untilInputM_(self, f) {
    return checkM_(self, (i) => T.map_(f(i), (b) => !b));
}
exports.untilInputM_ = untilInputM_;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilOutput(f) {
    return (self) => untilOutput_(self, f);
}
exports.untilOutput = untilOutput;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilOutput_(self, f) {
    return check_(self, (_, o) => !f(o));
}
exports.untilOutput_ = untilOutput_;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilOutputM(f) {
    return (self) => untilOutputM_(self, f);
}
exports.untilOutputM = untilOutputM;
/**
 * Returns a new schedule that continues until the specified predicate on the input evaluates
 * to true.
 */
function untilOutputM_(self, f) {
    return checkM_(self, (_, o) => T.map_(f(o), (b) => !b));
}
exports.untilOutputM_ = untilOutputM_;
/**
 * Returns a new schedule that continues for as long the specified predicate on the input
 * evaluates to true.
 */
function whileInput(f) {
    return (self) => whileInput_(self, f);
}
exports.whileInput = whileInput;
/**
 * Returns a new schedule that continues for as long the specified predicate on the input
 * evaluates to true.
 */
function whileInput_(self, f) {
    return check_(self, (i) => f(i));
}
exports.whileInput_ = whileInput_;
/**
 * Returns a new schedule that continues for as long the specified effectful predicate on the
 * input evaluates to true.
 */
function whileInputM(f) {
    return (self) => whileInputM_(self, f);
}
exports.whileInputM = whileInputM;
/**
 * Returns a new schedule that continues for as long the specified effectful predicate on the
 * input evaluates to true.
 */
function whileInputM_(self, f) {
    return checkM_(self, (i) => f(i));
}
exports.whileInputM_ = whileInputM_;
/**
 * Returns a new schedule that continues for as long the specified predicate on the output
 * evaluates to true.
 */
function whileOutput(f) {
    return (self) => whileOutput_(self, f);
}
exports.whileOutput = whileOutput;
/**
 * Returns a new schedule that continues for as long the specified predicate on the output
 * evaluates to true.
 */
function whileOutput_(self, f) {
    return check_(self, (_, o) => f(o));
}
exports.whileOutput_ = whileOutput_;
/**
 * Returns a new schedule that continues for as long the specified effectful predicate on the
 * output evaluates to true.
 */
function whileOutputM(f) {
    return (self) => whileOutputM_(self, f);
}
exports.whileOutputM = whileOutputM;
/**
 * Returns a new schedule that continues for as long the specified effectful predicate on the
 * output evaluates to true.
 */
function whileOutputM_(self, f) {
    return checkM_(self, (_, o) => T.map_(f(o), (b) => !b));
}
exports.whileOutputM_ = whileOutputM_;
function windowedLoop(interval, startMillis, n) {
    return (now, _) => T.succeed(O.fold_(startMillis, () => Decision.makeContinue(n + 1, now + interval, windowedLoop(interval, O.some(now), n + 1)), (startMillis) => {
        return Decision.makeContinue(n + 1, now + (interval - ((now - startMillis) % interval)), windowedLoop(interval, O.some(startMillis), n + 1));
    }));
}
/**
 * A schedule that divides the timeline to `interval`-long windows, and sleeps
 * until the nearest window boundary every time it recurs.
 *
 * For example, `windowed(10_000)` would produce a schedule as follows:
 * <pre>
 *      10s        10s        10s       10s
 * |----------|----------|----------|----------|
 * |action------|sleep---|act|-sleep|action----|
 * </pre>
 */
function windowed(interval) {
    return new Schedule(windowedLoop(interval, O.none, 0));
}
exports.windowed = windowed;
function unfoldLoop(a, f) {
    return (now, _) => T.succeed(Decision.makeContinue(a, now, unfoldLoop(f(a), f)));
}
/**
 * Unfolds a schedule that repeats one time from the specified state and iterator.
 */
function unfold(f) {
    return (a) => unfold_(a, f);
}
exports.unfold = unfold;
/**
 * Unfolds a schedule that repeats one time from the specified state and iterator.
 */
function unfold_(a, f) {
    return new Schedule((now) => T.succeedWith(() => Decision.makeContinue(a, now, unfoldLoop(f(a), f))));
}
exports.unfold_ = unfold_;
/**
 * Unfolds a schedule that repeats one time from the specified state and iterator.
 */
function unfoldM(f) {
    return (a) => unfoldM_(a, f);
}
exports.unfoldM = unfoldM;
function unfoldMLoop(a, f) {
    return (now, _) => T.succeed(Decision.makeContinue(a, now, (n, i) => T.chain_(f(a), (x) => unfoldMLoop(x, f)(n, i))));
}
/**
 * Unfolds a schedule that repeats one time from the specified state and iterator.
 */
function unfoldM_(a, f) {
    return new Schedule(unfoldMLoop(a, f));
}
exports.unfoldM_ = unfoldM_;
/**
 * Returns a new schedule that performs a geometric intersection on the intervals defined
 * by both schedules.
 */
function zip_(self, that) {
    return intersectWith_(self, that, (d, d2) => Math.max(d, d2));
}
exports.zip_ = zip_;
/**
 * Returns a new schedule that performs a geometric intersection on the intervals defined
 * by both schedules.
 */
function zip(that) {
    return (self) => intersectWith_(self, that, (d, d2) => Math.max(d, d2));
}
exports.zip = zip;
/**
 * Same as zip but ignores the right output.
 */
function zipLeft(that) {
    return (self) => zipLeft_(self, that);
}
exports.zipLeft = zipLeft;
/**
 * Same as zip but ignores the right output.
 */
function zipLeft_(self, that) {
    return map_(zip_(self, that), (_) => _.get(0));
}
exports.zipLeft_ = zipLeft_;
/**
 * Same as zip but ignores the right output.
 */
function zipRight(that) {
    return (self) => zipRight_(self, that);
}
exports.zipRight = zipRight;
/**
 * Same as zip but ignores the right output.
 */
function zipRight_(self, that) {
    return map_(zip_(self, that), (_) => _.get(1));
}
exports.zipRight_ = zipRight_;
/**
 * Equivalent to `zip` followed by `map`.
 */
function zipWith(that, f) {
    return (self) => zipWith_(self, that, f);
}
exports.zipWith = zipWith;
/**
 * Equivalent to `zip` followed by `map`.
 */
function zipWith_(self, that, f) {
    return map_(zip_(self, that), ({ tuple: [o, o1] }) => f(o, o1));
}
exports.zipWith_ = zipWith_;
//# sourceMappingURL=schedule.js.map