/* TeXForm: convert Yacas objects to TeX math mode strings */

/* version 0.4 */

/* Changelog
	0.1	basic functionality
	0.2 fixed bracketing of Exp, added all infix ops and math functions
	0.3 fixed bracketing of lists, changed bracketing of math functions, modified TeX representation of user-defined functions (up to two-letter functions are in italics), added TeX Greek letters
	0.4 added nth roots, Sum, Limit, Integrate, hyperbolics, set operations, Abs, Max, Min, "==", ":=", Infinity; support indexed expressions A[i] and matrices.
*/

/* To do:
	0. Find and fix bugs.
	1. The current bracketing approach has limitations: can't omit extra brackets sometimes. " sin a b" is ambiguous, so need to do either "sin a sin b" or "(sin a) b" Hold((a*b)*Sqrt(x)). This doesn't hurt of course.
	2. Need to figure out how to deal with variable names such as "a2" or "alpha3"
*/

TexForm(_expr) <-- [WriteString(TeXForm(expr));NewLine();];

RuleBase("TeXForm",{expression});
RuleBase("TeXForm",{expression, precedence});

/* Boolean predicate */


/* this function will put TeX brackets around the string if predicate holds */

Function ("TeXFormBracketIf", {predicate, string})
[
	Check(IsBoolean(predicate) And IsString(string), "TeXForm internal error: non-boolean and/or non-string argument of TeXFormBracketIf");
	If(predicate, ConcatStrings("\\left( ", string, "\\right) "), string);
];

/* First, we convert TeXForm(x) to TeXForm(x, precedence). The enveloping precedence will determine whether we need to bracket the results. So TeXForm(x, TeXFormMaxPrec) will always print "x", while TeXForm(x,-TeXFormMaxPrec) will always print "(x)".
*/

TeXFormMaxPrec := 60000;	 /* This precedence will never be bracketed. It is equal to KMaxPrec */

100 # TeXForm(_x) <-- ConcatStrings("$", TeXForm(x, TeXFormMaxPrec), "$");

/* Replace numbers and variables -- never bracketed except explicitly */
110 # TeXForm(Pi, _p) <-- "\\pi ";
110 # TeXForm(I, _p) <-- "i";
110 # TeXForm(Infinity, _p) <-- "\\infty ";
110 # TeXForm(x_IsNumber, _p) <-- String(x);
/* Variables */
200 # TeXForm(x_IsAtom, _p) <-- TeXFormTextify(String(x));

/* Strings must be quoted but not bracketed */
100 # TeXForm(x_IsString, _p) <-- ConcatStrings("\\mathrm{", x, "}");

/* Lists: make sure to have matrices processed before them. Enveloping precedence is irrelevant because lists are always bracketed. List items are never bracketed. Note that TeXFormFinishList({a,b}) generates ",a,b" */

100 # TeXForm(x_IsList, _p)_(Length(x)=0) <-- TeXFormBracketIf(True, "");
110 # TeXForm(x_IsList, _p) <-- TeXFormBracketIf(True, ConcatStrings(TeXForm(Head(x), TeXFormMaxPrec), TeXFormFinishList(Tail(x)) ) );
100 # TeXFormFinishList(x_IsList)_(Length(x)=0) <-- "";
110 # TeXFormFinishList(x_IsList) <-- ConcatStrings(", ", TeXForm(Head(x), TeXFormMaxPrec), TeXFormFinishList(Tail(x)));

/* Replace operations */

/* arithmetic */

	/* addition, subtraction, multiplication, all comparison and logical operations are "regular" */

TeXFormRegularOps := { {"+"," + "}, {"-"," - "}, {"*"," "}, {":=","\\equiv "}, {"=="," = "}, {"="," = "}, {"!=","\\neq "}, {"<=","\\leq "}, {">=","\\geq "}, {"<"," < "}, {">"," > "}, {"And"," \\& "}, {"Or"," | "} };

	/* This is the template for "regular" binary infix operators:
100 # TeXForm(_x + _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("+"), ConcatStrings(TeXForm(x, OpLeftPrecedence("+")), " + ", TeXForm(y, OpRightPrecedence("+")) ) );
	*/ 

