"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 __decorate =
  (this && this.__decorate) ||
  function (decorators, target, key, desc) {
    var c = arguments.length,
      r =
        c < 3
          ? target
          : desc === null
          ? (desc = Object.getOwnPropertyDescriptor(target, key))
          : desc,
      d
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
      r = Reflect.decorate(decorators, target, key, desc)
    else
      for (var i = decorators.length - 1; i >= 0; i--)
        if ((d = decorators[i]))
          r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r
    return c > 3 && r && Object.defineProperty(target, key, r), r
  }
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
  }
var __metadata =
  (this && this.__metadata) ||
  function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
      return Reflect.metadata(k, v)
  }
var _a
var _b
Object.defineProperty(exports, "__esModule", { value: true })
exports.unit =
  exports.success =
  exports.or =
  exports.or_ =
  exports.not =
  exports.forEach =
  exports.failure =
  exports.collectAll =
  exports.any =
  exports.and =
  exports.and_ =
  exports.all =
  exports.mapM =
  exports.mapM_ =
  exports.map =
  exports.map_ =
  exports.isSuccess =
  exports.isFailure =
  exports.iff =
  exports.iff_ =
  exports.implies =
  exports.implies_ =
  exports.fold =
  exports.fold_ =
  exports.chainM =
  exports.chainM_ =
  exports.chain =
  exports.chain_ =
  exports.failures =
  exports.as =
  exports.as_ =
  exports.isBoolAlgebra =
  exports.isNot =
  exports.Not =
  exports.isOr =
  exports.Or =
  exports.isAnd =
  exports.And =
  exports.isValue =
  exports.Value =
  exports.BoolAlgebra =
  exports.concrete =
  exports.NotTypeId =
  exports.OrTypeId =
  exports.AndTypeId =
  exports.ValueTypeId =
  exports.BoolAlgebraTypeId =
    void 0
const tracing_1 = __importStar(require("../../Tracing"))
const tracing_2 = __importStar(require("../../Tracing"))
const fileName_1 = "(@effect-ts/system): _src/Testing/BoolAlgebra/index.ts"
const A = __importStar(require("../../Collections/Immutable/Array"))
const NEA = __importStar(require("../../Collections/Immutable/NonEmptyArray"))
const T = __importStar(require("../../Effect"))
const E = __importStar(require("../../Either"))
const Function_1 = require("../../Function")
const O = __importStar(require("../../Option"))
const ST = __importStar(require("../../Structural"))
const Utils_1 = require("../../Utils")
const PR = __importStar(require("../Primitives"))
exports.BoolAlgebraTypeId = Symbol()
exports.ValueTypeId = Symbol()
exports.AndTypeId = Symbol()
exports.OrTypeId = Symbol()
exports.NotTypeId = Symbol()
function concrete(_) {
  //
}
exports.concrete = concrete
/**
 * A `BoolAlgebra<A>` is a description of logical operations on values of type
 * `A`.
 */
