/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    convert WordPerfect 5.x or 6.x files into LaTeX               *
 * modul:       formulas.cc                                                   *
 * description: Procedures for converting WP formulas into LaTeX equivalents. *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#include <std_str.h>
#include "stringa.h"
#include <sets.h>
#include "wp2latex.h"


int FormulaNo = 0;


void WPU2cm(string & Numeral)
{
double f;
 f = atof(Numeral());
 Numeral.printf("%2.2fcm", f/470);
}


#define WP2LATEX	1
#define AMSSYMB		2
#define LATEXSYM	4
#define ACCENTS		8
#define SCALEREL	16


/* Special meaning of digits in the LATEX term:
** 0: previous argument,
** 1: next argument,
** 2: next next argument,
** 3: 3rd argument
** 4: 4th argument
** 5: same word as WordPerfect, with "\" before, lower case,
** 6: same word as WordPerfect, with "\" before, right case
** 7: count Ampersands and write count times the following char
** 8: advanced previous argument with all xx_yy^zz stuffs (similiarly to 0)
** 9: advanced next argument with all xx_yy^zz stuffs (similiarly to 1)
**
** #: converts following argument from number in WPUnits to pt
** |: converts following argument from number in % to ratio
** @: output exactly next character e.g. number (@1 produces 1; @@ -> @)
** !: ignore following character - placeholder
**
** lower case chars in the WP-term match only lower case chars in
** the WordPerfect formula, upper case chars match both cases
**
** note: the order of words is sometimes essential !
** first the more specialized and longer words
** then the more common and shorter words. */

