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.legacy.linear;
018
019import org.apache.commons.math4.legacy.core.IntegerSequence;
020
021import java.util.Collection;
022import java.util.concurrent.CopyOnWriteArrayList;
023
024/**
025 * This abstract class provides a general framework for managing iterative
026 * algorithms. The maximum number of iterations can be set, and methods are
027 * provided to monitor the current iteration count. A lightweight event
028 * framework is also provided.
029 *
030 */
031public class IterationManager {
032    /** The collection of all listeners attached to this iterative algorithm. */
033    private final Collection<IterationListener> listeners;
034    /** Maximum number of iterations. */
035    private final int maxIterations;
036    /** Callback. */
037    private final IntegerSequence.Incrementor.MaxCountExceededCallback callback;
038    /** Keeps a count of the number of iterations. */
039    private IntegerSequence.Incrementor iterations;
040
041    /**
042     * Creates a new instance of this class.
043     *
044     * @param maxIterations Maximum number of iterations.
045     * {@link org.apache.commons.math4.legacy.exception.MaxCountExceededException}
046     * will be raised at counter exhaustion.
047     */
048    public IterationManager(final int maxIterations) {
049        this(maxIterations, null);
050    }
051
052    /**
053     * Creates a new instance of this class.
054     *
055     * @param maxIterations the maximum number of iterations
056     * @param callback the function to be called when the maximum number of
057     * iterations has been reached.
058     * If {@code null}, {@link org.apache.commons.math4.legacy.exception.MaxCountExceededException}
059     * will be raised at counter exhaustion.
060     *
061     * @since 3.1
062     */
063    public IterationManager(final int maxIterations,
064                            final IntegerSequence.Incrementor.MaxCountExceededCallback callback) {
065        this.maxIterations = maxIterations;
066        this.callback = callback;
067        this.listeners = new CopyOnWriteArrayList<>();
068        resetCounter();
069    }
070
071    /**
072     * Attaches a listener to this manager.
073     *
074     * @param listener A {@code IterationListener} object.
075     */
076    public void addIterationListener(final IterationListener listener) {
077        listeners.add(listener);
078    }
079
080    /**
081     * Informs all registered listeners that the initial phase (prior to the
082     * main iteration loop) has been completed.
083     *
084     * @param e The {@link IterationEvent} object.
085     */
086    public void fireInitializationEvent(final IterationEvent e) {
087        for (IterationListener l : listeners) {
088            l.initializationPerformed(e);
089        }
090    }
091
092    /**
093     * Informs all registered listeners that a new iteration (in the main
094     * iteration loop) has been performed.
095     *
096     * @param e The {@link IterationEvent} object.
097     */
098    public void fireIterationPerformedEvent(final IterationEvent e) {
099        for (IterationListener l : listeners) {
100            l.iterationPerformed(e);
101        }
102    }
103
104    /**
105     * Informs all registered listeners that a new iteration (in the main
106     * iteration loop) has been started.
107     *
108     * @param e The {@link IterationEvent} object.
109     */
110    public void fireIterationStartedEvent(final IterationEvent e) {
111        for (IterationListener l : listeners) {
112            l.iterationStarted(e);
113        }
114    }
115
116    /**
117     * Informs all registered listeners that the final phase (post-iterations)
118     * has been completed.
119     *
120     * @param e The {@link IterationEvent} object.
121     */
122    public void fireTerminationEvent(final IterationEvent e) {
123        for (IterationListener l : listeners) {
124            l.terminationPerformed(e);
125        }
126    }
127
128    /**
129     * Returns the number of iterations of this solver, 0 if no iterations has
130     * been performed yet.
131     *
132     * @return the number of iterations.
133     */
134    public int getIterations() {
135        return iterations.getCount();
136    }
137
138    /**
139     * Returns the maximum number of iterations.
140     *
141     * @return the maximum number of iterations.
142     */
143    public int getMaxIterations() {
144        return iterations.getMaximalCount();
145    }
146
147    /**
148     * Increments the iteration count by one, and throws an exception if the
149     * maximum number of iterations is reached. This method should be called at
150     * the beginning of a new iteration.
151     *
152     * @throws org.apache.commons.math4.legacy.exception.MaxCountExceededException
153     * if the maximum number of iterations is reached.
154     */
155    public void incrementIterationCount() {
156        iterations.increment();
157    }
158
159    /**
160     * Removes the specified iteration listener from the list of listeners
161     * currently attached to {@code this} object. Attempting to remove a
162     * listener which was <em>not</em> previously registered does not cause any
163     * error.
164     *
165     * @param listener The {@link IterationListener} to be removed.
166     */
167    public void removeIterationListener(final IterationListener listener) {
168        listeners.remove(listener);
169    }
170
171    /**
172     * Sets the iteration count to 0. This method must be called during the
173     * initial phase.
174     */
175    public void resetIterationCount() {
176        resetCounter();
177    }
178
179    /** Reset counter. */
180    private void resetCounter() {
181        iterations = IntegerSequence.Incrementor.create()
182            .withMaximalCount(maxIterations);
183        if (callback != null) {
184            iterations = iterations.withCallback(callback);
185        }
186    }
187}