#pragma once
#include <memory>
#include "../arhiplex_itf.h"
#include "../arhiplex_utils.h"
#include "../arhiplex_cpp.h"
#include "../arhiplex_cpp_defines.h"
#include "variable.h"
#include "linear_expression.h"
#include "constraint.h"
#include "general_constraint.h"
#include "utils.h"
#include "solve_result.h"

#ifdef ARHIPLEX_ENTERPRISE
#include "compute_iis_result.h"
#endif

namespace arhiplex
{
/**
 \~russian
 * Предоставляет функциии чтения/записи файлов, модификации модели, управления переменными, ограничениями, а также целевой функцией
 \brief Класс для работы с моделью

 \~english
 * Provides functions for reading/writing files, modifying the model, managing variables, constraints, and the objective function
 \brief Class for working with the model

 \~chinese-traditional
 * Provides functions for reading/writing files, modifying the model, managing variables, constraints, and the objective function
 \brief Class for working with the model
 */

class Model
{
public:

#ifndef OEM_LICENSING
/**
\~russian
Создает экземпляр модели
\param[in] name Имя модели

\~english
Creates an instance of the model
\param[in] name The name of the model

\~chinese-traditional
Creates an instance of the model
\param[in] name The name of the model
*/
    Model(const char *name = nullptr) : _model(CreateModel(), releasable_object_deleter)
    {
        CHECKERR(_model);
        if (name)
            _model->SetName(name);
    }
#else
/**
\~russian
Создает экземпляр модели
\param[in] oem_license_key Ключ OEM лицензии
\param[in] name Имя модели

\~english
Creates an instance of the model
\param[in] oem_license_key OEM license key
\param[in] name Model name

\~chinese-traditional
Creates an instance of the model
\param[in] oem_license_key OEM license key
\param[in] name Model name
*/
    Model(const char *oem_license_key = nullptr, const char *name = nullptr)
        : _model(CreateModel(), releasable_object_deleter) {
        CHECKERR(_model);

        _model->SetOemLicenseKey(oem_license_key);

        if (name) {
            _model->SetName(name);
        }
    }
#endif

/**\private*/
    Model(arhiplex::IModel *model) : _model(model, releasable_object_deleter)
    {
    }

/**
\~russian
Читает модель из файла, тип модели определяется по расширению
\param[in] szFileName путь к файлу модели

\~english
Reads a model from a file, the model type is determined by the extension
\param[in] szFileName path to the model file

\~chinese-traditional
Reads a model from a file, the model type is determined by the extension
\param[in] szFileName path to the model file
*/
    void Read(const char *szFileName)
    {
        _model->Read(szFileName);
        CHECKERR(_model);
    }

/**
\~russian
Читает модель из файла, при этом он будет читаться как MPS файл независимо от расширения
\param[in] szFileName путь к файлу модели

\~english
Reads a model from a file, it will be read as an MPS file regardless of the extension
\param[in] szFileName path to the model file

\~chinese-traditional
Reads a model from a file, it will be read as an MPS file regardless of the extension
\param[in] szFileName path to the model file
*/
    void ReadMps(const char *szFileName)
    {
        _model->ReadMps(szFileName);
        CHECKERR(_model);
    }

/**
\~russian
Читает модель из файла, при этом он будет читаться как LP файл независимо от расширения
\param[in] szFileName путь к файлу модели

\~english
Reads a model from a file, it will be read as an LP file regardless of the extension
\param[in] szFileName path to the model file

\~chinese-traditional
Reads a model from a file, it will be read as an LP file regardless of the extension
\param[in] szFileName path to the model file
*/
    void ReadLp(const char *szFileName)
    {
        _model->ReadLp(szFileName);
        CHECKERR(_model);
    }

/**
\~russian
Получает целочисленный параметр
\param[in] szParam имя параметра
\return Значение параметра

\~english
Gets an integer parameter
\param[in] szParam parameter name
\return Parameter value

\~chinese-traditional
Gets an integer parameter
\param[in] szParam parameter name
\return Parameter value
*/
    int GetIntParam(const char *szParam)
    {
        int val = _model->GetIntParam(szParam);
        CHECKERR(_model);
        return val;
    }

/**
\~russian
Получает параметр с плавающей точкой
\param[in] szParam имя параметра
\return Значение параметра

\~english
Gets a floating point parameter
\param[in] szParam parameter name
\return Parameter value

\~chinese-traditional
Gets a floating point parameter
\param[in] szParam parameter name
\return Parameter value
*/
    double GetDblParam(const char *szParam)
    {
        double val = _model->GetDblParam(szParam);
        CHECKERR(_model);
        return val;
    }

/**
\~russian
Получает строковый параметр
\param[in] szParam имя параметра
\return Значение параметра

\~english
Gets a string parameter
\param[in] szParam parameter name
\return Parameter value

\~chinese-traditional
Gets a string parameter
\param[in] szParam parameter name
\return Parameter value
*/
    std::string GetStringParam(const char *szParam)
    {
        std::string val = _model->GetStringParam(szParam);
        CHECKERR(_model);
        return val;
    }

/**
\~russian
Получает логический параметр
\param[in] szParam имя параметра
\return Значение параметра

\~english
Gets a boolean parameter
\param[in] szParam parameter name
\return Parameter value

\~chinese-traditional
Gets a boolean parameter
\param[in] szParam parameter name
\return Parameter value
*/
    bool GetBoolParam(const char *szParam)
    {
        bool val = _model->GetBoolParam(szParam);
        CHECKERR(_model);
        return val;
    }

/**
\~russian
Задаёт целочисленный параметр
\param[in] szParam имя параметра
\param[in] nVal Новое значение параметра

\~english
Sets an integer parameter
\param[in] szParam parameter name
\param[in] nVal New value of the parameter

\~chinese-traditional
Sets an integer parameter
\param[in] szParam parameter name
\param[in] nVal New value of the parameter
*/
    void SetIntParam(const char *szParam, int nVal)
    {
        _model->SetIntParam(szParam, nVal);
        CHECKERR(_model);
    }

/**
\~russian
Задаёт параметр с плавающей точкой
\param[in] szParam имя параметра
\param[in] dVal Новое значение параметра

\~english
Sets a floating point parameter
\param[in] szParam parameter name
\param[in] dVal New value of the parameter

\~chinese-traditional
Sets a floating point parameter
\param[in] szParam parameter name
\param[in] dVal New value of the parameter
*/
    void SetDblParam(const char *szParam, double dVal)
    {
        _model->SetDblParam(szParam, dVal);
        CHECKERR(_model);
    }

/**
\~russian
Задаёт строковый параметр
\param[in] szParam имя параметра
\param[in] cVal Новое значение параметра

\~english
Sets a string parameter
\param[in] szParam parameter name
\param[in] cVal New parameter value

\~chinese-traditional
Sets a string parameter
\param[in] szParam parameter name
\param[in] cVal New parameter value
*/
    void SetStringParam(const char *szParam, const char* cVal)
    {
        _model->SetStringParam(szParam, cVal);
        CHECKERR(_model);
    }

/**
\~russian
Задаёт логический параметр
\param[in] szParam имя параметра
\param[in] bVal Новое значение параметра

\~english
Sets a boolean parameter
\param[in] szParam parameter name
\param[in] bVal New parameter value

\~chinese-traditional
Sets a boolean parameter
\param[in] szParam parameter name
\param[in] bVal New parameter value
*/
    void SetBoolParam(const char *szParam, bool bVal)
    {
        _model->SetBoolParam(szParam, bVal);
        CHECKERR(_model);
    }

/**
\~russian
Записывает модель в файл. Тип будет определен по расширению
\param[in] szFileName путь к записываемому файлу
\param[in] name_mapping_file путь к файлу, который будет содержать
соответствие между именами модели при анонимизации (если пустой - запись
без анонимизации)

\~english
Writes the model to a file. The type will be determined by the extension
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)

\~chinese-traditional
Writes the model to a file. The type will be determined by the extension
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)
*/