const struct FmlTranslationArray FmlTransTable[] =
{
/* Highest priority space fix */
   {"`","\\,", 0 },

/* Hard FIX for accents in the math mode */
   {"\2acute","{\\acute{1}}",0},
   {"\2bar","{\\bar{1}}",0},
   {"\2breve","{\\breve{1}}",0},
   {"\2check","{\\check{1}}",0},
   {"\2dot","{\\dot{1}}",0},
   {"\2grave","{\\grave{1}}",0},
   {"\2hat","{\\hat{1}}",0},
   {"\2mathring","{\\mathring{1}}",ACCENTS},
   {"\2tilde","{\\tilde{1}}",0},   

/*High priority symbols*/
   { "LEFT", "5", 0 },
   { "RIGHT", "5", 0 },
   { "HAT", "{5{0}}", 0 },
   { "DOT", "{5{0}}", 0 },
   { "MATRIX", "\\begin{array}{7}1\\end{array}", 0 },

/* 11 char words */
   { "SMALLCOPROD", "\\coprod", 0 },	/* bad translation */
/* 10 char words */
   { "STACKALIGN", "\\begin{array}{r@@{}l}1\\end{array}", 0 },
   { "varepsilon", "5", 0 },
/* 9 char words */
   { "BIGOMINUS", "\\bigominus", WP2LATEX},	/* in WP2LaTeX style */
   { "BIGOTIMES", "5", 0 },
   { "IDENTICAL", "5", WP2LATEX},		/* in WP2LaTeX style */
   { "LINESPACE", "\\vspace{#1}", 0 },	/* very bad translation */
   { "MINUSPLUS", "\\mp", 0 },
   { "PLUSMINUS", "\\pm", 0 },
   { "SMALLOINT", "\\oint", 0 },		/* bad translation */
   { "SMALLPROD", "\\prod", 0 },          /* bad translation */
   { "THEREFORE", "5", WP2LATEX},		/* in WP2LaTeX style */
   { "UNDERLINE", "5{1}", 0 },
/* 8 char words */
   { "ANGSTROM", "\\AA", 0 },
   { "SCALESYM", "\\scaleobj{|1}{2}", SCALEREL},   
   { "BIGOPLUS", "5", 0 },
   { "BIGSQCAP", "\\bigsqcap", WP2LATEX},	/* in WP2LaTeX style */
   { "BIGSQCUP", "5", 0 },
   { "BIGUPLUS", "5", 0 },
   { "BIGWEDGE", "5", 0 },
   { "DOTSAXIS", "\\cdots", 0 },
   { "DOTSDIAG", "\\ddots", 0 },
   { "DOTSVERT", "\\vdots", 0 },
   { "EMPTYSET", "5", 0 },
   { "INFINITY", "\\infty", 0 },
   { "LONGDIVS", "\\div", 0 },		/* very bad translation */
   { "OVERLINE", "5{1}", 0 },
   { "PARALLEL", "5", 0 },
   { "SETMINUS", "\\\\", 0 },
   { "SMALLINT", "\\int", 0 },		/* very bad translation */
   { "SMALLSUM", "\\sum", 0 },            /* very bad translation */
   { "SQSUBSET", "5", LATEXSYM },
   { "SQSUPSET", "5", LATEXSYM },
   { "varsigma", "5", 0 },
   { "vartheta", "5", 0 },
/* 7 char words */
   { "BECAUSE", "5", WP2LATEX},			/* in WP2LaTeX style */
   { "BETWEEN", "\\between ", WP2LATEX},	/* in WP2LaTeX style */
   { "BIGODIV", "\\div", 0 },		/* very bad translation */
   { "BIGODOT", "5", 0 },
   { "BINOMSM", "{\\small {1 \\choose 2}}", 0 },
   { "DOTSLOW", "\\ldots", 0 },
   { "epsilon", "5", 0 },
   { "EPSILON", "\\epsilon", 0 },
   { "LDBRACK", "[", 0 },			/* very bad translation */
   { "LONGDIV", "\\div", 0 },			/* very bad translation */
   { "omikron", "o", 0 },			/* very bad translation */
   { "OMIKRON", "O", 0 },			/* very bad translation */
   { "MASSERT", "\\dashv", 0 },
   { "MATFORM", "\\iffalse 1 \\fi ", 0 },	/* {MATRIX{MATFORM{ALIGNC&ALIGNC&ALIGNC&ALIGNC}{t_1(s)}&{0}&{. . . . . . */
   { "MSANGLE", "\\measuredangle", AMSSYMB },
   { "NASYMEQ", "\\not\\asymp", 0 },
   { "PARTIAL", "5", 0 },
   { "PHANTOM", "5{1}", 0 },
   { "RDBRACK", "]", 0 },			/* very bad translation */
   { "RTANGLE", "?", 0 },			/* no translation known */
   { "upsilon", "\\upsilon", 0 },
   { "UPSILON", "\\Upsilon", 0 },
/* 6 char words */
   { "ALIGNC", "", 0 },				/* Valid only in MATRIX header MATFORM */
   { "ALIGNL", "", 0 },				/* Valid only in MATRIX header MATFORM */
   { "ALINGR", "", 0 },				/* Valid only in MATRIX header MATFORM */
   { "APPROX", "5", 0 },
   { "arccos", "5", 0 },
   { "arcsin", "5", 0 },
   { "arctan", "5", 0 },
   { "ASSERT", "\\vdash", 0 },
   { "ASYMEQ", "\\asymp", 0 },
   { "BIGCAP", "5", 0 },
   { "BIGCUP", "5", 0 },
   { "BIGVEE", "5", 0 },
   { "CIRCLE", "\\dot{0}", 0 },		/* no translation known */
   { "COPROD", "5", 0 },
   { "EXISTS", "5", 0 },
   { "FORALL", "5", 0 },
   { "lambda", "5", 0 },
   { "LAMBDA", "\\Lambda", 0 },
   { "LANGLE", "5", 0 },
   { "LBRACE", "\\{", 0 },
   { "LFLOOR", "5", 0 },
   { "liminf", "5", 0 },
   { "limsup", "5", 0 },   
   { "MODELS", "5", 0 },
   { "NEQUIV", "\\not\\equiv", 0 },
   { "OMINUS", "5", 0 },
   { "OTIMES", "5", 0 },
   { "OVERSM", "{\\mathsmaller{\\frac{0}{1}}}", WP2LATEX}, /* in WP2LaTeX style */
   { "PRECEQ", "5", 0 },
   { "PROPTO", "5", 0 },
   { "QEQUAL", "\\stackrel{?}{=}", 0 },
   { "RANGLE", "5", 0 },
   { "RBRACE", "\\}", 0 },
   { "RFLOOR", "5", 0 },
   { "RIMAGE", "5", WP2LATEX },			/* in WP2LaTeX style */
   { "SANGLE", "\\sphericalangle", AMSSYMB },
   { "SUBSET", "5", 0 },
   { "SUCCEQ", "5", 0 },
   { "SUPSET", "5", 0 },
   { "varphi", "5", 0 },
   { "varrho", "5", 0 },
   { "WREATH", "\\wr", 0 },
/* 5 char words */
   { "ACUTE", "5{0}", 0 },
   { "alpha", "5", 0 },
   { "ALPHA", "A", 0 },
   { "ANGLE", "5", 0 },
   { "BINOM", "{1 \\choose 2}", 0 },
   { "BREVE", "5{0}", 0 },
   { "cosec", "\\csc", 0 },
   { "DDDOT", "\\ddot{0}", 0 },
   { "delta", "5", 0 },
   { "DELTA", "\\Delta", 0 },
   { "DLINE", "\\|", 0 },
   { "DOTEQ", "5", 0 },
   { "EQUIV", "5", 0 },
   { "FROWN", "5", 0 },
   { "gamma", "5", 0 },
   { "GAMMA", "\\Gamma", 0 },
   { "GRAVE", "5{0}", 0 },
   { "IMAGE", "5", WP2LATEX},			/* in WP2LaTeX style */
   { "kappa", "5", 0 },
   { "KAPPA", "\\Kappa", 0 },
   { "LCEIL", "5", 0 },
   { "NOTIN", "5", 0 },
   { "NROOT", " \\sqrt[1]{2}", 0 },
   { "omega", "\\omega", 0 },
   { "OMEGA", "\\Omega", 0 },
   { "OPLUS", "5", 0 },
   { "RCEIL", "5", 0 },
// { "RIGHT", "5", 0 }, moved up
   { "sigma", "5", 0 },
   { "SIGMA", "\\Sigma", 0 },
   { "SIMEQ", "5", 0 },
   { "SMILE", "5", 0 },
   { "SQCAP", "5", 0 },
   { "SQCUP", "5", 0 },
   { "STACK", "\\begin{array}{c}1\\end{array}", 0 },
   { "theta", "5", 0 },
   { "THETA", "\\Theta", 0 },
   { "TILDE", "{5{0}}", 0 },
   { "TIMES", "5", 0 },
   { "UPLUS", "5", 0 },
   { "varpi", "5", 0 },
   { "VARPI", "6", 0 },
/* Words with 4 chars */
   { "beta", "5", 0 },
   { "BETA", "B", 0 },
   { "BOLD", "{\\bf 1}", 0 },
   { "CDOT", "5", 0 },
   { "CONG", "5", 0 },
   { "cosh", "5", 0 },
   { "coth", "5", 0 },
   { "DDOT", "5{0}", 0 },
   { "DSUM", "\\dot{+}?", 0 },
   { "DYAD", "{\\bar{0}}", 0 },
   { "FROM", "_{9}", 0 },
   { "FUNC", "{\\rm 1}", 0 },
   { "GRAD", "\\nabla", 0 },
   { "HORZ", "\\hspace{#1}", 0 },
   { "IMAG", "\\Im", 0 },
   { "iota", "5", 0 },
   { "IOTA", "I", 0 },
   { "ITAL", "{\\it 1}", 0 },
// { "LEFT", "5", 0 }, 		moved up
   { "LINE", "|", 0 },
   { "NISO", "\\not\\Bumpeq", AMSSYMB },	/* translation only with amssymb.sty */
   { "ODIV", "\\div", 0 },
   { "ODOT", "5", 0 },
   { "OINT", "5", 0 },
   { "OVER", "{\\frac{8}{9}}", 0 },
   { "OWNS", "\\ni", 0 },
   { "PERP", "5", 0 },
   { "PREC", "5", 0 },
   { "PROD", "5", 0 },
   { "REAL", "\\Re", 0 },
   { "ROOT", " \\sqrt{1}", 0 },
   { "sinh", "5", 0 },
   { "SQRT", "5{1}", 0 },
   { "SUCC", "5", 0 },
   { "tanh", "5", 0 },
   { "VERT", "\\vspace{#1}", 0 },
   { "zeta", "5", 0 },
   { "ZETA", "Z", 0 },
/* Words with 3 chars */
   {"'''","{0'''}", 0 },
   { "AND", "\\wedge", 0 },
   { "arc", "{\\rm arc}", 0 },
   { "BAR", "{5{0}}", 0 },
   { "CAP", "5", 0 },
   { "chi", "5", 0 },
   { "CHI", "X", 0 },
   { "cos", "5", 0 },
   { "cot", "5", 0 },
   { "CUP", "5", 0 },
   { "DEG", "\\degrees", WP2LATEX },		// Wp2LaTeX or TextComp
   { "det", "5", 0 },
   { "DIV", "5", 0 },
   { "eta", "5", 0 },
   { "ETA", "H", 0 },
   { "exp", "5", 0 },
   { "gcd", "5", 0 },
   { "GGG", "\\gg", 0 },
   { "INF", "\\infty", 0 },
   { "INT", "5", 0 },
   { "ISO", "\\Bumpeq", AMSSYMB },		/* translation only with amssymb.sty */
   { "lim", "5", 0 },
   { "LLL", "\\ll", 0 },
   { "log", "{\\rm log}", 0 },
   { "max", "5", 0 },
   { "MHO", "5", LATEXSYM },
   { "min", "5", 0 },
   { "mod", "{\\rm mod}", 0 },
   { "NOT", "\\neg", 0 },
   { "phi", "5", 0 },
   { "PHI", "\\Phi", 0 },
   { "psi", "5", 0 },
   { "PSI", "\\Psi", 0 },
   { "rho", "5", 0 },
   { "RHO", "P", 0 },
   { "sec", "5", 0 },
   { "SIM", "5", 0 },
   { "sin", "5", 0 },
   { "SUB", "_{1}", 0 },
   { "SUM", "5", 0 },
   { "SUP", "^{1}", 0 },
   { "tan", "5", 0 },
   { "tau", "5", 0 },
   { "TAU", "\\Tau", 0 },
   { "TOP", "5", 0 },
   { "VEC", "5{0}", 0 },
   { "XOR", "\\underline\\vee", 0 },
/* words with 2 chars */
   { "!=", "\\neq", 0 },
   { "==", "\\equiv", 0 },
   { "+-", "\\pm", 0 },
   { "-+", "\\mp", 0 },
   { "<=", "\\le", 0 },
   { "<<", "\\ll", 0 },
   { ">=", "\\ge", 0 },
   { ">>", "\\gg", 0 },
   { "IN", "5", 0 },
   { "ln", "5", 0 },
   { "mu", "\\mu", 0 },
   { "MU", "\\Mu", 0 },
   { "nu", "\\nu", 0 },
   { "NU", "N", 0 },
   { "OR", "\\vee", 0 },
   { "pi", "5", 0 },
   { "PI", "\\Pi", 0 },
   { "TO", "^{9}", 0 },
   { "xi", "5", 0 },
   { "XI", "\\Xi", 0 },
   { "\\'", "'", 0 },			/* Bad conversion - symbol should be like ^{|} */
/* words with 1 char */
   { "^", "^{9}", 0 },
   { "_", "_{9}", 0 },
   {"#"," \\\\ ", 0 },			/* New line for multiline formula */
   { "'", "{0'}", 0 },
   { "%", "\\%", }

/*  (szWP:''';szTeX:'{0'}'),
    {"\\'","\\dq"},
    {"&","&"}   Ampersand is solved another way*/
    };
static const int nFmlTransWords = sizeof(FmlTransTable)/sizeof(FmlTransTable[0]);


/** This procedure checks the number of braces '{', '}' and returns discrepancy.
 * @param[in]     EquStr	String that contains a formula.
 * @param[out]	  Braces	Amount of curly braces.
 * @param[out]	  BegBrace	Amount of unbalanced curly braces at the beginning. */
