"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.untracedSafe = exports.untraced = exports.stripInterruptsSafe = exports.stripInterrupts = exports.stripFailuresSafe = exports.stripFailures = exports.squash = exports.sequenceCauseOption = exports.sequenceCauseOptionSafe = exports.sequenceCauseEitherSafe = exports.sequenceCauseEither = exports.keepDefects = exports.keepDefectsSafe = exports.interruptors = exports.interruptedOnly = exports.interruptOption = exports.interrupted = exports.reduceLeft = exports.foldSafe = exports.fold = exports.flatten = exports.findSafe = exports.find = exports.stripSomeDefectsSafe = exports.stripSomeDefects_ = exports.stripSomeDefects = exports.failures = exports.failureOrCause = exports.failed = exports.failureOption = exports.died = exports.dieOption = exports.defects = exports.containsSafe = exports.contains = exports.map = exports.map_ = exports.chainSafe_ = exports.chain = exports.chain_ = exports.as = exports.ap = exports.isEmpty = exports.traced = exports.then = exports.interrupt = exports.fail = exports.empty = exports.die = exports.both = void 0;
exports.flipCauseOption = void 0;
// ets_tracing: off
/* eslint-disable prefer-const */
const A = __importStar(require("../Collections/Immutable/Array"));
const E = __importStar(require("../Either"));
const Function_1 = require("../Function");
const S = __importStar(require("../IO"));
const O = __importStar(require("../Option"));
const Stack_1 = require("../Stack");
const cause_1 = require("./cause");
const errors_1 = require("./errors");
var cause_2 = require("./cause");
Object.defineProperty(exports, "both", { enumerable: true, get: function () { return cause_2.both; } });
Object.defineProperty(exports, "die", { enumerable: true, get: function () { return cause_2.die; } });
Object.defineProperty(exports, "empty", { enumerable: true, get: function () { return cause_2.empty; } });
Object.defineProperty(exports, "fail", { enumerable: true, get: function () { return cause_2.fail; } });
Object.defineProperty(exports, "interrupt", { enumerable: true, get: function () { return cause_2.interrupt; } });
Object.defineProperty(exports, "then", { enumerable: true, get: function () { return cause_2.then; } });
Object.defineProperty(exports, "traced", { enumerable: true, get: function () { return cause_2.traced; } });
Object.defineProperty(exports, "isEmpty", { enumerable: true, get: function () { return cause_2.isEmpty; } });
/**
 * Applicative's ap
 */
function ap(fa) {
    return chain((f) => (map(f)(fa)));
}
exports.ap = ap;
/**
 * Substitute the E in the cause
 */
function as(e) {
    return map(() => e);
}
exports.as = as;
/**
 * Builds a Cause depending on the result of another
 */
function chain_(cause, f) {
    return S.run(chainSafe_(cause, f));
}
exports.chain_ = chain_;
/**
 * Builds a Cause depending on the result of another
 */
function chain(f) {
    return (cause) => chain_(cause, f);
}
exports.chain = chain;
/**
 * Builds a Cause depending on the result of another
 */
function chainSafe_(cause, f) {
    switch (cause._tag) {
        case "Empty": {
            return S.succeed(cause_1.empty);
        }
        case "Fail": {
            return S.succeed(f(cause.value));
        }
        case "Die": {
            return S.succeed(cause);
        }
        case "Interrupt": {
            return S.succeed(cause);
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => chainSafe_(cause.left, f)), S.suspend(() => chainSafe_(cause.right, f)), (l, r) => (0, cause_1.then)(l, r));
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => chainSafe_(cause.left, f)), S.suspend(() => chainSafe_(cause.right, f)), (l, r) => (0, cause_1.both)(l, r));
        }
        case "Traced": {
            return S.map_(chainSafe_(cause.cause, f), (x) => (0, cause_1.traced)(x, cause.trace));
        }
    }
}
exports.chainSafe_ = chainSafe_;
/**
 * Equivalent to chain((a) => Fail(f(a)))
 */
