/***************************************************************************************************
 * Copyright (c) 2017-2020, NVIDIA CORPORATION.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice,
 *this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *notice, this list of conditions and the following disclaimer in the
 *documentation and/or other materials provided with the distribution.
 *     * Neither the name of the NVIDIA CORPORATION nor the names of its
 *contributors may be used to endorse or promote products derived from this
 *software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY DIRECT,
 *INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 *OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TOR (INCLUDING
 *NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **************************************************************************************************/
/*! \file
\brief unit tests for tensor layout
*/

#include "../common/cutlass_unit_test.h"

#include "cutlass/layout/tensor.h"
#include "cutlass/tensor_coord.h"

/////////////////////////////////////////////////////////////////////////////////////////////////
namespace test {
namespace layout {
void test_NHWC_layout(int n_size, int h_size, int w_size, int c_size) {
    int ldc = c_size + 1;
    int ldw = ldc * (w_size + 2);
    int ldh = ldw * (h_size + 3);

    cutlass::layout::TensorNHWC::Stride tensor_stride({ldc, ldw, ldh});

    cutlass::layout::TensorNHWC tensor_nhwc(tensor_stride);

    // test pointer offset
    for (int n_idx = 0; n_idx < n_size; n_idx++) {
        for (int h_idx = 0; h_idx < h_size; h_idx++) {
            for (int w_idx = 0; w_idx < w_size; w_idx++) {
                for (int c_idx = 0; c_idx < c_size; c_idx++) {
                    cutlass::Tensor4DCoord tensor_coord(n_idx, h_idx, w_idx,
                                                        c_idx);
                    auto ptr_offset = tensor_nhwc(tensor_coord);
                    decltype(ptr_offset) reference_offset =
                            c_idx + w_idx * ldc + h_idx * ldw + n_idx * ldh;
                    EXPECT_EQ(ptr_offset, reference_offset);
                }
            }
        }
    }

    // test stride
    auto stride = tensor_nhwc.stride();
    EXPECT_EQ(stride, tensor_stride);

    // test capacity
    auto capacity = tensor_nhwc.capacity(
            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));
    decltype(capacity) referece_capacity = ldh * n_size;
    EXPECT_EQ(capacity, referece_capacity);

    // test packed
    auto packed_tensor_layout = tensor_nhwc.packed(
            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));
    auto packed_stride = packed_tensor_layout.stride();
    EXPECT_EQ(packed_stride,
              cutlass::layout::TensorNHWC::Stride(
                      {c_size, w_size * c_size, h_size * w_size * c_size}));
}

void test_NCHW_layout(int n_size, int c_size, int h_size, int w_size) {
    int ldw = w_size + 1;
    int ldh = ldw * (h_size + 2);
    int ldc = ldh * (c_size + 1);

    cutlass::layout::TensorNCHW::Stride tensor_stride({ldw, ldh, ldc});

    cutlass::layout::TensorNCHW tensor_nchw(tensor_stride);

    // test pointer offset
    for (int n_idx = 0; n_idx < n_size; n_idx++) {
        for (int c_idx = 0; c_idx < c_size; c_idx++) {
            for (int h_idx = 0; h_idx < w_size; h_idx++) {
                for (int w_idx = 0; w_idx < c_size; w_idx++) {
                    // tensor4DCoord is always created in nhwc order
                    cutlass::Tensor4DCoord tensor_coord(n_idx, h_idx, w_idx,
                                                        c_idx);
                    auto ptr_offset = tensor_nchw(tensor_coord);
                    decltype(ptr_offset) reference_offset =
                            w_idx + h_idx * ldw + c_idx * ldh + n_idx * ldc;
                    EXPECT_EQ(ptr_offset, reference_offset);
                }
            }
        }
    }

    // test stride
    auto stride = tensor_nchw.stride();
    EXPECT_EQ(stride, tensor_stride);

    // test capacity
    auto capacity = tensor_nchw.capacity(
            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));
    decltype(capacity) referece_capacity = ldc * n_size;
    EXPECT_EQ(capacity, referece_capacity);

    // test packed
    auto packed_tensor_layout = tensor_nchw.packed(
            cutlass::Tensor4DCoord(n_size, h_size, w_size, c_size));
    auto packed_stride = packed_tensor_layout.stride();
    EXPECT_EQ(packed_stride,
              cutlass::layout::TensorNHWC::Stride(
                      {w_size, w_size * h_size, w_size * h_size * c_size}));
}
}  // namespace layout
}  // namespace test

/////////////////////////////////////////////////////////////////////////////////////////////////

TEST(Layout_Tensor, NHWC_32_12_10_14) {
    int n_size = 32;
    int h_size = 12;
    int w_size = 10;
    int c_size = 14;
    test::layout::test_NHWC_layout(n_size, h_size, w_size, c_size);
}

/////////////////////////////////////////////////////////////////////////////////////////////////

TEST(Layout_Tensor, NCHW_32_12_10_14) {
    int n_size = 32;
    int c_size = 12;
    int h_size = 10;
    int w_size = 14;
    test::layout::test_NCHW_layout(n_size, c_size, h_size, w_size);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
