/* numObscure - obscure but sometimes useful numerical functions. */

/* Copyright (C) 2009 The Regents of the University of California 
 * See README in this or parent directory for licensing information. */
#include "common.h"
#include "obscure.h"

static void rangeIncludingZero(double start, double end, double *retStart, double *retEnd)
/* If start == end, then make range go from zero to the start/end value. */
{
if (start < 0.0)
    {
    *retStart = start;
    *retEnd = 0;
    }
else if (start > 0.0)
    {
    *retStart = 0;
    *retEnd = end;
    }
else
    {
    *retStart = 0;
    *retEnd = 1;
    }
return;
}

void rangeRoundUp(double start, double end, double *retStart, double *retEnd)
/* Round start and end so that they cover a slightly bigger range, but with more round
 * numbers.  For instance 0.23:9.89 becomes 0:10 */
{
double size = end - start;
if (size < 0)
    errAbort("start (%g) after end (%g) in rangeRoundUp", start, end);

/* Flat ranges get moved to include zero for scale. */
if (size == 0.0)
    {
    rangeIncludingZero(start, end, &start, &end);
    size = end - start;
    }

/* Figure out "increment", which will be 1, 2, 5, or 10, or a multiple of 10 of these 
 * Want to have at least two increments in range. */
double exponent = 1;
double scaledSize = size;
double increment = 0;
while (scaledSize < 100)
    {
    scaledSize *= 10;
    exponent /= 10;
    }
while (scaledSize >= 100)
    {
    scaledSize /= 10;
    exponent *= 10;
    }
/* At this point have a number between 10 and 100 */
if (scaledSize < 12)
    increment = 1;
else if (scaledSize < 20)
    increment = 2;
else if (scaledSize < 75)
    increment = 5;
else
    increment = 10;
increment *= exponent;

int startInIncrements = floor(start/increment);
int endInIncrements = ceil(end/increment);
*retStart = startInIncrements * increment;
*retEnd = endInIncrements * increment;
}

void rangeFromMinMaxMeanStd(double minVal, double maxVal, double mean, double std,
	double *retStart, double *retEnd)
/* Given some basic statistical properties, set a range that will be good on a wide
 * range of biological data. */
{
double start,end;
if (isnan(std))
    {
    /* Handle a case that occurred in version 1 bigWigs and bigBeds due to fault in
     * sumSquares calculation. */
    start = mean-5;
    end = mean+5;
    if (start < minVal) start = minVal;
    if (end > maxVal) end = maxVal;
    }
else if (std == 0)
    {
    start = end = mean;
    rangeIncludingZero(start, end, &start, &end);
    }
else
    {
    start = mean - 5*std;
    end = mean + 5*std;
    if (start < minVal) start = minVal;
    if (end > maxVal) end = maxVal;
    }
*retStart = start;
*retEnd = end;
}

