2009-05-06 12:10:27 +02:00
/*
2020-01-10 17:55:57 +01:00
* Copyright © 2008 - 2020 Dynare Team
2009-05-19 10:57:07 +02:00
*
* This file is part of Dynare .
*
* Dynare is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Dynare is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with Dynare . If not , see < http : //www.gnu.org/licenses/>.
*/
2009-05-06 12:10:27 +02:00
2019-04-02 16:33:15 +02:00
/* Defines the entry point for the k-order perturbation application DLL.
2010-08-30 17:11:58 +02:00
2019-04-02 16:33:15 +02:00
See matlab / mex / k_order_perturbation . m for a description of inputs and
outputs .
2010-08-30 17:11:58 +02:00
*/
2009-05-06 12:10:27 +02:00
2010-12-17 18:34:23 +01:00
# include "dynamic_m.hh"
2009-12-17 12:00:50 +01:00
# include "dynamic_dll.hh"
2019-04-02 16:33:15 +02:00
# include "k_ord_dynare.hh"
# include "approximation.hh"
# include "exception.hh"
# include "dynare_exception.hh"
# include "kord_exception.hh"
# include "tl_exception.hh"
# include "SylvException.hh"
2009-05-06 12:10:27 +02:00
2019-03-07 18:17:43 +01:00
# include <algorithm>
2010-02-08 16:58:24 +01:00
# include <cassert>
2009-05-06 12:10:27 +02:00
2019-03-07 18:17:43 +01:00
# include "dynmex.h"
2010-09-20 16:57:02 +02:00
2019-04-08 16:59:48 +02:00
/* Vector for storing field names like “g_0”, “g_1”, …
A static structure is needed since MATLAB apparently does not create its own
copy of the strings ( contrary to what is said at :
https : //fr.mathworks.com/matlabcentral/answers/315937-mxcreatestructarray-and-mxcreatestructmatrix-field-name-memory-management
) */
std : : vector < std : : string > g_fieldnames ;
2019-04-08 17:53:20 +02:00
/* Convert MATLAB Dynare endo and exo names cell array to a vector<string> array of
string pointers . */
std : : vector < std : : string >
DynareMxArrayToString ( const mxArray * mxFldp )
2009-11-30 17:31:27 +01:00
{
2019-04-08 17:53:20 +02:00
assert ( mxIsCell ( mxFldp ) ) ;
std : : vector < std : : string > r ;
for ( size_t i = 0 ; i < mxGetNumberOfElements ( mxFldp ) ; i + + )
2019-07-09 17:12:23 +02:00
{
const mxArray * cell_mx = mxGetCell ( mxFldp , i ) ;
if ( ! ( cell_mx & & mxIsChar ( cell_mx ) ) )
mexErrMsgTxt ( " Cell is not a character array " ) ;
r . emplace_back ( mxArrayToString ( cell_mx ) ) ;
}
2010-03-09 18:22:33 +01:00
2019-04-08 17:53:20 +02:00
return r ;
2009-11-30 17:31:27 +01:00
}
2017-05-16 16:30:27 +02:00
void
2019-04-08 16:59:48 +02:00
copy_derivatives ( mxArray * destin , const Symmetry & sym , const FGSContainer & derivs , const char * fieldname )
2012-07-31 21:50:28 +02:00
{
2019-04-02 16:39:32 +02:00
const FGSTensor & x = derivs . get ( sym ) ;
auto x_unfolded = x . unfold ( ) ;
2019-04-16 12:40:50 +02:00
int n = x_unfolded - > nrows ( ) ;
int m = x_unfolded - > ncols ( ) ;
2012-07-31 21:50:28 +02:00
mxArray * tmp = mxCreateDoubleMatrix ( n , m , mxREAL ) ;
2019-04-02 16:39:32 +02:00
std : : copy_n ( x_unfolded - > getData ( ) . base ( ) , n * m , mxGetPr ( tmp ) ) ;
2019-04-08 16:59:48 +02:00
mxSetField ( destin , 0 , fieldname , tmp ) ;
2012-07-31 21:50:28 +02:00
}
2009-05-06 12:10:27 +02:00
extern " C " {
2009-05-27 16:28:23 +02:00
void
mexFunction ( int nlhs , mxArray * plhs [ ] ,
int nrhs , const mxArray * prhs [ ] )
2009-05-06 12:10:27 +02:00
{
2020-01-10 17:55:57 +01:00
if ( nrhs < 3 | | nlhs < 1 | | nlhs > 2 )
mexErrMsgTxt ( " Must have at least 3 input parameters and takes 1 or 2 output parameters. " ) ;
2019-07-09 17:12:23 +02:00
// Give explicit names to input arguments
const mxArray * dr_mx = prhs [ 0 ] ;
const mxArray * M_mx = prhs [ 1 ] ;
const mxArray * options_mx = prhs [ 2 ] ;
auto get_int_field = [ ] ( const mxArray * struct_mx , const std : : string & fieldname )
{
mxArray * field_mx = mxGetField ( struct_mx , 0 , fieldname . c_str ( ) ) ;
if ( ! ( field_mx & & mxIsScalar ( field_mx ) & & mxIsNumeric ( field_mx ) ) )
mexErrMsgTxt ( ( " Field ` " + fieldname + " ' should be a numeric scalar " ) . c_str ( ) ) ;
return static_cast < int > ( mxGetScalar ( field_mx ) ) ;
} ;
// Extract various fields from options_
const int kOrder = get_int_field ( options_mx , " order " ) ;
2019-04-15 17:24:05 +02:00
if ( kOrder < 1 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " options_.order must be at least 1 " ) ;
2009-12-16 18:18:38 +01:00
2019-07-09 17:12:23 +02:00
const mxArray * use_dll_mx = mxGetField ( options_mx , 0 , " use_dll " ) ;
if ( ! ( use_dll_mx & & mxIsLogicalScalar ( use_dll_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " options_.use_dll should be a logical scalar " ) ;
2019-07-09 17:12:23 +02:00
bool use_dll = static_cast < bool > ( mxGetScalar ( use_dll_mx ) ) ;
2009-05-27 16:28:23 +02:00
2019-07-09 17:12:23 +02:00
double qz_criterium = 1 + 1e-6 ;
const mxArray * qz_criterium_mx = mxGetField ( options_mx , 0 , " qz_criterium " ) ;
if ( qz_criterium_mx & & mxIsScalar ( qz_criterium_mx ) & & mxIsNumeric ( qz_criterium_mx ) )
qz_criterium = mxGetScalar ( qz_criterium_mx ) ;
2019-07-09 17:32:33 +02:00
const mxArray * threads_mx = mxGetField ( options_mx , 0 , " threads " ) ;
if ( ! threads_mx )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " Can't find field options_.threads " ) ;
2019-07-09 17:32:33 +02:00
const mxArray * num_threads_mx = mxGetField ( threads_mx , 0 , " k_order_perturbation " ) ;
if ( ! ( num_threads_mx & & mxIsScalar ( num_threads_mx ) & & mxIsNumeric ( num_threads_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " options_.threads.k_order_perturbation be a numeric scalar " ) ;
2019-07-09 17:32:33 +02:00
int num_threads = static_cast < int > ( mxGetScalar ( num_threads_mx ) ) ;
2019-07-09 17:12:23 +02:00
// Extract various fields from M_
const mxArray * fname_mx = mxGetField ( M_mx , 0 , " fname " ) ;
if ( ! ( fname_mx & & mxIsChar ( fname_mx ) & & mxGetM ( fname_mx ) = = 1 ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.fname should be a character string " ) ;
2019-07-09 17:12:23 +02:00
std : : string fname { mxArrayToString ( fname_mx ) } ;
const mxArray * params_mx = mxGetField ( M_mx , 0 , " params " ) ;
if ( ! ( params_mx & & mxIsDouble ( params_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.params should be a double precision array " ) ;
2019-07-09 17:12:23 +02:00
Vector modParams { ConstVector { params_mx } } ;
2011-02-07 11:12:32 +01:00
if ( ! modParams . isFinite ( ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.params contains NaN or Inf " ) ;
2009-05-27 16:28:23 +02:00
2019-07-09 17:12:23 +02:00
const mxArray * sigma_e_mx = mxGetField ( M_mx , 0 , " Sigma_e " ) ;
if ( ! ( sigma_e_mx & & mxIsDouble ( sigma_e_mx ) & & mxGetM ( sigma_e_mx ) = = mxGetN ( sigma_e_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.Sigma_e should be a double precision square matrix " ) ;
2019-07-09 17:12:23 +02:00
TwoDMatrix vCov { ConstTwoDMatrix { sigma_e_mx } } ;
2011-02-07 11:12:32 +01:00
if ( ! vCov . isFinite ( ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.Sigma_e contains NaN or Inf " ) ;
2019-07-09 17:12:23 +02:00
const int nStat = get_int_field ( M_mx , " nstatic " ) ;
const int nPred = get_int_field ( M_mx , " npred " ) ;
const int nBoth = get_int_field ( M_mx , " nboth " ) ;
const int nForw = get_int_field ( M_mx , " nfwrd " ) ;
const int nExog = get_int_field ( M_mx , " exo_nbr " ) ;
const int nEndo = get_int_field ( M_mx , " endo_nbr " ) ;
const int nPar = get_int_field ( M_mx , " param_nbr " ) ;
const mxArray * lead_lag_incidence_mx = mxGetField ( M_mx , 0 , " lead_lag_incidence " ) ;
if ( ! ( lead_lag_incidence_mx & & mxIsDouble ( lead_lag_incidence_mx ) & & mxGetM ( lead_lag_incidence_mx ) = = 3
& & mxGetN ( lead_lag_incidence_mx ) = = static_cast < size_t > ( nEndo ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.lead_lag_incidence should be a double precision matrix with 3 rows and M_.endo_nbr columns " ) ;
2019-07-09 17:12:23 +02:00
ConstTwoDMatrix llincidence { lead_lag_incidence_mx } ;
const mxArray * nnzderivatives_mx = mxGetField ( M_mx , 0 , " NNZDerivatives " ) ;
if ( ! ( nnzderivatives_mx & & mxIsDouble ( nnzderivatives_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.NNZDerivatives should be a double precision array " ) ;
2019-07-09 17:12:23 +02:00
ConstVector NNZD { nnzderivatives_mx } ;
if ( NNZD . length ( ) < kOrder | | NNZD [ kOrder - 1 ] = = - 1 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " The derivatives were not computed for the required order. Make sure that you used the right order option inside the `stoch_simul' command " ) ;
2009-05-27 16:28:23 +02:00
2019-07-09 17:12:23 +02:00
const mxArray * endo_names_mx = mxGetField ( M_mx , 0 , " endo_names " ) ;
if ( ! ( endo_names_mx & & mxIsCell ( endo_names_mx ) & & mxGetNumberOfElements ( endo_names_mx ) = = static_cast < size_t > ( nEndo ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.endo_names should be a cell array of M_.endo_nbr elements " ) ;
2019-07-09 17:12:23 +02:00
std : : vector < std : : string > endoNames = DynareMxArrayToString ( endo_names_mx ) ;
const mxArray * exo_names_mx = mxGetField ( M_mx , 0 , " exo_names " ) ;
if ( ! ( exo_names_mx & & mxIsCell ( exo_names_mx ) & & mxGetNumberOfElements ( exo_names_mx ) = = static_cast < size_t > ( nExog ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.exo_names should be a cell array of M_.exo_nbr elements " ) ;
2019-07-09 17:12:23 +02:00
std : : vector < std : : string > exoNames = DynareMxArrayToString ( exo_names_mx ) ;
const mxArray * dynamic_tmp_nbr_mx = mxGetField ( M_mx , 0 , " dynamic_tmp_nbr " ) ;
if ( ! ( dynamic_tmp_nbr_mx & & mxIsDouble ( dynamic_tmp_nbr_mx ) & & mxGetNumberOfElements ( dynamic_tmp_nbr_mx ) > = static_cast < size_t > ( kOrder + 1 ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " M_.dynamic_tmp_nbr should be a double precision array with strictly more elements than the order of derivation " ) ;
2019-07-09 17:12:23 +02:00
int ntt = std : : accumulate ( mxGetPr ( dynamic_tmp_nbr_mx ) , mxGetPr ( dynamic_tmp_nbr_mx ) + kOrder + 1 , 0 ) ;
// Extract various fields from dr
2019-12-20 14:50:19 +01:00
const mxArray * ys_mx = mxGetField ( dr_mx , 0 , " ys " ) ; // and not in order of dr.order_var
2019-07-09 17:12:23 +02:00
if ( ! ( ys_mx & & mxIsDouble ( ys_mx ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " dr.ys should be a double precision array " ) ;
2019-07-09 17:12:23 +02:00
Vector ySteady { ConstVector { ys_mx } } ;
2011-02-07 11:12:32 +01:00
if ( ! ySteady . isFinite ( ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " dr.ys contains NaN or Inf " ) ;
2019-07-09 17:12:23 +02:00
const mxArray * order_var_mx = mxGetField ( dr_mx , 0 , " order_var " ) ;
if ( ! ( order_var_mx & & mxIsDouble ( order_var_mx ) & & mxGetNumberOfElements ( order_var_mx ) = = static_cast < size_t > ( nEndo ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " dr.order_var should be a double precision array of M_.endo_nbr elements " ) ;
2019-04-10 09:23:32 +02:00
std : : vector < int > dr_order ( nEndo ) ;
2019-07-09 17:12:23 +02:00
std : : transform ( mxGetPr ( order_var_mx ) , mxGetPr ( order_var_mx ) + nEndo , dr_order . begin ( ) ,
2019-04-10 09:23:32 +02:00
[ ] ( double x ) { return static_cast < int > ( x ) - 1 ; } ) ;
2009-05-27 16:28:23 +02:00
const int nSteps = 0 ; // Dynare++ solving steps, for time being default to 0 = deterministic steady state
try
{
2019-07-09 17:12:23 +02:00
Journal journal ( fname + " .jnl " ) ;
2010-12-17 18:34:23 +01:00
2019-02-06 15:50:01 +01:00
std : : unique_ptr < DynamicModelAC > dynamicModelFile ;
2019-03-07 18:17:43 +01:00
if ( use_dll )
2019-07-09 17:12:23 +02:00
dynamicModelFile = std : : make_unique < DynamicModelDLL > ( fname , ntt , kOrder ) ;
2010-12-17 18:34:23 +01:00
else
2019-07-09 17:12:23 +02:00
dynamicModelFile = std : : make_unique < DynamicModelMFile > ( fname , ntt ) ;
2009-05-27 16:28:23 +02:00
// intiate tensor library
2019-02-21 18:46:53 +01:00
TLStatic : : init ( kOrder , nStat + 2 * nPred + 3 * nBoth + 2 * nForw + nExog ) ;
2009-05-27 16:28:23 +02:00
2019-07-09 17:32:33 +02:00
// Set number of parallel threads
sthread : : detach_thread_group : : max_parallel_threads = num_threads ;
2009-05-27 16:28:23 +02:00
// make KordpDynare object
2019-04-02 16:33:15 +02:00
KordpDynare dynare ( endoNames , exoNames , nExog , nPar ,
2009-05-27 16:28:23 +02:00
ySteady , vCov , modParams , nStat , nPred , nForw , nBoth ,
2019-04-02 16:33:15 +02:00
NNZD , nSteps , kOrder , journal , std : : move ( dynamicModelFile ) ,
2019-04-10 18:56:14 +02:00
dr_order , llincidence ) ;
2009-05-27 16:28:23 +02:00
2019-04-10 18:56:14 +02:00
// If model derivatives have been passed as arguments
if ( nrhs > 3 )
{
dynare . push_back_md ( prhs [ 3 ] ) ;
if ( nrhs > 4 )
dynare . push_back_md ( prhs [ 4 ] ) ;
if ( nrhs > 5 )
dynare . push_back_md ( prhs [ 5 ] ) ;
}
2009-12-16 18:18:38 +01:00
2019-04-10 18:56:14 +02:00
// construct main K-order approximation class
2019-03-07 18:17:43 +01:00
Approximation app ( dynare , journal , nSteps , false , qz_criterium ) ;
2009-05-27 16:28:23 +02:00
// run stochastic steady
app . walkStochSteady ( ) ;
2019-04-08 16:59:48 +02:00
const FoldDecisionRule & fdr = app . getFoldDecisionRule ( ) ;
2009-05-27 16:28:23 +02:00
2019-04-08 16:59:48 +02:00
// Add possibly missing field names
for ( int i = static_cast < int > ( g_fieldnames . size ( ) ) ; i < = kOrder ; i + + )
g_fieldnames . emplace_back ( " g_ " + std : : to_string ( i ) ) ;
// Create structure for storing derivatives in Dynare++ format
const char * g_fieldnames_c [ kOrder + 1 ] ;
for ( int i = 0 ; i < = kOrder ; i + + )
g_fieldnames_c [ i ] = g_fieldnames [ i ] . c_str ( ) ;
2020-01-10 17:55:57 +01:00
plhs [ 0 ] = mxCreateStructMatrix ( 1 , 1 , kOrder + 1 , g_fieldnames_c ) ;
2009-05-27 16:28:23 +02:00
2019-04-08 16:59:48 +02:00
// Fill that structure
for ( int i = 0 ; i < = kOrder ; i + + )
2009-05-27 16:28:23 +02:00
{
2019-04-08 16:59:48 +02:00
const FFSTensor & t = fdr . get ( Symmetry { i } ) ;
2019-04-16 12:40:50 +02:00
mxArray * tmp = mxCreateDoubleMatrix ( t . nrows ( ) , t . ncols ( ) , mxREAL ) ;
2019-04-08 16:59:48 +02:00
const ConstVector & vec = t . getData ( ) ;
2010-02-08 16:58:24 +01:00
assert ( vec . skip ( ) = = 1 ) ;
2019-04-08 16:59:48 +02:00
std : : copy_n ( vec . base ( ) , vec . length ( ) , mxGetPr ( tmp ) ) ;
2020-01-10 17:55:57 +01:00
mxSetField ( plhs [ 0 ] , 0 , ( " g_ " + std : : to_string ( i ) ) . c_str ( ) , tmp ) ;
2009-05-27 16:28:23 +02:00
}
2019-04-08 16:59:48 +02:00
2020-01-10 17:55:57 +01:00
if ( nlhs > 1 )
2009-05-27 16:28:23 +02:00
{
2019-04-08 16:59:48 +02:00
/* Return as 3rd argument a struct containing derivatives in Dynare
format ( unfolded matrices , without Taylor coefficient ) up to 3 rd
order */
const FGSContainer & derivs = app . get_rule_ders ( ) ;
size_t nfields = ( kOrder = = 1 ? 2 : ( kOrder = = 2 ? 6 : 12 ) ) ;
const char * c_fieldnames [ ] = { " gy " , " gu " , " gyy " , " gyu " , " guu " , " gss " ,
" gyyy " , " gyyu " , " gyuu " , " guuu " , " gyss " , " guss " } ;
2020-01-10 17:55:57 +01:00
plhs [ 1 ] = mxCreateStructMatrix ( 1 , 1 , nfields , c_fieldnames ) ;
2019-04-08 16:59:48 +02:00
2020-01-10 17:55:57 +01:00
copy_derivatives ( plhs [ 1 ] , Symmetry { 1 , 0 , 0 , 0 } , derivs , " gy " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 0 , 1 , 0 , 0 } , derivs , " gu " ) ;
2019-04-08 16:59:48 +02:00
if ( kOrder > = 2 )
2009-05-27 16:28:23 +02:00
{
2020-01-10 17:55:57 +01:00
copy_derivatives ( plhs [ 1 ] , Symmetry { 2 , 0 , 0 , 0 } , derivs , " gyy " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 0 , 2 , 0 , 0 } , derivs , " guu " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 1 , 1 , 0 , 0 } , derivs , " gyu " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 0 , 0 , 0 , 2 } , derivs , " gss " ) ;
2009-05-27 16:28:23 +02:00
}
2019-04-08 16:59:48 +02:00
if ( kOrder > = 3 )
2017-05-16 16:30:27 +02:00
{
2020-01-10 17:55:57 +01:00
copy_derivatives ( plhs [ 1 ] , Symmetry { 3 , 0 , 0 , 0 } , derivs , " gyyy " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 0 , 3 , 0 , 0 } , derivs , " guuu " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 2 , 1 , 0 , 0 } , derivs , " gyyu " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 1 , 2 , 0 , 0 } , derivs , " gyuu " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 1 , 0 , 0 , 2 } , derivs , " gyss " ) ;
copy_derivatives ( plhs [ 1 ] , Symmetry { 0 , 1 , 0 , 2 } , derivs , " guss " ) ;
2017-05-16 16:30:27 +02:00
}
}
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const KordException & e )
2009-05-06 12:10:27 +02:00
{
2009-05-27 16:28:23 +02:00
e . print ( ) ;
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( ( " dynare:k_order_perturbation: Caught Kord exception: " + e . get_message ( ) ) . c_str ( ) ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const TLException & e )
{
e . print ( ) ;
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " dynare:k_order_perturbation: Caught TL exception " ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( SylvException & e )
{
e . printMessage ( ) ;
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " dynare:k_order_perturbation: Caught Sylv exception " ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const DynareException & e )
{
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( ( " dynare:k_order_perturbation: Caught KordDynare exception: " + e . message ( ) ) . c_str ( ) ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const ogu : : Exception & e )
{
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( ( " dynare:k_order_perturbation: Caught general exception: " + e . message ( ) ) . c_str ( ) ) ;
2009-11-30 17:31:27 +01:00
}
} // end of mexFunction()
} // end of extern C