    void Write(const char *szFileName, const char* name_mapping_file = "")
    {
        _model->Write(szFileName, name_mapping_file);
        CHECKERR(_model);
    }

/**
\~russian
Записывает модель в файл в формате MPS
\param[in] szFileName путь к записываемому файлу
\param[in] name_mapping_file путь к файлу, который будет содержать
соответствие между именами модели при анонимизации (если пустой - запись
без анонимизации)

\~english
Writes the model to a file in MPS format
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)

\~chinese-traditional
Writes the model to a file in MPS format
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)
*/

    void WriteMps(const char *szFileName, const char* name_mapping_file = "")
    {
        _model->WriteMps(szFileName, name_mapping_file);
        CHECKERR(_model);
    }

/**
\~russian
Записывает модель в файл в формате LP
\param[in] szFileName путь к записываемому файлу
\param[in] name_mapping_file путь к файлу, который будет содержать
соответствие между именами модели при анонимизации (если пустой - запись
без анонимизации)

\~english
Writes the model to a file in LP format
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)

\~chinese-traditional
Writes the model to a file in LP format
\param[in] szFileName path to the file being written
\param[in] name_mapping_file path to the file that will contain
the correspondence between the model names during anonymization (if empty - write
without anonymization)
*/

    void WriteLp(const char *szFileName, const char* name_mapping_file = "")
    {
        _model->WriteLp(szFileName, name_mapping_file);
        CHECKERR(_model);
    }

/**
\~russian
Добавляет переменную в модель с заданными параметрами
\param[in] lb нижняя граница переменной
\param[in] ub верхняя граница переменной
\param[in] obj коэффициент к переменной в целевой функции
\param[in] var_type тип переменной
\param[in] szName имя переменной
\return объект новой переменной

\~english
Adds a variable to the model with the given parameters
\param[in] lb lower bound of the variable
\param[in] ub upper bound of the variable
\param[in] obj coefficient of the variable in the objective function
\param[in] var_type variable type
\param[in] szName variable name
\return new variable object

\~chinese-traditional
Adds a variable to the model with the given parameters
\param[in] lb lower bound of the variable
\param[in] ub upper bound of the variable
\param[in] obj coefficient of the variable in the objective function
\param[in] var_type variable type
\param[in] szName variable name
\return new variable object
*/
    Variable AddVariable(double lb = 0, double ub = inf, double obj = 0.0,
            variable_type var_type = variable_type::continuous, const char *szName = "")
    {
        Variable var = _model->AddVariable(lb, ub, obj, var_type, szName);
        CHECKERR(_model);
        return var;
    }

/**
\~russian
Добавляет ограничение в модель с заданными параметрами
\param[in] expr линейное выражение для ограничения
\param[in] sense тип ограничения ( <=, >= , == )
\param[in] rhs константное значение в правой части ограничения
\param[in] szName имя ограничения. Если передана пустая строка или nullptr, имя ограничения будет сгенерировано
\return объект нового ограничения

\~english
Adds a constraint to the model with the given parameters
\param[in] expr linear expression for the constraint
\param[in] sense constraint type ( <=, >= , == )
\param[in] rhs constant value on the right side of the constraint
\param[in] szName constraint name. If an empty string or nullptr is passed, the constraint name will be generated
\return new constraint object

\~chinese-traditional
Adds a constraint to the model with the given parameters
\param[in] expr linear expression for the constraint
\param[in] sense constraint type ( <=, >= , == )
\param[in] rhs constant value on the right side of the constraint
\param[in] szName constraint name. If an empty string or nullptr is passed, the constraint name will be generated
\return new constraint object
*/
    Constraint AddConstraint(const LinearExpression &expr, constraint_sense sense, double rhs,
                             const char *szName)
    {
        Constraint constr = _model->AddConstraint(expr.Get(), sense, rhs, szName);
        CHECKERR(_model);
        return constr;
    }

/**
\~russian
Добавляет ограничение в модель с заданными параметрами
\param[in] expr квадратичное выражение для ограничения
\param[in] sense тип ограничения ( <=, >= , == )
\param[in] rhs константное значение в правой части ограничения
\param[in] szName имя ограничения. Если передана пустая строка или nullptr, имя ограничения будет сгенерировано
\return объект нового ограничения

\~english
Adds a constraint to the model with the given parameters
\param[in] expr quadratic expression for the constraint
\param[in] sense constraint type ( <=, >= , == )
\param[in] rhs constant value on the right side of the constraint
\param[in] szName constraint name. If an empty string or nullptr is passed, the constraint name will be generated
\return new constraint object

\~chinese-traditional
Adds a constraint to the model with the given parameters
\param[in] expr quadratic expression for the constraint
\param[in] sense constraint type ( <=, >= , == )
\param[in] rhs constant value on the right side of the constraint
\param[in] szName constraint name. If an empty string or nullptr is passed, the constraint name will be generated
\return new constraint object
*/
    Constraint AddConstraint(const QuadExpression& expr, constraint_sense sense, double rhs,
        const char* szName)
    {
        Constraint constr = _model->AddConstraint(expr.Get(), sense, rhs, szName);
        CHECKERR(_model);
        return constr;
    }


/**
\~russian
Добавляет ограничение в модель с заданными параметрами
\param[in] lhs выражение для ограничения (левая часть)
\param[in] sense переменная знака ограничения ( <=, >= , == )
\param[in] rhs выражение в правой части ограничения
\param[in] szName имя ограничения
\return объект нового ограничения

\~english
Adds a constraint to the model with the given parameters
\param[in] lhs expression for the constraint (left side)
\param[in] sense variable of the constraint sign ( <=, >= , == )
\param[in] rhs expression on the right side of the constraint
\param[in] szName name of the constraint
\return object of the new constraint

\~chinese-traditional
Adds a constraint to the model with the given parameters
\param[in] lhs expression for the constraint (left side)
\param[in] sense variable of the constraint sign ( <=, >= , == )
\param[in] rhs expression on the right side of the constraint
\param[in] szName name of the constraint
\return object of the new constraint
*/

