/*
 * Copyright (C) Andrew Wuensche
 *
 *  This file is part of DDLab (Discrete Dynamics Lab) <www.ddlab.org>
 *
 *  DDLab 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.
 *
 *  This program 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 DDLab.  If not, see <http://www.gnu.org/licenses/>.
 */

/********************* decision tree for kcode **************/
#include "edecfunc.h"
/***************  list of functions in this file *************/
/*void free_v_code_arrays(void);*/
/*void set_up_v_code(void);*/
unsigned int add_node(char rem, char level, char k, char v, s_node *root,
		                               unsigned int t_index);
struct s_node* create_tree(char k, char v);
/*unsigned int search_tree(char *input, char length, 
                            s_node *root,char index);*/
s_node* NN(s_node *root, char value, unsigned int t_index, char k);
/*void free_tree(s_node *root, char *space);*/
void print_tree(s_node *root,char k, signed int v_bits, 
                             char rcycle, char *temparray);

/*********** free various  v_totalistic code arrays, Sept 2002 **************/
void free_v_code_arrays(void){
  signed int z;
  char this_nhood;
  char* space;

  if ((space=(char*)calloc(1000,sizeof(char)))
				  ==NULL) outof_memory("space*");

   /*fprintf(stderr,"\n>free_v_code_arrays:)");*/
   for (this_nhood=1;this_nhood<=nhood;this_nhood++){
      /*fprintf(stderr,"\nnhood %d: ",this_nhood);*/
      for (z=0;z<=v_tablecodemax[this_nhood];z++){
	  /*fprintf(stderr," %d ",z);*/
          free(v_index[this_nhood][z]);
      }
      free(v_index[this_nhood]);
   }
   /*fprintf(stderr,"\nfree(v_index)");*/
   free(v_index);
   /*fprintf(stderr,"\nfree((v_tablecodemax)");*/
   free(v_tablecodemax);
   /*free trees*/
   for (this_nhood=1;this_nhood<=nhood;this_nhood++){
     /*fprintf(stderr,"\n%d:",this_nhood);*/
      free_tree(v_tree[this_nhood],space);
   }
   free(space);
   /*fprintf(stderr,"\n>free(v_tree)");*/
   free(v_tree);
}