function map_(cause, f) {
    return chain_(cause, (e) => (0, cause_1.fail)(f(e)));
}
exports.map_ = map_;
/**
 * Equivalent to chain((a) => Fail(f(a)))
 */
function map(f) {
    return (cause) => map_(cause, f);
}
exports.map = map;
/**
 * Determines if this cause contains or is equal to the specified cause.
 */
function contains(that) {
    return (cause) => S.run(containsSafe(that)(cause));
}
exports.contains = contains;
/**
 * Determines if this cause contains or is equal to the specified cause.
 */
function containsSafe(that) {
    return (cause) => S.gen(function* (_) {
        if (yield* _(cause.equalsSafe(that))) {
            return true;
        }
        return yield* _((reduceLeft(S.succeed(false))((_, c) => O.some(S.chain_(_, (b) => (b ? S.succeed(b) : c.equalsSafe(that)))))(cause)));
    });
}
exports.containsSafe = containsSafe;
/**
 * Extracts a list of non-recoverable errors from the `Cause`.
 */
function defects(cause) {
    return (reduceLeft([])((a, c) => c._tag === "Die" ? O.some([...a, c.value]) : O.none)(cause));
}
exports.defects = defects;
/**
 * Returns the `Error` associated with the first `Die` in this `Cause` if
 * one exists.
 */
function dieOption(cause) {
    return (find((c) => (c._tag === "Die" ? O.some(c.value) : O.none))(cause));
}
exports.dieOption = dieOption;
/**
 * Returns if a cause contains a defect
 */
function died(cause) {
    return (O.getOrElse_(O.map_(dieOption(cause), () => true), () => false));
}
exports.died = died;
/**
 * Returns the `E` associated with the first `Fail` in this `Cause` if one
 * exists.
 */
function failureOption(cause) {
    return (find((c) => (c._tag === "Fail" ? O.some(c.value) : O.none))(cause));
}
exports.failureOption = failureOption;
/**
 * Returns if the cause has a failure in it
 */
function failed(cause) {
    return (O.getOrElse_(O.map_(failureOption(cause), () => true), () => false));
}
exports.failed = failed;
/**
 * Retrieve the first checked error on the `Left` if available,
 * if there are no checked errors return the rest of the `Cause`
 * that is known to contain only `Die` or `Interrupt` causes.
 * */
function failureOrCause(cause) {
    return (O.getOrElse_(O.map_(failureOption(cause), E.left), () => E.right(cause)));
}
exports.failureOrCause = failureOrCause;
/**
 * Produces a list of all recoverable errors `E` in the `Cause`.
 */
function failures(cause) {
    return (reduceLeft([])((a, c) => c._tag === "Fail" ? O.some([...a, c.value]) : O.none)(cause));
}
exports.failures = failures;
/**
 * Remove all `Die` causes that the specified partial function is defined at,
 * returning `Some` with the remaining causes or `None` if there are no
 * remaining causes.
 */
function stripSomeDefects(f) {
    return (cause) => {
        return S.run(stripSomeDefectsSafe(cause, f));
    };
}
exports.stripSomeDefects = stripSomeDefects;
/**
 * Remove all `Die` causes that the specified partial function is defined at,
 * returning `Some` with the remaining causes or `None` if there are no
 * remaining causes.
 */
function stripSomeDefects_(cause, f) {
    return S.run(stripSomeDefectsSafe(cause, f));
}
exports.stripSomeDefects_ = stripSomeDefects_;
/**
 * Filter out all `Die` causes according to the specified function,
 * returning `Some` with the remaining causes or `None` if there are no
 * remaining causes.
 */
