// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include <vector>
#include <numeric>
#include <gtest/gtest.h>
#include <layer_transform.hpp>
#include "gna_matcher.hpp"

using namespace InferenceEngine;
using namespace GNAPluginNS;
using namespace GNATestIRs;

class FP32NonQuantizedTest : public GNATest<>{
 protected:

    void SetUp() override  {
    }
};

class FP32TestParams {
 public:
    int nChannels = 1;
    enum tagEltwise : uint8_t {
        eSumm,
        eMul
    } eltwise_type;
    FP32TestParams(int nChannels, uint8_t eltwise) : nChannels(nChannels), eltwise_type((tagEltwise)eltwise) {}
};

/**
 * parameter defines one of key dims size - number of channels in input tensor
 * due to gna-plugin implementation esential it is required to check 64 bits aligned tensors and non aligned
 */
class GNAFP32ParametricTest : public GNATest<::testing::TestWithParam<FP32TestParams>> {

};

static std::string getTestName(testing::TestParamInfo<FP32TestParams> obj) {
    return  "channels_" + std::to_string(obj.param.nChannels) + "_" + (obj.param.eltwise_type == FP32TestParams::eSumm ? "summ" : "mull");
}


TEST_P(GNAFP32ParametricTest, SplitFollowedByEltwiseMulOnAllignedCPU) {
    auto c = GetParam().nChannels;
    auto isMull = GetParam().eltwise_type == FP32TestParams::eMul;
    std::vector<float> input_data1(c, 3.0);
    std::vector<float> input_data2(c, 2.0);
    std::vector<float> input_data;
    input_data.insert(input_data.end(), input_data1.begin(), input_data1.end());
    input_data.insert(input_data.end(), input_data2.begin(), input_data2.end());

    std::vector<float> expected_result(c, isMull ? 6.0 : 5.0);
    assert_that().onInferModel(EltwiseAfterSplitModel(c, isMull))
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with().input("input_1", input_data).equals_to(expected_result);
}

FP32TestParams gna_fp32_test_params[] = {
    {7, FP32TestParams::eMul},
    {7, FP32TestParams::eSumm},
    {10, FP32TestParams::eMul},
    {10, FP32TestParams::eSumm},
    {32, FP32TestParams::eMul},
    {32, FP32TestParams::eSumm}
};

INSTANTIATE_TEST_CASE_P(GNAFP32Tests, GNAFP32ParametricTest,
    ::testing::ValuesIn(gna_fp32_test_params), getTestName);

