/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.object.bezier;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

import java.io.IOException;
import java.io.ObjectInputStream;

import java.util.ArrayList;
import java.util.List;

import net.sf.jaxodraw.object.JaxoObject;
import net.sf.jaxodraw.object.JaxoObjectEditPanel;
import net.sf.jaxodraw.object.JaxoWiggleObject;
import net.sf.jaxodraw.util.JaxoUtils;
import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;


/**
 * Implementation of a bezier for gluon lines.
 *
 * @since 2.0
 */
public class JaxoGlBezier extends JaxoBezierObject implements JaxoWiggleObject {
    private static final long serialVersionUID = 2L;
    private transient float freq;
    private transient Point2D b2;
    private transient Point2D cp1;
    private transient Point2D cp1up;
    private transient Point2D cp1down;
    private transient Point2D cp2;
    private transient Point2D cp2up;
    private transient Point2D cp2down;
    private transient Point2D sp1;
    private transient Point2D sp2;
    private transient Point2D sp3;
    private transient Point2D sp4;
    private transient Point2D spbuffer1;
    private transient Point2D spbuffer2;
    private transient Point2D spbuffer3;
    private transient List<Double> renormsteps;

    private static final int TOLERANCE = 1;

    /** Constructor: just calls super(). */
    public JaxoGlBezier() {
        super();
        initParams();
    }

