///|
fn validate_load_order(order : MemoryOrdering) -> Unit raise SyncError {
  match order {
    Release | AcqRel =>
      raise SyncError::InvalidMemoryOrdering(
        "load does not accept Release or AcqRel",
      )
    _ => ()
  }
}

///|
fn validate_store_order(order : MemoryOrdering) -> Unit raise SyncError {
  match order {
    Acquire | AcqRel =>
      raise SyncError::InvalidMemoryOrdering(
        "store does not accept Acquire or AcqRel",
      )
    _ => ()
  }
}

///|
fn failure_rank(order : MemoryOrdering) -> Int {
  match order {
    Relaxed => 0
    Acquire => 1
    SeqCst => 2
    Release | AcqRel => -1
  }
}

///|
fn success_failure_rank(order : MemoryOrdering) -> Int {
  match order {
    Relaxed | Release => 0
    Acquire | AcqRel => 1
    SeqCst => 2
  }
}

///|
fn validate_compare_exchange_orders(
  success : MemoryOrdering,
  failure : MemoryOrdering,
) -> Unit raise SyncError {
  let failure_level = failure_rank(failure)
  if failure_level < 0 || failure_level > success_failure_rank(success) {
    raise SyncError::InvalidMemoryOrdering(
      "compare_exchange failure ordering cannot be Release, AcqRel, or stronger than success",
    )
  }
}

///|
fn packed_exchanged(value : UInt64) -> Bool {
  value >> 32 != 0
}

///|
fn packed_observed_uint(value : UInt64) -> UInt {
  (value & 0xFFFF_FFFFUL).to_uint()
}

///|
/// Creates an atomic boolean. Defaults on operations are SeqCst.
pub fn AtomicBool::new(value : Bool) -> AtomicBool {
  native_atomic_bool_new(value)
}

///|
/// Creates an independent wrapper for use by another OS thread.
pub fn AtomicBool::share(self : AtomicBool) -> AtomicBool {
  native_atomic_bool_share(self)
}

///|
pub fn AtomicBool::load(
  self : AtomicBool,
  order? : MemoryOrdering = SeqCst,
) -> Bool raise SyncError {
  validate_load_order(order)
  native_atomic_bool_load(self, order.code())
}

///|
pub fn AtomicBool::store(
  self : AtomicBool,
  value : Bool,
  order? : MemoryOrdering = SeqCst,
) -> Unit raise SyncError {
  validate_store_order(order)
  native_atomic_bool_store(self, value, order.code())
}

///|
pub fn AtomicBool::swap(
  self : AtomicBool,
  value : Bool,
  order? : MemoryOrdering = SeqCst,
) -> Bool {
  native_atomic_bool_swap(self, value, order.code())
}

///|
pub fn AtomicBool::compare_exchange(
  self : AtomicBool,
  current : Bool,
  next : Bool,
  success? : MemoryOrdering = SeqCst,
  failure? : MemoryOrdering = SeqCst,
) -> CompareExchangeResult[Bool] raise SyncError {
  validate_compare_exchange_orders(success, failure)
  let packed = native_atomic_bool_compare_exchange(
    self,
    current,
    next,
    success.code(),
    failure.code(),
  )
  { observed: (packed & 1UL) != 0, exchanged: packed_exchanged(packed) }
}

///|
/// Creates a 32-bit signed atomic integer.
pub fn AtomicInt::new(value : Int) -> AtomicInt {
  native_atomic_int_new(value)
}

///|
/// Creates an independent wrapper for use by another OS thread.
pub fn AtomicInt::share(self : AtomicInt) -> AtomicInt {
  native_atomic_int_share(self)
}

///|
pub fn AtomicInt::load(
  self : AtomicInt,
  order? : MemoryOrdering = SeqCst,
) -> Int raise SyncError {
  validate_load_order(order)
  native_atomic_int_load(self, order.code())
}

///|
pub fn AtomicInt::store(
  self : AtomicInt,
  value : Int,
  order? : MemoryOrdering = SeqCst,
) -> Unit raise SyncError {
  validate_store_order(order)
  native_atomic_int_store(self, value, order.code())
}

///|
pub fn AtomicInt::swap(
  self : AtomicInt,
  value : Int,
  order? : MemoryOrdering = SeqCst,
) -> Int {
  native_atomic_int_swap(self, value, order.code())
}

///|
pub fn AtomicInt::compare_exchange(
  self : AtomicInt,
  current : Int,
  next : Int,
  success? : MemoryOrdering = SeqCst,
  failure? : MemoryOrdering = SeqCst,
) -> CompareExchangeResult[Int] raise SyncError {
  validate_compare_exchange_orders(success, failure)
  let packed = native_atomic_int_compare_exchange(
    self,
    current,
    next,
    success.code(),
    failure.code(),
  )
  {
    observed: packed_observed_uint(packed).reinterpret_as_int(),
    exchanged: packed_exchanged(packed),
  }
}

///|
pub fn AtomicInt::fetch_add(
  self : AtomicInt,
  delta : Int,
  order? : MemoryOrdering = SeqCst,
) -> Int {
  native_atomic_int_fetch_add(self, delta, order.code())
}

///|
/// Creates a 32-bit unsigned atomic integer.
pub fn AtomicUInt::new(value : UInt) -> AtomicUInt {
  native_atomic_uint_new(value)
}

///|
/// Creates an independent wrapper for use by another OS thread.
pub fn AtomicUInt::share(self : AtomicUInt) -> AtomicUInt {
  native_atomic_uint_share(self)
}

///|
pub fn AtomicUInt::load(
  self : AtomicUInt,
  order? : MemoryOrdering = SeqCst,
) -> UInt raise SyncError {
  validate_load_order(order)
  native_atomic_uint_load(self, order.code())
}

///|
pub fn AtomicUInt::store(
  self : AtomicUInt,
  value : UInt,
  order? : MemoryOrdering = SeqCst,
) -> Unit raise SyncError {
  validate_store_order(order)
  native_atomic_uint_store(self, value, order.code())
}

///|
pub fn AtomicUInt::swap(
  self : AtomicUInt,
  value : UInt,
  order? : MemoryOrdering = SeqCst,
) -> UInt {
  native_atomic_uint_swap(self, value, order.code())
}

///|
pub fn AtomicUInt::compare_exchange(
  self : AtomicUInt,
  current : UInt,
  next : UInt,
  success? : MemoryOrdering = SeqCst,
  failure? : MemoryOrdering = SeqCst,
) -> CompareExchangeResult[UInt] raise SyncError {
  validate_compare_exchange_orders(success, failure)
  let packed = native_atomic_uint_compare_exchange(
    self,
    current,
    next,
    success.code(),
    failure.code(),
  )
  {
    observed: packed_observed_uint(packed),
    exchanged: packed_exchanged(packed),
  }
}

///|
pub fn AtomicUInt::fetch_add(
  self : AtomicUInt,
  delta : UInt,
  order? : MemoryOrdering = SeqCst,
) -> UInt {
  native_atomic_uint_fetch_add(self, delta, order.code())
}