TfLiteStatus EvalHybrid(
    const TfLiteTensor* input, const TfLiteTensor* input_weights,
    const TfLiteTensor* recurrent_weights, const TfLiteTensor* bias,
    const TfLiteSequenceRNNParams* params, TfLiteTensor* input_scratch,
    TfLiteTensor* hidden_state_scratch, TfLiteTensor* scaling_factors,
    TfLiteTensor* hidden_state, TfLiteTensor* output, TfLiteTensor* zero_points,
    TfLiteTensor* accum_scratch, TfLiteTensor* row_sums,
    bool* compute_row_sums) {
  const bool time_major = params->time_major;
  const int batch_size =
      (time_major) ? input->dims->data[1] : input->dims->data[0];
  const int max_time =
      (time_major) ? input->dims->data[0] : input->dims->data[1];
  const int num_units = input_weights->dims->data[0];
  const int input_size = input->dims->data[2];

  // Initialize the pointer bias.
  const float* bias_ptr = GetTensorData<float>(bias);

  // Initialize input_weights, recurrent_weights, and temporary storage for
  // quantized values.
  const int8_t* input_weights_ptr = GetTensorData<int8_t>(input_weights);
  const int8_t* recurrent_weights_ptr =
      GetTensorData<int8_t>(recurrent_weights);
  int8_t* quantized_input_ptr = GetTensorData<int8_t>(input_scratch);
  int8_t* quantized_hidden_state_ptr =
      GetTensorData<int8_t>(hidden_state_scratch);

  // Get the scale of the quantized weights.
  float input_weights_scale = input_weights->params.scale;
  float recurrent_weights_scale = recurrent_weights->params.scale;
  float* scaling_factors_ptr = GetTensorData<float>(scaling_factors);
  int32_t* accum_scratch_ptr = GetTensorData<int32_t>(accum_scratch);
  int32_t* zero_points_ptr = nullptr;
  int32_t* row_sums_ptr = nullptr;

  if (params->asymmetric_quantize_inputs) {
    zero_points_ptr = GetTensorData<int32_t>(zero_points);
    row_sums_ptr = GetTensorData<int32_t>(row_sums);
  }

  if (time_major) {
    // Initialize the pointer to hidden state.
    float* hidden_state_ptr_batch = GetTensorData<float>(hidden_state);
    // Unroll the sequence and use batch operations for efficiency.
    for (int s = 0; s < max_time; s++) {
      // Initialize the pointer to input and output.
      const float* input_ptr_batch =
          GetTensorData<float>(input) + s * input_size * batch_size;
      float* output_ptr_batch =
          GetTensorData<float>(output) + s * num_units * batch_size;

      kernel_utils::RnnBatchStep(
          input_ptr_batch, input_weights_ptr, input_weights_scale,
          recurrent_weights_ptr, recurrent_weights_scale, bias_ptr, input_size,
          num_units, batch_size, num_units, params->activation,
          quantized_input_ptr, quantized_hidden_state_ptr, scaling_factors_ptr,
          hidden_state_ptr_batch, output_ptr_batch,
          params->asymmetric_quantize_inputs, zero_points_ptr,
          accum_scratch_ptr, row_sums_ptr, compute_row_sums);
    }
  } else {
    // For each batch
    for (int b = 0; b < batch_size; b++) {
      // Initialize the pointer to hidden state.
      float* hidden_state_ptr_batch =
          GetTensorData<float>(hidden_state) + b * num_units;
      for (int s = 0; s < max_time; s++) {
        // Initialize the pointer to input and output.
        const float* input_ptr_batch = GetTensorData<float>(input) +
                                       b * input_size * max_time +
                                       s * input_size;
        float* output_ptr_batch = GetTensorData<float>(output) +
                                  b * num_units * max_time + s * num_units;
        kernel_utils::RnnBatchStep(
            input_ptr_batch, input_weights_ptr, input_weights_scale,
            recurrent_weights_ptr, recurrent_weights_scale, bias_ptr,
            input_size, num_units, /*batch_size=*/1, num_units,
            params->activation, quantized_input_ptr, quantized_hidden_state_ptr,
            scaling_factors_ptr, hidden_state_ptr_batch, output_ptr_batch,
            params->asymmetric_quantize_inputs, zero_points_ptr,
            accum_scratch_ptr, row_sums_ptr, compute_row_sums);
      }
    }
  }
  return kTfLiteOk;
}
TfLiteStatus EvalHybrid(
    const TfLiteTensor* input, const TfLiteTensor* input_weights,
    const TfLiteTensor* recurrent_weights, const TfLiteTensor* bias,
    const TfLiteSequenceRNNParams* params, TfLiteTensor* input_scratch,
    TfLiteTensor* hidden_state_scratch, TfLiteTensor* scaling_factors,
    TfLiteTensor* hidden_state, TfLiteTensor* output, TfLiteTensor* zero_points,
    TfLiteTensor* accum_scratch, TfLiteTensor* row_sums,
    bool* compute_row_sums) {
  const bool time_major = params->time_major;
  const int batch_size =
      (time_major) ? input->dims->data[1] : input->dims->data[0];
  const int max_time =
      (time_major) ? input->dims->data[0] : input->dims->data[1];
  const int num_units = input_weights->dims->data[0];
  const int input_size = input->dims->data[2];

   
  const float* bias_ptr = GetTensorData<float>(bias);

   
   
  const int8_t* input_weights_ptr = GetTensorData<int8_t>(input_weights);
  const int8_t* recurrent_weights_ptr =
      GetTensorData<int8_t>(recurrent_weights);
  int8_t* quantized_input_ptr = GetTensorData<int8_t>(input_scratch);
  int8_t* quantized_hidden_state_ptr =
      GetTensorData<int8_t>(hidden_state_scratch);

   
  float input_weights_scale = input_weights->params.scale;
  float recurrent_weights_scale = recurrent_weights->params.scale;
  float* scaling_factors_ptr = GetTensorData<float>(scaling_factors);
  int32_t* accum_scratch_ptr = GetTensorData<int32_t>(accum_scratch);
  int32_t* zero_points_ptr = nullptr;
  int32_t* row_sums_ptr = nullptr;

  if (params->asymmetric_quantize_inputs) {
    zero_points_ptr = GetTensorData<int32_t>(zero_points);
    row_sums_ptr = GetTensorData<int32_t>(row_sums);
  }

  if (time_major) {
     
    float* hidden_state_ptr_batch = GetTensorData<float>(hidden_state);
     
    for (int s = 0; s < max_time; s++) {
       
      const float* input_ptr_batch =
          GetTensorData<float>(input) + s * input_size * batch_size;
      float* output_ptr_batch =
          GetTensorData<float>(output) + s * num_units * batch_size;

      kernel_utils::RnnBatchStep(
          input_ptr_batch, input_weights_ptr, input_weights_scale,
          recurrent_weights_ptr, recurrent_weights_scale, bias_ptr, input_size,
          num_units, batch_size, num_units, params->activation,
          quantized_input_ptr, quantized_hidden_state_ptr, scaling_factors_ptr,
          hidden_state_ptr_batch, output_ptr_batch,
          params->asymmetric_quantize_inputs, zero_points_ptr,
          accum_scratch_ptr, row_sums_ptr, compute_row_sums);
    }
  } else {
     
    for (int b = 0; b < batch_size; b++) {
       
      float* hidden_state_ptr_batch =
          GetTensorData<float>(hidden_state) + b * num_units;
      for (int s = 0; s < max_time; s++) {
         
        const float* input_ptr_batch = GetTensorData<float>(input) +
                                       b * input_size * max_time +
                                       s * input_size;
        float* output_ptr_batch = GetTensorData<float>(output) +
                                  b * num_units * max_time + s * num_units;
        kernel_utils::RnnBatchStep(
            input_ptr_batch, input_weights_ptr, input_weights_scale,
            recurrent_weights_ptr, recurrent_weights_scale, bias_ptr,
            input_size, num_units,  1, num_units,
            params->activation, quantized_input_ptr, quantized_hidden_state_ptr,
            scaling_factors_ptr, hidden_state_ptr_batch, output_ptr_batch,
            params->asymmetric_quantize_inputs, zero_points_ptr,
            accum_scratch_ptr, row_sums_ptr, compute_row_sums);
      }
    }
  }
  return kTfLiteOk;
}