/************** create v/k tree **************/
void set_up_v_code(void){
  char this_nhood,
       *temparray; /*for print_tree*/
  signed int v_bits;
  unsigned int bytes_req,z;
        /*for each current k (k-mix) set up v_code totalistic value array 
		 v_index[k][local rule index][char local totals]*/
        /*draw_box(5,(signed int)((float)Maxx)/3,2,
	     ((signed int)((float)Maxx)/2)-3,2+CHeight+vgay(6),0,-1);*/
        /*memory_box(2,"setting up kcode, wait...");*/ /*can take time*/
        
        if ((v_index =(char***)calloc(nhood+1,sizeof(char**)))
				  ==NULL) outof_memory("v_index***");
        if ((v_tablecodemax =(unsigned int*)
                 calloc(nhood+1,sizeof(unsigned int)))
				 ==NULL) outof_memory("v_tablecodemax");
        /*new tree method, alloc mem to trees 1 to nhood*/
        if ((v_tree =(s_node**)calloc(nhood+1,sizeof(s_node*)))
				  ==NULL) outof_memory("v_tree");
   
        /*fprintf(stderr,"\n %d:",value_range);*/
        for (this_nhood=1;this_nhood<=nhood;this_nhood++){
           if ((v_tree[this_nhood] =(s_node*)calloc(1,sizeof(s_node)))
				  ==NULL) outof_memory("v_tree[]");

            v_tree[this_nhood]=create_tree(this_nhood,value_range);

            /*fprintf(stderr,"\nv%dk%d tree, v_tmax=%d...\n",
	      value_range,this_nhood,v_tablecodemax[this_nhood]);*/
            /*fprintf(stderr,"%d ",v_tablecodemax[this_nhood]+1);*/

            /*fprintf(stderr,"\n%d/%d=%d%%",
	      this_nhood,nhood,(signed int)(100*this_nhood/nhood));*/		    
            /*to monitor progress in memory box -- significant for large v/k*/
            sprintf(textb[3],"setting up kcode %d%%",
                               (signed int)(100*(float)this_nhood/nhood));
            memory_box(2,textb[3]);
            sprintf(textb[3],"");

            v_bits=check_value_bits(this_nhood+1);
            bytes_req=(unsigned int)ceil(v_bits*(double)value_range/8);
            
            if ((v_index[this_nhood] =(char**)calloc(
                v_tablecodemax[this_nhood]+1,sizeof(char*)))
			  ==NULL) outof_memory("v_index[this_nhood]");
            for (z=0;z<=v_tablecodemax[this_nhood];z++){
               if ((v_index[this_nhood][z]=(char*)calloc(
                      bytes_req,sizeof(char)))
			  ==NULL) outof_memory("v_index[this_nhood]");
            }
            /* fill v_index and/or print the tree*/            
            /*for all sizes of nhood, was memory bug in mtree05.c*/
            if ((temparray =(char*)calloc( 
	                 (nhood+1)*value_range,sizeof(char)))  
		   ==NULL) outof_memory("temparray");

	    print_tree(v_tree[this_nhood],this_nhood,v_bits,-1,temparray);

            free(temparray);
            /*check that it works*/
            /*{
	    signed int zv;
            fprintf(stderr,"\nv_bits=%d v_tmax=%d\n",v_bits,
                                        v_tablecodemax[this_nhood]);
               for (z=0;z<=v_tablecodemax[this_nhood];z++){ 
                  for (zv=value_range-1; zv>=0; zv--) 
                     fprintf(stderr,"%d ",
                       get_value_flex(v_index[this_nhood][z],zv,v_bits));
                  fprintf(stderr,"= %d\n",z);
	       } 
	     }*/
        }
        /*restore pixels below memory box*/
        /*sprintf(textb[9],"%s","boximage[5]");
          putimage((signed int)((float)Maxx)/3,2,boximage[5],COPY_PUT,textb[9]);
	  freeimage(boximage[5]);*/
}

/************** create v/k tree **************/
 unsigned int add_node(char rem, char level, char k, char v, s_node *root,
                                        unsigned int t_index){
 /* v:value range*/
 /* k:neighborhood size*/
   s_node *newnode;
   signed int i;

   /*fprintf(stderr,"entered in add_node...\n");
     fprintf((stderr,"rem=%d\n",rem);*/
   if (rem==0){
      /*fprintf((stderr,"Adding new node...\n");*/
      if (level-1 > 0) {
         /*we aren't at the end*/
         newnode=NN(root,0,t_index,k);
         t_index=add_node(rem,level-1,k,v,newnode,t_index);
      } else {  
         newnode=NN(root,0,t_index,k);
         t_index++;
      }
   } 
   else { 
     /*fprintf((stderr,"rem != 0 \n");*/
     if (level-1 == 0) {
         /*fprintf((stderr,"last level -1 \n");*/
         /*we are at the last level -1*/
         newnode=NN(root,rem,t_index,k);
         /*add reference into vector of pointers*/
	 t_index++;
     } else {
       if (level-2 == 0) {
	  /*fprintf((stderr,"last level -2 \n");*/
	  /*we are at the last level -1*/
          newnode=NN(root,rem,t_index,k);
	 /*add reference into vector of pointers*/
	  t_index=add_node(k-rem,level-1,k,v,newnode,t_index);
       } else {
	 /*we must loop*/ 
	 /*fprintf((stderr,"intermediate level \n");*/
	 for(i=0;i<=rem;i++) {
	    /*fprintf((stderr,"i= %d \n",i);*/
	    newnode=NN(root,i,t_index,k); 
	    /*add reference into vector of pointers*/
	    t_index=add_node(rem-i,level-1,k,v,newnode,t_index);
	 }     
       } 
     }
   }
   return(t_index);
 }

