/*
 * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */
@file:OptIn(ExperimentalAtomicApi::class)
package test.concurrent.atomics

import test.properties.delegation.references.Data
import kotlin.concurrent.atomics.*
import kotlin.test.*

class AtomicIntArrayTest {
    @Test
    fun ctor() {
        val arr1 = AtomicIntArray(6)
        assertEquals(0, arr1.loadAt(4))
        assertEquals(6, arr1.size)

        val arr2 = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(40, arr2.loadAt(4))
        assertEquals(10, arr2.size)

        val emptyArr = AtomicIntArray(0)
        assertEquals(0, emptyArr.size)

        assertFails {
            val arrNegativeSize = AtomicIntArray(-5)
        }
    }

    @Test
    fun getter() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        for (i in 0 until atomicIntArr.size) {
            assertEquals(i * 10, atomicIntArr.loadAt(i), "getter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.loadAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.loadAt(-1)
        }
    }

    @Test
    fun setter() {
        val atomicIntArr = AtomicIntArray(10)
        for (i in 0 until atomicIntArr.size) {
            atomicIntArr.storeAt(i, i * 10)
        }
        for (i in 0 until atomicIntArr.size) {
            assertEquals(i * 10, atomicIntArr.loadAt(i), "setter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicIntArr.storeAt(22, 100)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicIntArr.storeAt(-1, 100)
        }
    }

    @Test
    fun addAndFetchAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(110, atomicIntArr.addAndFetchAt(1, 100), "addAndFetchAt: FAIL 1")
        assertEquals(110, atomicIntArr.loadAt(1), "addAndFetchAt: FAIL 2")
        assertEquals(10, atomicIntArr.addAndFetchAt(1, -100), "addAndFetchAt: FAIL 3")
        assertEquals(10, atomicIntArr.loadAt(1), "addAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.addAndFetchAt(22, 33535)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.addAndFetchAt(-1, 33535)
        }
    }

