home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
099
/
SCALE.ZIP
/
SCALE.C
next >
Wrap
C/C++ Source or Header
|
1993-01-12
|
12KB
|
368 lines
/* scale.c */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include "scale.h"
/* The set of potential multipliers for nice numbers. */
/* (10.0 is included only as a convenience for computing geometric */
/* means for Lewart's algorithm.) */
static double pdSet[] = {1.0, 2.0, 5.0, 10.0};
#define SET_LEN (sizeof (pdSet) / sizeof (double) - 1)
/* Function prototypes. */
static double scFirstNiceNum (double, int *, double *);
static double scNextNiceNum (double *, int, int *, double *);
static void scCalcExtLabel (double, double, double, double *, double *);
static void scCalcIntLabel (double, double, double, double *, double *);
static double scPower (double, int);
/* Enhanced Dixon-Kronmal algorithm. */
/* Scale minimum = *pdMinMult * *pdNiceNum */
/* Scale Maximum = *pdMaxMult * *pdNiceNum */
void scDixonKronmal (dDataMin, dDataMax, nExactIntervals,
pdNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
int nExactIntervals; /* (I) Exact number of intervals to use. */
double *pdNiceNum; /* (O) Nice number. */
double *pdMinMult; /* (O) Multiplier for scale minimum. */
double *pdMaxMult; /* (O) Multiplier for scale maximum. */
{
double dIntervalSize;
int iIndex;
double dPowerOfTen;
double dAdjMinMult;
double dAdjMaxMult;
int nActualIntervals;
int nDiffIntervals;
int nAdjIntervals;
assert (dDataMin < dDataMax);
assert (nExactIntervals >= 2);
/* Calculate the smallest potential interval size. */
dIntervalSize = (dDataMax - dDataMin) / nExactIntervals;
/* Calculate the smallest nice number not smaller than dIntervalSize. */
for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
*pdNiceNum < dIntervalSize;
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
{
;
}
/* Produce the scale using the specified nice number. */
scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
/* Continue to re-scale the data with new nice numbers until the */
/* requested number of intervals is not exceeded. */
while ((int) (*pdMaxMult - *pdMinMult) > nExactIntervals)
{
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen);
scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
}
/* Calculate the actual number of intervals spanned by data. */
nActualIntervals = (int) (*pdMaxMult - *pdMinMult);
/* Adjust lo and hi multiples to account for the additional */
/* intervals required. Adjust in favor of centering. */
nDiffIntervals = nExactIntervals - nActualIntervals;
nAdjIntervals = nDiffIntervals / 2;
if (nDiffIntervals & 1)
/* nDiffIntervals is odd. Decide where the extra interval should go. */
{
if (dDataMin - *pdMinMult * *pdNiceNum <
*pdMaxMult * *pdNiceNum - dDataMax)
{
nAdjIntervals++;
}
}
dAdjMinMult = *pdMinMult - (double) nAdjIntervals;
dAdjMaxMult = dAdjMinMult + (double) nExactIntervals;
if (dAdjMinMult < 0.0 && *pdMinMult >= 0.0)
/* Avoid adjustments that cause negative scales for non-negative data. */
{
*pdMinMult = 0.0;
*pdMaxMult = (double) nExactIntervals;
}
else if (dAdjMaxMult > 0.0 && *pdMaxMult <= 0.0)
/* Avoid adjustments that cause positive scales for non-positive data. */
{
*pdMaxMult = 0.0;
*pdMinMult = (double) -nExactIntervals;
}
else
{
*pdMinMult = dAdjMinMult;
*pdMaxMult = dAdjMaxMult;
}
}
/* Lewart's algorithm. */
/* Scale minimum = *pdMinMult * *pdNiceNum */
/* Scale Maximum = *pdMaxMult * *pdNiceNum */
void scLewart (dDataMin, dDataMax, nApproxIntervals,
pdNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
int nApproxIntervals; /* (I) Approximate number of intervals to use. */
double *pdNiceNum; /* (O) Nice number. */
double *pdMinMult; /* (O) Multiplier for scale minimum. */
double *pdMaxMult; /* (O) Multiplier for scale maximum. */
{
double dIntervalSize;
int iIndex;
double dPowerOfTen;
assert (dDataMin < dDataMax);
assert (nApproxIntervals >= 2);
/* Calculate the smallest potential interval size. */
dIntervalSize = (dDataMax - dDataMin) / nApproxIntervals;
/* Find the nice number that is "closest to" the smallest potential */
/* interval size. Use the geometric means of adjacent multiplier */
/* values as break points. */
for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
sqrt (pdSet[iIndex] * pdSet[iIndex + 1]) * dPowerOfTen <
dIntervalSize;
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
{
;
}
/* Produce the scale using the specified nice number. */
scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
}
/* Algorithm for scaling with a maximum number of intervals. */
/* Scale minimum = *pdMinMult * *pdNiceNum */
/* Scale Maximum = *pdMaxMult * *pdNiceNum */
void scMaxInterval (dDataMin, dDataMax, nMaxIntervals,
pdNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
int nMaxIntervals; /* (I) Maximum number of intervals to use. */
double *pdNiceNum; /* (O) Nice number. */
double *pdMinMult; /* (O) Multiplier for scale minimum. */
double *pdMaxMult; /* (O) Multiplier for scale maximum. */
{
double dIntervalSize;
int iIndex;
double dPowerOfTen;
assert (dDataMin < dDataMax);
assert (nMaxIntervals >= 2);
/* Calculate the smallest potential interval size. */
dIntervalSize = (dDataMax - dDataMin) / nMaxIntervals;
/* Calculate the smallest nice number not smaller than dIntervalSize. */
for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
*pdNiceNum < dIntervalSize;
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
{
;
}
/* Produce the scale using the specified nice number. */
scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
/* Continue to re-scale the data with new nice numbers until the */
/* requested number of intervals is not exceeded. */
while ((int) (*pdMaxMult - *pdMinMult) > nMaxIntervals)
{
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen);
scCalcExtLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
}
}
/* Algorithm for internal labeling. */
/* First reference value = *pdMinMult * *pdNiceNum */
/* Last reference value = *pdMaxMult * *pdNiceNum */
void scInternal (dDataMin, dDataMax, nMaxIntervals,
pdNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
int nMaxIntervals; /* (I) Maximum number of intervals to use. */
double *pdNiceNum; /* (O) Nice number. */
double *pdMinMult; /* (O) Multiplier for minimum reference value. */
double *pdMaxMult; /* (O) Multiplier for maximum reference value. */
{
double dIntervalSize;
int iIndex;
double dPowerOfTen;
assert (dDataMin < dDataMax);
assert (nMaxIntervals >= 5);
/* Calculate the smallest potential interval size. */
dIntervalSize = (dDataMax - dDataMin) / nMaxIntervals;
/* Calculate the smallest nice number not smaller than dIntervalSize. */
for (*pdNiceNum = scFirstNiceNum (dIntervalSize, &iIndex, &dPowerOfTen);
*pdNiceNum < dIntervalSize;
*pdNiceNum = scNextNiceNum (pdSet, SET_LEN, &iIndex, &dPowerOfTen))
{
;
}
/* Produce the internal scale using the specified nice number. */
scCalcIntLabel (dDataMin, dDataMax, *pdNiceNum, pdMinMult, pdMaxMult);
}
/* Calculate an initial value for the nice number. */
double scFirstNiceNum (dIntervalSize, piIndex, pdPowerOfTen)
double dIntervalSize; /* (I) Interval size. */
int *piIndex; /* (O) Index into multiplier array for nice number. */
double *pdPowerOfTen; /* (O) Power of ten for nice number. */
{
int iExponent;
/* Calculate an initial power of 10. */
iExponent = (int) floor (log10 (dIntervalSize));
/* Perform some extra checking. */
*pdPowerOfTen = scPower (10.0, iExponent);
if (*pdPowerOfTen * 10.0 <= dIntervalSize)
{
*pdPowerOfTen *= 10.0;
}
/* Initial index is always 0. */
*piIndex = 0;
return (*pdPowerOfTen);
}
/* Calculate the next nice number. */
double scNextNiceNum (pdSet, nSet, piIndex, pdPowerOfTen)
double *pdSet; /* (I) Set of multipliers. */
int nSet; /* (I) Number of elements in set. */
int *piIndex; /* (IO) Index into multiplier array for nice number. */
double *pdPowerOfTen; /* (IO) Power of ten for nice number. */
{
/* Increment the index. */
(*piIndex)++;
/* If the maximum index has been exceeded, reset the index to */
/* 0 and increase the power of 10. */
if (*piIndex >= nSet)
{
*piIndex = 0;
*pdPowerOfTen *= 10.0;
}
return (pdSet[*piIndex] * *pdPowerOfTen);
}
/* Calculate an externally labeled scale. */
void scCalcExtLabel (dDataMin, dDataMax, dNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
double dNiceNum; /* (I) Nice number. */
double *pdMinMult; /* (O) Multiplier for scale minimum. */
double *pdMaxMult; /* (O) Multiplier for scale maximum. */
{
/* Calculate the low multiple. */
*pdMinMult = floor (dDataMin / dNiceNum);
/* Perform some extra checking. */
if ((*pdMinMult + 1.0) * dNiceNum <= dDataMin)
{
*pdMinMult = *pdMinMult + 1.0;
}
/* Calculate the high multiple. */
*pdMaxMult = ceil (dDataMax / dNiceNum);
/* Perform some extra checking. */
if ((*pdMaxMult - 1.0) * dNiceNum >= dDataMax)
{
*pdMaxMult = *pdMaxMult - 1.0;
}
}
/* Calculate an internally labeled scale. */
void scCalcIntLabel (dDataMin, dDataMax, dNiceNum, pdMinMult, pdMaxMult)
double dDataMin; /* (I) Data minimum. */
double dDataMax; /* (I) Data maximum. */
double dNiceNum; /* (I) Nice number. */
double *pdMinMult; /* (O) Multiplier for minimum reference value. */
double *pdMaxMult; /* (O) Multiplier for maximum reference value. */
{
/* Calculate the low multiple. */
*pdMinMult = ceil (dDataMin / dNiceNum);
/* Perform some extra checking. */
if ((*pdMinMult - 1.0) * dNiceNum >= dDataMin)
{
*pdMinMult = *pdMinMult - 1.0;
}
/* Calculate the high multiple. */
*pdMaxMult = floor (dDataMax / dNiceNum);
/* Perform some extra checking. */
if ((*pdMaxMult + 1.0) * dNiceNum <= dDataMax)
{
*pdMaxMult = *pdMaxMult + 1.0;
}
}
/* Raise a double to an integer power. */
/*
** Adapted from an algorithm described in "Algorithms" by Robert Sedgewick
** First Edition, pp. 46-47.
*/
static double scPower (dRoot, iExponent)
double dRoot; /* (I) Root to be raised to a power. */
int iExponent; /* (I) Power to which the root should be raised. */
{
double dResult;
/* For negative exponents, invert root and use a positive exponent. */
if (iExponent < 0)
{
dRoot = 1.0 / dRoot;
iExponent = -iExponent;
}
/* Perform multiple multiplications. */
dResult = 1.0;
while (iExponent)
{
if (iExponent & 1)
{
dResult *= dRoot;
}
iExponent >>= 1;
if (iExponent)
{
dRoot *= dRoot;
}
}
return (dResult);
}