static bool CheckFormula(const char *EquStr, int & Braces, int & BegBrace)
{
 BegBrace = 0;
 Braces = 0;
 if(EquStr==NULL) return(false);

 while(*EquStr != 0)
	{
	switch(*EquStr)
	   {
	   case '\\':if(EquStr[1]!=0) EquStr++;	// prevent to go over zero terminator.
		     break;
           case  '{':Braces++;
           	     break;
           case  '}':Braces--;
		     if(BegBrace>Braces) BegBrace=Braces;
                     break;
           }
        EquStr++;
        }

return(BegBrace!=0 || Braces!=0);
}


/** This function attempts to check for and fix invalid amount of curly braces.
 * @param[in]	  cq		Conversion wrapper structure.
 * @param[in,out] EquStr	String that contains a formula.
 * @param[out]	  Braces	Amount of curly braces.
 * @param[out]	  BegBrace	Amount of unbalanced curly braces at the beginning. */
bool FixFormula(TconvertedPass1 *cq, string & EquStr, int & Braces, int & BegBrace)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#FixFormula(%s) ",chk(EquStr()));fflush(cq->log);
#endif
  if(CheckFormula(EquStr(),Braces,BegBrace))
    {		//An attempt to fix invalid equation is made
    if(cq->err != NULL)
       {
       cq->perc.Hide();
       fprintf(cq->err, _("\nWarning: Formula #%d is syntactically wrong, No. of '{'-'}' is %d, %d!"),FormulaNo,Braces,BegBrace);
       }
    while(BegBrace<0 || Braces<0)
	{
	EquStr = '{'+EquStr;
	BegBrace++;
	Braces++;
	}
    while(Braces>0)
	{
        EquStr+='}';
	Braces--;
	}
    return true;
    }
 return false;
}


/** This procedure checks the number of braces \left{, \right } and returns discrepancy.
 * @param[in]     EquStr	String that contains a formula.
 * @param[out]	  Braces	Amount of curly braces.
 * @param[out]	  BegBrace	Amount of unbalanced curly braces at the beginning. */
static bool CheckFormulaLeftRight(const char *EquStr, int & Braces, int & BegBrace)
{
int len;

 BegBrace = 0;
 Braces = 0;
 if(EquStr==NULL) return false;
 len = strlen(EquStr);

 while(len>0)
   {
   if(*EquStr=='\\')
     {     
       len--;EquStr++;			// skip this and also a next character.

       if(!strncmp(EquStr,"left",4))
       {
         if(!isalpha(EquStr[4]))	// there could be \leftarrow or something like this.
         {
           Braces++;
           len-=3; EquStr+=3;
         }
       }
       else if(!strncmp(EquStr,"right",5))
       {
         if(!isalpha(EquStr[5]))	// there could be \rightarrow or something like this.
         {
           Braces--;
           if(BegBrace>Braces) BegBrace=Braces;
           len-=4; EquStr+=4;
         }
       }
     }

   len--;
   EquStr++;
   }

return(BegBrace!=0 || Braces!=0);
}


/** Remove one symbol from WP formula string.
 *   blah blah blah SYMBOL blah
 * @param[in,out]	str1	Text before symbol, only appended.
 * @param[in,out]	str2	Text after symbol, schrinked.
 * @param[in]		symbol	Symbol to be removed. */
bool RemoveSymbol(string & str1, string & str2, const char *symbol)
{
const char *str, *UPStr;
unsigned char c;
int p, symlen;
unsigned i;
string t;

  if(symbol==NULL) return false;
  symlen = strlen(symbol);
  str = StrStr(str2(), symbol);

NewTest:
  if(str!=NULL)
    {
    if(str>str2())
	{			//fix symbol, which begins by \\anything
	c=*(str-1);
	if( (c=='\\' || c==1) ||
	    ( (isalpha(c)) && (isalpha(*symbol)) ) ||
	    ( (isdigit(c)) && (isdigit(*symbol)) ) )
		  {
		  str = strstr(++str, symbol);
		  goto NewTest;
		  }
	 }

     if((isalpha(*(str+symlen))||isdigit(*(str+symlen))) &&
	    isalpha(*symbol) )
		  {
		  str=strstr(++str, symbol);
		  goto NewTest;
		  }
     }
  p = -1;
  if(str!=NULL)
	{
	p = str-str2();
	if(p<symlen) goto SkipUpcaseTest;
	}

  if(*symbol > 'z' || *symbol < 'a')	//small speedup trick
    {
    t=str2;
    for (i=0; i<length(t); i++)
	{
	t[i] = toupper(t[i]);
	}
    UPStr=StrStr(t(), symbol);		//find upper case match
    }
  else UPStr=NULL;

NewTest2:
  if(str!=NULL)
    {
    if(UPStr==NULL) goto SkipUpcaseTest;	//only str matches
    if(UPStr-t()>=p) goto SkipUpcaseTest;	//str match is better
    }
  else
    {
    if(UPStr == NULL) return false;  //Not found
    }

  if(UPStr>t())
	{			//fix symbol, which begins by \\anything
	c=*(UPStr-1);
	if( (c=='\\' || c==1) ||
	    ( (isalpha(c)) && (isalpha(*symbol)) ) ||
	    ( (isdigit(c)) && (isdigit(*symbol)) ) )
		  {
		  UPStr=strstr(++UPStr, symbol);
		  goto NewTest2;
		  }
	 }

  if(isalpha(*(UPStr+symlen))||isdigit(*(UPStr+symlen)))
	 {
	 UPStr=strstr(++UPStr, symbol);
	 goto NewTest2;
	 }

  p = UPStr-t();

SkipUpcaseTest:
  if (p >= 1 && (str2[p-1] == '\\' || isalpha(str2[p-1])) &&
      isalnum(symbol[0]) )
    return false;

  str1 += copy(str2,0,p);
  str2  = copy(str2, p+strlen(symbol), length(str2)-strlen(symbol)-p);
  
return true;
}


struct FixArray
    {
    const char  *lo;		///< original WordPerfect term.
    const char  *up;		///< translated LATEX term.
    unsigned    Len;		///< cached term length.
    signed char inc;		///< brace level increment.
    };

const struct FixArray FixTable[] =
{
  {"left","LEFT",4,1},
  {"right","RIGHT",5,-1},
  {"overline","OVERLINE",8,0}
};


void DeleteCurlyBraces(string & str)
{
  if(length(str)<2) return;

  while(str[0]=='{' && str[length(str)-1]=='}')	// remove surrounding {...}
   {
   int BraceLevel=1;
   for(unsigned i=1; i<length(str)-2; i++)
     {
     if(str[i]=='{') BraceLevel++;
     if(str[i]=='}') 
       {
       BraceLevel--;
       if(BraceLevel<=0) return;		// braces zero cross detected
       }
     }
   str = copy(str,1,length(str)-2);
   while(isspace(str[0])) str = copy(str,1,length(str)-1);
   while(isspace((unsigned char)str[length(str)-1])) str = copy(str,0,length(str)-1);
   }
}