    @Test
    fun compareAndExchange() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        val res1 = atomicIntArr.compareAndExchangeAt(2, 20, 222) // success
        assertTrue(res1 == 20 && atomicIntArr.loadAt(2) == 222, "compareAndExchange: FAIL 1")
        val res2 = atomicIntArr.compareAndExchangeAt(2, 222, 2222) // success
        assertTrue(res2 == 222 && atomicIntArr.loadAt(2) == 2222, "compareAndExchangeAt: FAIL 2")
        val res3 = atomicIntArr.compareAndExchangeAt(2, 223, 22222) // should fail
        assertTrue(res3 == 2222 && atomicIntArr.loadAt(2) == 2222, "compareAndExchangeAt: FAIL 3")
        val res4 = atomicIntArr.compareAndExchangeAt(9, 10, 999) // should fail
        assertTrue(res4 == 90 && atomicIntArr.loadAt(9) == 90, "compareAndExchangeAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.compareAndExchangeAt(10, 33535, 39530)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.compareAndExchangeAt(-1, 33535, 39530)
        }
    }

    @Test
    fun compareAndSetAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        val res1 = atomicIntArr.compareAndSetAt(2, 20, 222) // success
        assertTrue(res1 && atomicIntArr.loadAt(2) == 222, "compareAndSetAt: FAIL 1")
        val res2 = atomicIntArr.compareAndSetAt(2, 222, 2222) // success
        assertTrue(res2 && atomicIntArr.loadAt(2) == 2222, "compareAndSetAt: FAIL 2")
        val res3 = atomicIntArr.compareAndSetAt(2, 223, 22222) // should fail
        assertTrue(!res3 && atomicIntArr.loadAt(2) == 2222, "compareAndSetAt: FAIL 3")
        val res4 = atomicIntArr.compareAndSetAt(9, 10, 999) // should fail
        assertTrue(!res4 && atomicIntArr.loadAt(9) == 90, "compareAndSetAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.compareAndSetAt(10, 33535, 39530)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.compareAndSetAt(-1, 33535, 39530)
        }
    }

    @Test
    fun exchangeAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(20, atomicIntArr.exchangeAt(2, 200), "exchangeAt: FAIL 1")
        assertEquals(200, atomicIntArr.exchangeAt(2, 2000), "exchangeAt: FAIL 2")
        assertEquals(2000, atomicIntArr.loadAt(2), "exchangeAt: FAIL 3")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.exchangeAt(22, 33535)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.exchangeAt(-1, 33535)
        }
    }

    @Test
    fun fetchAndAddAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(10, atomicIntArr.fetchAndAddAt(1, 100), "fetchAndAddAt: FAIL 1")
        assertEquals(110, atomicIntArr.loadAt(1), "fetchAndAddAt: FAIL 2")
        assertEquals(110, atomicIntArr.fetchAndAddAt(1, -100), "fetchAndAddAt: FAIL 3")
        assertEquals(10, atomicIntArr.loadAt(1), "fetchAndAddAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndAddAt(22, 33535)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndAddAt(-1, 33535)
        }
    }

    @Test
    fun fetchAndIncrementAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(10, atomicIntArr.fetchAndIncrementAt(1), "fetchAndIncrementAt: FAIL 1")
        assertEquals(11, atomicIntArr.loadAt(1), "fetchAndIncrementAt: FAIL 2")
        assertEquals(11, atomicIntArr.fetchAndIncrementAt(1), "fetchAndIncrementAt: FAIL 3")
        assertEquals(12, atomicIntArr.loadAt(1), "fetchAndIncrementAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndIncrementAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndIncrementAt(-1)
        }
    }

    @Test
    fun incrementAndFetchAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(11, atomicIntArr.incrementAndFetchAt(1), "incrementAndFetchAt: FAIL 1")
        assertEquals(11, atomicIntArr.loadAt(1), "incrementAndFetchAt: FAIL 2")
        assertEquals(12, atomicIntArr.incrementAndFetchAt(1), "incrementAndFetchAt: FAIL 3")
        assertEquals(12, atomicIntArr.loadAt(1), "incrementAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.incrementAndFetchAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.incrementAndFetchAt(-1)
        }
    }

    @Test
    fun fetchAndDecrementAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(10, atomicIntArr.fetchAndDecrementAt(1), "fetchAndDecrementAt: FAIL 1")
        assertEquals(9, atomicIntArr.loadAt(1), "fetchAndDecrementAt: FAIL 2")
        assertEquals(9, atomicIntArr.fetchAndDecrementAt(1), "fetchAndDecrementAt: FAIL 3")
        assertEquals(8, atomicIntArr.loadAt(1), "fetchAndDecrementAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndDecrementAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.fetchAndDecrementAt(-1)
        }
    }

    @Test
    fun decrementAndFetchAt() {
        val atomicIntArr = AtomicIntArray(10) { i: Int -> i * 10 }
        assertEquals(9, atomicIntArr.decrementAndFetchAt(1), "decrementAndFetchAt: FAIL 1")
        assertEquals(9, atomicIntArr.loadAt(1), "decrementAndFetchAt: FAIL 2")
        assertEquals(8, atomicIntArr.decrementAndFetchAt(1), "decrementAndFetchAt: FAIL 3")
        assertEquals(8, atomicIntArr.loadAt(1), "decrementAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.decrementAndFetchAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicIntArr.decrementAndFetchAt(-1)
        }
    }

    @Test
    fun toStringTest() {
        val array = AtomicIntArray(3) { it }
        assertEquals("[0, 1, 2]", array.toString())
    }

    @Test
    fun updateAt() {
        val array = AtomicIntArray(intArrayOf(1, 2, 3))

        array.updateAt(1) { it * 3 }
        assertEquals("[1, 6, 3]", array.toString())

        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(3) { it * 3 } }
    }

    @Test
    fun updateAndFetchAt() {
        val array = AtomicIntArray(intArrayOf(1, 2, 3))

        assertEquals(6, array.updateAndFetchAt(1) { it * 3 })
        assertEquals("[1, 6, 3]", array.toString())
        assertEquals(6, array.updateAndFetchAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(3) { it * 3 } }
    }

    @Test
    fun fetchAndUpdateAt() {
        val array = AtomicIntArray(intArrayOf(1, 2, 3))

        assertEquals(2, array.fetchAndUpdateAt(1) { it * 3 })
        assertEquals("[1, 6, 3]", array.toString())
        assertEquals(6, array.fetchAndUpdateAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(3) { it * 3 } }
    }
}

class AtomicLongArrayTest {
    @Test
    fun ctor() {
        val arr1 = AtomicLongArray(6)
        assertEquals(arr1.loadAt(4), 0L)
        assertEquals(arr1.size, 6)
        val arr2 = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(arr2.loadAt(4), 40L)
        assertEquals(arr2.size, 10)

        val emptyArr = AtomicLongArray(0)
        assertEquals(emptyArr.size, 0)

        assertFails {
            val arrNegativeSize = AtomicLongArray(-5)
        }
    }

    @Test
    fun getter() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        for (i in 0 until atomicLongArr.size) {
            assertEquals(i * 10L, atomicLongArr.loadAt(i), "getter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.loadAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.loadAt(-1)
        }
    }

