%mathpiper,def="LnCombine"

////////////////////// Log rules stuff //////////////////////

// LnCombine is nice and simple now
LnCombine(_a) <-- DoLnCombine(CanonicalAdd(a));

// Combine single terms.  This can always be done without a recursive call.
1 # DoLnCombine(Ln(_a))              <-- Ln(a);
1 # DoLnCombine(Ln(_a)*_b)           <-- Ln(a^b);
1 # DoLnCombine(_b*Ln(_a))           <-- Ln(a^b);

// Deal with the first two terms so they are both simple logs if at all
// possible.  This involves converting a*Ln(b) to Ln(b^a) and moving log terms
// to the start of expressions.  One of either of these operations always takes
// us to a strictly simpler form than we started in, so we can get away with
// calling DoLnCombine again with the partly simplified argument.

// TODO: Make this deal with division everywhere it deals with multiplication

// first term is a log multiplied by something
2 # DoLnCombine(Ln(_a)*_b+_c)        <-- DoLnCombine(Ln(a^b)+c);
2 # DoLnCombine(Ln(_a)*_b-_c)        <-- DoLnCombine(Ln(a^b)-c);
2 # DoLnCombine(_b*Ln(_a)+_c)        <-- DoLnCombine(Ln(a^b)+c);
2 # DoLnCombine(_b*Ln(_a)-_c)        <-- DoLnCombine(Ln(a^b)-c);

// second term of a two-term expression is a log multiplied by something
2 # DoLnCombine(_a+(_c*Ln(_b)))      <-- DoLnCombine(a+Ln(b^c));
2 # DoLnCombine(_a-(_c*Ln(_b)))      <-- DoLnCombine(a-Ln(b^c));
2 # DoLnCombine(_a+(Ln(_b)*_c))      <-- DoLnCombine(a+Ln(b^c));
2 # DoLnCombine(_a-(Ln(_b)*_c))      <-- DoLnCombine(a-Ln(b^c));

// second term of a three-term expression is a log multiplied by something
2 # DoLnCombine(_a+((Ln(_b)*_c)+_d)) <-- DoLnCombine(a+(Ln(b^c)+d));
2 # DoLnCombine(_a+((Ln(_b)*_c)-_d)) <-- DoLnCombine(a+(Ln(b^c)-d));
2 # DoLnCombine(_a-((Ln(_b)*_c)+_d)) <-- DoLnCombine(a-(Ln(b^c)+d));
2 # DoLnCombine(_a-((Ln(_b)*_c)-_d)) <-- DoLnCombine(a-(Ln(b^c)-d));

2 # DoLnCombine(_a+((_c*Ln(_b))+_d)) <-- DoLnCombine(a+(Ln(b^c)+d));
2 # DoLnCombine(_a+((_c*Ln(_b))-_d)) <-- DoLnCombine(a+(Ln(b^c)-d));
2 # DoLnCombine(_a-((_c*Ln(_b))+_d)) <-- DoLnCombine(a-(Ln(b^c)+d));
2 # DoLnCombine(_a-((_c*Ln(_b))-_d)) <-- DoLnCombine(a-(Ln(b^c)-d));

// Combine the first two terms if they are logs, otherwise move one or both to
// the front, then recurse on the remaining possibly-log-containing portion.
// (the code makes more sense than this comment)
3 # DoLnCombine(Ln(_a)+Ln(_b))       <-- Ln(a*b);
3 # DoLnCombine(Ln(_a)-Ln(_b))       <-- Ln(a/b);
3 # DoLnCombine(Ln(_a)+(Ln(_b)+_c))  <-- DoLnCombine(Ln(a*b)+c);
3 # DoLnCombine(Ln(_a)+(Ln(_b)-_c))  <-- DoLnCombine(Ln(a*b)-c);
3 # DoLnCombine(Ln(_a)-(Ln(_b)+_c))  <-- DoLnCombine(Ln(a/b)-c);
3 # DoLnCombine(Ln(_a)-(Ln(_b)-_c))  <-- DoLnCombine(Ln(a/b)+c);

// We know that at least one of the first two terms isn't a log
4 # DoLnCombine(Ln(_a)+(_b+_c))      <-- b+DoLnCombine(Ln(a)+c);
4 # DoLnCombine(Ln(_a)+(_b-_c))      <-- b+DoLnCombine(Ln(a)-c);
4 # DoLnCombine(Ln(_a)-(_b+_c))      <-- DoLnCombine(Ln(a)-c)-b;
4 # DoLnCombine(Ln(_a)-(_b-_c))      <-- DoLnCombine(Ln(a)+c)-b;

4 # DoLnCombine(_a+(Ln(_b)+_c))      <-- a+DoLnCombine(Ln(b)+c);
4 # DoLnCombine(_a+(Ln(_b)-_c))      <-- a+DoLnCombine(Ln(b)-c);
4 # DoLnCombine(_a-(Ln(_b)+_c))      <-- a-DoLnCombine(Ln(b)+c);
4 # DoLnCombine(_a-(Ln(_b)-_c))      <-- a-DoLnCombine(Ln(b)-c);

// If we get here we know that neither of the first two terms is a log
5 # DoLnCombine(_a+(_b+_c))          <-- a+(b+DoLnCombine(c));

// Finished
6 # DoLnCombine(_a)                  <-- a;


%/mathpiper



%mathpiper_docs,name="LnCombine",categories="User Functions;Expression Simplification"
*CMD LnCombine --- combine logarithmic expressions using standard logarithm rules
*STD
*CALL
	LnCombine(expr)

*PARMS

{expr} -- an expression possibly containing multiple {Ln} terms to be combined

*DESC

{LnCombine} finds {Ln} terms in the expression it is given, and combines them
using logarithm rules.  It is intended to be the exact converse of {LnExpand}.

*E.G.
In> LnCombine(Ln(a)+Ln(b)*n)
Result: Ln(a*b^n)
In> LnCombine(2*Ln(2)+Ln(3)-Ln(5))
Result: Ln(12/5)

*SEE Ln, LnExpand
%/mathpiper_docs