text: //
  // Copyright © 2020 Arm Ltd. All rights reserved.
  // SPDX-License-Identifier: MIT
  //
  
  #include "../DriverTestHelpers.hpp"
  #include "../TestTensor.hpp"
  
  #include "../1.3/HalPolicy.hpp"
  
  #include <armnn/utility/IgnoreUnused.hpp>
  
  #include <boost/test/unit_test.hpp>
  #include <boost/test/data/test_case.hpp>
  #include <boost/math/special_functions/relative_difference.hpp>
  
  #include <array>
  
  BOOST_AUTO_TEST_SUITE(QLSTMTests)
  
  using ArmnnDriver   = armnn_driver::ArmnnDriver;
  using DriverOptions = armnn_driver::DriverOptions;
  
  using namespace driverTestHelpers;
  using namespace android::hardware;
  
  using HalPolicy = hal_1_3::HalPolicy;
  
  namespace
  {
  
  template<typename T>
  RequestArgument CreateRequestArgument(const std::vector<T>& value, unsigned int poolIndex)
  {
      DataLocation inputInloc = {};
      inputInloc.poolIndex = poolIndex;
      inputInloc.offset = 0;
      inputInloc.length = value.size() * sizeof(T);
      RequestArgument inputRequestArgument = {};
      inputRequestArgument.location = inputInloc;
      inputRequestArgument.dimensions = hidl_vec<uint32_t>{};
      return inputRequestArgument;
  }
  
  // Returns true if the relative difference between two float values is less than the tolerance value given.
  // This is used because the floating point comparison tolerance (set on each BOOST_AUTO_TEST_CASE) does not work!
  bool TolerantCompareEqual(float a, float b, float tolerance = 1.0f)
  {
      float rd;
      if (a == 0.0f)
      {
          rd = fabs(b);
      }
      else if (b == 0.0f)
      {
          rd = fabs(a);
      }
      else
      {
          rd = boost::math::relative_difference(a, b);
      }
      return rd < tolerance;
  }
  
  // Helper function to create an OperandLifeTime::NO_VALUE for testing.
  // To be used on optional input operands that have no values - these are valid and should be tested.
  HalPolicy::OperandLifeTime CreateNoValueLifeTime(const hidl_vec<uint32_t>& dimensions)
  {
      // Only create a NO_VALUE for optional operands that have no elements
      if (dimensions.size() == 0 || dimensions[0] == 0)
      {
          return HalPolicy::OperandLifeTime::NO_VALUE;
      }
      return HalPolicy::OperandLifeTime::CONSTANT_COPY;
  }
  
  void ExecuteModel(const armnn_driver::hal_1_3::HalPolicy::Model& model,
                    armnn_driver::ArmnnDriver& driver,
                    const V1_0::Request& request)
  {
      android::sp<V1_3::IPreparedModel> preparedModel = PrepareModel_1_3(model, driver);
      if (preparedModel.get() != nullptr)
      {
          Execute(preparedModel, request);
      }
  }
  
  #ifndef ARMCOMPUTECL_ENABLED
  static const std::array<armnn::Compute, 1> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef }};
  #else
  static const std::array<armnn::Compute, 2> COMPUTE_DEVICES = {{ armnn::Compute::CpuRef, armnn::Compute::CpuAcc }};
  #endif
  
  // Add our own tests here since we skip the qlstm tests which Google supplies (because of non-const weights)
  void QLstmTestImpl(const hidl_vec<uint32_t>&   inputDimensions,
                     const std::vector<int8_t>&   inputValue,
                     const hidl_vec<uint32_t>&    inputToInputWeightsDimensions,
                     const std::vector<int8_t>&   inputToInputWeightsValue,
                     const hidl_vec<uint32_t>&    inputToForgetWeightsDimensions,
                     const std::vector<int8_t>&   inputToForgetWeightsValue,
                     const hidl_vec<uint32_t>&    inputToCellWeightsDimensions,
                     const std::vector<int8_t>&   inputToCellWeightsValue,
                     const hidl_vec<uint32_t>&    inputToOutputWeightsDimensions,
                     const std::vector<int8_t>&   inputToOutputWeightsValue,
                     const hidl_vec<uint32_t>&    recurrentToInputWeightsDimensions,
                     const std::vector<int8_t>&   recurrentToInputWeightsValue,
                     const hidl_vec<uint32_t>&    recurrentToForgetWeightsDimensions,
                     const std::vector<int8_t>&   recurrentToForgetWeightsValue,
                     const hidl_vec<uint32_t>&    recurrentToCellWeightsDimensions,
                     const std::vector<int8_t>&   recurrentToCellWeightsValue,
                     const hidl_vec<uint32_t>&    recurrentToOutputWeightsDimensions,
                     const std::vector<int8_t>&   recurrentToOutputWeightsValue,
                     const hidl_vec<uint32_t>&    cellToInputWeightsDimensions,
                     const std::vector<int16_t>&  cellToInputWeightsValue,
                     const hidl_vec<uint32_t>&    cellToForgetWeightsDimensions,
                     const std::vector<int16_t>&  cellToForgetWeightsValue,
                     const hidl_vec<uint32_t>&    cellToOutputWeightsDimensions,
                     const std::vector<int16_t>&  cellToOutputWeightsValue,
                     const hidl_vec<uint32_t>&    inputGateBiasDimensions,
                     const std::vector<int32_t>&  inputGateBiasValue,
                     const hidl_vec<uint32_t>&    forgetGateBiasDimensions,
                     const std::vector<int32_t>&  forgetGateBiasValue,
                     const hidl_vec<uint32_t>&    cellBiasDimensions,
                     const std::vector<int32_t>&  cellBiasValue,
                     const hidl_vec<uint32_t>&    outputGateBiasDimensions,
                     const std::vector<int32_t>&  outputGateBiasValue,
                     const hidl_vec<uint32_t>&    projectionWeightsDimensions,
                     const std::vector<int8_t>&   projectionWeightsValue,
                     const hidl_vec<uint32_t>&    projectionBiasDimensions,
                     const std::vector<int32_t>&  projectionBiasValue,
                     const hidl_vec<uint32_t>&    outputPreviousTimeStepInDimensions,
                     const std::vector<int8_t>&   outputPreviousTimeStepInValue,
                     const hidl_vec<uint32_t>&    cellStatePreviousTimeStepInDimensions,
                     const std::vector<int16_t>&  cellStatePreviousTimeStepInValue,
                     const hidl_vec<uint32_t>&    inputLayerNormWeightsDimensions,
                     const std::vector<int16_t>&  inputLayerNormWeightsValue,
                     const hidl_vec<uint32_t>&    forgetLayerNormWeightsDimensions,
                     const std::vector<int16_t>&  forgetLayerNormWeightsValue,
                     const hidl_vec<uint32_t>&    cellLayerNormWeightsDimensions,
                     const std::vector<int16_t>&  cellLayerNormWeightsValue,
                     const hidl_vec<uint32_t>&    outputLayerNormWeightsDimensions,
                     const std::vector<int16_t>&  outputLayerNormWeightsValue,
                     const float&                 cellClipValue,
                     const float&                 projectionClipValue,
                     const float&                 matMulInputGateValue,
                     const float&                 matMulForgetGateValue,
                     const float&                 matMulCellGateValue,
                     const float&                 matMulOutputGateValue,
                     const int32_t&               projInputZeroPointValue,
                     const float&                 projInputScaleValue,
                     const hidl_vec<uint32_t>&    outputStateOutDimensions,
                     const std::vector<int8_t>&   outputStateOutValue,
                     const hidl_vec<uint32_t>&    cellStateOutDimensions,
                     const std::vector<int16_t>&  cellStateOutValue,
                     const hidl_vec<uint32_t>&    outputDimensions,
                     const std::vector<int8_t>&   outputValue,
                     armnn::Compute               compute)
  {
      auto driver = std::make_unique<ArmnnDriver>(DriverOptions(compute));
      HalPolicy::Model model = {};
  
      // Scale/Offset quantization info
      float inputScale    = 0.0078125f;
      int32_t inputOffset = 0;
  
      int32_t hiddenStateZeroPoint = 0;
      float hiddenStateScale       = 0.007f;
  
      float outputScale    = hiddenStateScale;
      int32_t outputOffset = hiddenStateZeroPoint;
  
      float cellStateScale    = 3.05176e-05f;
      float cellWeightsScale  = 1.0f;
      int32_t cellStateOffset = 0;
  
      float weightsScale    = 0.00784314f;
      int32_t weightsOffset = 0;
  
      float layerNormScale    = 3.05182e-05f;
      int32_t layerNormOffset = 0;
  
      float biasScale    = layerNormScale / 1024;
      int32_t biasOffset = 0;
  
      // Inputs:
      // 00: The input to the LSTM cell. Type: ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED Shape: [batchSize, inputSize]
      AddInputOperand<HalPolicy>(model,
                                 inputDimensions,
                                 HalPolicy::OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
                                 inputScale,
                                 inputOffset);
  
      // 01: The input-to-input weights. Optional. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, inputSize]
      AddTensorOperand<HalPolicy>(model,
                                  inputToInputWeightsDimensions,
                                  inputToInputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(inputToInputWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 02: The input-to-forget weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, inputSize]
      AddTensorOperand<HalPolicy>(model,
                                  inputToForgetWeightsDimensions,
                                  inputToForgetWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(inputToForgetWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 03: The input-to-cell weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, inputSize]
      AddTensorOperand<HalPolicy>(model,
                                  inputToCellWeightsDimensions,
                                  inputToCellWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(inputToCellWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 04: The input-to-output weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, inputSize]
      AddTensorOperand<HalPolicy>(model,
                                  inputToOutputWeightsDimensions,
                                  inputToOutputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(inputToOutputWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 05: The recurrent-to-input weights. Optional. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM
      //     Shape: [numUnits, outputSize]
      AddTensorOperand<HalPolicy>(model,
                                  recurrentToInputWeightsDimensions,
                                  recurrentToInputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(recurrentToInputWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 06: The recurrent-to-forget weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, outputSize]
      AddTensorOperand<HalPolicy>(model,
                                  recurrentToForgetWeightsDimensions,
                                  recurrentToForgetWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(recurrentToForgetWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 07: The recurrent-to-cell weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, outputSize]
      AddTensorOperand<HalPolicy>(model,
                                  recurrentToCellWeightsDimensions,
                                  recurrentToCellWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(recurrentToCellWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 08: The recurrent-to-output weights. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [numUnits, outputSize]
      AddTensorOperand<HalPolicy>(model,
                                  recurrentToOutputWeightsDimensions,
                                  recurrentToOutputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(recurrentToOutputWeightsDimensions),
                                  weightsScale,
                                  weightsOffset);
  
      // 09: The cell-to-input weights (for peephole). Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM
      //     Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  cellToInputWeightsDimensions,
                                  cellToInputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT16_SYMM ,
                                  CreateNoValueLifeTime(cellToInputWeightsDimensions),
                                  cellWeightsScale,
                                  weightsOffset);
  
      // 10: The cell-to-forget weights (for peephole). Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM
      //     Shape: [numUnits].
      AddTensorOperand<HalPolicy>(model,
                                  cellToForgetWeightsDimensions,
                                  cellToForgetWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                  CreateNoValueLifeTime(cellToForgetWeightsDimensions),
                                  cellWeightsScale,
                                  weightsOffset);
  
      // 11: The cell-to-output weights (for peephole). Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM
      //     Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  cellToOutputWeightsDimensions,
                                  cellToOutputWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                  CreateNoValueLifeTime(cellToOutputWeightsDimensions),
                                  cellWeightsScale,
                                  weightsOffset);
  
      // 12: The input gate bias. Quantized with scale being the product of input and weights scales
      //     and zeroPoint equal to 0. Optional. Type: ANEURALNETWORKS_TENSOR_INT32 Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  inputGateBiasDimensions,
                                  inputGateBiasValue,
                                  HalPolicy::OperandType::TENSOR_INT32,
                                  CreateNoValueLifeTime(inputGateBiasDimensions),
                                  biasScale,
                                  biasOffset);
  
      // 13: The forget gate bias. Quantized with scale being the product of input and weights scales
      //     and zeroPoint equal to 0. Type: ANEURALNETWORKS_TENSOR_INT32 Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  forgetGateBiasDimensions,
                                  forgetGateBiasValue,
                                  HalPolicy::OperandType::TENSOR_INT32,
                                  CreateNoValueLifeTime(forgetGateBiasDimensions),
                                  biasScale,
                                  biasOffset);
  
      // 14: The cell bias. Quantized with scale being the product of input and weights scales and zeroPoint equal to 0.
      //     Type: ANEURALNETWORKS_TENSOR_INT32 Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  cellBiasDimensions,
                                  cellBiasValue,
                                  HalPolicy::OperandType::TENSOR_INT32,
                                  CreateNoValueLifeTime(cellBiasDimensions),
                                  biasScale,
                                  biasOffset);
  
      // 15: The output gate bias. Quantized with scale being the product of input and weights scales
      //     and zeroPoint equal to 0. Type: ANEURALNETWORKS_TENSOR_INT32 Shape: [numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  outputGateBiasDimensions,
                                  outputGateBiasValue,
                                  HalPolicy::OperandType::TENSOR_INT32,
                                  CreateNoValueLifeTime(outputGateBiasDimensions),
                                  biasScale,
                                  biasOffset);
  
      // 16: The projection weights. Optional. Type: ANEURALNETWORKS_TENSOR_QUANT8_SYMM Shape: [outputSize, numUnits]
      AddTensorOperand<HalPolicy>(model,
                                  projectionWeightsDimensions,
                                  projectionWeightsValue,
                                  HalPolicy::OperandType::TENSOR_QUANT8_SYMM,
                                  CreateNoValueLifeTime(projectionWeightsDimensions),
                                  0.00392157f,
                                  weightsOffset);
  
      // 17: The projection bias. Quantized with scale being the product of input and weights scales
      //     and zeroPoint equal to 0. Optional. Type: ANEURALNETWORKS_TENSOR_INT32 Shape: [outputSize]
      AddTensorOperand<HalPolicy>(model,
                                  projectionBiasDimensions,
                                  projectionBiasValue,
                                  HalPolicy::OperandType::TENSOR_INT32,
                                  CreateNoValueLifeTime(projectionBiasDimensions),
                                  0.0f,
                                  biasOffset);
  
      // 18: The output from the previous time step. Type: ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED
      //     Shape: [batchSize, outputSize]
      AddInputOperand<HalPolicy>(model,
                                 outputPreviousTimeStepInDimensions,
                                 HalPolicy::OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
                                 cellStateScale,
                                 inputOffset);
  
      // 19: The cell state from the previous time step. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM
      //     Shape: [batchSize, numUnits]
      AddInputOperand<HalPolicy>(model,
                                 cellStatePreviousTimeStepInDimensions,
                                 HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                 cellStateScale,
                                 cellStateOffset);
  
      // If any of the tensors have a value all normalization tensors are set
      if (!inputLayerNormWeightsValue.empty()  ||
          !forgetLayerNormWeightsValue.empty() ||
          !cellLayerNormWeightsValue.empty()   ||
          !outputLayerNormWeightsValue.empty())
      {
          // Normalization:
          // 20: The input layer normalization weights. Used to rescale normalized inputs to activation at input gate.
          //      Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM Shape: [numUnits]
          AddTensorOperand<HalPolicy>(model,
                                      inputLayerNormWeightsDimensions,
                                      inputLayerNormWeightsValue,
                                      HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                      CreateNoValueLifeTime(inputLayerNormWeightsDimensions),
                                      layerNormScale,
                                      layerNormOffset);
  
          // 21: The forget layer normalization weights. Used to rescale normalized inputs to activation at forget gate.
          //     Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM Shape: [numUnits]
          AddTensorOperand<HalPolicy>(model,
                                      forgetLayerNormWeightsDimensions,
                                      forgetLayerNormWeightsValue,
                                      HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                      CreateNoValueLifeTime(forgetLayerNormWeightsDimensions),
                                      layerNormScale,
                                      layerNormOffset);
  
          // 22: The cell layer normalization weights. Used to rescale normalized inputs to activation at cell gate.
          //     Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM Shape: [numUnits]
          AddTensorOperand<HalPolicy>(model,
                                      cellLayerNormWeightsDimensions,
                                      cellLayerNormWeightsValue,
                                      HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                      CreateNoValueLifeTime(cellLayerNormWeightsDimensions),
                                      layerNormScale,
                                      layerNormOffset);
  
          // 23: The output layer normalization weights. Used to rescale normalized inputs to activation at output gate.
          //     Optional. Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM Shape: [numUnits]
          AddTensorOperand<HalPolicy>(model,
                                      outputLayerNormWeightsDimensions,
                                      outputLayerNormWeightsValue,
                                      HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                      CreateNoValueLifeTime(outputLayerNormWeightsDimensions),
                                      layerNormScale,
                                      layerNormOffset);
      }
  
      // Constant scalar values
      // 24: The cell clip. If provided the cell state is clipped by this value prior to the cell output activation.
      //     Optional. Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, cellClipValue);
  
      // Constant scalar values
      // 25: The projection clip. If provided and projection is enabled, this is used for clipping the projected values.
      //     Optional. Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, projectionClipValue);
  
      // Constant scalar values
      // 26: The scale of the intermediate result of matmul, i.e. input to layer normalization, at input gate.
      //     Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, matMulInputGateValue);
  
      // Constant scalar values
      // 27: The scale of the intermediate result of matmul, i.e. input to layer normalization, at forget gate.
      //     Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, matMulForgetGateValue);
  
      // Constant scalar values
      // 28: The scale of the intermediate result of matmul, i.e. input to layer normalization, at cell gate.
      //     Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, matMulCellGateValue);
  
      // Constant scalar values
      // 29: The scale of the intermediate result of matmul, i.e. input to layer normalization, at output gate.
      //     Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, matMulOutputGateValue);
  
      // Constant scalar values
      // 30: The zero point of the hidden state, i.e. input to projection. Type: ANEURALNETWORKS_INT32.
      AddIntOperand<HalPolicy>(model, projInputZeroPointValue);
  
      // Constant scalar values
      // 31: The scale of the hidden state, i.e. input to projection. Type: ANEURALNETWORKS_FLOAT32.
      AddFloatOperand<HalPolicy>(model, projInputScaleValue);
  
      // Outputs:
      //  0: The output state (out). Type: ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED Shape: [batchSize, outputSize]
      AddOutputOperand<HalPolicy>(model,
                                  outputStateOutDimensions,
                                  HalPolicy::OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
                                  cellStateScale,
                                  cellStateScale);
  
      //  1: The cell state (out). Type: ANEURALNETWORKS_TENSOR_QUANT16_SYMM Shape: [batchSize, numUnits].
      AddOutputOperand<HalPolicy>(model,
                                  cellStateOutDimensions,
                                  HalPolicy::OperandType::TENSOR_QUANT16_SYMM,
                                  cellStateScale,
                                  cellStateOffset);
  
      //  2: The output. This is effectively the same as the current "output state (out)" value.
      //     Type: ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED Shape: [batchSize, outputSize]
      AddOutputOperand<HalPolicy>(model,
                                  outputDimensions,
                                  HalPolicy::OperandType::TENSOR_QUANT8_ASYMM_SIGNED,
                                  cellStateScale,
                                  cellStateScale);
  
      // make the QUANTIZED_LSTM operation
      model.main.operations.resize(1);
      model.main.operations[0].type = HalPolicy::OperationType::QUANTIZED_LSTM;
  
      model.main.operations[0].inputs = hidl_vec<uint32_t> { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
                                                            12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                                                            24, 25, 26, 27, 28, 29, 30, 31};
      model.main.operations[0].outputs = hidl_vec<uint32_t> {32, 33, 34};
  
      // define the input values
      hidl_vec<RequestArgument> inputArguments;
      inputArguments.resize(3);
  
      inputArguments[0] = CreateRequestArgument<int8_t>(inputValue, 0);
      inputArguments[1] = CreateRequestArgument<int8_t>(outputPreviousTimeStepInValue, 1);
      inputArguments[2] = CreateRequestArgument<int16_t>(cellStatePreviousTimeStepInValue, 2);
  
      // define the expected output values
      hidl_vec<RequestArgument> outputArguments;
      outputArguments.resize(3);
  
      outputArguments[0] = CreateRequestArgument<int8_t>(outputStateOutValue, 3);
      outputArguments[1] = CreateRequestArgument<int16_t>(cellStateOutValue, 4);
      outputArguments[2] = CreateRequestArgument<int8_t>(outputValue, 5);
  
      android::hardware::neuralnetworks::V1_0::Request request = {};
      request.inputs  = inputArguments;
      request.outputs = outputArguments;
  
      // set the input data
      AddPoolAndSetData(inputValue.size(), request, inputValue.data());
      AddPoolAndSetData(outputPreviousTimeStepInValue.size(), request, outputPreviousTimeStepInValue.data());
      AddPoolAndSetData(cellStatePreviousTimeStepInValue.size(), request, cellStatePreviousTimeStepInValue.data());
  
      // add memory for the outputs
      android::sp<IMemory> outputStateOutMemory = AddPoolAndGetData<int8_t>(outputStateOutValue.size(), request);
      int8_t* outputStateOutData = static_cast<int8_t*>(static_cast<void*>(outputStateOutMemory->getPointer()));
  
      android::sp<IMemory> cellStateOutMemory = AddPoolAndGetData<int16_t>(cellStateOutValue.size(), request);
      int16_t* cellStateOutData = static_cast<int16_t*>(static_cast<void*>(cellStateOutMemory->getPointer()));
  
      android::sp<IMemory> outputMemory = AddPoolAndGetData<int8_t>(outputValue.size(), request);
      int8_t* outputData = static_cast<int8_t*>(static_cast<void*>(outputMemory->getPointer()));
  
      // make the prepared model and run the execution
      ExecuteModel(model, *driver, request);
  
      // check the results
      for (size_t i = 0; i < outputStateOutValue.size(); ++i)
      {
          BOOST_TEST(TolerantCompareEqual(outputStateOutValue[i], outputStateOutData[i]),
                     "outputStateOut[" << i << "]: " << outputStateOutValue[i] << " != " << outputStateOutData[i]);
      }
  
      // CELL STATE OUTPUT Does not match currently: IVGCVSW-4860 Verify remaining VTS tests (2) for QLSTM
      // Comment out for now
      // for (size_t i = 0; i < cellStateOutValue.size(); ++i)
      // {
      //    BOOST_TEST(TolerantCompareEqual(cellStateOutValue[i], cellStateOutData[i]),
      //               "cellStateOut[" << i << "]: " << cellStateOutValue[i] << " != " << cellStateOutData[i]);
      //}
  
      for (size_t i = 0; i < outputValue.size(); ++i)
      {
          BOOST_TEST(TolerantCompareEqual(outputValue[i], outputData[i]),
                     "output[" << i << "]: " << outputValue[i] << " != " << outputData[i]);
      }
  }
  
  void QLstmWithProjection(armnn::Compute compute)
  {
      // This replicates android/frameworks/ml/nn/runtime/test/specs/V1_3/qlstm_projection.mod.py
      // with values from android/frameworks/ml/nn/runtime/test/generated/spec_V1_3/qlstm_projection.example.cpp
      // and weights, biases and scalars passed as CONSTANT_COPY tensors (instead of SUBGRAPH_INPUT tensors).
  
      uint32_t batchSize  = 2;
      uint32_t inputSize  = 5;
      uint32_t outputSize = 3;
      uint32_t numUnits   = 4;
  
      // Inputs:
      hidl_vec<uint32_t> inputDimensions{batchSize, inputSize};
      std::vector<int8_t> inputValue{ 90, 102, 13, 26, 38, 102, 13, 26, 51, 64};
  
      hidl_vec<uint32_t> inputToInputWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToInputWeightsValue{   64,  77,   89, -102,
                                                    -115,  13,   25,   38,
                                                     -51,  64, -102,   89,
                                                     -77,  64,  -51,  -64,
                                                     -51, -38,  -25,  -13 };
  
      hidl_vec<uint32_t> inputToForgetWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToForgetWeightsValue{ -77,  -13,  38,  25,
                                                     115,  -64, -25, -51,
                                                      38, -102, -51,  38,
                                                     -64,  -51, -77,  38,
                                                     -51,  -77, -64, -64 };
  
      hidl_vec<uint32_t> inputToCellWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToCellWeightsValue{  -51, -38, -25, -13,
                                                    -64,  64, -25, -38,
                                                    -25, -77,  77, -13,
                                                    -51, -38, -89,  89,
                                                   -115, -64, 102,  77 };
  
      hidl_vec<uint32_t> inputToOutputWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToOutputWeightsValue{ -102, -51, -25, -115,
                                                      -13, -89,  38,  -38,
                                                     -102, -25,  77,  -25,
                                                       51, -89, -38,  -64,
                                                       13,  64, -77,  -51 };
  
      hidl_vec<uint32_t> recurrentToInputWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToInputWeightsValue{ -25, -38, 51, 13, -64, 115, -25, -38, -89, 6, -25, -77 };
  
      hidl_vec<uint32_t> recurrentToForgetWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToForgetWeightsValue{ -64, -38, -64, -25, 77, 51, 115, 38, -13, 25, 64, 25 };
  
      hidl_vec<uint32_t> recurrentToCellWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToCellWeightsValue{ -38, 25, 13, -38, 102, -10, -25, 38, 102, -77, -13, 25 };
  
      hidl_vec<uint32_t> recurrentToOutputWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToOutputWeightsValue{ 38, -13, 13, -25, -64, -89, -25, -77, -13, -51, -89, -25 };
  
      hidl_vec<uint32_t> cellToInputWeightsDimensions{0};
      std::vector<int16_t> cellToInputWeightsValue;
  
      hidl_vec<uint32_t> cellToForgetWeightsDimensions{0};
      std::vector<int16_t> cellToForgetWeightsValue;
  
      hidl_vec<uint32_t> cellToOutputWeightsDimensions{0};
      std::vector<int16_t> cellToOutputWeightsValue;
  
      hidl_vec<uint32_t> inputGateBiasDimensions{numUnits};
      std::vector<int32_t> inputGateBiasValue{ 644245, 3221226, 4724464, 8160438 };
  
      hidl_vec<uint32_t> forgetGateBiasDimensions{numUnits};
      std::vector<int32_t> forgetGateBiasValue{ 2147484, -6442451, -4294968, 2147484 };
  
      hidl_vec<uint32_t> cellBiasDimensions{numUnits};
      std::vector<int32_t> cellBiasValue{-1073742, 15461883, 5368709, 1717987 };
  
      hidl_vec<uint32_t> outputGateBiasDimensions{numUnits};
      std::vector<int32_t> outputGateBiasValue{ 1073742, -214748, 4294968, 2147484 };
  
      hidl_vec<uint32_t> projectionWeightsDimensions{outputSize, numUnits};
      std::vector<int8_t> projectionWeightsValue{ -25, 51, 3, -51, 25, 127, 77, 20, 18, 51, -102, 51 };
  
      hidl_vec<uint32_t> projectionBiasDimensions{outputSize};
      std::vector<int32_t> projectionBiasValue{ 0, 0, 0 };
  
      hidl_vec<uint32_t> outputStateInDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateInValue{ 0, 0, 0, 0, 0, 0 };
  
      hidl_vec<uint32_t> cellStateInDimensions{batchSize, numUnits};
      std::vector<int16_t> cellStateInValue{ 0, 0, 0, 0, 0, 0, 0, 0 };
  
      // Normalization:
      hidl_vec<uint32_t> inputLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> inputLayerNormWeightsValue{ 3277, 6553, 9830, 16384 };
  
      hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> forgetLayerNormWeightsValue{ 6553, 6553, 13107, 9830 };
  
      hidl_vec<uint32_t> cellLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> cellLayerNormWeightsValue{ 22937, 6553, 9830, 26214 };
  
      hidl_vec<uint32_t> outputLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> outputLayerNormWeightsValue{ 19660, 6553, 6553, 16384 };
  
      float cellClipValue           = 0.0f;
      float projectionClipValue     = 0.0f;
      float inputIntermediateScale  = 0.007059f;
      float forgetIntermediateScale = 0.007812f;
      float cellIntermediateScale   = 0.007059f;
      float outputIntermediateScale = 0.007812f;
      int32_t hiddenStateZeroPoint  = 0;
      float hiddenStateScale        = 0.007f;
  
      // Outputs:
      hidl_vec<uint32_t> outputStateOutDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateOutValue{ 127, 127, -108, -67, 127, 127 };
  
      hidl_vec<uint32_t> cellStateOutDimensions{batchSize, numUnits};
      std::vector<int16_t> cellStateOutValue { -14650, 8939, 5771, 6715, -11843, 7847, 1508, 12939 };
  
      hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
      std::vector<int8_t> outputValue { 127, 127, -108, -67, 127, 127 };
  
      QLstmTestImpl(inputDimensions,                       inputValue,
                    inputToInputWeightsDimensions,         inputToInputWeightsValue,
                    inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
                    inputToCellWeightsDimensions,          inputToCellWeightsValue,
                    inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
                    recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
                    recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
                    recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
                    recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
                    cellToInputWeightsDimensions,          cellToInputWeightsValue,
                    cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
                    cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
                    inputGateBiasDimensions,               inputGateBiasValue,
                    forgetGateBiasDimensions,              forgetGateBiasValue,
                    cellBiasDimensions,                    cellBiasValue,
                    outputGateBiasDimensions,              outputGateBiasValue,
                    projectionWeightsDimensions,           projectionWeightsValue,
                    projectionBiasDimensions,              projectionBiasValue,
                    outputStateInDimensions,               outputStateInValue,
                    cellStateInDimensions,                 cellStateInValue,
                    inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
                    forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
                    cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
                    outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
                    cellClipValue,
                    projectionClipValue,
                    inputIntermediateScale,
                    forgetIntermediateScale,
                    cellIntermediateScale,
                    outputIntermediateScale,
                    hiddenStateZeroPoint,
                    hiddenStateScale,
                    outputStateOutDimensions,              outputStateOutValue,
                    cellStateOutDimensions,                cellStateOutValue,
                    outputDimensions,                      outputValue,
                    compute);
  }
  
  void QLstmWithNoProjection(armnn::Compute compute)
  {
      // This replicates android/frameworks/ml/nn/runtime/test/specs/V1_3/qlstm_noprojection.mod.py
      // with values from android/frameworks/ml/nn/runtime/test/generated/spec_V1_3/qlstm_noprojection.example.cpp
      // and weights, biases and scalars passed as CONSTANT_COPY tensors (instead of SUBGRAPH_INPUT tensors).
  
      uint32_t batchSize  = 2;
      uint32_t inputSize  = 5;
      uint32_t outputSize = 4;
      uint32_t numUnits   = 4;
  
      // Inputs:
      hidl_vec<uint32_t> inputDimensions{batchSize, inputSize};
      std::vector<int8_t> inputValue { 90, 102, 13, 26, 38, 102, 13, 26, 51, 64 };
  
      hidl_vec<uint32_t> inputToInputWeightsDimensions{0, 0};
      std::vector<int8_t> inputToInputWeightsValue;
  
      hidl_vec<uint32_t> inputToForgetWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToForgetWeightsValue { -77, -13,  38,  25,  115,
                                                      -64, -25, -51,  38, -102,
                                                      -51,  38, -64, -51,  -77,
                                                       38, -51, -77, -64,  -64 };
  
      hidl_vec<uint32_t> inputToCellWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToCellWeightsValue { -51,  -38, -25, -13, -64,
                                                     64,  -25, -38, -25, -77,
                                                     77,  -13, -51, -38, -89,
                                                     89, -115, -64, 102,  77 };
  
      hidl_vec<uint32_t> inputToOutputWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToOutputWeightsValue { -102, -51, -25, -115, -13,
                                                       -89,  38, -38, -102, -25,
                                                        77, -25,  51,  -89, -38,
                                                       -64,  13,  64,  -77, -51 };
  
      hidl_vec<uint32_t> recurrentToInputWeightsDimensions{0, 0};
      std::vector<int8_t> recurrentToInputWeightsValue;
  
      hidl_vec<uint32_t> recurrentToForgetWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToForgetWeightsValue { -64, -38, -64, -25,
                                                           77,  51, 115,  38,
                                                          -13,  25,  64,  25,
                                                           25,  38, -13,  51 };
  
      hidl_vec<uint32_t> recurrentToCellWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToCellWeightsValue { -38,  25,  13, -38,
                                                        102, -10, -25,  38,
                                                        102, -77, -13,  25,
                                                         38, -13,  25,  64 };
  
      hidl_vec<uint32_t> recurrentToOutputWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToOutputWeightsValue {  38, -13,  13, -25,
                                                          -64, -89, -25, -77,
                                                          -13, -51, -89, -25,
                                                           13,  64,  25, -38 };
  
      hidl_vec<uint32_t> cellToInputWeightsDimensions{0};
      std::vector<int16_t> cellToInputWeightsValue;
  
      hidl_vec<uint32_t> cellToForgetWeightsDimensions{0};
      std::vector<int16_t> cellToForgetWeightsValue;
  
      hidl_vec<uint32_t> cellToOutputWeightsDimensions{0};
      std::vector<int16_t> cellToOutputWeightsValue;
  
      hidl_vec<uint32_t> inputGateBiasDimensions{0};
      std::vector<int32_t> inputGateBiasValue;
  
      hidl_vec<uint32_t> forgetGateBiasDimensions{numUnits};
      std::vector<int32_t> forgetGateBiasValue { 2147484, -6442451, -4294968, 2147484 };
  
      hidl_vec<uint32_t> cellBiasDimensions{numUnits};
      std::vector<int32_t> cellBiasValue { -1073742, 15461883, 5368709, 1717987 };
  
      hidl_vec<uint32_t> outputGateBiasDimensions{numUnits};
      std::vector<int32_t> outputGateBiasValue { 1073742, -214748, 4294968, 2147484 };
  
      hidl_vec<uint32_t> projectionWeightsDimensions{0, 0};
      std::vector<int8_t> projectionWeightsValue;
  
      hidl_vec<uint32_t> projectionBiasDimensions{0};
      std::vector<int32_t> projectionBiasValue;
  
      hidl_vec<uint32_t> outputStateInDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateInValue { 0, 0, 0, 0, 0, 0, 0, 0 };
  
      hidl_vec<uint32_t> cellStateInDimensions{batchSize, numUnits};
      std::vector<int16_t> cellStateInValue { 0, 0, 0, 0, 0, 0, 0, 0 };
  
      // Normalization:
      hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
      std::vector<int16_t> inputLayerNormWeightsValue;
  
      hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> forgetLayerNormWeightsValue { 6553, 6553, 13107, 9830 };
  
      hidl_vec<uint32_t> cellLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> cellLayerNormWeightsValue { 22937, 6553, 9830, 26214 };
  
      hidl_vec<uint32_t> outputLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> outputLayerNormWeightsValue { 19660, 6553, 6553, 16384 };
  
      float cellClipValue           = 0.0f;
      float projectionClipValue     = 0.0f;
      float inputIntermediateScale  = 0.007059f;
      float forgetIntermediateScale = 0.007812f;
      float cellIntermediateScale   = 0.007059f;
      float outputIntermediateScale = 0.007812f;
      int32_t hiddenStateZeroPoint  = 0;
      float hiddenStateScale        = 0.007f;
  
      // Outputs:
      hidl_vec<uint32_t> outputStateOutDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateOutValue { -15, 21, 14, 20, -15, 15, 5, 27 };
  
      hidl_vec<uint32_t> cellStateOutDimensions{batchSize, numUnits};
      std::vector<int16_t> cellStateOutValue { -11692, 9960, 5491, 8861, -9422, 7726, 2056, 13149 };
  
      hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
      std::vector<int8_t> outputValue { -15, 21, 14, 20, -15, 15, 5, 27 };
  
      QLstmTestImpl(inputDimensions,                       inputValue,
                    inputToInputWeightsDimensions,         inputToInputWeightsValue,
                    inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
                    inputToCellWeightsDimensions,          inputToCellWeightsValue,
                    inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
                    recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
                    recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
                    recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
                    recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
                    cellToInputWeightsDimensions,          cellToInputWeightsValue,
                    cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
                    cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
                    inputGateBiasDimensions,               inputGateBiasValue,
                    forgetGateBiasDimensions,              forgetGateBiasValue,
                    cellBiasDimensions,                    cellBiasValue,
                    outputGateBiasDimensions,              outputGateBiasValue,
                    projectionWeightsDimensions,           projectionWeightsValue,
                    projectionBiasDimensions,              projectionBiasValue,
                    outputStateInDimensions,               outputStateInValue,
                    cellStateInDimensions,                 cellStateInValue,
                    inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
                    forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
                    cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
                    outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
                    cellClipValue,
                    projectionClipValue,
                    inputIntermediateScale,
                    forgetIntermediateScale,
                    cellIntermediateScale,
                    outputIntermediateScale,
                    hiddenStateZeroPoint,
                    hiddenStateScale,
                    outputStateOutDimensions,              outputStateOutValue,
                    cellStateOutDimensions,                cellStateOutValue,
                    outputDimensions,                      outputValue,
                    compute);
  }
  
  void DynamicOutputQLstmWithNoProjection(armnn::Compute compute)
  {
      // This replicates android/frameworks/ml/nn/runtime/test/specs/V1_3/qlstm_noprojection.mod.py
      // with values from android/frameworks/ml/nn/runtime/test/generated/spec_V1_3/qlstm_noprojection.example.cpp
      // and weights, biases and scalars passed as CONSTANT_COPY tensors (instead of SUBGRAPH_INPUT tensors)
      // and made cellStateOutput dynamic.
  
      uint32_t batchSize  = 2;
      uint32_t inputSize  = 5;
      uint32_t outputSize = 4;
      uint32_t numUnits   = 4;
  
      // Inputs:
      hidl_vec<uint32_t> inputDimensions{batchSize, inputSize};
      std::vector<int8_t> inputValue { 90, 102, 13, 26, 38, 102, 13, 26, 51, 64 };
  
      hidl_vec<uint32_t> inputToInputWeightsDimensions{0, 0};
      std::vector<int8_t> inputToInputWeightsValue;
  
      hidl_vec<uint32_t> inputToForgetWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToForgetWeightsValue { -77, -13,  38,  25,  115,
                                                      -64, -25, -51,  38, -102,
                                                      -51,  38, -64, -51,  -77,
                                                      38, -51, -77, -64,  -64 };
  
      hidl_vec<uint32_t> inputToCellWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToCellWeightsValue { -51,  -38, -25, -13, -64,
                                                    64,  -25, -38, -25, -77,
                                                    77,  -13, -51, -38, -89,
                                                    89, -115, -64, 102,  77 };
  
      hidl_vec<uint32_t> inputToOutputWeightsDimensions{numUnits, inputSize};
      std::vector<int8_t> inputToOutputWeightsValue { -102, -51, -25, -115, -13,
                                                      -89,  38, -38, -102, -25,
                                                      77, -25,  51,  -89, -38,
                                                      -64,  13,  64,  -77, -51 };
  
      hidl_vec<uint32_t> recurrentToInputWeightsDimensions{0, 0};
      std::vector<int8_t> recurrentToInputWeightsValue;
  
      hidl_vec<uint32_t> recurrentToForgetWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToForgetWeightsValue { -64, -38, -64, -25,
                                                          77,  51, 115,  38,
                                                          -13,  25,  64,  25,
                                                          25,  38, -13,  51 };
  
      hidl_vec<uint32_t> recurrentToCellWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToCellWeightsValue { -38,  25,  13, -38,
                                                        102, -10, -25,  38,
                                                        102, -77, -13,  25,
                                                        38, -13,  25,  64 };
  
      hidl_vec<uint32_t> recurrentToOutputWeightsDimensions{numUnits, outputSize};
      std::vector<int8_t> recurrentToOutputWeightsValue {  38, -13,  13, -25,
                                                           -64, -89, -25, -77,
                                                           -13, -51, -89, -25,
                                                           13,  64,  25, -38 };
  
      hidl_vec<uint32_t> cellToInputWeightsDimensions{0};
      std::vector<int16_t> cellToInputWeightsValue;
  
      hidl_vec<uint32_t> cellToForgetWeightsDimensions{0};
      std::vector<int16_t> cellToForgetWeightsValue;
  
      hidl_vec<uint32_t> cellToOutputWeightsDimensions{0};
      std::vector<int16_t> cellToOutputWeightsValue;
  
      hidl_vec<uint32_t> inputGateBiasDimensions{0};
      std::vector<int32_t> inputGateBiasValue;
  
      hidl_vec<uint32_t> forgetGateBiasDimensions{numUnits};
      std::vector<int32_t> forgetGateBiasValue { 2147484, -6442451, -4294968, 2147484 };
  
      hidl_vec<uint32_t> cellBiasDimensions{numUnits};
      std::vector<int32_t> cellBiasValue { -1073742, 15461883, 5368709, 1717987 };
  
      hidl_vec<uint32_t> outputGateBiasDimensions{numUnits};
      std::vector<int32_t> outputGateBiasValue { 1073742, -214748, 4294968, 2147484 };
  
      hidl_vec<uint32_t> projectionWeightsDimensions{0, 0};
      std::vector<int8_t> projectionWeightsValue;
  
      hidl_vec<uint32_t> projectionBiasDimensions{0};
      std::vector<int32_t> projectionBiasValue;
  
      hidl_vec<uint32_t> outputStateInDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateInValue { 0, 0, 0, 0, 0, 0, 0, 0 };
  
      hidl_vec<uint32_t> cellStateInDimensions{batchSize, numUnits};
      std::vector<int16_t> cellStateInValue { 0, 0, 0, 0, 0, 0, 0, 0 };
  
      // Normalization:
      hidl_vec<uint32_t> inputLayerNormWeightsDimensions{0};
      std::vector<int16_t> inputLayerNormWeightsValue;
  
      hidl_vec<uint32_t> forgetLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> forgetLayerNormWeightsValue { 6553, 6553, 13107, 9830 };
  
      hidl_vec<uint32_t> cellLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> cellLayerNormWeightsValue { 22937, 6553, 9830, 26214 };
  
      hidl_vec<uint32_t> outputLayerNormWeightsDimensions{numUnits};
      std::vector<int16_t> outputLayerNormWeightsValue { 19660, 6553, 6553, 16384 };
  
      float cellClipValue           = 0.0f;
      float projectionClipValue     = 0.0f;
      float inputIntermediateScale  = 0.007059f;
      float forgetIntermediateScale = 0.007812f;
      float cellIntermediateScale   = 0.007059f;
      float outputIntermediateScale = 0.007812f;
      int32_t hiddenStateZeroPoint  = 0;
      float hiddenStateScale        = 0.007f;
  
      // Outputs:
      hidl_vec<uint32_t> outputStateOutDimensions{batchSize, outputSize};
      std::vector<int8_t> outputStateOutValue { -15, 21, 14, 20, -15, 15, 5, 27 };
  
      hidl_vec<uint32_t> cellStateOutDimensions{};
      std::vector<int16_t> cellStateOutValue { -11692, 9960, 5491, 8861, -9422, 7726, 2056, 13149 };
  
      hidl_vec<uint32_t> outputDimensions{batchSize, outputSize};
      std::vector<int8_t> outputValue { -15, 21, 14, 20, -15, 15, 5, 27 };
  
      QLstmTestImpl(inputDimensions,                       inputValue,
                    inputToInputWeightsDimensions,         inputToInputWeightsValue,
                    inputToForgetWeightsDimensions,        inputToForgetWeightsValue,
                    inputToCellWeightsDimensions,          inputToCellWeightsValue,
                    inputToOutputWeightsDimensions,        inputToOutputWeightsValue,
                    recurrentToInputWeightsDimensions,     recurrentToInputWeightsValue,
                    recurrentToForgetWeightsDimensions,    recurrentToForgetWeightsValue,
                    recurrentToCellWeightsDimensions,      recurrentToCellWeightsValue,
                    recurrentToOutputWeightsDimensions,    recurrentToOutputWeightsValue,
                    cellToInputWeightsDimensions,          cellToInputWeightsValue,
                    cellToForgetWeightsDimensions,         cellToForgetWeightsValue,
                    cellToOutputWeightsDimensions,         cellToOutputWeightsValue,
                    inputGateBiasDimensions,               inputGateBiasValue,
                    forgetGateBiasDimensions,              forgetGateBiasValue,
                    cellBiasDimensions,                    cellBiasValue,
                    outputGateBiasDimensions,              outputGateBiasValue,
                    projectionWeightsDimensions,           projectionWeightsValue,
                    projectionBiasDimensions,              projectionBiasValue,
                    outputStateInDimensions,               outputStateInValue,
                    cellStateInDimensions,                 cellStateInValue,
                    inputLayerNormWeightsDimensions,       inputLayerNormWeightsValue,
                    forgetLayerNormWeightsDimensions,      forgetLayerNormWeightsValue,
                    cellLayerNormWeightsDimensions,        cellLayerNormWeightsValue,
                    outputLayerNormWeightsDimensions,      outputLayerNormWeightsValue,
                    cellClipValue,
                    projectionClipValue,
                    inputIntermediateScale,
                    forgetIntermediateScale,
                    cellIntermediateScale,
                    outputIntermediateScale,
                    hiddenStateZeroPoint,
                    hiddenStateScale,
                    outputStateOutDimensions,              outputStateOutValue,
                    cellStateOutDimensions,                cellStateOutValue,
                    outputDimensions,                      outputValue,
                    compute);
  }
  
  } // anonymous namespace
  
  // Support is not added yet
  //BOOST_DATA_TEST_CASE(QLSTMWithProjectionTest, COMPUTE_DEVICES)
  //{
  //     QLstmWithProjection(sample);
  //}
  
  BOOST_DATA_TEST_CASE(QLSTMWithNoProjectionTest, COMPUTE_DEVICES)
  {
      QLstmWithNoProjection(sample);
  }
  
  BOOST_DATA_TEST_CASE(DynamicOutputQLSTMWithNoProjectionTest, COMPUTE_DEVICES)
  {
      DynamicOutputQLstmWithNoProjection(sample);
  }
  
  BOOST_AUTO_TEST_SUITE_END()