Category talk:Wren-linear

From Rosetta Code

Linear Algebra

There have been several RC tasks where the use of linear algebra solvers has produced results much more quickly than more basic algorithms and the purpose of this module is to provide such functionality for the Wren programmer.

Rather than try to write a solver from scratch in Wren itself which would be very difficult and relatively slow, I've decided instead to write a Wren wrapper for most of the GNU Linear Programming Kit (GLPK) - only the more advanced routines have been omitted. Although not the fastest solver available, GLPK is open source, well documented and written in Ansi C which makes it a good fit for solving smaller problems using Wren.

Despite its C heritage, GLPK uses 1-based arrays but, as a convenience feature, one can pass Wren's normal zero-based lists to the C wrapper and the underlying C code will change them to 1-based before passing them to the API functions themselves.

Whilst calling API functions directly is usually faster, it is also possible to write problems using GLPK's MathProg language and then process these using methods in the Tran class. This is far easier when there are a large number of variables and/or constraints to deal with.

Source code (Wren)

/* Module "linear.wren" */

/* Glp contains various glpk constants and miscellaneous methods. */
class Glp {
    /* optimization direction flag */
    static MIN { 1 }  // minimization
    static MAX { 2 }  // maximization

    /* kind of structural variable */
    static CV { 1 }  // continuous
    static IV { 2 }  // integer
    static BV { 3 }  // binary

    /* type of auxiliary/structural variable */
    static FR { 1 }  // free (unbounded)
    static LO { 2 }  // has lower bound
    static UP { 3 }  // has upper bound
    static DB { 4 }  // has double bound
    static FX { 5 }  // fixed

     /* status of auxiliary/structural variable */
    static BS { 1 }  // basic
    static NL { 2 }  // non-basic on lower bound
    static NU { 3 }  // non-basic on upper bound
    static NF { 4 }  // non-basic free (unbounded)
    static NS { 5 }  // non-basic fixed

     /* scaling options */
    static SF_GM   { 0x01 }  // geometric mean
    static SF_EQ   { 0x10 }  // equilibration
    static SF_2N   { 0x20 }  // power of two
    static SF_SKIP { 0x40 }  // skip if well scaled
    static SF_AUTO { 0x80 }  // automatic

    /* solution indicator */
    static SOL { 1 }  // basic
    static IPT { 2 }  // interior-point
    static MIP { 3 }  // mixed integer

    /* solution status */
    static UNDEF  { 1 }  // undefined
    static FEAS   { 2 }  // feasible
    static INFEAS { 3 }  // infeasible
    static NOFEAS { 4 }  // no feasible exists
    static OPT    { 5 }  // optimal
    static UNBND  { 6 }  // unbounded

    /* simplex solver control message level */
    static MSG_OFF { 0 }  // no output
    static MSG_ERR { 1 }  // warning and error messages only
    static MSG_ON  { 2 }  // normal output
    static MSG_ALL { 3 }  // full output
    static MSG_DBG { 4 }  // debug output

    /* simplex solver control method option */
    static PRIMAL { 1 }  // use primal
    static DUALP  { 2 }  // use dual; if it fails, use primal
    static DUAL   { 3 }  // use dual

    /* simplex solver control pricing technique */
    static PT_STD { 0x11 }  // standard (Dantzig's rule)
    static PT_PSE { 0x22 }  // projected steepest edge

    /* simplex solver control ratio test technique */
    static RT_STD { 0x11 }  // standard (textbook)
    static RT_HAR { 0x22 }  // Harris' two-pass

    /* interior-point solver control ordering algorithm */
    static ORD_NONE   { 0 }  // natural (original) ordering
    static ORD_QMD    { 1 }  // quotient minimum degree (QMD)
    static ORD_AMD    { 2 }  // approx. minimum degree (AMD)
    static ORD_SYMAMD { 3 }  // approx. minimum degree (SYMAMD)

    /* integer optimizer control branching technique */
    static BR_FFV { 1 }  // first fractional variable
    static BR_LFV { 2 }  // last fractional variable
    static BR_MFV { 3 }  // most fractional variable
    static BR_DTH { 4 }  // heuristic by Driebeck and Tomlin
    static BR_PCH { 5 }  // hybrid pseudocost heuristic

    /* integer optimizer control backtracking technique */
    static BT_DFS { 1 }  // depth first search
    static BT_BFS { 2 }  // breadth first search
    static BT_BLB { 3 }  // best local bound
    static BT_BPH { 4 }  // best projection heuristic

    /* integer optimizer control preprocessing technique */
    static PP_NONE { 0 }  // disable preprocessing
    static PP_ROOT { 1 }  // preprocessing only on root level
    static PP_ALL  { 2 }  // preprocessing on all levels

    /* enable/disable flag */
    static ON  { 1 }  // enable something
    static OFF { 0 }  // disable something

    /* return codes */
    static EBADB   { 0x01 }  // invalid basis
    static ESING   { 0x02 }  // singular matrix
    static ECOND   { 0x03 }  // ill-conditioned matrix
    static EBOUND  { 0x04 }  // invalid bounds
    static EFAIL   { 0x05 }  // solver failed
    static EOBJLL  { 0x06 }  // objective lower limit reached
    static EOBJUL  { 0x07 }  // objective upper limit reached
    static EITLIM  { 0x08 }  // iteration limit exceeded
    static ETMLIM  { 0x09 }  // time limit exceeded
    static ENOPFS  { 0x0A }  // no primal feasible solution
    static ENODFS  { 0x0B }  // no dual feasible solution
    static EROOT   { 0x0C }  // root LP optimum not provided
    static ESTOP   { 0x0D }  // search terminated by application
    static EMIPGAP { 0x0E }  // relative mip gap tolerance reached
    static ENOFEAS { 0x0F }  // no primal/dual feasible solution
    static ENOCVG  { 0x10 }  // no convergence
    static EINSTAB { 0x11 }  // numerical instability
    static EDATA   { 0x12 }  // invalid data
    static ERANGE  { 0x13 }  // result out of range

