Category : Files from Magazines
Archive   : SCALER.ZIP
Filename : SCALE.C
#include
#include
#include
#include
#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);
}
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/