/* 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.
	0.4.1 bugfixes for [] operator, support for multiple indices a[1][2][3]
	0.4.2 fix for variable names ending on digits "a2" represented as $a_2$
	0.4.3 bugfixes: complex I, indeterminate integration; relaxed bracketing of Sin()-like functions; implemented $TeX$ and $LaTeX$ correctly now (using \textrm{})
	0.4.4 use ordinary instead of partial derivative if expression has only one variable
	0.4.5 fixes for bracketing of Sum(); added <> to render as \sim and <=> to render as \approx; added Bin()
	0.4.6 moved the <> and <=> operators to stdopers.ys
	0.4.7 added Factorize() i.e. Product()
	0.4.8 added D(x,n), Deriv(x,n), =>, and fixed errors with ArcSinh, ArcCosh, ArcTanh
	0.4.9 fixed omission: (fraction)^n was not put in brackets
	0.4.10 cosmetic change: insert \cdot between numbers in cases like 2*10^n
*/

/* 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)). The current approach is *not* to bracket functions unless the enveloping operation is more binding than multiplication. This produces "sin a b" for both Sin(a*b) and Sin(a)*b but is the current mathematical practice.
	2. Need to figure out how to deal with variable names such as "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(x_IsNumber, _p) <-- String(x);
/* Variables */
200 # TeXForm(x_IsAtom, _p) <-- TeXFormTeXify(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 */

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

TeXFormRegularOps := { {"+"," + "}, {"-"," - "}, {"*"," "}, {":=","\\equiv "}, {"=="," = "}, {"="," = "}, {"!=","\\neq "}, {"<=","\\leq "}, {">=","\\geq "}, {"<"," < "}, {">"," > "}, {"And"," \\wedge "}, {"Or"," \\vee "}, {"<>", "\\sim "}, {"<=>", "\\approx "}, {"=>", "\\Rightarrow "} };

	/* Template for "regular" binary infix operators:
100 # TeXForm(_x + _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("+"), ConcatStrings(TeXForm(x, OpLeftPrecedence("+")), " + ", TeXForm(y, OpRightPrecedence("+")) ) );
	*/
// special cases: things like x*2 and 2*10^x look ugly without a period
115 # TeXForm(_expr * n_IsNumber, _p) <-- TeXFormBracketIf(p<OpPrecedence("*"), ConcatStrings(TeXForm(expr, OpLeftPrecedence("*")), "\\cdot ", TeXForm(n, OpRightPrecedence("*")) ) );
// cases such as 2*20!, 2*10^x
116 # TeXForm(n_IsNumber * _expr, _p) _ (IsFunction(expr) And Contains({"^", "!", "!!"}, Type(expr)) And IsNumber(Listify(expr)[2])) <-- TeXFormBracketIf(p<OpPrecedence("*"), ConcatStrings(TeXForm(n, OpLeftPrecedence("*")), "\\cdot ", TeXForm(expr, OpRightPrecedence("*")) ) );

	/* generic binary ops here */
120 # TeXForm(expr_IsFunction, _p)_(NrArgs(expr)=2 And IsInfix(Type(expr)) ) <-- TeXFormBracketIf(p<OpPrecedence(Type(expr)), ConcatStrings(TeXForm(Listify(expr)[2], OpLeftPrecedence(Type(expr))), TeXFormTeXify(Type(expr)), TeXForm(Listify(expr)[3], OpRightPrecedence(Type(expr))) ) );

TeXFormRegularPrefixOps := { {"+"," + "}, {"-"," - "}, {"Not"," \\neg "} };
	/* Template for "regular" unary prefix operators:
100 # TeXForm(+ _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("+"), ConcatStrings(" + ", TeXForm(y, OpRightPrecedence("+")) ) );
	Note that OpRightPrecedence needs to be defined for prefix ops or else we won't be able to tell combined prefix/infix ops like "-" from strictly prefix ops.
	*/

	/* generic unary ops here */
	/* prefix */
120 # TeXForm(expr_IsFunction, _p)_(NrArgs(expr)=1 And IsPrefix(Type(expr))) <-- TeXFormBracketIf(p<OpRightPrecedence(Type(expr)), ConcatStrings(
	TeXFormTeXify(Type(expr)),
	TeXForm(Listify(expr)[2], OpRightPrecedence(Type(expr)))
) );
	/* postfix */
120 # TeXForm(expr_IsFunction, _p)_(NrArgs(expr)=1 And IsPostfix(Type(expr))) <-- TeXFormBracketIf(p<OpLeftPrecedence(Type(expr)), ConcatStrings(
	TeXForm(Listify(expr)[2], OpLeftPrecedence(Type(expr))),
	TeXFormTeXify(Type(expr))
) );

	/* fraction and its operands are never bracketed except when they are under power */
100 # TeXForm(_x / _y, _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), 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/2), _p) <-- ConcatStrings("\\sqrt{", TeXForm(x, TeXFormMaxPrec), "}");
101 # 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"};

TeXFormSpecialNames := {
	{"I", "\\imath "},
	{"Pi", "\\pi "},
	{"Infinity", "\\infty "},
	{"TeX", "\\textrm{\\TeX\\/}"},
	{"LaTeX", "\\textrm{\\LaTeX\\/}"}
};