function stripSomeDefectsSafe(cause, f) {
    switch (cause._tag) {
        case "Empty": {
            return S.succeed(O.none);
        }
        case "Interrupt": {
            return S.succeed(O.some(cause));
        }
        case "Fail": {
            return S.succeed(O.some(cause));
        }
        case "Die": {
            return S.succeed(O.map_(f(cause.value), cause_1.die));
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => stripSomeDefectsSafe(cause.left, f)), S.suspend(() => stripSomeDefectsSafe(cause.right, f)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.both)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return l;
                }
                else if (r._tag === "Some") {
                    return r;
                }
                else {
                    return O.none;
                }
            });
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => stripSomeDefectsSafe(cause.left, f)), S.suspend(() => stripSomeDefectsSafe(cause.right, f)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.then)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return l;
                }
                else if (r._tag === "Some") {
                    return r;
                }
                else {
                    return O.none;
                }
            });
        }
        case "Traced": {
            return S.suspend(() => stripSomeDefectsSafe(cause.cause, f));
        }
    }
}
exports.stripSomeDefectsSafe = stripSomeDefectsSafe;
/**
 * Finds the first result matching f
 */
function find(f) {
    return (cause) => S.run(findSafe(f)(cause));
}
exports.find = find;
/**
 * Finds the first result matching f
 */
function findSafe(f) {
    return (cause) => {
        const apply = f(cause);
        if (apply._tag === "Some") {
            return S.succeed(apply);
        }
        switch (cause._tag) {
            case "Then": {
                return S.chain_(S.suspend(() => findSafe(f)(cause.left)), (isLeft) => {
                    if (isLeft._tag === "Some") {
                        return S.succeed(isLeft);
                    }
                    else {
                        return findSafe(f)(cause.right);
                    }
                });
            }
            case "Traced": {
                return S.suspend(() => findSafe(f)(cause.cause));
            }
            case "Both": {
                return S.chain_(S.suspend(() => findSafe(f)(cause.left)), (isLeft) => {
                    if (isLeft._tag === "Some") {
                        return S.succeed(isLeft);
                    }
                    else {
                        return findSafe(f)(cause.right);
                    }
                });
            }
            default: {
                return S.succeed(apply);
            }
        }
    };
}
exports.findSafe = findSafe;
/**
 * Equivalent to chain(identity)
 */
exports.flatten = chain(Function_1.identity);
/**
 * Folds over a cause
 */
function fold(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase) {
    return (cause) => S.run(foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause));
}
exports.fold = fold;
/**
 * Folds over a cause
 */
function foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase) {
    return (cause) => {
        switch (cause._tag) {
            case "Empty": {
                return S.succeedWith(empty);
            }
            case "Fail": {
                return S.succeed(failCase(cause.value));
            }
            case "Die": {
                return S.succeed(dieCase(cause.value));
            }
            case "Interrupt": {
                return S.succeed(interruptCase(cause.fiberId));
            }
            case "Traced": {
                return S.map_(S.suspend(() => foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause.cause)), (x) => tracedCase(x, cause.trace));
            }
            case "Both": {
                return S.zipWith_(S.suspend(() => foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause.left)), S.suspend(() => foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause.right)), (l, r) => bothCase(l, r));
            }
            case "Then": {
                return S.zipWith_(S.suspend(() => foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause.left)), S.suspend(() => foldSafe(empty, failCase, dieCase, interruptCase, thenCase, bothCase, tracedCase)(cause.right)), (l, r) => thenCase(l, r));
            }
        }
    };
}
exports.foldSafe = foldSafe;
/**
 * Accumulates a state over a Cause
 */
function reduceLeft(z) {
    return (f) => {
        return (cause) => {
            let causes = undefined;
            let current = cause;
            let acc = z;
            while (current) {
                const x = f(acc, current);
                acc = x._tag === "Some" ? x.value : acc;
                switch (current._tag) {
                    case "Then": {
                        causes = new Stack_1.Stack(current.right, causes);
                        current = current.left;
                        break;
                    }
                    case "Both": {
                        causes = new Stack_1.Stack(current.right, causes);
                        current = current.left;
                        break;
                    }
                    case "Traced": {
                        current = current.cause;
                        break;
                    }
                    default: {
                        current = undefined;
                        break;
                    }
                }
                if (!current && causes) {
                    current = causes.value;
                    causes = causes.previous;
                }
            }
            return acc;
        };
    };
}
exports.reduceLeft = reduceLeft;
/**
 * Returns if the cause contains an interruption in it
 */
