--- a/src/iterate.rs Tue Feb 20 12:33:16 2024 -0500 +++ b/src/iterate.rs Mon Feb 03 19:22:16 2025 -0500 @@ -19,6 +19,7 @@ .. Default::default() }; let mut x = 1 as float; +# let mut iter_clone = iter.clone(); iter.iterate(|state|{ // This is our computational step x = x + x.sqrt(); @@ -26,7 +27,17 @@ // return current value when requested return x }) -}) +}); +// or alternatively (avoiding problems with moves) +# iter = iter_clone; +for state in iter.iter() { + // This is our computational step + x = x + x.sqrt(); + state.if_verbose(||{ + // return current value when requested + return x + }) +} ``` There is no colon after `state.if_verbose`, because we need to return its value. If you do something after the step, you need to store the result in a variable and return it from the main computational step afterwards. @@ -52,6 +63,8 @@ use std::marker::PhantomData; use std::time::Duration; use std::error::Error; +use std::cell::RefCell; +use std::rc::Rc; use crate::types::*; use crate::logger::*; @@ -114,7 +127,7 @@ /// /// This is the parameter obtained by the closure passed to [`AlgIterator::iterate`] or /// [`AlgIteratorFactory::iterate`]. -pub trait AlgIteratorState { +pub trait AlgIteratorState : Sized { /// Call `call_objective` if this is a verbose iteration. /// /// Verbosity depends on the [`AlgIterator`] that produced this state. @@ -122,15 +135,18 @@ /// The closure `calc_objective` should return an arbitrary value of type `V`, to be inserted /// into the log, or whatever is deemed by the [`AlgIterator`]. For usage instructions see the /// [module documentation][self]. - fn if_verbose<V, E : Error>(&self, calc_objective : impl FnMut() -> V) -> Step<V, E>; + fn if_verbose<V, E : Error>(self, calc_objective : impl FnOnce() -> V) -> Step<V, Self, E>; /// Returns the current iteration count. fn iteration(&self) -> usize; + + /// Indicates whether the iterator is quiet + fn is_quiet(&self) -> bool; } /// Result of a step of an [`AlgIterator`] #[derive(Debug, Serialize)] -pub enum Step<V, Fail : Error = std::convert::Infallible> { +pub enum Step<V, S, Fail : Error = std::convert::Infallible> { /// Iteration should be terminated Terminated, /// Iteration should be terminated due to failure @@ -138,14 +154,14 @@ /// No result this iteration (due to verbosity settings) Quiet, /// Result of this iteration (due to verbosity settings) - Result(V), + Result(V, S), } -impl<V, E : Error> Step<V, E> { +impl<V, S, E : Error> Step<V, S, E> { /// Maps the value contained within the `Step`, if any, by the closure `f`. - pub fn map<U>(self, mut f : impl FnMut(V) -> U) -> Step<U, E> { + pub fn map<U>(self, mut f : impl FnMut(V) -> U) -> Step<U, S, E> { match self { - Step::Result(v) => Step::Result(f(v)), + Step::Result(v, s) => Step::Result(f(v), s), Step::Failure(e) => Step::Failure(e), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, @@ -153,6 +169,12 @@ } } +impl<V, S, E : Error> Default for Step<V, S, E> { + fn default() -> Self { + Step::Quiet + } +} + /// An iterator for algorithms, produced by [`AlgIteratorFactory::prepare`]. /// /// Typically not accessed directly, but transparently produced by an [`AlgIteratorFactory`]. @@ -160,13 +182,26 @@ pub trait AlgIterator : Sized { /// The state type type State : AlgIteratorState; - /// The output type for [`Self::step`]. + /// The output type for [`Self::poststep`] and [`Self::step`]. type Output; - /// The error type for [`Self::step`] and [`Self::iterate`]. - type Err : Error; + /// The input type for [`Self::poststep`]. + type Input; - /// Advance the iterator. - fn step(&mut self) -> Step<Self::Output, Self::Err>; + /// Advance the iterator, performing `step_fn` with the state + fn step<F, E>(&mut self, step_fn : &mut F) -> Step<Self::Output, Self::State, E> + where F : FnMut(Self::State) -> Step<Self::Input, Self::State, E>, + E : Error { + self.prestep().map_or(Step::Terminated, + |state| self.poststep(step_fn(state))) + } + + /// Initial stage of advancing the iterator, before the actual step + fn prestep(&mut self) -> Option<Self::State>; + + /// Handle step result + fn poststep<E>(&mut self, result : Step<Self::Input, Self::State, E>) + -> Step<Self::Output, Self::State, E> + where E : Error; /// Return current iteration count. fn iteration(&self) -> usize { @@ -176,56 +211,29 @@ /// Return current state. fn state(&self) -> Self::State; - /// Iterate the `AlgIterator` until termination. + /// Iterate the `AlgIterator` until termination, erforming `step_fn` on each step. /// /// Returns either `()` or an error if the step closure terminated in [`Step::FailureĀ“]. #[inline] - fn iterate(&mut self) -> Result<(), Self::Err> { + fn iterate<F, E>(&mut self, mut step_fn : F) -> Result<(), E> + where F : FnMut(Self::State) -> Step<Self::Input, Self::State, E>, + E : Error { loop { - match self.step() { + match self.step(&mut step_fn) { Step::Terminated => return Ok(()), Step::Failure(e) => return Err(e), _ => {}, } } } - - /// Converts the `AlgIterator` into a plain [`Iterator`]. - /// - /// [`Step::Quiet`] results are discarded, and [`Step::Failure`] results **panic**. - fn downcast(self) -> AlgIteratorI<Self> { - AlgIteratorI(self) - } -} - -/// Conversion of an `AlgIterator` into a plain [`Iterator`]. -/// -/// The conversion discards [`Step::Quiet`] and **panics** on [`Step::Failure`]. -pub struct AlgIteratorI<A>(A); - -impl<A> Iterator for AlgIteratorI<A> -where A : AlgIterator { - type Item = A::Output; - - fn next(&mut self) -> Option<A::Output> { - loop { - match self.0.step() { - Step::Result(v) => return Some(v), - Step::Failure(e) => panic!("{e:?}"), - Step::Terminated => return None, - Step::Quiet => continue, - } - } - } } /// A factory for producing an [`AlgIterator`]. /// /// For usage instructions see the [module documentation][self]. pub trait AlgIteratorFactory<V> : Sized { - type Iter<F, E> : AlgIterator<State = Self::State, Output = Self::Output, Err = E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter : AlgIterator<State = Self::State, Input = V, Output = Self::Output>; + /// The state type of the corresponding [`AlgIterator`]. /// A reference to this is passed to the closures passed to methods such as [`Self::iterate`]. type State : AlgIteratorState; @@ -235,12 +243,7 @@ type Output; /// Prepare an [`AlgIterator`], consuming the factory. - /// - /// The function `step_fn` should accept a `state` ([`AlgIteratorState`] parameter, and return - /// a [`Step`]. - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + fn prepare(self) -> Self::Iter; /// Iterate the the closure `step`. /// @@ -252,12 +255,12 @@ /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`]. #[inline] fn iterate_fallible<F, E>(self, step : F) -> Result<(), E> - where F : FnMut(&Self::State) -> Step<V, E>, + where F : FnMut(Self::State) -> Step<V, Self::State, E>, E : Error { - self.prepare(step).iterate() + self.prepare().iterate(step) } - /// Iterate the the closure `step`. + /// Iterate the closure `step`. /// /// The closure should accept a `state` parameter (satisfying the trait [`AlgIteratorState`]), /// It should return the output of @@ -269,7 +272,7 @@ /// with the error type `E=`[`std::convert::Infallible`]. #[inline] fn iterate<F>(self, step : F) - where F : FnMut(&Self::State) -> Step<V> { + where F : FnMut(Self::State) -> Step<V, Self::State> { self.iterate_fallible(step).unwrap_or_default() } @@ -285,13 +288,14 @@ /// /// For usage instructions see the [module documentation][self]. #[inline] - fn iterate_data_fallible<F, D, I, E>(self, mut datasource : I, mut step : F) -> Result<(), E> - where F : FnMut(&Self::State, D) -> Step<V, E>, + fn iterate_data_fallible<F, D, I, E>(self, mut datasource : I, mut step : F) + -> Result<(), E> + where F : FnMut(Self::State, D) -> Step<V, Self::State, E>, I : Iterator<Item = D>, E : Error { - self.prepare(move |state| { + self.prepare().iterate(move |state| { datasource.next().map_or(Step::Terminated, |d| step(state, d)) - }).iterate() + }) } /// Iterate the closure `step` with data produced by `datasource`. @@ -306,7 +310,7 @@ /// For usage instructions see the [module documentation][self]. #[inline] fn iterate_data<F, D, I>(self, datasource : I, step : F) - where F : FnMut(&Self::State, D) -> Step<V>, + where F : FnMut(Self::State, D) -> Step<V, Self::State>, I : Iterator<Item = D> { self.iterate_data_fallible(datasource, step).unwrap_or_default() } @@ -395,6 +399,26 @@ /// Is the iterator quiet, i.e., on-verbose? fn is_quiet(&self) -> bool { false } + + /// Returns an an [`std::iter::Iterator`] that can be used in a `for`-loop. + fn iter(self) -> AlgIteratorIterator<Self::Iter> { + AlgIteratorIterator { + algi : Rc::new(RefCell::new(self.prepare())), + } + } + + /// Returns an an [`std::iter::Iterator`] that can be used in a `for`-loop, + /// also inputting an initial iteration status calculated by `f` if needed. + fn iter_init(self, f : impl FnOnce() -> <Self::Iter as AlgIterator>::Input) + -> AlgIteratorIterator<Self::Iter> { + let mut i = self.prepare(); + let st = i.state(); + let step : Step<<Self::Iter as AlgIterator>::Input, Self::State> = st.if_verbose(f); + i.poststep(step); + AlgIteratorIterator { + algi : Rc::new(RefCell::new(i)), + } + } } /// Options for [`BasicAlgIteratorFactory`]. @@ -427,6 +451,11 @@ /// * every 10 iterations from there on until 100 iterations, /// * every 100 iteartinos frmo there on until 1000 iterations, etc. Logarithmic(usize), + /// Same as `Logarithmic`, but $\log_b(n)$ is replaced by $min\{c, \log_b(n)\}$ where $c$ + /// is the given `cap`. For example, with `base=10` and `cap=2`, the first ten iterations + /// will be output, then every tenth iteration, and after 100 iterations, every 100th iteration, + /// without further logarithmic progression. + LogarithmicCap{ base : usize, cap : u32 }, } impl Verbose { @@ -443,6 +472,10 @@ let every = base.pow((iter as float).log(base as float).floor() as u32); iter % every == 0 } + &Verbose::LogarithmicCap{base, cap} => { + let every = base.pow(((iter as float).log(base as float).floor() as u32).min(cap)); + iter % every == 0 + } } } } @@ -467,6 +500,8 @@ verbose : bool, /// Whether results should be calculated. calc : bool, + /// Indicates whether the iteration is quiet + quiet : bool, } /// [`AlgIteratorFactory`] for [`BasicAlgIterator`] @@ -478,11 +513,10 @@ /// The simplest [`AlgIterator`], created by [`BasicAlgIteratorFactory`] #[derive(Clone,Debug)] -pub struct BasicAlgIterator<F, V, E : Error> { +pub struct BasicAlgIterator<V> { options : AlgIteratorOptions, iter : usize, - step_fn : F, - _phantoms : PhantomData<(V, E)>, + _phantoms : PhantomData<V>, } impl AlgIteratorOptions { @@ -500,18 +534,13 @@ impl<V> AlgIteratorFactory<V> for AlgIteratorOptions where V : LogRepr { type State = BasicState; - type Iter<F, E> = BasicAlgIterator<F, V, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = BasicAlgIterator<V>; type Output = V; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { BasicAlgIterator{ options : self, iter : 0, - step_fn, _phantoms : PhantomData, } } @@ -525,18 +554,13 @@ impl<V> AlgIteratorFactory<V> for BasicAlgIteratorFactory<V> where V : LogRepr { type State = BasicState; - type Iter<F, E> = BasicAlgIterator<F, V, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = BasicAlgIterator<V>; type Output = V; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { BasicAlgIterator { options : self.options, iter : 0, - step_fn, _phantoms : PhantomData } } @@ -547,33 +571,33 @@ } } -impl<F, V, E> AlgIterator for BasicAlgIterator<F, V, E> -where V : LogRepr, - E : Error, - F : FnMut(&BasicState) -> Step<V, E> { +impl<V> AlgIterator for BasicAlgIterator<V> +where V : LogRepr { type State = BasicState; type Output = V; - type Err = E; + type Input = V; #[inline] - fn step(&mut self) -> Step<V, E> { + fn prestep(&mut self) -> Option<Self::State> { if self.iter >= self.options.max_iter { - Step::Terminated + None } else { self.iter += 1; - let state = self.state(); - let res = (self.step_fn)(&state); - if let Step::Result(ref val) = res { - if state.verbose && !self.options.quiet { - println!("{}{}/{} {}{}", "".dimmed(), - state.iter, - self.options.max_iter, - val.logrepr(), - "".clear()); - } + Some(self.state()) + } + } + + fn poststep<E : Error>(&mut self, res : Step<V, Self::State, E>) -> Step<V, Self::State, E> { + if let Step::Result(ref val, ref state) = res { + if state.verbose && !self.options.quiet { + println!("{}{}/{} {}{}", "".dimmed(), + state.iter, + self.options.max_iter, + val.logrepr(), + "".clear()); } - res } + res } #[inline] @@ -585,19 +609,20 @@ fn state(&self) -> BasicState { let iter = self.iter; let verbose = self.options.verbose_iter.is_verbose(iter); - BasicState{ + BasicState { iter : iter, verbose : verbose, calc : verbose, + quiet : self.options.quiet } } } impl AlgIteratorState for BasicState { #[inline] - fn if_verbose<V, E : Error>(&self, mut calc_objective : impl FnMut() -> V) -> Step<V, E> { + fn if_verbose<V, E : Error>(self, calc_objective : impl FnOnce() -> V) -> Step<V, Self, E> { if self.calc { - Step::Result(calc_objective()) + Step::Result(calc_objective(), self) } else { Step::Quiet } @@ -605,7 +630,12 @@ #[inline] fn iteration(&self) -> usize { - return self.iter; + self.iter + } + + #[inline] + fn is_quiet(&self) -> bool { + self.quiet } } @@ -636,17 +666,13 @@ for StallIteratorFactory<U, BaseFactory> where BaseFactory : AlgIteratorFactory<V, Output=U>, U : SignedNum + PartialOrd { - type Iter<F, E> = StallIterator<U, BaseFactory::Iter<F, E>> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = StallIterator<U, BaseFactory::Iter>; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { StallIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), stall : self.stall, previous_value : None, } @@ -662,18 +688,24 @@ where BaseIterator : AlgIterator<Output=U>, U : SignedNum + PartialOrd { type State = BaseIterator::State; - type Output = BaseIterator::Output; - type Err = BaseIterator::Err; + type Output = U; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step<U, Self::Err> { - match self.base_iterator.step() { - Step::Result(nv) => { + fn prestep(&mut self) -> Option<Self::State> { + self.base_iterator.prestep() + } + + #[inline] + fn poststep<E>(&mut self, res : Step<Self::Input, Self::State, E>) -> Step<U, Self::State, E> + where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(nv, state) => { let previous_v = self.previous_value; self.previous_value = Some(nv); match previous_v { Some(pv) if (nv - pv).abs() <= self.stall * pv.abs() => Step::Terminated, - _ => Step::Result(nv), + _ => Step::Result(nv, state), } }, val => val, @@ -711,17 +743,13 @@ for ValueIteratorFactory<U, BaseFactory> where BaseFactory : AlgIteratorFactory<V, Output=U>, U : SignedNum + PartialOrd { - type Iter<F, E> = ValueIterator<U, BaseFactory::Iter<F, E>> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = ValueIterator<U, BaseFactory::Iter>; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { ValueIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), target : self.target } } @@ -736,17 +764,22 @@ where BaseIterator : AlgIterator<Output=U>, U : SignedNum + PartialOrd { type State = BaseIterator::State; - type Output = BaseIterator::Output; - type Err = BaseIterator::Err; + type Output = U; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step<U, Self::Err> { - match self.base_iterator.step() { - Step::Result(v) => { + fn prestep(&mut self) -> Option<Self::State> { + self.base_iterator.prestep() + } + + #[inline] + fn poststep<E>(&mut self, res : Step<Self::Input, Self::State, E>) -> Step<U, Self::State, E> where E : Error{ + match self.base_iterator.poststep(res) { + Step::Result(v, state) => { if v <= self.target { Step::Terminated } else { - Step::Result(v) + Step::Result(v, state) } }, val => val, @@ -792,16 +825,12 @@ where BaseFactory : AlgIteratorFactory<V>, BaseFactory::Output : 'log { type State = BaseFactory::State; - type Iter<F, E> = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter<F, E>> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter>; type Output = (); - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { LoggingIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), logger : self.logger, } } @@ -818,12 +847,17 @@ BaseIterator::Output : 'log { type State = BaseIterator::State; type Output = (); - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step<Self::Output, Self::Err> { - match self.base_iterator.step() { - Step::Result(v) => { + fn prestep(&mut self) -> Option<Self::State> { + self.base_iterator.prestep() + } + + #[inline] + fn poststep<E>(&mut self, res : Step<Self::Input, Self::State, E>) -> Step<(), Self::State, E> where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(v, _) => { self.logger.log(v); Step::Quiet }, @@ -845,7 +879,7 @@ } /// This [`AlgIteratorFactory`] allows output mapping. -/// +/// /// Typically produced with [`AlgIteratorFactory::mapped`]. #[derive(Debug)] pub struct MappingIteratorFactory<G, BaseFactory> { @@ -868,16 +902,12 @@ where BaseFactory : AlgIteratorFactory<V>, G : Fn(usize, BaseFactory::Output) -> U { type State = BaseFactory::State; - type Iter<F, E> = MappingIterator<G, BaseFactory::Iter<F, E>> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = MappingIterator<G, BaseFactory::Iter>; type Output = U; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { MappingIterator { - base_iterator : self.base_options.prepare(step_fn), + base_iterator : self.base_options.prepare(), map : self.map } } @@ -894,12 +924,17 @@ G : Fn(usize, BaseIterator::Output) -> U { type State = BaseIterator::State; type Output = U; - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; #[inline] - fn step(&mut self) -> Step<Self::Output, Self::Err> { - match self.base_iterator.step() { - Step::Result(v) => Step::Result((self.map)(self.iteration(), v)), + fn prestep(&mut self) -> Option<Self::State> { + self.base_iterator.prestep() + } + + #[inline] + fn poststep<E>(&mut self, res : Step<Self::Input, Self::State, E>) -> Step<Self::Output, Self::State, E> where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(v, state) => Step::Result((self.map)(self.iteration(), v), state), Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, Step::Failure(e) => Step::Failure(e), @@ -935,7 +970,11 @@ /// Data `U` with production time attached #[derive(Copy, Clone, Debug, Serialize)] pub struct Timed<U> { + /// CPU time taken pub cpu_time : Duration, + /// Iteration number + pub iter : usize, + /// User data //#[serde(flatten)] pub data : U } @@ -951,16 +990,12 @@ for TimingIteratorFactory<BaseFactory> where BaseFactory : AlgIteratorFactory<V> { type State = BaseFactory::State; - type Iter<F, E> = TimingIterator<BaseFactory::Iter<F, E>> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error; + type Iter = TimingIterator<BaseFactory::Iter>; type Output = Timed<BaseFactory::Output>; - fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E> - where F : FnMut(&Self::State) -> Step<V, E>, - E : Error { + fn prepare(self) -> Self::Iter { TimingIterator { - base_iterator : self.0.prepare(step_fn), + base_iterator : self.0.prepare(), start_time : ProcessTime::now() } } @@ -976,16 +1011,22 @@ where BaseIterator : AlgIterator { type State = BaseIterator::State; type Output = Timed<BaseIterator::Output>; - type Err = BaseIterator::Err; + type Input = BaseIterator::Input; + + #[inline] + fn prestep(&mut self) -> Option<Self::State> { + self.base_iterator.prestep() + } #[inline] - fn step(&mut self) -> Step<Self::Output, Self::Err> { - match self.base_iterator.step() { - Step::Result(data) => { + fn poststep<E>(&mut self, res : Step<Self::Input, Self::State, E>) -> Step<Self::Output, Self::State, E> where E : Error { + match self.base_iterator.poststep(res) { + Step::Result(data, state) => { Step::Result(Timed{ cpu_time : self.start_time.elapsed(), + iter : self.iteration(), data - }) + }, state) }, Step::Quiet => Step::Quiet, Step::Terminated => Step::Terminated, @@ -1005,6 +1046,82 @@ } // +// New for-loop interface +// + +pub struct AlgIteratorIterator<I : AlgIterator> { + algi : Rc<RefCell<I>>, +} + +pub struct AlgIteratorIteration<I : AlgIterator> { + state : I::State, + algi : Rc<RefCell<I>>, +} + +impl<I : AlgIterator> std::iter::Iterator for AlgIteratorIterator<I> { + type Item = AlgIteratorIteration<I>; + + fn next(&mut self) -> Option<Self::Item> { + let algi = self.algi.clone(); + RefCell::borrow_mut(&self.algi).prestep().map(|state| AlgIteratorIteration { + state, + algi, + }) + } +} + +/// Types of errors that may occur +#[derive(Debug,PartialEq,Eq)] +pub enum IterationError { + /// [`AlgIteratorIteration::if_verbose_check`] is not called in iteration order. + ReportingOrderingError +} + +impl<I : AlgIterator> AlgIteratorIteration<I> { + /// Call `call_objective` if this is a verbose iteration. + /// + /// Verbosity depends on the [`AlgIterator`] that produced this state. + /// + /// The closure `calc_objective` should return an arbitrary value of type `V`, to be inserted + /// into the log, or whatever is deemed by the [`AlgIterator`]. For usage instructions see the + /// [module documentation][self]. + /// + /// This function may panic if result reporting is not ordered correctly (an unlikely mistake + /// if using this facility correctly). For a version that propagates errors, see + /// [`Self::if_verbose_check`]. + pub fn if_verbose(self, calc_objective : impl FnOnce() -> I::Input) { + self.if_verbose_check(calc_objective).unwrap() + } + + /// Version of [`Self::if_verbose`] that propagates errors instead of panicking. + pub fn if_verbose_check(self, calc_objective : impl FnOnce() -> I::Input) + -> Result<(), IterationError> { + let mut algi = match RefCell::try_borrow_mut(&self.algi) { + Err(_) => return Err(IterationError::ReportingOrderingError), + Ok(algi) => algi + }; + if self.state.iteration() != algi.iteration() { + Err(IterationError::ReportingOrderingError) + } else { + let res : Step<I::Input, I::State, std::convert::Infallible> + = self.state.if_verbose(calc_objective); + algi.poststep(res); + Ok(()) + } + } + + /// Returns the current iteration count. + pub fn iteration(&self) -> usize { + self.state.iteration() + } + + /// Indicates whether the iterator is quiet + pub fn is_quiet(&self) -> bool { + self.state.is_quiet() + } +} + +// // Tests // @@ -1050,4 +1167,44 @@ .collect::<Vec<int>>()) } } + + #[test] + fn iteration_for_loop() { + let options = AlgIteratorOptions{ + max_iter : 10, + verbose_iter : Verbose::Every(3), + .. Default::default() + }; + + { + let mut start = 1 as int; + for state in options.iter() { + start = start * 2; + state.if_verbose(|| start) + } + assert_eq!(start, (2 as int).pow(10)); + } + + { + let mut start = 1 as int; + let mut log = Logger::new(); + let factory = options.instantiate() + .with_iteration_number() + .into_log(&mut log); + for state in factory.iter() { + start = start * 2; + state.if_verbose(|| start) + } + assert_eq!(start, (2 as int).pow(10)); + assert_eq!(log.data() + .iter() + .map(|LogItem{ data : v, iter : _ }| v.clone()) + .collect::<Vec<int>>(), + (1..10).map(|i| (2 as int).pow(i)) + .skip(2) + .step_by(3) + .collect::<Vec<int>>()) + } + } + }