"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.memoizeEq = exports.memoize = void 0
// tracing: off
const Tp = __importStar(require("../Collections/Immutable/Tuple"))
const Function_1 = require("../Function")
const P = __importStar(require("../Promise"))
const RefM = __importStar(require("../RefM"))
const core_1 = require("./core")
const Do = __importStar(require("./do"))
const map = __importStar(require("./map"))
const tap = __importStar(require("./tap"))
const to = __importStar(require("./to"))
/**
 * Returns a memoized version of the specified effectual function.
 */
function memoize(f, __trace) {
  return map.map_(
    RefM.makeRefM(new Map()),
    (ref) => (a) =>
      map.map_(
        Do.bind_(
          Do.bind_(Do.do, "promise", () =>
            RefM.modify((m) => {
              const memo = m.get(a)
              if (memo) {
                return (0, core_1.succeed)(Tp.tuple(memo, m))
              }
              return map.map_(
                tap.tap_(
                  Do.bind_(Do.do, "promise", () => P.make()),
                  ({ promise }) => (0, core_1.fork)(to.to_(f(a), promise))
                ),
                ({ promise }) => Tp.tuple(promise, m.set(a, promise))
              )
            })(ref)
          ),
          "b",
          ({ promise }) => P.await(promise)
        ),
        ({ b }) => b
      ),
    __trace
  )
}
exports.memoize = memoize
/**
 * Returns a memoized version of the specified effectual function.
 *
 * This variant uses the compare function to compare `A`
 */
function memoizeEq(compare) {
  return (f) =>
    map.map_(
      RefM.makeRefM(new Map()),
      (ref) => (a) =>
        map.map_(
          Do.bind_(
            Do.bind_(Do.do, "promise", () =>
              RefM.modify((m) => {
                for (const [k, v] of m) {
                  if (compare(k)(a)) {
                    return (0, core_1.succeed)(Tp.tuple(v, m))
                  }
                }
                return map.map_(
                  tap.tap_(
                    Do.bind_(Do.do, "promise", () => P.make()),
                    ({ promise }) => (0, core_1.fork)(to.to_(f(a), promise))
                  ),
                  ({ promise }) => Tp.tuple(promise, m.set(a, promise))
                )
              })(ref)
            ),
            "b",
            ({ promise }) => P.await(promise)
          ),
          ({ b }) => b
        )
    )
}
exports.memoizeEq = memoizeEq
//# sourceMappingURL=memoize.js.map