function interrupted(cause) {
    return (O.getOrElse_(O.map_(interruptOption(cause), () => true), () => false));
}
exports.interrupted = interrupted;
/**
 * Returns the `FiberID` associated with the first `Interrupt` in this `Cause` if one
 * exists.
 */
function interruptOption(cause) {
    return (find((c) => (c._tag === "Interrupt" ? O.some(c.fiberId) : O.none))(cause));
}
exports.interruptOption = interruptOption;
/**
 * Determines if the `Cause` contains only interruptions and not any `Die` or
 * `Fail` causes.
 */
function interruptedOnly(cause) {
    return (O.getOrElse_(find((c) => (c._tag === "Die" || c._tag === "Fail" ? O.some(false) : O.none))(cause), () => true));
}
exports.interruptedOnly = interruptedOnly;
/**
 * Returns a set of interruptors, fibers that interrupted the fiber described
 * by this `Cause`.
 */
function interruptors(cause) {
    return Array.from((reduceLeft(new Set())((s, c) => c._tag === "Interrupt" ? O.some(s.add(c.fiberId)) : O.none)(cause)));
}
exports.interruptors = interruptors;
/**
 * Remove all `Fail` and `Interrupt` nodes from this `Cause`,
 * return only `Die` cause/finalizer defects.
 */
function keepDefectsSafe(cause) {
    switch (cause._tag) {
        case "Empty": {
            return S.succeed(O.none);
        }
        case "Fail": {
            return S.succeed(O.none);
        }
        case "Interrupt": {
            return S.succeed(O.none);
        }
        case "Die": {
            return S.succeed(O.some(cause));
        }
        case "Traced": {
            return S.map_(S.suspend(() => keepDefectsSafe(cause.cause)), (x) => O.map_(x, (_) => (0, cause_1.traced)(_, cause.trace)));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => keepDefectsSafe(cause.left)), S.suspend(() => keepDefectsSafe(cause.right)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.then)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return l;
                }
                else if (r._tag === "Some") {
                    return r;
                }
                else {
                    return O.none;
                }
            });
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => keepDefectsSafe(cause.left)), S.suspend(() => keepDefectsSafe(cause.right)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.both)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return l;
                }
                else if (r._tag === "Some") {
                    return r;
                }
                else {
                    return O.none;
                }
            });
        }
    }
}
exports.keepDefectsSafe = keepDefectsSafe;
/**
 * Remove all `Fail` and `Interrupt` nodes from this `Cause`,
 * return only `Die` cause/finalizer defects.
 */
function keepDefects(cause) {
    return S.run(keepDefectsSafe(cause));
}
exports.keepDefects = keepDefects;
/**
 * Converts the specified `Cause<Either<E, A>>` to an `Either<Cause<E>, A>`.
 */
function sequenceCauseEither(c) {
    return S.run(sequenceCauseEitherSafe(c));
}
exports.sequenceCauseEither = sequenceCauseEither;
/**
 * Converts the specified `Cause<Either<E, A>>` to an `Either<Cause<E>, A>`.
 */