/** This procedure removes argument from the beginning of the string. */
char Remove1stArg(string & str, string & arg)
{
int i;
char c;
char LastC;
string InternalArg;
bool FirstCipher = false;

  arg.erase();

  while(!str.isEmpty() && str[0]==' ')
    str = copy(str,1,length(str)-1);

  LastC = '\0';
  i = 0;
  while(!str.isEmpty() && (str[0]!=' ' || i!=0))
     {
     c = str[0];

     if(i <= 0 && LastC!='\\' && length(arg)>0)
	{
	if (c == '^' || c == '_' || c == '{' || c == '\\' || c == '&' || c == '#' || c == ',' || c == ';' ||
	    c == '$' || c == '@' || c == '%' || c == '*'  || isspace(c) || c == '+' || c == '-' || c == '=' ||
	    c == ']' || c == '[' || c == '(' || c == ')' || c == '/' ||
	    (FirstCipher && !isdigit(c))  )
			break;
	}
     if(!LastC) 
       FirstCipher = isdigit(c); //Test whether first character is digit 0..9

     if(!strncmp(str(),"overline",8) || !strncmp(str(),"OVERLINE",8))
       {
       if((isspace(8)||str[8]=='{') && (LastC=='\\' || isspace(LastC)))
	 {
	 c = str[8];
	 str = copy(str, 8, length(str)-8);
	 arg += "overline";
	 if(c=='{')
           {
	   str=copy(str,1,length(str)-1);
	   arg += '{';
	   i++;
           }
	 continue;			// continue outer loop
	 }
       }

     str=copy(str,1,length(str)-1);

     if(LastC != '\\')
	 {
	 if (c == '{')
	      {
	      if(i==0 && LastC!=0)	//argument is already read
			 {
			 str = c+str;	// return curly brace back
			 break;
			 }
	      i++;
	      }
	 if(c == '}')
	      {
	      i--;
	      if(i==-1)			 //we touch a brace after argument
		 {
		 str='}'+str;
		 i++;
		 break;
		 }
	      if(i==0)			//closing brace found
		 {
		 arg+='}';
		 LastC=c;
		 break;
		 }  
	      }
	 if ((c=='\\' || c=='+') &&
	      i==0 && !arg.isEmpty())
	     {
	     str=c+str;
	     break;
	     }
	 }

     arg+=c;

     if(c=='t' && (isspace(str[0])||str[0]==')'||str[0]=='('||str[0]==']'||str[0]=='['||str[0]=='|'))
	 {
	 if(LastC=='f' && arg[length(arg)-3]=='e' && arg[length(arg)-4]=='l')
		 {	//bracket    "\left ("   found
		 LastC = c;
		 if(length(arg)<5) c=' ';
			      else c=arg[length(arg)-5];
		 if(!isalnum(c)) i++;
		 continue;
		 }
	 if(LastC=='h' && arg[length(arg)-3]=='g' && arg[length(arg)-4]=='i' && arg[length(arg)-5]=='r')
		 {	//bracket    "\right )"   found
		 LastC = c;
		 c=arg[length(arg)-6];
		 if(!isalnum(c))
			 {
			 do {
			    c = str[0];
			    arg+=c;
			    str=copy(str,1,length(str)-1);
			    } while(isspace(c));
			 i--;
			 if(i==0) break;
			 }
		 continue;
		 }
	 }

     LastC = c;
     }

 if(arg.isEmpty()) return(LastC);
 DeleteCurlyBraces(arg);

 InternalArg = arg; //Check whether arg contains any non-terminal symbol that need to be expanded
 InternalArg.ToUpper();

 for(i=0; i<nFmlTransWords; i++)
  {
  if(arg==FmlTransTable[i].szWP || InternalArg==FmlTransTable[i].szWP)
    {
    if(strchr(FmlTransTable[i].szTeX,'1')!=NULL)
	{
	if(LastC=='}')	//the curly brace was removed - add it
	   {
	   arg='{'+arg+'}';
	   LastC=0;
	   }

	if(Remove1stArg(str,InternalArg)=='}')
	   arg=arg+'{'+InternalArg+'}';
	else
	   arg+=' '+InternalArg;
	if(strchr(FmlTransTable[i].szTeX,'2')!=NULL)
	   {
	   if(Remove1stArg(str,InternalArg)=='}')
	     arg=arg+'{'+InternalArg+'}';
	   else
	     arg+=' '+InternalArg;
	   }
	}
    break;
    }
  }

return(LastC);
}


/** This procedure removes argument from the beginning of the string with x^y_z stuff.
 * @param[in,out] str	Formula data that shoud be split.
 * @param[out]	  arg	Argument extracred from formula. */
static bool RemoveAdvanced1stArg(string & str, string & arg, char BlockChar=0)
{
int Level, ZeroPass;
char c;
char LastC, LastSupSub;
string InternalArg;

  arg.erase();

  while(!str.isEmpty() && str[0]==' ')
    str=copy(str,1,length(str)-1);

  LastSupSub = LastC = '\0';
  ZeroPass = Level = 0;
  while(!str.isEmpty())
     {
     c = str[0];
     if(Level<=0 && !isspace(c))
       {
       if(c==BlockChar || c=='#') break;
       if(!LastSupSub && !arg.isEmpty())	//argument is not empty
	 {
	 if((isspace(LastC) ||
	      LastC=='}' || LastC==']' || LastC==')' || LastC=='"' || LastC=='\'') &&
	    (!isspace(c) && c!='_' && c!='^') ) break;

	 if(c=='\\' ||				//These characters break WP argument
	   c==',' || c=='?' || c=='!' ||
	   c=='(' || c==')' || c=='[' || c==']' ||
	   c=='*' || c == '/' || c=='+' || c == '-' ||
	   c=='@' || c=='$'  || c=='=' || c=='~' || c=='&')
             {
             if(LastC == '\\')
               {
		 arg += c;
                 str = copy(str,1,length(str)-1);
               }
             break;
             }
	 }
       }

    for(int j=0; j<sizeof(FixTable)/sizeof(FixTable[0]); j++)
      if(!strncmp(str(),FixTable[j].lo,FixTable[j].Len) || !strncmp(str(),FixTable[j].up,FixTable[j].Len))
        {
        if((isspace(str[FixTable[j].Len])||str[FixTable[j].Len]=='{') && (LastC=='\\' || isspace(LastC)))
	  {
	  c = str[FixTable[j].Len];
	  Level += FixTable[j].inc;
	  LastSupSub = 2;		//protect next char against break
	  str = copy(str, FixTable[j].Len, length(str)-FixTable[j].Len);
	  arg += FixTable[j].lo;
	  if(c!='{') goto CONTINUE;	// continue outer loop
          LastC = ' ';	  
	  }
        break;				// do not iterate search when first substring matched
        }

    str = copy(str,1,length(str)-1);

    if(LastC != '\\')
        {
        if (c == '{')
	     {
	     Level++;
	     }
        if (c == '}')
	     {
	     Level--;
             if(Level==0) ZeroPass++;
	     }
        }

    arg += c;
    if(!isspace((unsigned char)c))
    {
      LastSupSub = 0;
      if(c=='_' || c=='^') LastSupSub=1;
    }
CONTINUE:
    LastC = c;
    }

while(!arg.isEmpty() && arg[length(arg)-1]==' ')
	arg = copy(arg,0,length(arg)-1);
if(ZeroPass==1 && arg[0]=='{' && arg[length(arg)-1]=='}')
	arg = copy(arg,1,length(arg)-2);


if(arg.isEmpty()) return(0);

InternalArg=arg; //Check whether arg contains any non-terminal symbol that need to be expanded
InternalArg.ToUpper();
for(int j=0; j<nFmlTransWords; j++)
  {
  if(arg==FmlTransTable[j].szWP || InternalArg==FmlTransTable[j].szWP)
    {
    if(strchr(FmlTransTable[j].szTeX,'1')!=NULL)
	{
	if(Remove1stArg(str,InternalArg)=='}')
	   arg = arg+"{"+InternalArg+"}";
	else
	   arg+=" "+InternalArg;
	if(strchr(FmlTransTable[j].szTeX,'2')!=NULL)
	   {
	   if(Remove1stArg(str,InternalArg)=='}')
	     arg = arg + "{"+InternalArg + "}";
	   else
	     arg += " " + InternalArg;
	   }
	}
    break;
    }
  }

return(0);
}


/** This procedure removes argument from the end of the string.
 * @param[in,out] str	Formula data that shoud be split.
 * @param[out]	  arg	Argument divided from formula. */
