/* This is a relatively complex class. It tackles the parsing of an 
   L String, which is passed in to the Lstring class by the << operator.
   The parsed string is stored in memory pointed to by the "Finalstring"
   pointer, which is allocated on the fly.

   The class also tackles mutating the Lstring.
*/
#ifdef WIN32
#define snprintf _snprintf
#endif

#include <math.h>
#include "extra_math.h"
#include "Lstring.h"

//some default strings
const string new_rule_start("@=");
const string default_rec_string("5 #this is the default recursion level, inserted to replace a badly formatted line");
const string default_ang_string("45 #this is the default angle, inserted to replace a badly formatted line");
const string default_thick_string("50 #this is the default starting thickness, inserted to replace a badly formatted line");

//size of memory allocation chunks for final string array
const unsigned long FS_Chunk=10000;

//Define useful characters
const string whitespace("\t ");
const string endline_markers("\n");
const string full_length_chars("Ffg");
const string half_length_chars("Zz");
const string increment_length_chars("\"");
const string decrement_length_chars("\'");
const string increment_angle_chars(";");
const string decrement_angle_chars(":");
const string other_angle_chars("+-<>&^");
const string increment_thick_chars("?");
const string decrement_thick_chars("!");
const string colour_chars("c");
const string trope_chars("t");
const string randrot_chars("~");
const string extra_chars("|%$.");
const string new_rule_chars("ABCDEGHIJKLMNOPQRSTUVWXYabcdehijklmnopqrsuvwxy");
const string inbrackets_chars("0123456789. ");
const string open_mutable_brackets("[{");

const string length_chars=full_length_chars+half_length_chars+increment_length_chars+decrement_length_chars;
const string angle_chars=increment_angle_chars+decrement_angle_chars+other_angle_chars;
const string thick_chars=increment_thick_chars+decrement_thick_chars;
const string bracketed_chars=length_chars+angle_chars+thick_chars+colour_chars+trope_chars+randrot_chars;
const string mutable_chars = length_chars+angle_chars+thick_chars+colour_chars+trope_chars+randrot_chars+extra_chars+new_rule_chars+open_mutable_brackets;  //don't include close brackets, otherwise brackets will have twice the chance of mutating to other characters

string snip_line(string &text)
{
//Desc: removes a line from "text", and returns the removed line
// as a string, minus leading whitespace.
  size_t marked_pos;
  string ss;

  if (text.length()==0)
    return "";

  //complex procedure to remove a line
  marked_pos=text.find_first_of(endline_markers);
  if (marked_pos==string::npos) {//last line
    ss=text;
    text="";
  } else {                       //end of line marker found
    ss        =text.substr(0,marked_pos);
    marked_pos=text.find_first_not_of(endline_markers,marked_pos);
    text.erase(0,marked_pos);
  };
  
  //strip leading whitespace
  marked_pos =  ss.find_first_not_of(whitespace);
  ss.erase(0,marked_pos);
  
  return ss;
}

/*********************************************
 ************ LlineClass methods *************
 *********************************************
 The LlineClass stores a single line of an L System 
 specification. If the line begins with a #, it
 is marked as a COMMENT line. Text after a # is always 
 ignored: to cope with this, the "end_text" variable
 marks where the # comment begins (actually, it marks
 the start of any whitespace before a # character).

 The line can be a RECursion line, a starting ANGle line,
 or a default THICKness line, in which 3 cases, it must
 consist of a single number. Otherwise, it can be an AXIOM
 line or a RULE line. There should only be one RECursion line, 
 one ANGle line, one THICKness line, and one AXIOM line.
 */

LlineClass::LlineClass(vector<LlineClass *> *the_list)
{
//Desc: constructor - an Lline must be added to a list of such lines. 
// The list to which it is added is passed to the constructor 
  the_text="";
  end_text=0;
  marker=COMMENT;
  the_list->push_back(this);
} 

LlineClass::LlineClass(vector<LlineClass *> *the_list,
		       LlineClass * const cpy)
{
//Desc: my own copy constructor
  the_text=cpy->the_text;
  end_text=cpy->end_text;
  marker=cpy->marker;
  the_list->push_back(this);
} 

void LlineClass::assign(char* const text_in, const line_label marker_in)
{
//Desc: allocate text (as a char array) to an Lline
  the_text="";
  the_text.append(text_in);
  //look for comments
  end_text=the_text.find('#');
  end_text=the_text.find_last_not_of("#\t ",end_text);
  if (end_text==string::npos)
    end_text=0;
  else
    end_text++;
  marker=marker_in;
} 

void LlineClass::assign(const string text_in, const line_label marker_in)
{
//Desc: allocate text (as a string) to an Lline
  the_text="";
  the_text.append(text_in);
  //cout << the_text << endl;
  //look for comments
  end_text=the_text.find('#');
  end_text=the_text.find_last_not_of("#\t ",end_text);
  if (end_text==string::npos)
    end_text=0;
  else
    end_text++;
  marker=marker_in;
}

inline const string LlineClass::text()
{
  return the_text;
}

inline void LlineClass::set_marker(line_label marker_in)
{
  marker=marker_in;
};

inline line_label LlineClass::get_marker()
{
  return marker;
}

inline size_t LlineClass::info_length()
{
  return end_text;
}

inline char LlineClass::first_char()
{
  return the_text[0];
}

inline char LlineClass::char_num(int i)
{
  return the_text[i];
}

inline char *LlineClass::rule_start()
{
  return &the_text[2];
}

inline bool LlineClass::has_value(size_t pos) {
  if (sscanf(the_text.data()+pos+1, "(%*f)")==1) return true;
  else return false;
} 


bool LlineClass::is_float()
{
  float dummy;
  if (sscanf(the_text.c_str(),"%f",&dummy)==1)
    return true;
  return false;
}

float LlineClass::as_float()
{
  float temp=0;
  
  if (sscanf(the_text.c_str(),"%f",&temp)==1)
    return temp;
  return sqrt(-1);

}

bool LlineClass::within_round_brackets(size_t the_pos, size_t &b, size_t &a)
{
//Desc: returns true if the character in position "the_pos" is, or is
// enclosed by, "(" and ")", and the only other characters enclosed
// are numbers or a decimal point. Also uses the "b" and "a" variables
// to return the positions of the "(" and ")" characters respectively. 
  static const string br_chars_aft=inbrackets_chars+"(";
  static const string br_chars_bef=inbrackets_chars+")";
  size_t first_pos;
  //Only look at RULE lines or the AXIOM line. 
  //If a RULE line, ignore the first 2 characters
  switch (marker) {
  case FZ_RULE:
  case USER_RULE:
  case STANDARD_RULE:
    first_pos=2;
    break;
  case AXIOM:
    first_pos=0;
    break;
  default:
    return false;
  };

  a=the_text.find_first_not_of(br_chars_aft, the_pos);
  b=the_text.find_last_not_of (br_chars_bef, the_pos);

  if ((a>=end_text) || (a==string::npos)) a=end_text;
  if ((b<first_pos) || (b==string::npos)) b=first_pos;
    
  if ((the_text[a] == ')') || (the_text[b]=='('))
    return true;
  else
    return false;
}

bool LlineClass::within_round_brackets(size_t the_pos)
{
//Desc: as the same named function above, but don't return before 
// and after positions of the brackets
 size_t a,b;
 return within_round_brackets(the_pos,b,a);
}