class BoolAlgebra {
  constructor() {
    this[_a] = exports.BoolAlgebraTypeId
  }
  get [((_a = exports.BoolAlgebraTypeId), PR._A, PR._C, (_b = ST.hashSym))]() {
    return fold_(
      this,
      (a) => ST.hash(a),
      (a, b) => a & b,
      (a, b) => a | b,
      (a) => ~a
    )
  }
  ["&&"](that) {
    return and_(this, that)
  }
  ["||"](that) {
    return or_(this, that)
  }
  get ["!"]() {
    return not(this)
  }
}
__decorate(
  [
    (0, Utils_1.LazyGetter)(),
    __metadata("design:type", Number),
    __metadata("design:paramtypes", [])
  ],
  BoolAlgebra.prototype,
  _b,
  null
)
exports.BoolAlgebra = BoolAlgebra
class Value extends BoolAlgebra {
  constructor(value) {
    super()
    this.value = value
    this.typeId = exports.ValueTypeId
  }
  [ST.equalsSym](that) {
    if (isBoolAlgebra(that)) {
      return this.equal(that) || doubleNegative(this, that)
    }
    return false
  }
  get [ST.hashSym]() {
    return fold_(
      this,
      (a) => ST.hash(a),
      (a, b) => a & b,
      (a, b) => a | b,
      (a) => ~a
    )
  }
  equal(that) {
    if (isValue(that)) {
      return ST.equals(this.value, that.value)
    }
    return false
  }
}
exports.Value = Value
function isValue(a) {
  concrete(a)
  return a.typeId === exports.ValueTypeId
}
exports.isValue = isValue
class And extends BoolAlgebra {
  constructor(left, right) {
    super()
    this.left = left
    this.right = right
    this.typeId = exports.AndTypeId
  }
  [ST.equalsSym](that) {
    if (isBoolAlgebra(that)) {
      return (
        this.equal(that) ||
        this.commutative(that) ||
        symmetric(And.associative)(this, that) ||
        symmetric(And.distributive)(this, that) ||
        doubleNegative(this, that) ||
        this.deMorgansLaws(that)
      )
    }
    return false
  }
  equal(that) {
    if (isAnd(that)) {
      return ST.equals(this.left, that.left) && ST.equals(this.right, that.right)
    }
    return false
  }
  static associative(left, right) {
    if (isAnd(left) && isAnd(right)) {
      if (isAnd(left.left) && isAnd(right.right)) {
        const { left: a1, right: b1 } = left.left
        const c1 = left.right
        const { left: b2, right: c2 } = right.right
        const a2 = right.left
        return ST.equals(a1, a2) && ST.equals(b1, b2) && ST.equals(c1, c2)
      }
    }
    return false
  }
  commutative(that) {
    if (isAnd(that)) {
      const { left: al, right: bl } = this
      const { left: ar, right: br } = that
      return ST.equals(al, br) && ST.equals(bl, ar)
    }
    return false
  }
  static distributive(left, right) {
    if (isAnd(left) && isOr(right)) {
      if (isOr(left.right) && isAnd(right.left) && isAnd(right.right)) {
        const a1 = left.left
        const { left: b1, right: c1 } = left.right
        const { left: a2, right: b2 } = right.left
        const { left: a3, right: c2 } = right.right
        return (
          ST.equals(a1, a2) &&
          ST.equals(a1, a3) &&
          ST.equals(b1, b2) &&
          ST.equals(c1, c2)
        )
      }
    }
    return false
  }
  deMorgansLaws(that) {
    if (isNot(that)) {
      if (isNot(this.left) && isNot(this.right)) {
        if (isOr(that.result)) {
          const a = this.left.result
          const b = this.right.result
          const { left: c, right: d } = that.result
          return ST.equals(a, c) && ST.equals(b, d)
        }
      }
    }
    return false
  }
}
exports.And = And
function isAnd(a) {
  concrete(a)
  return a.typeId === exports.AndTypeId
}
exports.isAnd = isAnd
class Or extends BoolAlgebra {
  constructor(left, right) {
    super()
    this.left = left
    this.right = right
    this.typeId = exports.OrTypeId
  }
  [ST.equalsSym](that) {
    if (isBoolAlgebra(that)) {
      return (
        this.equal(that) ||
        this.commutative(that) ||
        symmetric(Or.associative)(this, that) ||
        symmetric(Or.distributive)(this, that) ||
        doubleNegative(this, that) ||
        this.deMorgansLaws(that)
      )
    }
    return false
  }
  equal(that) {
    if (isOr(that)) {
      return ST.equals(this.left, that.left) && ST.equals(this.right, that.right)
    }
    return false
  }
  static associative(left, right) {
    if (isOr(left) && isOr(left.left)) {
      if (isOr(right) && isOr(right.right)) {
        const { left: a1, right: b1 } = left.left
        const c1 = left.right
        const a2 = right.left
        const { left: b2, right: c2 } = right.right
        return ST.equals(a1, a2) && ST.equals(b1, b2) && ST.equals(c1, c2)
      }
    }
    return false
  }
  commutative(that) {
    if (isOr(that)) {
      const { left: al, right: bl } = this
      const { left: ar, right: br } = that
      return ST.equals(al, br) && ST.equals(bl, ar)
    }
    return false
  }
  static distributive(left, right) {
    if (isOr(left) && isAnd(left.right)) {
      if (isAnd(right) && isOr(right.left) && isOr(right.right)) {
        const a1 = left.left
        const { left: b1, right: c1 } = left.right
        const { left: a2, right: b2 } = right.left
        const { left: a3, right: c2 } = right.right
        return (
          ST.equals(a1, a2) &&
          ST.equals(a1, a3) &&
          ST.equals(b1, b2) &&
          ST.equals(c1, c2)
        )
      }
    }
    return false
  }
  deMorgansLaws(that) {
    if (isNot(this.left) && isNot(this.right)) {
      if (isNot(that) && isAnd(that.result)) {
        const a = this.left.result
        const b = this.right.result
        const { left: c, right: d } = that.result
        return ST.equals(a, c) && ST.equals(b, d)
      }
    }
    return false
  }
}
exports.Or = Or
function isOr(a) {
  concrete(a)
  return a.typeId === exports.OrTypeId
}
exports.isOr = isOr
class Not extends BoolAlgebra {
  constructor(result) {
    super()
    this.result = result
    this.typeId = exports.NotTypeId
  }
  [ST.equalsSym](that) {
    if (isBoolAlgebra(that)) {
      return this.equal(that) || doubleNegative(that, this) || this.deMorgansLaws(that)
    }
    return false
  }
  equal(that) {
    if (isNot(that)) {
      return ST.equals(this.result, that.result)
    }
    return false
  }
  deMorgansLaws(that) {
    if (isAnd(that)) {
      if (isOr(this.result) && isNot(that.left) && isNot(that.right)) {
        const { left: a, right: b } = this.result
        const c = that.left.result
        const d = that.right.result
        return ST.equals(a, c) && ST.equals(b, d)
      }
    }
    if (isOr(that)) {
      if (isAnd(this.result) && isNot(that.left) && isNot(that.right)) {
        const { left: a, right: b } = this.result
        const c = that.left.result
        const d = that.right.result
        return ST.equals(a, c) && ST.equals(b, d)
      }
    }
    return false
  }
}
exports.Not = Not
function isNot(a) {
  concrete(a)
  return a.typeId === exports.NotTypeId
}
exports.isNot = isNot
function isBoolAlgebra(a) {
  return typeof a === "object" && a !== null && exports.BoolAlgebraTypeId in a
}
exports.isBoolAlgebra = isBoolAlgebra
function doubleNegative(left, right) {
  if (isNot(right) && isNot(right.result)) {
    return ST.equals(left, right.result.result)
  }
  return false
}
function symmetric(f) {
  return (a1, a2) => f(a1, a2) || f(a2, a1)
}
/**
 * Returns a new result, with all values mapped to the specified constant.
 */