bool RemoveLastArg(string & str, string & arg)
{
//#ifdef DEBUG
//  fprintf(cq->log,"\n#RemoveLastArg(%s,%s) ", chk(str()), chk(arg())); fflush(cq->log);
//#endif
int i, j;
char c, LastC;

  LastC=0;
  arg.erase();
  while(!str.isEmpty() && str[length(str)-1] == ' ')
      {
      str = copy(str,0,length(str)-1);
      }

  i = 0;			//Tady je chyba !!!!!!!!!!!!!!!!!!!!!
  while(!str.isEmpty()) // && (str[length(str) - 1] != ' ' || i != 0))
      {
      c = str[length(str) - 1];
      if (i <= 0)
	 {
	 if(length(arg)>0)
		{
		if(c == '^' || c == '_' || c == '{' || c == '&' || c == '#' || c == ',' || c == ';' ||
		    c == '$' || c == '@' || c == '%' || c == '*'  || isspace(c) || c == '+' || c == '-' || c == '=' ||
		    c == ']' || c == '[' || c == '(' || c == ')' || c == '/')
				  break;
		if((isalpha(LastC) || LastC==0) && !isalpha(LastC)) break;
		if(c == '\\')
			{
			str=copy(str,0,length(str)-1);
			arg=c+arg;
			break;
			}
		}
	 }

      str=copy(str,0,length(str)-1);

      if(c == '}')
	  {
	  i++;
	  if(i == 1)
		{
		if(arg.isEmpty()) continue;
		str+='}';	// fix the situation    "{...}arg"
		//i--;		//'i' is not needed because of break.
		break;
		}
	  }
      if(c == '{')
	  {
	  i--;
	  if(i == 0) break;
	  }

      if(c==')' || c==']' || str[0]=='|')
	  {
	  j=length(str) - 2;
	  if(isspace(str[j])) j--;
	  if( str[j]=='t' && str[j-1]=='h' && str[j-2]=='g' && str[j-3]=='i' && str[j-4]=='r')
		{  // 'right )' detected
		arg="right "+(c+arg);
		LastC='r';
		str=copy(str,0,j-4);
		i++;
		continue;
		}
	  }
      if((c=='(' || c=='[' || str[0]=='|') && i>0)
	  {
	  j=length(str) - 2;
	  if(isspace(str[j])) j--;
	  if( str[j]=='t' && str[j-1]=='f' && str[j-2]=='e' && str[j-3]=='l')
		{  // 'left (' detected
		arg="left "+(c+arg);
		LastC='r';
		str=copy(str,0,j-3);
		i--;
		continue;
		}
	  }

      arg=c+arg;
      LastC=c;
      }

return(0);
}


/** This procedure removes argument from the end of the string with x^y_z stuff.
 * @param[in,out] str	Formula data that shoud be split.
 * @param[out]	  arg	Argument extracred from formula. */
static bool RemoveAdvancedLastArg(string & str, string & arg)
{
//#ifdef DEBUG
//  fprintf(cq->log,"\n#RemoveAdvancedLastArg(%s,%s) ", chk(str()), chk(arg()));fflush(cq->log);
//#endif
int i, j;
char c, LastC;
uint8_t LastSupSub, LastCBrace;

  LastCBrace=LastSupSub=LastC=0;
  arg.erase();
  while(!str.isEmpty() && str[length(str) - 1] == ' ')
      {
      str=copy(str,0,length(str)-1);
      }

  i=0;			//Tady je chyba !!!!!!!!!!!!!!!!!!!!!
  while(!str.isEmpty()) // && (str[length(str) - 1] != ' ' || i != 0))
      {
      c = str[length(str) - 1];
      if (i <= 0)
	 {
	 if(length(arg)>0 && !LastSupSub)
		{
		if (c == '{' || c == '&' || c == '#' || c == ',' || c == ';' ||
		    c == '$' || c == '@' || c == '%' || c == '*'  || isspace(c) || c == '+' || c == '-' || c == '=' ||
		    c == ']' || c == '[' || c == '(' || c == ')' || c == '/')
				  break;
		if ( (isalpha(LastC) || LastC==0) && !isalpha(LastC)) break;
		if ( c == '\\')
			{
			str=copy(str,0,length(str)-1);
			arg=c+arg;
			break;
			}
		}
	 }

      str=copy(str,0,length(str)-1);

      if(c == '}')
	  {
	  i++;
	  if(i==1 && !LastSupSub)
		{
		if(arg.isEmpty()) {LastCBrace=1;continue;}	//The curly brace was temporarily omitted
		str+='}';	// fix the situation    "{...}arg"
		i--;
		break;
		}
	  }

      if(c == '{')
	  {
	  i--;
	  if(i==0)
	    {
	    j=length(str) - 1;
	    if(str[j]!='_' && str[j]!='^')
		{
		if(!LastCBrace) arg='{'+arg;
		break;
		}
	    j=length(arg) - 1;		//Fix the situation where ...^{arg}
	    if(LastCBrace) {arg+='}';LastCBrace=0;}
	    }
	  }

      if(c==')' || c==']' || str[0]=='|')
	  {
	  j=length(str) - 2;
	  if(isspace(str[j])) j--;
	  if( str[j]=='t' && str[j-1]=='h' && str[j-2]=='g' && str[j-3]=='i' && str[j-4]=='r')
		{  // 'right )' detected
		arg="right "+(c+arg);
		LastC='r';
		str=copy(str,0,j-4);
		i++;
		continue;
		}
	  }
      if( (c=='(' || c=='[' || str[0]=='|') && i>0)
	  {
	  j=length(str) - 2;
	  if(isspace(str[j])) j--;
	  if( str[j]=='t' && str[j-1]=='f' && str[j-2]=='e' && str[j-3]=='l')
		{  // 'left (' detected
		arg="left "+(c+arg);
		LastC='r';
		str=copy(str,0,j-3);
		i--;
		continue;
		}
	  }

      arg = c+arg;
      LastC = c;
      if(!isspace(c)) LastSupSub=0;
      if(c == '_' || c == '^') LastSupSub=1;
      }

return(0);
}


/** This procedure converts one preprocessed WP command to its LaTeX equivalent. */
void Interpret(TconvertedPass1 *cq, string & strB, string & strE,
	       const char *seq, const char *symbol, const string arg[])
{
#ifdef DEBUG
  fprintf(cq->log,"\n#Interpret(%s,%s) ",chk(strB()),chk(strE()));fflush(cq->log);
#endif
  unsigned j, a, a2;
  int p;
  string Numeral;

  if(seq==NULL) return;
  if(strE[0]!='_' && strE[0]!='^' && strE[0]!='}')
      strE=' '+strE;

  p = 0;
  while(*seq!=0)
    {
    switch(*seq)
	{
	case '8':
	case '0': strB+=arg[0];
		  break;

	case '1': if (p == 0) p = length(strB);
		  strB+=arg[1];
		  break;

	case '2': if(p == 0)  p = length(strB);
		  strB+=arg[2];
		  break;

	case '3': if(p == 0)  p = length(strB);
		  strB+=arg[3];
		  break;

	case '4': if(p == 0)  p = length(strB);
		  strB+=arg[4];
		  break;

	case '5': strB += "\\" + ToLower(symbol);
		  break;

	case '6': strB += "\\" + string(symbol);
		  break;

        case '7': a = 0;
                  a2 = 0;
		  j = 0;
		  while(j<length(arg[1]) && isspace(arg[1][j])) j++;
		  if(!strncmp("MATFORM",arg[1]()+j,7))			// The matrix contains a header.
		      {
		      j += 7;
		      while(j<length(arg[1]) && isspace(arg[1][j])) j++;
		      string Header;
		      string Str1(arg[1]()+j);
		      Remove1stArg(Str1,Header);
		      if(!Header.isEmpty())
			{
			char DefaultAlign = 'c';
			for(j=0; j<length(Header); j++)
			  {
			  if(Header[j]=='&')
			    {
			    strB += DefaultAlign;
			    DefaultAlign = 'c';
			    continue;
			    }
			  if(Header[j]=='A')
			    {
			    if(!strncmp(Header()+j,"ALIGNC",6)) {DefaultAlign='c';continue;}
			    if(!strncmp(Header()+j,"ALIGNL",6)) {DefaultAlign='l';continue;}
		            if(!strncmp(Header()+j,"ALIGNR",6)) {DefaultAlign='r';continue;}
			    }
			  }
			strB += DefaultAlign;
		        break;
                        }
		      }
		  while(j < length(arg[1]))
                      {
		      if(arg[1][j] == '&') a2++;
			if(arg[1][j] == '#')
			  {
			  if(a2 > a) a = a2;
                          a2 = 0;
                          }
			j++;
                      }
                   if(a2 > a) 	a = a2;

		   for(j=0; j<=a; j++)  strB+='c';
                   break;

        case '9': if (p == 0) p = length(strB);
		  strB+=arg[1];
                  break;

        case '|': switch(seq[1])
			{
			case '0':Numeral=arg[0];seq++;break;
			case '1':Numeral=arg[1];seq++;break;
			case '2':Numeral=arg[2];seq++;break;
			case '3':Numeral=arg[3];seq++;break;
			case '4':Numeral=arg[4];seq++;break;
                        default: Numeral='|'; break;
			};
                  if(Numeral.isEmpty())
		   	{
			if (cq->err != NULL)
			    {
			    cq->perc.Hide();
			    fprintf(cq->err, _("\nWarning: Formula #%d - missing numeric argument or error in formula grammar!"),FormulaNo);
			    cq->perc.Show();
			    }
                        strB+="1";
			break;
                        }
                  if(Numeral!="|")
                      {
                      double f = atof(Numeral()); 
                      Numeral.printf("%2.2f", f/100);
                      }
                  strB += Numeral;
                  break;

        case '#': switch(seq[1])
			{
			case '0':Numeral=arg[0];seq++;break;
			case '1':Numeral=arg[1];seq++;break;
			case '2':Numeral=arg[2];seq++;break;
			case '3':Numeral=arg[3];seq++;break;
			case '4':Numeral=arg[4];seq++;break;
			default: Numeral.erase(); break;
			};
                  if(Numeral.isEmpty())
		   	{
			if (cq->err != NULL)
			    {
			    cq->perc.Hide();
			    fprintf(cq->err, _("\nWarning: Formula #%d - missing numeric argument or error in formula grammar!"),FormulaNo);
			    cq->perc.Show();
			    }
                        strB += "0pt ";
			break;
                        }
                  WPU2cm(Numeral);
                  strB+= Numeral;
                  break;

        case '!':seq++;			//ignore following character
		 if(*seq==0) {seq--;break;}
		 break;
	case '@':seq++; 		//use exact following character without interpreting
		 if(*seq==0) {seq--;break;}
	default: strB+=*seq;
		 break;
	}
  seq++;
  }

  if (p != 0) {
	     strE = copy(strB,p,length(strB)-p) + strE;
	     strB = copy(strB,0,p);
	     }
}