size_t LlineClass::matching_bracket(size_t the_pos)
{
//Desc: Return the position of the matching bracket of a pair, where the 
// position of one of the pair is "the_pos". Brackets can be [],{}, or ()
  size_t found;
  int    depth=1;
  switch (the_text[the_pos]) {
  case '{':
    found=the_pos;
    do {
      found=the_text.find_first_of("{}",found+1);
      if ((found==string::npos) || (found>=end_text)) return string::npos;
      if (the_text[found]=='{') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  case '}':
    found=the_pos;
    do {
      found=the_text.find_last_of("{}",found-1);
      if ((found==string::npos) || (found<2)) return string::npos;
      if (the_text[found]=='}') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  case '(':
    found=the_pos;
    do {
      found=the_text.find_first_of("()",found+1);
      if ((found==string::npos) || (found>=end_text)) return string::npos;
      if (the_text[found]=='(') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  case ')':
    found=the_pos;
    do {
      found=the_text.find_last_of("()",found-1);
      if ((found==string::npos) || (found<2)) return string::npos;
      if (the_text[found]==')') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  case '[':
    found=the_pos;
    do {
      found=the_text.find_first_of("[]",found+1);
      if ((found==string::npos) || (found>=end_text)) return string::npos;
      if (the_text[found]=='[') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  case ']':
    found=the_pos;
    do {
      found=the_text.find_last_of("[]",found-1);
      if ((found==string::npos) || (found<2)) return string::npos;
      if (the_text[found]==']') depth++;
      else                      depth--;
    } while (depth>0);
    return found;

  default:
    return string::npos;
  };
}

double LlineClass::assoc_param(const size_t the_pos, int& the_length)
{ 
//Desc: Returns the associated parameter, and also gives the number
// of characters (including brackets) of the bracketed number 
// associated with a character in the L string. This character 
// must be one of those listed in the bracketed_chars string. 
// Returns NaN if no parameter or a bad parameter.
  float param;
  int   conv,dummy;
  the_length=0;
  if (the_pos==info_length()-1) return sqrt(-1);
  if (bracketed_chars.find(the_text[the_pos])==string::npos) return sqrt(-1);

  conv=sscanf(&(the_text.data()[the_pos+1]),"(%f)%n",&param,&the_length);
  if (conv==sscanf("","%n",&dummy)) {
    the_length=0;
    return sqrt(-1);
  } else {
    return param;
  };
}

bool LlineClass::increment(size_t &the_pos)
{ 
//Desc: increments the_pos along an L string line , skipping 
// any parameters associated with an L string character (i.e.
// numbers in round brackets). Returns false if at the end of 
// the informative characters in the line.
  size_t bef,aft;
  switch (marker) {   
    //only look at RULE and AXIOM lines
  case USER_RULE:
  case FZ_RULE:
  case STANDARD_RULE:
  case AXIOM:
    break;
  default:
    the_pos=0;
    return false;
  };
  
  if (!(the_pos < end_text)) {
    the_pos=end_text;
    return false;
    
  } else {
    if ((bracketed_chars.find(the_text[the_pos])!=string::npos) &&
	(within_round_brackets(the_pos+1,bef,aft)))
      the_pos=aft;
    the_pos++;
    
    if (the_pos<=end_text) {
      return true;
    } else {
      the_pos=end_text;
      return false;
    };
  };
}

bool LlineClass::decrement(size_t &the_pos)
{
//Desc: as for LlineClass::increment, but decrements the_pos, 
// returning false if at the start of the informative characters
// (this is at pos 0 for the axiom, but pos 2 for rules).
  size_t first_pos,bef,aft;
  switch (marker) {
  case USER_RULE:
  case FZ_RULE:
  case STANDARD_RULE:
    first_pos=2;
    break;
  case AXIOM:
    first_pos=0;
    break;
  default:
    the_pos=0;
    return false;
  };

  the_pos--;
  if ((the_pos>first_pos) && (within_round_brackets(the_pos,bef,aft))) {
    the_pos = bef;
    if ((the_pos!=string::npos) &&
	(the_pos>first_pos)     && 
	(bracketed_chars.find(the_text[the_pos-1])!=string::npos))
      the_pos--;
  };
  
  if ((the_pos!=string::npos) && (the_pos>=first_pos))
    return true;
  else {
    the_pos=first_pos;
    return false;
  };
}


/*********************************************
 ******* Mutation Parameter methods  *********
 *********************************************/

MutationParameters::MutationParameters(  
  // defaults for these values are set in the corresponding header file
  unsigned int v_MaxMutTries,
  char         *v_DefaultNewRule,
  double       v_pr_R,
  double       v_R_stdev,
  double       v_pr_A,
  double       v_A_stdev,
  double       v_pr_T,
  double       v_T_stdev,
  double       v_pr_length_gain_brack,
  double       v_pr_length_lose_brack,
  double       v_length_stdev,
  double       v_length_decr_stdev, 
  double       v_pr_angle_gain_brack,
  double       v_pr_angle_lose_brack,
  double       v_angle_stdev,
  double       v_angle_decr_stdev,
  double       v_pr_thick_gain_brack,
  double       v_pr_thick_lose_brack,
  double       v_thick_decr_stdev,
  double       v_pr_colour_gain_brack,
  double       v_pr_colour_lose_brack,
  double       v_pr_trope_gain_brack,
  double       v_pr_trope_lose_brack,
  double       v_tr_stdev,
  double       v_pr_randrot_gain_brack,
  double       v_pr_randrot_lose_brack,
  double       v_randrot_stdev,
  double       v_pr_add_or_del_rule,
  double       v_pr_add_rule,
  double       v_pr_add_to_rule,
  double       v_new_brack_dist_stdev
  ):
  //allocate aliases
  pr_R                 (ParamArr[0]),
  R_stdev              (ParamArr[1]),
  pr_A                 (ParamArr[2]),
  A_stdev              (ParamArr[3]),
  pr_T                 (ParamArr[4]),
  T_stdev              (ParamArr[5]), 
  pr_length_gain_brack (ParamArr[6]),
  pr_length_lose_brack (ParamArr[7]),
  length_stdev         (ParamArr[8]),
  length_decr_stdev    (ParamArr[9]), 
  pr_angle_gain_brack  (ParamArr[10]),
  pr_angle_lose_brack  (ParamArr[11]),
  angle_stdev          (ParamArr[12]),
  angle_decr_stdev     (ParamArr[13]),
  pr_thick_gain_brack  (ParamArr[14]),
  pr_thick_lose_brack  (ParamArr[15]),
  thick_decr_stdev     (ParamArr[16]), 
  pr_colour_gain_brack (ParamArr[17]),
  pr_colour_lose_brack (ParamArr[18]),
  pr_trope_gain_brack  (ParamArr[19]),
  pr_trope_lose_brack  (ParamArr[20]),
  tr_stdev             (ParamArr[21]),
  pr_randrot_gain_brack(ParamArr[23]),
  pr_randrot_lose_brack(ParamArr[24]),
  randrot_stdev        (ParamArr[24]),
  pr_add_or_del_rule   (ParamArr[25]),
  pr_add_rule          (ParamArr[26]),
  pr_add_to_rule       (ParamArr[27]),
  new_brack_dist_stdev (ParamArr[28])
{
  //place parameters given into the aliases
  MaxMutTries           = v_MaxMutTries;
  DefaultNewRule        = v_DefaultNewRule;
  pr_R                  = v_pr_R;
  R_stdev               = v_R_stdev;
  pr_A                  = v_pr_A;
  A_stdev               = v_A_stdev;
  pr_T                  = v_pr_T;
  T_stdev               = v_T_stdev; 
  pr_length_gain_brack  = v_pr_length_gain_brack;
  pr_length_lose_brack  = v_pr_length_lose_brack;
  length_stdev          = v_length_stdev;
  length_decr_stdev     = v_length_decr_stdev; 
  pr_angle_gain_brack   = v_pr_angle_gain_brack;
  pr_angle_lose_brack   = v_pr_angle_lose_brack;
  angle_stdev           = v_angle_stdev;
  angle_decr_stdev      = v_angle_decr_stdev;
  pr_thick_gain_brack   = v_pr_thick_gain_brack;
  pr_thick_lose_brack   = v_pr_thick_lose_brack;
  thick_decr_stdev      = v_thick_decr_stdev; 
  pr_colour_gain_brack  = v_pr_colour_gain_brack;
  pr_colour_lose_brack  = v_pr_colour_lose_brack;
  pr_trope_gain_brack   = v_pr_trope_gain_brack;
  pr_trope_lose_brack   = v_pr_trope_lose_brack;
  tr_stdev              = v_tr_stdev;
  pr_randrot_gain_brack = v_pr_randrot_gain_brack;
  pr_randrot_lose_brack = v_pr_randrot_lose_brack;
  randrot_stdev         = v_randrot_stdev;
  pr_add_or_del_rule    = v_pr_add_or_del_rule;
  pr_add_rule           = v_pr_add_rule;
  pr_add_to_rule        = v_pr_add_to_rule;
  new_brack_dist_stdev  = v_new_brack_dist_stdev;
}

/*********************************************
 ************** Lstring methods **************
 *********************************************/

Lstring::Lstring(UIClass *ptr, MutationParameters *mut_param):
  UIClassPtr(ptr),
  FinalString(NULL),
  DefaultStdRule(NULL),
  MutParam(mut_param)
{
//Desc: constructor
  DEBUGGING_MESSAGE1("starting Lstring construction\n.\n.\n.\n");
  CountChar[127]='\0';     //CountChar should be an unsigned char 
  for (int i=0;i<127;i++)  //string with ascii 128, 129, 130 etc. 
    CountChar[i]=128+i;    //terminated (as all char strings) with \0
  GenerateStdRules();
  Clear();
  DEBUGGING_MESSAGE1("finished\n");  
}

Lstring::~Lstring()
{
  Clear();
  StdRule.clear();
}

Lstring &Lstring::operator=(Lstring& lstr)
{
  string temp;
  lstr >> temp;
  *this << temp;
  return (*this);
}

void Lstring::Clear()
{
DEBUGGING_MESSAGE1("Clearing\n");
  //clear the user lines
  for (vector<LlineClass *>::iterator it=line.begin(); 
       it!=line.end();
       ++it)
    delete *it;
DEBUGGING_MESSAGE1(".\n");
  line.clear();
DEBUGGING_MESSAGE1(".\n");
  user_rules="";
  num_axiom_and_rule_chars=0;
  line_ptr[REC_LINE]=NULL;
  line_ptr[ANG_LINE]=NULL;
  line_ptr[THICK_LINE]=NULL;
  line_ptr[AXIOM_LINE]=NULL;
DEBUGGING_MESSAGE1(".\n");
  //free the FinalString memory
  valid_string=false;
  if (FinalString!=NULL)
    free(FinalString);
  FinalString=NULL;
  FS_Lgth=0;
  FS_MemSize=0;
  FS_Lev=0;
DEBUGGING_MESSAGE1(".\n");
  SetStdRules();
DEBUGGING_MESSAGE1("ended\n");
}

void Lstring::GenerateStdRules()
{
  unsigned int i;
  char c3[4], c4[5];
  StdRule.clear();

  // Generate rules that replace symbols with themselves. This
  //allows fast parsing of an L string in the Parse() method.
  GenerateStdRule(".=.");
  GenerateStdRule("1=1");
  GenerateStdRule("2=2");
  GenerateStdRule("3=3");
  GenerateStdRule("4=4");
  GenerateStdRule("5=5");
  GenerateStdRule("6=6");
  GenerateStdRule("7=7");
  GenerateStdRule("8=8");
  GenerateStdRule("9=9");
  GenerateStdRule("0=0");
  GenerateStdRule("(=(");
  GenerateStdRule(")=)");

  GenerateStdRule("+=+");
  GenerateStdRule("-=-");
  GenerateStdRule("&=&");
  GenerateStdRule("^=^");
  GenerateStdRule("<=<");
  GenerateStdRule(">=>");

  GenerateStdRule("%=%");
  GenerateStdRule("|=|");
  GenerateStdRule("!=!");
  GenerateStdRule("?=?");
  GenerateStdRule(":=:");
  GenerateStdRule(";=;");
  GenerateStdRule("\'=\'");
  GenerateStdRule("\"=\"");
  GenerateStdRule("c=c");
  
  GenerateStdRule("[=[");
  GenerateStdRule("]=]");
  GenerateStdRule("{={");
  GenerateStdRule("}=}");
  
  GenerateStdRule("F=F");
  GenerateStdRule("f=f");

  GenerateStdRule("t=t");
  GenerateStdRule("g=g");
  GenerateStdRule("Z=Z");
  GenerateStdRule("z=z");
  GenerateStdRule("*=*");
  GenerateStdRule("$=$");

  GenerateStdRule("_=_");
  GenerateStdRule("`=`");

  //The rule ~ is the only one which incorporates randomness into 
  // the Lstring interpretation. This makes it complicated, as we 
  // want to be able to track the number of recursions which it has 
  // experienced, so that we can show the stages in its growth. We 
  // use the characters above #128 listed in the CountChars string.

  //replace ~ with `C where C is the first CountChar 
  sprintf(c4,"~=`%.1s",(char *) CountChar); 
  GenerateStdRule(c4);

  //on each pass of the parser, replace any CountChar with the next one
  for (i=1;CountChar[i]!='\0';i++){
    sprintf(c3,"%.1s=%.1s",&CountChar[i-1],&CountChar[i]);
    GenerateStdRule(c3);
  };

  //replace any instance of the last CountChar with the first CountChar 
  sprintf(c3,"%.1s=%.1s",&CountChar[i-1],&CountChar[0]);
  GenerateStdRule(c3);


  //Spaces in an Lstring are removed when parsing
  GenerateStdRule(" =");
  //Any unspecified character also gets removed
  DefaultStdRule=*StdRule.rbegin();
}

void Lstring::GenerateStdRule(char *the_rule)
{
  LlineClass *temp=new LlineClass(&StdRule);
  temp->assign(the_rule, STANDARD_RULE);
}

void Lstring::SetStdRules()
{
  //put a pointer to each rule in an array indexed by the first character
  for (unsigned int n=0;n<256;n++)
    SetRule(DefaultStdRule,(unsigned char) n);

  for (vector<LlineClass *>::iterator i=StdRule.begin();
       i!=StdRule.end();
       ++i)
    SetRule(*i);
}

/*******************************************
 ** methods used for forming the L string **
 *******************************************/

bool Lstring::InsertVariable(string &to_insert, var_line_label which)
{
//Desc: Insert one of the 3 variable lines (recursion, angle, 
// & thickness) into the Lstring object. Returns false if the
// line has not been inserted, if the the line is a comment, or
// if "which" turns out not to be one of the 3 variable lines
  string     the_line;
  LlineClass *VarLine;

  //grab a line from the string
  the_line=snip_line(to_insert);

  //if the line is empty, delete it
  if (the_line.length()==0) {
    UIClassPtr->Error(LINE_EMPTY_AND_IGNORED);
    return false;
  };

  //allocate a new line
  VarLine=new LlineClass(&line);
  //mark comments
  if (the_line[0]=='#') { 
    VarLine->assign(the_line,COMMENT);
    return false;
  };
  VarLine->assign(the_line,(line_label) which);
  switch (which) {
  case REC_VARIABLE:
    if (line_ptr[REC_LINE]!=NULL)
      UIClassPtr->ProgrammingError("Lstring::InsertVariable",
				   "recursion line already allocated");
    if (VarLine->is_float()==false) {
      UIClassPtr->Error(REC_LINE_BAD);
      VarLine->assign(default_rec_string,RECURSION);
    };
    line_ptr[REC_LINE]=VarLine;
    break;

  case ANG_VARIABLE:
    if (line_ptr[ANG_LINE]!=NULL)
      UIClassPtr->ProgrammingError("Lstring::InsertVariable",
				   "angle line already allocated");
    if (VarLine->is_float()==false) {
      UIClassPtr->Error(ANG_LINE_BAD);
      VarLine->assign(default_ang_string,ANGLE);
    };
    
    line_ptr[ANG_LINE]=VarLine;
    break;
    
  case THICK_VARIABLE:
    if (line_ptr[THICK_LINE]!=NULL)
      UIClassPtr->ProgrammingError("Lstring::InsertVariable",
				   "thickness line already allocated");
    if (VarLine->is_float()==false) {
      UIClassPtr->Error(THICK_LINE_BAD);
      VarLine->assign(default_thick_string,THICKNESS);
    };
    
    line_ptr[THICK_LINE]=VarLine;
    break;
  };
  return true;
}


bool Lstring::InsertAxiom(string &to_insert)
{ 
//Desc: insert an axiom line into the Lstring object. 
// Returns false if comment or empty line, true otherwise

  LlineClass *AxiomLine;
  string the_line;

  //mess with the string
  the_line=snip_line(to_insert);
  if (the_line.length()==0) {
    UIClassPtr->Error(LINE_EMPTY_AND_IGNORED);
    return false;
  };
  
  AxiomLine = new LlineClass(&line);
  
  if  (the_line[0]=='#') {
    AxiomLine->assign(the_line, COMMENT);
    return false;
  } else {
    if (line_ptr[AXIOM_LINE]!=NULL)
      UIClassPtr->ProgrammingError("Lstring::InsertAxiom",
				   "axiom line already allocated");
    line_ptr[AXIOM_LINE]=AxiomLine;
    if (ReplaceAxiom(the_line)==false) 
      {
	UIClassPtr->ProgrammingError("Lstring::InsertAxiom",
				     "error replacing axiom");
	return false;
      };
  };
  return true;
}

bool Lstring::allocate_new_rule_char(string &rule)
{
//Desc: replace the first character in a string by a new
// character, and also replace occurences of the old character
// in the string. If no new character can be found, return false
  size_t pos     =0;
  size_t new_rule=new_rule_chars.find_first_not_of(user_rules);
  if (new_rule==string::npos) return false;
  char   old_char=rule[0], new_char=new_rule_chars[new_rule];
  
  while ( (pos=rule.find(old_char,pos))!=string::npos)
    rule.replace(pos,1,new_char,1);
  return true;
}

bool Lstring::InsertUserRule(string &to_insert)
{ 
//Desc: Insert a rule into the L string object.
//      Returns true if a rule or comment line is inserted
  LlineClass   *the_new_rule;
  string       the_line;

  //get a line
  the_line=snip_line(to_insert);

  if (the_line.length()==0) {
    UIClassPtr->Error(LINE_EMPTY_AND_IGNORED);
    return false;
  };
  
  if (the_line[0]=='@')  //indicates that this is a new rule
    if (allocate_new_rule_char(the_line)==false) {
      UIClassPtr->Error(OUT_OF_RULE_CHARS);
      return false;
    };

  the_new_rule = new LlineClass(&line);
  
  //make new Lline
  switch (the_line[0]) {
  case '#':
    the_new_rule->assign(the_line, COMMENT);
    return true;
    
  case 'F':
  case 'f':
  case 'Z':
  case 'z':
    the_new_rule->assign(the_line, FZ_RULE);
    break;
    
  default:
    the_new_rule->assign(the_line, USER_RULE);
  };
  
  //set up other stuff
  SetUserRule(the_new_rule);
  
  return true;
}

/****************************************************
 ****** methods for changing a current L string *****
 ****************************************************/

bool Lstring::ReplaceVariable(const string new_value, 
			      const var_line_label which)
{
//Desc: changes the string in one of the 3 variable lines
  if (line_ptr[(single_line_label) which]==NULL)
    return false;
  line_ptr[(single_line_label) which]
    ->assign(new_value,(line_label) which);
  return true;
}

bool Lstring::ReplaceVariable(const float new_value, const var_line_label which)
{
//Desc: changes the value in one of the 3 variable lines to a float
// whilst keeping any comments that were there previously
  
  string tempStr;
  char   tempChr[40];
  
  tempStr=line_ptr[(single_line_label) which]->text();
  snprintf(tempChr,39,"%f",new_value);
  tempStr.replace(0, line_ptr[(single_line_label) which]->info_length(),tempChr);
  
  return ReplaceVariable(tempStr, which);
}

bool Lstring::ReplaceVariable(const int new_value, const var_line_label which)
{
//Desc: changes the value in one of the 3 variable lines to an int
// whilst keeping any comments that were there previously
  
  string tempStr;
  char   tempChr[40];
  
  tempStr=line_ptr[(single_line_label) which]->text();
  snprintf(tempChr,39,"%i",new_value);
  tempStr.replace(0, line_ptr[(single_line_label) which]->info_length(),tempChr);
  
  return ReplaceVariable(tempStr, which);
}

bool Lstring::ReplaceAxiom(const string to_insert)
{
//Desc: replaces the axiom line with a new string. Returns false
// if there is no previous axiom string, or if the replacement
// is blank or a comment
  if (line_ptr[AXIOM_LINE]==NULL) return false;
  if (to_insert.length()==0)    return false;
  if (to_insert[0]=='#')        return false;

  num_axiom_and_rule_chars-=line_ptr[AXIOM_LINE]->info_length();
  line_ptr[AXIOM_LINE]->assign(to_insert,AXIOM);
  num_axiom_and_rule_chars+=line_ptr[AXIOM_LINE]->info_length();
  return true;
}

void Lstring::SetRule(LlineClass *the_rule, unsigned char the_pos)
{ 
//Desc: simply point the RuleForChar array at the_rule
  RuleForChar[the_pos]=the_rule;
}

void Lstring::SetRule(LlineClass *the_rule, char the_pos)
{
//Desc: as above, but for a signed char variable
  unsigned char arr_index;
  memcpy(&arr_index,&the_pos,1);
  SetRule(the_rule,arr_index);
}

void Lstring::SetRule(LlineClass *the_rule)
{
//Use the first character of "the_rule" as the index in RuleForChar
  SetRule(the_rule, the_rule->first_char());
}

bool Lstring::SetUserRule(LlineClass *the_rule)
{
//Desc: add "the_rule" to the RuleForChar array and augment the 
// number of mutable characters.
  if ((the_rule->get_marker()!=USER_RULE) &&
      (the_rule->get_marker()!=FZ_RULE))
    return false;
  
  num_axiom_and_rule_chars += the_rule->info_length()-2;
  user_rules        += the_rule->first_char();
  SetRule(the_rule);
  return(true);
}

//Methods to change the text value of lines in an L string

bool Lstring::UnsetUserRule(LlineClass *the_rule)
{
//Desc: remove the rule from the RuleForChar array
  LlineClass *replacement_rule=DefaultStdRule;
  if ((the_rule->get_marker()!=USER_RULE) &&
      (the_rule->get_marker()!=FZ_RULE))
    return false;

  //find the appropriate Standard Rule, if any
  for (vector<LlineClass *>::iterator i=StdRule.begin();i!=StdRule.end();++i)
    if ((*i)->first_char()==the_rule->first_char()) {
      replacement_rule=*i;
      break;
    };
  
  user_rules.replace(user_rules.find(the_rule->first_char()),1,"");
  num_axiom_and_rule_chars-=the_rule->info_length() - 2;
  SetRule(replacement_rule, the_rule->first_char());
  return true;
}

void Lstring::DeleteUserRule(LlineClass *to_delete)
{
//Deletes a rule from the L string
  UnsetUserRule(to_delete);
  delete to_delete;
  vector<LlineClass *>::iterator i=line.begin(); 
  while ((*i)!=to_delete)
    ++i;
  line.erase(i);
}


bool Lstring::ReplaceUserRule(string to_insert, LlineClass *to_replace)
{
  to_insert=snip_line(to_insert);
  
  if (to_insert.length()==0) {
    UIClassPtr->Error(LINE_EMPTY_AND_IGNORED);
    return false;
  };
  
  //check that the rule name isn't duplicated
  if ((to_insert[0]!=to_replace->first_char()) &&
      (user_rules.find(to_insert[0]) != string::npos)) {
    UIClassPtr->Error(USER_RULE_DUPLICATION);
    return false;
  };
  UnsetUserRule(to_replace);
  
  switch (to_insert[0]) {
  case '#':
    to_replace->assign(to_insert, COMMENT);
    break;
    
  case 'F':
  case 'f':
  case 'Z':
  case 'z':
    to_replace->assign(to_insert, FZ_RULE);
    break;
    
  default:
    to_replace->assign(to_insert, USER_RULE);
  };
  SetUserRule(to_replace);
  return true;
}

/********************************************
 **** Mutation Routines etc
 ********************************************/

char Lstring::pick_axiom_or_rule_char(const bool random_line, vector<LlineClass *>::iterator &ln_itr, int &char_pos)
{
//Desc: picks a random character in the Lstring, either from the Axiom,
// or from the right hand side of a rule, and returns the character. 
// The returned character may also be '\0', which indicates that the 
// end of a line has been picked. The line in which the character 
// resides is returned in "line_itr", and the position in that line 
// is returned in "char_pos".
// If "random_line" is false, the character is picked randomly from
// all possible characters. If "random_line" is true, a line is picked
// at random, and a character then picked at random from that line. This
// second method of picking a character will not bias mutation towards
// occuring in longer rules.

  if (valid_string==false)
    UIClassPtr->ProgrammingError("Lstring::pick_axiom_or_rule_char",
				 "not a valid string");

  //point line_itr to the axiom
  ln_itr=line.begin();
  while  (*ln_itr != line_ptr[AXIOM_LINE])
    ++ln_itr;
  
  // if there are no characters in the axiom or rules, point to the 
  //end of the (presumably empty) axiom
  if (num_axiom_and_rule_chars==0) {
    char_pos=0;
    return '\0';
  }
  
  vector<LlineClass *>:: iterator ln_itr2=ln_itr;
  int                             ln_cnt=0, lsize, rand_pos, rand_ln;

  //otherwise, count the number of non-comment lines
  while (ln_itr2!=line.end()) {
    if ((*ln_itr2)->get_marker()!=COMMENT)
      ln_cnt++;
    ++ln_itr2;
  };

DEBUGGING_MESSAGE2("(there are %i mutable lines)",ln_cnt);
  //Initially set the line size to that of the Axiom line + 1 
  //(to include the end of line character) 
  lsize  =line_ptr[AXIOM_LINE]->info_length()+1;
DEBUGGING_MESSAGE1(".");
  if (random_line) {
DEBUGGING_MESSAGE1("rl");
    // *********** Pick a line at random...
    rand_ln=static_cast<int>(floor(Rnd()*ln_cnt));
    while (rand_ln!=0) {
      ++ln_itr;
      if (((*ln_itr)->get_marker()!=COMMENT) &&
	  ((*ln_itr)->info_length()>=2) ){
	//reset line size to RHS of rule + end char
	lsize=(*ln_itr)->info_length()-1; 
	rand_ln--;
      };
    };
    rand_pos = static_cast<int>(floor(Rnd() * lsize)); //rnd char in line
    
    
  } else {          
DEBUGGING_MESSAGE1("rc");
    // ************ Or pick a random character from the entire string 
    rand_pos = static_cast<int>(floor(Rnd()*num_axiom_and_rule_chars+ln_cnt));
    while (rand_pos>=lsize) {
      ++ln_itr;
      if (((*ln_itr)->get_marker()!=COMMENT) &&
	  ((*ln_itr)->info_length()>=2) ){
	rand_pos-=lsize;
	//reset line size to RHS of rule + end char
	lsize=(*ln_itr)->info_length()-1;
      };
    };
  };
DEBUGGING_MESSAGE1(".");
  //Now finish off
  char_pos = (*ln_itr)->info_length()-rand_pos;//pick char from end
DEBUGGING_MESSAGE1(".");  
  if (rand_pos==0) //end of line
    return '\0';
  else
    return (*ln_itr)->char_num(char_pos);
}

void Lstring::mutate_General_Parameter()
{
DEBUGGING_MESSAGE1("Mutating parameter\n");
  if (Rnd()<(MutParam->pr_R + MutParam->pr_A + MutParam->pr_T))
    mutate_Variable();
  else
    mutate_ChangeRuleParameter();
}

void Lstring::mutate_General_Algorithm()
{
  float r1=Rnd(), r2=Rnd();
DEBUGGING_MESSAGE3("Mutating algorithm (r1=%f, r2=%f)\n",r1,r2);
 DEBUGGING_MESSAGE2("mp=%f\n",MutParam->pr_add_or_del_rule);
  if (r1<MutParam->pr_add_or_del_rule) {
    if (r2<MutParam->pr_add_rule) 
      mutate_AddRule();
    else
      mutate_DeleteRule();
  } else {
    if (r2<MutParam->pr_add_to_rule)
      mutate_ChangeRuleAdd();
    else
      mutate_ChangeRuleDelete();
  };
}

void Lstring::mutate_Variable()
{
DEBUGGING_MESSAGE1("Mutating variable: ");
  r32   randnum=Rnd()*(MutParam->pr_R+MutParam->pr_A+MutParam->pr_T);
  if (randnum<MutParam->pr_R) {
    //mutate recursion level
DEBUGGING_MESSAGE1("recursion parameter augmented by");
    if (this->growing()) {
      float v, min=0, max=LENGTH_MAX;
      int   i=MutParam->MaxMutTries;
      while (i--) {
	v = N(0,MutParam->R_stdev);  
DEBUGGING_MESSAGE2(" %f:",v);

	v+= this->get_lev()-1 + this->get_fraction();
	
	if ((v >= min) && (v <= max)) {
	  ReplaceVariable(v, REC_VARIABLE);
DEBUGGING_MESSAGE1("suceeded!");
	  break;
	} else {
DEBUGGING_MESSAGE1("failed;");
	};
      };
    } else {
      int v, min=0, max=(int) LENGTH_MAX;
      int i=MutParam->MaxMutTries;
      while (i--) {
	v=(int) (N(0,MutParam->R_stdev));
DEBUGGING_MESSAGE2(" %i:",v);
	v += this->get_lev();
	
	if ((v >= min) && (v <= max)) {
	  ReplaceVariable(v, REC_VARIABLE);
DEBUGGING_MESSAGE1("suceeded!");
	  break;
	} else {
DEBUGGING_MESSAGE1("failed;");
	};
      };
    };
  } else if (randnum< (MutParam->pr_R+MutParam->pr_A)) {
    //mutate angle
    float v, min=ANGLE_MIN, max=ANGLE_MAX;
    int   i=MutParam->MaxMutTries;
DEBUGGING_MESSAGE1("default angle parameter augmented by");
    while (i--) {
      v=N(0,MutParam->A_stdev);
DEBUGGING_MESSAGE2(" %f:",v);
      v += this->get_ang();
      
      if ((v >= min) && (v <= max)) {
	ReplaceVariable(v, ANG_VARIABLE);
DEBUGGING_MESSAGE1("suceeded!");
	break;
      } else {
DEBUGGING_MESSAGE1("failed;");
      };
    };
  } else {

    //mutate thickness
    float v, min=0, max=THICK_MAX;
    int   i=MutParam->MaxMutTries;
DEBUGGING_MESSAGE1("starting thickness parameter augmented by");
    while (i--) {
      v=N(0,MutParam->T_stdev);
DEBUGGING_MESSAGE2(" %f:",v);
      v += this->get_thick();
      if ((v >= min) && (v <= max)) {
	ReplaceVariable(v, THICK_VARIABLE);
DEBUGGING_MESSAGE1("suceeded!");
	break;
      } else {
DEBUGGING_MESSAGE1("failed;");
      };
    };
  };
DEBUGGING_MESSAGE1("\n");
}

void Lstring::mutate_AddRule()
{
DEBUGGING_MESSAGE1("Mutating : rule added\n");
  string new_rule=new_rule_start;
  new_rule += MutParam->DefaultNewRule;
  InsertUserRule(new_rule);
}

bool Lstring::mutate_DeleteRule()
{
DEBUGGING_MESSAGE1("Mutating : rule deleted\n");
  if (user_rules.length()==0) return false;

  string temp;
  size_t d_pos;
  int   to_del = static_cast<int>(floor(Rnd()*user_rules.length()));
  char  rule_to_del       = user_rules[to_del];
  LlineClass *line_to_del = RuleForChar[rule_to_del];
  
  DeleteUserRule(line_to_del);
  //delete references to this rule in other rules or in the axiom
  
  vector<LlineClass *>::iterator line_itr = line.begin();
  while  (*line_itr != line_ptr[AXIOM_LINE])
    ++line_itr;

  while (line_itr!=line.end()){
    temp=(*line_itr)->text();
    if ((d_pos=temp.find(rule_to_del))!=string::npos) {
      do {
	temp.replace(d_pos,1,"");
	d_pos=temp.find(rule_to_del,d_pos);
      } while (d_pos!=string::npos);
      if (*line_itr==line_ptr[AXIOM_LINE])
	ReplaceAxiom(temp);
      else
	ReplaceUserRule(temp,*line_itr);
    };
    ++line_itr;
  };
  return true;
}

void Lstring::mutate_ChangeRuleDelete()
{
  vector<LlineClass *>::iterator line_itr;
  int                 char_pos;
  char                found_char;
  bool                searching=true;
  string              tempStr;
  for (unsigned int i=0;i<MutParam->MaxMutTries;i++) {
DEBUGGING_MESSAGE1("Mutating : removing character ");
    found_char = pick_axiom_or_rule_char(false, line_itr, char_pos);
    tempStr    = (*line_itr)->text();
DEBUGGING_MESSAGE3("from %s at position %i, ",  (line_ptr[AXIOM_LINE]==(*line_itr)) ? "Axiom" : (string("Rule ")+(*line_itr)->first_char()).c_str(), char_pos);
    if (found_char=='\0') {
    } else if (found_char=='.') {
      if ((*line_itr)->within_round_brackets(char_pos) == false) {
	searching=false;
	break;
      };
    } else if (mutable_chars.find(found_char) != string::npos) {
      searching=false;
      break;
    };
DEBUGGING_MESSAGE2("should not remove \"%c\" - trying again\n",found_char); 
  };
  
  size_t c=0;
  if (searching==false) 
    {
DEBUGGING_MESSAGE2("removing char %c\n",found_char); 
      if ((found_char=='{') || (found_char == '[')) {
	c=(*line_itr)->matching_bracket(char_pos);
	if (c!=string::npos)
	  {
	    tempStr.replace(c,1,"");
	    tempStr.replace(char_pos,1,"");
	  };

      } else {
	int length;
	(*line_itr)->assoc_param(char_pos, length);
	tempStr.replace(char_pos, 1+length, "");
      };
      
      if ((*line_itr)==line_ptr[AXIOM_LINE])
	ReplaceAxiom(tempStr);
      else
	ReplaceUserRule(tempStr,*line_itr);
    };
}

void Lstring::mutate_ChangeRuleAdd()
{
  unsigned int r_num;
  int                              insertion_point=-1;  
  LlineClass                       *insertion_line=NULL;
  vector<LlineClass *>::iterator   line_itr;

  for (unsigned int i=0;i<MutParam->MaxMutTries;i++) {
DEBUGGING_MESSAGE1("Mutating : adding character ");
    pick_axiom_or_rule_char(true, line_itr, insertion_point);
DEBUGGING_MESSAGE3("to %s at position %i, ", (line_ptr[AXIOM_LINE]==(*line_itr)) ? "Axiom" : (string("Rule ")+(*line_itr)->first_char()).c_str(), insertion_point);    
    if ((insertion_point==(*line_itr)->info_length()) ||
	((*line_itr)->within_round_brackets(insertion_point)==false)) {

      //now into the serious body of the function.
      //first pick a character to insert
      string poss_new_chars = length_chars+angle_chars+colour_chars+
	                      thick_chars+extra_chars+user_rules+"[{";
      char   char_picked    = poss_new_chars[static_cast<int>(floor(Rnd()*poss_new_chars.length()))];
DEBUGGING_MESSAGE2(" picked %c to add, ",char_picked);
      //insert the character(s)
      size_t other_bracket;
      int    move_chars;
      string tempStr      =(*line_itr)->text();
      char   corresponding_bracket='}';
      
      //tackle the situation where we are inserting an opening bracket
      //this is complicated, since we need to add the closing bracket too
      switch (char_picked) {
      case '[':
	corresponding_bracket=']';
      case '{':
	other_bracket = insertion_point;
	move_chars=static_cast<int>((N(0,MutParam->new_brack_dist_stdev)));
DEBUGGING_MESSAGE2("bracket distance is %i",move_chars);
        if (move_chars<0) {
	  while ((move_chars) && ((*line_itr)->decrement(other_bracket)))
	    move_chars++;
	  tempStr.insert(insertion_point,1,corresponding_bracket);
	  tempStr.insert(other_bracket,1,char_picked);
	} else {
	  while ((move_chars) && ((*line_itr)->increment(other_bracket)))
	    move_chars--;
	  tempStr.insert(other_bracket,1,corresponding_bracket);
	  tempStr.insert(insertion_point,1,char_picked);
	};
DEBUGGING_MESSAGE2("other bracket inserted at pos %i\n",other_bracket);
        break;
      
      default:
	tempStr.insert(insertion_point,1,char_picked);
DEBUGGING_MESSAGE1("\n");
      };
      
      if ((*line_itr)==line_ptr[AXIOM_LINE])
	ReplaceAxiom(tempStr);
      else
	ReplaceUserRule(tempStr,*line_itr);

      return;
    } else {
DEBUGGING_MESSAGE1(" tried to add within () - trying again\n");
    };
  }; //end i (loop to try to find a valid char) 
}

void Lstring::mutate_ChangeRuleParameter()
{
  vector<LlineClass *>::iterator  theFoundLine;
  int                             i;
  int                             theFoundPos, theParamLength;
  char                            theFoundChar;
  float                           theFoundParam,min=0,max=0;
  float                           pr_lose=0,pr_gain=0,base_val=0,stdev=0;

  //pick a character
  i=MutParam->MaxMutTries;
  while(i--) {
DEBUGGING_MESSAGE1("Mutating : changing parameter ");
    //make sure it is one that can have a parameter
    theFoundChar=pick_axiom_or_rule_char(false,theFoundLine,theFoundPos);
DEBUGGING_MESSAGE2("in %s ", (line_ptr[AXIOM_LINE]==(*theFoundLine)) ? "Axiom" : (string("Rule ")+(*theFoundLine)->first_char()).c_str());
    if (bracketed_chars.find(theFoundChar)!=string::npos)
      break;
DEBUGGING_MESSAGE3("at pos %i, char %c rejected.\n" ,theFoundPos, theFoundChar);
  };
  if (i<=0) return;
DEBUGGING_MESSAGE3("at pos %i, char %c accepted. " ,theFoundPos, theFoundChar);

  
  /******* length character *******/
  if        (length_chars.find(theFoundChar)!=string::npos) {
    pr_gain = MutParam->pr_length_gain_brack;
    pr_lose = MutParam->pr_length_lose_brack;
    if      (full_length_chars.find(theFoundChar)!=string::npos) {
      stdev    = MutParam->length_stdev;
      min=0; max=LENGTH_MAX;
      base_val = UIClassPtr->get_dis();
    } else if (half_length_chars.find(theFoundChar)!=string::npos) {
      stdev    = MutParam->length_stdev/2.0;
      min=0; max=LENGTH_MAX;
      base_val = UIClassPtr->get_dis()/2.0;
    } else {
      stdev    = MutParam->length_decr_stdev;
      min=0; max=LENGTH_MAX;
      if      (increment_length_chars.find(theFoundChar)!=string::npos)
	base_val = 1.0/LENGTH_DECR;
      else if (decrement_length_chars.find(theFoundChar)!=string::npos)
	base_val = LENGTH_DECR;
      else 
	UIClassPtr->ProgrammingError("Lstring::mutate_ChangeRuleParameter","cannot find length character");
    };  
    /******* angle character *******/
  } else if (angle_chars.find(theFoundChar)!=string::npos) {
    pr_gain = MutParam->pr_angle_gain_brack;
    pr_lose = MutParam->pr_angle_lose_brack;
    if        (other_angle_chars.find(theFoundChar)!=string::npos) {
      stdev    = MutParam->angle_stdev;
      min=ANGLE_MIN; max=ANGLE_MAX;
      base_val=get_ang();
    } else {
      stdev    = MutParam->angle_decr_stdev;
      min=0; max=ANGLE_MAX;
      if      (decrement_angle_chars.find(theFoundChar)!=string::npos)
	base_val = ANGLE_DECR;
      else if (increment_angle_chars.find(theFoundChar)!=string::npos)
	base_val=1.0/ANGLE_DECR;
      else
	UIClassPtr->ProgrammingError("Lstring::mutate_ChangeRuleParameter",
				     "cannot find angle character");
    };
  /******* thickness character *******/
  } else if (thick_chars.find(theFoundChar)!=string::npos) {
    pr_gain=MutParam->pr_thick_gain_brack;
    pr_lose=MutParam->pr_thick_lose_brack;

    stdev    = MutParam->thick_decr_stdev;
    min=0; max=THICK_MAX;
    if      (increment_thick_chars.find(theFoundChar)!=string::npos)
      base_val=1.0/THICK_DECR;
    else if (decrement_thick_chars.find(theFoundChar)!=string::npos)
      base_val=THICK_DECR;
    else
      UIClassPtr->ProgrammingError("Lstring::mutate_ChangeRuleParameter","cannot find thickness character");
    
  /******* colour character *******/
  } else if (colour_chars.find(theFoundChar)!=string::npos) {
    pr_gain = MutParam->pr_colour_gain_brack;
    pr_lose = MutParam->pr_colour_lose_brack;
    base_val  = 2;
    stdev     = 0;
    min=0; max=COLOUR_MAX;
    
    /******* trope character *******/
  } else if (trope_chars.find(theFoundChar)!=string::npos) {
    pr_gain = MutParam->pr_trope_gain_brack;
    pr_lose = MutParam->pr_trope_lose_brack;
    base_val  = UIClassPtr->get_tr();
    stdev     = MutParam->tr_stdev;
    min=0; max=1;

  /******* ~ character *******/
  } else if (randrot_chars.find(theFoundChar)!=string::npos) {
    pr_gain = MutParam->pr_randrot_gain_brack;
    pr_lose = MutParam->pr_randrot_lose_brack;
    base_val  = UIClassPtr->rand_amount();
    stdev     = MutParam->randrot_stdev;
    min=0; max=180;
  } else {
    char msg[]="\"*\" character found - don't know how to add or delete a parameter!";
    msg[1]=theFoundChar;
    UIClassPtr->ProgrammingError("Lstring::mutate_ChangeRuleParameter",msg);
  };

  //replace base by user specified parameter, if present
  theFoundParam=(*theFoundLine)->assoc_param(theFoundPos,theParamLength);
  if (theParamLength>2) 
    base_val = theFoundParam;

  //make a copy of the line
  string tempStr =(*theFoundLine)->text();  
  
  //does it have a parameter
  float r=Rnd();
DEBUGGING_MESSAGE2("(r=%f) ",r);
  if        ( ( theParamLength>=3) && (r<pr_lose)) {         //remove
    tempStr.replace(theFoundPos+1,theParamLength,"");
DEBUGGING_MESSAGE1("parameter removed\n");
  } else if ( ((theParamLength>=3) && (r>pr_lose)) ||
	      ((theParamLength< 3) && (r<pr_gain)) ) {       //change
      float param;
      int   i=MutParam->MaxMutTries;
DEBUGGING_MESSAGE1("Parameter changed by");
      while(i--) {
	param =  N(0,stdev);
DEBUGGING_MESSAGE2(" %f: ",param);
	param += base_val;
	if ((param>=min) && (param<=max)) {
	  char pStr[50];
	  if (colour_chars.find(theFoundChar)!=string::npos) 
	    snprintf(pStr, 49, "(%i)",(int) param);
	  else
	    snprintf(pStr, 49, "(%f)",param);
	  tempStr.replace(theFoundPos+1,theParamLength,pStr);
DEBUGGING_MESSAGE1("suceeded!");
	  break;
	} else {
DEBUGGING_MESSAGE1("failed; ");
	};
      };
  } else {
DEBUGGING_MESSAGE1("Nothing done");
  };  

DEBUGGING_MESSAGE1("\n");

  if ((*theFoundLine)==line_ptr[AXIOM_LINE])
    ReplaceAxiom(tempStr);
  else
    ReplaceUserRule(tempStr,*theFoundLine);
}


/************************
 **** Public methods ****
 ************************/

void Lstring::operator<<(string& p)
{
//Desc: read a text string into the Lstring class
DEBUGGING_MESSAGE1("Starting to read an Lstring in\n");
  string     tempStr1, tempStr2;
  line_label i   = RECURSION;
  
  Clear();
DEBUGGING_MESSAGE1("Stripping text\n");
  //strip the last @ from the string and any following text.
  tempStr1=p.substr(0,p.find('@'));
  //strip trailing newlines
  tempStr2=tempStr1.substr(0,p.find_last_not_of(endline_markers));

  while (tempStr2.length()!=0) {
DEBUGGING_MESSAGE2("i is %i.\n",(int) i);
    switch (i) {
    case RECURSION:
    case ANGLE:
    case THICKNESS:
      if (InsertVariable(tempStr2, (var_line_label) i)!=false)
	i=static_cast<line_label>(i+1);  //advance i
      break;

    case AXIOM:
      if (InsertAxiom(tempStr2)==true) {
	valid_string=true;
	i=USER_RULE;
      };
      break;

    case USER_RULE:
      InsertUserRule(tempStr2);
      break;

    default:
      UIClassPtr->ProgrammingError("Lstring::operator<<",
				   "bad case in switch statement");
      break;
    };
  };
DEBUGGING_MESSAGE1("finished\n");
}

void Lstring::operator>>(string& out)
{
  //Desc: output the L string description stored in the Lstring object
  for (vector<LlineClass *>::iterator i=line.begin();
       i!=line.end(); 
       ++i) {
    out += ((*i)->text());
    out += "\n"; 
  };
  out += "@";
  out += "\n";
}

bool Lstring::Parse(void)
{
  return this->Parse(-1);
}

bool Lstring::FS_alloc(size_t mem)
{
  void *new_mem=realloc(FinalString,mem);
  if (new_mem) {
    FinalString=(unsigned char *) new_mem;
    FS_MemSize=mem;
    return true;
  } else {
    UIClassPtr->Error(OUT_OF_MEMORY);
    return false;
  }
}

//quick work around

inline size_t Max(size_t i1, size_t i2)
{
  if (i1>i2) return i1;
  else       return i2;
}

bool Lstring::Parse(const s16 reclevel)
{
//Desc: Expand l-system into production string
  size_t          k,remaining,s;
  void            *new_mem;
  s16             l, lev = get_lev();
  LlineClass      *i;
  bool            marker = false, fractional_recursion;

DEBUGGING_MESSAGE2("###Parsed %p!###\n", this);
  
  if (valid_string==false) {
    return false;
  };
  
  //store the actual number of recursions done in FS_Lev
  if ((reclevel<0) || (reclevel==lev)) {  // use the Lstring rec level
    fractional_recursion=growing();
    FS_Lev = lev;
  } else if (lev>reclevel) {              // use a lower rec level
    fractional_recursion=false;
    FS_Lev = reclevel;
  } else {   // a rec level higher than that defined has been requested
    return false;
  };
DEBUGGING_MESSAGE2("Recursion level used is %i\n",FS_Lev);
DEBUGGING_MESSAGE1("Allocating memory for FinalString\n");
  //allocate FinalString and so forth (assumes 'char' is 1 byte)
  FS_Lgth=0;
  //initially allocate FS_Chunk of memory if we have none
  if (FinalString==NULL)
    if (FS_alloc(FS_Chunk)==false)
      return false;
  //see if we need more memory for the axiom
DEBUGGING_MESSAGE1("Allocating more memory for FinalString\n");
  k=line_ptr[AXIOM_LINE]->info_length();
  if (FS_alloc(Max(k+1,FS_MemSize))==false)
    return false;

DEBUGGING_MESSAGE1("Copying into FinalString\n");
  strncpy((char *) FinalString, 
	  line_ptr[AXIOM_LINE]->text().c_str(), 
	  k);
  FinalString[k]='\0'; //make sure string is terminated
DEBUGGING_MESSAGE2("Got here, final string is %s\n",(char *) FinalString);

  //*******************************
  //******* Start parsing *********
  //*******************************
  remaining=k; //count of characters remaining to parse
  for (l = 0; l < FS_Lev; l++) {
DEBUGGING_MESSAGE2("FS: %s\n", FinalString);
    k=0;
    marker = ((l == (FS_Lev - 1)) && fractional_recursion); 
        
    //start a single pass of the parser: k is the current parse position
    while (FinalString[k]!='\0') {
      i  = RuleForChar[FinalString[k]]; // i points to a replacement rule
      s  = i->info_length();            // s is total length of rule

DEBUGGING_MESSAGE1(".");

      //longer string needed
      if ((k+s+remaining+2) > FS_MemSize) {
	if (FS_alloc(FS_MemSize+FS_Chunk)==false) {
	  free(FinalString);
	  FS_MemSize=0;
	  FS_Lgth=0;
	  return false;
	};
      };
      

      //replace character with rule, surrounded by the @ character
      //if this is the last round of recursion, and a fractional
      //recursion level has been requested.
      if (marker && (i->get_marker()==USER_RULE)) {
	memmove(&FinalString[k+s],&FinalString[k+1],remaining);
	FinalString[k]='@';
	memcpy(&FinalString[k+1],i->rule_start(),s-2);
	FinalString[k+s-1]='@';
	k+=s;
      } else {
	memmove(&FinalString[k+s-2],&FinalString[k+1],remaining);
	memcpy(&FinalString[k],i->rule_start(),s-2);
	k+=s-2;
      };

      remaining --;                     // one character down      
    }; //end of a single parse
DEBUGGING_MESSAGE1("\n");
    remaining=k; //reset the count of remaining characters
  }; //end of series of parses
  FS_Lgth=k;
DEBUGGING_MESSAGE2("Final length is %i\n",FS_Lgth);
  return true;
}


/******************************************
 ******* Methods to get information *******
 ******************************************/

bool  Lstring::Get_value(unsigned long &curr_pos, r32 &val)
{                 
//Desc: reads an (xx) value from a production string at the curr_pos,
// and make it into a real. Then advance curr_pos to the next position.
// Returns true if there is such a bracketed value.

  int                     characters_read = 0;
  if (curr_pos>=FS_Lgth) 
    return false;
  if (FinalString[curr_pos]=='(')
    sscanf((char *) &(FinalString[curr_pos]), "(%f)%n", &val, &characters_read);
  
  curr_pos += characters_read;
  if (characters_read>2)
    return true;
  else
    return false;
}

int Lstring::Get_RecursionChar(unsigned long &s_pos)
{ 
//Desc: if the character at position s_pos in the FinalString is a
// CountChar, return the CountChar number and advance s_pos by one.
  if (s_pos<=FS_Lgth) {
    int i=0;
    unsigned char theChar = FinalString[s_pos];
    while (CountChar[i]!='\0')
      if (CountChar[i++]==theChar) break;
    if (i!='\0') {
      s_pos++;
      return i;
    };
  };
  return 0;
};

string Lstring::FormattedFinalString()
{
//Desc: return the FinalString, minus any CountChars
  string fs=(char *) FinalString;
  string ss=(char *) CountChar;
  size_t pos=0;
  while (pos!=string::npos) {
    pos=fs.find_first_of(ss,pos);
    if (pos!=string::npos)
      fs.replace(pos,1,"");
  };
  return fs;
}

bool Lstring::FinalStringExists()
{
  if (FinalString==NULL)
    return false;
  else
    return true;
}

int  Lstring::FinalStringLength()
{
  return FS_Lgth;
}

unsigned char Lstring::FinalStringChr(unsigned int i)
{
  return FinalString[i];
}

int  Lstring::FinalStringRecLev()
{
  return FS_Lev;
}

string Lstring::get_axiom()
{
  return (line_ptr[AXIOM_LINE]->text());
}

float  Lstring::get_ang()
{
  return (line_ptr[ANG_LINE]->as_float());
  return (float) 0.1;
}

float  Lstring::get_thick()
{
  return (line_ptr[THICK_LINE]->as_float());
};

r32  Lstring::get_fraction()
{
  double temp=line_ptr[REC_LINE]->as_float();
  return static_cast<r32>(temp-floor(temp));
}

int  Lstring::get_lev()
{
  double temp=line_ptr[REC_LINE]->as_float();
  return static_cast<s16>(ceil(temp));
}

bool Lstring::growing()
{
  //if there is a decimal place in the recursion line...
  if (line_ptr[REC_LINE]->text().find('.')==string::npos)
    return false;
  else
    return true;
}
