//
// Copyright © 2017-2023 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <Layer.hpp>
#include <LayersFwd.hpp>

#include <armnn/Types.hpp>
#include <armnn/backends/IBackendInternal.hpp>
#include <armnn/backends/ILayerSupport.hpp>
#include <armnn/BackendHelper.hpp>
#include <armnn/BackendRegistry.hpp>
#include <armnn/utility/PolymorphicDowncast.hpp>
#include <armnn/utility/TransformIterator.hpp>

#include <armnn/backends/WorkloadFactory.hpp>

#include <sstream>

namespace armnn
{

namespace
{
using LayerList = std::list<Layer*>;
using Iterator = LayerList::const_iterator; // Const so pointers in the list can't be modified externally.

const TensorInfo OverrideDataType(const TensorInfo& info, Optional<DataType> type)
{
    if (!type)
    {
        return info;
    }

    return TensorInfo(info.GetShape(),
                      type.value(),
                      info.GetQuantizationScale(),
                      info.GetQuantizationOffset(),
                      info.IsConstant());
}

} // anonymous namespace

inline armnn::Optional<armnn::DataType> GetBiasTypeFromWeightsType(armnn::Optional<armnn::DataType> weightsType)
{
    if (!weightsType)
    {
        return weightsType;
    }

    switch(weightsType.value())
    {
        case armnn::DataType::BFloat16:
        case armnn::DataType::Float16:
        case armnn::DataType::Float32:
            return weightsType;
        case armnn::DataType::QAsymmS8:
        case armnn::DataType::QAsymmU8:
        case armnn::DataType::QSymmS8:
        case armnn::DataType::QSymmS16:
            return armnn::DataType::Signed32;
        default:
            ARMNN_ASSERT_MSG(false, "GetBiasTypeFromWeightsType(): Unsupported data type.");
    }
    return armnn::EmptyOptional();
}


bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId,
                                                     const IConnectableLayer& connectableLayer,
                                                     Optional<DataType> dataType,
                                                     std::string& outReasonIfUnsupported,
                                                     const ModelOptions& modelOptions)
{
    Optional<std::string&> reason = outReasonIfUnsupported;
    bool result;
    const Layer& layer = *(PolymorphicDowncast<const Layer*>(&connectableLayer));

    auto const& backendRegistry = BackendRegistryInstance();
    if (!backendRegistry.IsBackendRegistered(backendId))
    {
        std::stringstream ss;
        ss << connectableLayer.GetName() << " is not supported on " << backendId
           << " because this backend is not registered.";

        outReasonIfUnsupported = ss.str();
        return false;
    }

    auto backendFactory = backendRegistry.GetFactory(backendId);
    auto backendObject = backendFactory();
    auto layerSupport = backendObject->GetLayerSupport(modelOptions);
    auto layerSupportObject = LayerSupportHandle(layerSupport, backendId);

    switch(layer.GetType())
    {
        case LayerType::Activation:
        {
            auto cLayer = PolymorphicDowncast<const ActivationLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsActivationSupported(
                                           OverrideDataType(input, dataType),
                                           OverrideDataType(output, dataType),
                                           cLayer->GetParameters(),
                                           reason);
            break;
        }
        case LayerType::Addition:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsAdditionSupported(
                                        OverrideDataType(input0, dataType),
                                        OverrideDataType(input1, dataType),
                                        OverrideDataType(output, dataType),
                                        reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::ArgMinMax:
        {
            auto cLayer = PolymorphicDowncast<const ArgMinMaxLayer*>(&layer);
            const ArgMinMaxDescriptor& descriptor = cLayer->GetParameters();

            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsArgMinMaxSupported(
                    OverrideDataType(input, dataType),
                    OverrideDataType(output, DataType::Signed32),
                    descriptor,
                    reason);
            break;
        }
        case LayerType::BatchMatMul:
        {
            auto cLayer = PolymorphicDowncast<const BatchMatMulLayer*>(&layer);
            const BatchMatMulDescriptor& descriptor = cLayer->GetParameters();

            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsBatchMatMulSupported(
                            OverrideDataType(input0, dataType),
                            OverrideDataType(input1, dataType),
                            OverrideDataType(output, dataType),
                            descriptor,
                            reason);
            break;
        }
        case LayerType::BatchNormalization:
        {
            auto cLayer = PolymorphicDowncast<const BatchNormalizationLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            const TensorInfo& mean = cLayer->m_Mean->GetTensorInfo();
            const TensorInfo& var = cLayer->m_Variance->GetTensorInfo();
            const TensorInfo& beta = cLayer->m_Beta->GetTensorInfo();
            const TensorInfo& gamma = cLayer->m_Gamma->GetTensorInfo();
            result = layerSupportObject.IsBatchNormalizationSupported(
                                                   OverrideDataType(input, dataType),
                                                   OverrideDataType(output, dataType),
                                                   OverrideDataType(mean, dataType),
                                                   OverrideDataType(var, dataType),
                                                   OverrideDataType(beta, dataType),
                                                   OverrideDataType(gamma, dataType),
                                                   cLayer->GetParameters(),
                                                   reason);
            break;
        }
        case LayerType::BatchToSpaceNd:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            auto cLayer = PolymorphicDowncast<const BatchToSpaceNdLayer*>(&layer);

            result = layerSupportObject.IsBatchToSpaceNdSupported(OverrideDataType(input, dataType),
                                                                  OverrideDataType(output, dataType),
                                                                  cLayer->GetParameters(),
                                                                  reason);
            break;
        }
        case LayerType::Cast:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsCastSupported(OverrideDataType(input, dataType),
                                                        OverrideDataType(output, dataType),
                                                        reason);
            break;
        }
        case LayerType::ChannelShuffle:
        {
            auto cLayer = PolymorphicDowncast<const ChannelShuffleLayer*>(&layer);

            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetInputSlot(0).GetTensorInfo();

            const ChannelShuffleDescriptor descriptor = cLayer->GetParameters();

            result = layerSupportObject.IsChannelShuffleSupported(OverrideDataType(input, dataType),
                                                                  OverrideDataType(output, dataType),
                                                                  descriptor,
                                                                  reason);
            break;
        }
        case LayerType::Comparison:
        {
            auto cLayer = PolymorphicDowncast<const ComparisonLayer*>(&layer);

            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsComparisonSupported(OverrideDataType(input0, dataType),
                                                              OverrideDataType(input1, dataType),
                                                              OverrideDataType(output, DataType::Boolean),
                                                              cLayer->GetParameters(),
                                                              reason);
            break;
        }
        case LayerType::Constant:
        {
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsConstantSupported(OverrideDataType(output, dataType), reason);
            break;
        }
        case LayerType::ConvertFp16ToFp32:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsConvertFp16ToFp32Supported(input, output, reason);
            break;
        }
        case LayerType::ConvertFp32ToFp16:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsConvertFp32ToFp16Supported(input, output, reason);
            break;
        }
        case LayerType::Convolution2d:
        {
            auto cLayer = PolymorphicDowncast<const Convolution2dLayer*>(&layer);

            const TensorInfo input  = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                       dataType);
            const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
            ARMNN_ASSERT_MSG(layer.GetInputSlot(1).GetConnection(),
                             "Convolution2dLayer: Weights should be connected as a Constant Layer.");
            const TensorInfo weights = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(),
                                                        dataType);

            const Convolution2dDescriptor& descriptor = cLayer->GetParameters();

            // Construct optional biases object based on the value of m_BiasEnabled
            Optional<TensorInfo> biases;
            if (descriptor.m_BiasEnabled)
            {
                ARMNN_ASSERT_MSG(layer.GetInputSlot(2).GetConnection(),
                                 "Convolution2dLayer: Bias should be connected as a Constant Layer.");
                biases = OverrideDataType(layer.GetInputSlot(2).GetTensorInfo(),
                                          GetBiasTypeFromWeightsType(dataType));
            }

            result = layerSupportObject.IsConvolution2dSupported(
                                              input,
                                              output,
                                              descriptor,
                                              weights,
                                              biases,
                                              reason);
            break;
        }
        case LayerType::Convolution3d:
        {
            auto cLayer = PolymorphicDowncast<const Convolution3dLayer*>(&layer);

            const TensorInfo input  = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                       dataType);
            const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);

            ARMNN_ASSERT_MSG(layer.GetInputSlot(1).GetConnection(),
                             "Convolution3dLayer: Weights should be connected as a Constant Layer.");
            const TensorInfo weights = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(),
                                                        dataType);

            const Convolution3dDescriptor& descriptor = cLayer->GetParameters();

            // Construct optional biases object based on the value of m_BiasEnabled
            Optional<TensorInfo> biases;
            if (descriptor.m_BiasEnabled)
            {
                biases = OverrideDataType(layer.GetInputSlot(2).GetTensorInfo(),
                                          GetBiasTypeFromWeightsType(dataType));
            }

            result = layerSupportObject.IsConvolution3dSupported(
                                              input,
                                              output,
                                              descriptor,
                                              weights,
                                              biases,
                                              reason);
            break;
        }
        case LayerType::Debug:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsDebugSupported(OverrideDataType(input, dataType),
                                                          OverrideDataType(output, dataType),
                                                          reason);
            break;
        }
        case LayerType::DepthToSpace:
        {
            auto cLayer = PolymorphicDowncast<const DepthToSpaceLayer*>(&layer);

            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsDepthToSpaceSupported(OverrideDataType(input, dataType),
                                                                 OverrideDataType(output, dataType),
                                                                 cLayer->GetParameters(),
                                                                 reason);
            break;
        }
        case LayerType::DepthwiseConvolution2d:
        {
            auto cLayer = PolymorphicDowncast<const DepthwiseConvolution2dLayer*>(&layer);
            const TensorInfo& input   = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                         dataType);
            const TensorInfo& output  = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
            const TensorInfo& weights = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(),
                                                         dataType);

            ARMNN_ASSERT(cLayer->GetInputSlot(1).GetConnection() != nullptr);

            const DepthwiseConvolution2dDescriptor& descriptor = cLayer->GetParameters();

            // Construct optional biases object based on the value of m_BiasEnabled
            Optional<TensorInfo> biases;
            if (descriptor.m_BiasEnabled)
            {
                biases = OverrideDataType(cLayer->GetInputSlot(2).GetTensorInfo(),
                                          GetBiasTypeFromWeightsType(dataType));
            }

            result = layerSupportObject.IsDepthwiseConvolutionSupported(input,
                                                                        output,
                                                                        descriptor,
                                                                        weights,
                                                                        biases,
                                                                        reason);
            break;
        }
        case LayerType::Dequantize:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsDequantizeSupported(input,
                                                              OverrideDataType(output, dataType),
                                                              reason);
            break;
        }
        case LayerType::DetectionPostProcess:
        {
            auto cLayer = PolymorphicDowncast<const DetectionPostProcessLayer*>(&layer);
            const TensorInfo& boxEncodings = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& scores = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& anchors = cLayer->m_Anchors->GetTensorInfo();

            const TensorInfo& detectionBoxes = layer.GetOutputSlot(0).GetTensorInfo();
            const TensorInfo& detectionClasses = layer.GetOutputSlot(1).GetTensorInfo();
            const TensorInfo& detectionScores = layer.GetOutputSlot(2).GetTensorInfo();
            const TensorInfo& numDetections = layer.GetOutputSlot(3).GetTensorInfo();

            const DetectionPostProcessDescriptor& descriptor = cLayer->GetParameters();
            result = layerSupportObject.IsDetectionPostProcessSupported(boxEncodings,
                                                                        scores,
                                                                        anchors,
                                                                        detectionBoxes,
                                                                        detectionClasses,
                                                                        detectionScores,
                                                                        numDetections,
                                                                        descriptor,
                                                                        reason);
            break;
        }
        case LayerType::ElementwiseBinary:
        {
            auto cLayer = PolymorphicDowncast<const ElementwiseBinaryLayer*>(&layer);

            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            std::vector<TensorInfo> infos = { OverrideDataType(input0, dataType),
                                              OverrideDataType(input1, dataType),
                                              OverrideDataType(output, dataType) };
            result = layerSupport->IsLayerSupported(LayerType::ElementwiseBinary,
                                                    infos,
                                                    cLayer->GetParameters(),
                                                    EmptyOptional(),
                                                    EmptyOptional(),
                                                    reason);
            break;
        }
        case LayerType::ElementwiseUnary:
        {
            auto cLayer = PolymorphicDowncast<const ElementwiseUnaryLayer*>(&layer);

            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsElementwiseUnarySupported(OverrideDataType(input, dataType),
                                                                    OverrideDataType(output, dataType),
                                                                    cLayer->GetParameters(),
                                                                    reason);
            break;
        }
        case LayerType::Fill:
        {
            auto cLayer = PolymorphicDowncast<const FillLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            const FillDescriptor& descriptor = cLayer->GetParameters();

            result = layerSupportObject.IsFillSupported(
                OverrideDataType(input, dataType),
                OverrideDataType(output, dataType),
                descriptor,
                reason);
            break;
        }
        case LayerType::FakeQuantization:
        {
            auto cLayer = PolymorphicDowncast<const FakeQuantizationLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsFakeQuantizationSupported(OverrideDataType(input, dataType),
                                                                    cLayer->GetParameters(),
                                                                    reason);
            break;
        }
        case LayerType::Floor:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsFloorSupported(OverrideDataType(input, dataType),
                                                         OverrideDataType(output, dataType),
                                                         reason);
            break;
        }
        case LayerType::FullyConnected:
        {
            auto cLayer = PolymorphicDowncast<const FullyConnectedLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            const FullyConnectedDescriptor& descriptor = cLayer->GetParameters();
            TensorInfo weightsInfo;
            const TensorInfo* weightsInfoPtr = nullptr;

            weightsInfo = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(), dataType);
            weightsInfoPtr = &weightsInfo;

            TensorInfo biasInfo;
            const TensorInfo* biasInfoPtr = nullptr;
            static const TensorInfo dummyBFloat16Bias(TensorShape({1,1,1,1}), DataType::BFloat16);
            static const TensorInfo dummyFloat16Bias(TensorShape({1,1,1,1}), DataType::Float16);
            static const TensorInfo dummyFloat32Bias(TensorShape({1,1,1,1}), DataType::Float32);
            static const TensorInfo dummyQA8Bias(TensorShape({1,1,1,1}), DataType::Signed32);

            if (descriptor.m_BiasEnabled)
            {
                biasInfo = OverrideDataType(layer.GetInputSlot(2).GetTensorInfo(), dataType);
                biasInfoPtr = &biasInfo;
            }
            else
            {
                // If biases are not enabled pass a dummy tensorinfo for the validation
                switch(input.GetDataType())
                {
                    case DataType::BFloat16:
                    {
                        biasInfoPtr = &dummyBFloat16Bias;
                        break;
                    }
                    case DataType::Float16:
                    {
                        biasInfoPtr = &dummyFloat16Bias;
                        break;
                    }
                    case DataType::Float32:
                    {
                        biasInfoPtr = &dummyFloat32Bias;
                        break;
                    }
                    case DataType::QAsymmU8:
                    case DataType::QAsymmS8:
                    case DataType::QSymmS8:
                    case DataType::QSymmS16:
                    {
                        biasInfoPtr = &dummyQA8Bias;
                        break;
                    }
                    default:
                    {
                        ARMNN_ASSERT_MSG(false, "Unexpected bias type");
                    }
                }
            }
            result = layerSupportObject.IsFullyConnectedSupported(
                                               OverrideDataType(input, dataType),
                                               OverrideDataType(output, dataType),
                                               *weightsInfoPtr,
                                               *biasInfoPtr,
                                               descriptor,
                                               reason);
            break;
        }
        case LayerType::Gather:
        {
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            auto cLayer = PolymorphicDowncast<const GatherLayer*>(&layer);
            const GatherDescriptor& descriptor = cLayer->GetParameters();
            result = layerSupportObject.IsGatherSupported(OverrideDataType(input0, dataType),
                                                          input1,
                                                          OverrideDataType(output, dataType),
                                                          descriptor,
                                                          reason);
            break;
        }
        case LayerType::GatherNd:
        {
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsGatherNdSupported(OverrideDataType(input0, dataType),
                                                            input1,
                                                            OverrideDataType(output, dataType),
                                                            reason);
            break;
        }
        case LayerType::Input:
        {
            const TensorInfo& input = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsInputSupported(OverrideDataType(input, dataType), reason);
            break;
        }
        case LayerType::InstanceNormalization:
        {
            auto cLayer = PolymorphicDowncast<const InstanceNormalizationLayer*>(&layer);
            const InstanceNormalizationDescriptor& descriptor = cLayer->GetParameters();

            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsInstanceNormalizationSupported(
                OverrideDataType(input, dataType),
                OverrideDataType(output, dataType),
                descriptor,
                reason);
            break;
        }
        case LayerType::L2Normalization:
        {
            auto cLayer = PolymorphicDowncast<const L2NormalizationLayer*>(&layer);
            const L2NormalizationDescriptor& descriptor = cLayer->GetParameters();

            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsL2NormalizationSupported(
                                                OverrideDataType(input, dataType),
                                                OverrideDataType(output, dataType),
                                                descriptor,
                                                reason);
            break;
        }
        case LayerType::LogicalBinary:
        {
            auto cLayer = PolymorphicDowncast<const LogicalBinaryLayer*>(&layer);

            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsLogicalBinarySupported(input0,
                                                                 input1,
                                                                 output,
                                                                 cLayer->GetParameters(),
                                                                 reason);
            break;
        }
        case LayerType::LogSoftmax:
        {
            auto cLayer = PolymorphicDowncast<const LogSoftmaxLayer*>(&layer);

            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsLogSoftmaxSupported(OverrideDataType(input, dataType),
                                                              OverrideDataType(output, dataType),
                                                              cLayer->GetParameters(),
                                                              reason);
            break;
        }
        case LayerType::Lstm:
        {
            auto cLayer = PolymorphicDowncast<const LstmLayer*>(&layer);
            const LstmDescriptor& descriptor = cLayer->GetParameters();

            // All inputs.
            const TensorInfo& input = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                       dataType);
            const TensorInfo& outputStateIn = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(),
                                                               dataType);
            const TensorInfo& cellStateIn = OverrideDataType(layer.GetInputSlot(2).GetTensorInfo(),
                                                             dataType);
            // All outputs
            const TensorInfo& scratchBuffer = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
            const TensorInfo& outputStateOut = OverrideDataType(layer.GetOutputSlot(1).GetTensorInfo(), dataType);
            const TensorInfo& cellStateOut = OverrideDataType(layer.GetOutputSlot(2).GetTensorInfo(), dataType);
            const TensorInfo& output = OverrideDataType(layer.GetOutputSlot(3).GetTensorInfo(), dataType);

            // Basic parameters
            const TensorInfo& inputToForgetWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToForgetWeights->GetTensorInfo(), dataType);
            const TensorInfo& inputToCellWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToCellWeights->GetTensorInfo(), dataType);
            const TensorInfo& inputToOutputWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToOutputWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToForgetWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToForgetWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToCellWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToCellWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToOutputWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToOutputWeights->GetTensorInfo(), dataType);
            const TensorInfo& forgetGateBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_ForgetGateBias->GetTensorInfo(), dataType);
            const TensorInfo& cellBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_CellBias->GetTensorInfo(), dataType);
            const TensorInfo& outputGateBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_OutputGateBias->GetTensorInfo(), dataType);

            LstmInputParamsInfo paramsInfo;

            paramsInfo.m_InputToForgetWeights     = &inputToForgetWeights;
            paramsInfo.m_InputToCellWeights       = &inputToCellWeights;
            paramsInfo.m_InputToOutputWeights     = &inputToOutputWeights;
            paramsInfo.m_RecurrentToForgetWeights = &recurrentToForgetWeights;
            paramsInfo.m_RecurrentToCellWeights   = &recurrentToCellWeights;
            paramsInfo.m_RecurrentToOutputWeights = &recurrentToOutputWeights;
            paramsInfo.m_ForgetGateBias           = &forgetGateBias;
            paramsInfo.m_CellBias                 = &cellBias;
            paramsInfo.m_OutputGateBias           = &outputGateBias;


            // Optional parameters
            TensorInfo optInputToInputWeights;
            TensorInfo optRecurrentToInputWeights;
            TensorInfo optCellToInputWeights;
            TensorInfo optInputGateBias;
            TensorInfo optProjectionWeights;
            TensorInfo optProjectionBias;
            TensorInfo optCellToForgetWeights;
            TensorInfo optCellToOutputWeights;
            TensorInfo optInputLayerNormWeights;
            TensorInfo optForgetLayerNormWeights;
            TensorInfo optCellLayerNormWeights;
            TensorInfo optOutputLayerNormWeights;

            if(!descriptor.m_CifgEnabled)
            {
                optInputToInputWeights =
                    OverrideDataType(cLayer->m_CifgParameters.m_InputToInputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_InputToInputWeights = &optInputToInputWeights;

                optRecurrentToInputWeights =
                    OverrideDataType(cLayer->m_CifgParameters.m_RecurrentToInputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_RecurrentToInputWeights = &optRecurrentToInputWeights;
                optInputGateBias =
                       OverrideDataType(cLayer->m_CifgParameters.m_InputGateBias->GetTensorInfo(), dataType);
                paramsInfo.m_InputGateBias = &optInputGateBias;
            }

            if(descriptor.m_ProjectionEnabled)
            {
                optProjectionWeights =
                    OverrideDataType(cLayer->m_ProjectionParameters.m_ProjectionWeights->GetTensorInfo(), dataType);
                paramsInfo.m_ProjectionWeights = &optProjectionWeights;
                if (cLayer->m_ProjectionParameters.m_ProjectionBias != nullptr)
                {
                    optProjectionBias =
                        OverrideDataType(cLayer->m_ProjectionParameters.m_ProjectionBias->GetTensorInfo(), dataType);
                    paramsInfo.m_ProjectionBias = &optProjectionBias;
                }
            }

            if(descriptor.m_PeepholeEnabled)
            {
                if(!descriptor.m_CifgEnabled)
                {
                    optCellToInputWeights =
                            OverrideDataType(cLayer->m_PeepholeParameters.m_CellToInputWeights->GetTensorInfo(),
                                             dataType);
                    paramsInfo.m_CellToInputWeights = &optCellToInputWeights;
                }
                optCellToForgetWeights =
                    OverrideDataType(cLayer->m_PeepholeParameters.m_CellToForgetWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellToForgetWeights = &optCellToForgetWeights;
                optCellToOutputWeights =
                    OverrideDataType(cLayer->m_PeepholeParameters.m_CellToOutputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellToOutputWeights = &optCellToOutputWeights;
            }

            if(descriptor.m_LayerNormEnabled)
            {
                if (!descriptor.m_CifgEnabled)
                {
                    optInputLayerNormWeights = OverrideDataType(
                            cLayer->m_LayerNormParameters.m_InputLayerNormWeights->GetTensorInfo(), dataType);
                    paramsInfo.m_InputLayerNormWeights = &optInputLayerNormWeights;
                }

                optForgetLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_ForgetLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_ForgetLayerNormWeights = &optForgetLayerNormWeights;

                optCellLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_CellLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellLayerNormWeights = &optCellLayerNormWeights;

                optOutputLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_OutputLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_OutputLayerNormWeights = &optOutputLayerNormWeights;
            }

            result = layerSupportObject.IsLstmSupported(
                                     input,
                                     outputStateIn,
                                     cellStateIn,
                                     scratchBuffer,
                                     outputStateOut,
                                     cellStateOut,
                                     output,
                                     descriptor,
                                     paramsInfo,
                                     reason);
            break;
        }
        case LayerType::Maximum:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsMaximumSupported(OverrideDataType(input0, dataType),
                                                           OverrideDataType(input1, dataType),
                                                           OverrideDataType(output, dataType),
                                                           reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::MemCopy:
        {
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsMemCopySupported(OverrideDataType(input, dataType),
                                                           OverrideDataType(output, dataType),
                                                           reason);
            break;
        }
        case LayerType::MemImport:
        {
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsMemImportSupported(OverrideDataType(input, dataType),
                                                             OverrideDataType(output, dataType),
                                                             reason);
            break;
        }
        case LayerType::Merge:
        {
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsMergeSupported(OverrideDataType(input0, dataType),
                                                         OverrideDataType(input1, dataType),
                                                         OverrideDataType(output, dataType),
                                                         reason);
            break;
        }
        case LayerType::Concat:
        {
            auto cLayer = PolymorphicDowncast<const ConcatLayer*>(&layer);

            // Get vector of all inputs.
            auto getTensorInfo = [&dataType](const InputSlot& slot)
                {
                    return OverrideDataType(slot.GetConnectedOutputSlot()->GetTensorInfo(), dataType);
                };

            auto beginI = MakeTransformIterator(layer.GetInputSlots().begin(), getTensorInfo);
            auto endI = MakeTransformIterator(layer.GetInputSlots().end(), getTensorInfo);
            std::vector<TensorInfo> inputs(beginI, endI);

            auto getTensorInfoPtr = [](const TensorInfo& info)
                {
                    return &info;
                };

            auto beginPtr = MakeTransformIterator(inputs.begin(), getTensorInfoPtr);
            auto endPtr = MakeTransformIterator(inputs.end(), getTensorInfoPtr);
            std::vector<const TensorInfo*> inputPtrs(beginPtr, endPtr);

            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsConcatSupported(inputPtrs, output, cLayer->GetParameters(), reason);


            break;
        }
        case LayerType::Multiplication:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsMultiplicationSupported(
                                               OverrideDataType(input0, dataType),
                                               OverrideDataType(input1, dataType),
                                               OverrideDataType(output, dataType),
                                               reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::Normalization:
        {
            auto cLayer = PolymorphicDowncast<const NormalizationLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsNormalizationSupported(OverrideDataType(input, dataType),
                                                                 OverrideDataType(output, dataType),
                                                                 cLayer->GetParameters(),
                                                                 reason);
            break;
        }
        case LayerType::Output:
        {
            const TensorInfo& output = layer.GetInputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsOutputSupported(OverrideDataType(output, dataType), reason);
            break;
        }
        case LayerType::Permute:
        {
            auto cLayer = PolymorphicDowncast<const PermuteLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPermuteSupported(OverrideDataType(input, dataType),
                                                           OverrideDataType(output, dataType),
                                                           cLayer->GetParameters(),
                                                           reason);
            break;
        }
        case LayerType::Pad:
        {
            auto cLayer = PolymorphicDowncast<const PadLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPadSupported(
                                    OverrideDataType(input, dataType),
                                    OverrideDataType(output, dataType),
                                    cLayer->GetParameters(),
                                    reason);
            break;
        }
        case LayerType::Pooling2d:
        {
            auto cLayer = PolymorphicDowncast<const Pooling2dLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPooling2dSupported(OverrideDataType(input, dataType),
                                                             OverrideDataType(output, dataType),
                                                             cLayer->GetParameters(),
                                                             reason);
            break;
        }
        case LayerType::Pooling3d:
        {
            auto cLayer = PolymorphicDowncast<const Pooling3dLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPooling3dSupported(OverrideDataType(input, dataType),
                                                             OverrideDataType(output, dataType),
                                                             cLayer->GetParameters(),
                                                             reason);
            break;
        }
        case LayerType::PreCompiled:
        {
            auto cLayer = PolymorphicDowncast<const PreCompiledLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPreCompiledSupported(OverrideDataType(input, dataType),
                                                               cLayer->GetParameters(),
                                                               reason);
            break;
        }
        case LayerType::Quantize:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsQuantizeSupported(input, output, reason);
            break;
        }
        case LayerType::QLstm:
        {
            auto cLayer = PolymorphicDowncast<const QLstmLayer*>(&layer);
            const QLstmDescriptor& descriptor = cLayer->GetParameters();

            // Inputs
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& previousOutputIn = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& previousCellStateIn = layer.GetInputSlot(2).GetTensorInfo();

            // Outputs
            const TensorInfo& outputStateOut = layer.GetOutputSlot(0).GetTensorInfo();
            const TensorInfo& cellStateOut = layer.GetOutputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(2).GetTensorInfo();

            // Lstm parameters
            LstmInputParamsInfo paramsInfo;

            // Basic parameters
            ARMNN_ASSERT(cLayer->m_BasicParameters.m_InputToForgetWeights.get() != nullptr);
            ARMNN_ASSERT(cLayer->m_BasicParameters.m_InputToCellWeights.get() != nullptr);
            ARMNN_ASSERT(cLayer->m_BasicParameters.m_InputToOutputWeights.get() != nullptr);
            paramsInfo.m_InputToForgetWeights = &cLayer->m_BasicParameters.m_InputToForgetWeights->GetTensorInfo();
            paramsInfo.m_InputToCellWeights   = &cLayer->m_BasicParameters.m_InputToCellWeights->GetTensorInfo();
            paramsInfo.m_InputToOutputWeights = &cLayer->m_BasicParameters.m_InputToOutputWeights->GetTensorInfo();

            paramsInfo.m_RecurrentToForgetWeights =
                    &cLayer->m_BasicParameters.m_RecurrentToForgetWeights->GetTensorInfo();
            paramsInfo.m_RecurrentToCellWeights   =
                    &cLayer->m_BasicParameters.m_RecurrentToCellWeights->GetTensorInfo();
            paramsInfo.m_RecurrentToOutputWeights =
                    &cLayer->m_BasicParameters.m_RecurrentToOutputWeights->GetTensorInfo();

            paramsInfo.m_ForgetGateBias = &cLayer->m_BasicParameters.m_ForgetGateBias->GetTensorInfo();
            paramsInfo.m_CellBias       = &cLayer->m_BasicParameters.m_CellBias->GetTensorInfo();
            paramsInfo.m_OutputGateBias = &cLayer->m_BasicParameters.m_OutputGateBias->GetTensorInfo();

            if(!descriptor.m_CifgEnabled)
            {
                paramsInfo.m_InputToInputWeights = &cLayer->m_CifgParameters.m_InputToInputWeights->GetTensorInfo();
                paramsInfo.m_RecurrentToInputWeights =
                        &cLayer->m_CifgParameters.m_RecurrentToInputWeights->GetTensorInfo();
                paramsInfo.m_InputGateBias = &cLayer->m_CifgParameters.m_InputGateBias->GetTensorInfo();
            }

            if(descriptor.m_ProjectionEnabled)
            {
                paramsInfo.m_ProjectionWeights = &cLayer->m_ProjectionParameters.m_ProjectionWeights->GetTensorInfo();

                // Projection bias is optional even if projection is enabled
                if (cLayer->m_ProjectionParameters.m_ProjectionBias != nullptr)
                {
                    paramsInfo.m_ProjectionBias = &cLayer->m_ProjectionParameters.m_ProjectionBias->GetTensorInfo();
                }
            }

            if(descriptor.m_PeepholeEnabled)
            {
                if (!descriptor.m_CifgEnabled)
                {
                    paramsInfo.m_CellToInputWeights =
                            &cLayer->m_PeepholeParameters.m_CellToInputWeights->GetTensorInfo();
                }

                paramsInfo.m_CellToForgetWeights =
                        &cLayer->m_PeepholeParameters.m_CellToForgetWeights->GetTensorInfo();
                paramsInfo.m_CellToOutputWeights = &cLayer->m_PeepholeParameters.m_CellToOutputWeights->GetTensorInfo();
            }

            if(descriptor.m_LayerNormEnabled)
            {
                if (!descriptor.m_CifgEnabled)
                {
                    paramsInfo.m_InputLayerNormWeights =
                            &cLayer->m_LayerNormParameters.m_InputLayerNormWeights->GetTensorInfo();
                }

                paramsInfo.m_ForgetLayerNormWeights =
                        &cLayer->m_LayerNormParameters.m_ForgetLayerNormWeights->GetTensorInfo();
                paramsInfo.m_CellLayerNormWeights =
                        &cLayer->m_LayerNormParameters.m_CellLayerNormWeights->GetTensorInfo();
                paramsInfo.m_OutputLayerNormWeights =
                        &cLayer->m_LayerNormParameters.m_OutputLayerNormWeights->GetTensorInfo();
            }

            result = layerSupportObject.IsQLstmSupported(input,
                                                         previousOutputIn,
                                                         previousCellStateIn,
                                                         outputStateOut,
                                                         cellStateOut,
                                                         output,
                                                         descriptor,
                                                         paramsInfo,
                                                         reason);
            break;
        }
        case LayerType::QuantizedLstm:
        {
            auto cLayer = PolymorphicDowncast<const QuantizedLstmLayer*>(&layer);

            // Inputs
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& previousCellStateIn = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& previousOutputIn = layer.GetInputSlot(2).GetTensorInfo();

            // Outputs
            const TensorInfo& cellStateOut = layer.GetOutputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(1).GetTensorInfo();

            // QuantizedLstm parameters
            QuantizedLstmInputParamsInfo paramsInfo;

            paramsInfo.m_InputToInputWeights      =
                    &cLayer->m_QuantizedLstmParameters.m_InputToInputWeights->GetTensorInfo();
            paramsInfo.m_InputToForgetWeights     =
                    &cLayer->m_QuantizedLstmParameters.m_InputToForgetWeights->GetTensorInfo();
            paramsInfo.m_InputToCellWeights       =
                    &cLayer->m_QuantizedLstmParameters.m_InputToCellWeights->GetTensorInfo();
            paramsInfo.m_InputToOutputWeights     =
                    &cLayer->m_QuantizedLstmParameters.m_InputToOutputWeights->GetTensorInfo();

            paramsInfo.m_RecurrentToInputWeights  =
                    &cLayer->m_QuantizedLstmParameters.m_RecurrentToInputWeights->GetTensorInfo();
            paramsInfo.m_RecurrentToForgetWeights =
                    &cLayer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights->GetTensorInfo();
            paramsInfo.m_RecurrentToCellWeights   =
                    &cLayer->m_QuantizedLstmParameters.m_RecurrentToCellWeights->GetTensorInfo();
            paramsInfo.m_RecurrentToOutputWeights =
                    &cLayer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights->GetTensorInfo();

            paramsInfo.m_InputGateBias            =
                    &cLayer->m_QuantizedLstmParameters.m_InputGateBias->GetTensorInfo();
            paramsInfo.m_ForgetGateBias           =
                    &cLayer->m_QuantizedLstmParameters.m_ForgetGateBias->GetTensorInfo();
            paramsInfo.m_CellBias                 =
                    &cLayer->m_QuantizedLstmParameters.m_CellBias->GetTensorInfo();
            paramsInfo.m_OutputGateBias           =
                    &cLayer->m_QuantizedLstmParameters.m_OutputGateBias->GetTensorInfo();;

            result = layerSupportObject.IsQuantizedLstmSupported(input,
                                                                 previousCellStateIn,
                                                                 previousOutputIn,
                                                                 cellStateOut,
                                                                 output,
                                                                 paramsInfo,
                                                                 reason);
            break;
        }
        case LayerType::Division:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsDivisionSupported(
                                         OverrideDataType(input0, dataType),
                                         OverrideDataType(input1, dataType),
                                         OverrideDataType(output, dataType),
                                         reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::Rank:
        {
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsRankSupported(OverrideDataType(input, dataType),
                                                        OverrideDataType(output, dataType),
                                                        reason);
            break;
        }
        case LayerType::Reshape:
        {
            auto cLayer = PolymorphicDowncast<const ReshapeLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsReshapeSupported(OverrideDataType(input, dataType),
                                                           OverrideDataType(output, dataType),
                                                           cLayer->GetParameters(),
                                                           reason);
            break;
        }
        case LayerType::Resize:
        {
            auto cLayer = PolymorphicDowncast<const ResizeLayer*>(&layer);
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsResizeSupported(OverrideDataType(input, dataType),
                                                          OverrideDataType(output, dataType),
                                                          cLayer->GetParameters(),
                                                          reason);
            break;
        }
        case LayerType::ReverseV2:
        {
            const TensorInfo& input0  = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
            const TensorInfo& input1  = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsReverseV2Supported(OverrideDataType(input0, dataType),
                                                             OverrideDataType(input1, armnn::DataType::Signed32),
                                                             OverrideDataType(output, dataType),
                                                             reason);
            break;
        }
        case LayerType::Shape:
        {
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsShapeSupported(OverrideDataType(input, dataType),
                                                         OverrideDataType(output, dataType),
                                                         reason);
            break;
        }
        case LayerType::Slice:
        {
            auto cLayer = PolymorphicDowncast<const SliceLayer*>(&layer);

            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsSliceSupported(OverrideDataType(input, dataType),
                                                         OverrideDataType(output, dataType),
                                                         cLayer->GetParameters(),
                                                         reason);
            break;
        }
        case LayerType::Softmax:
        {
            auto cLayer = PolymorphicDowncast<const SoftmaxLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsSoftmaxSupported(OverrideDataType(input, dataType),
                                                           OverrideDataType(output, dataType),
                                                           cLayer->GetParameters(),
                                                           reason);
            break;
        }
        case LayerType::SpaceToBatchNd:
        {
            auto cLayer = PolymorphicDowncast<const SpaceToBatchNdLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsSpaceToBatchNdSupported(OverrideDataType(input, dataType),
                                                                  OverrideDataType(output, dataType),
                                                                  cLayer->GetParameters(),
                                                                  reason);
            break;
        }
        case LayerType::SpaceToDepth:
        {
            auto cLayer = PolymorphicDowncast<const SpaceToDepthLayer*>(&layer);

            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsSpaceToDepthSupported(OverrideDataType(input, dataType),
                                                                OverrideDataType(output, dataType),
                                                                cLayer->GetParameters(),
                                                                reason);
            break;
        }
        case LayerType::Splitter:
        {
            auto cLayer = PolymorphicDowncast<const SplitterLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();

            // Get vector of all outputs.
            auto getTensorInfo = [&dataType](const OutputSlot& slot)
            {
                return OverrideDataType(slot.GetTensorInfo(), dataType);
            };
            auto beginI = MakeTransformIterator(layer.GetOutputSlots().begin(), getTensorInfo);
            auto endI = MakeTransformIterator(layer.GetOutputSlots().end(), getTensorInfo);
            std::vector<TensorInfo> outputs(beginI, endI);

            const std::vector<std::reference_wrapper<TensorInfo>> outputPtrs(outputs.begin(), outputs.end());

            result = layerSupportObject.IsSplitterSupported(OverrideDataType(input, dataType),
                                                            outputPtrs,
                                                            cLayer->GetParameters(),
                                                            reason);
            break;
        }
        case LayerType::Stack:
        {
            auto cLayer = PolymorphicDowncast<const StackLayer*>(&layer);

            // Get vector of all inputs.
            auto getTensorInfo = [&dataType](const InputSlot& slot)
                {
                    return OverrideDataType(slot.GetConnectedOutputSlot()->GetTensorInfo(), dataType);
                };
            auto beginI = MakeTransformIterator(layer.GetInputSlots().begin(), getTensorInfo);
            auto endI = MakeTransformIterator(layer.GetInputSlots().end(), getTensorInfo);
            std::vector<TensorInfo> inputs(beginI, endI);

            auto getTensorInfoPtr = [](const TensorInfo& info)
                {
                    return &info;
                };
            auto beginPtr = MakeTransformIterator(inputs.begin(), getTensorInfoPtr);
            auto endPtr = MakeTransformIterator(inputs.end(), getTensorInfoPtr);
            std::vector<const TensorInfo*> inputPtrs(beginPtr, endPtr);

            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsStackSupported(inputPtrs, output, cLayer->GetParameters(), reason);

            break;
        }
        case LayerType::StandIn:
        {
            auto cLayer = PolymorphicDowncast<const StandInLayer*>(&layer);

            // Get vector of all inputs.
            auto getTensorInfoIn = [&dataType](const InputSlot& slot)
                {
                    return OverrideDataType(slot.GetConnectedOutputSlot()->GetTensorInfo(), dataType);
                };
            auto getTensorInfoOut = [&dataType](const OutputSlot& slot)
                {
                    return OverrideDataType(slot.GetTensorInfo(), dataType);
                };
            auto beginI = MakeTransformIterator(layer.GetInputSlots().begin(), getTensorInfoIn);
            auto endI = MakeTransformIterator(layer.GetInputSlots().end(), getTensorInfoIn);
            std::vector<TensorInfo> inputs(beginI, endI);

            auto beginO = MakeTransformIterator(layer.GetOutputSlots().begin(), getTensorInfoOut);
            auto endO = MakeTransformIterator(layer.GetOutputSlots().end(), getTensorInfoOut);
            std::vector<TensorInfo> outputs(beginO, endO);


            auto getTensorInfoPtr = [](const TensorInfo& info)
                {
                    return &info;
                };
            auto beginPtrI = MakeTransformIterator(inputs.begin(), getTensorInfoPtr);
            auto endPtrI = MakeTransformIterator(inputs.end(), getTensorInfoPtr);
            std::vector<const TensorInfo*> inputPtrs(beginPtrI, endPtrI);

            auto beginPtrO = MakeTransformIterator(outputs.begin(), getTensorInfoPtr);
            auto endPtrO = MakeTransformIterator(outputs.end(), getTensorInfoPtr);
            std::vector<const TensorInfo*> outputPtrs(beginPtrO, endPtrO);


            result = layerSupportObject.IsStandInSupported(inputPtrs,
                                                           outputPtrs,
                                                           cLayer->GetParameters(),
                                                           reason);
            break;
        }
        case LayerType::StridedSlice:
        {
            auto cLayer = PolymorphicDowncast<const StridedSliceLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsStridedSliceSupported(OverrideDataType(input, dataType),
                                                                OverrideDataType(output, dataType),
                                                                cLayer->GetParameters(),
                                                                reason);
            break;
        }
        case LayerType::Subtraction:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsSubtractionSupported(
                                            OverrideDataType(input0, dataType),
                                            OverrideDataType(input1, dataType),
                                            OverrideDataType(output, dataType),
                                            reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::Switch:
        {
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output0 = layer.GetOutputSlot(0).GetTensorInfo();
            const TensorInfo& output1 = layer.GetOutputSlot(1).GetTensorInfo();
            result = layerSupportObject.IsSwitchSupported(OverrideDataType(input0, dataType),
                                                          OverrideDataType(input1, dataType),
                                                          OverrideDataType(output0, dataType),
                                                          OverrideDataType(output1, dataType),
                                                          reason);
            break;
        }
        case LayerType::Mean:
        {
            auto cLayer = PolymorphicDowncast<const MeanLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsMeanSupported(
                                     OverrideDataType(input, dataType),
                                     OverrideDataType(output, dataType),
                                     cLayer->GetParameters(),
                                     reason);
            break;
        }
        case LayerType::Minimum:
        {
            ARMNN_NO_DEPRECATE_WARN_BEGIN
            const TensorInfo& input0 = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& input1 = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsMinimumSupported(OverrideDataType(input0, dataType),
                                                           OverrideDataType(input1, dataType),
                                                           OverrideDataType(output, dataType),
                                                           reason);
            ARMNN_NO_DEPRECATE_WARN_END
            break;
        }
        case LayerType::Prelu:
        {
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& alpha  = layer.GetInputSlot(1).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsPreluSupported(OverrideDataType(input,  dataType),
                                                         OverrideDataType(alpha,  dataType),
                                                         OverrideDataType(output, dataType),
                                                         reason);
            break;
        }
        case LayerType::Tile:
        {
            auto cLayer = PolymorphicDowncast<const TileLayer*>(&layer);
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsTileSupported(OverrideDataType(input, dataType),
                                                        OverrideDataType(output, dataType),
                                                        cLayer->GetParameters(),
                                                        reason);

            break;
        }
        case LayerType::Transpose:
        {
            auto cLayer = PolymorphicDowncast<const TransposeLayer*>(&layer);
            const TensorInfo& input = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
            result = layerSupportObject.IsTransposeSupported(OverrideDataType(input, dataType),
                                                             OverrideDataType(output, dataType),
                                                             cLayer->GetParameters(),
                                                             reason);
            break;
        }
        case LayerType::TransposeConvolution2d:
        {
            auto cLayer = PolymorphicDowncast<const TransposeConvolution2dLayer*>(&layer);

            const TensorInfo input  = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                       dataType);
            const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);

            const TransposeConvolution2dDescriptor& descriptor  = cLayer->GetParameters();

            Optional<TensorInfo> biases;
            if (descriptor.m_BiasEnabled)
            {
                ARMNN_ASSERT(cLayer->m_Bias.get() != nullptr);
                biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(),
                                          GetBiasTypeFromWeightsType(dataType));
            }

            ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
            const TensorInfo weights = OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType);

            result = layerSupportObject.IsTransposeConvolution2dSupported(input,
                                                                          output,
                                                                          descriptor,
                                                                          weights,
                                                                          biases,
                                                                          reason);

            break;
        }
        case LayerType::Reduce:
        {
            auto cLayer = PolymorphicDowncast<const ReduceLayer*>(&layer);
            const TensorInfo& input  = layer.GetInputSlot(0).GetTensorInfo();
            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();

            result = layerSupportObject.IsReduceSupported(OverrideDataType(input, dataType),
                                                          OverrideDataType(output, dataType),
                                                          cLayer->GetParameters(),
                                                          reason);
            break;
        }
        case LayerType::UnidirectionalSequenceLstm:
        {
            auto cLayer = PolymorphicDowncast<const UnidirectionalSequenceLstmLayer*>(&layer);
            const UnidirectionalSequenceLstmDescriptor& descriptor = cLayer->GetParameters();

            // All inputs.
            const TensorInfo& input = OverrideDataType(layer.GetInputSlot(0).GetTensorInfo(),
                                                       dataType);
            const TensorInfo& outputStateIn = OverrideDataType(layer.GetInputSlot(1).GetTensorInfo(),
                                                               dataType);
            const TensorInfo& cellStateIn = OverrideDataType(layer.GetInputSlot(2).GetTensorInfo(),
                                                             dataType);
            // Outputs
            const TensorInfo& outputStateOut = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
            const TensorInfo& cellStateOut = OverrideDataType(layer.GetOutputSlot(1).GetTensorInfo(), dataType);
            const TensorInfo& output = OverrideDataType(layer.GetOutputSlot(2).GetTensorInfo(), dataType);

            // Basic parameters
            const TensorInfo& inputToForgetWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToForgetWeights->GetTensorInfo(), dataType);
            const TensorInfo& inputToCellWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToCellWeights->GetTensorInfo(), dataType);
            const TensorInfo& inputToOutputWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_InputToOutputWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToForgetWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToForgetWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToCellWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToCellWeights->GetTensorInfo(), dataType);
            const TensorInfo& recurrentToOutputWeights
                    = OverrideDataType(cLayer->m_BasicParameters.m_RecurrentToOutputWeights->GetTensorInfo(), dataType);
            const TensorInfo& forgetGateBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_ForgetGateBias->GetTensorInfo(), dataType);
            const TensorInfo& cellBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_CellBias->GetTensorInfo(), dataType);
            const TensorInfo& outputGateBias
                    = OverrideDataType(cLayer->m_BasicParameters.m_OutputGateBias->GetTensorInfo(), dataType);

            LstmInputParamsInfo paramsInfo;

            paramsInfo.m_InputToForgetWeights     = &inputToForgetWeights;
            paramsInfo.m_InputToCellWeights       = &inputToCellWeights;
            paramsInfo.m_InputToOutputWeights     = &inputToOutputWeights;
            paramsInfo.m_RecurrentToForgetWeights = &recurrentToForgetWeights;
            paramsInfo.m_RecurrentToCellWeights   = &recurrentToCellWeights;
            paramsInfo.m_RecurrentToOutputWeights = &recurrentToOutputWeights;
            paramsInfo.m_ForgetGateBias           = &forgetGateBias;
            paramsInfo.m_CellBias                 = &cellBias;
            paramsInfo.m_OutputGateBias           = &outputGateBias;

            // Optional parameters
            TensorInfo optInputToInputWeights;
            TensorInfo optRecurrentToInputWeights;
            TensorInfo optCellToInputWeights;
            TensorInfo optInputGateBias;
            TensorInfo optProjectionWeights;
            TensorInfo optProjectionBias;
            TensorInfo optCellToForgetWeights;
            TensorInfo optCellToOutputWeights;
            TensorInfo optInputLayerNormWeights;
            TensorInfo optForgetLayerNormWeights;
            TensorInfo optCellLayerNormWeights;
            TensorInfo optOutputLayerNormWeights;

            if(!descriptor.m_CifgEnabled)
            {
                optInputToInputWeights =
                    OverrideDataType(cLayer->m_CifgParameters.m_InputToInputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_InputToInputWeights = &optInputToInputWeights;

                optRecurrentToInputWeights =
                    OverrideDataType(cLayer->m_CifgParameters.m_RecurrentToInputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_RecurrentToInputWeights = &optRecurrentToInputWeights;
                optInputGateBias =
                       OverrideDataType(cLayer->m_CifgParameters.m_InputGateBias->GetTensorInfo(), dataType);
                paramsInfo.m_InputGateBias = &optInputGateBias;
            }

            if(descriptor.m_ProjectionEnabled)
            {
                optProjectionWeights =
                    OverrideDataType(cLayer->m_ProjectionParameters.m_ProjectionWeights->GetTensorInfo(), dataType);
                paramsInfo.m_ProjectionWeights = &optProjectionWeights;
                if (cLayer->m_ProjectionParameters.m_ProjectionBias != nullptr)
                {
                    optProjectionBias =
                        OverrideDataType(cLayer->m_ProjectionParameters.m_ProjectionBias->GetTensorInfo(), dataType);
                    paramsInfo.m_ProjectionBias = &optProjectionBias;
                }
            }

            if(descriptor.m_PeepholeEnabled)
            {
                if(!descriptor.m_CifgEnabled)
                {
                    optCellToInputWeights =
                            OverrideDataType(cLayer->m_PeepholeParameters.m_CellToInputWeights->GetTensorInfo(),
                                             dataType);
                    paramsInfo.m_CellToInputWeights = &optCellToInputWeights;
                }
                optCellToForgetWeights =
                    OverrideDataType(cLayer->m_PeepholeParameters.m_CellToForgetWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellToForgetWeights = &optCellToForgetWeights;
                optCellToOutputWeights =
                    OverrideDataType(cLayer->m_PeepholeParameters.m_CellToOutputWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellToOutputWeights = &optCellToOutputWeights;
            }

            if(descriptor.m_LayerNormEnabled)
            {
                if (!descriptor.m_CifgEnabled)
                {
                    optInputLayerNormWeights = OverrideDataType(
                            cLayer->m_LayerNormParameters.m_InputLayerNormWeights->GetTensorInfo(), dataType);
                    paramsInfo.m_InputLayerNormWeights = &optInputLayerNormWeights;
                }

                optForgetLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_ForgetLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_ForgetLayerNormWeights = &optForgetLayerNormWeights;

                optCellLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_CellLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_CellLayerNormWeights = &optCellLayerNormWeights;

                optOutputLayerNormWeights = OverrideDataType(
                        cLayer->m_LayerNormParameters.m_OutputLayerNormWeights->GetTensorInfo(), dataType);
                paramsInfo.m_OutputLayerNormWeights = &optOutputLayerNormWeights;
            }

            result = layerSupportObject.IsUnidirectionalSequenceLstmSupported(input,
                                                                              outputStateIn,
                                                                              cellStateIn,
                                                                              outputStateOut,
                                                                              cellStateOut,
                                                                              output,
                                                                              descriptor,
                                                                              paramsInfo,
                                                                              reason);
            break;
        }
        default:
        {
            ARMNN_ASSERT_MSG(false, "WorkloadFactory did not recognise type of layer.");
            reason.value() = "Unrecognised layer type";
            result = false;
            break;
        }
    }
    return result;
}

bool IWorkloadFactory::IsLayerSupported(const BackendId& backendId,
                                        const IConnectableLayer& connectableLayer,
                                        Optional<DataType> dataType,
                                        std::string& outReasonIfUnsupported)
{
    return IsLayerConfigurationSupported(backendId, connectableLayer, dataType, outReasonIfUnsupported);
}

bool IWorkloadFactory::IsLayerSupported(const IConnectableLayer& connectableLayer,
                                        Optional<DataType> dataType,
                                        std::string& outReasonIfUnsupported)
{
    auto layer = PolymorphicDowncast<const Layer*>(&connectableLayer);
    return IsLayerConfigurationSupported(layer->GetBackendId(), connectableLayer, dataType, outReasonIfUnsupported);
}

bool IWorkloadFactory::IsLayerSupported(const IConnectableLayer& connectableLayer,
                                        Optional<DataType> dataType,
                                        std::string& outReasonIfUnsupported,
                                        const ModelOptions& modelOptions)
{
    auto layer = PolymorphicDowncast<const Layer*>(&connectableLayer);
    return IsLayerConfigurationSupported(layer->GetBackendId(),
                                         connectableLayer,
                                         dataType,
                                         outReasonIfUnsupported,
                                         modelOptions);
}

bool IWorkloadFactory::IsLayerSupported(const BackendId& backendId,
                                        const IConnectableLayer& connectableLayer,
                                        Optional<DataType> dataType,
                                        std::string& outReasonIfUnsupported,
                                        const ModelOptions& modelOptions)
{
    return IsLayerConfigurationSupported(backendId,
                                         connectableLayer,
                                         dataType,
                                         outReasonIfUnsupported,
                                         modelOptions);
}

} // namepsace armnn