/** This procedure creates readable format of formula string and writes the string to the file. */
void PutFormula(TconvertedPass1 *cq, char *Str, const TBox & Box)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#PutFormula(%s)",chk(Str));fflush(cq->log);
#endif

bool MathArray = false;

 if(StrStr(Str," \\nonumber ")!=NULL) MathArray=true;

 /*if(cq->envir=='B')
   {
   if(StrStr(Str," \\\\")!=NULL) MathArray=true;
   } */

 if(Box.AnchorType==2 || cq->envir=='B')
   {
   const char *pom,*pom2;
   pom = pom2 = "";
   if(MathArray)
	{
	pom="\\begin{array}{c}";
	pom2="\\end{array}";
	}
   if(cq->attr.Math_Depth)
	       fprintf(cq->strip, "}{%s%s%s}{\\rm ",pom,Str,pom2);
	  else fprintf(cq->strip, " $%s%s%s$ ",pom,Str,pom2);

   pom = Str;
   while(pom!=NULL)	//every \n must create a new line
     {
     pom = StrChr(pom,'\n');
     if(pom!=NULL)
       {
       NewLines(cq,1,false);
       pom++;
       }
     }
   return;
   }

 cq->envir = ' ';
 if(cq->line_term=='h') cq->char_on_line=true; //after HRt leave one row empty
 cq->line_term = 's';			/* Soft return */
 if(cq->char_on_line==CHAR_PRESENT)
	{
	NewLine(cq);
	}

 if(Box.Type==1 && Box.AnchorType!=2)
	{
	fprintf(cq->strip,"\\begin{table}[htbp]");
	NewLine(cq);
	fprintf(cq->strip,"\\begin{displaymath}");
	}
   else {
	if(Box.CaptionSize>0)
		{
		if(MathArray) fprintf(cq->strip,"\\begin{eqnarray}");
			 else fprintf(cq->strip,"\\begin{equation}");
		}
	else {
	     if(MathArray) fprintf(cq->strip,"\\begin{eqnarray*}");
		      else fprintf(cq->strip,"\\begin{displaymath}");
	     }
	}
 NewLine(cq);


 while(Str!=NULL)
   {
   char *pom = StrStr(Str,"\\\\");  // Look for all internal formula delimiters.
   if(pom!=NULL)
   {
     fwrite(Str,1,pom-Str,cq->strip);
     *pom = 0;				// the \n character will be searched in a current line.
   }
   else
       fputs(Str,cq->strip);		// Output a current line.

   Str = StrChr(Str,'\n');		// All new lines in formula must be also handled.
   while(Str!=NULL)
     {
     NewLines(cq,1,false);
     Str = StrChr(Str+1,'\n');
     }

   if(pom!=NULL)
	{
	fputs("\\\\",cq->strip);
	Str = pom + 2;
	while(*Str==' ') Str++;
	if(*Str==0) break;
	NewLine(cq);
	}
   else Str=NULL;
   }

 NewLine(cq);
 cq->Dispatch(DISP_EXTRACT_LABEL, &Box);

 if(Box.Type==1 && Box.AnchorType!=2)
	{
	fprintf(cq->strip,"\\end{displaymath}");
	NewLine(cq);
	if(Box.CaptionSize>0)
		      {
		      fseek(cq->wpd, Box.CaptionPos, SEEK_SET);
		      cq->Dispatch(DISP_DO_CAPTION, &Box.CaptionSize);
		      }
	fprintf(cq->strip,"\\end{table}");
	}
   else {
	if(Box.CaptionSize>0)
	    {				//!Fix this, caption is lost here!
	    if(MathArray) fprintf(cq->strip,"\\end{eqnarray}");
		     else fprintf(cq->strip,"\\end{equation}");
	    }
	else {
	     if(MathArray) fprintf(cq->strip,"\\end{eqnarray*}");
		      else fprintf(cq->strip,"\\end{displaymath}");
	     }
	}
 NewLine(cq);
}


/** This procedure fixes nested occurences of superscript/subscript. */
void FixSubSup(string & StrBeg, string & StrEnd)
{
string arg;
char ch2;

ch2 = 0;
while(length(StrEnd)>0)
{
  const char ch = StrEnd[0];
  switch(ch)
  {
	case '^':
	case '_':StrBeg+=ch;
		 StrEnd=copy(StrEnd,1,length(StrEnd)-1);
		 Remove1stArg(StrEnd, arg);
		 if(StrEnd[0]==ch || (ch2 && (StrEnd[0]=='_' || StrEnd[0]=='^')) )
		{
		  StrBeg+="{{";
		  StrBeg+=arg;
		  StrBeg+='}';
		  RemoveAdvanced1stArg(StrEnd, arg);
		  StrEnd=arg+'}'+StrEnd;
		  ch2=0;
		}
		 else
		{
		  StrBeg+='{'+arg+'}';
		  ch2 = ch;
		}
		break;
	default:StrBeg+=StrEnd[0];
		StrEnd=copy(StrEnd,1,length(StrEnd)-1);
		ch2 = 0;
	  }
	}
}


