
#include <config.h>
#include <cmath.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "f2c.h"
#include "utils.h"


/* Transcrit de /usr/local/PV/scilab/nicecolorbar/nicecolorbar.sci */

void nicesegment (double xmi ,double xma ,double nb ,double *x0 ,double *x1);
static double nicenumber(double x, int round);

/**
 * niceanchor : return a "nice" anchor point in or near the interval  [xmi ,xma]
 *              Of course, the notion of "nice number" is rather subjective...
 *    with double, precision is limited to 16 decimal digits
 */
static double niceanchor (double xmi ,double xma)
{
  double xmip ,xmap      ,sgn ,lxm ,l10 ,anchor ,x ,xnice;
  int n ,i ,ld;
  double nicenb[11] = {5. ,2. ,2.5 ,4. ,3. ,5. ,6. ,7. ,8. ,9. ,1.};   /* in  preference order */
  double p10;

  /* If xmi and xma have opposite signs, 0.0 is the best  */
  if (xmi*xma <= 0.0) return (0.0);

  xmip = fabs(xmi);
  xmap = fabs(xma);
  /* Now xmi and xma have same sign and we want 0 < xmip < xmap */
  if (xmap < xmip) fswap (&xmip ,&xmap);

  sgn = 1.0;
  if (xmi < 0.0) sgn = -1.0;
  lxm = xmap - xmip;             /* length of the segment */


  /* xmip is near zero in terms of interval length */
  if (xmip/lxm < 0.02)  return (0.0);

  l10 = log10(xmap);
  n = ceil(l10);                 /* power of ten */

  /* Try first 10**n */
  anchor = pow(10.0 ,(double)n);
  /* anchor  is into the interval, thus anchor it is a good choice  */
  if ((anchor-xmip)*(anchor-xmap) < 0.0)  return (sgn * anchor);
    /* anchor is outside the interval but very near to the max,
     *  thus it is a good choice  */
  if (xmap/anchor > 0.98) return (sgn * anchor);

  /* When 10**n is not into the interval, we build a new anchor, digit by digit,
   * by picking "good numbers" into the list */
  anchor = 0.0;
  for (ld = 1; ld < 16; ld++) {           /* ld: last digit */
    p10 = pow (10.0 ,(double)(n-ld));     /* power of 10 examined: 10**(n-ld) */
    for (i = 0; i < 11; i++) {            /* is there a suitable nice number  */
      xnice = nicenb[i] * p10;            /*        with the current power ?  */
      x = anchor + xnice;
      if ((x-xmip)*(xmap-x) >= 0.0) {     /* x is in the segment: it is good */
	return (sgn * x);
      }
    }

    for (i = 1; i < 10; i++) {           /* looks for the digit giving  */
      x = anchor + (double)i*p10;        /* anchor immediatelly smaller */
      if (x > xmip) break;               /*               than the xmip */
    }
    anchor = anchor + (i-1)*p10;         /* i-1 gives the good one */
  }
  /* the search has failed */
  return (0.0);
}

/* From XMGR (the ancestor of xmgrace) an old version of nicenum */
static double nicenumber (double x, int round)
{
    int exp;
    double f, y;

    exp = floor(log10(x));
    f = x / pow(10., (double) exp);	/* fraction between 1 and 10 */

    if (round) {
      if (f < 1.5) {
	y = 1.;
      }	else if (f < 3.) {
	y = 2.;
      }	else if (f < 7.) {
	y = 5.;
      }	else {
	y = 10.;
      }
    } else {
      if (f <= 1.) {
	y = 1.;
      } else if (f <= 2.) {
	y = 2.;
      } else if (f <= 5.) {
	y = 5.;
      } else {
	y = 10.;
      }
    }
    return y * pow(10., (double) exp);
}


/* Computes a "nice" segment with approximately nb tics
 */
void nicesegment (double xmi ,double xma ,double nb ,double *x0 ,double *x1)
{
  double anchor ,lx ,dx;

  if (xma < xmi) fswap (&xmi ,&xma);

  anchor = niceanchor (xmi ,xma);
  lx 	 = nicenumber (xma-xmi ,0);    /* longueur intervalle */
  dx 	 = nicenumber (lx / nb ,1);    /* distance entre tics */

  *x0 = anchor - ceil((anchor-xmi)/dx)*dx;
  *x1 = anchor + ceil((xma-anchor)/dx)*dx;
}

/**
 * Returns the number of digit needed to differentiate two tics A REVOIR
 */
int niceprec (double x0 ,double x1)
{
  double ax0 ,ax1 ,axmax ,axmin ,umax ,dx ,udx ,dn;
  ax0 = fabs (x0);
  ax1 = fabs (x1);
  axmin = MIN2 (ax0 ,ax1);
  axmax = MAX2 (ax0 ,ax1);
  umax  = MAX2 (axmax ,1.0/axmin);
  dx = x1 -x0;
  udx = 10.0 / fabs (x1 -x0);  // assume 10 tics
  dn = log10 (umax);
  return (round(dn) + 1);
}


/**
 * Smart formatting to print "x" into "buf", tacking into
 *   account the segment of interest [x0,x1]
 * Avoid to write .xxxe-17 instead of 0. (under Windows)
 * Write x value with one space before and one space after
 */
void sprintf_with_niceprec (char *buf ,double x ,double x0 ,double x1)
{
  int prec;
  double ax ,ax0 ,ax1  ,axmax ,axmin ,adx ,dn;
  ax  = fabs (x);
  adx = fabs (x1 -x0) / 10.0;      /* assume 10 tics */
  if (ax/adx < 1.e-12) {
    sprintf (buf ," 0 ");
  } else {
    ax0 = fabs (x0);
    ax1 = fabs (x1);
    axmin = MIN2 (ax0 ,ax1);
    axmax = MAX2 (ax0 ,ax1);
    if (axmax >= 1.0 && axmax < 1.e6) {
      dn = -log10 (adx);
      if (dn < 0.0) dn = 0.0;
      prec = round(dn) + 1;
      sprintf (buf ," %.*f " ,prec ,x);
    } else if (axmin > 1.e-6 && axmin <= 1.0) {
      dn = - log10 (axmin);
      prec = round(dn) + 1;
      sprintf (buf ," %.*f " ,prec ,x);
    } else {
      dn = log10 (axmax / adx);
      prec = (round(dn) + 1);
      sprintf (buf ," %.*e " ,prec ,x);
    }
  }
}

