#include "decimal.h"
 #include <stdlib.h>
 
+#include "gl/xalloc.h"
+#include "gl/minmax.h"
+#include "gl/xvasprintf.h"
+
 static const double standard_tick[] = {1, 2, 5, 10};
 
 /* Adjust tick to be a sensible value
        }
     }
 }
+
+/*
+ * Compute the optimum format string and the scaling
+ * for the tick drawing on a chart axis
+ * Input:  max:     the maximum value of the range
+ *         min:     the minimum value of the range
+ *         nticks:  the number of tick intervals (bins) on the axis
+ * Return: fs:      format string for printf to print the tick value
+ *         scale:   scaling factor for the tick value
+ * The format string has to be freed after usage.
+ * An example format string and scalefactor:
+ * Non Scientific: "%.3lf", scale=1.00
+ * Scientific:     "%.2lfe3", scale = 0.001
+ * Usage example:
+ *   fs = chart_get_ticks_format(95359943.3,34434.9,8,&scale,&long);
+ *   printf(fs,value*scale);
+ *   free(fs);
+ */
+char *
+chart_get_ticks_format (const double max, const double min,
+                       const unsigned int nticks, double *scale)
+{
+  assert(max > min);
+  double interval = (max - min)/nticks;
+  double logmax = log10(fmax(fabs(max),fabs(min)));
+  double logintv = log10(interval);
+  int logshift = 0;
+  char *format_string = NULL;
+  int nrdecs = 0;
+
+  if (logmax > 0.0 && logintv < 0.0)
+    {
+      nrdecs = MIN(6,(int)(fabs(logintv))+1);
+      logshift = 0;
+      format_string = xasprintf("%%.%dlf",nrdecs);
+    }
+  else if (logmax > 0.0) /*logintv is > 0*/
+    {
+      if (logintv < 3.0)
+       {
+         logshift = 0; /* No scientific format */
+         nrdecs = 0;
+         format_string = xstrdup("%.0lf");
+       }
+      else
+       {
+         logshift = (int)logmax;
+         nrdecs = MIN(6,(int)(logmax-logintv)+1);
+         format_string = xasprintf("%%.%dlfe%d",nrdecs,logshift);
+       }
+    }
+  else /* logmax and logintv are < 0 */
+    {
+      if (logmax > -3.0)
+       {
+         logshift = 0; /* No scientific format */
+         nrdecs = (int)(-logintv) + 1;
+         format_string = xasprintf("%%.%dlf",nrdecs);
+       }
+      else
+       {
+         logshift = (int)logmax-1;
+         nrdecs = MIN(6,(int)(logmax-logintv)+1);
+         format_string = xasprintf("%%.%dlfe%d",nrdecs,logshift);
+       }
+    }
+  *scale = pow(10.0,-(double)logshift);
+  return format_string;
+}
 
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2015 Free Software Foundation, Inc.
 
    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
 void chart_get_scale (double high, double low,
                      struct decimal *lower, struct decimal *interval, int *n_ticks);
 
+char *
+chart_get_ticks_format (const double max, const double min, const unsigned int nticks,
+                       double *scale);
 
 #endif
 
   cairo_line_to (cr, x2, y2);
   cairo_stroke (cr);
 }
+
+void
+xrchart_text_extents (cairo_t *cr, const struct xrchart_geometry *geom,
+                     const char *utf8,
+                     double *width, double *height)
+{
+  PangoFontDescription *desc;
+  PangoLayout *layout;
+  int width_pango;
+  int height_pango;
+
+  desc = pango_font_description_from_string ("sans serif");
+  if (desc == NULL)
+      return;
+  pango_font_description_set_absolute_size (desc, geom->font_size * PANGO_SCALE);
+  layout = pango_cairo_create_layout (cr);
+  pango_layout_set_font_description (layout, desc);
+  pango_layout_set_text (layout, utf8, -1);
+  pango_layout_get_size (layout, &width_pango, &height_pango);
+  *width = (double) width_pango / PANGO_SCALE;
+  *height = (double) height_pango / PANGO_SCALE;
+  g_object_unref (layout);
+  pango_font_description_free (desc);
+}
 
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2011, 2015 Free Software Foundation, Inc.
 
    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
 void xrchart_draw_scatterplot (const struct chart_item *, cairo_t *,
                          struct xrchart_geometry *);
 