function as_(self, b) {
  return map_(self, (_) => b)
}
exports.as_ = as_
/**
 * Returns a new result, with all values mapped to the specified constant.
 */
function as(b) {
  return (self) => as_(self, b)
}
exports.as = as
/**
 * If this result is a success returns `None`. If it is a failure returns a
 * new result containing all failures that are relevant to this result being
 * a failure.
 */
function failures(self) {
  return E.fold_(
    fold_(
      self,
      (a) => E.right(success(a)),
      (l, r) => {
        if (E.isRight(l)) {
          if (E.isRight(r)) {
            return E.right(and_(l.right, r.right))
          } else {
            return E.left(r.left)
          }
        } else {
          if (E.isRight(r)) {
            return E.left(l.left)
          } else {
            return E.left(and_(l.left, r.left))
          }
        }
      },
      (l, r) => {
        if (E.isRight(l)) {
          if (E.isRight(r)) {
            return E.right(or_(l.right, r.right))
          } else {
            return E.right(l.right)
          }
        } else {
          if (E.isRight(r)) {
            return E.right(r.right)
          } else {
            return E.left(or_(l.left, r.left))
          }
        }
      },
      (r) => E.swap(r)
    ),
    (_) => O.some(_),
    (_) => O.none
  )
}
exports.failures = failures
/**
 * Returns a new result, with all values mapped to new results using the
 * specified function.
 */
function chain_(self, f) {
  return fold_(self, f, and_, or_, not)
}
exports.chain_ = chain_
/**
 * Returns a new result, with all values mapped to new results using the
 * specified function.
 */
function chain(f) {
  return (self) => chain_(self, f)
}
exports.chain = chain
/**
 * Returns a new result, with all values mapped to new results using the
 * specified effectual function.
 */
function chainM_(self, f) {
  return fold_(
    self,
    f,
    (_) => T.zipWith_(_, _, and_, fileName_1 + ":463:22"),
    (_) => T.zipWith_(_, _, or_, fileName_1 + ":464:22"),
    (_) => T.map_(_, not, fileName_1 + ":465:18")
  )
}
exports.chainM_ = chainM_
/**
 * Returns a new result, with all values mapped to new results using the
 * specified effectual function.
 */
function chainM(f) {
  return (self) => chainM_(self, f)
}
exports.chainM = chainM
/**
 * Folds over the result bottom up, first converting values to `B`
 * values, and then combining the `B` values, using the specified functions.
 */
function fold_(self, caseValue, caseAnd, caseOr, caseNot) {
  concrete(self)
  switch (self.typeId) {
    case exports.ValueTypeId:
      return caseValue(self.value)
    case exports.AndTypeId:
      return caseAnd(
        fold_(self.left, caseValue, caseAnd, caseOr, caseNot),
        fold_(self.right, caseValue, caseAnd, caseOr, caseNot)
      )
    case exports.OrTypeId:
      return caseOr(
        fold_(self.left, caseValue, caseAnd, caseOr, caseNot),
        fold_(self.right, caseValue, caseAnd, caseOr, caseNot)
      )
    case exports.NotTypeId:
      return caseNot(fold_(self.result, caseValue, caseAnd, caseOr, caseNot))
  }
}
exports.fold_ = fold_
/**
 * Folds over the result bottom up, first converting values to `B`
 * values, and then combining the `B` values, using the specified functions.
 */
