You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by lu...@apache.org on 2011/06/21 17:47:24 UTC

svn commit: r1138061 - in /commons/proper/math/trunk/src: main/java/org/apache/commons/math/ode/sampling/ site/xdoc/ test/java/org/apache/commons/math/ode/sampling/

Author: luc
Date: Tue Jun 21 15:47:23 2011
New Revision: 1138061

URL: http://svn.apache.org/viewvc?rev=1138061&view=rev
Log:
Extended StepNormalizer with normalization mode and bounds settings.

JIRA: MATH-595

Added:
    commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java   (with props)
    commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java   (with props)
    commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java   (with props)
    commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java   (with props)
    commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java   (with props)
Modified:
    commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java
    commons/proper/math/trunk/src/site/xdoc/changes.xml

Modified: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java?rev=1138061&r1=1138060&r2=1138061&view=diff
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java (original)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizer.java Tue Jun 21 15:47:23 2011
@@ -18,6 +18,9 @@
 package org.apache.commons.math.ode.sampling;
 
 import org.apache.commons.math.exception.MathUserException;
+import org.apache.commons.math.ode.sampling.FixedStepHandler;
+import org.apache.commons.math.ode.sampling.StepHandler;
+import org.apache.commons.math.ode.sampling.StepInterpolator;
 import org.apache.commons.math.util.FastMath;
 
 /**
@@ -31,50 +34,140 @@ import org.apache.commons.math.util.Fast
  *
  * <p>The stepsize used is selected at construction time. The {@link
  * FixedStepHandler#handleStep handleStep} method of the underlying
- * {@link FixedStepHandler} object is called at the beginning time of
- * the integration t0 and also at times t0+h, t0+2h, ... If the
- * integration range is an integer multiple of the stepsize, then the
- * last point handled will be the endpoint of the integration tend, if
- * not, the last point will belong to the interval [tend - h ;
- * tend].</p>
+ * {@link FixedStepHandler} object is called at normalized times. The
+ * normalized times can be influenced by the {@link StepNormalizerMode} and
+ * {@link StepNormalizerBounds}.</p>
  *
- * <p>There is no constraint on the integrator, it can use any
- * timestep it needs (time steps longer or shorter than the fixed time
- * step and non-integer ratios are all allowed).</p>
+ * <p>There is no constraint on the integrator, it can use any time step
+ * it needs (time steps longer or shorter than the fixed time step and
+ * non-integer ratios are all allowed).</p>
+ *
+ * <p>
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan=6><font size="+2">Examples (step size = 0.5)</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Start time</td><td>End time</td>
+ *  <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
+ *  <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></font></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * </table>
+ * </p>
  *
  * @see StepHandler
  * @see FixedStepHandler
+ * @see StepNormalizerMode
+ * @see StepNormalizerBounds
  * @version $Id$
  * @since 1.2
  */
 
 public class StepNormalizer implements StepHandler {
-
     /** Fixed time step. */
     private double h;
 
     /** Underlying step handler. */
     private final FixedStepHandler handler;
 
+    /** First step time. */
+    private double firstTime;
+
     /** Last step time. */
     private double lastTime;
 
-    /** Last State vector. */
+    /** Last state vector. */
     private double[] lastState;
 
-    /** Last Derivatives vector. */
+    /** Last derivatives vector. */
     private double[] lastDerivatives;
 
     /** Integration direction indicator. */
     private boolean forward;
 
-    /** Simple constructor.
+    /** The step normalizer bounds settings to use. */
+    private final StepNormalizerBounds bounds;
+
+    /** The step normalizer mode to use. */
+    private final StepNormalizerMode mode;
+
+    /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+     * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
+     * backwards compatibility.
      * @param h fixed time step (sign is not used)
      * @param handler fixed time step handler to wrap
      */
     public StepNormalizer(final double h, final FixedStepHandler handler) {
+        this(h, handler, StepNormalizerMode.INCREMENT,
+             StepNormalizerBounds.FIRST);
+    }
+
+    /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
+     * bounds setting.
+     * @param h fixed time step (sign is not used)
+     * @param handler fixed time step handler to wrap
+     * @param mode step normalizer mode to use
+     * @since 3.0
+     */
+    public StepNormalizer(final double h, final FixedStepHandler handler,
+                          final StepNormalizerMode mode) {
+        this(h, handler, mode, StepNormalizerBounds.FIRST);
+    }
+
+    /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+     * mode.
+     * @param h fixed time step (sign is not used)
+     * @param handler fixed time step handler to wrap
+     * @param bounds step normalizer bounds setting to use
+     * @since 3.0
+     */
+    public StepNormalizer(final double h, final FixedStepHandler handler,
+                          final StepNormalizerBounds bounds) {
+        this(h, handler, StepNormalizerMode.INCREMENT, bounds);
+    }
+
+    /** Simple constructor.
+     * @param h fixed time step (sign is not used)
+     * @param handler fixed time step handler to wrap
+     * @param mode step normalizer mode to use
+     * @param bounds step normalizer bounds setting to use
+     * @since 3.0
+     */
+    public StepNormalizer(final double h, final FixedStepHandler handler,
+                          final StepNormalizerMode mode,
+                          final StepNormalizerBounds bounds) {
         this.h       = FastMath.abs(h);
         this.handler = handler;
+        this.mode    = mode;
+        this.bounds  = bounds;
         reset();
     }
 
@@ -93,6 +186,7 @@ public class StepNormalizer implements S
      * handled.
      */
     public void reset() {
+        firstTime       = Double.NaN;
         lastTime        = Double.NaN;
         lastState       = null;
         lastDerivatives = null;
@@ -112,50 +206,89 @@ public class StepNormalizer implements S
      * @throws MathUserException this exception is propagated to the
      * caller if the underlying user function triggers one
      */
-    public void handleStep(final StepInterpolator interpolator, final boolean isLast)
-        throws MathUserException {
-
+    public void handleStep(final StepInterpolator interpolator,
+                           final boolean isLast) throws MathUserException {
+        // The first time, update the last state with the start information.
         if (lastState == null) {
-
+            firstTime = interpolator.getPreviousTime();
             lastTime = interpolator.getPreviousTime();
             interpolator.setInterpolatedTime(lastTime);
             lastState = interpolator.getInterpolatedState().clone();
             lastDerivatives = interpolator.getInterpolatedDerivatives().clone();
 
-            // take the integration direction into account
+            // Take the integration direction into account.
             forward = interpolator.getCurrentTime() >= lastTime;
-            if (! forward) {
-                h = -h;
-            }
-
+            if (!forward) h = -h;
         }
 
-        double nextTime = lastTime + h;
-        boolean nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
+        double nextTime = (mode == StepNormalizerMode.INCREMENT) ?
+                          lastTime + h :
+                          (FastMath.floor(lastTime / h) + 1) * h;
+        boolean nextInStep = isNextInStep(nextTime, interpolator);
         while (nextInStep) {
+            // Output the stored previous step.
+            doNormalizedStep(false);
 
-            // output the stored previous step
-            handler.handleStep(lastTime, lastState, lastDerivatives, false);
-
-            // store the next step
-            lastTime = nextTime;
-            interpolator.setInterpolatedTime(lastTime);
-            System.arraycopy(interpolator.getInterpolatedState(), 0,
-                             lastState, 0, lastState.length);
-            System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
-                             lastDerivatives, 0, lastDerivatives.length);
-
-            nextTime  += h;
-            nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
+            // Store the next step as last step.
+            storeStep(interpolator, nextTime);
 
+            // Move on to the next step.
+            nextTime += h;
+            nextInStep = isNextInStep(nextTime, interpolator);
         }
 
         if (isLast) {
-            // there will be no more steps,
-            // the stored one should be flagged as being the last
-            handler.handleStep(lastTime, lastState, lastDerivatives, true);
+            // There will be no more steps. The stored one should be given to
+            // the handler. We may have to output one more step. Only the last
+            // one of those should be flagged as being the last.
+            boolean addLast = bounds.lastIncluded() &&
+                              lastTime != interpolator.getCurrentTime();
+            doNormalizedStep(!addLast);
+            if (addLast) {
+                storeStep(interpolator, interpolator.getCurrentTime());
+                doNormalizedStep(true);
+            }
         }
+    }
 
+    /**
+     * Returns a value indicating whether the next normalized time is in the
+     * current step.
+     * @param nextTime the next normalized time
+     * @param interpolator interpolator for the last accepted step, to use to
+     * get the end time of the current step
+     * @return value indicating whether the next normalized time is in the
+     * current step
+     */
+    private boolean isNextInStep(double nextTime,
+                                 StepInterpolator interpolator) {
+        return forward ? nextTime <= interpolator.getCurrentTime()
+                       : nextTime >= interpolator.getCurrentTime();
+    }
+
+    /**
+     * Invokes the underlying step handler for the current normalized step.
+     * @param isLast true if the step is the last one
+     * @throws MathUserException this exception is propagated to the
+     * caller if the underlying user function triggers one
+     */
+    private void doNormalizedStep(boolean isLast) throws MathUserException {
+        if (!bounds.firstIncluded() && firstTime == lastTime) return;
+        handler.handleStep(lastTime, lastState, lastDerivatives, isLast);
     }
 
+    /** Stores the interpolated information for the given time in the current
+     * state.
+     * @param interpolator interpolator for the last accepted step, to use to
+     * get the interpolated information
+     * @param t the time for which to store the interpolated information
+     */
+    private void storeStep(StepInterpolator interpolator, double t) {
+        lastTime = t;
+        interpolator.setInterpolatedTime(lastTime);
+        System.arraycopy(interpolator.getInterpolatedState(), 0,
+                         lastState, 0, lastState.length);
+        System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
+                         lastDerivatives, 0, lastDerivatives.length);
+    }
 }

Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java?rev=1138061&view=auto
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java (added)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java Tue Jun 21 15:47:23 2011
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.sampling;
+
+/** {@link StepNormalizer Step normalizer} bounds settings. They influence
+ * whether the underlying fixed step size step handler is called for the first
+ * and last points. Note that if the last point coincides with a normalized
+ * point, then the underlying fixed step size step handler is always called,
+ * regardless of these settings.
+ * @see StepNormalizer
+ * @see StepNormalizerMode
+ * @version $Id$
+ * @since 3.0
+ */
+public enum StepNormalizerBounds {
+    /** Do not include the first and last points. */
+    NEITHER(false, false),
+
+    /** Include the first point, but not the last point. */
+    FIRST(true, false),
+
+    /** Include the last point, but not the first point. */
+    LAST(false, true),
+
+    /** Include both the first and last points. */
+    BOTH(true, true);
+
+    /** Whether the first point should be passed to the underlying fixed
+     * step size step handler.
+     */
+    private final boolean first;
+
+    /** Whether the last point should be passed to the underlying fixed
+     * step size step handler.
+     */
+    private final boolean last;
+
+    /**
+     * Simple constructor.
+     * @param first Whether the first point should be passed to the
+     * underlying fixed step size step handler.
+     * @param last Whether the last point should be passed to the
+     * underlying fixed step size step handler.
+     */
+    private StepNormalizerBounds(final boolean first, final boolean last) {
+        this.first = first;
+        this.last = last;
+    }
+
+    /**
+     * Returns a value indicating whether the first point should be passed
+     * to the underlying fixed step size step handler.
+     * @return value indicating whether the first point should be passed
+     * to the underlying fixed step size step handler.
+     */
+    public boolean firstIncluded() {
+        return first;
+    }
+
+    /**
+     * Returns a value indicating whether the last point should be passed
+     * to the underlying fixed step size step handler.
+     * @return value indicating whether the last point should be passed
+     * to the underlying fixed step size step handler.
+     */
+    public boolean lastIncluded() {
+        return last;
+    }
+}

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerBounds.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java?rev=1138061&view=auto
==============================================================================
--- commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java (added)
+++ commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java Tue Jun 21 15:47:23 2011
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math.ode.sampling;
+
+
+/** {@link StepNormalizer Step normalizer} modes. Determines how the step size
+ * is interpreted.
+ * @see StepNormalizer
+ * @see StepNormalizerBounds
+ * @version $Id$
+ * @since 3.0
+ */
+public enum StepNormalizerMode {
+    /**
+     * Steps are fixed increments of the start value. In other words, they
+     * are relative to the start value.
+     *
+     * <p>If the integration start time is t0, then the points handled by
+     * the underlying fixed step size step handler are t0 (depending on
+     * the {@link StepNormalizerBounds bounds settings}), t0+h, t0+2h, ...</p>
+     *
+     * <p>If the integration range is an integer multiple of the step size
+     * (h), then the last point handled will be the end point of the
+     * integration (tend). If not, the last point may be the end point
+     * tend, or it may be a point belonging to the interval [tend - h ;
+     * tend], depending on the {@link StepNormalizerBounds bounds settings}.
+     * </p>
+     *
+     * @see StepNormalizer
+     * @see StepNormalizerBounds
+     */
+    INCREMENT,
+
+    /** Steps are multiples of a fixed value. In other words, they are
+     * relative to the first multiple of the step size that is encountered
+     * after the start value.
+     *
+     * <p>If the integration start time is t0, and the first multiple of
+     * the fixed step size that is encountered is t1, then the points
+     * handled by the underlying fixed step size step handler are t0
+     * (depending on the {@link StepNormalizerBounds bounds settings}), t1,
+     * t1+h, t1+2h, ...</p>
+     *
+     * <p>If the end point of the integration range (tend) is an integer
+     * multiple of the step size (h) added to t1, then the last point
+     * handled will be the end point of the integration (tend). If not,
+     * the last point may be the end point tend, or it may be a point
+     * belonging to the interval [tend - h ; tend], depending on the
+     * {@link StepNormalizerBounds bounds settings}.</p>
+     *
+     * @see StepNormalizer
+     * @see StepNormalizerBounds
+     */
+    MULTIPLES;
+}

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/main/java/org/apache/commons/math/ode/sampling/StepNormalizerMode.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: commons/proper/math/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/site/xdoc/changes.xml?rev=1138061&r1=1138060&r2=1138061&view=diff
==============================================================================
--- commons/proper/math/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/math/trunk/src/site/xdoc/changes.xml Tue Jun 21 15:47:23 2011
@@ -52,6 +52,9 @@ The <action> type attribute can be add,u
     If the output is not quite correct, check for invisible trailing spaces!
      -->
     <release version="3.0" date="TBD" description="TBD">
