/* ----------------------------------------------------------------------------

nlmdl: nlmdl.cc

nlmdl is a C++ implementation of the statistical methods in A. Ronald 
Gallant, "Nonlinear Statistical Models," New York: John Wiley and Sons, 
1987, ISBN 0-471-80260-3, using a matrix class realmat that is distributed 
with it.  The header files nlmdl.h and realmat.h describe the use of the 
program and matrix class, respectively.  

Copyright (C) 1990, 1991, 1993, 1994, 2002.

A. Ronald Gallant
Post Office Box 659
Chapel Hill NC 27514-0659
USA   

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, provided 
that the above copyright notice appears in all copies and that both that 
copyright notice and this permission notice appear in supporting 
documentation.  

This software is provided "as is" without any expressed or implied warranty.

---------------------------------------------------------------------------- */
#include "nlmdl.h"

typedef void (*ZERO_ARGUMENT_NL_OPR)();
typedef void (*ONE_ARGUMENT_NL_OPR)(int);

status s; 
model  m;

ZERO_ARGUMENT_NL_OPR opr_mgn;
ZERO_ARGUMENT_NL_OPR opr_obj;
ONE_ARGUMENT_NL_OPR  opr_var;
ZERO_ARGUMENT_NL_OPR opr_V;

REAL const zero = (REAL) 0.0;

#include "nlmdl.opr"

int main()
{
  char msg[MAX_STATUS_LINE];

  s.from(s.starting); 

  repeat:

    if(m.initialize()==0) exit(0);

    s.from(s.starting);

    if(strcmp(s.detail,"none") != 0) {
      sprintf(msg,"/nlmdl %s//_",VERSION);
      cout << starbox(msg);
      s.display(START_UP);
      cout << starbox("/Starting theta//_") << s.theta; 
    }  

    if(strcmp(s.method,"SUR") == 0) {
      opr_mgn = &SUR_mgn; 
      opr_obj = &SUR_obj; 
      opr_var = &SUR_var; 
      opr_V   = &SUR_V;
    }
    else if(strcmp(s.method,"TSLS") == 0) {

      if ( strcmp(s.vartype,"heteroskedastic") == 0 || s.MA>0 ) {
        cerr << "\nError, nlmdl, Use GMM for this case, not TSLS.\n\n";
        exit(1);
      }
      opr_mgn = &TSLS_mgn; 
      opr_obj = &TSLS_obj; 
      opr_var = &SUR_var; 
      opr_V   = &TSLS_V;

      qZ.resize(s.M*s.K,1);
      QZ.resize(s.M*s.K,s.p);
    }
    else if(strcmp(s.method,"GMM") == 0) {
      opr_mgn = &GMM_mgn;
      opr_obj = &GMM_obj; 
      opr_var = &GMM_var; 
      opr_V   = &GMM_V;

      qZ.resize(s.M*s.K,1);
      QZ.resize(s.M*s.K,s.p);
    }
    else {
      cerr << "Error, nlmdl, s.method set wrong.\n";
      exit(1);
    }

    m.e_t.resize(s.M,1);
    m.dele_t.resize(s.M,s.p);
    if (s.K > 0 ) m.Z_t.resize(s.K,1);

    s.V.resize(s.p, s.p, zero );
    for (INTEGER i = 1; i <= s.p; i++) s.V(i,i) = 1.0;
    s.D.resize(s.p, 1, zero );

    for (int var_loop = 0; var_loop <= s.ivar; var_loop++) {

      (*opr_var)(var_loop);

      if(strcmp(s.detail,"full") == 0) {
        cout << "\nvar_loop " << var_loop << "\n";
        s.display(VAR_ITERATE);
      }

      for (INTEGER theta_loop = 0; theta_loop <= s.itheta; theta_loop++) {

        (*opr_mgn)();

        if(strcmp(s.detail,"full") == 0) {
          cout << "\ntheta_loop " << theta_loop << "\n";
          s.display(THETA_ITERATE);
        }
        
        int rv = line_search(&msg[0]);

        if(strcmp(s.detail,"full") == 0) cout << msg;
        if(rv != 0) break;
      }
    }

  (*opr_mgn)();
  (*opr_V)();

  s.to(s.ending);

  if (strcmp(s.detail,"none") != 0) s.display(TERMINATION);

  if(m.terminate() != 0) goto repeat;

  exit(0);
}