    @Test
    fun setter() {
        val atomicLongArr = AtomicLongArray(10)
        for (i in 0 until atomicLongArr.size) {
            atomicLongArr.storeAt(i, i * 10L)
        }
        for (i in 0 until atomicLongArr.size) {
            assertEquals(i * 10L, atomicLongArr.loadAt(i), "setter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.storeAt(10, 3998009)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.storeAt(-1, 3998009)
        }
    }

    @Test
    fun addAndFetchAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(110L, atomicLongArr.addAndFetchAt(1, 100L), "addAndFetchAt: FAIL 1")
        assertEquals(110L, atomicLongArr.loadAt(1), "addAndFetchAt: FAIL 2")
        assertEquals(10L, atomicLongArr.addAndFetchAt(1, -100L), "addAndFetchAt: FAIL 3")
        assertEquals(10L, atomicLongArr.loadAt(1), "addAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.addAndFetchAt(22, 33535)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.addAndFetchAt(-1, 33535)
        }
    }

    @Test
    fun compareAndExchangeAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        val res1 = atomicLongArr.compareAndExchangeAt(2, 20L, 222L) // success
        assertTrue(res1 == 20L && atomicLongArr.loadAt(2) == 222L, "compareAndExchangeAt: FAIL 1")
        val res2 = atomicLongArr.compareAndExchangeAt(2, 222L, 2222L) // success
        assertTrue(res2 == 222L && atomicLongArr.loadAt(2) == 2222L, "compareAndExchangeAt: FAIL 2")
        val res3 = atomicLongArr.compareAndExchangeAt(2, 223L, 22222L) // should fail
        assertTrue(res3 == 2222L && atomicLongArr.loadAt(2) == 2222L, "compareAndExchangeAt: FAIL 3")
        val res4 = atomicLongArr.compareAndExchangeAt(9, 10L, 999L) // should fail
        assertTrue(res4 == 90L && atomicLongArr.loadAt(9) == 90L, "compareAndExchangeAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.compareAndExchangeAt(10, 9353, 39058308)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.compareAndExchangeAt(-1, 9353, 39058308)
        }
    }

    @Test
    fun compareAndSetAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        val res1 = atomicLongArr.compareAndSetAt(2, 20L, 222L) // success
        assertTrue(res1 && atomicLongArr.loadAt(2) == 222L, "compareAndSetAt: FAIL 1")
        val res2 = atomicLongArr.compareAndSetAt(2, 222L, 2222L) // success
        assertTrue(res2 && atomicLongArr.loadAt(2) == 2222L, "compareAndSetAt: FAIL 2")
        val res3 = atomicLongArr.compareAndSetAt(2, 223L, 22222L) // should fail
        assertTrue(!res3 && atomicLongArr.loadAt(2) == 2222L, "compareAndSetAt: FAIL 3")
        val res4 = atomicLongArr.compareAndSetAt(9, 10L, 999L) // should fail
        assertTrue(!res4 && atomicLongArr.loadAt(9) == 90L, "compareAndSetAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.compareAndSetAt(10, 9353, 39058308)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            atomicLongArr.compareAndSetAt(-1, 9353, 39058308)
        }
    }

    @Test
    fun exchangeAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(20L, atomicLongArr.exchangeAt(2, 200L), "exchangeAt: FAIL 1")
        assertEquals(200L, atomicLongArr.exchangeAt(2, 2000L), "exchangeAt: FAIL 2")
        assertEquals(2000L, atomicLongArr.loadAt(2), "exchangeAt: FAIL 3")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.exchangeAt(22, 9353)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.exchangeAt(-1, 9353)
        }
    }

    @Test
    fun fetchAndAddAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(10L, atomicLongArr.fetchAndAddAt(1, 100L), "fetchAndAddAt: FAIL 1")
        assertEquals(110L, atomicLongArr.loadAt(1), "fetchAndAddAt: FAIL 2")
        assertEquals(110L, atomicLongArr.fetchAndAddAt(1, -100L), "fetchAndAddAt: FAIL 3")
        assertEquals(10L, atomicLongArr.loadAt(1), "fetchAndAddAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.fetchAndAddAt(100, 9353)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.fetchAndAddAt(-1, 9353)
        }
    }

    @Test
    fun fetchAndIncrementAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(10L, atomicLongArr.fetchAndIncrementAt(1), "fetchAndIncrementAt: FAIL 1")
        assertEquals(11L, atomicLongArr.loadAt(1), "fetchAndIncrementAt: FAIL 2")
        assertEquals(11L, atomicLongArr.fetchAndIncrementAt(1), "fetchAndIncrementAt: FAIL 3")
        assertEquals(12L, atomicLongArr.loadAt(1), "fetchAndIncrementAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.fetchAndIncrementAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.addAndFetchAt(-1, 33535)
        }
    }

    @Test
    fun incrementAndFetchAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(11L, atomicLongArr.incrementAndFetchAt(1), "incrementAndFetchAt: FAIL 1")
        assertEquals(11L, atomicLongArr.loadAt(1), "incrementAndFetchAt: FAIL 2")
        assertEquals(12L, atomicLongArr.incrementAndFetchAt(1), "incrementAndFetchAt: FAIL 3")
        assertEquals(12L, atomicLongArr.loadAt(1), "incrementAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.incrementAndFetchAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.incrementAndFetchAt(-1)
        }
    }

    @Test
    fun fetchAndDecrementAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(10L, atomicLongArr.fetchAndDecrementAt(1), "fetchAndDecrementAt: FAIL 1")
        assertEquals(9L, atomicLongArr.loadAt(1), "fetchAndDecrementAt: FAIL 2")
        assertEquals(9L, atomicLongArr.fetchAndDecrementAt(1), "fetchAndDecrementAt: FAIL 3")
        assertEquals(8L, atomicLongArr.loadAt(1), "fetchAndDecrementAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.fetchAndDecrementAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.fetchAndDecrementAt(-1)
        }
    }

    @Test
    fun decrementAndFetchAt() {
        val atomicLongArr = AtomicLongArray(10) { i: Int -> i * 10L }
        assertEquals(9L, atomicLongArr.decrementAndFetchAt(1), "decrementAndFetchAt: FAIL 1")
        assertEquals(9L, atomicLongArr.loadAt(1), "decrementAndFetchAt: FAIL 2")
        assertEquals(8L, atomicLongArr.decrementAndFetchAt(1), "decrementAndFetchAt: FAIL 3")
        assertEquals(8L, atomicLongArr.loadAt(1), "decrementAndFetchAt: FAIL 4")
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.decrementAndFetchAt(22)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = atomicLongArr.decrementAndFetchAt(-1)
        }
    }

    @Test
    fun toStringTest() {
        val array = AtomicLongArray(3) { it.toLong() }
        assertEquals("[0, 1, 2]", array.toString())
    }

    @Test
    fun updateAt() {
        val array = AtomicLongArray(longArrayOf(1, 2, 3))

        array.updateAt(1) { it * 3 }
        assertEquals("[1, 6, 3]", array.toString())

        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(3) { it * 3 } }
    }

    @Test
    fun updateAndFetchAt() {
        val array = AtomicLongArray(longArrayOf(1, 2, 3))

        assertEquals(6, array.updateAndFetchAt(1) { it * 3 })
        assertEquals("[1, 6, 3]", array.toString())
        assertEquals(6, array.updateAndFetchAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(3) { it * 3 } }
    }

    @Test
    fun fetchAndUpdateAt() {
        val array = AtomicLongArray(longArrayOf(1, 2, 3))

        assertEquals(2, array.fetchAndUpdateAt(1) { it * 3 })
        assertEquals("[1, 6, 3]", array.toString())
        assertEquals(6, array.fetchAndUpdateAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(-1) { it * 3 } }
        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(3) { it * 3 } }
    }
}

