"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.filterMapWithIndex_ =
  exports.compact =
  exports.chainWithIndex =
  exports.chainWithIndex_ =
  exports.chain =
  exports.chain_ =
  exports.map =
  exports.map_ =
  exports.mapWithIndex =
  exports.mapWithIndex_ =
  exports.forEach =
  exports.forEach_ =
  exports.forEachWithIndex =
  exports.forEachWithIndex_ =
  exports.reduce =
  exports.reduce_ =
  exports.reduceWithIndex =
  exports.reduceWithIndex_ =
  exports.update =
  exports.update_ =
  exports.values =
  exports.keys =
  exports.visitLazy =
  exports.visitLazyChildren =
  exports.applyCont =
  exports.mutate_ =
  exports.mutate =
  exports.endMutation =
  exports.beginMutation =
  exports.remove =
  exports.remove_ =
  exports.set =
  exports.set_ =
  exports.modify =
  exports.modify_ =
  exports.modifyHash_ =
  exports.isEmpty =
  exports.has =
  exports.has_ =
  exports.hasHash_ =
  exports.get =
  exports.get_ =
  exports.unsafeGet =
  exports.unsafeGet_ =
  exports.getHash_ =
  exports.tryGetHash_ =
  exports.setTree_ =
  exports.make =
  exports.HashMapIterator =
  exports.HashMap =
    void 0
exports.removeMany =
  exports.removeMany_ =
  exports.size =
  exports.filter =
  exports.filter_ =
  exports.filterWithIndex =
  exports.filterWithIndex_ =
  exports.filterMap =
  exports.filterMap_ =
  exports.filterMapWithIndex =
    void 0
// tracing: off
require("../../../Operator")
const Function_1 = require("../../../Function")
const GlobalExceptions_1 = require("../../../GlobalExceptions")
const I = __importStar(require("../../../Iterable"))
const O = __importStar(require("../../../Option"))
const St = __importStar(require("../../../Structural"))
const Tp = __importStar(require("../Tuple"))
const Bitwise_1 = require("./Bitwise")
const Config_1 = require("./Config")
const Nodes_1 = require("./Nodes")
class HashMap {
  constructor(editable, edit, root, size) {
    this.editable = editable
    this.edit = edit
    this.root = root
    this.size = size
    this.tupleIterator = {
      [Symbol.iterator]: () => new HashMapIterator(this, ([k, v]) => Tp.tuple(k, v))
    }
  }
  [Symbol.iterator]() {
    return new HashMapIterator(this, Function_1.identity)
  }
  get [St.hashSym]() {
    return St.hashIterator(
      new HashMapIterator(this, ([k, v]) => St.combineHash(St.hash(k), St.hash(v)))
    )
  }
  [St.equalsSym](that) {
    return (
      that instanceof HashMap &&
      that.size === this.size &&
      I.corresponds(this.tupleIterator, that.tupleIterator, St.equals)
    )
  }
}
exports.HashMap = HashMap
class HashMapIterator {
  constructor(map, f) {
    this.map = map
    this.f = f
    this.v = visitLazy(this.map.root, this.f, undefined)
  }
  next() {
    if (O.isNone(this.v)) {
      return { done: true, value: undefined }
    }
    const v0 = this.v.value
    this.v = applyCont(v0.cont)
    return { done: false, value: v0.value }
  }
  [Symbol.iterator]() {
    return new HashMapIterator(this.map, this.f)
  }
}
exports.HashMapIterator = HashMapIterator
/**
 * Creates a new map
 */
function make() {
  return new HashMap(false, 0, new Nodes_1.Empty(), 0)
}
exports.make = make
/**
 * Set the root of the map
 */
function setTree_(map, newRoot, newSize) {
  if (map.editable) {
    map.root = newRoot
    map.size = newSize
    return map
  }
  return newRoot === map.root
    ? map
    : new HashMap(map.editable, map.edit, newRoot, newSize)
}
exports.setTree_ = setTree_
/**
 * Lookup the value for `key` in `map` using custom hash.
 */
function tryGetHash_(map, key, hash) {
  let node = map.root
  let shift = 0
  // eslint-disable-next-line no-constant-condition
  while (true)
    switch (node._tag) {
      case "LeafNode": {
        return St.equals(key, node.key) ? node.value : O.none
      }
      case "CollisionNode": {
        if (hash === node.hash) {
          const children = node.children
          for (let i = 0, len = children.length; i < len; ++i) {
            const child = children[i]
            if ("key" in child && St.equals(key, child.key)) return child.value
          }
        }
        return O.none
      }
      case "IndexedNode": {
        const frag = (0, Bitwise_1.hashFragment)(shift, hash)
        const bit = (0, Bitwise_1.toBitmap)(frag)
        if (node.mask & bit) {
          node = node.children[(0, Bitwise_1.fromBitmap)(node.mask, bit)]
          shift += Config_1.SIZE
          break
        }
        return O.none
      }
      case "ArrayNode": {
        node = node.children[(0, Bitwise_1.hashFragment)(shift, hash)]
        if (node) {
          shift += Config_1.SIZE
          break
        }
        return O.none
      }
      default:
        return O.none
    }
}
exports.tryGetHash_ = tryGetHash_
/**
 * Lookup the value for `key` in `map` using custom hash.
 */
