TfLiteStatus EvalFloat(const TfLiteTensor* input, const TfLiteTensor* bw_input,
                       const TfLiteTensor* fw_input_weights,
                       const TfLiteTensor* fw_recurrent_weights,
                       const TfLiteTensor* fw_bias,
                       const TfLiteTensor* bw_input_weights,
                       const TfLiteTensor* bw_recurrent_weights,
                       const TfLiteTensor* bw_bias,
                       const TfLiteTensor* aux_input,
                       const TfLiteTensor* fw_aux_input_weights,
                       const TfLiteTensor* bw_aux_input_weights,
                       const TfLiteBidirectionalSequenceRNNParams* params,
                       TfLiteTensor* fw_hidden_state, TfLiteTensor* fw_output,
                       TfLiteTensor* bw_hidden_state, TfLiteTensor* bw_output) {
  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 input_size = input->dims->data[2];
  const int aux_input_size = (aux_input) ? aux_input->dims->data[2] : 0;

  const int fw_num_units = fw_input_weights->dims->data[0];
  const float* fw_bias_ptr = GetTensorData<float>(fw_bias);
  const float* fw_input_weights_ptr = GetTensorData<float>(fw_input_weights);
  const float* fw_recurrent_weights_ptr =
      GetTensorData<float>(fw_recurrent_weights);

  const int bw_num_units = bw_input_weights->dims->data[0];
  const float* bw_bias_ptr = GetTensorData<float>(bw_bias);
  const float* bw_input_weights_ptr = GetTensorData<float>(bw_input_weights);
  const float* bw_recurrent_weights_ptr =
      GetTensorData<float>(bw_recurrent_weights);

  const float* fw_aux_input_weights_ptr =
      (fw_aux_input_weights != nullptr)
          ? GetTensorData<float>(fw_aux_input_weights)
          : nullptr;
  const float* bw_aux_input_weights_ptr =
      (bw_aux_input_weights != nullptr)
          ? GetTensorData<float>(bw_aux_input_weights)
          : nullptr;

  const int fw_output_step =
      params->merge_outputs ? fw_num_units + bw_num_units : fw_num_units;
  const int bw_output_step =
      params->merge_outputs ? fw_num_units + bw_num_units : bw_num_units;
  if (time_major) {
    // Forward cell.
    float* fw_hidden_state_ptr_batch = GetTensorData<float>(fw_hidden_state);
    for (int s = 0; s < max_time; s++) {
      const float* input_ptr_batch =
          GetTensorData<float>(input) + s * input_size * batch_size;
      const float* aux_input_ptr_batch =
          (aux_input != nullptr)
              ? GetTensorData<float>(aux_input) + s * input_size * batch_size
              : nullptr;
      float* output_ptr_batch =
          GetTensorData<float>(fw_output) + s * fw_output_step * batch_size;

      kernel_utils::RnnBatchStep(
          input_ptr_batch, fw_input_weights_ptr, aux_input_ptr_batch,
          fw_aux_input_weights_ptr, fw_recurrent_weights_ptr, fw_bias_ptr,
          input_size, aux_input_size, fw_num_units, batch_size, fw_output_step,
          params->activation, fw_hidden_state_ptr_batch, output_ptr_batch);
    }
    // Backward cell.
    float* bw_hidden_state_ptr_batch = GetTensorData<float>(bw_hidden_state);
    for (int s = max_time - 1; s >= 0; s--) {
      const float* input_ptr_batch =
          GetTensorData<float>(bw_input) + s * input_size * batch_size;
      const float* aux_input_ptr_batch =
          (aux_input != nullptr)
              ? GetTensorData<float>(aux_input) + s * input_size * batch_size
              : nullptr;
      float* output_ptr_batch =
          (params->merge_outputs
               ? GetTensorData<float>(fw_output) + fw_num_units
               : GetTensorData<float>(bw_output)) +
          s * bw_output_step * batch_size;

      kernel_utils::RnnBatchStep(
          input_ptr_batch, bw_input_weights_ptr, aux_input_ptr_batch,
          bw_aux_input_weights_ptr, bw_recurrent_weights_ptr, bw_bias_ptr,
          input_size, aux_input_size, bw_num_units, batch_size, bw_output_step,
          params->activation, bw_hidden_state_ptr_batch, output_ptr_batch);
    }
  } else {
    for (int b = 0; b < batch_size; b++) {
      // Forward cell.
      float* fw_hidden_state_ptr_batch =
          GetTensorData<float>(fw_hidden_state) + b * fw_num_units;
      float* fw_output_offset =
          GetTensorData<float>(fw_output) + b * fw_output_step * max_time;
      for (int s = 0; s < max_time; s++) {
        const float* input_ptr_batch = GetTensorData<float>(input) +
                                       b * input_size * max_time +
                                       s * input_size;
        const float* aux_input_ptr_batch =
            (aux_input != nullptr)
                ? GetTensorData<float>(aux_input) +
                      b * aux_input_size * max_time + s * aux_input_size
                : nullptr;
        float* output_ptr_batch = fw_output_offset + s * fw_output_step;

        kernel_utils::RnnBatchStep(
            input_ptr_batch, fw_input_weights_ptr, aux_input_ptr_batch,
            fw_aux_input_weights_ptr, fw_recurrent_weights_ptr, fw_bias_ptr,
            input_size, aux_input_size, fw_num_units, /*batch_size=*/1,
            fw_output_step, params->activation, fw_hidden_state_ptr_batch,
            output_ptr_batch);
      }
      // Backward cell.
      float* bw_hidden_state_ptr_batch =
          GetTensorData<float>(bw_hidden_state) + b * bw_num_units;
      float* bw_output_offset =
          params->merge_outputs
              ? GetTensorData<float>(fw_output) +
                    b * bw_output_step * max_time + fw_num_units
              : GetTensorData<float>(bw_output) + b * bw_output_step * max_time;
      for (int s = max_time - 1; s >= 0; s--) {
        const float* input_ptr_batch = GetTensorData<float>(input) +
                                       b * input_size * max_time +
                                       s * input_size;
        const float* aux_input_ptr_batch =
            (aux_input != nullptr)
                ? GetTensorData<float>(aux_input) +
                      b * aux_input_size * max_time + s * aux_input_size
                : nullptr;
        float* output_ptr_batch = bw_output_offset + s * bw_output_step;

        kernel_utils::RnnBatchStep(
            input_ptr_batch, bw_input_weights_ptr, aux_input_ptr_batch,
            bw_aux_input_weights_ptr, bw_recurrent_weights_ptr, bw_bias_ptr,
            input_size, aux_input_size, bw_num_units, /*batch_size=*/1,
            bw_output_step, params->activation, bw_hidden_state_ptr_batch,
            output_ptr_batch);
      }
    }
  }
  return kTfLiteOk;
}