TEST_F(FP32NonQuantizedTest, SplitFollowedByFCAndEltwiseOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {12.0, 12.0, 12.0, 12.0, 12.0,
                                          12.0, 12.0, 12.0, 12.0, 12.0};
    assert_that().onInferModel(FCWithPaddingAfterSplitModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, SliceFollowedByFCAndEltwiseOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {14.0, 14.0, 14.0, 14.0, 14.0, 14.0, 14.0, 14.0};
    assert_that().onInferModel(FCWithPaddingAfterSliceModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, SliceFollowedByAlignedFCAndEltwiseOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {18.0, 18.0, 18.0, 18.0};
    assert_that().onInferModel(SliceModelWithAlignedOutputs())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, SliceFollowedBy2FCsAnd2EltwisesOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {27.0, 27.0, 27.0, 27.0, 27.0, 27.0, 27.0, 27.0};
    assert_that().onInferModel(twoFCWithPaddingAfterSliceModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitAfterFCFollowedByFCAndEltwiseOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {232.0, 232.0, 232.0, 232.0, 232.0,
                                          232.0, 232.0, 232.0, 232.0, 232.0};
    assert_that().onInferModel(FCBeforeSplitModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}


TEST_F(FP32NonQuantizedTest, ConcatPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0};

    assert_that().onInferModel(concatModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, DoubleConcatPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0,
                                          141.0, 141.0, 141.0, 141.0, 141.0};

    assert_that().onInferModel(doubleConcatModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, multiple_inputs_correct_results) {
    std::vector<float> input_data  = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> input2_data = {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0};
    std::vector<float> result      = {30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0, 30.0};

    assert_that().onInferModel(two_inputs_to_affine())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with().input("input_1", input_data).And().input("input_2", input2_data).result().equals_to(result);
}


TEST_F(FP32NonQuantizedTest, CropWithoutOffsetPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {11.0, 11.0, 11.0, 11.0, 11.0,
                                          11.0, 11.0, 11.0, 11.0, 11.0};

    assert_that().onInferModel(cropWithoutOffsetModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, CropWithAlignedOffsetPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {3.0, 3.0, 3.0, 3.0, 3.0,
                                          3.0, 3.0, 3.0, 3.0, 3.0};

    assert_that().onInferModel(cropWithAlignedOffsetModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, CropWithOffsetPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {7.0, 7.0, 7.0, 7.0, 7.0,
                                          7.0, 7.0, 7.0, 7.0, 7.0};

    assert_that().onInferModel(cropWithOffsetModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, CropWithMaxOffsetPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {1.0, 1.0, 1.0, 1.0, 1.0,
                                          1.0, 1.0, 1.0, 1.0, 1.0};

    assert_that().onInferModel(cropWithMaxOffsetModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, CropWithOffsetAfterFCPropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {111.0, 111.0, 111.0, 111.0, 111.0,
                                          111.0, 111.0, 111.0, 111.0, 111.0};

    assert_that().onInferModel(cropWithOffsetExtendedModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}


TEST_F(FP32NonQuantizedTest, TwoCropsWithDistanceInBetween) {
    std::vector<float> input_data ={1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
                                    17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0,
                                    33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0};

    std::vector<float> expected_result = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
                                          33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0};

    assert_that().onInferModel(twoCropsModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, ThreeCropsWithDistanceInBetween) {
    std::vector<float> input_data ={1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
                                    9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
                                    17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0,
                                    25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0,
                                    33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0};

    std::vector<float> expected_result = {1.0,   2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,
                                          17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0,
                                          33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0};

    assert_that().onInferModel(threeCropsModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, ThreeCropsWithReshapeWithDistanceInBetween) {
    std::vector<float> input_data ={1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
                                    9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
                                    17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0,
                                    25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0,
                                    33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0};

    std::vector<float> expected_result = {1.0,   2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,
                                          17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0,
                                          33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0};

    assert_that().onInferModel(threeCropsWithReshapeModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}


TEST_F(FP32NonQuantizedTest, CopySimpleCasePropagateForwardWithSuccessOnCPU) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
                                     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    std::vector<float> expected_result = {12.0, 12.0, 12.0, 12.0, 12.0,
                                          12.0, 12.0, 12.0, 12.0, 12.0,
                                          11.0, 11.0, 11.0, 11.0, 11.0,
                                          11.0, 11.0, 11.0, 11.0, 11.0,};

    assert_that().onInferModel(copyModel())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}


TEST_F(FP32NonQuantizedTest, ScaleShiftWithBroadcastSupported) {
    std::vector<float> input_data (40, 1.0);

    std::vector<float> expected_result = {2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0,
                                          2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0,
                                          2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0,
                                          2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0,
                                          2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0};

    assert_that().onInferModel(ScaleShift3DModel()).withWeigthsPattern({1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f,8.0f})
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input_and_expected_output(input_data, expected_result);
}

TEST_F(FP32NonQuantizedTest, ConcatWithConstInputPropagatedForward) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};

    std::vector<float> expected_result = {121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0,
                                          121.0, 121.0, 121.0, 121.0, 121.0};

    assert_that().onInferModel(concatModelWithConstLayer())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, InputSplitConcatPropagateForward) {
    std::vector<float> input_data(64, 1.0f);
    std::vector<float> expected_result(10, 64.f);

    assert_that().onInferModel(InputSplitConcatModel())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, InputSplitConcatUnalignedPropagateForward) {
    std::vector<float> input_data(20, 1.0f);
    std::vector<float> expected_result(10, 20.f);

    assert_that().onInferModel(InputSplitConcatModelUnaligned())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, InputSplitConcatReshapeUnalignedPropagateForward) {
    std::vector<float> input_data(20, 1.0f);
    std::vector<float> expected_result(10, 20.f);

    assert_that().onInferModel(InputSplitConcatReshapeModelUnaligned())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, LSTMCellPropagateForward) {
    std::vector<float> input_data(96, 0.10f);
    std::vector<float> expected_result(32, 0.27119124f);

    assert_that().onInferModel(LSTMCellOnlyModel()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, LSTMCellUnalignedPropagateForward) {
    std::vector<float> input_data(30, 0.10f);
    std::vector<float> expected_result(10, 0.10488615);

    assert_that().onInferModel(LSTMCellOnlyModelUnaligned()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, TI1PropagateForward) {
    std::vector<float> input_data(10, 0.10f);
    std::vector<float> expected_result1(10, 0.22478661f);
    std::vector<float> expected_result2(12, 0.22699516);

    assert_that().onInferModel(TIModelWithLSTMCell1()).withWeigthsPattern("ScaleShift_1", {1.0f}).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result1).equals_to(expected_result2);
}

TEST_F(FP32NonQuantizedTest, TI1PropagateForwardWithoutScaleShift) {
    std::vector<float> input_data(10, 0.10f);
    std::vector<float> expected_result1(10, 0.22478661f);
    std::vector<float> expected_result2(12, 0.22699516f);

    assert_that().onInferModel(TIModelWithLSTMCell1WithoutScaleShift()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result1).equals_to(expected_result2);
}

TEST_F(FP32NonQuantizedTest, TI1AlignedPropagateForward) {
    std::vector<float> input_data(32, 0.1f);
    std::vector<float> expected_result1(32, 0.25883245);
    std::vector<float> expected_result2(12, 0.59515548f);

    assert_that().onInferModel(TIModelWithLSTMCell1Aligned()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result1).And().equals_to(expected_result2);
}

TEST_F(FP32NonQuantizedTest, TI3AlignedPropagateForward) {
    std::vector<float> input_data(96, 0.1f);
    std::vector<float> expected_result1(32, 0.42592844f);
    std::vector<float> expected_result2(12, 0.97069889f);

    assert_that().onInferModel(TIModelWithLSTMCell3Aligned()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result1).And().equals_to(expected_result2);
}

TEST_F(FP32NonQuantizedTest, TI2PropagateForward) {
    std::vector<float> input_data(20, 0.1f);
    std::vector<float> expected_result1(10, 0.22478661f);
    std::vector<float> expected_result2(12, 0.22699516f);

    assert_that().onInferModel(TIModelWithLSTMCell2()).withWeigthsPattern({0.1f})
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result1).equals_to(expected_result2);
}

TEST_F(FP32NonQuantizedTest, EltwiseWithConstInputPropagatedForward) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0};

    assert_that().onInferModel(eltwiseSumModelWithConstLayer())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, EltwiseWithConstInputReorderPropagatedForward) {
    std::vector<float> input_data = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    std::vector<float> expected_result = {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0};

    assert_that().onInferModel(eltwiseSumModelWithConstLayer2())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, EltwiseMulWithConstInputReorderPropagatedForward) {
    std::vector<float> input_data = {3.0, 1.0, 3.0, 1.0, 3.0, 1.0, 3.0, 1.0, 3.0, 1.0};
    std::vector<float> expected_result = {6.0, 2.0, 6.0, 2.0, 6.0, 2.0, 6.0, 2.0, 6.0, 2.0};

    assert_that().onInferModel(eltwiseMulModelWithConstLayer())
        .inNotCompactMode().withWeigthsPattern({2}).gna().propagate_forward().onCPU()
        .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, PowerWithScaleFactorPropagateForward) {
    std::vector<float> input_data(10, 2.f);
    std::vector<float> expected_result(12, 21.f);

    assert_that().onInferModel(PowerWithScaleFactor1())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatThroughScaleShiftPropagateForward) {
    std::vector<float> input_data(30, 1.f);
    std::vector<float> expected_result(20, 41.f);
    expected_result.insert(expected_result.end(), 20, 2.f);

    assert_that().onInferModel(SplitToConcatThroughScaleShift())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, TwoOutputsPropagateForward) {
    std::vector<float> input_data(10, 1);
    std::vector<float> result1(20, 11.f);
    std::vector<float> result2(10, 11.f);

    assert_that().onInferModel(TwoOutputs())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with().input("input_1", input_data).result().equals_to(result1).And().equals_to(result2);
}

TEST_F(FP32NonQuantizedTest, TwoOutputsDiffPrecisionPropagateForward) {
    std::vector<float> input_data(10, 1);
    std::vector<float> result1(10, 11.f);
    std::vector<float> result2(20, 11.f);

    assert_that().onInferModel(TwoOutputsDiffPrecision())
        .inNotCompactMode().gna().propagate_forward().onCPU()
        .called_with().input("input_1", input_data).result().equals_to(result1).And().equals_to(result2);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsNotAlignedNoFC) {
    std::vector<float> input_data(20);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsNotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2By50InputsNotAlignedNoFC) {
    std::vector<float> input_data(100);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2By50InputsNotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy) {
    std::vector<float> input_data(100);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2By50InputsNotAlignedNoFCWithInCopyWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsNotAlignedNoFC) {
    std::vector<float> input_data(30);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsNotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith4InputsNotAlignedNoFC) {
    std::vector<float> input_data(40);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith4InputsNotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy) {
    std::vector<float> input_data(40);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith4InputsNotAlignedNoFCWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsNotAlignedNoFC) {
    std::vector<float> input_data(100);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy) {
    std::vector<float> input_data(100);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedNoFCWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy) {
    std::vector<float> input_data(10);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith10By1InputsNotAlignedNoFCWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsNotAlignedWithFC) {
    std::vector<float> input_data(20);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(10, 211.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsNotAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsNotAlignedWithFC) {
    std::vector<float> input_data(30);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(10, 466.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsNotAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3By512InputsWithOutCopy) {
    std::vector<float> input_data(1536);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith3By512InputsWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsNotAlignedWithFC) {
    std::vector<float> input_data(100);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(10, 5051.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsNotAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2InputsAlignedNoFC) {
    std::vector<float> input_data(64);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith2By64InputsAlignedNoFC) {
    std::vector<float> input_data(128);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2By64InputsAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2Inputs1360NotAlignedNoFC) {
    std::vector<float> input_data(1360);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2Inputs1360NotAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy) {
    std::vector<float> input_data(128);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2By64InputsAlignedNoFCWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy) {
    std::vector<float> input_data(64);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsAlignedNoFCWithInCopyWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith3InputsAlignedNoFC) {
    std::vector<float> input_data(96);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy) {
    std::vector<float> input_data(96);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsAlignedNoFCWithInCopyWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, DISABLED_SplitToConcatWith10InputsAlignedNoFC) {
    std::vector<float> input_data(320);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsAlignedNoFC())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy) {
    std::vector<float> input_data(320);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsAlignedNoFCWithInCopyWithOutCopy())
            .inNotCompactMode().gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(input_data);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedWithFC) {
    std::vector<float> input_data(64);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 2081.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith2InputsAlignedWithFCWithInCopy) {
    std::vector<float> input_data(64);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 2081.0f);
    assert_that().onInferModel(SplitToConcatWith2InputsAlignedWithFCWithInCopy())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedWithFC) {
    std::vector<float> input_data(96);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 4657.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith3InputsAlignedWithFCWithInCopy) {
    std::vector<float> input_data(96);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 4657.0f);
    assert_that().onInferModel(SplitToConcatWith3InputsAlignedWithFCWithInCopy())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedWithFC) {
    std::vector<float> input_data(320);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 51361.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsAlignedWithFC())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, SplitToConcatWith10InputsAlignedWithFCWithInCopy) {
    std::vector<float> input_data(320);
    std::iota(input_data.begin(), input_data.end(), 1.0f);
    std::vector<float> expected_result(32, 51361.0f);
    assert_that().onInferModel(SplitToConcatWith10InputsAlignedWithFCWithInCopy())
            .inNotCompactMode().withWeigthsPattern({1}).gna().propagate_forward().onCPU()
            .called_with_input(input_data).equals_to(expected_result);
}

TEST_F(FP32NonQuantizedTest, ReshapeConvolutionLessThan48Filters) {
    std::vector<float> input_data(800, 1.f);
    std::vector<float> expected_result(1600, 8.f);

    assert_that().onInferModel(ReshapeConvolutionLessThan48Filters())
            .inNotCompactMode()
            .withWeigthsPattern({1})
            .gna()
            .propagate_forward()
            .onCPU()
            .called_with_input(input_data)
            .equals_to(expected_result);
}