/************** create_tree **************/
struct s_node* create_tree(char k, char v) {
  struct s_node *root;
  unsigned int max_tindex;

  v=v+1;
  /*allocating root node*/
  if ((root=(struct s_node *)calloc(1,sizeof(struct s_node)))==NULL) 
          outof_memory("root");
  if ((root->children=calloc(k+1,sizeof(struct s_node *)))==NULL) 
          outof_memory("root->children");
  if ((root->childindex=calloc(k+1,sizeof(int)))==NULL) 
          outof_memory("root->childindex");
  root->value=0;
  max_tindex=add_node(k, v, k, v, root, 0);
  /*fprintf(stderr,"\nv%dk%d index size = %d",v-1,k,max_tindex);*/
  v_tablecodemax[k]=max_tindex-1;
  return root;
}

/************** search_tree **************/
unsigned int search_tree(char *input, char length, 
                         s_node *root,char index){

   /*fprintf(stderr,"entered search_tree: index=%d, 
                       input=%d\n",index,input[index]);*/
   if (index==(length-1)) {
      /*  end of tree */
      /*fprintf(stderr,"index found!! %d\n",root->t_index);*/
      return(root->t_index);
   } else {
     /* middle of tree */
     /*fprintf(stderr,"calling searh_tree\n");*/
     return(search_tree(input,length,
            root->children[root->childindex[input[index]]],index+1));
   }
 }

/************** create a new node **************/ 
struct s_node* NN(s_node *root, char  value, unsigned int t_index, char k){
   s_node *newchild;

   /*printf("entered  in NN\n");*/
   if ((newchild=(struct s_node *)calloc(1,sizeof(struct s_node)))==NULL) 
        printf("cannot allocate a newchild\n");
   if ((newchild->children=calloc(k+1,sizeof(struct s_node *)))==NULL) 
        printf("cannot allocate a new children array ...\n");
   if ((newchild->childindex=calloc(k+1,sizeof(char)))==NULL) 
         printf("cannot allocate a new children index array ...\n"); 
   root->children[root->num_children]=newchild;
   /*fprintf(stderr,"==> value: %d\n",value);*/
   /*fprintf(stderr,"==> index: %d, reference= %d\n",
                              value,root->num_children);*/
   root->childindex[value]=root->num_children;
   root->num_children++;
   newchild->value=value;
   newchild->num_children=0;
   newchild->t_index=t_index;
   return newchild;
 }

/************** free tree **************/
 void free_tree(s_node *root, char *space){
   signed int j;
   //char ss[100000]; /*was 100 - maybe caused intermittent segfault*/
   char ss[1000];

   strcpy(ss,space);
   /*fprintf(stderr,"\nnum_children=%d ",root->num_children);*/
   if (root->num_children!=0) {
     /*strcat(ss,"..");*/
     for (j=0; j<root->num_children; j++){
       /*fprintf(stderr,"j=%d",j);*/
       free_tree(root->children[j],ss);
     }
   }
   free(root->children);
   free(root->childindex);
   free(root);
   /*fprintf(stderr," *");*/
   return;
}

/************** print tree **************/
void print_tree(s_node *root, char k, signed int v_bits,
                           char rcycle, char *temparray){
   signed int j,i,thispos;

   rcycle+=1;
   /*fprintf(stderr,"\nrcycle=%d entered free-tree, num ofchild:%d",
                    rcycle,root->num_children);*/
   if (root->num_children!=0) {
     temparray[rcycle]=root->value;
     for (j=0; j<root->num_children; j++){
       print_tree(root->children[j],k,v_bits,rcycle,temparray);
     } 
   } 
   else {
     rcycle=-1;
     /*fprintf(stderr,"\n%lu: ",root->t_index);*/
     thispos=value_range;
     for (i=1;i<=value_range;i++){
         thispos-=1;
         /*fprintf(stderr,"%d ",temparray[i]);*/
         set_value_flex(v_index[k][(signed int)root->t_index],
                                    thispos,temparray[i],v_bits);
     }
   }
   return;
}
/*----------------------------------------------------------------*/