    private void readObject(final ObjectInputStream in)
        throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        initParams();
    }

    private void initParams() {
        freq = 0.f;
        b2 = new Point2D.Double();
        cp1 = new Point2D.Double(0.d, 0.d);
        cp1up = new Point2D.Double(0.d, 0.d);
        cp1down = new Point2D.Double(0.d, 0.d);
        cp2 = new Point2D.Double(0.d, 0.d);
        cp2up = new Point2D.Double(0.d, 0.d);
        cp2down = new Point2D.Double(0.d, 0.d);
        sp1 = new Point2D.Double(0.d, 0.d);
        sp2 = new Point2D.Double(0.d, 0.d);
        sp3 = new Point2D.Double(0.d, 0.d);
        sp4 = new Point2D.Double(0.d, 0.d);
        spbuffer1 = new Point2D.Double(0.d, 0.d);
        spbuffer2 = new Point2D.Double(0.d, 0.d);
        spbuffer3 = new Point2D.Double(0.d, 0.d);
        renormsteps = new ArrayList<Double>(500);
    }

    /** Returns an exact copy of this JaxoFBezier.
     * @return A copy of this JaxoFArc.
     */
    @Override
    public final JaxoObject copy() {
        final JaxoGlBezier temp = new JaxoGlBezier();
        temp.copyFrom(this);
        return temp;
    }

    /** {@inheritDoc} */
    @Override
    public final boolean isCopy(final JaxoObject comp) {
        boolean isCopy = false;

        if (comp instanceof JaxoGlBezier) {
            isCopy = super.isCopy(comp);
        }

        return isCopy;
    }

    /** Sets all parameters from the given object to the current one.
     * @param temp The object to copy from.
     */
    public void copyFrom(final JaxoGlBezier temp) {
        super.copyFrom(temp);
        this.freq = temp.getFrequency();
    }

    /** {@inheritDoc} */
    @Override
    public void setState(final JaxoObject o) {
        if (o instanceof JaxoGlBezier) {
            copyFrom((JaxoGlBezier) o);
        } else {
            throw new UnsupportedOperationException("Cannot copy from super type!");
        }
    }

    /** {@inheritDoc} */
    public final void paint(final JaxoGraphics2D g2) {
        g2.setColor(getColor());
        g2.setStroke(getStroke());
        g2.draw(getObjectPath());
    }

    /**
     * Returns the bounding box of this object.
     *
     * @return the bounding box of this object.
     */
    public Rectangle getBounds() {
        // use Area for bounding box calculation instead of gp.getBounds(),
        // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4599407
        return getStroke().createStrokedShape(new Area(getObjectPath()))
                   .getBounds();
    }

    /** {@inheritDoc} */
    public final String latexCommand(final float scale, final Dimension canvasDim) {
        if (JaxoUtils.zero(freq)) {
            setFrequencyFromWiggles(getWiggles());
        } else {
            setWigglesFromFrequency();
        }

        initRenormSteps();

        final String command = constructLatexString(scale, canvasDim.height);

        // Construct the JDidentifier for import purposes

        final String jdIdentifier = "%JaxoID:GlBez"
                + "(" + getX() + "," + getY() + ")" + "(" + getX2() + "," + getY2() + ")"
                + "(" + getX3() + "," + getY3() + ")" + "(" + getX4() + "," + getY4() + ")"
                + "{" + getAmp() + "}" + "{" + getWiggles() + "}" + "[" + isNoFreqStretching() + "]";

        return command + jdIdentifier;
    }

    /** {@inheritDoc} */
    public float getFrequency() {
        return freq;
    }

    /** {@inheritDoc} */
    public void setWigglesFromFrequency(final float frequency) {
        this.freq = frequency;
        setWigglesFromFrequency();
    }

    /** {@inheritDoc} */
    public void setWigglesFromFrequency() {
        final int n = (int) Math.round(getBezierLength() * getFrequency());
        setWiggles(n);
    }

    /** {@inheritDoc} */
    public void setFrequencyFromWiggles(final int wiggles) {
        setWiggles(wiggles);
        this.freq = (float) (wiggles / getBezierLength());
    }

    /** {@inheritDoc} */
    @Override
    public void setPreferences() {
        super.setPreferences();
        setWigglesFromFrequency(GLUON_FREQ);
    }

    /** {@inheritDoc} */
    public void prepareEditPanel(final JaxoObjectEditPanel editPanel) {
        editPanel.add4PointsPanel(getPoints(), 0, 0, 3);
        editPanel.addLineColorPanel(getColor(), 3, 0);
        //editPanel.addDoubleLinePanel(bezier, 3, 0);
        editPanel.addStrokePanel(getStrokeWidth(), 0, 1);
        editPanel.addWigglePanel(getAmp(), getWiggles(), 1, 1);
        editPanel.addStretchingPanel(isNoFreqStretching(), 2, 1);

        editPanel.setTitleAndIcon("Gluon_bezier_parameters", "beziergl.png");
    }

      //
     // private methods
    //

    private double setNextPoint(final Point2D b1, final double par, final double tstep, final boolean noFreqStretching) {
        double tpar = par;

        tryNextBezierPoint(tpar);

        if (noFreqStretching) {
            double length = b2.distance(b1);
            final double reflength = 1.d / getFrequency();

            if ((length - reflength) > TOLERANCE) {
                double rtpar;
                int alter = -1;
                for (int l = 1; l < 20; l++) {
                    rtpar = tpar + ((alter * tstep) / Math.pow(2.d, l));
                    tryNextBezierPoint(rtpar);
                    length = b2.distance(b1);
                    if (Math.abs(length - reflength) <= TOLERANCE) {
                        tpar = rtpar;
                        break;
                    } else if ((length - reflength) > TOLERANCE) {
                        alter = -1;
                        tpar = rtpar;
                    } else {
                        tpar = rtpar;
                        alter = +1;
                    }
                }
            } else if ((length - reflength) < -TOLERANCE) {
                double rtpar;
                int alter = -1;
                boolean seconditer = true;

                //find step for overshooting
                for (int l = 1; l < 10; l++) {
                    rtpar = tpar + (l * tstep);
                    tryNextBezierPoint(rtpar);
                    length = b2.distance(b1);
                    if (Math.abs(length - reflength) <= TOLERANCE) {
                        seconditer = false;
                        tpar = rtpar;
                        break;
                    } else if ((length - reflength) > TOLERANCE) {
                        seconditer = true;
                        alter = -1;
                        tpar = rtpar;
                        break;
                    } else {
                        tpar = rtpar;
                    }
                }

                if (seconditer) {
                    for (int l = 1; l < 20; l++) {
                        rtpar = tpar + ((alter * tstep) / Math.pow(2.d, l));
                        tryNextBezierPoint(rtpar);
                        length = b2.distance(b1);
                        if (Math.abs(length - reflength) <= TOLERANCE) {
                            tpar = rtpar;
                            break;
                        } else if ((length - reflength) > TOLERANCE) {
                            alter = -1;
                            tpar = rtpar;
                        } else {
                            tpar = rtpar;
                            alter = +1;
                        }
                    }
                }
            }
        }
        renormsteps.add(Double.valueOf(tpar));

        return tpar;
    }

    private void tryNextBezierPoint(final double t) {
        b2.setLocation(getPointOnCurve(t));
    }

    private double getBFStep(final Point2D b1, final double rfact) {
        // Determine the shift for the points sets CP1 and CP2 as a function
        // of the current segment length
        final double length = b2.distance(b1);
        return (length / rfact);
    }

    private void setAllCP1(final Point2D b1, final double bfstep, final boolean first) {
        // Sets the CP1 points set
        final Point2D tmpcp1ud = new Point2D.Double(0.d, 0.d);
        final AffineTransform at = new AffineTransform();
        final double theta = Math.atan2(b2.getY() - b1.getY(), b2.getX() - b1.getX());
        final double lx = 0.5 * getAmp() * Math.cos(theta);
        final double ly = 0.5 * getAmp() * Math.sin(theta);
        final double lx1 = bfstep * Math.cos(theta);
        final double ly1 = bfstep * Math.sin(theta);

        if (first) {
            this.cp1.setLocation(b1);
        } else {
            this.cp1.setLocation(b1.getX() + lx1, b1.getY() + ly1);
        }

        tmpcp1ud.setLocation(cp1.getX() + lx, cp1.getY() + ly);

        at.rotate(-Math.PI / 2.d, cp1.getX(), cp1.getY());
        at.transform(tmpcp1ud, this.cp1up);

        at.setToRotation(Math.PI / 2.d, cp1.getX(), cp1.getY());
        at.transform(tmpcp1ud, this.cp1down);
    }

    private void setAllCP2(final Point2D b1, final double bfstep, final boolean last) {
        // Sets the CP2 points set
        final Point2D tmpcp2ud = new Point2D.Double(0.d, 0.d);
        final AffineTransform at = new AffineTransform();
        final double theta = Math.atan2(b2.getY() - b1.getY(), b2.getX() - b1.getX());
        final double lx = 0.5 * getAmp() * Math.cos(theta);
        final double ly = 0.5 * getAmp() * Math.sin(theta);
        final double lx1 = bfstep * Math.cos(theta);
        final double ly1 = bfstep * Math.sin(theta);

        if (last) {
            this.cp2.setLocation(b2);
        } else {
            this.cp2.setLocation(b2.getX() - lx1, b2.getY() - ly1);
        }

        tmpcp2ud.setLocation(cp2.getX() + lx, cp2.getY() + ly);

        at.rotate(-Math.PI / 2.d, cp2.getX(), cp2.getY());
        at.transform(tmpcp2ud, this.cp2up);

        at.setToRotation(Math.PI / 2.d, cp2.getX(), cp2.getY());
        at.transform(tmpcp2ud, this.cp2down);
    }

    private void storePoints(final boolean first) {
        // Store the points for the next step
        this.spbuffer1.setLocation(this.sp3);
        this.spbuffer2.setLocation(this.sp2);
        if (first) {
            this.spbuffer3.setLocation(this.cp1);
        } else {
            this.spbuffer3.setLocation(this.sp1);
        }
        this.sp1.setLocation(this.cp2down);
        this.sp2.setLocation(this.cp2);
        this.sp3.setLocation(this.cp2up);
        if (first) {
            this.sp4.setLocation(this.cp1);
        } else {
            this.sp4.setLocation(this.cp1down);
        }
    }

    // TODO: the number of drawn wiggles is not exactly the same as the number derived from frequency
    private GeneralPath getObjectPath() {
        if (JaxoUtils.zero(freq)) {
            setFrequencyFromWiggles(getWiggles());
        } else {
            setWigglesFromFrequency();
        }

        initRenormSteps();

        return drawingRoutine();
    }

    // drawingRoutine() almost duplicates constructLatexString(), simplify?
    private GeneralPath drawingRoutine() {
        final GeneralPath gp = getGeneralPath();
        gp.reset();

        boolean firststep = true;
        boolean firstleftcurl = true;
        boolean laststep = false;

        final Point2D b1 = new Point2D.Double(getX(), getY());

        for (int t = 0; t < renormsteps.size(); t++) {
            if (t == (renormsteps.size() - 1)) {
                laststep = true;
            }

            tryNextBezierPoint(renormsteps.get(t).doubleValue());

            if (firststep) {
                setAllCP1(b1, 0.45d * getAmp(), firststep);
                setAllCP2(b1, 0.45d * getAmp(), laststep);
                this.sp2.setLocation(cp1);
                this.sp3.setLocation(cp1up);
                storePoints(firststep);
                firststep = false;
            } else {
                setAllCP1(b1, 0.45d * getAmp(), firststep);
                setAllCP2(b1, 0.45d * getAmp(), laststep);
                if (firstleftcurl) {
                    // Need optimization
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;
                    final double p1x = (spbuffer1.getX() + cp1up.getX()) * 0.5d;
                    final double p1y = (spbuffer1.getY() + cp1up.getY()) * 0.5d;
                    final double p3x = (spbuffer1.getX() + p2x) * 0.5d;
                    final double p3y = (spbuffer1.getY() + p2y) * 0.5d;
                    final double p4x = (spbuffer1.getX() - cp1up.getX()) * 0.75d + cp1up.getX();
                    final double p4y = (spbuffer1.getY() - cp1up.getY()) * 0.75d + cp1up.getY();
                    final double p11x = (sp1.getX() + cp1down.getX()) * 0.5d;
                    final double p11y = (sp1.getY() + cp1down.getY()) * 0.5d;
                    //left curl
                    gp.moveTo(p2x, p2y);
                    gp.curveTo(p3x, p3y, p4x, p4y, p1x, p1y);
                    //right curl
                    gp.curveTo(cp1up.getX(), cp1up.getY(), cp1down.getX(), cp1down.getY(), p11x, p11y);
                    firstleftcurl = false;
                } else {
                    final double p1x = (spbuffer1.getX() - cp1up.getX()) * 0.5d + cp1up.getX();
                    final double p1y = (spbuffer1.getY() - cp1up.getY()) * 0.5d + cp1up.getY();
                    final double p11x = (sp1.getX() + cp1down.getX()) * 0.5d;
                    final double p11y = (sp1.getY() + cp1down.getY()) * 0.5d;
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;
                    //left curls
                    gp.moveTo(p2x, p2y);
                    gp.curveTo(spbuffer3.getX(), spbuffer3.getY(), spbuffer1.getX(), spbuffer1.getY(), p1x, p1y);
                    //right curls
                    gp.curveTo(cp1up.getX(), cp1up.getY(), cp1down.getX(), cp1down.getY(), p11x, p11y);
                }
                if (laststep) {
                    storePoints(firststep);
                    setAllCP2(b1, 0.45d * getAmp(), laststep); // this is different from constructLatexString, why?
                    final double p1x = (spbuffer1.getX() + cp2up.getX()) * 0.5d;
                    final double p1y = (spbuffer1.getY() + cp2up.getY()) * 0.5d;
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;
                    final double p3x = (spbuffer1.getX() - cp2up.getX()) * 0.25d + cp2up.getX();
                    final double p3y = (spbuffer1.getY() - cp2up.getY()) * 0.25d + cp2up.getY();
                    final double p4x = (cp2up.getX() + cp2.getX()) * 0.5d;
                    final double p4y = (cp2up.getY() + cp2.getY()) * 0.5d;
                    //left curl
                    gp.moveTo(p2x, p2y);
                    gp.curveTo(spbuffer3.getX(), spbuffer3.getY(), spbuffer1.getX(), spbuffer1.getY(), p1x, p1y);
                    //right curl
                    gp.curveTo(p3x, p3y, p4x, p4y, cp2.getX(), cp2.getY());
                }    // constructLatexString() almost duplicates drawingRoutine(), simplify?

                storePoints(firststep);
            }
            b1.setLocation(b2);
        }

        return gp;
    }

    // constructLatexString() almost duplicates drawingRoutine(), simplify?
    private String constructLatexString(final float scale, final int canvasHeight) {
        final StringBuffer command = new StringBuffer(128);
        boolean firststep = true;
        boolean firstleftcurl = true;
        boolean laststep = false;

        final Point2D b1 = new Point2D.Double(getX(), getY());

        for (int t = 0; t < renormsteps.size(); t++) {
            if (t == (renormsteps.size() - 1)) {
                laststep = true;
            }

            tryNextBezierPoint(renormsteps.get(t).doubleValue());

            if (firststep) {
                setAllCP1(b1, 0.45d * getAmp(), firststep);
                setAllCP2(b1, 0.45d * getAmp(), laststep);
                this.sp2.setLocation(cp1);
                this.sp3.setLocation(cp1up);
                storePoints(firststep);
                firststep = false;
            } else {
                setAllCP1(b1, 0.45d * getAmp(), firststep);
                setAllCP2(b1, 0.45d * getAmp(), laststep);
                if (firstleftcurl) {
                    // Need optimization
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;
                    final double p1x = (spbuffer1.getX() + cp1up.getX()) * 0.5d;
                    final double p1y = (spbuffer1.getY() + cp1up.getY()) * 0.5d;
                    final double p3x = (spbuffer1.getX() + p2x) * 0.5d;
                    final double p3y = (spbuffer1.getY() + p2y) * 0.5d;
                    final double p4x = (spbuffer1.getX() - cp1up.getX()) * 0.75d + cp1up.getX();
                    final double p4y = (spbuffer1.getY() - cp1up.getY()) * 0.75d + cp1up.getY();
                    final double p11x = (sp1.getX() + cp1down.getX()) * 0.5d;
                    final double p11y = (sp1.getY() + cp1down.getY()) * 0.5d;

                    final Point2D latexP1 = getLatexPoint(p1x, p1y, scale, canvasHeight);
                    final Point2D latexP2 = getLatexPoint(p2x, p2y, scale, canvasHeight);
                    final Point2D latexP3 = getLatexPoint(p3x, p3y, scale, canvasHeight);
                    final Point2D latexP4 = getLatexPoint(p4x, p4y, scale, canvasHeight);
                    final Point2D latexP11 = getLatexPoint(p11x, p11y, scale, canvasHeight);
                    final Point2D latexCp1up = getLatexPoint(cp1up.getX(), cp1up.getY(), scale, canvasHeight);
                    final Point2D latexCp1down = getLatexPoint(cp1down.getX(), cp1down.getY(), scale, canvasHeight);
                    //left curl
                    command.append(' ').append(bezierLatexCommand("", latexP2, latexP3, latexP4, latexP1));
                    //right curl
                    command.append(' ').append(bezierLatexCommand("", latexP1, latexCp1up, latexCp1down, latexP11));
                    firstleftcurl = false;
                } else {
                    final double p1x = (spbuffer1.getX() - cp1up.getX()) * 0.5d + cp1up.getX();
                    final double p1y = (spbuffer1.getY() - cp1up.getY()) * 0.5d + cp1up.getY();
                    final double p11x = (sp1.getX() + cp1down.getX()) * 0.5d;
                    final double p11y = (sp1.getY() + cp1down.getY()) * 0.5d;
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;

                    final Point2D latexP1 = getLatexPoint(p1x, p1y, scale, canvasHeight);
                    final Point2D latexP2 = getLatexPoint(p2x, p2y, scale, canvasHeight);
                    final Point2D latexP11 = getLatexPoint(p11x, p11y, scale, canvasHeight);
                    final Point2D latexCp1up = getLatexPoint(cp1up.getX(), cp1up.getY(), scale, canvasHeight);
                    final Point2D latexCp1down = getLatexPoint(cp1down.getX(), cp1down.getY(), scale, canvasHeight);
                    final Point2D latexSpbuf1 = getLatexPoint(spbuffer1.getX(), spbuffer1.getY(), scale, canvasHeight);
                    final Point2D latexSpbuf3 = getLatexPoint(spbuffer3.getX(), spbuffer3.getY(), scale, canvasHeight);
                    //left curls
                    command.append(' ').append(bezierLatexCommand("", latexP2, latexSpbuf3, latexSpbuf1, latexP1));
                    //right curls
                    command.append(' ').append(bezierLatexCommand("", latexP1, latexCp1up, latexCp1down, latexP11));
                }
                if (laststep) {
                    storePoints(firststep);
                    setAllCP2(b1, getBFStep(b1, 4.d), laststep);
                    final double p1x = (spbuffer1.getX() + cp2up.getX()) * 0.5d;
                    final double p1y = (spbuffer1.getY() + cp2up.getY()) * 0.5d;
                    final double p2x = (sp4.getX() + spbuffer3.getX()) * 0.5d;
                    final double p2y = (sp4.getY() + spbuffer3.getY()) * 0.5d;
                    final double p3x = (spbuffer1.getX() - cp2up.getX()) * 0.25d + cp2up.getX();
                    final double p3y = (spbuffer1.getY() - cp2up.getY()) * 0.25d + cp2up.getY();
                    final double p4x = (cp2up.getX() + cp2.getX()) * 0.5d;
                    final double p4y = (cp2up.getY() + cp2.getY()) * 0.5d;

                    final Point2D latexP1 = getLatexPoint(p1x, p1y, scale, canvasHeight);
                    final Point2D latexP2 = getLatexPoint(p2x, p2y, scale, canvasHeight);
                    final Point2D latexP3 = getLatexPoint(p3x, p3y, scale, canvasHeight);
                    final Point2D latexP4 = getLatexPoint(p4x, p4y, scale, canvasHeight);
                    final Point2D latexCp2 = getLatexPoint(cp2.getX(), cp2.getY(), scale, canvasHeight);
                    final Point2D latexSpbuf1 = getLatexPoint(spbuffer1.getX(), spbuffer1.getY(), scale, canvasHeight);
                    final Point2D latexSpbuf3 = getLatexPoint(spbuffer3.getX(), spbuffer3.getY(), scale, canvasHeight);
                    //left curl
                    command.append(' ').append(bezierLatexCommand("", latexP2, latexSpbuf3, latexSpbuf1, latexP1));
                    //right curl
                    command.append(' ').append(bezierLatexCommand("", latexP1, latexP3, latexP4, latexCp2));
                }
                storePoints(firststep);
            }
            b1.setLocation(b2);
        }

        return command.toString();
    }

    private void initRenormSteps() {
        // Initialize the vector of renormalized timesteps
        renormsteps.clear();

        // First thing: calculate the (approximate) length of the curve.
        final double length = getBezierLength();

        final int wnumb = (int) Math.round(length * getFrequency());

        // Second: define the time step
        // and the tolerance for the last point according to frequency stretching;
        final double tstep = 1.f / wnumb;
        final double lptol = isNoFreqStretching() ? .015 : .0001;

        // Third: draw the Bezier
        final Point2D b1 = new Point2D.Double(getX(), getY());

        // First loop: fill
        // the vector of renormalized time steps to be used when drawing
        // the curve; if frequency stretching is allowed, the vector will
        // contain equal timesteps
        double tpar = tstep;
        while (tpar <= (1.d + tstep)) {
            tpar = setNextPoint(b1, tpar, tstep, this.isNoFreqStretching());

            if (Math.abs(tpar - 1 - tstep) < lptol
                    && ((Math.abs(b2.getX() - getX()) > .1)
                    || (Math.abs(b2.getY() - getY()) > .1))) {
                break;
            }
            b1.setLocation(b2);
            tpar += tstep;
        }

        // Overshooting will eventually occur only for the last point in
        // renormsteps; if this is the case remove the last element
        if (((renormsteps.get(renormsteps.size() - 1)).doubleValue() - 1.d) > .0000001) {
            renormsteps.remove(renormsteps.size() - 1);
        }

        // Calculate the correction to apply for each timestep, and
        // reset all the components of the renormalized timestep vector
        // in such a way that its last component will be 1.0
        final double corr = (1.d - (renormsteps.get(renormsteps.size() - 1)).doubleValue())
                / (renormsteps.size());

        for (int i = 0; i < renormsteps.size(); i++) {
            final double rvalue = (renormsteps.get(i)).doubleValue() + corr * (i + 1);
            renormsteps.set(i, Double.valueOf(rvalue));
        }
    }
}
