/* Apache License, Version 2.0 */

#include <limits>

#include "atomic_ops.h"
#include "testing/testing.h"

#ifdef __GNUC__
#  pragma GCC diagnostic error "-Wsign-compare"
#  pragma GCC diagnostic error "-Wsign-conversion"
#endif

/* -------------------------------------------------------------------- */
/** \name 64 bit unsigned int atomics
 * \{ */

TEST(atomic, atomic_add_and_fetch_uint64)
{
  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_add_and_fetch_uint64(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  {
    uint64_t value = 0x1020304050607080;
    EXPECT_EQ(atomic_add_and_fetch_uint64(&value, 0x0807060504030201), 0x1827364554637281);
    EXPECT_EQ(value, 0x1827364554637281);
  }

  {
    uint64_t value = 0x9020304050607080;
    EXPECT_EQ(atomic_add_and_fetch_uint64(&value, 0x0807060504030201), 0x9827364554637281);
    EXPECT_EQ(value, 0x9827364554637281);
  }
}

TEST(atomic, atomic_sub_and_fetch_uint64)
{
  {
    uint64_t value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_uint64(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  {
    uint64_t value = 0x1827364554637281;
    EXPECT_EQ(atomic_sub_and_fetch_uint64(&value, 0x0807060504030201), 0x1020304050607080);
    EXPECT_EQ(value, 0x1020304050607080);
  }

  {
    uint64_t value = 0x9827364554637281;
    EXPECT_EQ(atomic_sub_and_fetch_uint64(&value, 0x0807060504030201), 0x9020304050607080);
    EXPECT_EQ(value, 0x9020304050607080);
  }

  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_sub_and_fetch_uint64(&value, 2), 0xffffffffffffffff);
    EXPECT_EQ(value, 0xffffffffffffffff);
  }
}

TEST(atomic, atomic_fetch_and_add_uint64)
{
  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_fetch_and_add_uint64(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  {
    uint64_t value = 0x1020304050607080;
    EXPECT_EQ(atomic_fetch_and_add_uint64(&value, 0x0807060504030201), 0x1020304050607080);
    EXPECT_EQ(value, 0x1827364554637281);
  }

  {
    uint64_t value = 0x9020304050607080;
    EXPECT_EQ(atomic_fetch_and_add_uint64(&value, 0x0807060504030201), 0x9020304050607080);
    EXPECT_EQ(value, 0x9827364554637281);
  }
}

TEST(atomic, atomic_fetch_and_sub_uint64)
{
  {
    uint64_t value = 3;
    EXPECT_EQ(atomic_fetch_and_sub_uint64(&value, 2), 3);
    EXPECT_EQ(value, 1);
  }

  {
    uint64_t value = 0x1827364554637281;
    EXPECT_EQ(atomic_fetch_and_sub_uint64(&value, 0x0807060504030201), 0x1827364554637281);
    EXPECT_EQ(value, 0x1020304050607080);
  }

  {
    uint64_t value = 0x9827364554637281;
    EXPECT_EQ(atomic_fetch_and_sub_uint64(&value, 0x0807060504030201), 0x9827364554637281);
    EXPECT_EQ(value, 0x9020304050607080);
  }

  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_fetch_and_sub_uint64(&value, 2), 1);
    EXPECT_EQ(value, 0xffffffffffffffff);
  }
}