120 # TeXForm(expr_IsFunction, _p)_(NrArgs(expr)=2 And Contains(AssocIndices(TeXFormRegularOps), Type(expr)) ) <-- TeXFormBracketIf(p<OpPrecedence(Type(expr)), ConcatStrings(TeXForm(Listify(expr)[2], OpLeftPrecedence(Type(expr))), TeXFormRegularOps[Type(expr)], TeXForm(Listify(expr)[3], OpRightPrecedence(Type(expr))) ) );

	/* unary addition */
100 # TeXForm(+ _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("+"), ConcatStrings(" + ", TeXForm(y, OpRightPrecedence("+")) ) );

	/* unary subtraction */
100 # TeXForm(- _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("-"), ConcatStrings(" - ", TeXForm(y, OpRightPrecedence("-")) ) );

	/* division and its operands are never bracketed */
100 # TeXForm(_x / _y, _p) <-- ConcatStrings("\\frac{", TeXForm(x, TeXFormMaxPrec), "}{", TeXForm(y, TeXFormMaxPrec), "} ");

	/* power's argument is never bracketed but it must be put in braces. Chained powers must be bracketed. Powers of 1/n are displayed as roots. */
100 # TeXForm(_x ^ (1/_y), _p) <-- ConcatStrings("\\sqrt[", TeXForm(y, TeXFormMaxPrec), "]{", TeXForm(x, TeXFormMaxPrec), "}");

120 # TeXForm(_x ^ _y, _p) <-- TeXFormBracketIf(p<=OpPrecedence("^"), ConcatStrings(TeXForm(x, OpPrecedence("^")), " ^{", TeXForm(y, TeXFormMaxPrec), "}" ) );

/* functions */

	/* Unknown function, precedence 200. Leave as is, never bracket the function itself and bracket the argument(s) automatically since it's a list. Other functions are precedence 100 */

