/*
 *  Alex Mathews, amathews@jhu.edu
 *  Pranav Lakshminarayanan, plakshm1@jhu.edu
 *  Computer Integrated Surgery I - Fall 2014
 */
package iterativeclosestpoint;

import java.util.ArrayList;

/**
 * Converges when change in error is below a threshold 
 *  for a number of iterations.
 * @author Pranav
 */
public class FlatlineConvergence implements Convergence {

    /**
     * Bound value for change in error.
     */
    private final double diffBound;
    
    /**
     * Desired number of iterations to be within the threshold value.
     */
    private final int threshNumIter;
    
    /**
     * Number of iterations that error has been below threshold.
     */
    private int threshIter = 0;
    
    /**
     * Maximum number of iterations to be run.
     */
    private final int totalNumIter;
    
    /**
     * Total number of iterations complete.
     */
    private int totalIter;
    
    /**
     * List of errors from each iteration.
     */
    private final ArrayList<Double> errors = new ArrayList<>();
    
    /**
     * Constructs a convergence criteria 
     *  that converges when errors stop changing.
     * @param e change in error should be less than abs(e)
     * @param niTot how many iterations should the error remain constant? 
     * @param niMax maximum number of iterations to be run
     */
    public FlatlineConvergence(double e, int niTot, int niMax) {
        this.diffBound = e;
        this.threshNumIter = niTot;
        this.totalNumIter = niMax;
    }
    
    @Override
    public ArrayList<Double> getErrors() {
        return this.errors;
    }

    @Override
    public boolean isConverged(double err) {
        this.errors.add(err);
        if (this.totalIter <= this.totalNumIter) {
            this.totalIter++;
            //If change in error is within threshold, 
            //  data is starting to converge
            if (this.errors.size() > 2
                    && Math.abs(err - this.errors.get(this.errors.size() - 2)) 
                    < this.diffBound) {
                this.threshIter++;
            } else {
                this.threshIter = 0;
            }
            return this.threshIter >= this.threshNumIter;
        }
        return true;
    }
    
}