function fold(caseValue, caseAnd, caseOr, caseNot) {
  return (self) => fold_(self, caseValue, caseAnd, caseOr, caseNot)
}
exports.fold = fold
function implies_(self, that) {
  return or_(not(self), that)
}
exports.implies_ = implies_
function implies(that) {
  return (self) => implies_(self, that)
}
exports.implies = implies
function iff_(self, that) {
  return and_(implies_(self, that), implies_(that, self))
}
exports.iff_ = iff_
function iff(that) {
  return (self) => iff_(self, that)
}
exports.iff = iff
/**
 * Determines whether the result is a failure, where values represent success
 * and are combined using logical conjunction, disjunction, and negation.
 */
function isFailure(self) {
  return !isSuccess(self)
}
exports.isFailure = isFailure
/**
 * Determines whether the result is a success, where values represent success
 * and are combined using logical conjunction, disjunction, and negation.
 */
function isSuccess(self) {
  return fold_(
    self,
    (_) => true,
    (a, b) => a && b,
    (a, b) => a || b,
    (a) => !a
  )
}
exports.isSuccess = isSuccess
/**
 * Returns a new result, with all values mapped by the specified function.
 */
function map_(self, f) {
  return chain_(self, (0, Function_1.flow)(f, success))
}
exports.map_ = map_
/**
 * Returns a new result, with all values mapped by the specified function.
 */
function map(f) {
  return (self) => map_(self, f)
}
exports.map = map
/**
 * Returns a new result, with all values mapped by the specified effectual
 * function.
 */
function mapM_(self, f) {
  return chainM_(self, (a) => T.map_(f(a), success, fileName_1 + ":584:37"))
}
exports.mapM_ = mapM_
/**
 * Returns a new result, with all values mapped by the specified effectual
 * function.
 */
function mapM(f) {
  return (self) => mapM_(self, f)
}
exports.mapM = mapM
/**
 * Returns a result that is the logical conjunction of all of the results in
 * the specified collection.
 */
function all(as) {
  const arr = A.from(as)
  if (A.isNonEmpty(arr)) {
    return O.some(A.reduce_(A.dropLeft_(arr, 1), arr[0], and_))
  }
  return O.none
}
exports.all = all
/**
 * Constructs a result that is the logical conjunction of two results.
 */
function and_(left, right) {
  return new And(left, right)
}
exports.and_ = and_
/**
 * Constructs a result that is the logical conjunction of two results.
 */
function and(right) {
  return (left) => and_(left, right)
}
exports.and = and
/**
 * Returns a result that is the logical disjunction of all of the results in
 * the specified collection.
 */
function any(as) {
  const arr = A.from(as)
  if (A.isNonEmpty(arr)) {
    const [init, ...rest] = arr
    return O.some(A.reduce_(rest, init, or_))
  }
  return O.none
}
exports.any = any
/**
 * Combines a collection of results to create a single result that succeeds
 * if all of the results succeed.
 */
function collectAll(as) {
  return forEach(as, Function_1.identity)
}
exports.collectAll = collectAll
/**
 * Constructs a failed result with the specified value.
 */
function failure(a) {
  return not(success(a))
}
exports.failure = failure
/**
 * Applies the function `f` to each element of the `Iterable[A]` to produce
 * a collection of results, then combines all of those results to create a
 * single result that is the logical conjunction of all of the results.
 */
function forEach(as, f) {
  const arr = A.from(as)
  if (A.isNonEmpty(arr)) {
    const result = NEA.map_(arr, f)
    return O.some(A.reduce_(NEA.tail(result), NEA.head(result), and_))
  }
  return O.none
}
exports.forEach = forEach
/**
 * Constructs a result that is the logical negation of the specified result.
 */
function not(result) {
  return new Not(result)
}
exports.not = not
/**
 * Constructs a result a that is the logical disjunction of two results.
 */
function or_(left, right) {
  return new Or(left, right)
}
exports.or_ = or_
/**
 * Constructs a result a that is the logical disjunction of two results.
 */
function or(right) {
  return (left) => or_(left, right)
}
exports.or = or
/**
 * Constructs a successful result with the specified value.
 */
function success(a) {
  return new Value(a)
}
exports.success = success
/**
 * A successful result with the unit value.
 */
exports.unit = success(undefined)
//# sourceMappingURL=index.js.map