function getHash_(map, key, hash) {
  return tryGetHash_(map, key, hash)
}
exports.getHash_ = getHash_
/**
 * Lookup the value for `key` in `map` using internal hash function.
 */
function unsafeGet_(map, key) {
  const element = tryGetHash_(map, key, St.hash(key))
  if (O.isNone(element)) {
    throw new GlobalExceptions_1.NoSuchElementException()
  }
  return element.value
}
exports.unsafeGet_ = unsafeGet_
/**
 * Lookup the value for `key` in `map` using internal hash function.
 *
 * @dataFirst unsafeGet_
 */
function unsafeGet(key) {
  return (map) => unsafeGet_(map, key)
}
exports.unsafeGet = unsafeGet
/**
 * Lookup the value for `key` in `map` using internal hash function.
 */
function get_(map, key) {
  return tryGetHash_(map, key, St.hash(key))
}
exports.get_ = get_
/**
 * Lookup the value for `key` in `map` using internal hash function.
 *
 * @dataFirst get_
 */
function get(key) {
  return (map) => get_(map, key)
}
exports.get = get
/**
 * Does an entry exist for `key` in `map`? Uses custom `hash`.
 */
function hasHash_(map, key, hash) {
  return O.isNone(tryGetHash_(map, key, hash))
}
exports.hasHash_ = hasHash_
/**
 * Does an entry exist for `key` in `map`? Uses internal hash function.
 */
function has_(map, key) {
  return O.isSome(tryGetHash_(map, key, St.hash(key)))
}
exports.has_ = has_
/**
 * Does an entry exist for `key` in `map`? Uses internal hash function.
 *
 * @dataFirst has_
 */
function has(key) {
  return (map) => has_(map, key)
}
exports.has = has
/**
 * Does `map` contain any elements?
 */
function isEmpty(map) {
  return map && !!(0, Nodes_1.isEmptyNode)(map.root)
}
exports.isEmpty = isEmpty
/**
 * Alter the value stored for `key` in `map` using function `f` using custom hash.
 *
 *  `f` is invoked with the current value for `k` if it exists,
 * or no arguments if no such value exists.
 *
 * `modify` will always either update or insert a value into the map.
 * Returns a map with the modified value. Does not alter `map`.
 */
function modifyHash_(map, key, hash, f) {
  const size = { value: map.size }
  const newRoot = map.root.modify(map.editable ? map.edit : NaN, 0, f, hash, key, size)
  return setTree_(map, newRoot, size.value)
}
exports.modifyHash_ = modifyHash_
/**
 * Alter the value stored for `key` in `map` using function `f` using internal hash function.
 *
 *  `f` is invoked with the current value for `k` if it exists,
 * or no arguments if no such value exists.
 *
 * `modify` will always either update or insert a value into the map.
 * Returns a map with the modified value. Does not alter `map`.
 */
function modify_(map, key, f) {
  return modifyHash_(map, key, St.hash(key), f)
}
exports.modify_ = modify_
/**
 * Alter the value stored for `key` in `map` using function `f` using internal hash function.
 *
 *  `f` is invoked with the current value for `k` if it exists,
 * or no arguments if no such value exists.
 *
 * `modify` will always either update or insert a value into the map.
 * Returns a map with the modified value. Does not alter `map`.
 *
 * @dataFirst modify_
 */
function modify(key, f) {
  return (map) => modify_(map, key, f)
}
exports.modify = modify
/**
 * Store `value` for `key` in `map` using internal hash function.
 */
function set_(map, key, value) {
  return modify_(map, key, (0, Function_1.constant)(O.some(value)))
}
exports.set_ = set_
/**
 * Store `value` for `key` in `map` using internal hash function.
 *
 * @dataFirst set_
 */
function set(key, value) {
  return (map) => set_(map, key, value)
}
exports.set = set
/**
 * Remove the entry for `key` in `map` using internal hash.
 */
function remove_(map, key) {
  return modify_(map, key, (0, Function_1.constant)(O.none))
}
exports.remove_ = remove_
/**
 * Remove the entry for `key` in `map` using internal hash.
 *
 * @dataFirst remove_
 */