    /* condition indicator */
    static KKT_PE { 1 }  // primal equalities
    static KKT_PB { 2 }  // primal bounds
    static KKT_DE { 3 }  // dual equalities
    static KKT_DB { 4 }  // dual bounds
    static KKT_CS { 5 }  // complementary slackness

    /* MPS file format */
    static MPS_DECK { 1 }  // fixed (ancient)
    static MPS_FILE { 2 }  // free (modern)

    /* Miscellaneous methods */
    foreign static checkDup(m, n, ne, ia, ja)
    foreign static initEnv()
    foreign static version
    foreign static freeEnv()
    foreign static termOut(flag)
}

/* Prob represents an optimization problem. */
foreign class Prob {
    construct create() {}

    foreign setProbName(name)
    foreign setObjName(name)
    foreign setObjDir(dir)
    foreign addRows(nrs)
    foreign addCols(ncs)
    foreign setRowName(i, name)
    foreign setColName(j, name)
    foreign setRowBnds(i, type, lb, ub)
    foreign setColBnds(j, type, lb, ub)
    foreign setObjCoef(j, coef)
    foreign setMatRow(i, len, ind, val)
    foreign setMatCol(j, len, ind, val)
    foreign loadMatrix(ne, ia, ja, ar)
    foreign sortMatrix()
    foreign delRows(nrs, num)
    foreign delCols(ncs, num)
    foreign copy(names)
    foreign erase()
    foreign delete()

    foreign probName
    foreign objName
    foreign objDir
    foreign numRows
    foreign numCols
    foreign rowName(i)
    foreign colName(j)
    foreign rowType(i)
    foreign rowLB(i)
    foreign rowUB(i)
    foreign colType(j)
    foreign colLB(j)
    foreign colUB(j)
    foreign objCoef(j)
    foreign numNZ
    foreign matRow(i, ind, val)
    foreign matCol(j, ind, val)

    foreign createIndex()
    foreign findRow(name)
    foreign findCol(name)
    foreign deleteIndex()

    foreign setRii(i, rii)
    foreign setSjj(j, sjj)
    foreign rii(i)
    foreign sjj(j)
    foreign scale(flags)
    foreign unscale()

    foreign setRowStat(i, stat)
    foreign setColStat(j, stat)
    foreign stdBasis()
    foreign advBasis()
    foreign cpxBasis()

    foreign simplex(parm)
    foreign exact(parm)
    foreign status
    foreign primStat
    foreign dualStat
    foreign objVal
    foreign rowStat(i)
    foreign rowPrim(i)
    foreign rowDual(i)
    foreign colStat(j)
    foreign colPrim(j)
    foreign colDual(j)
    foreign unbndRay

    foreign interior(parm)
    foreign iptStatus
    foreign iptObjVal
    foreign iptRowPrim(i)
    foreign iptRowDual(i)
    foreign iptColPrim(j)
    foreign iptColDual(j)

    foreign setColKind(j, kind)
    foreign colKind(j)
    foreign numInt
    foreign numBin
    foreign intOpt(parm)
    foreign mipStatus
    foreign mipObjVal
    foreign mipRowVal(i)
    foreign mipColVal(j)

    foreign checkKKT(sol, cond, aeMax, aeInd, reMax, reInd)

    foreign readMPS(format, fname)
    foreign writeMPS(format, fname)
    foreign readLP(fname)
    foreign writeLP(fname)
    foreign readProb(fname)
    foreign writeProb(fname)

    foreign printSol(fname)
    foreign readSol(fname)
    foreign writeSol(fname)
    foreign printIpt(fname)
    foreign readIpt(fname)
    foreign writeIpt(fname)
    foreign printMip(fname)
    foreign readMip(fname)
    foreign writeMip(fname)

    foreign printRanges(len, list, fname)
}

/* Smcp represents simplex solver control parameters. */
foreign class Smcp {
    construct init() {} 

    foreign meth
    foreign msgLev
    foreign pricing
    foreign rTest

    foreign meth=(m)
    foreign msgLev=(l)
    foreign pricing=(p)
    foreign rTest=(r)
}

/* Iptcp represents interior-point solver control parameters. */
foreign class Iptcp {
    construct init() {}

    foreign msgLev
    foreign ordAlg

    foreign msgLev=(l)
    foreign ordAlg=(o)
}

/* Iocp represents MIP solver control parameters. */
foreign class Iocp {
    construct init() {}

    foreign msgLev
    foreign brTech
    foreign btTech
    foreign ppTech
    foreign presolve

    foreign msgLev=(l)
    foreign brTech=(b)
    foreign btTech=(b)
    foreign ppTech=(p)
    foreign presolve=(b)
}

/* Tran contains routines for processing MathProg models. */
foreign class Tran {
    construct mplAllocWksp() {}

    foreign mplInitRand(seed)
    foreign mplReadModel(fname, skip)
    foreign mplReadData(fname)
    foreign mplGenerate(fname)
    foreign mplBuildProb(p)
    foreign mplPostSolve(p, sol)
    foreign mplFreeWksp()
}
/* File contains routines for writing text to a
   temporary file and removing it when no longer needed. */
class File {
    foreign static write(fname, text)
    foreign static remove(fname)
}

Source code (C)

