flag.pony

interface val Flag[A: (Unsigned & Integer[A] val)]
  """
  A flag should be a primitive with a value method that returns the bits that
  represent the flag. This allows a flag to encode a single bit, or any
  combination of bits.
  """
  fun value(): A

class Flags[A: Flag[B] val, B: (Unsigned & Integer[B] val) = U64] is
  Comparable[Flags[A, B] box]
  """
  Flags is a set of flags. The flags that are recognised should be passed as
  a union type for type parameter A. For example:

  primitive SSE
    fun value(): U64 => 1

  primitive AVX
    fun value(): U64 => 2

  primitive RDTSCP
    fun value(): U64 => 4

  type Features is Flags[(SSE | AVX | RDTSCP)]

  Type parameter B is the underlying field used to store the flags.
  """
  var _value: B = 0

  new iso create(value': B = 0) =>
    """
    Create a Flags instance with an optional initial value.
    Default is 0 (no flags set).
    """
    _value = value'

  fun value(): B =>
    """
    Returns the bit encoding of the set flags.
    """
    _value

  fun apply(flag: A): Bool =>
    """
    Returns true if the flag is set.
    """
    (_value and flag.value()) > 0

  fun ref all() =>
    """
    Sets all bits, including undefined flags.
    """
    _value = -1

  fun ref clear() =>
    """
    Unsets all flags.
    """
    _value = 0

  fun ref set(flag: A) =>
    """
    Sets the flag.
    """
    _value = _value or flag.value()

  fun ref unset(flag: A) =>
    """
    Unsets the flag.
    """
    _value = _value and not flag.value()

  fun ref flip(flag: A) =>
    """
    Sets the flag if it is unset, unsets the flag if it is set.
    """
    _value = _value xor flag.value()

  fun ref union(that: Flags[A, B] box) =>
    """
    The union of this and that.
    """
    _value = this._value or that._value

  fun ref intersect(that: Flags[A, B] box) =>
    """
    The intersection of this and that.
    """
    _value = this._value and that._value

  fun ref difference(that: Flags[A, B] box) =>
    """
    The symmetric difference of this and that.
    """
    _value = this._value xor that._value

  fun ref remove(that: Flags[A, B] box) =>
    """
    Unset flags that are set in that.
    """
    _value = this._value and not that._value

  fun add(flag: A): Flags[A, B] iso^ =>
    """
    This with the flag set.
    """
    let f = recover Flags[A, B] end
    f._value = this._value or flag.value()
    f

  fun sub(flag: A): Flags[A, B] iso^ =>
    """
    This with the flag unset.
    """
    let f = recover Flags[A, B] end
    f._value = this._value and not flag.value()
    f

  fun op_or(that: Flags[A, B] box): Flags[A, B] iso^ =>
    """
    The union of this and that.
    """
    let f = recover Flags[A, B] end
    f._value = this._value or that._value
    f

  fun op_and(that: Flags[A, B] box): Flags[A, B] iso^ =>
    """
    The intersection of this and that.
    """
    let f = recover Flags[A, B] end
    f._value = this._value and that._value
    f

  fun op_xor(that: Flags[A, B] box): Flags[A, B] iso^ =>
    """
    The symmetric difference of this and that.
    """
    let f = recover Flags[A, B] end
    f._value = this._value xor that._value
    f

  fun without(that: Flags[A, B] box): Flags[A, B] iso^ =>
    """
    The flags in this that are not in that.
    """
    let f = recover Flags[A, B] end
    f._value = this._value and not that._value
    f

  fun clone(): Flags[A, B] iso^ =>
    """
    Create a clone.
    """
    let f = recover Flags[A, B] end
    f._value = this._value
    f

  fun eq(that: Flags[A, B] box): Bool =>
    """
    Returns true if this has the same flags set as that.
    """
    _value == that._value

  fun lt(that: Flags[A, B] box): Bool =>
    """
    Returns true if the flags set on this are a strict subset of the flags set
    on that. Flags is only partially ordered, so lt is not the opposite of ge.
    """
    (_value != that._value) and ((_value and not that._value) == 0)

  fun le(that: Flags[A, B] box): Bool =>
    """
    Returns true if the flags set on this are a subset of the flags set on
    that or they are the same. Flags is only partially ordered, so le is not
    the opposite of te.
    """
    ((_value and not that._value) == 0)

  fun gt(that: Flags[A, B] box): Bool =>
    """
    Returns true if the flags set on this are a struct superset of the flags
    set on that. Flags is only partially ordered, so gt is not the opposite of
    le.
    """
    (_value != that._value) and ((that._value and not _value) == 0)

  fun ge(that: Flags[A, B] box): Bool =>
    """
    Returns true if the flags set on this are a superset of the flags set on
    that or they are the same. Flags is only partially ordered, so ge is not
    the opposite of lt.
    """
    ((that._value and not _value) == 0)