+/* Get the width and height of rendered label text */
+void xrchart_text_extents (cairo_t *cr, const struct xrchart_geometry *geom,
+                          const char *utf8,
+                          double *width, double *height);
 
 #endif /* output/cairo-chart.h */
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "math/decimal.h"
+#include "math/chart-geometry.h"
 #include "output/charts/plot-hist.h"
 
 #include <float.h>
 #include "output/cairo-chart.h"
 
 #include "gl/xvasprintf.h"
+#include "gl/minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 static void
 hist_draw_bar (cairo_t *cr, const struct xrchart_geometry *geom,
-               const gsl_histogram *h, int bar, bool label)
+               const gsl_histogram *h, int bar, const char *tick_format_string,
+              const double tickscale, const bool tickoversize)
 {
   double upper;
   double lower;
   cairo_restore (cr);
   cairo_stroke (cr);
 
-  if (label)
-    {
-      struct decimal decupper;
-      struct decimal declower;
-      struct decimal middle;
-      decimal_from_double (&declower, lower);
-      decimal_from_double (&decupper, upper);
-      middle = declower;
-      decimal_add (&middle, &decupper);
-      decimal_int_divide (&middle, 2);
-      char *str = decimal_to_string (&middle);
-      draw_tick (cr, geom, SCALE_ABSCISSA, bins > 10,
-                x_pos + width / 2.0, "%s", str);
-      free (str);
-    }
+  draw_tick (cr, geom, SCALE_ABSCISSA, tickoversize,
+            x_pos + width / 2.0, tick_format_string, (upper+lower)/2.0*tickscale);
+
 }
 
 void
   struct histogram_chart *h = to_histogram_chart (chart_item);
   int i;
   int bins;
+  char *tick_format_string;
+  char *test_text;
+  double width, left_width, right_width, unused;
+  double tickscale;
+  bool tickoversize;
 
   xrchart_write_title (cr, geom, _("HISTOGRAM"));
 
       return;
     }
 
-  bins = gsl_histogram_bins (h->gsl_hist);
-
   xrchart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist));
 
+  /* Draw the ticks and compute if the rendered tick text is wider than the bin */
+  bins = gsl_histogram_bins (h->gsl_hist);
+  tick_format_string = chart_get_ticks_format (gsl_histogram_max (h->gsl_hist),
+                                              gsl_histogram_min (h->gsl_hist),
+                                              bins,
+                                              &tickscale);
+  test_text = xasprintf(tick_format_string, gsl_histogram_max (h->gsl_hist)*tickscale);
+  xrchart_text_extents (cr, geom, test_text, &right_width, &unused);
+  free(test_text);
+  test_text = xasprintf(tick_format_string, gsl_histogram_min (h->gsl_hist)*tickscale);
+  xrchart_text_extents (cr, geom, test_text, &left_width, &unused);
+  free(test_text);
+  width = MAX(left_width, right_width);
+  tickoversize = width > 0.9 *
+    ((double)(geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min))/bins;
   for (i = 0; i < bins; i++)
     {
-      hist_draw_bar (cr, geom, h->gsl_hist, i, true);
+      hist_draw_bar (cr, geom, h->gsl_hist, i, tick_format_string, tickscale, tickoversize);
     }
+  free(tick_format_string);
 
   histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
 
 
        src/math/libpspp-math.la \
        src/libpspp/liblibpspp.la \
        src/libpspp-core.la \
-       gl/libgl.la 
+       gl/libgl.la
 
+check_PROGRAMS += tests/math/chart-get-ticks-format-test
+tests_math_chart_get_ticks_format_test_SOURCES = tests/math/chart-get-ticks-format-test.c
+tests_math_chart_get_ticks_format_test_LDADD = \
+       src/math/libpspp-math.la \
+       src/libpspp/liblibpspp.la \
+       src/libpspp-core.la \
+       gl/libgl.la
 
 check_PROGRAMS += tests/math/decimal-test
 tests_math_decimal_test_SOURCES = tests/math/decimal-test.c
 
 AT_CHECK([../../math/chart-get-scale-test], [0], [ignore])
 
 AT_CLEANUP
