/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
$Id: ArithProg.java,v 1.14 2009-03-02 23:35:48 curtis Exp $
*/
package visad.data.in;
import visad.VisADException;
/**
* Provides support for determining if a sequence of numeric values corresponds
* to an arithmetic progression and, if so, the progression parameters.
*
* @author Steven R. Emmerson
*/
public class ArithProg
{
protected long n;
protected double first = Double.NaN;
protected double last = Double.NaN;
protected double meanDel = Double.NaN;
protected boolean isConsistent = true;
protected final double fEps = 5e-5f; // 5 * C FLT_EPS
protected final double dEps = 5e-9; // 5 * C DBL_EPS
private final double[] dValues = new double[1];
private final float[] fValues = new float[1];
/**
* Accumulates a set of floats. Indicates whether or not the sequence is
* consistent with the arithmetic progression so far.
*
* @param values The values to accumulate.
* @return False if the difference between any current
* and previous value normalized by the current
* increment differs from unity by more than the
* nearness threshold; otherwise, true.
* @throws VisADException {@link #isConsistent()} was false before this
* method was invoked.
* @precondition isConsistent() is true.
* @postcondition A subsequent getNumber() will return
* <code>values.length</code> more than previously
* if the function returns true.
* @postcondition A subsequent getLast() will return the
* value argument if the function returns true.
*/
public synchronized boolean accumulate(float[] values)
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".accumulate(float[]): " +
"Sequence isn't an arithmetic progression");
for (int i = 0; i < values.length; i++)
{
double value = values[i];
if (n == 0)
{
first = value;
}
else if (n == 1)
{
meanDel = value - first;
}
else if (isConsistent)
{
accum(value, fEps);
}
last = value;
n++;
}
return isConsistent;
}
/**
* Accumulates a set of doubles. Indicates whether or not the sequence is
* consistent with the arithmetic progression so far.
*
* @param values The values to accumulate.
* @return False if the difference between any current
* and previous value normalized by the current
* increment differs from unity by more than the
* nearness threshold; otherwise, true.
* @throws VisADException {@link #isConsistent()} was false before this
* method was invoked.
* @precondition isConsistent() is true.
* @postcondition A subsequent getNumber() will return
* <code>values.length</code> more than previously
* if the function returns true.
* @postcondition A subsequent getLast() will return the
* value argument if the function returns true.
*/
public synchronized boolean accumulate(double[] values)
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".accumulate(double[]): " +
"Sequence isn't an arithmetic progression");
for (int i = 0; i < values.length; i++)
{
double value = values[i];
if (n == 0)
{
first = value;
}
else if (n == 1)
{
meanDel = value - first;
}
else if (isConsistent)
{
accum(value, dEps);
}
last = value;
n++;
}
return isConsistent;
}
private void accum(double value, double eps)
{
double uncLast = last*eps;
double uncValue = value*eps;
double var = uncLast*uncLast + uncValue*uncValue;
double err = value - (last + meanDel);
if (err*err > var)
{
isConsistent = false;
}
else
{
meanDel = (value - first) / n;
}
}
/**
* Accumulates a value. Indicate whether or not the value is
* consistent with the arithmetic progression so far.
*
* @param value The value to accumulate.
* @return False if the difference between any current
* and previous value normalized by the current
* increment differs from unity by more than the
* nearness threshold; otherwise, true.
* @throws VisADException The sequence isn't an arithmetic progression.
* @precondition isConsistent() is true.
* @postcondition A subsequent getNumber() will return
* <code>values.length</code> more than previously
* if the function returns true.
* @postcondition A subsequent getLast() will return the
* value argument if the function returns true.
*/
public final synchronized boolean accumulate(float value)
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".accumulate(double): " +
"Sequence isn't an arithmetic progression");
fValues[0] = value;
return accumulate(fValues);
}
/**
* Accumulates a value. Indicate whether or not the value is
* consistent with the arithmetic progression so far.
*
* @param value The value to accumulate.
* @return False if the difference between any current
* and previous value normalized by the current
* increment differs from unity by more than the
* nearness threshold; otherwise, true.
* @throws VisADException The sequence isn't an arithmetic progression.
* @precondition isConsistent() is true.
* @postcondition A subsequent getNumber() will return
* <code>values.length</code> more than previously
* if the function returns true.
* @postcondition A subsequent getLast() will return the
* value argument if the function returns true.
*/
public final synchronized boolean accumulate(double value)
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".accumulate(double): " +
"Sequence isn't an arithmetic progression");
dValues[0] = value;
return accumulate(dValues);
}
/**
* Indicates whether or not the sequence so far is consistent with an
* arithmetic progression.
*
* @return <code>true</code> if and only if the sequence
* of values seen so far is consistent with an
* arithmetic progression.
*/
public synchronized final boolean isConsistent()
{
return isConsistent;
}
/**
* Gets the number of values. Only meaningfull if {@link #isConsistent()}
* is true.
*
* @return The number of values accumulated so far.
* @throws VisADException The sequence isn't an arithmetic progression.
* @require isConsistent() is true.
*/
public synchronized final long getNumber()
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".getNumber(): " +
"Sequence isn't an arithmetic progression");
return n;
}
/**
* Gets the first value.
*
* @return The first accumulated value.
* @throws VisADException The sequence isn't an arithmetic progression.
* @require isConsistent() is true.
*/
public synchronized final double getFirst()
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".getFirst(): " +
"Sequence isn't an arithmetic progression");
return first;
}
/**
* Returns the "last" value accumulated. It is only meaningfull if
* {@link #isConsistent} is true.
*
* @return The last accumulated value.
* @throws VisADException The sequence isn't an arithmetic progression.
*/
public synchronized final double getLast()
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".getLast(): " +
"Sequence isn't an arithmetic progression");
return last;
}
/**
* Gets the current common difference. Only meaningfull if {@link
* #isConsistent()} is true.
*
* @return The computed common difference so far.
* @throws VisADException The sequence isn't an arithmetic progression.
* @require isConsistent() is true.
*/
public synchronized final double getCommonDifference()
throws VisADException
{
if (!isConsistent())
throw new VisADException(
getClass().getName() + ".getCommonDifference(): " +
"Sequence isn't an arithmetic progression");
return meanDel;
}
public static void main(String[] args)
throws Exception
{
double[] lats = {
39.2, 39.21, 39.22, 39.23, 39.24, 39.25, 39.26, 39.27, 39.28, 39.29,
39.3, 39.31, 39.32, 39.33, 39.34, 39.35, 39.36, 39.37, 39.38, 39.39,
39.4, 39.41, 39.42, 39.43, 39.44, 39.45, 39.46, 39.47, 39.48, 39.49,
39.5, 39.51, 39.52, 39.53, 39.54, 39.55, 39.56, 39.57, 39.58, 39.59,
39.6, 39.61, 39.62, 39.63, 39.64, 39.65, 39.66, 39.67, 39.68, 39.69,
39.7, 39.71, 39.72, 39.73, 39.74, 39.75, 39.76, 39.77, 39.78, 39.79,
39.8, 39.81, 39.82, 39.83, 39.84, 39.85, 39.86, 39.87, 39.88, 39.89,
39.9, 39.91, 39.92, 39.93, 39.94, 39.95, 39.96, 39.97, 39.98, 39.99,
40 };
ArithProg ap = new ArithProg();
ap.accumulate(lats);
}
}