/*
This file is the driver for problem defined by Julia functions.
The Julia driver "DriverOPT.m" creates function handles of a cost function and
parameters for both solvers and manifolds, which are passing to the
binary file generated by this C++ driver.

---- WH
*/

#ifndef DRIVERJULIAPROB_H
#define DRIVERJULIAPROB_H

#include <iostream>
#include "Others/randgen.h"
#include "Manifolds/Manifold.h"
#include "Problems/Problem.h"
#include "Solvers/SolversLS.h"
#include <ctime>

#include "Manifolds/Euclidean/Euclidean.h"
#include "Manifolds/Euclidean/EucVariable.h"
#include "Manifolds/Euclidean/EucVector.h"

#include "Manifolds/Stiefel/StieVector.h"
#include "Manifolds/Stiefel/StieVariable.h"
#include "Manifolds/Stiefel/Stiefel.h"

#include "Solvers/RSD.h"
#include "Solvers/RNewton.h"
#include "Solvers/RCG.h"
#include "Solvers/RBroydenFamily.h"
#include "Solvers/RWRBFGS.h"
#include "Solvers/RBFGS.h"
#include "Solvers/LRBFGS.h"
#include "Solvers/RBFGSLPSub.h"
#include "Solvers/LRBFGSLPSub.h"
#include "Solvers/RGS.h"

#include "Solvers/SolversTR.h"
#include "Solvers/RTRSD.h"
#include "Solvers/RTRNewton.h"
#include "Solvers/RTRSR1.h"
#include "Solvers/LRTRSR1.h"

#include "Others/def.h"

#include "Manifolds/Sphere/Sphere.h"
#include "Manifolds/Sphere/SphereVariable.h"
#include "Manifolds/Sphere/SphereVector.h"

#include "Manifolds/Oblique/Oblique.h"
#include "Manifolds/Oblique/ObliqueVariable.h"
#include "Manifolds/Oblique/ObliqueVector.h"

#include "Manifolds/LowRank/LowRank.h"
#include "Manifolds/LowRank/LowRankVariable.h"
#include "Manifolds/LowRank/LowRankVector.h"

#include "Manifolds/OrthGroup/OrthGroup.h"
#include "Manifolds/OrthGroup/OrthGroupVariable.h"
#include "Manifolds/OrthGroup/OrthGroupVector.h"

#include "Manifolds/SPDManifold/SPDManifold.h"
#include "Manifolds/SPDManifold/SPDVariable.h"
#include "Manifolds/SPDManifold/SPDVector.h"

#include "Manifolds/L2Sphere/L2Sphere.h"
#include "Manifolds/L2Sphere/L2SphereVariable.h"
#include "Manifolds/L2Sphere/L2SphereVector.h"

#include "Manifolds/CpxNStQOrth/CpxNStQOrth.h"
#include "Manifolds/CpxNStQOrth/CSOVariable.h"
#include "Manifolds/CpxNStQOrth/CSOVector.h"

#include "Manifolds/Grassmann/Grassmann.h"
#include "Manifolds/Grassmann/GrassVariable.h"
#include "Manifolds/Grassmann/GrassVector.h"

#include "Manifolds/EucPositive/EucPositive.h"
#include "Manifolds/EucPositive/EucPosVariable.h"
#include "Manifolds/EucPositive/EucPosVector.h"

#include "Manifolds/PreShapeCurves/PreShapeCurves.h"
#include "Manifolds/PreShapeCurves/PSCVariable.h"
#include "Manifolds/PreShapeCurves/PSCVector.h"

#include "Manifolds/SPDTensor/SPDTensor.h"
#include "Manifolds/SPDTensor/SPDTVariable.h"
#include "Manifolds/SPDTensor/SPDTVector.h"

#include "Manifolds/ProductElement.h"
#include "Manifolds/ProductManifold.h"

#include "Problems/juliaProblem.h"

#ifdef DRIVERJULIAPROB

#include "julia.h"

using namespace ROPTLIB;

struct FunHandles {
    const char *fname;
    const char *gfname;
    const char *hfname;
    const char *isstopped;
    const char *LinesearchInput;
};

struct SolverParams {
    long int IsCheckParams;
    long int IsCheckGradHess;
    const char *name;
    //Solvers
    long int Stop_Criterion;
    double Tolerance;
    double Diffx;
    long int NumExtraGF;
    long int TimeBound;
    long int Min_Iteration;
    long int Max_Iteration;
    long int OutputGap;
    long int DEBUG;
    //QuasiNewton
    long int isconvex;
    double nu;
    double mu;
    long int LengthSY;
    double lambdaLower;
    double lambdaUpper;
    //SolversLS
    long int LineSearch_LS;
    long int IsPureLSInput;
    double LS_alpha;
    double LS_beta;
    double Minstepsize;
    double Maxstepsize;
    double LS_ratio1;
    double LS_ratio2;
    double Initstepsize;
    double Accuracy;
    double Finalstepsize;
    long int Num_pre_funs;
    long int InitSteptype;
    //SolversTR
    double Acceptence_Rho;
    double Shrinked_tau;
    double Magnified_tau;
    double minimum_Delta;
    double maximum_Delta;
    long int useRand;
    long int Max_Inner_Iter;
    long int Min_Inner_Iter;
    double theta;
    double kappa;
    double initial_Delta;
    //SolversLSLPSub
    double Eps;
    double Theta_eps;
    double Min_Eps;
    double Del;
    double Theta_del;
};

struct ManiParams {
    long int IsCheckParams;
    long int numoftypes;
    const char **name;
    long int *numofmani;
    long int *paramset;
    long int *m;
    long int *n;
    long int *p;
};

/*This function checks the number and formats of input parameters.
nlhs: the number of output in mxArray format
plhs: the output objects in mxArray format
nrhs: the number of input in mxArray format
prhs: the input objects in mxArray format */
double *DriverJuliaProb(struct FunHandles *Handles, struct SolverParams *Sparams,
                     struct ManiParams *Mparams, integer inHasHHR, double *X0, integer length_X0, double *soln = nullptr);

/*This file creates a C++ solver based on the input parameters and run the solver to obtain a solution.*/
double *ParseSolverParamsAndOptimizing(struct SolverParams *Sparams, struct FunHandles *Handles, Problem *Prob, Variable *initialX, Variable *SolnX);

/*This file creates all components of the product manifolds. Note that the cost function is always
defined on a product manifolds.*/
bool ParseManiParams(struct ManiParams *Mparams, Manifold **&manifolds, Element **&elements,
    integer *&powsinterval, integer &numoftype, integer &numoftotal);

/*Create a manifold based on the parameters.*/
Manifold *GetAManifold(const char *name, integer n, integer m, integer p = 1);

/*Create an element based on the parameters. Note that the element is a component of the initial iterate.*/
Element *GetAnElement(const char *name, integer n, integer m, integer p = 1);


namespace RJULIA{
    extern jl_function_t *isstopped;
    /*This function defines the stopping criterion that may be used in the C++ solver*/
    bool juliaInnerStop(Variable *x, Vector *gf, double f, double ngf, double ngf0, const Problem *prob, const Solvers *solver);
    extern jl_function_t *LinesearchInput;
    /*This function defines the line search algorithm that may be used in the C++ solver*/
    double juliaLinesearchInput(integer iter, Variable *x1, Vector *eta1, double initialstepsize, double initialslope, const Problem *prob, const Solvers *solver);
};

#endif // end of DRIVERJULIAPROB

#endif // end of DRIVERMEXPROB_H