function sequenceCauseEitherSafe(c) {
    switch (c._tag) {
        case "Empty": {
            return S.succeed(E.left(cause_1.empty));
        }
        case "Interrupt": {
            return S.succeed(E.left(c));
        }
        case "Fail": {
            return S.succeed(c.value._tag === "Left" ? E.left((0, cause_1.fail)(c.value.left)) : E.right(c.value.right));
        }
        case "Traced": {
            return S.map_(S.suspend(() => sequenceCauseEitherSafe(c.cause)), (x) => E.mapLeft_(x, (_) => (0, cause_1.traced)(_, c.trace)));
        }
        case "Die": {
            return S.succeed(E.left(c));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => sequenceCauseEitherSafe(c.left)), S.suspend(() => sequenceCauseEitherSafe(c.right)), (l, r) => {
                if (l._tag === "Left") {
                    if (r._tag === "Right") {
                        return E.right(r.right);
                    }
                    else {
                        return E.left((0, cause_1.then)(l.left, r.left));
                    }
                }
                else {
                    return E.right(l.right);
                }
            });
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => sequenceCauseEitherSafe(c.left)), S.suspend(() => sequenceCauseEitherSafe(c.right)), (l, r) => {
                if (l._tag === "Left") {
                    if (r._tag === "Right") {
                        return E.right(r.right);
                    }
                    else {
                        return E.left((0, cause_1.both)(l.left, r.left));
                    }
                }
                else {
                    return E.right(l.right);
                }
            });
        }
    }
}
exports.sequenceCauseEitherSafe = sequenceCauseEitherSafe;
/**
 * Converts the specified `Cause<Option<E>>` to an `Option<Cause<E>>` by
 * recursively stripping out any failures with the error `None`.
 */
function sequenceCauseOptionSafe(c) {
    switch (c._tag) {
        case "Empty": {
            return S.succeed(O.some(cause_1.empty));
        }
        case "Interrupt": {
            return S.succeed(O.some(c));
        }
        case "Traced": {
            return S.map_(S.suspend(() => sequenceCauseOptionSafe(c.cause)), (x) => O.map_(x, (_) => (0, cause_1.traced)(_, c.trace)));
        }
        case "Fail": {
            return S.succeed(O.map_(c.value, cause_1.fail));
        }
        case "Die": {
            return S.succeed(O.some(c));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => sequenceCauseOptionSafe(c.left)), S.suspend(() => sequenceCauseOptionSafe(c.right)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.then)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return O.some(l.value);
                }
                else if (r._tag === "Some") {
                    return O.some(r.value);
                }
                else {
                    return O.none;
                }
            });
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => sequenceCauseOptionSafe(c.left)), S.suspend(() => sequenceCauseOptionSafe(c.right)), (l, r) => {
                if (l._tag === "Some" && r._tag === "Some") {
                    return O.some((0, cause_1.both)(l.value, r.value));
                }
                else if (l._tag === "Some") {
                    return O.some(l.value);
                }
                else if (r._tag === "Some") {
                    return O.some(r.value);
                }
                else {
                    return O.none;
                }
            });
        }
    }
}
exports.sequenceCauseOptionSafe = sequenceCauseOptionSafe;
/**
 * Converts the specified `Cause<Option<E>>` to an `Option<Cause<E>>` by
 * recursively stripping out any failures with the error `None`.
 */
function sequenceCauseOption(c) {
    return S.run(sequenceCauseOptionSafe(c));
}
exports.sequenceCauseOption = sequenceCauseOption;
/**
 * Squashes a `Cause` down to a single `Throwable`, chosen to be the
 * "most important" `Throwable`.
 */
function squash(f) {
    return (cause) => (O.getOrElse_(((o) => (o._tag === "Some" ? o : A.head(defects(cause))))(((o) => o._tag === "Some"
        ? o
        : interrupted(cause)
            ? O.some(new errors_1.InterruptedException("Interrupted by fibers: " +
                Array.from(interruptors(cause))
                    .map((_) => _.seqNumber.toString())
                    .map((_) => "#" + _)
                    .join(", ")))
            : O.none)(O.map_(failureOption(cause), f))), () => new errors_1.InterruptedException()));
}
exports.squash = squash;
/**
 * Discards all typed failures kept on this `Cause`.
 */
function stripFailures(cause) {
    switch (cause._tag) {
        case "Empty": {
            return cause_1.empty;
        }
        case "Fail": {
            return cause_1.empty;
        }
        case "Interrupt": {
            return cause;
        }
        case "Die": {
            return cause;
        }
        default: {
            return S.run(stripFailuresSafe(cause));
        }
    }
}
exports.stripFailures = stripFailures;
/**
 * Discards all typed failures kept on this `Cause`.
 */
