/************************************************************************
 ************************************************************************
    FAUST compiler
	Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
    ---------------------------------------------------------------------
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ************************************************************************
 ************************************************************************/



#include <stdio.h>
#include <string.h>
#include "doc_Text.hh"
#include "compatibility.hh"
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <assert.h>
#include <cmath>
#include <stdlib.h>

#include "floats.hh"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#endif

#ifndef M_PI_4
#define M_PI_4 0.785398163397448309616
#endif

#ifndef M_E
#define M_E 2.71828182845904523536
#endif

extern bool gInternDoubleSwitch;
const string symbolicNumber (double n);



#if 0
/**
 * Suppress trailing zero in a string representating a floating point number.
 * example : 1.00000  -> 1.0
 * example : 1.00000f -> 1.0f
 */

static void zdel(char* c)
{
    int     l = strlen(c) - 1;
    bool    f = (c[l] == 'f');

    if (f) c[l--] = 0;      // remove trailing if any f
    while ( l>1 && c[l-1] != '.' && c[l] == '0')  c[l--] = 0;
    if (f) c[++l] = 'f';    // restaure trailing f if needed
}
#endif

string docT (char* c) 	{ return string(c); }
string docT (int n) 	{ char c[64]; snprintf(c, 63, "%d",n);  return string(c); }
string docT (long n) 	{ char c[64]; snprintf(c, 63, "%ld",n); return string(c); }
string docT (double n) { return symbolicNumber(n); }


//
//*****************************SYMBOLIC NUMBER REPRESENTATION*******************
//

/**
 * Compute the smallest float representable
 * difference epsilon such that 1 != 1+epsilon
 */
float fltEpsilon()
{
   float machEps = 1.0f;
   do {
      machEps /= 2.0f;
   } while ((float)(1.0 + (machEps/2.0)) != 1.0);
   return machEps;
}


/**
 * Compute the smallest double representable
 * difference epsilon such that 1 != 1+epsilon
 */
double dblEpsilon()
{
   double machEps = 1.0f;
   do {
      machEps /= 2.0f;
   } while ((1.0 + (machEps/2.0)) != 1.0);
   return machEps;
}


/**
 * Check if two floating point numbers are (almost) equal
 * Abs(x-y) < epsilon
 */
static bool AlmostEqual(double A, double B)
{
    double maxRelativeError = 2*dblEpsilon();
    double maxAbsoluteError = maxRelativeError;


    if (fabs(A - B) < maxAbsoluteError)
        return true;
    double relativeError;
    if (fabs(B) > fabs(A))
        relativeError = fabs((A - B) / B);
    else
        relativeError = fabs((A - B) / A);
    if (relativeError <= maxRelativeError)
        return true;
    return false;
}


/**
 * Return true if n>0 is equal to PI^k for some small integer k.
 * k = log(n)/log(pi) is integer => n = exp(int(k)*log(pi))
 * The latex representation \pi^{k} is returned in string s
 */
bool isPiPower (double n, string& s)
{
    assert(n>0);
    stringstream ss (stringstream::out|stringstream::in);
    int k = floor(log(n)/log(M_PI));
    if ( AlmostEqual(n, exp(k * log(M_PI))) && (k!=0) && (abs(k)<5.0) ) {
        ss << "\\pi";
        if (k!=1)  ss << "^{"<< k <<"}";
        s = ss.str();
        return true;
    } else {
        return false;
    }
}


/**
 * Return true if n>0 is equal to e^k for some small integer k.
 * The latex representation e^{k} is returned in string s
 */
bool isExpPower (double n, string& s)
{
    assert(n>0);
    stringstream ss (stringstream::out|stringstream::in);
    int k = floor(log(n));
    if ( AlmostEqual(n, exp(k)) && (k!=0) && (abs(k)<5.0) ) {
        ss << "e";
        if (k!=1)  ss << "^{"<< k <<"}";
        s = ss.str();
        return true;
    } else {
        return false;
    }
}


/**
 * Return true if n>0 is equal to e^k or PI^k for some integer k
 * The symbolic latex representation is returned in string s
 */
bool isSymbolicPower (double n, string& s)
{
    assert(n>0);
    if (isPiPower(n,s)) {
        return true;
    } else if (isExpPower(n,s)) {
        return true;
    } else {
        return false;
    }
}


/**
 * Return exp or num.exp, or exp/denom, or num/denom.exp
 */
const string addFraction (int num, int denom, const string& exp)
{
    stringstream ss (stringstream::out|stringstream::in);

    if ((num==1) & (denom==1)) {
        ss << exp;
    } else if ((num==1) & (denom!=1)) {
        ss << "\\frac{"<< exp <<  "}{" << denom << "}";
    } else if ((num!=1) & (denom==1)) {
        ss << num << "*" << exp;
    } else {
        ss << "\\frac{"<< num <<  "}{" << denom << "}*" << exp;
    }
    return ss.str();
}


/**
 * Return symbolic or numerical representation of n>0
 */
const string positiveSymbolicNumber (double n)
{
    string s;
    assert(n>0);

    // Try to find a symbolic representation

    for (int i=1;i<10;i++) {
        for(int j=1;j<10;j++) {
            if (isSymbolicPower(i*n/j,s)) {
                return addFraction(j,i,s);
            }
        }
    }

    // No symbolic representation,
    // Then numerical representation x.10^k

    char tmp[64];
    string entree = " * 10^{";
    char sortie = '}';
    string::size_type ps;

    snprintf(tmp, 63, "%.15g", n); // Warning: over 15 decimals, results are wrong !!
    s = tmp;
    ps = s.find('e');

    if (ps != string::npos) {
        s.replace(ps, 1, "");
        s.insert(ps, entree);
        s += sortie;
    }

    return s;

}


/**
 * Return symbolic or numerical representation of n
 */
const string symbolicNumber (double n)
{
    if (n>0.0) {
        return positiveSymbolicNumber(n);
    } else if (n<0.0) {
        return string("-") + positiveSymbolicNumber(-n);
    } else {
        return "0";
    }
}