function remove(key) {
  return (map) => remove_(map, key)
}
exports.remove = remove
/**
 * Mark `map` as mutable.
 */
function beginMutation(map) {
  return new HashMap(true, map.edit + 1, map.root, map.size)
}
exports.beginMutation = beginMutation
/**
 * Mark `map` as immutable.
 */
function endMutation(map) {
  map.editable = false
  return map
}
exports.endMutation = endMutation
/**
 * Mutate `map` within the context of `f`.
 *
 * @dataFirst mutate_
 */
function mutate(f) {
  return (map) => mutate_(map, f)
}
exports.mutate = mutate
/**
 * Mutate `map` within the context of `f`.
 */
function mutate_(map, f) {
  const transient = beginMutation(map)
  f(transient)
  return endMutation(transient)
}
exports.mutate_ = mutate_
function applyCont(cont) {
  return cont ? visitLazyChildren(cont[0], cont[1], cont[2], cont[3], cont[4]) : O.none
}
exports.applyCont = applyCont
function visitLazyChildren(len, children, i, f, cont) {
  while (i < len) {
    const child = children[i++]
    if (child && !(0, Nodes_1.isEmptyNode)(child)) {
      return visitLazy(child, f, [len, children, i, f, cont])
    }
  }
  return applyCont(cont)
}
exports.visitLazyChildren = visitLazyChildren
/**
 * Visit each leaf lazily
 */
function visitLazy(node, f, cont = undefined) {
  switch (node._tag) {
    case "LeafNode": {
      return O.isSome(node.value)
        ? O.some({
            value: f((0, Function_1.tuple)(node.key, node.value.value)),
            cont
          })
        : applyCont(cont)
    }
    case "CollisionNode":
    case "ArrayNode":
    case "IndexedNode": {
      const children = node.children
      return visitLazyChildren(children.length, children, 0, f, cont)
    }
    default: {
      return applyCont(cont)
    }
  }
}
exports.visitLazy = visitLazy
/**
 * Get an IterableIterator of the map keys
 */
function keys(map) {
  return new HashMapIterator(map, ([k]) => k)
}
exports.keys = keys
/**
 * Get an IterableIterator of the map values
 */
function values(map) {
  return new HashMapIterator(map, ([, v]) => v)
}
exports.values = values
/**
 * Update a value if exists
 */
function update_(map, key, f) {
  return modify_(map, key, O.map(f))
}
exports.update_ = update_
/**
 * Update a value if exists
 *
 * @dataFirst update_
 */
function update(key, f) {
  return (map) => update_(map, key, f)
}
exports.update = update
/**
 * Reduce a state over the map entries
 */
function reduceWithIndex_(map, z, f) {
  const root = map.root
  if (root._tag === "LeafNode")
    return O.isSome(root.value) ? f(z, root.key, root.value.value) : z
  if (root._tag === "Empty") {
    return z
  }
  const toVisit = [root.children]
  let children
  while ((children = toVisit.pop())) {
    for (let i = 0, len = children.length; i < len; ) {
      const child = children[i++]
      if (child && !(0, Nodes_1.isEmptyNode)(child)) {
        if (child._tag === "LeafNode") {
          if (O.isSome(child.value)) {
            z = f(z, child.key, child.value.value)
          }
        } else toVisit.push(child.children)
      }
    }
  }
  return z
}
exports.reduceWithIndex_ = reduceWithIndex_
/**
 * Reduce a state over the map entries
 *
 * @dataFirst reduceWithIndex_
 */
function reduceWithIndex(z, f) {
  return (map) => reduceWithIndex_(map, z, f)
}
exports.reduceWithIndex = reduceWithIndex
/**
 * Reduce a state over the map entries
 */
function reduce_(map, z, f) {
  return reduceWithIndex_(map, z, (z, _, v) => f(z, v))
}
exports.reduce_ = reduce_
/**
 * Reduce a state over the map entries
 *
 * @dataFirst reduce_
 */
function reduce(z, f) {
  return (map) => reduce_(map, z, f)
}
exports.reduce = reduce
/**
 * Apply f to each element
 */
function forEachWithIndex_(map, f) {
  reduceWithIndex_(map, undefined, (_, key, value) => f(key, value))
}
exports.forEachWithIndex_ = forEachWithIndex_
/**
 * Apply f to each element
 *
 * @dataFirst forEachWithIndex_
 */
function forEachWithIndex(f) {
  return (map) => forEachWithIndex_(map, f)
}
exports.forEachWithIndex = forEachWithIndex
/**
 * Apply f to each element
 */
function forEach_(map, f) {
  forEachWithIndex_(map, (_, value) => f(value))
}
exports.forEach_ = forEach_
/**
 * Apply f to each element
 *
 * @dataFirst forEach_
 */