class AtomicArrayTest {
    private data class Data(val value: Int)

    @Test
    fun ctor() {
        val arr2 = AtomicArray<Data?>(10) { null }
        assertEquals(arr2.loadAt(4), null)
        assertEquals(arr2.size, 10)

        val emptyArr = AtomicArray<Data?>(0) { Data(1) }
        assertEquals(emptyArr.size, 0)

        assertFails {
            val arrNegativeSize = AtomicArray<Data?>(-5) { Data(1) }
        }
    }

    @Test
    fun getter() {
        val refArr = AtomicArray<Data?>(10) { i -> Data(i) }
        for (i in 0 until refArr.size) {
            assertEquals(Data(i), refArr.loadAt(i), "getter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = refArr.loadAt(100)
        }
        assertFailsWith<IndexOutOfBoundsException> {
            val res = refArr.loadAt(-1)
        }
    }

    @Test
    fun setter() {
        val refArr = AtomicArray<Data?>(10) { null }
        for (i in 0 until refArr.size) {
            refArr.storeAt(i, Data(i))
        }
        for (i in 0 until refArr.size) {
            assertEquals(Data(i), refArr.loadAt(i), "setter: FAIL $i")
        }
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.storeAt(100, Data(100))
        }
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.storeAt(-1, Data(-1))
        }
    }

    @Test
    fun compareAndExchangeAt() {
        val refArr = AtomicArray<Data?>(10) { null }
        val newValue = Data(1)
        val res1 = refArr.compareAndExchangeAt(3, null, newValue)
        assertTrue(res1 == null && refArr.loadAt(3) == newValue, "compareAndExchangeAt: FAIL 1")
        val res2 = refArr.compareAndExchangeAt(3, newValue, Data(2))
        assertTrue(res2 == newValue && refArr.loadAt(3) == Data(2), "compareAndExchangeAt: FAIL 2")
        val res3 = refArr.compareAndExchangeAt(3, newValue, Data(3))
        assertTrue(res3 == Data(2) && refArr.loadAt(3) == Data(2), "compareAndExchangeAt: FAIL 3")
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.compareAndExchangeAt(10, newValue, Data(4))
        }
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.compareAndExchangeAt(-1, newValue, Data(4))
        }
    }

    @Test
    fun compareAndSetAt() {
        val refArr = AtomicArray<Data?>(10) { null }
        val newValue = Data(1)
        val res1 = refArr.compareAndSetAt(3, null, newValue)
        assertTrue(res1 && refArr.loadAt(3) == newValue, "testAtomicArraycompareAndSetAt: FAIL 1")
        val res2 = refArr.compareAndSetAt(3, newValue, Data(2))
        assertTrue(res2 && refArr.loadAt(3) == Data(2), "testAtomicArraycompareAndSetAt: FAIL 2")
        val res3 = refArr.compareAndSetAt(3, newValue, Data(3))
        assertTrue(!res3 && refArr.loadAt(3) == Data(2), "testAtomicArraycompareAndSetAt: FAIL 3")
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.compareAndSetAt(10, newValue, Data(4))
        }
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.compareAndSetAt(-1, newValue, Data(4))
        }
    }

    @Test
    fun exchangeAt() {
        val refArr = AtomicArray<Data?>(10) { null }
        val res4 = refArr.exchangeAt(4, Data(1))
        assertEquals(null, res4, "exchangeAt: FAIL 1")
        val res5 = refArr.exchangeAt(4, Data(2))
        assertEquals(Data(1), res5, "exchangeAt: FAIL 2")
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.exchangeAt(10, Data(1))
        }
        assertFailsWith<IndexOutOfBoundsException> {
            refArr.exchangeAt(-1, Data(1))
        }
    }

    @Test
    fun compareAndSetComparingByReference() {
        val datum1 = Data(1)
        val datum2 = Data(1)

        val atomicArray = AtomicArray(arrayOf(datum1))

        assertEquals(datum1, datum2)
        assertNotSame(datum1, datum2)

        // datum1 is equal to itself, CAS should succeed
        assertTrue(atomicArray.compareAndSetAt(0, datum1, datum2))

        // datum2 is not equal to datum1, they are two distinct, so CAS should fail
        assertFalse(atomicArray.compareAndSetAt(0, datum1, Data(2)))
    }

    @Test
    fun toStringTest() {
        val array = AtomicArray(arrayOf(Data(0), Data(1), Data(2)))
        assertEquals("[Data(value=0), Data(value=1), Data(value=2)]", array.toString())
    }

    @Test
    fun updateAt() {
        data class Data(val value: Int)

        val array = AtomicArray(3) { Data(it + 1) }

        array.updateAt(1) { it.copy(value = it.value * 3) }
        assertEquals("[Data(value=1), Data(value=6), Data(value=3)]", array.toString())

        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(-1) { it } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAt(3) { it } }
    }

    @Test
    fun updateAndFetchAt() {
        data class Data(val value: Int)

        val array = AtomicArray(3) { Data(it + 1) }

        assertEquals(Data(6), array.updateAndFetchAt(1) { it.copy(value = it.value * 3) })
        assertEquals("[Data(value=1), Data(value=6), Data(value=3)]", array.toString())
        assertEquals(Data(6), array.updateAndFetchAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(-1) { it } }
        assertFailsWith<IndexOutOfBoundsException> { array.updateAndFetchAt(3) { it } }
    }

    @Test
    fun fetchAndUpdateAt() {
        data class Data(val value: Int)

        val array = AtomicArray(3) { Data(it + 1) }

        assertEquals(Data(2), array.fetchAndUpdateAt(1) { it.copy(value = it.value * 3) })
        assertEquals("[Data(value=1), Data(value=6), Data(value=3)]", array.toString())
        assertEquals(Data(6), array.fetchAndUpdateAt(1) { it })

        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(-1) { it } }
        assertFailsWith<IndexOutOfBoundsException> { array.fetchAndUpdateAt(3) { it } }
    }
}