/* 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 ("TeXFormTeXify", {string})
[
	Check(IsString(string), "TeXForm internal error: non-string argument of TeXFormTeXify");
	/* Check if it's a greek letter or a special name */
	If (Contains(AssocIndices(TeXFormSpecialNames), string), TeXFormSpecialNames[string],
	If (Contains(TeXFormGreekLetters, string), ConcatStrings("\\", string, " "),
	If (Contains(AssocIndices(TeXFormRegularOps), string), TeXFormRegularOps[string],
	If (Contains(AssocIndices(TeXFormRegularPrefixOps), string), TeXFormRegularPrefixOps[string],
	If (Length(string) >= 2 And IsNumber(Atom(StringMid(2, Length(string)-1, string))), ConcatStrings(StringMid(1,1,string), "_{", StringMid(2, Length(string)-1, string), "}"),
	If (Length(string) > 2, ConcatStrings("\\mathrm{ ", string, " }"),
	string
	))))));
];

/* */

/* Unknown bodied function */

200 # TeXForm(x_IsFunction, _p) _ (IsBodied(Type(x))) <-- [
	Local(func, args, last'arg);
	func := Type(x);
	args := Tail(Listify(x));
	last'arg := PopBack(args);
	TeXFormBracketIf(p<OpPrecedence(func), ConcatStrings(
	  TeXFormTeXify(func), TeXForm(args, TeXFormMaxPrec),  TeXForm(last'arg, OpPrecedence(func))
	));
];

/* Unknown infix function : already done above
210 # TeXForm(x_IsFunction, _p)_(IsInfix(Type(x))) <-- ConcatStrings(TeXFormTeXify(Type(x)), TeXForm(Tail(Listify(x)), TeXFormMaxPrec) );
*/
/* Unknown function that is not prefix, infix or postfix */
220 # TeXForm(x_IsFunction, _p) <-- ConcatStrings(TeXFormTeXify(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","\\mathrm{arccosh}\\, "}, {"ArcSinh","\\mathrm{arcsinh}\\, "}, {"ArcTanh","\\mathrm{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) <-- TeXForm(Hold(I), p);
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("/")) ) );

/// Binomial coefficients: always bracketed
100 # TeXForm(Bin(_n, _m), _p) <-- TeXFormBracketIf(False, ConcatStrings("{", TeXForm(n, TeXFormMaxPrec), " \\choose ", TeXForm(m, TeXFormMaxPrec), "}" )
);

/* 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(Factorize(_x, _x1, _x2, _expr), _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings("\\prod _{", 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), " } ", TeXForm(expr, OpPrecedence("*")), " d", TeXForm(x, TeXFormMaxPrec)
) );

/* indeterminate integration */
100 # TeXForm(Integrate(_x) _expr, _p) <-- TeXFormBracketIf(p<OpPrecedence("/"), ConcatStrings(
"\\int ", TeXForm(expr, OpPrecedence("*")), " d", TeXForm(x, TeXFormMaxPrec)
) );

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

/* Derivatives */

/* Use partial derivative only when the expression has several variables */
100 # TeXForm(Deriv(_x)_y, _p) <-- TeXFormBracketIf(p<OpPrecedence("-"), ConcatStrings(
	If(Length(VarList(y))>1, "\\frac{\\partial}{\\partial ", "\\frac{d}{d "
	), TeXForm(x, OpPrecedence("^")), "}", TeXForm(y, OpPrecedence("/")) ) );

100 # TeXForm(Deriv(_x, _n)_y, _p) <-- TeXFormBracketIf(p<OpPrecedence("-"), ConcatStrings(
	If(
		Length(VarList(y))>1,
		"\\frac{\\partial^" : TeXForm(n, TeXFormMaxPrec) : "}{\\partial ",
		"\\frac{d^" : TeXForm(n, TeXFormMaxPrec) : "}{d "
	), TeXForm(x, OpPrecedence("^")), " ^", TeXForm(n, TeXFormMaxPrec), "}", TeXForm(y, OpPrecedence("/")) ) );
100 # TeXForm(D(_x)_y, _p) <-- TeXForm(Deriv(x) y, p);
100 # TeXForm(D(_x, _n)_y, _p) <-- TeXForm(Deriv(x, n) y, p);

/* Indexed expressions */

/* This seems not to work because x[i] is replaced by Nth(x,i) */
/*
100 # TeXForm(_x [ _i ], _p) <-- ConcatStrings(TeXForm(x, TeXFormMaxPrec), " _{", TeXForm(i, TeXFormMaxPrec), "}");
*/
/* Need to introduce auxiliary function, or else have trouble with arguments of Nth being lists */
100 # TeXForm(Nth(Nth(_x, i_IsList), _j), _p) <-- TeXForm(TeXFormNth(x, Append(i,j)), p);
100 # TeXForm(TeXFormNth(Nth(_x, i_IsList), _j), _p) <-- TeXForm(TeXFormNth(x, Append(i,j)), p);
110 # TeXForm(Nth(Nth(_x, _i), _j), _p) <-- TeXForm(TeXFormNth(x, List(i,j)), p);
120 # TeXForm(Nth(_x, _i), _p) <-- ConcatStrings(TeXForm(x, TeXFormMaxPrec), " _{", TeXForm(i, TeXFormMaxPrec), "}");
120 # TeXForm(TeXFormNth(_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} ");
];