function forEach(f) {
  return (map) => forEach_(map, f)
}
exports.forEach = forEach
/**
 * Maps over the map entries
 */
function mapWithIndex_(map, f) {
  return reduceWithIndex_(map, make(), (z, k, v) => set_(z, k, f(k, v)))
}
exports.mapWithIndex_ = mapWithIndex_
/**
 * Maps over the map entries
 *
 * @dataFirst mapWithIndex_
 */
function mapWithIndex(f) {
  return (map) => mapWithIndex_(map, f)
}
exports.mapWithIndex = mapWithIndex
/**
 * Maps over the map entries
 */
function map_(map, f) {
  return reduceWithIndex_(map, make(), (z, k, v) => set_(z, k, f(v)))
}
exports.map_ = map_
/**
 * Maps over the map entries
 *
 * @dataFirst map_
 */
function map(f) {
  return (map) => map_(map, f)
}
exports.map = map
/**
 * Chain over the map entries, the hash and equal of the 2 maps has to be the same
 */
function chain_(map, f) {
  return reduceWithIndex_(map, make(), (z, _, v) =>
    mutate_(z, (m) => {
      forEachWithIndex_(f(v), (_k, _a) => {
        set_(m, _k, _a)
      })
    })
  )
}
exports.chain_ = chain_
/**
 * Chain over the map entries, the hash and equal of the 2 maps has to be the same
 *
 * @dataFirst chain_
 */
function chain(f) {
  return (map) => chain_(map, f)
}
exports.chain = chain
/**
 * Chain over the map entries, the hash and equal of the 2 maps has to be the same
 */
function chainWithIndex_(map, f) {
  return reduceWithIndex_(map, make(), (z, k, v) =>
    mutate_(z, (m) => {
      forEachWithIndex_(f(k, v), (_k, _a) => {
        set_(m, _k, _a)
      })
    })
  )
}
exports.chainWithIndex_ = chainWithIndex_
/**
 * Chain over the map entries, the hash and equal of the 2 maps has to be the same
 *
 * @dataFirst chainWithIndex_
 */
function chainWithIndex(f) {
  return (map) => chainWithIndex_(map, f)
}
exports.chainWithIndex = chainWithIndex
/**
 * Removes None values
 */
function compact(fa) {
  return filterMapWithIndex_(fa, (_, a) => a)
}
exports.compact = compact
/**
 * Filter out None and map
 */
function filterMapWithIndex_(fa, f) {
  const m = make()
  return mutate_(m, (m) => {
    for (const [k, a] of fa) {
      const o = f(k, a)
      if (O.isSome(o)) {
        set_(m, k, o.value)
      }
    }
  })
}
exports.filterMapWithIndex_ = filterMapWithIndex_
/**
 * Filter out None and map
 *
 * @dataFirst filterMapWithIndex_
 */
function filterMapWithIndex(f) {
  return (fa) => filterMapWithIndex_(fa, f)
}
exports.filterMapWithIndex = filterMapWithIndex
/**
 * Filter out None and map
 */
function filterMap_(fa, f) {
  return filterMapWithIndex_(fa, (_, a) => f(a))
}
exports.filterMap_ = filterMap_
/**
 * Filter out None and map
 *
 * @dataFirst filterMap_
 */
function filterMap(f) {
  return (fa) => filterMap_(fa, f)
}
exports.filterMap = filterMap
/**
 * Filter out by predicate
 */
function filterWithIndex_(fa, p) {
  const m = make()
  return mutate_(m, (m) => {
    for (const [k, a] of fa) {
      if (p(k, a)) {
        set_(m, k, a)
      }
    }
  })
}
exports.filterWithIndex_ = filterWithIndex_
/**
 * Filter out by predicate
 *
 * @dataFirst filterWithIndex_
 */
function filterWithIndex(p) {
  return (fa) => filterWithIndex_(fa, p)
}
exports.filterWithIndex = filterWithIndex
function filter_(fa, p) {
  return filterWithIndex_(fa, (_, a) => p(a))
}
exports.filter_ = filter_
function filter(p) {
  return (fa) => filter_(fa, p)
}
exports.filter = filter
/**
 * Calculate the number of key/value pairs in a map
 */
function size(map) {
  return map.size
}
exports.size = size
/**
 * Remove many keys
 */
function removeMany_(self, ks) {
  return mutate_(self, (m) => {
    for (const k of ks) {
      remove_(m, k)
    }
  })
}
exports.removeMany_ = removeMany_
/**
 * Remove many keys
 *
 * @dataFirst removeMany_
 */
function removeMany(ks) {
  return (self) => removeMany_(self, ks)
}
exports.removeMany = removeMany
//# sourceMappingURL=core.js.map
