#include <vtkSmartPointer.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGridReader.h>
#include <vtkUnstructuredGridWriter.h>
#include <vtkCellData.h>
#include <vtkPointData.h>
#include <vtkDoubleArray.h>
#include <vtkCellDataToPointData.h>
#include <vtkPointDataToCellData.h>
#include <vtkDataReader.h>
#include <vtkWriter.h>
#include <vtk_kwiml.h>
#include <torch/torch.h>
#include <torch/script.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <memory>

using namespace std;

extern "C"
{
  void init_ANNpredict();
  void ANNpredict(float *output,
                  float *input_0, float *input_1, float *input_2, float *input_3, float *input_4, 
                  float *input_5, float *input_6, float *input_7, float *input_8, float *input_9,
                  float *input_10, float *input_11, float *input_12, float *input_13, float *input_14,
                  float *input_15, float *input_16, float *input_17, float *input_18, float *input_19,
                  float *input_20, float *input_21, float *input_22, float *input_23, float *input_24,
                  float *input_25, float *input_26, float *input_27, float *input_28, float *input_29,
                  float *input_30, float *input_31, float *input_32, float *input_33, float *input_34,
                  float *input_35, float *input_36, float *input_37, float *input_38, float *input_39,
                  float *input_40, float *input_41, float *input_42, float *input_43, float *input_44,
                  float *input_45, float *input_46, float *input_47, float *input_48, float *input_49,
                  float *input_bound_0, float *input_bound_1, float *input_bound_2, float *input_bound_3, float *input_bound_4, 
                  float *input_bound_5, float *input_bound_6, float *input_bound_7, float *input_bound_8, float *input_bound_9,
                  float *input_bound_10, float *input_bound_11, float *input_bound_12, float *input_bound_13, float *input_bound_14,
                  float *input_bound_15, float *input_bound_16, float *input_bound_17, float *input_bound_18, float *input_bound_19,
                  float *input_bound_20, float *input_bound_21, float *input_bound_22, float *input_bound_23, float *input_bound_24,
                  float *input_bound_25, float *input_bound_26, float *input_bound_27, float *input_bound_28, float *input_bound_29,
                  float *input_bound_30, float *input_bound_31, float *input_bound_32, float *input_bound_33, float *input_bound_34,
                  float *input_bound_35, float *input_bound_36, float *input_bound_37, float *input_bound_38, float *input_bound_39,
                  float *input_bound_40, float *input_bound_41, float *input_bound_42, float *input_bound_43, float *input_bound_44,
                  float *input_bound_45, float *input_bound_46, float *input_bound_47, float *input_bound_48, float *input_bound_49);
  void end_freemem();
}

void init_ANNpredict()
{
  return;
}

