X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fmath%2Fchart-geometry.c;h=35380a6929b6c4529e0fe2d5579e03cad9b25f23;hb=3380cd42c3831b98b71709a0c1fcb175e3e4a5d9;hp=0125360f925d68b4006546409c96a72093f7f975;hpb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;p=pspp diff --git a/src/math/chart-geometry.c b/src/math/chart-geometry.c index 0125360f92..35380a6929 100644 --- a/src/math/chart-geometry.c +++ b/src/math/chart-geometry.c @@ -1,55 +1,151 @@ -/* PSPP - computes sample statistics. - Copyright (C) 2004 Free Software Foundation, Inc. - Written by John Darrington +/* PSPP - a program for statistical analysis. + 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 the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + 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. + 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., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ - + along with this program. If not, see . */ +#include #include #include +#include #include "chart-geometry.h" +#include "decimal.h" +#include + +static const double standard_tick[] = {1, 2, 5, 10}; -/* Adjust tick to be a sensible value +/* Adjust tick to be a sensible value ie: ... 0.1,0.2,0.5, 1,2,5, 10,20,50 ... */ -double -chart_rounded_tick(double tick) +void +chart_rounded_tick (double tick, struct decimal *result) { - int i; - double diff = DBL_MAX; - double t = tick; - - static const double standard_ticks[] = {1, 2, 5, 10}; + struct decimal ddif = {1, 1000}; + + /* Avoid arithmetic problems with very small values */ + if (fabs (tick) < DBL_EPSILON) + { + result->ordinate = 0; + result->mantissa = 0; + return; + } - const double factor = pow(10,ceil(log10(standard_ticks[0] / tick))) ; + struct decimal dt; + decimal_from_double (&dt, tick); + + double expd = dec_log10 (&dt) - 1; - for (i = 3 ; i >= 0 ; --i) + for (i = 0 ; i < 4 ; ++i) { - const double d = fabs( tick - standard_ticks[i] / factor ) ; + struct decimal candidate; + struct decimal delta; - if ( d < diff ) + decimal_init (&candidate, standard_tick[i], expd); + + delta = dt; + decimal_subtract (&delta, &candidate); + delta.ordinate = llabs (delta.ordinate); + + if (decimal_cmp (&delta, &ddif) < 0) { - diff = d; - t = standard_ticks[i] / factor ; + ddif = delta; + *result = candidate; } } - - return t; - } +/* + Find a set {LOWER, INTERVAL, N_TICKS} such that: + + LOWER <= LOWDBL, + LOWER + INTERVAL > LOWDBL, + + LOWER + N_TICKS * INTERVAL < HIGHDBL + LOWER + (N_TICKS + 1) * INTERVAL >= HIGHDBL + + INTERVAL = X * 10^N + where: N is integer + and X is an element of {1, 2, 5} + + In other words: + + INTERVAL + > < + |....+....+....+. .+....| + LOWER 1 2 3 N_TICKS + ^LOWDBL ^HIGHDBL +*/ +void +chart_get_scale (double highdbl, double lowdbl, + struct decimal *lower, + struct decimal *interval, + int *n_ticks) +{ + int i; + double fitness = DBL_MAX; + *n_ticks = 0; + struct decimal high; + struct decimal low; + + assert (highdbl >= lowdbl); + + decimal_from_double (&high, highdbl); + decimal_from_double (&low, lowdbl); + + struct decimal diff = high; + decimal_subtract (&diff, &low); + + double expd = dec_log10 (&diff) - 2; + + /* Find the most pleasing interval */ + for (i = 1; i < 4; ++i) + { + struct decimal clbound = low; + struct decimal cubound = high; + struct decimal candidate; + decimal_init (&candidate, standard_tick[i], expd); + + decimal_divide (&clbound, &candidate); + int fl = decimal_floor (&clbound); + decimal_int_multiply (&candidate, fl); + clbound = candidate; + + + decimal_init (&candidate, standard_tick[i], expd); + decimal_divide (&cubound, &candidate); + int fu = decimal_ceil (&cubound); + decimal_int_multiply (&candidate, fu); + + cubound = candidate; + + decimal_init (&candidate, standard_tick[i], expd); + decimal_subtract (&cubound, &clbound); + decimal_divide (&cubound, &candidate); + + + ord_t n_int = decimal_floor (&cubound); + + /* We prefer to have between 5 and 10 tick marks on a scale */ + double f = 1 - exp (-0.2 * fabs (n_int - 7.5) / 7.5); + + if (f < fitness) + { + fitness = f; + *lower = clbound; + *interval = candidate; + *n_ticks = n_int; + } + } +}