    Constraint AddConstraint(const LinearExpression &lhs, constraint_sense sense, const LinearExpression &rhs,
                         const char *szName)
    {
        auto copy = lhs.CreateFreeCopy();
        copy -= rhs;
        return AddConstraint(copy, sense, 0.0, szName);
    }


/**
\~russian
Добавляет интервальное ограничение в модель с заданными параметрами, вида lhs <= expr <= rhs
\param[in] expr выражение для ограничения
\param[in] lb нижняя граница ограничения
\param[in] ub верхняя граница ограничения
\param[in] szName имя ограничения
\return объект нового ограничения

\~english
Adds an interval constraint to the model with the given parameters, of the form lhs <= expr <= rhs
\param[in] expr expression for the constraint
\param[in] lb lower bound of the constraint
\param[in] ub upper bound of the constraint
\param[in] szName name of the constraint
\return object of the new constraint

\~chinese-traditional
Adds an interval constraint to the model with the given parameters, of the form lhs <= expr <= rhs
\param[in] expr expression for the constraint
\param[in] lb lower bound of the constraint
\param[in] ub upper bound of the constraint
\param[in] szName name of the constraint
\return object of the new constraint
*/
    Constraint AddConstraint(const LinearExpression &expr, double lb, double ub, const char *szName)
    {
        Constraint constr = _model->AddConstraint(expr.Get(), lb, ub, szName);
        CHECKERR(_model);
        return constr;
    }

/**
\~russian
Добавляет интервальное ограничение в модель с заданными параметрами, вида lhs <= expr <= rhs
\param[in] expr выражение для ограничения
\param[in] lb нижняя граница ограничения
\param[in] ub верхняя граница ограничения
\param[in] szName имя ограничения
\return объект нового ограничения

\~english
Adds an interval constraint to the model with the given parameters, of the form lhs <= expr <= rhs
\param[in] expr expression for the constraint
\param[in] lb lower bound of the constraint
\param[in] ub upper bound of the constraint
\param[in] szName name of the constraint
\return object of the new constraint

\~chinese-traditional
Adds an interval constraint to the model with the given parameters, of the form lhs <= expr <= rhs
\param[in] expr expression for the constraint
\param[in] lb lower bound of the constraint
\param[in] ub upper bound of the constraint
\param[in] szName name of the constraint
\return object of the new constraint
*/
    Constraint AddConstraint(const QuadExpression& expr, double lb, double ub, const char* szName)
    {
        Constraint constr = _model->AddConstraint(expr.Get(), lb, ub, szName);
        CHECKERR(_model);
        return constr;
    }

/**
\~russian
Добавляет уже существующее ограничение в модель
\param[in] constr ограничение
\param[in] szName имя ограничения
\return объект нового ограничения

\~english
Adds an existing constraint to the model
\param[in] constr constraint
\param[in] szName constraint name
\return new constraint object

\~chinese-traditional
Adds an existing constraint to the model
\param[in] constr constraint
\param[in] szName constraint name
\return new constraint object
*/
    Constraint AddConstraint(const Constraint &constr, const char *szName)
    {
        Constraint new_constr = _model->AddConstraint(constr.Get(), szName);
        CHECKERR(_model);
        return new_constr;
    }

/**
\~russian
Получает переменную модели по индексу
\param[in] idx индекс переменной
\return объект переменной

\~english
Gets a model variable by index
\param[in] idx variable index
\return variable object

\~chinese-traditional
Gets a model variable by index
\param[in] idx variable index
\return variable object
*/
    Variable GetVariable(int idx) const
    {
        Variable var = _model->GetVariable(idx);
        CHECKERR(_model);
        return var;
    }

/**
\~russian
Получает количество переменных в модели
\return количество переменных в модели

\~english
Gets the number of variables in the model
\return the number of variables in the model

\~chinese-traditional
Gets the number of variables in the model
\return the number of variables in the model
*/
    int GetVariablesCount() const
    {
        int count = _model->GetVariablesCount();
        CHECKERR(_model);
        return count;
    }

/**
\~russian
Получает переменную из модели по имени
\param[in] name имя переменной
\return объект переменной

\~english
Gets a variable from the model by name
\param[in] name variable name
\return variable object

\~chinese-traditional
Gets a variable from the model by name
\param[in] name variable name
\return variable object
*/
    Variable GetVariableByName(const char *name)
    {
        Variable var = _model->GetVariableByName(name);
        CHECKERR(_model);
        return var;
    }

/**
\~russian
Получает ограничение из модели по индексу
\param[in] idx индекс ограничения
\return объект ограничения

\~english
Gets a constraint from the model by index
\param[in] idx constraint index
\return constraint object

\~chinese-traditional
Gets a constraint from the model by index
\param[in] idx constraint index
\return constraint object
*/
    Constraint GetConstraint(int idx) const
    {
        Constraint constr = _model->GetConstraint(idx);
        CHECKERR(_model);
        return constr;
    }

/**
\~russian
Получает количество ограничений модели
\return количество ограничений модели

\~english
Gets the number of model constraints
\return the number of model constraints

\~chinese-traditional
Gets the number of model constraints
\return the number of model constraints
*/
    int GetConstraintsCount() const
    {
        int count = _model->GetConstraintsCount();
        CHECKERR(_model);
        return count;
    }

/**
\~russian
Получает ограничение из модели по имени
\param[in] szName имя ограничения
\return объект ограничения

\~english
Gets a constraint from the model by name
\param[in] szName constraint name
\return constraint object

\~chinese-traditional
Gets a constraint from the model by name
\param[in] szName constraint name
\return constraint object
*/
    Constraint GetConstrByName(const char *szName)
    {
        Constraint constr = _model->GetConstraintByName(szName);
        CHECKERR(_model);
        return constr;
    }

/**
\~russian
Задаёт тип оптимизации целевой функции - максимизация или минимизация
\param[in] sense тип оптимизации

\~english
Specifies the type of optimization of the objective function - maximization and minimization
\param[in] sense optimization type

\~chinese-traditional
Specifies the type of optimization of the objective function - maximization and minimization
\param[in] sense optimization type
*/    
    void SetObjectiveSense(objective_sense sense)
    {
        _model->SetObjectiveSense(sense);
        CHECKERR(_model);
    }

/**
\~russian
Получает тип оптимизации целевой функции
\return тип оптимизации

\~english
Gets the optimization type of the objective function
\return optimization type

\~chinese-traditional
Gets the optimization type of the objective function
\return optimization type
*/ 
    objective_sense GetObjectiveSense() const
    {
        return _model->GetObjectiveSense();
    }

/**
\~russian
Задаёт константу в выражении целевой функции
\param[in] constant значение константы в целевой функции

\~english
Specifies a constant in the objective function expression
\param[in] constant the value of the constant in the objective function

\~chinese-traditional
Specifies a constant in the objective function expression
\param[in] constant the value of the constant in the objective function
*/ 
    void SetObjectiveOffset(double constant)
    {
        _model->SetObjectiveOffset(constant);
        CHECKERR(_model);
    }

/**
\~russian
Задаёт выражение целевой функции и тип оптимизации
\param[in] expr линейное выражение
\param[in] sense тип оптимизации

\~english
Specifies the objective function expression and optimization type
\param[in] expr linear expression
\param[in] sense optimization type

\~chinese-traditional
Specifies the objective function expression and optimization type
\param[in] expr linear expression
\param[in] sense optimization type
*/ 
    void SetObjective(const LinearExpression& expr, objective_sense sense = objective_sense::minimize)
    {
        _model->SetObjective(expr.Get(), sense);
        CHECKERR(_model);
    }

/**
\~russian
Задаёт выражение целевой функции и тип оптимизации
\param[in] expr квадратичное выражение
\param[in] sense тип оптимизации

\~english
Specifies the objective function expression and optimization type
\param[in] expr quadratic expression
\param[in] sense optimization type

\~chinese-traditional
Specifies the objective function expression and optimization type
\param[in] expr quadratic expression
\param[in] sense optimization type
*/
    void SetObjective(const QuadExpression& expr, objective_sense sense = objective_sense::minimize)
    {
        _model->SetObjective(expr.Get(), sense);
        CHECKERR(_model);
    }


/**
\~russian
Получает выражение целевой функции
\return Объект выражения

\~english
Gets the objective function expression
\return Expression object

\~chinese-traditional
Gets the objective function expression
\return Expression object
*/ 
    LinearExpression GetObjective() const
    {
        LinearExpression expr = _model->GetObjective();
        CHECKERR(_model);
        return expr;
    }

/**
\~russian
Стартует оптимизацию модели
\return Объект с результатом оптимизации

\~english
Starts model optimization
\return Object with optimization result

\~chinese-traditional
Starts model optimization
\return Object with optimization result
*/ 
    SolveResult Solve()
    {
        auto res = _model->Solve();
        CHECKERR(_model);
        return res;
    }

/**
\~russian
Стартует оптимизацию модели на удаленном сервере в синхронном режиме.
Предварительно необходимо установить переменную окружения
X_API_KEY.
\param[in] name_mapping_file путь к файлу, который будет записан
и будет содержать соответствие оригинальных имен модели и анонимизированных
\return Объект с результатом оптимизации

\~english
Starts model optimization on a remote server in synchronous mode.
You must first set the environment variable
X_API_KEY.
\param[in] name_mapping_file path to the file that will be written
and will contain the correspondence between the original names of the model and the anonymized
\return Object with the optimization result

\~chinese-traditional
Starts model optimization on a remote server in synchronous mode.
You must first set the environment variable
X_API_KEY.
\param[in] name_mapping_file path to the file that will be written
and will contain the correspondence between the original names of the model and the anonymized
\return Object with the optimization result
*/
    SolveResult SolveRemote(const char* name_mapping_file)
    {
        auto res = _model->SolveRemote(name_mapping_file);
        CHECKERR(_model);
        return res;
    }

/**
\~russian
Стартует оптимизацию модели на удаленном сервере в асинхронном
режиме без ожидания результата.
Предварительно необходимо установить переменную окружения
X_API_KEY.
\param[in] name_mapping_file путь к файлу, который будет записан
и будет содержать соответствие оригинальных имен модели и анонимизированных

\~english
Starts model optimization on a remote server in asynchronous mode
without waiting for the result.
You must first set the environment variable
X_API_KEY.
\param[in] name_mapping_file path to the file that will be written
and will contain the correspondence between the original names of the model and the anonymized ones

\~chinese-traditional
Starts model optimization on a remote server in asynchronous mode
without waiting for the result.
You must first set the environment variable
X_API_KEY.
\param[in] name_mapping_file path to the file that will be written
and will contain the correspondence between the original names of the model and the anonymized ones
*/
    void SolveRemoteAsync(const char* name_mapping_file)
    {
        _model->SolveRemoteAsync(name_mapping_file);
        CHECKERR(_model);
    }

#ifdef ARHIPLEX_ENTERPRISE
/**
\~russian
Стартует вычисление несократимого недостижимого множества
\return Объект с результатом вычисления несократимого недостижимого множества

\~english
Starts the computing of the irreducible infeasible set
\return An object with the result of the computing of the irreducible infeasible set

\~chinese-traditional
Starts the computing of the irreducible infeasible set
\return An object with the result of the computing of the irreducible infeasible set
*/
    ComputeIisResult ComputeIis() {
        const auto compute_iis_result = _model->ComputeIis();
        CHECKERR(_model);
        return compute_iis_result;
    }
#endif

/**
\~russian
Получает лог удалённого расчета
\param[in] szCalcUID идентификатор удалённого расчета
\return лог удалённого расчета

\~english
Gets the remote calculation log
\param[in] szCalcUID remote calculation identifier
\return remote calculation log

\~chinese-traditional
Gets the remote calculation log
\param[in] szCalcUID remote calculation identifier
\return remote calculation log
*/
    const char* GetRemoteSolveLog(const char* szCalcUID) const
    {
        auto res = _model->GetRemoteSolveLog(szCalcUID);
        CHECKERR(_model);
        return res;
    }

/**
\~russian
Удаляет переменную из модели
\param[in] var переменная

\~english
Removes a variable from the model
\param[in] var variable

\~chinese-traditional
Removes a variable from the model
\param[in] var variable
*/
    void Remove(Variable& var)
    {
        _model->Remove(var.Get());
        CHECKERR(_model);
    }

/**
\~russian
Удаляет ограничение из модели
\param[in] constr ограничение

\~english
Removes a constraint from the model
\param[in] constr constraint

\~chinese-traditional
Removes a constraint from the model
\param[in] constr constraint
*/
    void Remove(Constraint& constr)
    {
        _model->Remove(constr.Get());
        CHECKERR(_model);
    }

/**
\~russian
Очищает модель от всех данных. Все старые переменные и ограничения становятся невалидными

\~english
Clears the model of all data. All old variables and constraints become invalid

\~chinese-traditional
Clears the model of all data. All old variables and constraints become invalid
*/ 
    void Clear()
    {
        _model->Clear();
        CHECKERR(_model);
    }


/**
\~russian
Задать файл для записи лога решения
\param[in] szLogFile файл лога

\~english
Set file for writing solution log
\param[in] szLogFile log file

\~chinese-traditional
Set file for writing solution log
\param[in] szLogFile log file
*/ 
    void SetLogFile(const char *szLogFile)
    {
        _model->SetLogFile(szLogFile);
        CHECKERR(_model);
    }

/**
\~russian
Задать интерфейс для обратного вызова при записи лога
\param[in] pcb интерфейс для обратного вызова

\~english
Set interface for callback when writing log
\param[in] pcb interface for callback

\~chinese-traditional
Set interface for callback when writing log
\param[in] pcb interface for callback
*/ 
    void SetLogCallback(ILogCallback *pcb)
    {
        _model->SetLogCallback(pcb);
    }

/**
\~russian
Добавить возможное значение переменной в решении в качестве "подсказки". Стартовые значения очищаются после начала процесса решения.
\param[in] var_name имя переменной
\param[in] var_value значение переменной

\~english
Add a possible value of a variable to the solution as a "hint". Starting values are cleared after the solution process begins.
\param[in] var_name variable name
\param[in] var_value variable value

\~chinese-traditional
Add a possible value of a variable to the solution as a "hint". Starting values are cleared after the solution process begins.
\param[in] var_name variable name
\param[in] var_value variable value
*/ 
    void AddMipStartValue(const char* var_name, double var_value)
    {
        _model->AddMipStartValue(var_name, var_value);
        CHECKERR(_model);
    }

/**
\~russian
Добавить возможное значение переменной в решении в качестве "подсказки". Стартовые значения очищаются после начала процесса решения.
\param[in] var переменная
\param[in] var_value значение переменной

\~english
Add a possible value of a variable to the solution as a "hint". Starting values are cleared after the solution process begins.
\param[in] var variable
\param[in] var_value variable value

\~chinese-traditional
Add a possible value of a variable to the solution as a "hint". Starting values are cleared after the solution process begins.
\param[in] var variable
\param[in] var_value variable value
*/ 
    void AddMipStartValue(const Variable& var, double var_value)
    {
        _model->AddMipStartValue(var.Get(), var_value);
        CHECKERR(_model);
    }

/**
\~russian
Добавить возможные значения переменных в решении в качестве "подсказки". Стартовые значения очищаются после начала процесса решения.
\param[in] sol_file путь к файлу с решением

\~english
Add possible values of variables in the solution as a "hint". The initial values are cleared after the solution process starts.
\param[in] sol_file path to the file with the solution

\~chinese-traditional
Add possible values of variables in the solution as a "hint". The initial values are cleared after the solution process starts.
\param[in] sol_file path to the file with the solution
*/
    void AddMipStartValues(const char* sol_file)
    {
        _model->AddMipStartValues(sol_file);
        CHECKERR(_model);
    }

/**
\~russian
Удалить все стартовые значения для поиска решения

\~english
Remove all starting values to find a solution

\~chinese-traditional
Remove all starting values to find a solution
*/ 
    void ClearMipStartValues()
    {
        _model->ClearMipStartValues();
        CHECKERR(_model);
    }

/**
\~russian
Получает имя модели
\return имя модели

\~english
Gets the model name
\return model name

\~chinese-traditional
Gets the model name
\return model name
*/
    const char* GetName() const
    {
        const char* name = _model->GetName();
        CHECKERR(_model);
        return name;
    }

/**
\~russian
Задаёт имя модели
\param[in] szName имя модели

\~english
Sets the model name
\param[in] szName model name

\~chinese-traditional
Sets the model name
\param[in] szName model name
*/
    void SetName(const char* szName)
    {
        _model->SetName(szName);
        CHECKERR(_model);
    }

/**
\~russian
Получить уникальный идентификатор последнего удаленного расчета.
\return идентификатор удалённого расчета

\~english
Get the unique identifier of the last remote calculation.
\return remote calculation identifier

\~chinese-traditional
Get the unique identifier of the last remote calculation.
\return remote calculation identifier
*/
    std::string GetCalcUID() const
    {
        const char* uid {_model->GetCalcUID()};
        CHECKERR(_model);
        std::string result = uid ? uid : "";
        return result;
    }

/**
\~russian
Проверяет является ли задача целочисленной.
\return true если задача целочисленная, иначе false.

\~english
Checks if the problem is an integer.
\return true if the problem is an integer, otherwise false.

\~chinese-traditional
Checks if the problem is an integer.
\return true if the problem is an integer, otherwise false.
*/
    bool IsMip() const {
        const bool is_mip {_model->IsMip()};
        CHECKERR(_model);
        return is_mip;
    }

/**
\~russian
Проверяет является ли задача нелинейной.
\return true если задача нелинейная, иначе false.

\~english
Checks whether the problem is nonlinear.
\return true if the problem is nonlinear, otherwise false.

\~chinese-traditional
Checks whether the problem is nonlinear.
\return true if the problem is nonlinear, otherwise false.
*/
    bool IsNonlinear() const {
        const bool is_nonlinear {_model->IsNonlinear()};
        CHECKERR(_model);
        return is_nonlinear;
    }

