package jas.hist; import java.io.Serializable; import java.util.Observable; import java.util.Observer; public abstract class Fitter extends Observable implements Observer, Runnable, Serializable { public abstract double getChiSquared(); public abstract double[] getParameterSigmas(); protected abstract void fit(Fittable1DFunction fa, double[] x, double[] y, double[] sigmaY) throws FitFailed; public void fit() throws FitFailed { if (state != READYTOFIT) throw new FitFailed("Not ready to fit"); internalFit(); observeData(); } public synchronized void start() { if (state == READYTOFIT) { thread = new Thread(this,"Fit thread"); thread.start(); } } public synchronized void stop() { if (thread != null) thread.stop(); } public void run() { try { internalFit(); observeData(); } catch (FitFailed x) {} finally { synchronized (this) { thread = null; } } } public synchronized void update(Observable obs, Object arg) { if (obs == m_data) { if (state == FIT) setState(READYTOFIT); start(); } } public int getState() { return state; } private synchronized void observeData() { if (!observing && m_data instanceof Observable) ((Observable) m_data).addObserver(this); } private synchronized void setState(int state) { this.state = state; setChanged(); notifyObservers(new FitUpdate(state)); } private synchronized void setState(int state, FitFailed x) { this.state = state; setChanged(); notifyObservers(new FitUpdate(state,x)); } protected void setPercentComplete(int percent) { setChanged(); notifyObservers(new FitUpdate(state,percent)); } private void internalFit() throws FitFailed { setState(FITTING); try { int n = m_data.getNPoints(); double[] x = new double[n]; double[] y = new double[n]; double[] yerr = new double[n]; for (int i=0; i<n; i++) { x[i] = m_data.getX(i); y[i] = m_data.getY(i); yerr[i] = m_data.getPlusError(i); if (yerr[i] != m_data.getMinusError(i)) throw new FitFailed("Cannot fit data with asymmetric error bars"); } fit(new FitAdapter1D(m_func), x, y, yerr); /* * The FitAdapter1D is an extension * of Fittable1DFunction so it looks * to the subclass of Fitter like it * was passed a Fittable1DFunction. * In fact, it has been passed an * adapter class that passes to the * fitter only selected parameters * (those selected by the user) and * provides methods that look * just like those in * Fittable1DFunction. Those methods * in fact perform a parameter list * conversion each time. */ setState(FIT); } catch (FitFailed x) { setState(FAILED,x); throw x; } } public synchronized void setFunction(Fittable1DFunction func) { if (m_func != null && m_func.getFit() == this) m_func.clearFit(); m_func = func; if (m_func != null & m_data != null) setState(READYTOFIT); } public Fittable1DFunction getFunction() { return m_func; } public synchronized void setData(XYDataSource data) { if (observing) { ((Observable) m_data).deleteObserver(this); observing = false; } m_data = data; if (m_func != null & m_data != null) setState(READYTOFIT); } public XYDataSource getData() { return m_data; } protected synchronized void dispose() { if (thread != null) thread.stop(); // Tell any observers we are outahere setState(OUTAHERE); deleteObservers(); setData(null); setFunction(null); } public final static int FITTING = 0; public final static int FIT = 1; public final static int FAILED = 2; public final static int READYTOFIT = 3; public final static int NOTREADYTOFIT = 4; public final static int OUTAHERE = 5; static final long serialVersionUID = -7769799329320822801L; private XYDataSource m_data; private Fittable1DFunction m_func; private int state = NOTREADYTOFIT; private Thread thread; private boolean observing = false; }