/* part3.yac - Model compiler for the nonlinear programming specification
language */

/* project is getting too complicated.  Got rid of sum function and of
user-defined functions.  Users define global variables (not arrays) instead.
*/

/* You must fill in the missing code whereever ZZZ appears. */

%{
#include <iostream.h>

#define YYDEBUG 1

// the following are needed by bison
int yylex(void);
void yyerror(char * msg);

#define TABLLENG 100
int symcount = 0;
struct symrec
  {
  char name;
  int type;
  int size;
  };

symrec symtbl[TABLLENG + 1];


struct treenode
  {
  int token;
  union
    {
    int index;
    double number;
    struct
      {
      treenode * left;
      treenode * right;
      } root;
    };
  };

// Lines needed if you use bison instead of yacc
treenode * makeleaf(int tok, int index);
treenode * makeleaf(int tok, double num);
treenode * maketree(int tok, treenode * lft, treenode * rht);

treenode * types_list;
treenode * ranges_list;
treenode * definitions_list;
treenode * problem_tree;

int optimization;

%}

%union
  {
  int index;
  treenode * node;
  double number;
  }

%token LE GE NE INT REAL IF OTHERWISE FORALL FROM TO STEP ABS
%token MAXIMIZE MINIMIZE DIM IS LET SUBJECT AND 
%token LIST UMINUS
%token <index> INTLIT UCASE LCASE ARRAY 
%token <number> REALLIT 

%type <node> simple_type compound_type simple_type_list
%type <index> optional_sign optional_step sign compare optimize
%type <node> one_range simple_range compound_range variable
%type <number> number unsigned_number
%type <node> expr simple_range_list one_definition definition_body
%type <node> simple_assignment
%type <node> assignment_list otherwise_clause conditional_assignment
%type <node> condition_list condition constraints constraint_list expr1
%type <node> term factor simple_factor 
%type <node> constraint 


%start program

%%

program : dimensions types ranges definitions problem
        ;

/* Bison allows two successive blocks of code in a rule, but yacc does not. */
dimensions : dimensions DIM UCASE '=' INTLIT
	     {
	     symtbl[$3].type = ARRAY;
             symtbl[$3].size = $5+1;
	     }
           |
	   ;

types : types simple_type
        {types_list = maketree(LIST,types_list,$2);}
      | types compound_type
        {types_list = maketree(LIST,types_list,$2);}
      | {types_list = 0;}
      ;

simple_type : variable IS INT
              {$$ = maketree(IS,$1,makeleaf(INT,0));}
            | variable IS REAL
              {$$ = maketree(IS,$1,makeleaf(REAL,0));}
	    ;

compound_type : FORALL LCASE FROM expr TO expr optional_step ','
                simple_type_list
		{ $$ = maketree(FORALL,
		                makeleaf(LCASE,$2),
				maketree(FROM,
				         $4,
					 maketree(TO,
					          $6,
						  maketree(STEP,
						           makeleaf(INT,$7),
							   $9))));}
	      ;

simple_type_list : simple_type_list ',' simple_type
                   {$$ = maketree(LIST,$1,$3);}
                 | simple_type
		 ;

optional_step : STEP optional_sign INTLIT
                {$$ = $2 * $3;}
              | {$$ = 1;}
	      ;

optional_sign : sign
              | {$$ = 1;}
	      ;

sign : '+'
       {$$ = 1;}
     | '-'
       {$$ = -1;}
     ;

ranges : ranges one_range
         {ZZZ - make ranges_list be a list of one_ranges }
       | one_range
	 {ZZZ}
       ;

one_range : simple_range
          | compound_range
	  ;

simple_range : number LE variable LE number
               {$$ = maketree(LE,
	                      makeleaf(REAL,$1),
			      maketree(LE,
			               $3,
				       makeleaf(REAL,$5)));}
             ;

compound_range : FORALL LCASE FROM expr TO expr optional_step ','
                 simple_range_list
		{ZZZ}
	       ;

simple_range_list : simple_range_list ',' simple_range
                   {ZZZ}
                  | simple_range
		  ;

definitions : definitions one_definition
              {ZZZ - make definitions_list be a list of definitions}
	    | {ZZZ - initialize definitions_list to be the empty list}
	    ;