+      <action dev="luc" type="fix" issue="MATH-595" due-to="Dennis Hendriks" >
+        Extended StepNormalizer with normalization mode and bounds settings.
+      </action>
       <action dev="mikl" type="fix" issue="MATH-597">
         Implemented faster generation of random exponential distributed values with
         algorithm from Ahrens and Dieter (1972): Computer methods for sampling 

Added: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java?rev=1138061&view=auto
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java (added)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java Tue Jun 21 15:47:23 2011
@@ -0,0 +1,52 @@
+package org.apache.commons.math.ode.sampling;
+
+/** Step normalizer output tests, for problems where the first and last points
+ * are overlap fixed points.
+ */
+public class StepNormalizerOutputOverlapTest extends StepNormalizerOutputTestBase {
+    @Override
+    protected double getStart() {
+        return 0.0;
+    }
+
+    @Override
+    protected double getEnd() {
+        return 10.0;
+    }
+
+    @Override
+    protected double[] getExpInc() {
+        return new double[] { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0,
+                              4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5,
+                              9.0, 9.5, 10.0 };
+    }
+
+    @Override
+    protected double[] getExpIncRev() {
+        return new double[] { 10.0, 9.5, 9.0, 8.5, 8.0, 7.5, 7.0, 6.5,
+                              6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5,
+                              2.0, 1.5, 1.0, 0.5, 0.0 };
+    }
+
+    @Override
+    protected double[] getExpMul() {
+        return new double[] { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0,
+                              4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5,
+                              9.0, 9.5, 10.0 };
+    }
+
+    @Override
+    protected double[] getExpMulRev() {
+        return new double[] { 10.0, 9.5, 9.0, 8.5, 8.0, 7.5, 7.0, 6.5,
+                              6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5,
+                              2.0, 1.5, 1.0, 0.5, 0.0 };
+    }
+
+    @Override
+    protected int[][] getO() {
+        return new int[][] {{1, 0}, {1, 0}, {0, 0}, {0, 0},
+                            {1, 0}, {1, 0}, {0, 0}, {0, 0},
+                            {1, 0}, {1, 0}, {0, 0}, {0, 0},
+                            {1, 0}, {1, 0}, {0, 0}, {0, 0}};
+    }
+}

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputOverlapTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java?rev=1138061&view=auto
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java (added)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java Tue Jun 21 15:47:23 2011
@@ -0,0 +1,52 @@
+package org.apache.commons.math.ode.sampling;
+
+/** Step normalizer output tests, for problems where the first and last points
+ * are not fixed points.
+ */
+public class StepNormalizerOutputTest extends StepNormalizerOutputTestBase {
+    @Override
+    protected double getStart() {
+        return 0.3;
+    }
+
+    @Override
+    protected double getEnd() {
+        return 10.1;
+    }
+
+    @Override
+    protected double[] getExpInc() {
+        return new double[] { 0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.3, 3.8, 4.3,
+                              4.8, 5.3, 5.8, 6.3, 6.8, 7.3, 7.8, 8.3, 8.8,
+                              9.3, 9.8, 10.1 };
+    }
+
+    @Override
+    protected double[] getExpIncRev() {
+        return new double[] { 10.1, 9.6, 9.1, 8.6, 8.1, 7.6, 7.1, 6.6,
+                              6.1, 5.6, 5.1, 4.6, 4.1, 3.6, 3.1, 2.6,
+                              2.1, 1.6, 1.1, 0.6, 0.3 };
+    }
+
+    @Override
+    protected double[] getExpMul() {
+        return new double[] { 0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0,
+                              4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5,
+                              9.0, 9.5, 10.0, 10.1 };
+    }
+
+    @Override
+    protected double[] getExpMulRev() {
+        return new double[] { 10.1, 10.0, 9.5, 9.0, 8.5, 8.0, 7.5, 7.0,
+                              6.5, 6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5,
+                              2.0, 1.5, 1.0, 0.5, 0.3 };
+    }
+
+    @Override
+    protected int[][] getO() {
+        return new int[][] {{1, 1}, {1, 1}, {0, 1}, {0, 1},
+                            {1, 0}, {1, 0}, {0, 0}, {0, 0},
+                            {1, 1}, {1, 1}, {0, 1}, {0, 1},
+                            {1, 0}, {1, 0}, {0, 0}, {0, 0}};
+    }
+}

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java
URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java?rev=1138061&view=auto
==============================================================================
--- commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java (added)
+++ commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java Tue Jun 21 15:47:23 2011
@@ -0,0 +1,224 @@
+package org.apache.commons.math.ode.sampling;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.exception.MathUserException;
+import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math.ode.FirstOrderIntegrator;
+import org.apache.commons.math.ode.IntegratorException;
+import org.apache.commons.math.ode.nonstiff.GraggBulirschStoerIntegrator;
+import org.junit.Test;
+
+/** Base class for step normalizer output tests. */
+public abstract class StepNormalizerOutputTestBase
+    implements FirstOrderDifferentialEquations, FixedStepHandler
+{
+    /** The normalized output time values. */
+    private List<Double> output;
+
+    /**
+     * Returns the start time.
+     * @return the start time
+     */
+    protected abstract double getStart();
+
+    /**
+     * Returns the end time.
+     * @return the end time
+     */
+    protected abstract double getEnd();
+
+    /**
+     * Returns the expected normalized output time values for increment mode.
+     * @return the expected normalized output time values for increment mode
+     */
+    protected abstract double[] getExpInc();
+
+    /**
+     * Returns the expected reversed normalized output time values for
+     * increment mode.
+     * @return the expected reversed normalized output time values for
+     * increment mode
+     */
+    protected abstract double[] getExpIncRev();
+
+    /**
+     * Returns the expected normalized output time values for multiples mode.
+     * @return the expected normalized output time values for multiples mode
+     */
+    protected abstract double[] getExpMul();
+
+    /**
+     * Returns the expected reversed normalized output time values for
+     * multiples mode.
+     * @return the expected reversed normalized output time values for
+     * multiples mode
+     */
+    protected abstract double[] getExpMulRev();
+
+    /**
+     * Returns the offsets for the unit tests below, in the order they are
+     * given below. For each test, the left and right offsets are returned.
+     * @return the offsets for the unit tests below, in the order they are
+     * given below
+     */
+    protected abstract int[][] getO();
+
+    /**
+     * Get the array, given left and right offsets.
+     * @param a the input array
+     * @param offsetL the left side offset
+     * @param offsetR the right side offset
+     * @return the modified array
+     */
+    private double[] getArray(double[] a, int offsetL, int offsetR) {
+        double[] copy = new double[a.length - offsetR - offsetL];
+        System.arraycopy(a, offsetL, copy, 0, copy.length);
+        return copy;
+    }
+
+    @Test
+    public void testIncNeither() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpInc(), getO()[0][0], getO()[0][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.NEITHER, exp, false);
+    }
+
+    @Test
+    public void testIncNeitherRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpIncRev(), getO()[1][0], getO()[1][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.NEITHER, exp, true);
+    }
+
+    @Test
+    public void testIncFirst() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpInc(), getO()[2][0], getO()[2][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.FIRST, exp, false);
+    }
+
+    @Test
+    public void testIncFirstRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpIncRev(), getO()[3][0], getO()[3][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.FIRST, exp, true);
+    }
+
+    @Test
+    public void testIncLast() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpInc(), getO()[4][0], getO()[4][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.LAST, exp, false);
+    }
+
+    @Test
+    public void testIncLastRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpIncRev(), getO()[5][0], getO()[5][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.LAST, exp, true);
+    }
+
+    @Test
+    public void testIncBoth() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpInc(), getO()[6][0], getO()[6][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.BOTH, exp, false);
+    }
+
+    @Test
+    public void testIncBothRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpIncRev(), getO()[7][0], getO()[7][1]);
+        doTest(StepNormalizerMode.INCREMENT, StepNormalizerBounds.BOTH, exp, true);
+    }
+
+    @Test
+    public void testMulNeither() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMul(), getO()[8][0], getO()[8][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.NEITHER, exp, false);
+    }
+
+    @Test
+    public void testMulNeitherRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMulRev(), getO()[9][0], getO()[9][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.NEITHER, exp, true);
+    }
+
+    @Test
+    public void testMulFirst() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMul(), getO()[10][0], getO()[10][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.FIRST, exp, false);
+    }
+
+    @Test
+    public void testMulFirstRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMulRev(), getO()[11][0], getO()[11][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.FIRST, exp, true);
+    }
+
+    @Test
+    public void testMulLast() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMul(), getO()[12][0], getO()[12][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.LAST, exp, false);
+    }
+
+    @Test
+    public void testMulLastRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMulRev(), getO()[13][0], getO()[13][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.LAST, exp, true);
+    }
+
+    @Test
+    public void testMulBoth() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMul(), getO()[14][0], getO()[14][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.BOTH, exp, false);
+    }
+
+    @Test
+    public void testMulBothRev() throws MathUserException, IntegratorException {
+        double[] exp = getArray(getExpMulRev(), getO()[15][0], getO()[15][1]);
+        doTest(StepNormalizerMode.MULTIPLES, StepNormalizerBounds.BOTH, exp, true);
+    }
+
+    /**
+     * The actual step normalizer output test code, shared by all the unit
+     * tests.
+     *
+     * @param mode the step normalizer mode to use
+     * @param bounds the step normalizer bounds setting to use
+     * @param expected the expected output (normalized time points)
+     * @param reverse whether to reverse the integration direction
+     */
+    private void doTest(StepNormalizerMode mode, StepNormalizerBounds bounds,
+                        double[] expected, boolean reverse)
+        throws MathUserException, IntegratorException
+    {
+        // Forward test.
+        FirstOrderIntegrator integ = new GraggBulirschStoerIntegrator(
+                                                        1e-8, 1.0, 1e-5, 1e-5);
+        integ.addStepHandler(new StepNormalizer(0.5, this, mode, bounds));
+        double[] y   = {0.0};
+        double start = reverse ? getEnd()   : getStart();
+        double end   = reverse ? getStart() : getEnd();
+        output       = new ArrayList<Double>();
+        integ.integrate(this, start, y, end, y);
+        double[] actual = new double[output.size()];
+        for(int i = 0; i < actual.length; i++) {
+            actual[i] = output.get(i);
+        }
+        assertArrayEquals(expected, actual, 1e-5);
+    }
+
+    /** {@inheritDoc} */
+    public int getDimension() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    public void computeDerivatives(double t, double[] y, double[] yDot)
+        throws MathUserException {
+        yDot[0] = y[0];
+    }
+
+    /** {@inheritDoc} */
+    public void handleStep(double t, double[] y, double[] yDot, boolean isLast)
+        throws MathUserException {
+        output.add(t);
+    }
+}

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/math/trunk/src/test/java/org/apache/commons/math/ode/sampling/StepNormalizerOutputTestBase.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision