"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.foldAll = exports.foldAll_ = exports.fold = exports.fold_ = exports.makeCommit = exports.makeCommitWith = exports.unsafeMake = exports.make = exports.makeWith = exports.concrete = exports.updateAndGet = exports.updateAndGet_ = exports.updateSomeAndGet = exports.updateSomeAndGet_ = exports.updateSome = exports.updateSome_ = exports.update = exports.update_ = exports.set = exports.getAndUpdateSome = exports.getAndUpdateSome_ = exports.getAndUpdate = exports.getAndUpdate_ = exports.getAndSet = exports.getAndSet_ = exports.modifySome = exports.modifySome_ = exports.modify = exports.modify_ = exports.set_ = exports.unsafeGet_ = exports.get = exports.DerivedAll = exports.Derived = exports.Atomic = exports.TRefTypeId = void 0;
// ets_tracing: off
require("../../Operator");
const E = __importStar(require("../../Either"));
const Function_1 = require("../../Function");
const O = __importStar(require("../../Option"));
const AtomicReference_1 = require("../../Support/AtomicReference");
const primitives_1 = require("../STM/_internal/primitives");
const STM = __importStar(require("../STM/core"));
const Entry_1 = require("../STM/Entry");
const Journal_1 = require("../STM/Journal");
const Versioned_1 = require("../STM/Versioned");
exports.TRefTypeId = Symbol();
class Atomic {
    constructor(versioned, todo) {
        this.versioned = versioned;
        this.todo = todo;
        this._typeId = exports.TRefTypeId;
        this._tag = "Atomic";
        this.atomic = this;
    }
    fold(_ea, _eb, ca, bd) {
        return new Derived(bd, ca, this, this.atomic);
    }
    foldAll(_ea, _eb, _ec, ca, bd) {
        return new DerivedAll(bd, ca, this, this.atomic);
    }
}
exports.Atomic = Atomic;
class Derived {
    constructor(getEither, setEither, value, atomic) {
        this.getEither = getEither;
        this.setEither = setEither;
        this.value = value;
        this.atomic = atomic;
        this._typeId = exports.TRefTypeId;
        this._tag = "Derived";
    }
    fold(ea, eb, ca, bd) {
        return new Derived((s) => E.fold_(this.getEither(s), (e) => E.left(eb(e)), bd), (c) => E.chain_(ca(c), (a) => E.fold_(this.setEither(a), (e) => E.left(ea(e)), E.right)), this.value, this.atomic);
    }
    foldAll(ea, eb, ec, ca, bd) {
        return new DerivedAll((s) => E.fold_(this.getEither(s), (e) => E.left(eb(e)), bd), (c) => (s) => E.chain_(E.fold_(this.getEither(s), (e) => E.left(ec(e)), ca(c)), (a) => E.fold_(this.setEither(a), (e) => E.left(ea(e)), E.right)), this.value, this.atomic);
    }
}
exports.Derived = Derived;
class DerivedAll {
    constructor(getEither, setEither, value, atomic) {
        this.getEither = getEither;
        this.setEither = setEither;
        this.value = value;
        this.atomic = atomic;
        this._typeId = exports.TRefTypeId;
        this._tag = "DerivedAll";
    }
    fold(ea, eb, ca, bd) {
        return new DerivedAll((s) => E.fold_(this.getEither(s), (e) => E.left(eb(e)), bd), (c) => (s) => E.chain_(ca(c), (a) => E.fold_(this.setEither(a)(s), (e) => E.left(ea(e)), E.right)), this.value, this.atomic);
    }
    foldAll(ea, eb, ec, ca, bd) {
        return new DerivedAll((s) => E.fold_(this.getEither(s), (e) => E.left(eb(e)), bd), (c) => (s) => E.chain_(E.fold_(this.getEither(s), (e) => E.left(ec(e)), ca(c)), (a) => E.fold_(this.setEither(a)(s), (e) => E.left(ea(e)), E.right)), this.value, this.atomic);
    }
}
exports.DerivedAll = DerivedAll;
function getOrMakeEntry(self, journal) {
    if (journal.has(self)) {
        return journal.get(self);
    }
    const entry = (0, Entry_1.makeEntry)(self, false);
    journal.set(self, entry);
    return entry;
}
/**
 * Retrieves the value of the `XTRef`.
 */
function get(self) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                return entry.use((_) => _.unsafeGet());
            });
        }
        case "Derived": {
            return STM.chain_(get(self.value), (s) => E.fold_(self.getEither(s), STM.fail, STM.succeed));
        }
        case "DerivedAll": {
            return STM.chain_(get(self.value), (s) => E.fold_(self.getEither(s), STM.fail, STM.succeed));
        }
    }
}
exports.get = get;
/**
 * Unsafely retrieves the value of the `XTRef`.
 */
function unsafeGet_(self, journal) {
    return getOrMakeEntry(self.atomic, journal).use((_) => _.unsafeGet());
}
exports.unsafeGet_ = unsafeGet_;
/**
 * Sets the value of the `XTRef`.
 */
function set_(self, a) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                return entry.use((_) => _.unsafeSet(a));
            });
        }
        case "Derived": {
            return E.fold_(self.setEither(a), STM.fail, (s) => set_(self.value, s));
        }
        case "DerivedAll": {
            return STM.absolve(modify_(self.value, (s) => E.fold_(self.setEither(a)(s), (e) => [E.leftW(e), s], (s) => [E.right(undefined), s])));
        }
    }
}
exports.set_ = set_;
/**
 * Updates the value of the variable, returning a function of the specified
 * value.
 */
function modify_(self, f) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const oldValue = entry.use((_) => _.unsafeGet());
                const [retValue, newValue] = f(oldValue);
                entry.use((_) => _.unsafeSet(newValue));
                return retValue;
            });
        }
        case "Derived": {
            return STM.absolve(modify_(self.value, (s) => E.fold_(self.getEither(s), (e) => [E.leftW(e), s], (a1) => {
                const [b, a2] = f(a1);
                return E.fold_(self.setEither(a2), (e) => [E.left(e), s], (s) => [E.right(b), s]);
            })));
        }
        case "DerivedAll": {
            return STM.absolve(modify_(self.value, (s) => E.fold_(self.getEither(s), (e) => [E.leftW(e), s], (a1) => {
                const [b, a2] = f(a1);
                return E.fold_(self.setEither(a2)(s), (e) => [E.left(e), s], (s) => [E.right(b), s]);
            })));
        }
    }
}
exports.modify_ = modify_;
/**
 * Updates the value of the variable, returning a function of the specified
 * value.
 *
 * @ets_data_first modify_
 */
function modify(f) {
    return (self) => modify_(self, f);
}
exports.modify = modify;
/**
 * Updates the value of the variable, returning a function of the specified
 * value.
 */
function modifySome_(self, b, f) {
    return modify_(self, (a) => O.fold_(f(a), () => [b, a], Function_1.identity));
}
exports.modifySome_ = modifySome_;
/**
 * Updates the value of the variable, returning a function of the specified
 * value.
 *
 * @ets_data_first modifySome_
 */
function modifySome(b, f) {
    return (self) => modifySome_(self, b, f);
}
exports.modifySome = modifySome;
/**
 * Sets the value of the `XTRef` and returns the old value.
 */
function getAndSet_(self, a) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const oldValue = entry.use((_) => _.unsafeGet());
                entry.use((_) => _.unsafeSet(a));
                return oldValue;
            });
        }
        default: {
            return modify_(self, (_) => [_, a]);
        }
    }
}
exports.getAndSet_ = getAndSet_;
/**
 * Sets the value of the `XTRef` and returns the old value.
 *
 * @ets_data_first getAndSet_
 */
function getAndSet(a) {
    return (self) => getAndSet_(self, a);
}
exports.getAndSet = getAndSet;
/**
 * Updates the value of the variable and returns the old value.
 */
function getAndUpdate_(self, f) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const oldValue = entry.use((_) => _.unsafeGet());
                entry.use((_) => _.unsafeSet(f(oldValue)));
                return oldValue;
            });
        }
        default: {
            return modify_(self, (_) => [_, f(_)]);
        }
    }
}
exports.getAndUpdate_ = getAndUpdate_;
/**
 * Updates the value of the variable and returns the old value.
 *
 * @ets_data_first getAndUpdate_
 */
function getAndUpdate(f) {
    return (self) => getAndUpdate_(self, f);
}
exports.getAndUpdate = getAndUpdate;
/**
 * Updates some values of the variable but leaves others alone, returning the
 * old value.
 */
function getAndUpdateSome_(self, f) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const oldValue = entry.use((_) => _.unsafeGet());
                const v = f(oldValue);
                if (O.isSome(v)) {
                    entry.use((_) => _.unsafeSet(v.value));
                }
                return oldValue;
            });
        }
        default: {
            return modify_(self, (_) => O.fold_(f(_), () => [_, _], (v) => [_, v]));
        }
    }
}
exports.getAndUpdateSome_ = getAndUpdateSome_;
/**
 * Updates some values of the variable but leaves others alone, returning the
 * old value.
 *
 * @ets_data_first getAndUpdateSome_
 */
function getAndUpdateSome(f) {
    return (self) => getAndUpdateSome_(self, f);
}
exports.getAndUpdateSome = getAndUpdateSome;
/**
 * Sets the value of the `XTRef`.
 *
 * @ets_data_first set_
 */
function set(a) {
    return (self) => set_(self, a);
}
exports.set = set;
/**
 * Updates the value of the variable.
 */
function update_(self, f) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const newValue = f(entry.use((_) => _.unsafeGet()));
                entry.use((_) => _.unsafeSet(newValue));
            });
        }
        default:
            return modify_(self, (a) => [undefined, f(a)]);
    }
}
exports.update_ = update_;
/**
 * Updates the value of the variable.
 *
 * @ets_data_first update_
 */
function update(f) {
    return (self) => update_(self, f);
}
exports.update = update;
/**
 * Updates some values of the variable but leaves others alone.
 */
function updateSome_(self, f) {
    return update_(self, (a) => O.fold_(f(a), () => a, Function_1.identity));
}
exports.updateSome_ = updateSome_;
/**
 * Updates some values of the variable but leaves others alone.
 *
 * @ets_data_first updateSome_
 */
function updateSome(f) {
    return (self) => updateSome_(self, f);
}
exports.updateSome = updateSome;
/**
 * Updates some values of the variable but leaves others alone.
 */
function updateSomeAndGet_(self, f) {
    return updateAndGet_(self, (a) => O.fold_(f(a), () => a, Function_1.identity));
}
exports.updateSomeAndGet_ = updateSomeAndGet_;
/**
 * Updates some values of the variable but leaves others alone.
 *
 * @ets_data_first updateSomeAndGet_
 */
function updateSomeAndGet(f) {
    return (self) => updateSomeAndGet_(self, f);
}
exports.updateSomeAndGet = updateSomeAndGet;
/**
 * Updates the value of the variable and returns the new value.
 */
function updateAndGet_(self, f) {
    ;
    switch (self._tag) {
        case "Atomic": {
            return new primitives_1.STMEffect((journal) => {
                const entry = getOrMakeEntry(self, journal);
                const oldValue = entry.use((_) => _.unsafeGet());
                const x = f(oldValue);
                entry.use((_) => _.unsafeSet(x));
                return x;
            });
        }
        default: {
            return modify_(self, (_) => {
                const x = f(_);
                return [x, x];
            });
        }
    }
}
exports.updateAndGet_ = updateAndGet_;
/**
 * Updates the value of the variable and returns the new value.
 *
 * @ets_data_first getAndUpdate_
 */
function updateAndGet(f) {
    return (self) => updateAndGet_(self, f);
}
exports.updateAndGet = updateAndGet;
/**
 * @ets_optimize remove
 */
function concrete(_) {
    //
}
exports.concrete = concrete;
/**
 * Makes a new `XTRef` that is initialized to the specified value.
 */
function makeWith(a) {
    return new primitives_1.STMEffect((journal) => {
        const value = a();
        const versioned = new Versioned_1.Versioned(value);
        const todo = new AtomicReference_1.AtomicReference(Journal_1.emptyTodoMap);
        const tref = new Atomic(versioned, todo);
        journal.set(tref, (0, Entry_1.makeEntry)(tref, true));
        return tref;
    });
}
exports.makeWith = makeWith;
/**
 * Makes a new `XTRef` that is initialized to the specified value.
 */
function make(a) {
    return new primitives_1.STMEffect((journal) => {
        const value = a;
        const versioned = new Versioned_1.Versioned(value);
        const todo = new AtomicReference_1.AtomicReference(Journal_1.emptyTodoMap);
        const tref = new Atomic(versioned, todo);
        journal.set(tref, (0, Entry_1.makeEntry)(tref, true));
        return tref;
    });
}
exports.make = make;
/**
 * Unsafely makes a new `XTRef` that is initialized to the specified value.
 */
function unsafeMake(a) {
    const value = a;
    const versioned = new Versioned_1.Versioned(value);
    const todo = new AtomicReference_1.AtomicReference(Journal_1.emptyTodoMap);
    return new Atomic(versioned, todo);
}
exports.unsafeMake = unsafeMake;
/**
 * Makes a new `XTRef` that is initialized to the specified value.
 */
function makeCommitWith(a) {
    return STM.commit(makeWith(a));
}
exports.makeCommitWith = makeCommitWith;
/**
 * Makes a new `XTRef` that is initialized to the specified value.
 */
function makeCommit(a) {
    return STM.commit(make(a));
}
exports.makeCommit = makeCommit;
/**
 * Folds over the error and value types of the `XTRef`. This is a highly
 * polymorphic method that is capable of arbitrarily transforming the error
 * and value types of the `XTRef`. For most use cases one of the more
 * specific combinators implemented in terms of `fold` will be more ergonomic
 * but this method is extremely useful for implementing new combinators.
 */
function fold_(self, ea, eb, ca, bd) {
    return self.fold(ea, eb, ca, bd);
}
exports.fold_ = fold_;
/**
 * Folds over the error and value types of the `XTRef`. This is a highly
 * polymorphic method that is capable of arbitrarily transforming the error
 * and value types of the `XTRef`. For most use cases one of the more
 * specific combinators implemented in terms of `fold` will be more ergonomic
 * but this method is extremely useful for implementing new combinators.
 *
 * @ets_data_first fold_
 */
function fold(ea, eb, ca, bd) {
    return (self) => fold_(self, ea, eb, ca, bd);
}
exports.fold = fold;
/**
 * Folds over the error and value types of the `XTRef`, allowing access to
 * the state in transforming the `set` value. This is a more powerful version
 * of `fold` but requires unifying the error types.
 */
function foldAll_(self, ea, eb, ec, ca, bd) {
    return self.foldAll(ea, eb, ec, ca, bd);
}
exports.foldAll_ = foldAll_;
/**
 * Folds over the error and value types of the `XTRef`, allowing access to
 * the state in transforming the `set` value. This is a more powerful version
 * of `fold` but requires unifying the error types.
 *
 * @ets_data_first foldAll_
 */
function foldAll(ea, eb, ec, ca, bd) {
    return (self) => self.foldAll(ea, eb, ec, ca, bd);
}
exports.foldAll = foldAll;
//# sourceMappingURL=index.js.map