"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.union =
  exports.union_ =
  exports.toggle_ =
  exports.toggle =
  exports.reduce =
  exports.reduce_ =
  exports.difference =
  exports.difference_ =
  exports.partition_ =
  exports.partition =
  exports.filter_ =
  exports.filter =
  exports.isSubset_ =
  exports.isSubset =
  exports.chain_ =
  exports.chain =
  exports.every_ =
  exports.every =
  exports.equal =
  exports.size =
  exports.some_ =
  exports.some =
  exports.map =
  exports.map_ =
  exports.intersection =
  exports.intersection_ =
  exports.forEach_ =
  exports.has_ =
  exports.values =
  exports.remove =
  exports.remove_ =
  exports.add =
  exports.add_ =
  exports.make =
  exports.SortedSet =
    void 0
// tracing: off
require("../../../Operator")
const Function_1 = require("../../../Function")
const St = __importStar(require("../../../Structural"))
const RB = __importStar(require("../RedBlackTree"))
class SortedSet {
  constructor(keyTree) {
    this.keyTree = keyTree
  }
  [Symbol.iterator]() {
    return RB.keys_(this.keyTree)
  }
  get [St.hashSym]() {
    return this.keyTree[St.hashSym]
  }
  [St.equalsSym](that) {
    return this.keyTree[St.equalsSym](that)
  }
}
exports.SortedSet = SortedSet
function make(K) {
  return new SortedSet(RB.make(K))
}
exports.make = make
function add_(set, v) {
  return RB.has_(set.keyTree, v) ? set : new SortedSet(RB.insert_(set.keyTree, v, true))
}
exports.add_ = add_
function add(v) {
  return (set) => add_(set, v)
}
exports.add = add
function remove_(set, v) {
  return new SortedSet(RB.removeFirst_(set.keyTree, v))
}
exports.remove_ = remove_
function remove(v) {
  return (set) => remove_(set, v)
}
exports.remove = remove
function values(set) {
  return RB.keys_(set.keyTree)
}
exports.values = values
function has_(set, v) {
  return RB.has_(set.keyTree, v)
}
exports.has_ = has_
/**
 * Apply f to each element
 */
function forEach_(map, f) {
  RB.forEach_(map.keyTree, (k) => {
    f(k)
  })
}
exports.forEach_ = forEach_
/**
 * The set of elements which are in both the first and second set,
 *
 * the hash and equal of the 2 sets has to be the same
 */
function intersection_(l, r) {
  let x = make(l.keyTree.ord)
  for (const k of r) {
    if (has_(l, k)) {
      x = add_(x, k)
    }
  }
  return x
}
exports.intersection_ = intersection_
/**
 * The set of elements which are in both the first and second set
 *
 * @dataFirst intersection_
 */
function intersection(r) {
  return (l) => intersection_(l, r)
}
exports.intersection = intersection
/**
 * Projects a Set through a function
 */
function map_(E) {
  return (set, f) => {
    let r = make(E)
    forEach_(set, (e) => {
      const v = f(e)
      if (!has_(r, v)) {
        r = add_(r, v)
      }
    })
    return r
  }
}
exports.map_ = map_
/**
 * Projects a Set through a function
 *
 * @dataFirst map_
 */
function map(E) {
  const m = map_(E)
  return (f) => (set) => m(set, f)
}
exports.map = map
/**
 * true if one or more elements match predicate
 *
 * @dataFirst some_
 */
function some(predicate) {
  return (set) => some_(set, predicate)
}
exports.some = some
/**
 * true if one or more elements match predicate
 */
function some_(set, predicate) {
  let found = false
  for (const e of set) {
    found = predicate(e)
    if (found) {
      break
    }
  }
  return found
}
exports.some_ = some_
/**
 * Calculate the number of keys pairs in a set
 */
function size(set) {
  return RB.size(set.keyTree)
}
exports.size = size
/**
 * Creates an equal for a set
 */
function equal() {
  return {
    equals: (x, y) => {
      if (y === x) {
        return true
      }
      if (size(x) !== size(y)) {
        return false
      }
      let eq = true
      for (const vx of x) {
        if (!has_(y, vx)) {
          eq = false
          break
        }
      }
      return eq
    }
  }
}
exports.equal = equal
/**
 * true if all elements match predicate
 *
 * @dataFirst every_
 */