one_definition : LET UCASE definition_body
                 {$$ = maketree(LET,makeleaf(UCASE,$2),$3);}
               ;

definition_body : simple_assignment
                | assignment_list otherwise_clause
		  {ZZZ - set $$ to a list}
		;

simple_assignment : '=' expr
		    {$$ = maketree('=',0,$2);}
                  ;

assignment_list : assignment_list ',' conditional_assignment
                  {ZZZ - set $$ to a list of conditional_assignments}
                | conditional_assignment
		;

otherwise_clause : ',' simple_assignment OTHERWISE
                   {$$ = $2;}
                 | {$$ = 0;}
		 ;

conditional_assignment : simple_assignment IF condition_list
                         { $$ = maketree(IF,$1,$3);}
                       ;

condition_list : condition_list AND condition
                 { ZZZ - set $$ to a list of conditions}
               | condition
	       ;

condition : expr compare expr
            { $$ = maketree($2,$1,$3);}
          ;

compare : '<'
          {$$ = '<';}
        | LE
	  {$$ = LE;}
	| '='
          {$$ = '=';}
	| GE
	  {$$ = GE;}
	| '>'
          {$$ = '>';}
	| NE
	  {$$ = NE;}
	;

problem : optimize expr constraints
          {problem_tree = maketree($1,$2,$3);}
        ;

optimize : MAXIMIZE
           {$$ = MAXIMIZE;}
         | MINIMIZE
           {$$ = MINIMIZE;}
	 ;

constraints : SUBJECT TO constraint_list
              {$$ = $3;}
            | {$$ = 0;}
	    ;

constraint_list : constraint_list ',' constraint
                  { ZZZ - set $$ to a list of constraints}
                | constraint
		;

constraint : condition
           ;

expr : expr1
     | '-' expr1
       {$$ = maketree(UMINUS,0,$2);}
     ;

expr1 : expr1 '+' term
       { $$ = maketree('+',$1,$3);}
      | expr1 '-' term
       { $$ = maketree('-',$1,$3);}
      | term
      ;

term : term factor
       { ZZZ }
     | term '/' factor
       { ZZZ }
     | factor
     ;

factor : simple_factor
       | simple_factor '^' simple_factor
         {ZZZ}
       ;

simple_factor : '(' expr ')'
                { $$ = $2;}
              | variable
	      | unsigned_number
		{ $$ = makeleaf(REAL,$1);}
	      | ABS '(' expr ')'
	        { $$ = maketree(ABS,0,$3);}
              ;

unsigned_number : INTLIT
                  { $$ = (double)$1;}
                | REALLIT
		;

number : unsigned_number
       | sign unsigned_number
         {$$ = $1 * $2;}
       ;

variable : LCASE
           { $$ = makeleaf(LCASE,$1);}
         | UCASE
           { ZZZ}
	 | ARRAY INTLIT
	   { $$ = maketree(ARRAY,makeleaf(UCASE,$1),makeleaf(INT,$2));}
	 | ARRAY LCASE
	   { ZZZ}
	 | ARRAY '(' expr ')'
	   { ZZZ}
	 ;

%%

void yyerror(char * msg)
  {
  cout << msg << endl;
  }

treenode * makeleaf(int tok, int index)
  {
  treenode * temp;
  temp = new treenode;
  temp->token = tok;
  temp->index = index;
  return temp;
  }

treenode * makeleaf(int tok, double num)
  {
  treenode * temp;
  temp = new treenode;
  temp->token = tok;
  temp->number = num;
  return temp;
  }

treenode * maketree(int tok, treenode * lft, treenode * rht)
  {
  treenode * temp;
  temp = new treenode;
  temp->token = tok;
  temp->root.left = lft;
  temp->root.right = rht;
  return temp;
  }

#include "lex.yy.c"