/// Chatacter "'" is expanded as ^{\prime}. Double '' causes error and must be fixed.
string FixMultiplePrime(string Str)
{
const char *ch;
int PrimeCount = 0;

  if(Str.length()<=0) return Str;
  ch = strchr(Str(), '\'');
  while(ch!=NULL)
  {
    string PrimeList("^{\\prime");
    PrimeCount++;
    int Pos = ch - Str();
    const char *ch2 = ch;

    do
      {      
      ch2++;

      if(*ch2 == 0) break;

      if(*ch2 == '\'')
        {
        PrimeList += "\\prime";
        PrimeCount++;
        continue;
        }

      if(*ch2 == '^')
        {
        ch2++;
        string TempTail(ch2);
        string Arg;

        Remove1stArg(TempTail, Arg);
        if(Arg[0]!='\\') PrimeList += ' ';
        PrimeList += Arg;
        Str = copy(Str,0,Pos) + TempTail;
        ch2 = Str() + Pos + 1;

        PrimeCount++;
        }

      } while(isspace(*ch2) || *ch2==1 || *ch2=='\'');

    if(PrimeCount>1)
    {    
      PrimeList += '}';
      
      int Pos2 = ch2 - Str();

      //string temp1 = copy(Str,0,Pos);
      //string temp2 = copy(Str,Pos2,Str.length()-Pos2);

      Str = copy(Str,0,Pos) + PrimeList + copy(Str,Pos2,Str.length()-Pos2);
      ch = strchr(Str()+Pos2, '\'');
    }
    else
       ch = strchr(ch2, '\'');

    PrimeCount = 0;
  }

  return Str;
}


/** Main formula string converting and optimizing procedure. */
bool OptimizeFormulaStr(TconvertedPass1 *cq, string & StrBeg)
{
int i, j;
string StrEnd, arg[3];
uint8_t styles;

  StrBeg.trim();
  if(((length(StrBeg)>2) && (StrBeg[length(StrBeg)-2]!='\\'))   //fix one error when equation end with dollar $
	 && (StrBeg[length(StrBeg)-1]=='&'))
		StrBeg = copy(StrBeg,0,length(StrBeg)-1);
	//We have all equation contents in the string StrBeg at this point

  if(StrBeg.isEmpty()) return(false);  /*equation is empty-nothing to do*/

  StrBeg = replacesubstring(StrBeg,"\1}","\\}");	// fix \} in WP -> \1 -> "\}"
  StrBeg = replacesubstring(StrBeg,"\1{","\\{");	// fix \} in WP -> \1 -> "\}"
  bool error = FixFormula(cq,StrBeg,i,j);

	//Here starts an equation conversion phase

	//Fix multiline equations here
  j = 0;
  for(i=0; i<length(StrBeg); i++)
	{
	if(StrBeg[i]=='\\')
		{
		i++;
		continue;
		}
	if(StrBeg[i]=='{') j++;
	if(StrBeg[i]=='}') j--;
	if(StrBeg[i]=='#' && j==0)
		{
		StrBeg = copy(StrBeg,0,i)+" \\nonumber # "+copy(StrBeg,i+1,length(StrBeg)-i-1);
		i+=11;   //  > strlen(" \\nonumber # ")-1
		}
	}

// if(cq->log!=NULL) fprintf(cq->log,"\n%s\n",StrBeg()); ///<<<<<

  StrEnd.erase();
  for(i=0; i<nFmlTransWords; i++)
      {
      StrEnd = StrBeg + StrEnd;
      StrBeg.erase();

      while(RemoveSymbol(StrBeg, StrEnd, FmlTransTable[i].szWP))
	   {
	   //if(cq->log!=NULL) fprintf(cq->log,"\n%s |||| %s\n",chk(StrBeg()),chk(StrEnd())); ///<<<<<
	   if(FmlTransTable[i].styles != 0) /* check additional styles */
		{
		styles = FmlTransTable[i].styles;
		if((styles & WP2LATEX) != 0) 
		  {
		  WP2LaTeXsty += sty_between;
		  WP2LaTeXsty += sty_bigominus;
		  WP2LaTeXsty += sty_bigsqcap;
		  WP2LaTeXsty += sty_because;
		  WP2LaTeXsty += sty_degrees;
		  WP2LaTeXsty += sty_identical;
		  WP2LaTeXsty += sty_image;
		  WP2LaTeXsty += sty_rimage;
		  WP2LaTeXsty += sty_therefore;
		  WP2LaTeXsty += sty_mathsmaller;
		  styles=0;
		  }
		if( (styles & AMSSYMB) != 0)
		  {
		  if(Amssymb>=STYLE_NOTUSED) {Amssymb=STYLE_USED; styles=0;}
		  }
                if( (styles & ACCENTS) != 0)
		  {
		  if(Accents>=STYLE_NOTUSED) {Accents=STYLE_USED; styles=0;}
		  }
                if((styles & SCALEREL) != 0)
		  {
		  if(Scalerel>=STYLE_NOTUSED) {Scalerel=STYLE_USED; styles=0;}
		  }
		if( (styles & LATEXSYM) != 0)
		  {
		  if ((LaTeX_Version & 0xFF00) >= 0x300)
		    {
		    if(LaTeXsym>=STYLE_NOTUSED) {LaTeXsym=STYLE_USED;styles=0;}
		    }
		  else styles=0;
		  }

		if(styles!=0)  /* The proper style cannot be used */
			{
			StrBeg += FmlTransTable[i].szWP;
			break;
			}
		}

	   arg[0].erase();
           arg[1].erase();
	   arg[2].erase();

	   if(strchr(FmlTransTable[i].szTeX, '8') != NULL)
	       {
	       RemoveAdvancedLastArg(StrBeg, arg[0]);
	       }
	   if(strchr(FmlTransTable[i].szTeX, '0') != NULL)
	       {
	       RemoveLastArg(StrBeg,arg[0]);
	       }

	   if(strchr(FmlTransTable[i].szTeX, '9') != NULL)
	       {
	       if(FmlTransTable[i].szWP[0]=='_') 
			RemoveAdvanced1stArg(StrEnd, arg[1], '^');	// Stop on oposite accent
	       else if(FmlTransTable[i].szWP[0]=='^') 
			RemoveAdvanced1stArg(StrEnd, arg[1], '_');	// Stop on oposite accent
	       else RemoveAdvanced1stArg(StrEnd, arg[1]);		// Grab both subscript and also superscript.
	       } 
           else if(strchr(FmlTransTable[i].szTeX, '1') != NULL)
	       {
	       Remove1stArg(StrEnd,arg[1]);
	       }

	   if(strchr(FmlTransTable[i].szTeX, '2') != NULL)
	       {
	       Remove1stArg(StrEnd,arg[2]);
	       }

	   if(FmlTransTable[i].szTeX[0]=='\\' || FmlTransTable[i].szTeX[0]=='{')
			   StrBeg = trim(StrBeg);
	   StrEnd = trim(StrEnd);

	   Interpret(cq, StrBeg, StrEnd, FmlTransTable[i].szTeX,
		     FmlTransTable[i].szWP, arg);
	   }
      }

  StrEnd = StrBeg + StrEnd; /* Fix double superscript/subscript */
  StrBeg.erase();

  //if(cq->log!=NULL) fprintf(cq->log,"\n%s\n",chk(StrEnd())); ///<<<<<

	// Check braces \left and \right
  if(CheckFormulaLeftRight(StrEnd(),i,j))
  {
    error = true;
    if(cq->err != NULL)
      {
      cq->perc.Hide();
      fprintf(cq->err, _("\nWarning: Formula #%d is syntactically wrong, No. of '\\left' - '\\right' is %d, %d!"),FormulaNo,i,j);
      }
    while(j<0 || i<0)
      {
      j++;
      i++;
      StrBeg += "\\left . ";
      }

    StrEnd = StrBeg + StrEnd;
    StrBeg.erase();
    while(i>0 && RemoveSymbol(StrBeg, StrEnd, "\\left"))
    {
      Remove1stArg(StrEnd, arg[1]);
      RemoveAdvanced1stArg(StrEnd, arg[2]);
      StrEnd.trim();
      StrBeg += "\\left ";
      StrBeg += arg[1];
      StrBeg += arg[2];
      if(strncmp(StrEnd(), "\\right", 6))	// If NOT present \right closing brace.
      {
        StrBeg += "\\right . ";
        i--;
      }
    }

    while(i>0)
      {
      i--;
      StrEnd += "\\right .";
      }
    StrEnd = StrBeg + StrEnd;
    StrBeg.erase();
  }

  FixSubSup(StrBeg,StrEnd);

  StrBeg += StrEnd; /* Prepare equation for writing */
  StrEnd.erase();

  StrBeg = FixMultiplePrime(StrBeg);

  StrBeg = replacesubstring(StrBeg,"\1\1","\\backslash");
  StrBeg = replacesubstring(StrBeg,"\1"," ");              /*fix \ in WP -> \1 -> ' '*/

  if(error)
    StrBeg = _("% Formula error fixed by WP2LaTeX!\n") + StrBeg;
    //StrBeg += _("% Formula error fixed by WP2LaTeX!");
return(error);
}