+
+
+AT_SETUP([Chart Ticks Format])
+
+AT_CHECK([../../math/chart-get-ticks-format-test], [0], [dnl
+max: 1000, min: 10, nticks: 10, fs: %.0lf, scale: 1, example: 505
+max: 10000, min: 10, nticks: 10, fs: %.0lf, scale: 1, example: 5005
+max: 100000, min: 10, nticks: 10, fs: %.2lfe5, scale: 1e-05, example: 0.50e5
+max: 1e+06, min: 10, nticks: 10, fs: %.2lfe6, scale: 1e-06, example: 0.50e6
+max: 1e+07, min: 10, nticks: 10, fs: %.2lfe7, scale: 1e-07, example: 0.50e7
+max: 1e+08, min: 10, nticks: 10, fs: %.2lfe8, scale: 1e-08, example: 0.50e8
+max: 0.1, min: 0.01, nticks: 10, fs: %.3lf, scale: 1, example: 0.055
+max: 1e-05, min: 1e-06, nticks: 10, fs: %.2lfe-6, scale: 1e+06, example: 5.50e-6
+max: 1.00001e-05, min: 1e-05, nticks: 10, fs: %.6lfe-5, scale: 100000, example: 1.000005e-5
+max: 1e+08, min: 1e+08, nticks: 10, fs: %.0lf, scale: 1, example: 100000005
+max: 100000, min: -500000, nticks: 10, fs: %.1lfe5, scale: 1e-05, example: -2.0e5
+max: 5, min: -5, nticks: 10, fs: %.0lf, scale: 1, example: 0
+max: 5, min: -4.999, nticks: 10, fs: %.1lf, scale: 1, example: 0.0
+max: 5, min: -4.999, nticks: 9, fs: %.0lf, scale: 1, example: 0
+max: 5, min: 0, nticks: 10, fs: %.1lf, scale: 1, example: 2.5
+max: 0, min: -5, nticks: 9, fs: %.1lf, scale: 1, example: -2.5
+max: 1.001e-95, min: 1e-95, nticks: 10, fs: %.5lfe-95, scale: 1e+95, example: 1.00050e-95
+max: 1.001e+98, min: 1e+98, nticks: 10, fs: %.5lfe98, scale: 1e-98, example: 1.00050e98
+max: 1.001e+33, min: 1e-22, nticks: 10, fs: %.2lfe33, scale: 1e-33, example: 0.50e33
+])
+
+AT_CLEANUP
 
--- /dev/null
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "math/chart-geometry.h"
+#include "libpspp/compiler.h"
+
+struct range {
+  double max;
+  double min;
+  int nticks;
+};
+
+struct range tv[] = {
+  {       1000.0,            10.0,     10},
+  {      10000.0,            10.0,     10},
+  {     100000.0,            10.0,     10},
+  {    1000000.0,            10.0,     10},
+  {   10000000.0,            10.0,     10},
+  {  100000000.0,            10.0,     10},
+  {          0.1,            0.01,     10},
+  {      0.00001,        0.000001,     10},
+  { 0.0000100001,         0.00001,     10},
+  {  100000010.0,     100000000.0,     10},
+  {     100000.0,       -500000.0,     10},
+  {          5.0,            -5.0,     10},
+  {          5.0,          -4.999,     10},
+  {          5.0,          -4.999,      9},
+  {          5.0,             0.0,     10},
+  {          0.0,            -5.0,      9},
+  {    1.001E-95,         1.0E-95,     10},
+  {     1.001E98,          1.0E98,     10},
+  {     1.001E33,         1.0E-22,     10},
+  {          0.0,             0.0,     -1}
+};
+
+int
+main (int argc UNUSED, char **argv UNUSED)
+{
+  char *fs;
+  double scale;
+  int i = 0;
+  double max, min;
+  int nticks;
+
+  for(i=0;tv[i].nticks > 0;i++)
+    {
+      max = tv[i].max;
+      min = tv[i].min;
+      nticks = tv[i].nticks;
+      fs = chart_get_ticks_format (max, min, nticks, &scale);
+      printf("max: %lg, min: %lg, nticks: %d, fs: %s, scale: %lg, example: ",
+            max, min, nticks, fs, scale);
+      printf(fs,((max-min)/2.0+min)*scale);
+      printf("\n");
+      free(fs);
+    }
+
+  return 0;
+}