void gen_variables()
  {
  cout << "#include <stdlib.h>" << endl;
  cout << "#include <iostream.h>" << endl;
  cout << "#include <math.h>" << endl << endl;
  cout << "struct one_var {double val; double best; double lo;";
  cout << "";
  cout << " double hi;int type;};" << endl;
  cout << "double obj_val, best_obj_val;" << endl << endl;
  int i;
  for (i=1;i<=symcount;i++)
    {
    switch (symtbl[i].type)
      {
      case UCASE:
	   cout << "one_var " << symtbl[i].name << ";" << endl;
	   break;
      case LCASE:
	   ZZZ - declare the variable to be an int
	   break;
      case ARRAY:
	   ZZZ - declare the variable to be an array of one_vars
	   break;
      }
    }
  }


void cout_leaf(treenode * x)
  {
  switch(x->token)
    {
    case INT: cout << x->index; break;
    case REAL: ZZZ - print out the number
    case LCASE: 
    case UCASE: ZZZ print out the variable name
    }
  }

void cout_expr(treenode * x);

void cout_var_name(treenode * x)
  {
  switch (x->token)
    {
    case UCASE:
    case LCASE:
      cout << symtbl[x->index].name;
      break;
    case ARRAY:
      ZZZ - print out array reference without any .field extension
      break;
    }
  }

void cout_expr(treenode * x)
  {
  switch (x->token)
    {
    case '+':
    case '-':
    case '*':
    case '/':
      cout << "(" ;
      cout_expr(x->root.left);
      cout << (char)x->token ;
      cout_expr(x->root.right);
      cout << ")" ;
      break;
    case '^':
      ZZZ - print out a call to pow function 
      break;
    case UCASE:
    case ARRAY:
      ZZZ - print out variable with .val extension
      break;
    case LCASE:
    case INT:
    case REAL:
      cout_leaf(x);
      break;
    case ABS:
      ZZZ - print out call to fabs
      break;
    case UMINUS:
      cout << "-";
      cout_expr(x->root.right);
      break;
    }
  }

void gen_types_set1(treenode * types_list)
  {
  if (types_list == 0) return;
  switch(types_list->token)
    {
    case LIST:
      gen_types_set1(types_list->root.left);
      gen_types_set1(types_list->root.right);
      break;
    case FORALL:
      cout << "for(";
      cout_leaf(types_list->root.left);
      cout << "=";
      cout_expr(types_list->root.right->root.left);
      cout << ";";
      cout_leaf(types_list->root.left);
      cout << "<=" ;
      cout_expr(types_list->root.right->root.right->root.left);
      cout << ";" ;
      cout_leaf(types_list->root.left);
      cout << "=" ;
      cout_leaf(types_list->root.left);
      cout << "+" ;
      cout_leaf(types_list->root.right->root.right->root.right->root.left);
      cout << ")" << endl;
      cout << "{" ;
      gen_types_set1(types_list->root.right->root.right->root.right->root.right);
      cout << "}" << endl;
      break;
    case IS:
      cout_var_name(types_list->root.left);
      cout << ".type = " << types_list->root.right->token << ";" << endl;
      break;
    }
  }

void gen_types_set()
  {
  cout << "void set_types() {int i;" << endl;
  int i;
  for (i=1;i<=symcount;i++)
    {
    switch (symtbl[i].type)
      {
      case UCASE:
	   cout << symtbl[i].name << ".type = " << REAL << ";" << endl;
	   break;
      case ARRAY:
           cout << "for (i=1;i<" << symtbl[i].size << ";i++)" << endl;
	   cout << symtbl[i].name << "[i].type = " << REAL << ";" << endl;
	   break;
      }
    }
  gen_types_set1(types_list);
  cout << "}" << endl;
  }

void gen_range_set1(treenode * x)
  {
  if (x == 0)
    {
    cout << "ERROR: no range statements" << endl;
    return;
    }
  else switch (x->token)
    {
    case LIST:
      ZZZ - apply gen_range_set1 to the components of the list
      break;
    case LE:
      cout_var_name(x->root.right->root.left);
      cout << ".lo = " ;
      cout_leaf(x->root.left);
      cout << ";";
      cout_var_name(x->root.right->root.left);
      cout << ".hi = " ;
      cout_leaf(x->root.right->root.right);
      cout << ";";
      break;
    case FORALL:
      ZZZ - print out for loop with gen_range_set1 applied to the body
      break;
    }
  }