    int GetGeneralConstraintsCount() const {
        auto ret = _model->GetGeneralConstraintsCount();
        CHECKERR(_model);
        return ret;
    }

    GeneralConstraint GetGeneralConstaint(int idx) {
        GeneralConstraint ret(_model->GetGeneralConstaint(idx));
        CHECKERR(_model);
        return ret;
    }

    GeneralConstraint GetGeneralConstaintByName(const char* name) {
        GeneralConstraint ret(_model->GetGeneralConstaintByName(name));
        CHECKERR(_model);
        return ret;
    }

    GeneralConstraint AddGeneralConstraint(const GeneralExpression& expr, const Variable& var, const char* name = nullptr) {
        GeneralConstraint ret(_model->AddGeneralConstraint(expr.Get(), var.Get(), name));
        CHECKERR(_model);
        return ret;
    }

    GeneralConstraint  AddGeneralConstraint(const QuadExpression& qexpr, const Variable& var, const char* name = nullptr) {
      GeneralExpression expr(qexpr.Get());
      GeneralConstraint ret(_model->AddGeneralConstraint(expr.Get(), var.Get(), name));
      CHECKERR(_model);
      return ret;
    }

    GeneralConstraint  AddGeneralConstraint(const LinearExpression& lexpr, const Variable& var, const char* name = nullptr) {
      GeneralExpression expr(lexpr.Get());
      GeneralConstraint ret(_model->AddGeneralConstraint(expr.Get(), var.Get(), name));
      CHECKERR(_model);
      return ret;
    }

    void AddGeneralConstraint(const GeneralConstraint& constr) {
        _model->AddGeneralConstraint(constr.Get());
        CHECKERR(_model);
    }

    void Remove(const GeneralConstraint& constr) {
        _model->Remove(constr.Get());
        CHECKERR(_model);
    }
protected:
    std::shared_ptr<arhiplex::IModel> _model;
};
}