function every(predicate) {
  return (set) => every_(set, predicate)
}
exports.every = every
/**
 * true if all elements match predicate
 */
function every_(set, predicate) {
  return (0, Function_1.not)(some((0, Function_1.not)(predicate)))(set)
}
exports.every_ = every_
/**
 * Map + Flatten
 *
 * @dataFirst chain_
 */
function chain(E) {
  const c = chain_(E)
  return (f) => (set) => c(set, f)
}
exports.chain = chain
/**
 * Map + Flatten
 */
function chain_(E) {
  return (set, f) => {
    let r = make(E)
    forEach_(set, (e) => {
      for (const a of f(e)) {
        if (!has_(r, a)) {
          r = add_(r, a)
        }
      }
    })
    return r
  }
}
exports.chain_ = chain_
/**
 * `true` if and only if every element in the first set is an element of the second set,
 *
 * the hash and equal of the 2 sets has to be the same
 *
 * @dataFirst isSubset_
 */
function isSubset(y) {
  return (x) => isSubset_(y, x)
}
exports.isSubset = isSubset
/**
 * `true` if and only if every element in the first set is an element of the second set,
 *
 * the hash and equal of the 2 sets has to be the same
 */
function isSubset_(x, y) {
  return every_(x, (a) => has_(y, a))
}
exports.isSubset_ = isSubset_
function filter(predicate) {
  return (set) => filter_(set, predicate)
}
exports.filter = filter
function filter_(set, predicate) {
  let r = make(set.keyTree.ord)
  const values_ = values(set)
  let e
  while (!(e = values_.next()).done) {
    const value = e.value
    if (predicate(value)) {
      r = add_(r, value)
    }
  }
  return r
}
exports.filter_ = filter_
function partition(predicate) {
  return (set) => partition_(set, predicate)
}
exports.partition = partition
function partition_(set, predicate) {
  const values_ = values(set)
  let e
  let right = make(set.keyTree.ord)
  let left = make(set.keyTree.ord)
  while (!(e = values_.next()).done) {
    const value = e.value
    if (predicate(value)) {
      right = add_(right, value)
    } else {
      left = add_(left, value)
    }
  }
  return { left, right }
}
exports.partition_ = partition_
/**
 * Form the set difference (`x` - `y`)
 */
function difference_(x, y) {
  let s = x
  for (const k of y) {
    s = remove_(s, k)
  }
  return s
}
exports.difference_ = difference_
/**
 * Form the set difference (`x` - `y`)
 *
 * @dataFirst difference_
 */
function difference(y) {
  return (x) => difference_(x, y)
}
exports.difference = difference
/**
 * Reduce a state over the map entries
 */
function reduce_(set, z, f) {
  return RB.reduceWithIndex_(set.keyTree, z, (z, v) => f(z, v))
}
exports.reduce_ = reduce_
/**
 * Reduce a state over the map entries
 *
 * @dataFirst reduce_
 */
function reduce(z, f) {
  return (set) => reduce_(set, z, f)
}
exports.reduce = reduce
/**
 * If element is present remove it, if not add it
 *
 * @dataFirst toggle_
 */
function toggle(a) {
  return (set) => toggle_(set, a)
}
exports.toggle = toggle
/**
 * If element is present remove it, if not add it
 */
function toggle_(set, a) {
  return (has_(set, a) ? remove : add)(a)(set)
}
exports.toggle_ = toggle_
/**
 * Form the union of two sets,
 *
 * the hash and equal of the 2 sets has to be the same
 */
function union_(l, r) {
  let x = make(l.keyTree.ord)
  forEach_(l, (a) => {
    x = add_(x, a)
  })
  for (const a of r) {
    x = add_(x, a)
  }
  return x
}
exports.union_ = union_
/**
 * Form the union of two sets,
 *
 * the hash and equal of the 2 sets has to be the same
 *
 * @dataFirst union_
 */
function union(y) {
  return (x) => union_(x, y)
}
exports.union = union
//# sourceMappingURL=index.js.map