/** This procedure writes starting command for the given box. */
bool BoxTexHeader(TconvertedPass1 *cq, const TBox & Box)
{

  switch(Box.Type)
    {
    case 0:switch(Box.AnchorType)
	     {
	     case 0:fprintf(cq->strip, "\\begin{figure}[htbp]");
		    return(true);
	     case 1:fprintf(cq->strip, "\\begin{figure}[p]");
		    return(true);
	     case 2:if(Box.Contents==3)
			{                       // Graphics in the minipage
			fprintf(cq->strip,"%%");
			return(false);
			}
		    fprintf(cq->strip, "\\makebox[%2.2fcm]{",float(Box.Width)/10.0);
		    break;
	     default:fprintf(cq->strip, "\\begin{figure}[htbp]");
		     return(true);
	     }
	   break;
    case 1:switch(Box.AnchorType)
	     {
	     case 0:fprintf(cq->strip, "\\begin{table}[htbp]");
		    return(true);
	     case 1:fprintf(cq->strip, "\\begin{table}[p]");
		    return(true);
	     case 2:if(Box.Contents==3)
			{                       // Graphics in the minipage
			fprintf(cq->strip,"%%");
			return(false);
			}
		    fprintf(cq->strip, "\\makebox[%2.2fcm]{",float(Box.Width)/10.0);
		    break;
	     default:fprintf(cq->strip, "\\begin{table}[htbp]");
		     return(true);
	     }
	    break;
    default:if(Box.Contents==3)
			{                       // Graphics in the minipage
			fprintf(cq->strip,"%%");
			return(false);
			}
	    fprintf(cq->strip,"\\begin{minipage}");
	    if(Box.HorizontalPos==3) fprintf(cq->strip,"{\\textwidth}");
				else fprintf(cq->strip,"{%2.2fcm}",float(Box.Width)/10.0);
    }

return(false);
}


/** This procedure writes ending command for the given box. */
void BoxTexFoot(TconvertedPass1 *cq, const TBox & Box)
{
  switch(Box.Type)
    {
    case 0:switch(Box.AnchorType)
	     {
	     case 0:
	     case 1:fprintf(cq->strip, "\\end{figure}"); break;
	     case 2:if(Box.Contents==3)
			{                       // Graphics contents
			fprintf(cq->strip,"%%");
			break;
			}
		    fprintf(cq->strip, "}");		 break;
	     default:fprintf(cq->strip, "\\end{figure}");break;
	     }
	    break;
    case 1:switch(Box.AnchorType)
	     {
	     case 0:
	     case 1:fprintf(cq->strip, "\\end{table}");	break;
	     case 2:if(Box.Contents==3)
			{                       // Graphics contents
			fprintf(cq->strip,"%%");
			break;
			}
		    fprintf(cq->strip, "}");		break;
	     default:fprintf(cq->strip, "\\end{table}");break;
	     }
	    break;
    default:if(Box.Contents==3) fprintf(cq->strip,"%%");
			   else fprintf(cq->strip,"\\end{minipage}");
    }
}


/* static const struct FmlTranslationArray PreTransTable[] =
{
	"\\i",	"\\imath",	0,
	"\\j",	"\\jmath",	0,
}; */


/** This function fixes LaTeX string before including into the formula string. */
string & FixFormulaStrFromTeX(string & StrEnd, const char chr_set)
{
  if(StrEnd[0] == '$')
  {
    StrEnd = copy(StrEnd,1,length(StrEnd)-2);
    if((("\\Biggl" IN StrEnd || "\\Biggr" IN StrEnd ||
	 "\\biggl" IN StrEnd || "\\biggr" IN StrEnd ) && length(StrEnd)>6) ||
	 (("\\Bigl" IN StrEnd || "\\Bigr" IN StrEnd ||
	  "\\bigl" IN StrEnd || "\\bigr" IN StrEnd ) && length(StrEnd)>5))
		StrEnd="{"+StrEnd+"}";
    else StrEnd+=' ';
  }
  else 
  {
    if(chr_set==10)
    {
      StrEnd = "{\\cyr " + StrEnd + "}";
      Cyrillic = true;
    }
    else
    {
      if("\\'{" IN StrEnd || "\\v{" IN StrEnd || "\\r{" IN StrEnd)
        StrEnd = "{" + StrEnd + "}";
      else
        StrEnd = "{\\rm " + StrEnd + "}";
    }
  }

  if("---{}" IN StrEnd)
  {
    if(AmsMath <= STYLE_DISABLED)
    {
      StrEnd = replacesubstring(StrEnd,"{\\rm ---{}}","\\mbox{---}");
    }
    else
    {
      StrEnd = replacesubstring(StrEnd,"{\\rm ---{}}","\\text{---}");
      StrEnd = replacesubstring(StrEnd,"---{}","\\text{---}");
      AmsMath = STYLE_USED;
    }
  }
  if("--{}" IN StrEnd)
  {
    if(AmsMath <= STYLE_DISABLED)
    {
      StrEnd = replacesubstring(StrEnd,"{\\rm --{}}","\\mbox{--}");
    }
    else
    {
      StrEnd = replacesubstring(StrEnd,"{\\rm --{}}","\\text{--}");
      StrEnd = replacesubstring(StrEnd,"--{}","\\text{--}");
      AmsMath = STYLE_USED;
    }
  }

  if("\\ss{}" IN StrEnd)
  {
    if(AmsMath <= STYLE_DISABLED)
    {
      StrEnd = replacesubstring(StrEnd,"\\rm \\ss{}","\\mbox{\\ss}");
    }
    else
    {
      AmsMath = STYLE_USED;
      if("\\rm \\ss{}" IN StrEnd)
          StrEnd = replacesubstring(StrEnd,"\\rm \\ss{}","\\text{\\ss}");
      else
          StrEnd = replacesubstring(StrEnd,"\\ss{}","\\text{\\ss}");
    }
  }

  string StrBeg;
  while(RemoveSymbol(StrBeg, StrEnd, "\\i"))
  {
    if(StrEnd.length()<=0 || !isalpha(StrEnd[0]))
      StrBeg += "\\imath ";
    else
      StrBeg += "\\i";		// There is some larger command like \isomething.
  }
  StrEnd = StrBeg + StrEnd;
  StrBeg.erase();
  while(RemoveSymbol(StrBeg, StrEnd, "\\j"))
  {
    if(StrEnd.length()<=0 || !isalpha(StrEnd[0]))
      StrBeg += "\\jmath ";
    else
      StrBeg += "\\j";		// There is some larger command like \jsomething.
  }
  StrEnd = StrBeg + StrEnd;
  StrBeg.erase();

  StrEnd = replacesubstring(StrEnd,"\\\"","\\ddot");	// Fix umlaut, \" cannot be placed in math mode.

	// https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/MathAccents.html
  StrEnd = replacesubstring(StrEnd,"\\'","\2acute ");
  StrEnd = replacesubstring(StrEnd,"\\=","\2bar ");  
  StrEnd = replacesubstring(StrEnd,"\\.","\2dot ");
  StrEnd = replacesubstring(StrEnd,"\\`","\2grave ");
  StrEnd = replacesubstring(StrEnd,"\\^","\2hat ");
  StrEnd = replacesubstring(StrEnd,"\\~","\2tilde ");
  StrEnd = replacesubstring(StrEnd,"\\accent23U","\2mathring");
  StrEnd = replacesubstring(StrEnd,"\\r{","\2mathring {");  
  StrEnd = replacesubstring(StrEnd,"\\u{","\2breve {");
  StrEnd = replacesubstring(StrEnd,"\\v{","\2check {");

return(StrEnd);
}


/*---------- End of formulas.cc.-------------*/