/* gcc -O3 wren-linear.c -o wren-linear -lglpk -lwren -lm  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glpk.h>
#include "wren.h"

/* Glp functions */
void Glp_checkDup(WrenVM* vm) {
    int m = (int)wrenGetSlotDouble(vm, 1);
    int n = (int)wrenGetSlotDouble(vm, 2);
    int ne = (int)wrenGetSlotDouble(vm, 3);
    int ia[ne+1];
    int ja[ne+1];
    for (int ix = 0; ix < ne; ++ix) {
        wrenGetListElement(vm, 4, ix, 1);
        ia[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        wrenGetListElement(vm, 5, ix, 1);
        ja[ix+1] = (int)wrenGetSlotDouble(vm, 1);
    }
    int res = glp_check_dup(m, n, ne, ia, ja);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Glp_initEnv(WrenVM* vm) {
    int res = glp_init_env();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Glp_version(WrenVM* vm) {
    const char *res = glp_version();
    wrenSetSlotString(vm, 0, res);
}

void Glp_freeEnv(WrenVM* vm) {
    int res = glp_free_env();
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Glp_termOut(WrenVM* vm) {
    int flag = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_term_out(flag);
    wrenSetSlotDouble(vm, 0, (double)res);
}

/* Prob functions */
void Prob_allocate(WrenVM* vm) {
    glp_prob **ppprob = (glp_prob**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_prob*));
    *ppprob = glp_create_prob();
}

void Prob_setProbName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *name = wrenGetSlotString(vm, 1);
    glp_set_prob_name(pprob, name);
}

void Prob_setObjName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *name = wrenGetSlotString(vm, 1);
    glp_set_obj_name(pprob, name);
}

void Prob_setObjDir(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int dir = (int)wrenGetSlotDouble(vm, 1);
    glp_set_obj_dir(pprob, dir);
}

void Prob_addRows(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int nrs = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_add_rows(pprob, nrs);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_addCols(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int ncs = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_add_cols(pprob, ncs);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_setRowName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    const char *name = wrenGetSlotString(vm, 2);
    glp_set_row_name(pprob, i, name);
}

void Prob_setColName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    const char *name = wrenGetSlotString(vm, 2);
    glp_set_col_name(pprob, j, name);
}

void Prob_setRowBnds(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    int type = (int)wrenGetSlotDouble(vm, 2);
    double lb = wrenGetSlotDouble(vm, 3);
    double ub = wrenGetSlotDouble(vm, 4);
    glp_set_row_bnds(pprob, i, type, lb, ub);
}

void Prob_setColBnds(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int type = (int)wrenGetSlotDouble(vm, 2);
    double lb = wrenGetSlotDouble(vm, 3);
    double ub = wrenGetSlotDouble(vm, 4);
    glp_set_col_bnds(pprob, j, type, lb, ub);
}

void Prob_setObjCoef(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double coef = wrenGetSlotDouble(vm, 2);
    glp_set_obj_coef(pprob, j, coef);
}

void Prob_setMatRow(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    int len = (int)wrenGetSlotDouble(vm, 2);
    if (!len) {
        glp_set_mat_row(pprob, i, 0, NULL, NULL);
        return;
    }
    int ind[len+1];
    double val[len+1];
    for (int ix = 0; ix < len; ++ix) {
        wrenGetListElement(vm, 3, ix, 1);
        ind[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        wrenGetListElement(vm, 4, ix, 2);
        val[ix+1] = wrenGetSlotDouble(vm, 2);
    }
    glp_set_mat_row(pprob, i, len, ind, val);
}

void Prob_setMatCol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int len = (int)wrenGetSlotDouble(vm, 2);
    if (!len) {
        glp_set_mat_col(pprob, j, 0, NULL, NULL);
        return;
    }
    int ind[len+1];
    double val[len+1];
    for (int ix = 0; ix < len; ++ix) {
        wrenGetListElement(vm, 3, ix, 1);
        ind[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        wrenGetListElement(vm, 4, ix, 2);
        val[ix+1] = wrenGetSlotDouble(vm, 2);
    }
    glp_set_mat_col(pprob, j, len, ind, val);
}

void Prob_loadMatrix(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int ne = (int)wrenGetSlotDouble(vm, 1);
    if (!ne) {
        glp_load_matrix(pprob, 0, NULL, NULL, NULL);
        return;
    }
    int ia[ne+1];
    int ja[ne+1];
    double ar[ne+1];
    for (int ix = 0; ix < ne; ++ix) {
        wrenGetListElement(vm, 2, ix, 1);
        ia[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        wrenGetListElement(vm, 3, ix, 1);
        ja[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        wrenGetListElement(vm, 4, ix, 1);
        ar[ix+1] = wrenGetSlotDouble(vm, 1);
    }
    glp_load_matrix(pprob, ne, ia, ja, ar);
}

void Prob_sortMatrix(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_sort_matrix(pprob);
}

void Prob_delRows(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int nrs = (int)wrenGetSlotDouble(vm, 1);
    int num[nrs+1];
    for (int ix = 0; ix < nrs; ++ix) {
        wrenGetListElement(vm, 2, ix, 1);
        num[ix+1] = (int)wrenGetSlotDouble(vm, 1);
    }
    glp_del_rows(pprob, nrs, num);
}

void Prob_delCols(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int ncs = (int)wrenGetSlotDouble(vm, 1);
    int num[ncs+1];
    for (int ix = 0; ix < ncs; ++ix) {
        wrenGetListElement(vm, 2, ix, 1);
        num[ix+1] = (int)wrenGetSlotDouble(vm, 1);
    }
    glp_del_cols(pprob, ncs, num);
}

void Prob_copy(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int names = (int)wrenGetSlotDouble(vm, 1);
    glp_prob **ppdest = (glp_prob**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_prob*));
    glp_copy_prob(*ppdest, pprob, names);
}

void Prob_erase(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_erase_prob(pprob);
}

void Prob_delete(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_delete_prob(pprob);
}

void Prob_probName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *res = glp_get_prob_name(pprob);
    wrenSetSlotString(vm, 0, res);
}

void Prob_objName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *res = glp_get_obj_name(pprob);
    wrenSetSlotString(vm, 0, res);
}

void Prob_objDir(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_obj_dir(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_numRows(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_num_rows(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_numCols(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_num_cols(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_rowName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    const char *res = glp_get_row_name(pprob, i);
    wrenSetSlotString(vm, 0, res);
}

void Prob_colName(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    const char *res = glp_get_col_name(pprob, j);
    wrenSetSlotString(vm, 0, res);
}

void Prob_rowType(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_get_row_type(pprob, i);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_rowLB(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_row_lb(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_rowUB(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_row_ub(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_colType(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_get_col_type(pprob, j);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_colLB(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_col_lb(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_colUB(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_col_ub(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_objCoef(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_obj_coef(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_numNZ(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_num_nz(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_matRow(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);    
    int i = (int)wrenGetSlotDouble(vm, 1);
    int len = wrenGetListCount(vm, 2);
    int ind[len+1];
    double val[len+1];
    int rlen = glp_get_mat_row(pprob, i, ind, val);
    for (int ix = 0; ix < rlen; ++ix) {
        wrenSetSlotDouble(vm, 1, (double)ind[ix+1]);
        wrenSetListElement(vm, 2, ix, 1);
        wrenSetSlotDouble(vm, 1, val[ix+1]);
        wrenSetListElement(vm, 3, ix, 1);
    }
}

void Prob_matCol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int len = wrenGetListCount(vm, 2);
    int ind[len+1];
    double val[len+1];
    int rlen = glp_get_mat_col(pprob, j, ind, val);
    for (int ix = 0; ix < rlen; ++ix) {
        wrenSetSlotDouble(vm, 1, (double)ind[ix+1]);
        wrenSetListElement(vm, 2, ix, 1);
        wrenSetSlotDouble(vm, 1, val[ix+1]);
        wrenSetListElement(vm, 3, ix, 1);
    }
}

void Prob_createIndex(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_create_index(pprob);
}

void Prob_findRow(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *name = wrenGetSlotString(vm, 1);
    int res = glp_find_row(pprob, name);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_findCol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *name = wrenGetSlotString(vm, 1);
    int res = glp_find_col(pprob, name);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_deleteIndex(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_delete_index(pprob);
}

void Prob_setRii(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double rii = wrenGetSlotDouble(vm, 2);
    glp_set_rii(pprob, i, rii);
}

void Prob_setSjj(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double sjj = wrenGetSlotDouble(vm, 2);
    glp_set_sjj(pprob, j, sjj);
}

void Prob_rii(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_rii(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_sjj(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_sjj(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_scale(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int flags = (int)wrenGetSlotDouble(vm, 1);
    glp_scale_prob(pprob, flags);
}

void Prob_unscale(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_unscale_prob(pprob);
}

void Prob_setRowStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    int stat = (int)wrenGetSlotDouble(vm, 2);
    glp_set_row_stat(pprob, i, stat);
}

void Prob_setColStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int stat = (int)wrenGetSlotDouble(vm, 2);
    glp_set_col_stat(pprob, j, stat);
}

void Prob_stdBasis(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);    
    glp_std_basis(pprob);
}

void Prob_advBasis(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    glp_adv_basis(pprob, 0);
}

void Prob_cpxBasis(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);    
    glp_cpx_basis(pprob);
}

void Prob_simplex(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res;
    if (wrenGetSlotType(vm, 1) == WREN_TYPE_NULL) {
        res = glp_simplex(pprob, NULL);
    } else {
        const glp_smcp *parm = (const glp_smcp*)wrenGetSlotForeign(vm, 1);
        res = glp_simplex(pprob, parm);
    }
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_exact(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const glp_smcp *parm = (const glp_smcp*)wrenGetSlotForeign(vm, 1);
    int res = glp_exact(pprob, parm);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_status(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_status(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_primStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_prim_stat(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_dualStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_dual_stat(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_objVal(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    double res = glp_get_obj_val(pprob);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_rowStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_get_row_stat(pprob, i);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_rowPrim(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_row_prim(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_rowDual(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_row_dual(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_colStat(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_get_col_stat(pprob, j);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_colPrim(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_col_prim(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_colDual(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_get_col_dual(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_unbndRay(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_unbnd_ray(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_interior(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res;
    if (wrenGetSlotType(vm, 1) == WREN_TYPE_NULL) {
        res = glp_interior(pprob, NULL);
    } else {
        const glp_iptcp *parm = (const glp_iptcp*)wrenGetSlotForeign(vm, 1);
        res = glp_interior(pprob, parm);
    }
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_iptStatus(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_ipt_status(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_iptObjVal(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    double res = glp_ipt_obj_val(pprob);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_iptRowPrim(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_ipt_row_prim(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_iptRowDual(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_ipt_row_dual(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_iptColPrim(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_ipt_col_prim(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_iptColDual(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_ipt_col_dual(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_setColKind(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int kind = (int)wrenGetSlotDouble(vm, 2);
    glp_set_col_kind(pprob, j, kind);
}

void Prob_colKind(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    int res = glp_get_col_kind(pprob, j);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_numInt(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_num_int(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_numBin(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_get_num_bin(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_intOpt(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res; 
    if (wrenGetSlotType(vm, 1) == WREN_TYPE_NULL) {
        res = glp_intopt(pprob, NULL);
    } else {
        const glp_iocp *parm = (const glp_iocp*)wrenGetSlotForeign(vm, 1);
        res = glp_intopt(pprob, parm);
    }
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_mipStatus(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int res = glp_mip_status(pprob);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_mipObjVal(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    double res = glp_mip_obj_val(pprob);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_mipRowVal(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int i = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_mip_row_val(pprob, i);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_mipColVal(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int j = (int)wrenGetSlotDouble(vm, 1);
    double res = glp_mip_col_val(pprob, j);
    wrenSetSlotDouble(vm, 0, res);
}

void Prob_checkKKT(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int sol = (int)wrenGetSlotDouble(vm, 1);
    int cond = (int)wrenGetSlotDouble(vm, 2);
    int ae_ind, re_ind;
    double ae_max, re_max;
    glp_check_kkt(pprob, sol, cond, &ae_max, &ae_ind, &re_max, &re_ind);
    wrenSetSlotDouble(vm, 1, ae_max);
    wrenSetListElement(vm, 3, 0, 1);
    wrenSetSlotDouble(vm, 1, (double)ae_ind);
    wrenSetListElement(vm, 4, 0, 1);
    wrenSetSlotDouble(vm, 1, re_max);
    wrenSetListElement(vm, 5, 0, 1);
    wrenSetSlotDouble(vm, 1, (double)re_ind);
    wrenSetListElement(vm, 6, 0, 1);
}

void Prob_readMPS(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int format = (int)wrenGetSlotDouble(vm, 1);
    const char *fname = wrenGetSlotString(vm, 2);
    int res = glp_read_mps(pprob, format, NULL, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeMPS(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int format = (int)wrenGetSlotDouble(vm, 1);
    const char *fname = wrenGetSlotString(vm, 2);
    int res = glp_write_mps(pprob, format, NULL, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_readLP(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_read_lp(pprob, NULL, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeLP(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_write_lp(pprob, NULL, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_readProb(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_read_prob(pprob, 0, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeProb(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_write_prob(pprob, 0, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_printSol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_print_sol(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_readSol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_read_sol(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeSol(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_write_sol(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_printIpt(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_print_ipt(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_readIpt(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_read_ipt(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeIpt(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_write_ipt(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_printMip(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_print_mip(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_readMip(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_read_mip(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_writeMip(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_write_mip(pprob, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Prob_printRanges(WrenVM* vm) {
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 0);
    int len = (int)wrenGetSlotDouble(vm, 1);
    const char *fname = wrenGetSlotString(vm, 3);
    int res;
    if (!len) {
        glp_print_ranges(pprob, 0, NULL, 0, fname);
    } else {
        int list[len+1];
        for (int ix = 0; ix < len; ++ix) {
            wrenGetListElement(vm, 2, ix, 1);
            list[ix+1] = (int)wrenGetSlotDouble(vm, 1);
        }
        res = glp_print_ranges(pprob, len, list, 0, fname);
    }
    wrenSetSlotDouble(vm, 0, (double)res);
}

/* Smcp functions */
void Smcp_allocate(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_smcp));
    glp_init_smcp(psmcp);
}

void Smcp_meth(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)psmcp->meth);
}

void Smcp_msgLev(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)psmcp->msg_lev);
}

void Smcp_pricing(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)psmcp->pricing);
}

void Smcp_rTest(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)psmcp->r_test);
}

void Smcp_setMeth(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    int meth = (int)wrenGetSlotDouble(vm, 1);
    psmcp->meth = meth;
}

void Smcp_setMsgLev(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    int msg_lev = (int)wrenGetSlotDouble(vm, 1);
    psmcp->msg_lev = msg_lev;
}

void Smcp_setPricing(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    int pricing = (int)wrenGetSlotDouble(vm, 1);
    psmcp->pricing = pricing;
}

void Smcp_setRTest(WrenVM* vm) {
    glp_smcp *psmcp = (glp_smcp*)wrenGetSlotForeign(vm, 0);
    int r_test = (int)wrenGetSlotDouble(vm, 1);
    psmcp->r_test = r_test;
}

/* Iptcp functions */
void Iptcp_allocate(WrenVM* vm) {
    glp_iptcp *piptcp = (glp_iptcp*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_iptcp));
    glp_init_iptcp(piptcp);
}

void Iptcp_msgLev(WrenVM* vm) {
    glp_iptcp *piptcp = (glp_iptcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piptcp->msg_lev);
}

void Iptcp_ordAlg(WrenVM* vm) {
    glp_iptcp *piptcp = (glp_iptcp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piptcp->ord_alg);
}

void Iptcp_setMsgLev(WrenVM* vm) {
    glp_iptcp *piptcp = (glp_iptcp*)wrenGetSlotForeign(vm, 0);
    int msg_lev = (int)wrenGetSlotDouble(vm, 1);
    piptcp->msg_lev = msg_lev;
}

void Iptcp_setOrdAlg(WrenVM* vm) {
    glp_iptcp *piptcp = (glp_iptcp*)wrenGetSlotForeign(vm, 0);
    int ord_alg = (int)wrenGetSlotDouble(vm, 1);
    piptcp->ord_alg = ord_alg;
}

/* Iocp functions */
void Iocp_allocate(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_iocp));
    glp_init_iocp(piocp);
}

void Iocp_msgLev(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piocp->msg_lev);
}

void Iocp_brTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piocp->br_tech);
}

void Iocp_btTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piocp->bt_tech);
}

void Iocp_ppTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)piocp->pp_tech);
}

void Iocp_presolve(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotBool(vm, 0, piocp->presolve);
}

void Iocp_setMsgLev(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    int msg_lev = (int)wrenGetSlotDouble(vm, 1);
    piocp->msg_lev = msg_lev;
}

void Iocp_setBrTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    int br_tech = (int)wrenGetSlotDouble(vm, 1);
    piocp->br_tech = br_tech;
}

void Iocp_setBtTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    int bt_tech = (int)wrenGetSlotDouble(vm, 1);
    piocp->bt_tech = bt_tech;
}

void Iocp_setPpTech(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    int pp_tech = (int)wrenGetSlotDouble(vm, 1);
    piocp->pp_tech = pp_tech;
}

void Iocp_setPresolve(WrenVM* vm) {
    glp_iocp *piocp = (glp_iocp*)wrenGetSlotForeign(vm, 0);
    bool presolve = wrenGetSlotBool(vm, 1);
    piocp->presolve = presolve;
}

/* Tran functions */
void Tran_allocate(WrenVM* vm) {
    glp_tran **pptran = (glp_tran**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(glp_tran*));
    *pptran = glp_mpl_alloc_wksp();
}

void Tran_mplInitRand(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    int seed = (int)wrenGetSlotDouble(vm, 1);
    glp_mpl_init_rand(ptran, seed);
}

void Tran_mplReadModel(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int skip = (int)wrenGetSlotDouble(vm, 2);
    int res = glp_mpl_read_model(ptran, fname, skip);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Tran_mplReadData(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    const char *fname = wrenGetSlotString(vm, 1);
    int res = glp_mpl_read_data(ptran, fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Tran_mplGenerate(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    int res;
    if (wrenGetSlotType(vm, 1) == WREN_TYPE_NULL) {
        res = glp_mpl_generate(ptran, NULL);
    } else {
        const char *fname = wrenGetSlotString(vm, 1);
        res = glp_mpl_generate(ptran, fname);
    }
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Tran_mplBuildProb(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 1);
    glp_mpl_build_prob(ptran, pprob);
}

void Tran_mplPostSolve(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    glp_prob *pprob = *(glp_prob**)wrenGetSlotForeign(vm, 1);
    int sol = (int)wrenGetSlotDouble(vm, 2);
    int res = glp_mpl_postsolve(ptran, pprob, sol);
    wrenSetSlotDouble(vm, 0, (double)res);
}

void Tran_mplFreeWksp(WrenVM* vm) {
    glp_tran *ptran = *(glp_tran**)wrenGetSlotForeign(vm, 0);
    glp_mpl_free_wksp(ptran);
}

/* File functions */

void File_write(WrenVM* vm) {
    const char *fname = wrenGetSlotString(vm, 1);
    const char *text = wrenGetSlotString(vm, 2);
    FILE *fp = fopen(fname, "w+");
    fputs(text, fp);
    fclose(fp);
}

void File_remove(WrenVM* vm) {
    const char *fname = wrenGetSlotString(vm, 1);
    int res = remove(fname);
    wrenSetSlotDouble(vm, 0, (double)res);
}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {
    WrenForeignClassMethods methods;
    methods.allocate = NULL;
    methods.finalize = NULL;
    if (strcmp(module, "./linear") == 0) {
        if (strcmp(className, "Prob") == 0) {
            methods.allocate = Prob_allocate;
        } else if (strcmp(className, "Smcp") == 0) {
            methods.allocate = Smcp_allocate;
        } else if (strcmp(className, "Iptcp") == 0) {
            methods.allocate = Iptcp_allocate;
        } else if (strcmp(className, "Iocp") == 0) {
            methods.allocate = Iocp_allocate;
        } else if (strcmp(className, "Tran") == 0) {
            methods.allocate = Tran_allocate;
        } 
    }
    return methods;
}

WrenForeignMethodFn bindForeignMethod(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {
    if (strcmp(module, "./linear") == 0) {
        if (strcmp(className, "Glp") == 0) {
            if(isStatic && strcmp(signature, "checkDup(_,_,_,_,_)") == 0) return Glp_checkDup;
            if(isStatic && strcmp(signature, "initEnv()") == 0)           return Glp_initEnv;
            if(isStatic && strcmp(signature, "version") == 0)             return Glp_version;
            if(isStatic && strcmp(signature, "freeEnv()") == 0)           return Glp_freeEnv;
            if(isStatic && strcmp(signature, "termOut(_)") == 0)          return Glp_termOut;
        } else if (strcmp(className, "Prob") == 0) {
            if(!isStatic && strcmp(signature, "setProbName(_)") == 0)      return Prob_setProbName;
            if(!isStatic && strcmp(signature, "setObjName(_)") == 0)       return Prob_setObjName;
            if(!isStatic && strcmp(signature, "setObjDir(_)") == 0)        return Prob_setObjDir;
            if(!isStatic && strcmp(signature, "addCols(_)") == 0)          return Prob_addCols;
            if(!isStatic && strcmp(signature, "addRows(_)") == 0)          return Prob_addRows;
            if(!isStatic && strcmp(signature, "setRowName(_,_)") == 0)     return Prob_setRowName;
            if(!isStatic && strcmp(signature, "setColName(_,_)") == 0)     return Prob_setColName;
            if(!isStatic && strcmp(signature, "setRowBnds(_,_,_,_)") == 0) return Prob_setRowBnds;
            if(!isStatic && strcmp(signature, "setColBnds(_,_,_,_)") == 0) return Prob_setColBnds;
            if(!isStatic && strcmp(signature, "setObjCoef(_,_)") == 0)     return Prob_setObjCoef;
            if(!isStatic && strcmp(signature, "setMatRow(_,_,_,_)") == 0)  return Prob_setMatRow;
            if(!isStatic && strcmp(signature, "setMatCol(_,_,_,_)") == 0)  return Prob_setMatCol;
            if(!isStatic && strcmp(signature, "loadMatrix(_,_,_,_)") == 0) return Prob_loadMatrix;
            if(!isStatic && strcmp(signature, "sortMatrix()") == 0)        return Prob_sortMatrix;
            if(!isStatic && strcmp(signature, "delRows(_,_)") == 0)        return Prob_delRows;
            if(!isStatic && strcmp(signature, "delCols(_,_)") == 0)        return Prob_delCols;
            if(!isStatic && strcmp(signature, "copy(_)") == 0)             return Prob_copy;
            if(!isStatic && strcmp(signature, "erase()") == 0)             return Prob_erase;
            if(!isStatic && strcmp(signature, "delete()") == 0)            return Prob_delete;

            if(!isStatic && strcmp(signature, "probName") == 0)      return Prob_probName;
            if(!isStatic && strcmp(signature, "objName") == 0)       return Prob_objName;
            if(!isStatic && strcmp(signature, "objDir") == 0)        return Prob_objDir;
            if(!isStatic && strcmp(signature, "numRows") == 0)       return Prob_numRows;
            if(!isStatic && strcmp(signature, "numCols") == 0)       return Prob_numCols;
            if(!isStatic && strcmp(signature, "rowName(_)") == 0)    return Prob_rowName;
            if(!isStatic && strcmp(signature, "colName(_)") == 0)    return Prob_colName;
            if(!isStatic && strcmp(signature, "rowType(_)") == 0)    return Prob_rowType;
            if(!isStatic && strcmp(signature, "rowLB(_)") == 0)      return Prob_rowLB;
            if(!isStatic && strcmp(signature, "rowUB(_)") == 0)      return Prob_rowUB;
            if(!isStatic && strcmp(signature, "colType(_)") == 0)    return Prob_colType;
            if(!isStatic && strcmp(signature, "colLB(_)") == 0)      return Prob_colLB;
            if(!isStatic && strcmp(signature, "colUB(_)") == 0)      return Prob_colUB;
            if(!isStatic && strcmp(signature, "objCoef(_)") == 0)    return Prob_objCoef;
            if(!isStatic && strcmp(signature, "numNZ") == 0)         return Prob_numNZ;
            if(!isStatic && strcmp(signature, "matRow(_,_,_)") == 0) return Prob_matRow;
            if(!isStatic && strcmp(signature, "matCol(_,_,_)") == 0) return Prob_matCol;

            if(!isStatic && strcmp(signature, "createIndex()") == 0) return Prob_createIndex;
            if(!isStatic && strcmp(signature, "findRow(_)") == 0)    return Prob_findRow;
            if(!isStatic && strcmp(signature, "findCol(_)") == 0)    return Prob_findCol;
            if(!isStatic && strcmp(signature, "deleteIndex()") == 0) return Prob_deleteIndex;

            if(!isStatic && strcmp(signature, "setRii(_,_)") == 0)   return Prob_setRii;
            if(!isStatic && strcmp(signature, "setSjj(_,_)") == 0)   return Prob_setSjj;
            if(!isStatic && strcmp(signature, "rii(_)") == 0)        return Prob_rii;
            if(!isStatic && strcmp(signature, "sjj(_)") == 0)        return Prob_sjj;
            if(!isStatic && strcmp(signature, "scale(_)") == 0)      return Prob_scale;
            if(!isStatic && strcmp(signature, "unscale()") == 0)     return Prob_unscale;

            if(!isStatic && strcmp(signature, "setRowStat(_,_)") == 0) return Prob_setRowStat;
            if(!isStatic && strcmp(signature, "setColStat(_,_)") == 0) return Prob_setColStat;
            if(!isStatic && strcmp(signature, "stdBasis()") == 0)      return Prob_stdBasis;
            if(!isStatic && strcmp(signature, "advBasis()") == 0)      return Prob_advBasis;
            if(!isStatic && strcmp(signature, "cpxBasis()") == 0)      return Prob_cpxBasis;

            if(!isStatic && strcmp(signature, "simplex(_)") == 0) return Prob_simplex;
            if(!isStatic && strcmp(signature, "exact(_)") == 0)   return Prob_exact;
            if(!isStatic && strcmp(signature, "status") == 0)     return Prob_status;           
            if(!isStatic && strcmp(signature, "primStat") == 0)   return Prob_primStat;
            if(!isStatic && strcmp(signature, "dualStat") == 0)   return Prob_dualStat;
            if(!isStatic && strcmp(signature, "objVal") == 0)     return Prob_objVal;
            if(!isStatic && strcmp(signature, "rowStat(_)") == 0) return Prob_rowStat;
            if(!isStatic && strcmp(signature, "rowPrim(_)") == 0) return Prob_rowPrim;
            if(!isStatic && strcmp(signature, "rowDual(_)") == 0) return Prob_rowDual;
            if(!isStatic && strcmp(signature, "colStat(_)") == 0) return Prob_colStat;
            if(!isStatic && strcmp(signature, "colPrim(_)") == 0) return Prob_colPrim;
            if(!isStatic && strcmp(signature, "colDual(_)") == 0) return Prob_colDual;
            if(!isStatic && strcmp(signature, "unbndRay") == 0)   return Prob_unbndRay;

            if(!isStatic && strcmp(signature, "interior(_)") == 0)   return Prob_interior;
            if(!isStatic && strcmp(signature, "iptStatus") == 0)     return Prob_iptStatus;
            if(!isStatic && strcmp(signature, "iptObjVal") == 0)     return Prob_iptObjVal;          
            if(!isStatic && strcmp(signature, "iptRowPrim(_)") == 0) return Prob_iptRowPrim;
            if(!isStatic && strcmp(signature, "iptRowDual(_)") == 0) return Prob_iptRowDual;
            if(!isStatic && strcmp(signature, "iptColPrim(_)") == 0) return Prob_iptColPrim;
            if(!isStatic && strcmp(signature, "iptColDual(_)") == 0) return Prob_iptColDual;

            if(!isStatic && strcmp(signature, "setColKind(_,_)") == 0) return Prob_setColKind;
            if(!isStatic && strcmp(signature, "colKind(_)") == 0)      return Prob_colKind;
            if(!isStatic && strcmp(signature, "numInt") == 0)          return Prob_numInt;
            if(!isStatic && strcmp(signature, "numBin") == 0)          return Prob_numBin;
            if(!isStatic && strcmp(signature, "intOpt(_)") == 0)       return Prob_intOpt;
            if(!isStatic && strcmp(signature, "mipStatus") == 0)       return Prob_mipStatus;
            if(!isStatic && strcmp(signature, "mipObjVal") == 0)       return Prob_mipObjVal;
            if(!isStatic && strcmp(signature, "mipRowVal(_)") == 0)    return Prob_mipRowVal;
            if(!isStatic && strcmp(signature, "mipColVal(_)") == 0)    return Prob_mipColVal;

            if(!isStatic && strcmp(signature, "checkKKT(_,_,_,_,_,_)") == 0)  return Prob_checkKKT;

            if(!isStatic && strcmp(signature, "readMPS(_,_)") == 0)  return Prob_readMPS;
            if(!isStatic && strcmp(signature, "writeMPS(_,_)") == 0) return Prob_writeMPS;
            if(!isStatic && strcmp(signature, "readLP(_)") == 0)     return Prob_readLP;
            if(!isStatic && strcmp(signature, "writeLP(_)") == 0)    return Prob_writeLP;
            if(!isStatic && strcmp(signature, "readProb(_)") == 0)   return Prob_readProb;
            if(!isStatic && strcmp(signature, "writeProb(_)") == 0)  return Prob_writeProb;

            if(!isStatic && strcmp(signature, "printSol(_)") == 0) return Prob_printSol;
            if(!isStatic && strcmp(signature, "readSol(_)") == 0)  return Prob_readSol;
            if(!isStatic && strcmp(signature, "writeSol(_)") == 0) return Prob_writeSol;
            if(!isStatic && strcmp(signature, "printIpt(_)") == 0) return Prob_printIpt;
            if(!isStatic && strcmp(signature, "readIpt(_)") == 0)  return Prob_readIpt;
            if(!isStatic && strcmp(signature, "writeIpt(_)") == 0) return Prob_writeIpt;
            if(!isStatic && strcmp(signature, "printMip(_)") == 0) return Prob_printMip;
            if(!isStatic && strcmp(signature, "readMip(_)") == 0)  return Prob_readMip;
            if(!isStatic && strcmp(signature, "writeMip(_)") == 0) return Prob_writeMip;

            if(!isStatic && strcmp(signature, "printRanges(_,_,_)") == 0) return Prob_printRanges;
        } else if (strcmp(className, "Smcp") == 0) {
            if(!isStatic && strcmp(signature, "meth") == 0)        return Smcp_meth;
            if(!isStatic && strcmp(signature, "msgLev") == 0)      return Smcp_msgLev;
            if(!isStatic && strcmp(signature, "pricing") == 0)     return Smcp_pricing;
            if(!isStatic && strcmp(signature, "rTest") == 0)       return Smcp_rTest;
            if(!isStatic && strcmp(signature, "meth=(_)") == 0)    return Smcp_setMeth;
            if(!isStatic && strcmp(signature, "msgLev=(_)") == 0)  return Smcp_setMsgLev;
            if(!isStatic && strcmp(signature, "pricing=(_)") == 0) return Smcp_setPricing;
            if(!isStatic && strcmp(signature, "rTest=(_)") == 0)   return Smcp_setRTest;
        } else if (strcmp(className, "Iptcp") == 0) {
            if(!isStatic && strcmp(signature, "msgLev") == 0)      return Iptcp_msgLev;
            if(!isStatic && strcmp(signature, "ordAlg") == 0)      return Iptcp_ordAlg;
            if(!isStatic && strcmp(signature, "msgLev=(_)") == 0)  return Iptcp_setMsgLev;
            if(!isStatic && strcmp(signature, "ordAlg=(_)") == 0)  return Iptcp_setOrdAlg;
        } else if (strcmp(className, "Iocp") == 0) {
            if(!isStatic && strcmp(signature, "msgLev") == 0)       return Iocp_msgLev;
            if(!isStatic && strcmp(signature, "brTech") == 0)       return Iocp_brTech;
            if(!isStatic && strcmp(signature, "btTech") == 0)       return Iocp_btTech;
            if(!isStatic && strcmp(signature, "ppTech") == 0)       return Iocp_ppTech;
            if(!isStatic && strcmp(signature, "presolve") == 0)     return Iocp_presolve;
            if(!isStatic && strcmp(signature, "msgLev=(_)") == 0)   return Iocp_setMsgLev;
            if(!isStatic && strcmp(signature, "brTech=(_)") == 0)   return Iocp_setBrTech;
            if(!isStatic && strcmp(signature, "btTech=(_)") == 0)   return Iocp_setBtTech;
            if(!isStatic && strcmp(signature, "ppTech=(_)") == 0)   return Iocp_setPpTech;
            if(!isStatic && strcmp(signature, "presolve=(_)") == 0) return Iocp_setPresolve;
        } else if (strcmp(className, "Tran") == 0) {
            if(!isStatic && strcmp(signature, "mplInitRand(_)") == 0)    return Tran_mplInitRand;
            if(!isStatic && strcmp(signature, "mplReadModel(_,_)") == 0) return Tran_mplReadModel;
            if(!isStatic && strcmp(signature, "mplReadData(_)") == 0)    return Tran_mplReadData;
            if(!isStatic && strcmp(signature, "mplGenerate(_)") == 0)    return Tran_mplGenerate;
            if(!isStatic && strcmp(signature, "mplBuildProb(_)") == 0)   return Tran_mplBuildProb;
            if(!isStatic && strcmp(signature, "mplPostSolve(_,_)") == 0) return Tran_mplPostSolve;
            if(!isStatic && strcmp(signature, "mplFreeWksp()") == 0)     return Tran_mplFreeWksp;
        } else if (strcmp(className, "File") == 0) {
            if(isStatic && strcmp(signature, "write(_,_)") == 0) return File_write;
            if(isStatic && strcmp(signature, "remove(_)") == 0)  return File_remove;
        }
    }
    return NULL;
}

static void writeFn(WrenVM* vm, const char* text) {
    printf("%s", text);
}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
    switch (errorType) {
        case WREN_ERROR_COMPILE:
            printf("[%s line %d] [Error] %s\n", module, line, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            printf("[%s line %d] in %s\n", module, line, msg);
            break;
        case WREN_ERROR_RUNTIME:
            printf("[Runtime Error] %s\n", msg);
            break;
    }
}

char *readFile(const char *fileName) {
    FILE *f = fopen(fileName, "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    rewind(f);
    char *script = malloc(fsize + 1);
    size_t ret = fread(script, 1, fsize, f);
    if (ret != fsize) printf("Error reading %s\n", fileName);
    fclose(f);
    script[fsize] = 0;
    return script;
}

static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) {
    if( result.source) free((void*)result.source);
}

WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) {
    WrenLoadModuleResult result = {0};
    if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) {
        result.onComplete = loadModuleComplete;
        char fullName[strlen(name) + 6];
        strcpy(fullName, name);
        strcat(fullName, ".wren");
        result.source = readFile(fullName);
    }
    return result;
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Please pass the name of the Wren file to be executed.\n");
        return 1;
    }
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = &writeFn;
    config.errorFn = &errorFn;
    config.bindForeignClassFn = &bindForeignClass;
    config.bindForeignMethodFn = &bindForeignMethod;
    config.loadModuleFn = &loadModule;
    WrenVM* vm = wrenNewVM(&config);
    const char* module = "main";
    const char* fileName = argv[1];
    char *script = readFile(fileName);
    WrenInterpretResult result = wrenInterpret(vm, module, script);
    switch (result) {
        case WREN_RESULT_COMPILE_ERROR:
            printf("Compile Error!\n");
            break;
        case WREN_RESULT_RUNTIME_ERROR:
            printf("Runtime Error!\n");
            break;
        case WREN_RESULT_SUCCESS:
            break;
    }
    wrenFreeVM(vm);
    free(script);
    return 0;
}