void gen_range_set(treenode * x)
  {
  cout << "void set_ranges() {" << endl;
  gen_range_set1(x);
  cout << "}" << endl;
  }

void gen_condition(treenode * x)
  {
  if(x == 0)
    {
    cout << "ERROR: no condition" << endl;
    return;
    }
  cout << "(";
  switch (x->token)
    {
    case '<':
      cout_expr(x->root.left);
      cout << "<";
      cout_expr(x->root.right);
      break;
    case '>':
      ZZZ
      break;
    case '=':
      ZZZ
      break;
    case LE:
      ZZZ
      break;
    case GE:
      ZZZ
      break;
    case NE:
      ZZZ
      break;
    case LIST:
    case AND:
      gen_condition(x->root.left);
      cout << "&&" ;
      gen_condition(x->root.right);
      break;
    }
  cout << ")";
  }

void gen_defs1(treenode * x)
  {
  if (x == 0) return;
  switch (x->token)
    {
    case LIST:
      gen_defs1(x->root.left);
      gen_defs1(x->root.right);
      break;
    case LET:
      cout_expr(x->root.left);
      cout << " = ";
      gen_defs1(x->root.right);
      cout << ";" << endl << endl;
      break;
    case '=':
      cout_expr(x->root.right);
      break;
    case IF:
      cout << "(";
      gen_condition(x->root.right);
      cout << ")?";
      gen_defs1(x->root.left);
      cout << ":";
      break;
    }
  }

void gen_defs(treenode * x)
  {
  cout << "void set_dep_vars() {" << endl;
  gen_defs1(x);
  cout << "}" << endl << endl;
  }

void gen_obj_fun()
  {
  optimization = problem_tree->token;
  cout << "double obj_fun() { return" << endl;
  cout_expr(problem_tree->root.left);
  cout << ";}" << endl << endl;
  }

void gen_constraints()
  {
    cout << "int constraints() {return" << endl;
  if (problem_tree->root.right == 0)
    {
    cout << 1;
    }
  else
    {
    gen_condition(problem_tree->root.right);
    }
  cout << ";}" << endl << endl;
  }