function stripFailuresSafe(cause) {
    switch (cause._tag) {
        case "Empty": {
            return S.succeed(cause_1.empty);
        }
        case "Fail": {
            return S.succeed(cause_1.empty);
        }
        case "Interrupt": {
            return S.succeed(cause);
        }
        case "Die": {
            return S.succeed(cause);
        }
        case "Traced": {
            return S.map_(S.suspend(() => stripFailuresSafe(cause.cause)), (x) => (0, cause_1.traced)(x, cause.trace));
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => stripFailuresSafe(cause.left)), S.suspend(() => stripFailuresSafe(cause.right)), (l, r) => (0, cause_1.both)(l, r));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => stripFailuresSafe(cause.left)), S.suspend(() => stripFailuresSafe(cause.right)), (l, r) => (0, cause_1.then)(l, r));
        }
    }
}
exports.stripFailuresSafe = stripFailuresSafe;
/**
 * Discards all typed failures kept on this `Cause`.
 */
function stripInterrupts(cause) {
    switch (cause._tag) {
        case "Empty": {
            return cause_1.empty;
        }
        case "Fail": {
            return cause;
        }
        case "Interrupt": {
            return cause_1.empty;
        }
        case "Die": {
            return cause;
        }
        default: {
            return S.run(stripInterruptsSafe(cause));
        }
    }
}
exports.stripInterrupts = stripInterrupts;
/**
 * Discards all typed failures kept on this `Cause`.
 */
function stripInterruptsSafe(cause) {
    switch (cause._tag) {
        case "Empty": {
            return S.succeed(cause_1.empty);
        }
        case "Fail": {
            return S.succeed(cause);
        }
        case "Interrupt": {
            return S.succeed(cause_1.empty);
        }
        case "Die": {
            return S.succeed(cause);
        }
        case "Traced": {
            return S.map_(S.suspend(() => stripInterruptsSafe(cause.cause)), (x) => (0, cause_1.traced)(x, cause.trace));
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => stripInterruptsSafe(cause.left)), S.suspend(() => stripInterruptsSafe(cause.right)), (l, r) => (0, cause_1.both)(l, r));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => stripInterruptsSafe(cause.left)), S.suspend(() => stripInterruptsSafe(cause.right)), (l, r) => (0, cause_1.then)(l, r));
        }
    }
}
exports.stripInterruptsSafe = stripInterruptsSafe;
/**
 * Returns a `Cause` that has been stripped of all tracing information.
 */
function untraced(cause) {
    switch (cause._tag) {
        case "Die":
        case "Empty":
        case "Fail":
        case "Interrupt":
            return cause;
        default:
            return S.run(untracedSafe(cause));
    }
}
exports.untraced = untraced;
/**
 * Returns a `Cause` that has been stripped of all tracing information.
 */
function untracedSafe(cause) {
    switch (cause._tag) {
        case "Traced": {
            return S.suspend(() => untracedSafe(cause.cause));
        }
        case "Both": {
            return S.zipWith_(S.suspend(() => untracedSafe(cause.left)), S.suspend(() => untracedSafe(cause.right)), (l, r) => (0, cause_1.both)(l, r));
        }
        case "Then": {
            return S.zipWith_(S.suspend(() => untracedSafe(cause.left)), S.suspend(() => untracedSafe(cause.right)), (l, r) => (0, cause_1.then)(l, r));
        }
        default: {
            return S.succeed(cause);
        }
    }
}
exports.untracedSafe = untracedSafe;
const FCOStackFrameDoneTypeId = Symbol();
class FCOStackFrameDone {
    constructor() {
        this._typeId = FCOStackFrameDoneTypeId;
    }
}
const FCOStackFrameTracedTypeId = Symbol();
class FCOStackFrameTraced {
    constructor(cause) {
        this.cause = cause;
        this._typeId = FCOStackFrameTracedTypeId;
    }
}
const FCOStackFrameThenLeftTypeId = Symbol();
class FCOStackFrameThenLeft {
    constructor(cause) {
        this.cause = cause;
        this._typeId = FCOStackFrameThenLeftTypeId;
    }
}
const FCOStackFrameThenRightTypeId = Symbol();
class FCOStackFrameThenRight {
    constructor(cause, leftResult) {
        this.cause = cause;
        this.leftResult = leftResult;
        this._typeId = FCOStackFrameThenRightTypeId;
    }
}
const FCOStackFrameBothLeftTypeId = Symbol();
class FCOStackFrameBothLeft {
    constructor(cause) {
        this.cause = cause;
        this._typeId = FCOStackFrameBothLeftTypeId;
    }
}
const FCOStackFrameBothRightTypeId = Symbol();
class FCOStackFrameBothRight {
    constructor(cause, leftResult) {
        this.cause = cause;
        this.leftResult = leftResult;
        this._typeId = FCOStackFrameBothRightTypeId;
    }
}
/**
 * Converts the specified `Cause<Either<E, A>>` to an `Either<Cause<E>, A>` by
 * recursively stripping out any failures with the error `None`.
 */