TeXFormGreekLetters := {"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", "Phi", "Psi", "Omega", "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu", "nu", "xi", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", "varpi", "varrho", "varsigma", "varphi", "varepsilon"};

/* this function will take a user-defined variable or function name and output either this name unmodified if it's only 2 characters long, or the name in normal text if it's longer, or a TeX Greek letter code */
Function ("TeXFormTextify", {string})
[
	Check(IsString(string), "TeXForm internal error: non-string argument of TeXFormTextify");
	/* Check if it's a greek letter */
	If(Contains(TeXFormGreekLetters, string), ConcatStrings("\\", string, " "), If(Length(string)>2, ConcatStrings("\\mathrm{", string, "}"), string ) );
];

200 # TeXForm(_x, _p)_(IsFunction(x)) <-- ConcatStrings(TeXFormTextify(Type(x)), TeXForm(Tail(Listify(x)), TeXFormMaxPrec) ); 

	/* Never bracket Sqrt or its arguments */
100 # TeXForm(Sqrt(_x), _p) <-- ConcatStrings("\\sqrt{", TeXForm(x, TeXFormMaxPrec), "}"); 

	/* Always bracket Exp's arguments */
100 # TeXForm(Exp(_x), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings("\\exp ", TeXFormBracketIf(True, TeXForm(x, TeXFormMaxPrec)) ) ); 

/* Sin, Cos, etc. and their argument is bracketed when it's a sum or product but not bracketed when it's a power. */

TeXFormMathFunctions := { {"Cos","\\cos "}, {"Sin","\\sin "}, {"Tan","\\tan "}, {"Cosh","\\cosh "}, {"Sinh","\\sinh "}, {"Tanh","\\tanh "}, {"Ln","\\ln "}, {"ArcCos","\\arccos "}, {"ArcSin","\\arcsin "}, {"ArcTan","\\arctan "}, {"ArcCosh","\\arccosh "}, {"ArcSinh","\\arcinh "}, {"ArcTanh","\\arctanh "}, {"Max","\\max "}, {"Min","\\min "} };

/* Precedence of 120 because we'd like to process other functions like sqrt or exp first */

120 # TeXForm(expr_IsFunction, _p) _ (NrArgs(expr)=1 And Contains(AssocIndices(TeXFormMathFunctions), Type(expr)) ) <-- TeXFormBracketIf(p<OpPrecedence("-"), ConcatStrings(TeXFormMathFunctions[Type(expr)], TeXForm( Listify(expr)[2], OpPrecedence("*")) ) );

/* Complex numbers */
100 # TeXForm(Complex(0, 1), _p) <-- "I";
100 # TeXForm(Complex(_x, 0), _p) <-- TeXForm(x, p);
110 # TeXForm(Complex(_x, 1), _p) <-- TeXForm(x+Hold(I), p);
110 # TeXForm(Complex(0, _y), _p) <-- TeXForm(Hold(I)*y, p);
120 # TeXForm(Complex(_x, _y), _p) <-- TeXForm(x+Hold(I)*y, p);

/* Abs() is displayed as special brackets */

100 # TeXForm(Abs(_x), _p) <-- ConcatStrings("\\left| ", TeXForm(x, TeXFormMaxPrec), "\\right| ");

/* Some functions which are displayed as infix: Mod, Union, Intersection, Difference, Contains */

100 # TeXForm(Mod(_x, _y), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(TeXForm(x, OpPrecedence("/")), "\\bmod ", TeXForm(y, OpPrecedence("/")) ) );

100 # TeXForm(Union(_x, _y), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(TeXForm(x, OpPrecedence("/")), "\\cup ", TeXForm(y, OpPrecedence("/")) ) );

100 # TeXForm(Intersection(_x, _y), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(TeXForm(x, OpPrecedence("/")), "\\cap ", TeXForm(y, OpPrecedence("/")) ) );

100 # TeXForm(Difference(_x, _y), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(TeXForm(x, OpPrecedence("/")), "\\setminus ", TeXForm(y, OpPrecedence("/")) ) );

/* "Contains" is displayed right to left */

100 # TeXForm(Contains(_x, _y), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(TeXForm(y, OpPrecedence("/")), "\\in ", TeXForm(x, OpPrecedence("/")) ) );

/* Some functions with limits: Lim, Sum, Integrate */

100 # TeXForm(Sum(_x, _x1, _x2, _expr), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings("\\sum _{", TeXForm(x, TeXFormMaxPrec), " = ", TeXForm(x1, TeXFormMaxPrec), " } ^{", TeXForm(x2, TeXFormMaxPrec), "} ", TeXForm(expr, OpPrecedence("/")) ) ); 

100 # TeXForm(Integrate(_x, _x1, _x2) _expr, _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings("\\int _{", TeXForm(x1, TeXFormMaxPrec), " } ^{", TeXForm(x2, TeXFormMaxPrec), " }d ", TeXForm(x, TeXFormMaxPrec), TeXForm(expr, OpPrecedence("/")) ) ); 

100 # TeXForm(Limit(_x, _x1) _expr, _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings("\\lim _{", TeXForm(x, TeXFormMaxPrec), "\\rightarrow ", TeXForm(x1, TeXFormMaxPrec), "} ", TeXForm(expr, OpPrecedence("/")) ) ); 

/* Derivatives */

100 # TeXForm(Deriv(_x)_y, _p) <-- TeXFormBracketIf(p<OpPrecedence("-"), ConcatStrings("\\frac{\\partial}{\\partial ", TeXForm(x, OpPrecedence("^")), "}", TeXForm(y, OpPrecedence("/")) ) );

/* Indexed expressions */

100 # TeXForm(_x [ _i ], _p) <-- ConcatStrings(TeXForm(x, TeXFormMaxPrec), " _{", TeXForm(i, TeXFormMaxPrec), "}");

/* Matrices are always bracketed. Precedence 80 because lists are at 100. */

80 # TeXForm(M_IsMatrix, _p) <-- TeXFormBracketIf(True, TeXFormPrintMatrix(M));

Function ("TeXFormPrintMatrix", {M})
[
/*
	Want something like "\begin{array}{cc} a & b \\ c & d \\ e & f \end{array}"
	here, "cc" is alignment and must be given for each column
*/
	Local(row, col, result, ncol);
	result := "\\begin{array}{";
	ForEach(col, M[1]) result:=ConcatStrings(result, "c");
	result := ConcatStrings(result, "}");
	
	ForEach(row, 1 .. Length(M)) [
		ForEach(col, 1 .. Length(M[row])) [
			result := ConcatStrings( result, " ", TeXForm(M[row][col], TeXFormMaxPrec), If(col = Length(M[row]), If(row = Length(M), "", " \\\\"), " &"));
		];
	];
	
	ConcatStrings(result, " \\end{array} ");
];

