001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.util;
018
019import java.util.Collection;
020import java.util.concurrent.CopyOnWriteArrayList;
021
022/**
023 * This abstract class provides a general framework for managing iterative
024 * algorithms. The maximum number of iterations can be set, and methods are
025 * provided to monitor the current iteration count. A lightweight event
026 * framework is also provided.
027 *
028 */
029public class IterationManager {
030    /** The collection of all listeners attached to this iterative algorithm. */
031    private final Collection<IterationListener> listeners;
032    /** Maximum number of iterations. */
033    private final int maxIterations;
034    /** Callback. */
035    private final IntegerSequence.Incrementor.MaxCountExceededCallback callback;
036    /** Keeps a count of the number of iterations. */
037    private IntegerSequence.Incrementor iterations;
038
039    /**
040     * Creates a new instance of this class.
041     *
042     * @param maxIterations Maximum number of iterations.
043     * {@link org.apache.commons.math4.exception.MaxCountExceededException}
044     * will be raised at counter exhaustion.
045     */
046    public IterationManager(final int maxIterations) {
047        this(maxIterations, null);
048    }
049
050    /**
051     * Creates a new instance of this class.
052     *
053     * @param maxIterations the maximum number of iterations
054     * @param callback the function to be called when the maximum number of
055     * iterations has been reached.
056     * If {@code null}, {@link org.apache.commons.math4.exception.MaxCountExceededException}
057     * will be raised at counter exhaustion.
058     *
059     * @since 3.1
060     */
061    public IterationManager(final int maxIterations,
062                            final IntegerSequence.Incrementor.MaxCountExceededCallback callback) {
063        this.maxIterations = maxIterations;
064        this.callback = callback;
065        this.listeners = new CopyOnWriteArrayList<>();
066        resetCounter();
067    }
068
069    /**
070     * Attaches a listener to this manager.
071     *
072     * @param listener A {@code IterationListener} object.
073     */
074    public void addIterationListener(final IterationListener listener) {
075        listeners.add(listener);
076    }
077
078    /**
079     * Informs all registered listeners that the initial phase (prior to the
080     * main iteration loop) has been completed.
081     *
082     * @param e The {@link IterationEvent} object.
083     */
084    public void fireInitializationEvent(final IterationEvent e) {
085        for (IterationListener l : listeners) {
086            l.initializationPerformed(e);
087        }
088    }
089
090    /**
091     * Informs all registered listeners that a new iteration (in the main
092     * iteration loop) has been performed.
093     *
094     * @param e The {@link IterationEvent} object.
095     */
096    public void fireIterationPerformedEvent(final IterationEvent e) {
097        for (IterationListener l : listeners) {
098            l.iterationPerformed(e);
099        }
100    }
101
102    /**
103     * Informs all registered listeners that a new iteration (in the main
104     * iteration loop) has been started.
105     *
106     * @param e The {@link IterationEvent} object.
107     */
108    public void fireIterationStartedEvent(final IterationEvent e) {
109        for (IterationListener l : listeners) {
110            l.iterationStarted(e);
111        }
112    }
113
114    /**
115     * Informs all registered listeners that the final phase (post-iterations)
116     * has been completed.
117     *
118     * @param e The {@link IterationEvent} object.
119     */
120    public void fireTerminationEvent(final IterationEvent e) {
121        for (IterationListener l : listeners) {
122            l.terminationPerformed(e);
123        }
124    }
125
126    /**
127     * Returns the number of iterations of this solver, 0 if no iterations has
128     * been performed yet.
129     *
130     * @return the number of iterations.
131     */
132    public int getIterations() {
133        return iterations.getCount();
134    }
135
136    /**
137     * Returns the maximum number of iterations.
138     *
139     * @return the maximum number of iterations.
140     */
141    public int getMaxIterations() {
142        return iterations.getMaximalCount();
143    }
144
145    /**
146     * Increments the iteration count by one, and throws an exception if the
147     * maximum number of iterations is reached. This method should be called at
148     * the beginning of a new iteration.
149     *
150     * @throws org.apache.commons.math4.exception.MaxCountExceededException
151     * if the maximum number of iterations is reached.
152     */
153    public void incrementIterationCount() {
154        iterations.increment();
155    }
156
157    /**
158     * Removes the specified iteration listener from the list of listeners
159     * currently attached to {@code this} object. Attempting to remove a
160     * listener which was <em>not</em> previously registered does not cause any
161     * error.
162     *
163     * @param listener The {@link IterationListener} to be removed.
164     */
165    public void removeIterationListener(final IterationListener listener) {
166        listeners.remove(listener);
167    }
168
169    /**
170     * Sets the iteration count to 0. This method must be called during the
171     * initial phase.
172     */
173    public void resetIterationCount() {
174        resetCounter();
175    }
176
177    /** Reset counter. */
178    private void resetCounter() {
179        iterations = IntegerSequence.Incrementor.create()
180            .withMaximalCount(maxIterations);
181        if (callback != null) {
182            iterations = iterations.withCallback(callback);
183        }
184    }
185}