/**
  * Advances the ODE as usual, except if an event takes place. Then it finds the event point and
  * applies the actions
  *
  * @return The actual step taken
  */
 public double step() { // Step from t=a to t=b(=a+dt)
   errorCode = ODEAdaptiveSolver.NO_ERROR;
   eventHappened = false;
   double t = 0, origDt = solver.getStepSize();
   do {
     triggerOde.readRealState(); // Prepare the faked ODE
     System.arraycopy(triggerOde.getState(), 0, statea, 0, size); // Set
     // statea
     // values at b
     double dt = solver.step();
     double[] state = triggerOde.getState();
     // Find which events have happened
     happened.clear();
     for (Enumeration<StateEvent> e = eventList.elements(); e.hasMoreElements(); ) {
       StateEvent evt = e.nextElement();
       if (evt.evaluate(state) <= -evt.getTolerance()) {
         happened.add(evt); // This event actually happened!
       }
     }
     // Check for no event
     if (happened.size() == 0) {
       triggerOde.updateRealState();
       solver.setStepSize(origDt);
       return dt;
     }
     eventHappened = true;
     // System.out.println ("An event!");
     // else System.out.println
     // ("N of events = "+happened.size()+" First is = "+happened.elementAt(0));
     /*
      * This is the moment of truth! We need to find the precise instant
      * of time for the first event
      */
     // Go back to statea
     triggerOde.setState(statea);
     // Check first for a itself
     // This is to make sure that when two events happen at the same
     // time they will be found at the exact same instant.
     // This is important for accuracy of results and better performance.
     StateEvent eventFound = null;
     for (StateEvent evt : happened) {
       if (Math.abs(evt.evaluate(statea)) < evt.getTolerance()) { // Found
         // at
         // a
         // itself
         eventFound = evt;
         // System.out.println("Found at a = " + state[state.length -
         // 1]);
         break; // No need to continue
       }
     }
     if (eventFound == null) { // Now find by subdivision
       // This synchronizes our triggerOde state with the state of the
       // ODEInterpolatorSolver
       if (solver instanceof ODEInterpolationSolver) solver.initialize(solver.getStepSize());
       for (int i = 0; i < MAX; i++) { // Start the subdivision
         // System.out.println ("Subdividing i = "+i+
         // "  t = "+state[state.length-1]);
         solver.setStepSize(dt *= 0.5); // Take half the step
         double c = solver.step();
         state = triggerOde.getState();
         StateEvent previousFound = null;
         for (StateEvent evt : happened) {
           double f_i = evt.evaluate(state);
           if (f_i <= -evt.getTolerance()) {
             previousFound = evt;
             break;
           }
           if (f_i < evt.getTolerance()) {
             eventFound = evt; // Do not break in case there is a
             // previous one
           }
         }
         if (previousFound != null) {
           /*
            * Eliminate events that may come later (This is not so
            * necessary)
            */
           for (StateEvent evt : happened) {
             if (evt != previousFound && (evt.evaluate(state) > -evt.getTolerance())) {
               happened.remove(evt);
             }
           }
           triggerOde.setState(statea); // go back to a
           // This synchronizes our triggerOde state with the state
           // of the ODEInterpolatorSolver
           if (solver instanceof ODEInterpolationSolver) solver.initialize(solver.getStepSize());
         } else { // Advance to new position
           t = t + c;
           System.arraycopy(state, 0, statea, 0, size);
           if (eventFound != null) { // We found it!
             // System.out.println
             // ("Found at "+state[state.length-1]);
             break;
           }
         }
       } // End of the subdivision scheme
       // The event is any of those which remain in the list of
       // happened
       if (eventFound == null) { // If this happens, the event is most
         // likely poorly designed!
         eventFound = (StateEvent) happened.elementAt(0);
         System.err.println(
             "BisectionEventSolver Warning : Event not found after " + MAX + " subdivisions!!!");
         System.err.println("  Event = " + eventFound);
         System.err.println(
             "  Please check your event algorithm or decrease the initial stepTime.");
         errorCode = ODEAdaptiveSolver.BISECTION_EVENT_NOT_FOUND;
       }
     }
     // System.out.println ("We are at time = "+state[state.length-1]);
     // Update real ODE
     triggerOde.updateRealState();
     if (eventFound.action()) {
       if (solver instanceof ODEInterpolationSolver) {
         triggerOde.readRealState();
         solver.initialize(origDt);
       } else solver.setStepSize(origDt);
       return t;
     }
     // System.out.println("t = " + t);
     if (solver instanceof ODEInterpolationSolver) {
       triggerOde.readRealState();
       solver.initialize(origDt - t);
     } else solver.setStepSize(origDt - t);
   } while (t < origDt);
   solver.setStepSize(origDt);
   return t;
 }
 public void setStepSize(final double stepSize) {
   solver.setStepSize(stepSize); // Defer to the real solver
 }