Edge Interface For Forward Iterators

#### Contents

## Introduction

We can define the concept of the forest iterator in terms of a general graph using the terminology from Boost graph - this is useful because at some point we may wish to generalize these constructs to apply to other graph structures.

A forest is (loosely) a Bidirectional Graph.

A forest iterator consists of the following:

`v`

, a vertex descriptor (in the forest implementation this is the pointer to the node).`e`

, an`in_edge`

index - this specifies how we are pointing at the node.`p`

, a property (this is fixed to a single property of the`value_type`

of the iterator).`f`

, a function to move from an`in_edge`

to a corresponding`out_edge`

(for a fullorder iterator on a forest this is the identity function).

An edge is a directed link (with a `from()`

and `to()`

function) - and because it is bidirectional you can find the corresponding input link.

The increment operation can be defined as follows in pseudo code (once I’m more fluent in Boost Graph this could be described more concretely):

current_edge = out_edge(*v, e); v = to(curent_edge); e = f(find_in_edge(*v, current_edge));

In this way we can think of a forest iterator as being a traversal defined over a Bidirectional Graph. The `edge()`

interface on the iterator allows you to manipulate what `in_edge`

is being used to reach the vertex, and correspondingly what `out_edge`

will be followed next. The type of the result is the index for the edge sequence. In the case of forest this is an array, so `std::size_t`

is the correct type, even though it will only be a value of 0 or 1.

The signature for `edge()`

is:

std::size_t edge() const; // read access to the edge std::size_t& edge(); // write access to the edge

## Child iterators

The same description holds for `child_iterator`

except `f()`

is replaced with `trailing_of(*v)`

and it no longer depends on which `in_edge`

you arrive on.

## The reverse iterator problem with edge()

The standard notion of a reverse iterator transforms a range `[first, last)`

to the range `(first, last]`

without introducing any extra positions by having the base iterator point one after the element. Reverse iterators for a strict sequence work fine with a forest, but a reverse iterator which supports `edge()`

is trickier.

The next (or prior) edge is dependent on where you came from and it is a function of a vertex. So to read the edge when the iterator is after the current mark you need to `decrement`

- but this means you are reading a vertex which is *before* your offset range - this is because the edge is defined even on an end iterator (the vertex exists even if the property doesn’t).

This violates our range just to read an `edge()`

- to set an edge you defer the actual assignment of the edge until you reach the node - then set the edge to determine where to go next - so incrementing a reverse iterator looks like:

- Decrement the base iterator
- Set the edge to our next edge
- remember what the next edge should be

We run into a big problem if we set the edge and then decrement the reverse iterator - setting the edge may have pivoted on the previous node and we should land on a node which isn’t reachable in one step from the current base!

The algorithm for decrementing the reverse iterator is

- Decrement the base iterator
- Set the edge to our next edge
- increment the base iterator ‘‘twice’’
- remember what the next edge should be

This all works on a forest, despite the range violations, because a forest is implemented as a loop structure and the prior vertex of any node is guaranteed to exist (that is, we have a `root()`

as well as an `end()`

and `--begin()`

will yield `root()`

).

A simpler form of a reverse iterator relies on the fact that the forest ranges are symmetrical. In that case `rend()`

could return `root()`

and we would scrap the whole off by one. This does not change the requirements of our reverse iterator at all, but for anyone familiar with STL reverse iterators it does change the behavior of `base()`

.

### Final Resolution

The simpler form mentioned above was implemented and I’ve decided the simplifications are well worth it. The off-by-one issue of `base()`

is resolved by simply having `base()`

return `next(base_m)`

. The requirement imposed by the reverse iterator is actually the same requirement imposed by `set_next()`

. For any valid range `[first, last)`

there must exist a valid range `(prior, last)`

such that next(prior) == first.