function flipCauseOption(c) {
    let stack = new Stack_1.Stack(new FCOStackFrameDone());
    let result;
    recursion: while (stack) {
        // eslint-disable-next-line no-constant-condition
        pushing: while (true) {
            switch (c._tag) {
                case "Empty":
                    result = O.some(cause_1.empty);
                    break pushing;
                case "Traced":
                    stack = new Stack_1.Stack(new FCOStackFrameTraced(c), stack);
                    c = c.cause;
                    continue pushing;
                case "Interrupt":
                    result = O.some((0, cause_1.interrupt)(c.fiberId));
                    break pushing;
                case "Die":
                    result = O.some(c);
                    break pushing;
                case "Fail":
                    result = O.fold_(c.value, () => O.none, (r) => O.some((0, cause_1.fail)(r)));
                    break pushing;
                case "Then":
                    stack = new Stack_1.Stack(new FCOStackFrameThenLeft(c), stack);
                    c = c.left;
                    continue pushing;
                case "Both":
                    stack = new Stack_1.Stack(new FCOStackFrameBothLeft(c), stack);
                    c = c.left;
                    continue pushing;
            }
        }
        // eslint-disable-next-line no-constant-condition
        popping: while (true) {
            const top = stack.value;
            stack = stack.previous;
            switch (top._typeId) {
                case FCOStackFrameDoneTypeId:
                    return result;
                case FCOStackFrameTracedTypeId:
                    result = O.map_(result, (_) => (0, cause_1.traced)(_, top.cause.trace));
                    continue popping;
                case FCOStackFrameThenLeftTypeId:
                    c = top.cause.right;
                    stack = new Stack_1.Stack(new FCOStackFrameThenRight(top.cause, result), stack);
                    continue recursion;
                case FCOStackFrameThenRightTypeId: {
                    const l = top.leftResult;
                    if (O.isSome(l) && O.isSome(result)) {
                        result = O.some((0, cause_1.then)(l.value, result.value));
                    }
                    if (O.isNone(l) && O.isSome(result)) {
                        result = O.some(result.value);
                    }
                    if (O.isSome(l) && O.isNone(result)) {
                        result = O.some(l.value);
                    }
                    result = O.none;
                    continue popping;
                }
                case FCOStackFrameBothLeftTypeId:
                    c = top.cause.right;
                    stack = new Stack_1.Stack(new FCOStackFrameBothRight(top.cause, result), stack);
                    continue recursion;
                case FCOStackFrameBothRightTypeId: {
                    const l = top.leftResult;
                    if (O.isSome(l) && O.isSome(result)) {
                        result = O.some((0, cause_1.both)(l.value, result.value));
                    }
                    if (O.isNone(l) && O.isSome(result)) {
                        result = O.some(result.value);
                    }
                    if (O.isSome(l) && O.isNone(result)) {
                        result = O.some(l.value);
                    }
                    result = O.none;
                    continue popping;
                }
            }
        }
    }
    throw new Error("Bug");
}
exports.flipCauseOption = flipCauseOption;
//# sourceMappingURL=core.js.map