void ANNpredict(float *output,
                float *input_0, float *input_1, float *input_2, float *input_3, float *input_4, 
                float *input_5, float *input_6, float *input_7, float *input_8, float *input_9,
                float *input_10, float *input_11, float *input_12, float *input_13, float *input_14,
                float *input_15, float *input_16, float *input_17, float *input_18, float *input_19,
                float *input_20, float *input_21, float *input_22, float *input_23, float *input_24,
                float *input_25, float *input_26, float *input_27, float *input_28, float *input_29,
                float *input_30, float *input_31, float *input_32, float *input_33, float *input_34,
                float *input_35, float *input_36, float *input_37, float *input_38, float *input_39,
                float *input_40, float *input_41, float *input_42, float *input_43, float *input_44,
                float *input_45, float *input_46, float *input_47, float *input_48, float *input_49,
                float *input_bound_0, float *input_bound_1, float *input_bound_2, float *input_bound_3, float *input_bound_4, 
                float *input_bound_5, float *input_bound_6, float *input_bound_7, float *input_bound_8, float *input_bound_9,
                float *input_bound_10, float *input_bound_11, float *input_bound_12, float *input_bound_13, float *input_bound_14,
                float *input_bound_15, float *input_bound_16, float *input_bound_17, float *input_bound_18, float *input_bound_19,
                float *input_bound_20, float *input_bound_21, float *input_bound_22, float *input_bound_23, float *input_bound_24,
                float *input_bound_25, float *input_bound_26, float *input_bound_27, float *input_bound_28, float *input_bound_29,
                float *input_bound_30, float *input_bound_31, float *input_bound_32, float *input_bound_33, float *input_bound_34,
                float *input_bound_35, float *input_bound_36, float *input_bound_37, float *input_bound_38, float *input_bound_39,
                float *input_bound_40, float *input_bound_41, float *input_bound_42, float *input_bound_43, float *input_bound_44,
                float *input_bound_45, float *input_bound_46, float *input_bound_47, float *input_bound_48, float *input_bound_49)
{

  /* IMPORT OF DATA FIELDS */
  const int num_samples = 81897;
  const int num_samples_bound = 55708;
  const int num_features = 6;
  const int num_features_bound = 2;
  const int num_snapshots = 50;
  const int num_points = 27896;

  // data for field variables
  auto data_0 = torch::from_blob(input_0, {num_samples, num_features}, torch::kFloat);
  auto data_1 = torch::from_blob(input_1, {num_samples, num_features}, torch::kFloat);
  auto data_2 = torch::from_blob(input_2, {num_samples, num_features}, torch::kFloat);
  auto data_3 = torch::from_blob(input_3, {num_samples, num_features}, torch::kFloat);
  auto data_4 = torch::from_blob(input_4, {num_samples, num_features}, torch::kFloat);
  auto data_5 = torch::from_blob(input_5, {num_samples, num_features}, torch::kFloat);
  auto data_6 = torch::from_blob(input_6, {num_samples, num_features}, torch::kFloat);
  auto data_7 = torch::from_blob(input_7, {num_samples, num_features}, torch::kFloat);
  auto data_8 = torch::from_blob(input_8, {num_samples, num_features}, torch::kFloat);
  auto data_9 = torch::from_blob(input_9, {num_samples, num_features}, torch::kFloat);
  auto data_10 = torch::from_blob(input_10, {num_samples, num_features}, torch::kFloat);
  auto data_11 = torch::from_blob(input_11, {num_samples, num_features}, torch::kFloat);
  auto data_12 = torch::from_blob(input_12, {num_samples, num_features}, torch::kFloat);
  auto data_13 = torch::from_blob(input_13, {num_samples, num_features}, torch::kFloat);
  auto data_14 = torch::from_blob(input_14, {num_samples, num_features}, torch::kFloat);
  auto data_15 = torch::from_blob(input_15, {num_samples, num_features}, torch::kFloat);
  auto data_16 = torch::from_blob(input_16, {num_samples, num_features}, torch::kFloat);
  auto data_17 = torch::from_blob(input_17, {num_samples, num_features}, torch::kFloat);
  auto data_18 = torch::from_blob(input_18, {num_samples, num_features}, torch::kFloat);
  auto data_19 = torch::from_blob(input_19, {num_samples, num_features}, torch::kFloat);
  auto data_20 = torch::from_blob(input_20, {num_samples, num_features}, torch::kFloat);
  auto data_21 = torch::from_blob(input_21, {num_samples, num_features}, torch::kFloat);
  auto data_22 = torch::from_blob(input_22, {num_samples, num_features}, torch::kFloat);
  auto data_23 = torch::from_blob(input_23, {num_samples, num_features}, torch::kFloat);
  auto data_24 = torch::from_blob(input_24, {num_samples, num_features}, torch::kFloat);
  auto data_25 = torch::from_blob(input_25, {num_samples, num_features}, torch::kFloat);
  auto data_26 = torch::from_blob(input_26, {num_samples, num_features}, torch::kFloat);
  auto data_27 = torch::from_blob(input_27, {num_samples, num_features}, torch::kFloat);
  auto data_28 = torch::from_blob(input_28, {num_samples, num_features}, torch::kFloat);
  auto data_29 = torch::from_blob(input_29, {num_samples, num_features}, torch::kFloat);
  auto data_30 = torch::from_blob(input_30, {num_samples, num_features}, torch::kFloat);
  auto data_31 = torch::from_blob(input_31, {num_samples, num_features}, torch::kFloat);
  auto data_32 = torch::from_blob(input_32, {num_samples, num_features}, torch::kFloat);
  auto data_33 = torch::from_blob(input_33, {num_samples, num_features}, torch::kFloat);
  auto data_34 = torch::from_blob(input_34, {num_samples, num_features}, torch::kFloat);
  auto data_35 = torch::from_blob(input_35, {num_samples, num_features}, torch::kFloat);
  auto data_36 = torch::from_blob(input_36, {num_samples, num_features}, torch::kFloat);
  auto data_37 = torch::from_blob(input_37, {num_samples, num_features}, torch::kFloat);
  auto data_38 = torch::from_blob(input_38, {num_samples, num_features}, torch::kFloat);
  auto data_39 = torch::from_blob(input_39, {num_samples, num_features}, torch::kFloat);
  auto data_40 = torch::from_blob(input_40, {num_samples, num_features}, torch::kFloat);
  auto data_41 = torch::from_blob(input_41, {num_samples, num_features}, torch::kFloat);
  auto data_42 = torch::from_blob(input_42, {num_samples, num_features}, torch::kFloat);
  auto data_43 = torch::from_blob(input_43, {num_samples, num_features}, torch::kFloat);
  auto data_44 = torch::from_blob(input_44, {num_samples, num_features}, torch::kFloat);
  auto data_45 = torch::from_blob(input_45, {num_samples, num_features}, torch::kFloat);
  auto data_46 = torch::from_blob(input_46, {num_samples, num_features}, torch::kFloat);
  auto data_47 = torch::from_blob(input_47, {num_samples, num_features}, torch::kFloat);
  auto data_48 = torch::from_blob(input_48, {num_samples, num_features}, torch::kFloat);
  auto data_49 = torch::from_blob(input_49, {num_samples, num_features}, torch::kFloat);

  std::vector<torch::Tensor> data_list = {data_0, data_1, data_2, data_3, data_4, data_5, data_6, data_7, data_8, data_9,
                                          data_10, data_11, data_12, data_13, data_14, data_15, data_16, data_17, data_18, data_19,
                                          data_20, data_21, data_22, data_23, data_24, data_25, data_26, data_27, data_28, data_29,
                                          data_30, data_31, data_32, data_33, data_34, data_35, data_36, data_37, data_38, data_39,
                                          data_40, data_41, data_42, data_43, data_44, data_45, data_46, data_47, data_48, data_49};
  auto data = torch::stack(data_list, 1);

  // data for boundary fields
  auto data_bound_0 = torch::from_blob(input_bound_0, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_1 = torch::from_blob(input_bound_1, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_2 = torch::from_blob(input_bound_2, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_3 = torch::from_blob(input_bound_3, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_4 = torch::from_blob(input_bound_4, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_5 = torch::from_blob(input_bound_5, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_6 = torch::from_blob(input_bound_6, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_7 = torch::from_blob(input_bound_7, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_8 = torch::from_blob(input_bound_8, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_9 = torch::from_blob(input_bound_9, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_10 = torch::from_blob(input_bound_10, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_11 = torch::from_blob(input_bound_11, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_12 = torch::from_blob(input_bound_12, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_13 = torch::from_blob(input_bound_13, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_14 = torch::from_blob(input_bound_14, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_15 = torch::from_blob(input_bound_15, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_16 = torch::from_blob(input_bound_16, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_17 = torch::from_blob(input_bound_17, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_18 = torch::from_blob(input_bound_18, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_19 = torch::from_blob(input_bound_19, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_20 = torch::from_blob(input_bound_20, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_21 = torch::from_blob(input_bound_21, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_22 = torch::from_blob(input_bound_22, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_23 = torch::from_blob(input_bound_23, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_24 = torch::from_blob(input_bound_24, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_25 = torch::from_blob(input_bound_25, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_26 = torch::from_blob(input_bound_26, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_27 = torch::from_blob(input_bound_27, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_28 = torch::from_blob(input_bound_28, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_29 = torch::from_blob(input_bound_29, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_30 = torch::from_blob(input_bound_30, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_31 = torch::from_blob(input_bound_31, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_32 = torch::from_blob(input_bound_32, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_33 = torch::from_blob(input_bound_33, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_34 = torch::from_blob(input_bound_34, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_35 = torch::from_blob(input_bound_35, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_36 = torch::from_blob(input_bound_36, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_37 = torch::from_blob(input_bound_37, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_38 = torch::from_blob(input_bound_38, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_39 = torch::from_blob(input_bound_39, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_40 = torch::from_blob(input_bound_40, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_41 = torch::from_blob(input_bound_41, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_42 = torch::from_blob(input_bound_42, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_43 = torch::from_blob(input_bound_43, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_44 = torch::from_blob(input_bound_44, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_45 = torch::from_blob(input_bound_45, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_46 = torch::from_blob(input_bound_46, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_47 = torch::from_blob(input_bound_47, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_48 = torch::from_blob(input_bound_48, {num_samples_bound, num_features_bound}, torch::kFloat);
  auto data_bound_49 = torch::from_blob(input_bound_49, {num_samples_bound, num_features_bound}, torch::kFloat);

  std::vector<torch::Tensor> data_list_b = {data_bound_0, data_bound_1, data_bound_2, data_bound_3, data_bound_4, data_bound_5, 
                                            data_bound_6, data_bound_7, data_bound_8, data_bound_9, data_bound_10, data_bound_11, 
                                            data_bound_12, data_bound_13, data_bound_14, data_bound_15, data_bound_16,
                                            data_bound_17, data_bound_18, data_bound_19, data_bound_20, data_bound_21,
                                            data_bound_22, data_bound_23, data_bound_24, data_bound_25, data_bound_26,
                                            data_bound_27, data_bound_28, data_bound_29, data_bound_30, data_bound_31,
                                            data_bound_32, data_bound_33, data_bound_34, data_bound_35, data_bound_36,
                                            data_bound_37, data_bound_38, data_bound_39, data_bound_40, data_bound_41,
                                            data_bound_42, data_bound_43, data_bound_44, data_bound_45, data_bound_46,
                                            data_bound_47, data_bound_48, data_bound_49};
  auto data_bound = torch::stack(data_list_b, 1);
  /* (END)IMPORT OF DATA FIELDS */

  /* creating vtk objects for cell_data to point_data conversion */
  // standard
  vtkSmartPointer<vtkUnstructuredGridReader> reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
  reader->SetFileName("/home/cenvinzf@coria.fr/LSTM/DATA/mesh/mesh_regular.vtk");
  reader->Update();
  vtkSmartPointer<vtkUnstructuredGrid> mesh = reader->GetOutput();
  std::vector<std::string> names = {"Pressure", "TempK", "TurbVisc", "Velocity:0", "Velocity:1", "Z"};

  // boundary
  vtkSmartPointer<vtkUnstructuredGridReader> reader_boundary = vtkSmartPointer<vtkUnstructuredGridReader>::New();
  reader_boundary->SetFileName("/home/cenvinzf@coria.fr/LSTM/DATA/mesh/mesh_boundary.vtk");
  reader_boundary->Update();
  vtkSmartPointer<vtkUnstructuredGrid> mesh_boundary = reader_boundary->GetOutput();
  std::vector<std::string> names_boundary = {"Norm Stress", "Stress:2"};

  torch::Tensor point_data_tensor = torch::zeros({num_points, num_snapshots, num_features+num_features_bound}, torch::kDouble);   // buffer tensor
  
  for (size_t t = 0; t < num_snapshots; ++t) {   // iter over all timesteps

    // boundary
    for (size_t j = 0; j < names_boundary.size(); ++j) {
      
      vtkSmartPointer<vtkDoubleArray> array_bound = vtkSmartPointer<vtkDoubleArray>::New();
      array_bound->SetName(names_boundary[j].c_str());
      array_bound->SetNumberOfComponents(1);

      for (int i = 0; i < data_bound.size(0); ++i) {
          array_bound->InsertNextValue(data_bound[i][t][j].item<double>());
      }

      mesh_boundary->GetCellData()->AddArray(array_bound);
    }

    // convert to point data
    vtkCellDataToPointData* c2p = vtkCellDataToPointData::New();
    c2p->SetInputData(mesh_boundary);
    c2p->Update();
    vtkSmartPointer<vtkUnstructuredGrid> pointMesh = vtkSmartPointer<vtkUnstructuredGrid>::New();
    pointMesh->DeepCopy(c2p->GetOutput());

    vtkSmartPointer<vtkPointData> point_data = pointMesh->GetPointData();

    if (point_data->GetNumberOfArrays() == 0) {
        std::cerr << "Errore: point_data non contiene array." << std::endl;
        return;
    }

    for (int j = 0; j < names_boundary.size(); ++j) {

      vtkDataArray* data_array = point_data->GetArray(names_boundary[j].c_str());
      
      for (vtkIdType i = 0; i < num_points; ++i) {
          point_data_tensor[i][t][j] = data_array->GetComponent(i, 0);
      }
    }

    // standard
    for (size_t j = 0; j < names.size(); ++j) {

      vtkSmartPointer<vtkDoubleArray> array = vtkSmartPointer<vtkDoubleArray>::New();
      array->SetName(names[j].c_str());
      array->SetNumberOfComponents(1);

      for (int i = 0; i < data.size(0); ++i) {
          array->InsertNextValue(data[i][t][j].item<double>());
      }

      mesh->GetCellData()->AddArray(array);
    }

    // convert to point data
    vtkCellDataToPointData* c2p_m = vtkCellDataToPointData::New();
    c2p_m->SetInputData(mesh);
    c2p_m->Update();
    vtkSmartPointer<vtkUnstructuredGrid> pointMesh_m = vtkSmartPointer<vtkUnstructuredGrid>::New();
    pointMesh_m->DeepCopy(c2p_m->GetOutput());
        
    vtkSmartPointer<vtkPointData> point_data_m = pointMesh_m->GetPointData();

    for (int j = 0; j < names.size(); ++j) {

      vtkDataArray* data_array_m = point_data_m->GetArray(names[j].c_str());
      for (vtkIdType i = 0; i < num_points; ++i) {
          point_data_tensor[i][t][j+num_features_bound] = data_array_m->GetComponent(i, 0);
      }
    }
  }   // (END) iter over all timesteps
  /* (END)creating vtk objects for cell_data to point_data conversion */

  /* normalization step */
  auto mean_t = torch::mean(point_data_tensor, 1);
  auto std_dev_t = torch::sqrt(torch::var(point_data_tensor, 1, true));

  std::vector<torch::jit::IValue> normalized_input;
  torch::Tensor normalized_data = torch::empty({num_points, num_snapshots, num_features+num_features_bound});

  // Normalization
  // Iter over timestep
  for (int k = 0; k < num_snapshots; ++k) {
    // Norm procedure: (data_timestep - mean) / stddev
    normalized_data.index({torch::indexing::Slice(), k, torch::indexing::Slice()}) = (point_data_tensor.index({torch::indexing::Slice(), k, torch::indexing::Slice()}) 
                                                                                      - mean_t) / (std_dev_t + 1E-06);
  }
  /* (END)normalization step */

  torch::jit::script::Module module;
  // Deserialize the ScriptModule from a file using torch::jit::load().
  module = torch::jit::load("/home/cenvinzf@coria.fr/workdir/nn-cfd/torch2c++/traced_model_les.pt");

  // Execute the model and turn its output into a tensor.
  normalized_input.push_back(normalized_data);
  at::Tensor outputs = module.forward(normalized_input).toTensor();
  // torch::save(outputs, "/home/cenvinzf@coria.fr/Desktop/test_cs_coupling/t15_ANN.pt");

  // Denormalization step
  outputs = (outputs * std_dev_t[-1]) + mean_t[-1];

  // converting point_data to cell_data
  double *data_ptr = outputs.data_ptr<double>();
  vtkSmartPointer<vtkUnstructuredGridReader> reader_z = vtkSmartPointer<vtkUnstructuredGridReader>::New();
  reader_z->SetFileName("/home/cenvinzf@coria.fr/LSTM/DATA/mesh/mesh_regular.vtk");
  reader_z->Update();
  vtkSmartPointer<vtkUnstructuredGrid> mesh_z = reader_z->GetOutput();

  vtkSmartPointer<vtkDoubleArray> array = vtkSmartPointer<vtkDoubleArray>::New();
  array->SetName("Z");
  array->SetNumberOfComponents(1);

  for (int i = 0; i < num_points; ++i) {
     array->InsertNextValue(data_ptr[i]);
  }

  mesh_z->GetPointData()->AddArray(array);

  // convert back to cell data
  vtkPointDataToCellData* p2c = vtkPointDataToCellData::New();
  p2c->SetInputData(mesh_z);
  p2c->Update();
  vtkSmartPointer<vtkUnstructuredGrid> cellMesh = vtkSmartPointer<vtkUnstructuredGrid>::New();
  cellMesh->DeepCopy(p2c->GetOutput());

  vtkSmartPointer<vtkCellData> cell_data = cellMesh->GetCellData();
  vtkDataArray* data_array = cell_data->GetArray("Z");

  for (vtkIdType i = 0; i < num_samples; ++i) {
    output[i] = data_array->GetComponent(i, 0);
  }

}   // (END)ANNPredict

void end_freemem()
{
  return;
}