void gen_main()
  {
  cout << "double crand() { return ((double)rand())/RAND_MAX;}" << endl << endl;
  cout << "double rrand(double lo, double hi) {return ";
  cout << "lo + crand()*(hi - lo);}" << endl << endl;
  cout << "double irand(double lo, double hi) {return ";
  cout << "floor(rrand(lo,hi+1));}" << endl << endl;
  cout << "double shrink_factor = 1.0;" << endl;
  cout << "double shrink_rate = 0.999;" << endl;
  cout << "#define NUM_TRIALS 100000" << endl << endl;
  cout << "void init_vars() {" << endl;
  int i;
  for (i=1;i<=symcount;i++)
    {
    char name;
    name = symtbl[i].name;
    switch (symtbl[i].type)
      {
      case UCASE:
	   cout << "if(" << name << ".type == " << REAL << ")" << endl;
	   cout << name << ".val = rrand(" ;
	   cout << name << ".lo," ;
	   cout << name << ".hi);" << endl;
	   cout << "else " << name << ".val = irand(" ;
	   cout << name << ".lo," ;
	   cout << name << ".hi);" << endl;
	   cout << name << ".best = " << name << ".val;" << endl;
	   break;
      case ARRAY:
           cout << "{int i; for (i=1;i<" << symtbl[i].size << ";i++) {" << endl;
	   cout << "if(" << name << "[i].type == " << REAL << ")" << endl;
	   cout << name << "[i].val = rrand(" ;
	   cout << name << "[i].lo," ;
	   cout << name << "[i].hi);" << endl;
	   cout << "else " << name << "[i].val = irand(" ;
	   cout << name << "[i].lo," ;
	   cout << name << "[i].hi);" << endl;
	   cout << name << "[i].best = " << name << "[i].val;}}" << endl;
	   break;
      }
    }
  cout << "set_dep_vars();" << endl;
  cout << "best_obj_val = obj_fun();}" << endl << endl;
  cout << "void new_vars() {double lo,hi,r;" << endl;
  for (i=1;i<=symcount;i++)
    {
    char name;
    name = symtbl[i].name;
    switch (symtbl[i].type)
      {
      case UCASE:
	   cout << "r = shrink_factor * 0.5 * (" << name << ".hi - ";
	   cout << name << ".lo);" << endl;
	   cout << "hi = " << name << ".best + r;" << endl;
	   cout << "if (hi > " << name << ".hi)" ;
	   cout << "hi = " << name << ".hi;" << endl;
	   cout << "lo = " << name << ".best - r;" << endl;
	   cout << "if (lo < " << name << ".lo)" ;
	   cout << "lo = " << name << ".lo;" << endl;
	   cout << "if(" << name << ".type == " << REAL << ")" << endl;
	   cout << name << ".val = rrand(lo,hi);" << endl;
	   cout << "else " << name << ".val = irand(lo,hi);" << endl;
	   break;
      case ARRAY:
           cout << "{int i; for (i=1;i<" << symtbl[i].size << ";i++) {" << endl;
	   cout << "r = shrink_factor * 0.5 * (" << name << "[i].hi - ";
	   cout << name << "[i].lo);" << endl;
	   cout << "hi = " << name << "[i].best + r;" << endl;
	   cout << "if (hi > " << name << "[i].hi)" ;
	   cout << "hi = " << name << "[i].hi;" << endl;
	   cout << "lo = " << name << "[i].best - r;" << endl;
	   cout << "if (lo < " << name << "[i].lo)" ;
	   cout << "lo = " << name << "[i].lo;" << endl;
	   cout << "if(" << name << "[i].type == " << REAL << ")" << endl;
	   cout << name << "[i].val = rrand(lo,hi);" << endl;
	   cout << "else " << name << "[i].val = irand(lo,hi);}}" << endl;
	   break;
      }
    }
  cout << "set_dep_vars();" << endl;
  cout << "obj_val = obj_fun();}" << endl << endl;
  cout << "void copy_vars() {" << endl;
  for (i=1;i<=symcount;i++)
    {
    char name;
    name = symtbl[i].name;
    switch (symtbl[i].type)
      {
      case UCASE:
	cout << name << ".best = " << name << ".val;" << endl;
	break;
      case ARRAY:
           cout << "{int i; for (i=1;i<" << symtbl[i].size << ";i++) {" << endl;
	   cout << name << "[i].best = " << name << "[i].val;}}" << endl;
	   break;
      }
    }
  cout << "best_obj_val = obj_val;}" << endl << endl;
  cout << "void show_vars () {" << endl;
  cout << "cout << \"objective function = \" << best_obj_val << endl;"  << endl;
  for (i=1;i<=symcount;i++)
    {
    char name;
    name = symtbl[i].name;
    switch (symtbl[i].type)
      {
      case UCASE:
	cout << "cout << \"" << name << "\" << \" = \" << ";
	cout << name << ".best << endl;" << endl;
	break;
     case ARRAY:
        cout << "{int i; for (i=1;i<" << symtbl[i].size << ";i++) {" << endl;
	cout << "cout << \"" << name << "\" << i << \" = \" << ";
	cout << name << "[i].best << endl;}}" << endl;
	break;
      }
    }
  cout << "}" << endl << endl;
  cout << "main() {int i;" << endl;
  cout << "set_types(); set_ranges();" << endl;
  cout << "do {init_vars();} while (!constraints());" << endl;
  cout << "for (i=0;i<NUM_TRIALS;i++) {" << endl;
  cout << "new_vars();" << endl;
  cout << "if ((obj_val ";
  if (optimization == MAXIMIZE) cout << ">";
  else cout << "<";
  cout << " best_obj_val) && constraints())" << endl;
  cout << "  {copy_vars(); cout << best_obj_val << endl;}" << endl;
  cout << "shrink_factor *= shrink_rate;}" << endl;
  cout << "show_vars();}" << endl;
  }

main()
  {
  int parse_error;
  parse_error = yyparse();
  if (parse_error) cout << "Parser reports failure" << endl;
  else
    {
    gen_variables();
    gen_types_set();
    gen_range_set(ranges_list);
    gen_defs(definitions_list);
    gen_obj_fun();
    gen_constraints();
    gen_main();
    }
  }
