//+------------------------------------------------------------------+
//|                        MathEngineOnnx.mqh                        |
//|                       https://www.mql5.com                       |
//+------------------------------------------------------------------+
#property copyright "Olamide Adebayo"
#property link      "https://www.mql5.com"

#include "BenchmarkConfig.mqh"

//+------------------------------------------------------------------+
//| CMathEngineOnnx                                                  |
//| Engine 3: ONNX Runtime through OnnxRun.                          |
//| Same trained weights as the other two, but the tensors are       |
//| float32. Any fidelity gap here comes from that round trip.       |
//+------------------------------------------------------------------+
class CMathEngineOnnx
  {
private:
   long              m_handle;
   bool              m_ready;

public:
                     CMathEngineOnnx(void) : m_handle(INVALID_HANDLE), m_ready(false) {}

//+------------------------------------------------------------------+
//| CMathEngineOnnx::~CMathEngineOnnx                                |
//+------------------------------------------------------------------+
                    ~CMathEngineOnnx(void)
     {
      if(m_handle != INVALID_HANDLE)
         OnnxRelease(m_handle);
     }

//+------------------------------------------------------------------+
//| CMathEngineOnnx::Init                                            |
//+------------------------------------------------------------------+
   bool              Init(const string onnx_filename)
     {
      m_handle = OnnxCreate(onnx_filename, ONNX_DEFAULT);
      if(m_handle == INVALID_HANDLE)
        {
         PrintFormat("[MathEngineOnnx] OnnxCreate failed, error %d", GetLastError());
         return false;
        }

      ulong input_shape[]  = {1, BENCH_INPUT_DIM};
      ulong output_shape[] = {1, BENCH_OUTPUT_DIM};

      if(!OnnxSetInputShape(m_handle, 0, input_shape))
        {
         PrintFormat("[MathEngineOnnx] OnnxSetInputShape failed, error %d", GetLastError());
         return false;
        }
      if(!OnnxSetOutputShape(m_handle, 0, output_shape))
        {
         PrintFormat("[MathEngineOnnx] OnnxSetOutputShape failed, error %d", GetLastError());
         return false;
        }

      // same idea as the WeightLoader checks: fail at load time
      if(input_shape[1] != BENCH_INPUT_DIM || output_shape[1] != BENCH_OUTPUT_DIM)
        {
         Print("[MathEngineOnnx] declared ONNX shape does not match BenchmarkConfig.mqh");
         return false;
        }

      m_ready = true;
      return true;
     }

//+------------------------------------------------------------------+
//| CMathEngineOnnx::Predict                                         |
//+------------------------------------------------------------------+
   double            Predict(const double &in_vec[])
     {
      float in_data[BENCH_INPUT_DIM];
      for(int i = 0; i < BENCH_INPUT_DIM; i++)
         in_data[i] = (float)in_vec[i];

      float out_data[BENCH_OUTPUT_DIM];
      if(!OnnxRun(m_handle, ONNX_DEFAULT, in_data, out_data))
        {
         PrintFormat("[MathEngineOnnx] OnnxRun failed, error %d", GetLastError());
         return 0.0;
        }
      return (double)out_data[0];
     }

   bool              IsReady(void) const { return m_ready; }
  };