TEST(atomic, atomic_cas_uint64)
{
  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_cas_uint64(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  {
    uint64_t value = 1;
    EXPECT_EQ(atomic_cas_uint64(&value, 2, 3), 1);
    EXPECT_EQ(value, 1);
  }

  {
    uint64_t value = 0x1234567890abcdef;
    EXPECT_EQ(atomic_cas_uint64(&value, 0x1234567890abcdef, 0xfedcba0987654321),
              0x1234567890abcdef);
    EXPECT_EQ(value, 0xfedcba0987654321);
  }

  {
    uint64_t value = 0x1234567890abcdef;
    EXPECT_EQ(atomic_cas_uint64(&value, 0xdeadbeefefefefef, 0xfedcba0987654321),
              0x1234567890abcdef);
    EXPECT_EQ(value, 0x1234567890abcdef);
  }
}

TEST(atomic, atomic_load_uint64)
{
  /* Make sure alias is implemented. */
  {
    uint64_t value = 2;
    EXPECT_EQ(atomic_load_uint64(&value), 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const uint64_t uint64_t_max = std::numeric_limits<uint64_t>::max();
    uint64_t value = uint64_t_max;
    EXPECT_EQ(atomic_load_uint64(&value), uint64_t_max);
  }
}

TEST(atomic, atomic_store_uint64)
{
  /* Make sure alias is implemented. */
  {
    uint64_t value = 0;
    atomic_store_uint64(&value, 2);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const uint64_t uint64_t_max = std::numeric_limits<uint64_t>::max();
    uint64_t value = 0;
    atomic_store_uint64(&value, uint64_t_max);
    EXPECT_EQ(value, uint64_t_max);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name 64 bit signed int atomics
 * \{ */

TEST(atomic, atomic_add_and_fetch_int64)
{
  {
    int64_t value = 1;
    EXPECT_EQ(atomic_add_and_fetch_int64(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  {
    int64_t value = 0x1020304050607080;
    EXPECT_EQ(atomic_add_and_fetch_int64(&value, 0x0807060504030201), 0x1827364554637281);
    EXPECT_EQ(value, 0x1827364554637281);
  }

  {
    int64_t value = -0x1020304050607080;
    EXPECT_EQ(atomic_add_and_fetch_int64(&value, -0x0807060504030201), -0x1827364554637281);
    EXPECT_EQ(value, -0x1827364554637281);
  }
}

TEST(atomic, atomic_sub_and_fetch_int64)
{
  {
    int64_t value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_int64(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  {
    int64_t value = 0x1827364554637281;
    EXPECT_EQ(atomic_sub_and_fetch_int64(&value, 0x0807060504030201), 0x1020304050607080);
    EXPECT_EQ(value, 0x1020304050607080);
  }

  {
    int64_t value = -0x1827364554637281;
    EXPECT_EQ(atomic_sub_and_fetch_int64(&value, -0x0807060504030201), -0x1020304050607080);
    EXPECT_EQ(value, -0x1020304050607080);
  }

  {
    int64_t value = 1;
    EXPECT_EQ(atomic_sub_and_fetch_int64(&value, 2), -1);
    EXPECT_EQ(value, -1);
  }
}

TEST(atomic, atomic_fetch_and_add_int64)
{
  {
    int64_t value = 1;
    EXPECT_EQ(atomic_fetch_and_add_int64(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  {
    int64_t value = 0x1020304050607080;
    EXPECT_EQ(atomic_fetch_and_add_int64(&value, 0x0807060504030201), 0x1020304050607080);
    EXPECT_EQ(value, 0x1827364554637281);
  }

  {
    int64_t value = -0x1020304050607080;
    EXPECT_EQ(atomic_fetch_and_add_int64(&value, -0x0807060504030201), -0x1020304050607080);
    EXPECT_EQ(value, -0x1827364554637281);
  }
}

TEST(atomic, atomic_fetch_and_sub_int64)
{
  {
    int64_t value = 3;
    EXPECT_EQ(atomic_fetch_and_sub_int64(&value, 2), 3);
    EXPECT_EQ(value, 1);
  }

  {
    int64_t value = 0x1827364554637281;
    EXPECT_EQ(atomic_fetch_and_sub_int64(&value, 0x0807060504030201), 0x1827364554637281);
    EXPECT_EQ(value, 0x1020304050607080);
  }

  {
    int64_t value = -0x1827364554637281;
    EXPECT_EQ(atomic_fetch_and_sub_int64(&value, -0x0807060504030201), -0x1827364554637281);
    EXPECT_EQ(value, -0x1020304050607080);
  }

  {
    int64_t value = 1;
    EXPECT_EQ(atomic_fetch_and_sub_int64(&value, 2), 1);
    EXPECT_EQ(value, -1);
  }
}

TEST(atomic, atomic_cas_int64)
{
  {
    int64_t value = 1;
    EXPECT_EQ(atomic_cas_int64(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  {
    int64_t value = 1;
    EXPECT_EQ(atomic_cas_int64(&value, 2, 3), 1);
    EXPECT_EQ(value, 1);
  }

  // 0xfedcba0987654321 is -0x012345f6789abcdf
  // 0xdeadbeefefefefef is -0x2152411010101011

  {
    int64_t value = 0x1234567890abcdef;
    EXPECT_EQ(atomic_cas_int64(&value, 0x1234567890abcdef, -0x012345f6789abcdf),
              0x1234567890abcdef);
    EXPECT_EQ(value, -0x012345f6789abcdf);
  }

  {
    int64_t value = 0x1234567890abcdef;
    EXPECT_EQ(atomic_cas_int64(&value, 0x2152411010101011, -0x012345f6789abcdf),
              0x1234567890abcdef);
    EXPECT_EQ(value, 0x1234567890abcdef);
  }
}

TEST(atomic, atomic_load_int64)
{
  /* Make sure alias is implemented. */
  {
    int64_t value = 2;
    EXPECT_EQ(atomic_load_int64(&value), 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const int64_t int64_t_max = std::numeric_limits<int64_t>::max();
    int64_t value = int64_t_max;
    EXPECT_EQ(atomic_load_int64(&value), int64_t_max);
  }
}

TEST(atomic, atomic_store_int64)
{
  /* Make sure alias is implemented. */
  {
    int64_t value = 0;
    atomic_store_int64(&value, 2);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const int64_t int64_t_max = std::numeric_limits<int64_t>::max();
    int64_t value = 0;
    atomic_store_int64(&value, int64_t_max);
    EXPECT_EQ(value, int64_t_max);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name 32 bit unsigned int atomics
 * \{ */

TEST(atomic, atomic_add_and_fetch_uint32)
{
  {
    uint32_t value = 1;
    EXPECT_EQ(atomic_add_and_fetch_uint32(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  {
    uint32_t value = 0x10203040;
    EXPECT_EQ(atomic_add_and_fetch_uint32(&value, 0x04030201), 0x14233241);
    EXPECT_EQ(value, 0x14233241);
  }

  {
    uint32_t value = 0x90203040;
    EXPECT_EQ(atomic_add_and_fetch_uint32(&value, 0x04030201), 0x94233241);
    EXPECT_EQ(value, 0x94233241);
  }
}

TEST(atomic, atomic_sub_and_fetch_uint32)
{
  {
    uint32_t value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_uint32(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  {
    uint32_t value = 0x14233241;
    EXPECT_EQ(atomic_sub_and_fetch_uint32(&value, 0x04030201), 0x10203040);
    EXPECT_EQ(value, 0x10203040);
  }

  {
    uint32_t value = 0x94233241;
    EXPECT_EQ(atomic_sub_and_fetch_uint32(&value, 0x04030201), 0x90203040);
    EXPECT_EQ(value, 0x90203040);
  }

  {
    uint32_t value = 1;
    EXPECT_EQ(atomic_sub_and_fetch_uint32(&value, 2), 0xffffffff);
    EXPECT_EQ(value, 0xffffffff);
  }
}

TEST(atomic, atomic_cas_uint32)
{
  {
    uint32_t value = 1;
    EXPECT_EQ(atomic_cas_uint32(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  {
    uint32_t value = 1;
    EXPECT_EQ(atomic_cas_uint32(&value, 2, 3), 1);
    EXPECT_EQ(value, 1);
  }

  {
    uint32_t value = 0x12345678;
    EXPECT_EQ(atomic_cas_uint32(&value, 0x12345678, 0x87654321), 0x12345678);
    EXPECT_EQ(value, 0x87654321);
  }

  {
    uint32_t value = 0x12345678;
    EXPECT_EQ(atomic_cas_uint32(&value, 0xdeadbeef, 0x87654321), 0x12345678);
    EXPECT_EQ(value, 0x12345678);
  }
}

TEST(atomic, atomic_load_uint32)
{
  /* Make sure alias is implemented. */
  {
    uint32_t value = 2;
    EXPECT_EQ(atomic_load_uint32(&value), 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const uint32_t uint32_t_max = std::numeric_limits<uint32_t>::max();
    uint32_t value = uint32_t_max;
    EXPECT_EQ(atomic_load_uint32(&value), uint32_t_max);
  }
}

TEST(atomic, atomic_store_uint32)
{
  /* Make sure alias is implemented. */
  {
    uint32_t value = 0;
    atomic_store_uint32(&value, 2);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const uint32_t uint32_t_max = std::numeric_limits<uint32_t>::max();
    uint32_t value = 0;
    atomic_store_uint32(&value, uint32_t_max);
    EXPECT_EQ(value, uint32_t_max);
  }
}

TEST(atomic, atomic_fetch_and_add_uint32)
{
  {
    uint32_t value = 1;
    EXPECT_EQ(atomic_fetch_and_add_uint32(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  {
    uint32_t value = 0x10203040;
    EXPECT_EQ(atomic_fetch_and_add_uint32(&value, 0x04030201), 0x10203040);
    EXPECT_EQ(value, 0x14233241);
  }

  {
    uint32_t value = 0x90203040;
    EXPECT_EQ(atomic_fetch_and_add_uint32(&value, 0x04030201), 0x90203040);
    EXPECT_EQ(value, 0x94233241);
  }
}

TEST(atomic, atomic_fetch_and_or_uint32)
{
  {
    uint32_t value = 12;
    EXPECT_EQ(atomic_fetch_and_or_uint32(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }

  {
    uint32_t value = 0x12345678;
    EXPECT_EQ(atomic_fetch_and_or_uint32(&value, 0x87654321), 0x12345678);
    EXPECT_EQ(value, 0x97755779);
  }

  {
    uint32_t value = 0x92345678;
    EXPECT_EQ(atomic_fetch_and_or_uint32(&value, 0x87654321), 0x92345678);
    EXPECT_EQ(value, 0x97755779);
  }
}

TEST(atomic, atomic_fetch_and_and_uint32)
{
  {
    uint32_t value = 12;
    EXPECT_EQ(atomic_fetch_and_and_uint32(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }

  {
    uint32_t value = 0x12345678;
    EXPECT_EQ(atomic_fetch_and_and_uint32(&value, 0x87654321), 0x12345678);
    EXPECT_EQ(value, 0x02244220);
  }

  {
    uint32_t value = 0x92345678;
    EXPECT_EQ(atomic_fetch_and_and_uint32(&value, 0x87654321), 0x92345678);
    EXPECT_EQ(value, 0x82244220);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name 32 bit signed int atomics
 * \{ */

TEST(atomic, atomic_add_and_fetch_int32)
{
  {
    int32_t value = 1;
    EXPECT_EQ(atomic_add_and_fetch_int32(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  {
    int32_t value = 0x10203040;
    EXPECT_EQ(atomic_add_and_fetch_int32(&value, 0x04030201), 0x14233241);
    EXPECT_EQ(value, 0x14233241);
  }

  {
    int32_t value = -0x10203040;
    EXPECT_EQ(atomic_add_and_fetch_int32(&value, -0x04030201), -0x14233241);
    EXPECT_EQ(value, -0x14233241);
  }
}

TEST(atomic, atomic_sub_and_fetch_int32)
{
  {
    int32_t value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_int32(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  {
    int32_t value = 0x14233241;
    EXPECT_EQ(atomic_sub_and_fetch_int32(&value, 0x04030201), 0x10203040);
    EXPECT_EQ(value, 0x10203040);
  }

  {
    int32_t value = -0x14233241;
    EXPECT_EQ(atomic_sub_and_fetch_int32(&value, -0x04030201), -0x10203040);
    EXPECT_EQ(value, -0x10203040);
  }

  {
    int32_t value = 1;
    EXPECT_EQ(atomic_sub_and_fetch_int32(&value, 2), 0xffffffff);
    EXPECT_EQ(value, 0xffffffff);
  }
}

TEST(atomic, atomic_cas_int32)
{
  {
    int32_t value = 1;
    EXPECT_EQ(atomic_cas_int32(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  {
    int32_t value = 1;
    EXPECT_EQ(atomic_cas_int32(&value, 2, 3), 1);
    EXPECT_EQ(value, 1);
  }

  // 0x87654321 is -0x789abcdf
  // 0xdeadbeef is -0x21524111

  {
    int32_t value = 0x12345678;
    EXPECT_EQ(atomic_cas_int32(&value, 0x12345678, -0x789abcdf), 0x12345678);
    EXPECT_EQ(value, -0x789abcdf);
  }

  {
    int32_t value = 0x12345678;
    EXPECT_EQ(atomic_cas_int32(&value, -0x21524111, -0x789abcdf), 0x12345678);
    EXPECT_EQ(value, 0x12345678);
  }
}

TEST(atomic, atomic_load_int32)
{
  /* Make sure alias is implemented. */
  {
    int32_t value = 2;
    EXPECT_EQ(atomic_load_int32(&value), 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const int32_t int32_t_max = std::numeric_limits<int32_t>::max();
    int32_t value = int32_t_max;
    EXPECT_EQ(atomic_load_int32(&value), int32_t_max);
  }
}

TEST(atomic, atomic_store_int32)
{
  /* Make sure alias is implemented. */
  {
    int32_t value = 0;
    atomic_store_int32(&value, 2);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const int32_t int32_t_max = std::numeric_limits<int32_t>::max();
    int32_t value = 0;
    atomic_store_int32(&value, int32_t_max);
    EXPECT_EQ(value, int32_t_max);
  }
}

TEST(atomic, atomic_fetch_and_add_int32)
{
  {
    int32_t value = 1;
    EXPECT_EQ(atomic_fetch_and_add_int32(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  {
    int32_t value = 0x10203040;
    EXPECT_EQ(atomic_fetch_and_add_int32(&value, 0x04030201), 0x10203040);
    EXPECT_EQ(value, 0x14233241);
  }

  {
    int32_t value = -0x10203040;
    EXPECT_EQ(atomic_fetch_and_add_int32(&value, -0x04030201), -0x10203040);
    EXPECT_EQ(value, -0x14233241);
  }
}

TEST(atomic, atomic_fetch_and_or_int32)
{
  {
    int32_t value = 12;
    EXPECT_EQ(atomic_fetch_and_or_int32(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }

  // 0x87654321 is -0x789abcdf

  {
    int32_t value = 0x12345678;
    EXPECT_EQ(atomic_fetch_and_or_int32(&value, -0x789abcdf), 0x12345678);
    EXPECT_EQ(value, 0x97755779);
  }
}

TEST(atomic, atomic_fetch_and_and_int32)
{
  {
    int32_t value = 12;
    EXPECT_EQ(atomic_fetch_and_and_int32(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }

  {
    int32_t value = 0x12345678;
    EXPECT_EQ(atomic_fetch_and_and_int32(&value, -0x789abcdf), 0x12345678);
    EXPECT_EQ(value, 0x02244220);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name 16 bit signed int atomics
 * \{ */

TEST(atomic, atomic_fetch_and_or_int16)
{
  {
    int16_t value = 12;
    EXPECT_EQ(atomic_fetch_and_or_int16(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }

  {
    int16_t value = 0x1234;
    EXPECT_EQ(atomic_fetch_and_or_int16(&value, -0x5678), 0x1234);
    EXPECT_EQ(value, -0x4444);
  }
}

TEST(atomic, atomic_fetch_and_and_int16)
{
  {
    int16_t value = 12;
    EXPECT_EQ(atomic_fetch_and_and_int16(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }

  {
    int16_t value = 0x1234;
    EXPECT_EQ(atomic_fetch_and_and_int16(&value, -0x789A), 0x1234);
    EXPECT_EQ(value, 0x224);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name 8 bit unsigned int atomics
 * \{ */

TEST(atomic, atomic_fetch_and_or_uint8)
{
  {
    uint8_t value = 12;
    EXPECT_EQ(atomic_fetch_and_or_uint8(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }
}

TEST(atomic, atomic_fetch_and_and_uint8)
{
  {
    uint8_t value = 12;
    EXPECT_EQ(atomic_fetch_and_and_uint8(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }
}

/** \} */

/** \name 8 bit signed int atomics
 * \{ */

TEST(atomic, atomic_fetch_and_or_int8)
{
  {
    int8_t value = 12;
    EXPECT_EQ(atomic_fetch_and_or_int8(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }
}

TEST(atomic, atomic_fetch_and_and_int8)
{
  {
    int8_t value = 12;
    EXPECT_EQ(atomic_fetch_and_and_int8(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name char aliases
 * \{ */

TEST(atomic, atomic_fetch_and_or_char)
{
  {
    char value = 12;
    EXPECT_EQ(atomic_fetch_and_or_char(&value, 5), 12);
    EXPECT_EQ(value, 13);
  }
}

TEST(atomic, atomic_fetch_and_and_char)
{
  {
    char value = 12;
    EXPECT_EQ(atomic_fetch_and_and_char(&value, 5), 12);
    EXPECT_EQ(value, 4);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name size_t aliases
 * \{ */

TEST(atomic, atomic_add_and_fetch_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 1;
    EXPECT_EQ(atomic_add_and_fetch_z(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = size_t_max - 10;
    EXPECT_EQ(atomic_add_and_fetch_z(&value, 2), size_t_max - 8);
    EXPECT_EQ(value, size_t_max - 8);
  }
}

TEST(atomic, atomic_sub_and_fetch_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_z(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = size_t_max - 10;
    EXPECT_EQ(atomic_sub_and_fetch_z(&value, 2), size_t_max - 12);
    EXPECT_EQ(value, size_t_max - 12);
  }
}

TEST(atomic, atomic_fetch_and_add_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 1;
    EXPECT_EQ(atomic_fetch_and_add_z(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = size_t_max - 10;
    EXPECT_EQ(atomic_fetch_and_add_z(&value, 2), size_t_max - 10);
    EXPECT_EQ(value, size_t_max - 8);
  }
}

TEST(atomic, atomic_fetch_and_sub_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 3;
    EXPECT_EQ(atomic_fetch_and_sub_z(&value, 2), 3);
    EXPECT_EQ(value, 1);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = size_t_max - 10;
    EXPECT_EQ(atomic_fetch_and_sub_z(&value, 2), size_t_max - 10);
    EXPECT_EQ(value, size_t_max - 12);
  }
}

TEST(atomic, atomic_cas_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 1;
    EXPECT_EQ(atomic_cas_z(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = 1;
    EXPECT_EQ(atomic_cas_z(&value, 1, size_t_max), 1);
    EXPECT_EQ(value, size_t_max);
  }
}

TEST(atomic, atomic_load_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 2;
    EXPECT_EQ(atomic_load_z(&value), 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = size_t_max;
    EXPECT_EQ(atomic_load_z(&value), size_t_max);
  }
}

TEST(atomic, atomic_store_z)
{
  /* Make sure alias is implemented. */
  {
    size_t value = 0;
    atomic_store_z(&value, 2);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const size_t size_t_max = std::numeric_limits<size_t>::max();
    size_t value = 0;
    atomic_store_z(&value, size_t_max);
    EXPECT_EQ(value, size_t_max);
  }
}

TEST(atomic, atomic_fetch_and_update_max_z)
{
  const size_t size_t_max = std::numeric_limits<size_t>::max();

  size_t value = 12;

  EXPECT_EQ(atomic_fetch_and_update_max_z(&value, 8), 12);
  EXPECT_EQ(value, 12);

  EXPECT_EQ(atomic_fetch_and_update_max_z(&value, 24), 12);
  EXPECT_EQ(value, 24);

  EXPECT_EQ(atomic_fetch_and_update_max_z(&value, size_t_max), 24);
  EXPECT_EQ(value, size_t_max);
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name unsigned int aliases
 * \{ */

TEST(atomic, atomic_add_and_fetch_u)
{
  /* Make sure alias is implemented. */
  {
    unsigned int value = 1;
    EXPECT_EQ(atomic_add_and_fetch_u(&value, 2), 3);
    EXPECT_EQ(value, 3);
  }

  /* Make sure alias is using proper bitness. */
  {
    const unsigned int uint_max = std::numeric_limits<unsigned int>::max();
    unsigned int value = uint_max - 10;
    EXPECT_EQ(atomic_add_and_fetch_u(&value, 2), uint_max - 8);
    EXPECT_EQ(value, uint_max - 8);
  }
}

TEST(atomic, atomic_sub_and_fetch_u)
{
  /* Make sure alias is implemented. */
  {
    unsigned int value = 3;
    EXPECT_EQ(atomic_sub_and_fetch_u(&value, 2), 1);
    EXPECT_EQ(value, 1);
  }

  /* Make sure alias is using proper bitness. */
  {
    const unsigned int uint_max = std::numeric_limits<unsigned int>::max();
    unsigned int value = uint_max - 10;
    EXPECT_EQ(atomic_sub_and_fetch_u(&value, 2), uint_max - 12);
    EXPECT_EQ(value, uint_max - 12);
  }
}

TEST(atomic, atomic_fetch_and_add_u)
{
  /* Make sure alias is implemented. */
  {
    unsigned int value = 1;
    EXPECT_EQ(atomic_fetch_and_add_u(&value, 2), 1);
    EXPECT_EQ(value, 3);
  }

  /* Make sure alias is using proper bitness. */
  {
    const unsigned int uint_max = std::numeric_limits<unsigned int>::max();
    unsigned int value = uint_max - 10;
    EXPECT_EQ(atomic_fetch_and_add_u(&value, 2), uint_max - 10);
    EXPECT_EQ(value, uint_max - 8);
  }
}

TEST(atomic, atomic_fetch_and_sub_u)
{
  /* Make sure alias is implemented. */
  {
    unsigned int value = 3;
    EXPECT_EQ(atomic_fetch_and_sub_u(&value, 2), 3);
    EXPECT_EQ(value, 1);
  }

  /* Make sure alias is using proper bitness. */
  {
    const unsigned int uint_max = std::numeric_limits<unsigned int>::max();
    unsigned int value = uint_max - 10;
    EXPECT_EQ(atomic_fetch_and_sub_u(&value, 2), uint_max - 10);
    EXPECT_EQ(value, uint_max - 12);
  }
}

TEST(atomic, atomic_cas_u)
{
  /* Make sure alias is implemented. */
  {
    unsigned int value = 1;
    EXPECT_EQ(atomic_cas_u(&value, 1, 2), 1);
    EXPECT_EQ(value, 2);
  }

  /* Make sure alias is using proper bitness. */
  {
    const unsigned int uint_max = std::numeric_limits<unsigned int>::max();
    unsigned int value = 1;
    EXPECT_EQ(atomic_cas_u(&value, 1, uint_max), 1);
    EXPECT_EQ(value, uint_max);
  }
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name pointer aliases
 * \{ */

#define INT_AS_PTR(a) reinterpret_cast<void *>((a))

TEST(atomic, atomic_cas_ptr)
{
  {
    void *value = INT_AS_PTR(0x7f);
    EXPECT_EQ(atomic_cas_ptr(&value, INT_AS_PTR(0x7f), INT_AS_PTR(0xef)), INT_AS_PTR(0x7f));
    EXPECT_EQ(value, INT_AS_PTR(0xef));
  }
}

TEST(atomic, atomic_load_ptr)
{
  {
    void *value = INT_AS_PTR(0x7f);
    void *dest = atomic_load_ptr(&value);
    EXPECT_EQ(dest, INT_AS_PTR(0x7f));
  }
}

TEST(atomic, atomic_store_ptr)
{
  {
    void *value = INT_AS_PTR(0x7f);
    void *dest = nullptr;
    atomic_store_ptr(&dest, value);
    EXPECT_EQ(dest, INT_AS_PTR(0x7f));
  }
}

#undef INT_AS_PTR

/** \} */

/* -------------------------------------------------------------------- */
/** \name floating point atomics
 * \{ */

TEST(atomic, atomic_cas_float)
{
  {
    float value = 1.234f;
    EXPECT_EQ(atomic_cas_float(&value, 1.234f, 2.71f), 1.234f);
    EXPECT_EQ(value, 2.71f);
  }
}

TEST(atomic, atomic_add_and_fetch_fl)
{
  {
    float value = 1.23f;
    EXPECT_NEAR(atomic_add_and_fetch_fl(&value, 2.71f), 3.94f, 1e-8f);
    EXPECT_NEAR(value, 3.94f, 1e-8f);
  }
}

/** \} */
