iterators-must-go

66
c 2009 Andrei Alexandrescu 1 / 52 Iterators Must Go Andrei Alexandrescu

Transcript of iterators-must-go

Page 1: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 1/66

c 2009 Andrei Alexandrescu 1 / 52

Iterators Must Go

Andrei Alexandrescu

Page 2: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 2/66

This Talk

c 2009 Andrei Alexandrescu 2 / 52

• The STL

• Iterators

• Range-based design• Conclusions

Page 3: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 3/66

What is the STL?

c 2009 Andrei Alexandrescu 3 / 52

Page 4: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 4/66

Yeah, what is  the STL?

c 2009 Andrei Alexandrescu 4 / 52

• A good library of algorithms and data structures.

Page 5: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 5/66

Yeah, what is  the STL?

c 2009 Andrei Alexandrescu 4 / 52

• A ( good|bad) library of algorithms and data structures.

Page 6: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 6/66

Yeah, what is  the STL?

c 2009 Andrei Alexandrescu 4 / 52

• A ( good|bad|ugly) library of algorithms and data

structures.

Page 7: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 7/66

Yeah, what is  the STL?

c 2009 Andrei Alexandrescu 4 / 52

• A ( good|bad|ugly) library of algorithms and data

structures.

• iterators = gcd(containers, algorithms);

Page 8: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 8/66

Yeah, what is  the STL?

c 2009 Andrei Alexandrescu 4 / 52

• A ( good|bad|ugly) library of algorithms and data

structures.

• iterators = gcd(containers, algorithms);

• Scrumptious Template Lore

• Snout to Tail Length

Page 9: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 9/66

What the STL is

c 2009 Andrei Alexandrescu 5 / 52

• More than the answer, the question  is important in the

STL

• “What would the most general implementations offundamental containers and algorithms look like?”

• Everything else is aftermath

• Most importantly: STL is one  answer, not the  answer

Page 10: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 10/66

STL is nonintuitive

c 2009 Andrei Alexandrescu 6 / 52

Page 11: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 11/66

STL is nonintuitive

c 2009 Andrei Alexandrescu 6 / 52

• Same way the theory of relativity is nonintuitive

• Same way complex numbers are nonintuitive (see

e.g. xkcd.com)

Page 12: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 12/66

Nonintuitive

c 2009 Andrei Alexandrescu 7 / 52

• “I want to design the most general algorithms.”

• “Sure. What you obviously need is something called

iterators. Five of ’em, to be precise.”

Page 13: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 13/66

Page 14: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 14/66

Fundamental vs. Incidental in STL

c 2009 Andrei Alexandrescu 8 / 52

• Algorithms defined for the narrowest interface

possible

• Broad iterator categories as required by algorithms

• Choice of iterator primitives

• Syntax of iterator primitives

Page 15: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 15/66

Fundamental vs. Incidental in STL

c 2009 Andrei Alexandrescu 8 / 52

• F: Algorithms defined for the narrowest interface

possible

• Broad iterator categories as required by algorithms

• Choice of iterator primitives

• Syntax of iterator primitives

Page 16: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 16/66

Fundamental vs. Incidental in STL

c 2009 Andrei Alexandrescu 8 / 52

• F: Algorithms defined for the narrowest interface

possible

• F: Broad iterator categories as required by algorithms

• Choice of iterator primitives

• Syntax of iterator primitives

Page 17: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 17/66

Fundamental vs. Incidental in STL

c 2009 Andrei Alexandrescu 8 / 52

• F: Algorithms defined for the narrowest interface

possible

• F: Broad iterator categories as required by algorithms

• I: Choice of iterator primitives

• Syntax of iterator primitives

Page 18: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 18/66

Fundamental vs. Incidental in STL

c 2009 Andrei Alexandrescu 8 / 52

• F: Algorithms defined for the narrowest interface

possible

• F: Broad iterator categories as required by algorithms

• I: Choice of iterator primitives

• I: Syntax of iterator primitives

Page 19: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 19/66

STL: The Good

c 2009 Andrei Alexandrescu 9 / 52

• Asked the right question

• General

• Efficient

• Reasonably extensible

• Integrated with built-in types

Page 20: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 20/66

STL: The Bad

c 2009 Andrei Alexandrescu 10 / 52

• Poor lambda functions support

Not an STL problem

High opportunity cost

• Some containers cannot be supported

E.g. sentinel-terminated containers

E.g. containers with distributed storage

• Some iteration methods cannot be supported

Page 21: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 21/66

STL: The Ugly

c 2009 Andrei Alexandrescu 11 / 52

• Attempts at for_each et al. didn’t help

• Integration with streams is tenuous

• One word: allocator

Page 22: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 22/66

STL: The Ugly

c 2009 Andrei Alexandrescu 11 / 52

• Attempts at for_each et al. didn’t help

• Integration with streams is tenuous

• One word: allocator

• Iterators suck

◦ Verbose

◦ Unsafe

◦ Poor Interface

Page 23: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 23/66

What’s the Deal with Iterators?

c 2009 Andrei Alexandrescu 12 / 52

Page 24: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 24/66

Iterators Rock

c 2009 Andrei Alexandrescu 13 / 52

• They broker interaction between containers and

algorithms

• “Strength reduction:” m+ n implementations instead ofm · n

• Extensible: there’s been a flurry of iterators ever sinceSTL saw the light of day

Page 25: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 25/66

Warning Sign #1

c 2009 Andrei Alexandrescu 14 / 52

• C++ Users Journal around 2001 ran an ad campaign

“Submit an article to CUJ!”

“No need to be an English major! Just start youreditor!”

“We’re interested in security, networking, C++

techniques, and more!”

Page 26: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 26/66

Warning Sign #1

c 2009 Andrei Alexandrescu 14 / 52

• C++ Users Journal around 2001 ran an ad campaign

“Submit an article to CUJ!”

“No need to be an English major! Just start youreditor!”

“We’re interested in security, networking, C++

techniques, and more!”

“Please note: Not interested in yet another iterator”

• How many of those published iterators survived?

Page 27: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 27/66

Warning Sign #2

c 2009 Andrei Alexandrescu 15 / 52

File copy circa 1975:

#include <stdio.h>

int main() {

int c;while ((c = getchar()) != EOF)

putchar(c);

return errno != 0;}

Page 28: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 28/66

Warning Sign #2

c 2009 Andrei Alexandrescu 16 / 52

Fast forward 20 years, and. . .

#include <iostream>

#include <algorithm>

#include <iterator>

#include <string>

using namespace std;

int main() {

copy(istream_iterator<string>(cin),

istream_iterator<string>(),ostream_iterator<string>(cout,"\n"));

}

Page 29: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 29/66

c 2009 Andrei Alexandrescu 17 / 52

(forgot the try / catch around main)

Page 30: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 30/66

c 2009 Andrei Alexandrescu 18 / 52

Something, somewhere, went terribly wrong.

Page 31: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 31/66

Warning Sign #3

c 2009 Andrei Alexandrescu 19 / 52

• Iterators are brutally hard to define

• Bulky implementations and many gotchas

• Boost includes an entire library that helps defining

iterators

• The essential primitives are like. . . three?

◦ At end

◦ Access◦ Bump

Page 32: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 32/66

Warning Sign #4

c 2009 Andrei Alexandrescu 20 / 52

• Iterators use pointer syntax & semantics

• Integration with pointers for the win/loss

• However, this limits methods of iteration

◦ Can’t walk a tree in depth, need ++ with a

parameter

◦ Output iterators can only accept one type:

ostream_iterator must be parameterized witheach specific type to output, although they all go to

the same place

Page 33: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 33/66

Final nail in the coffin

c 2009 Andrei Alexandrescu 21 / 52

• All  iterator primitives are fundamentally unsafe

• For most iterator types, given an iterator

◦ Can’t say whether it can be compared

◦ Can’t say whether it can be incremented

◦ Can’t say whether it can be dereferenced

• Safe iterators can and have been written

◦ At a high size+speed cost

◦ Mostly a good argument that the design hasn’t

been cut quite right

Page 34: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 34/66

Ranges

c 2009 Andrei Alexandrescu 22 / 52

Page 35: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 35/66

Enter Ranges

c 2009 Andrei Alexandrescu 23 / 52

• To partly avoid these inconveniences, ranges have

been defined

• A range is a pair of begin/end iterators packed together

• As such, a range has higher-level checkable invariants

• Boost and Adobe libraries defined ranges

Page 36: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 36/66

c 2009 Andrei Alexandrescu 24 / 52

They made an interesting step in a good direction.

Page 37: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 37/66

c 2009 Andrei Alexandrescu 25 / 52

Things must be taken much further.

Page 38: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 38/66

Look, Ma, no iterators!

c 2009 Andrei Alexandrescu 26 / 52

• How about defining ranges instead of iterators as the

primitive structure for iteration?

• Ranges should define primitive operations that do notrely on iteration

• There would be no more iterators, only ranges

• What primitives should ranges support?

Remember, begin / end are not an optionIf people squirrel away individual iterators, we’re

back to square one

Page 39: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 39/66

Defining Ranges

c 2009 Andrei Alexandrescu 27 / 52

• All of <algorithm> should be implementable with

ranges, and other algorithms as well

• Range primitives should be checkable at low cost

• Yet, ranges should not be less efficient than iterators

Page 40: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 40/66

Input/Forward Ranges

c 2009 Andrei Alexandrescu 28 / 52

template<class T> struct InputRange {

bool empty() const;

void popFront();T& front() const;

};

Page 41: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 41/66

Verifiable?

c 2009 Andrei Alexandrescu 29 / 52

template<class T> struct ContigRange {

bool empty() const { return b > = e ; }

void popFront() {

assert(!empty());

++b;

}

T& front() const {

assert(!empty());

return *b;

}private:

T *b, *e;

};

Page 42: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 42/66

Find

c 2009 Andrei Alexandrescu 30 / 52

// Original version per STL

template<class It, class T>

It find(It b, It e, T value) {

for (; b != e; ++b)

if (value == *b) break;

return b;

}

...

auto i = find(v.begin(), v.end(), value);if (i != v.end()) ...

Page 43: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 43/66

Design Question

c 2009 Andrei Alexandrescu 31 / 52

• What should find with ranges look like?

1. Return a range of one element (if found) or zero

elements (if not)?

2. Return the range before  the found element?

3. Return the range after  the found element?

Page 44: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 44/66

Design Question

c 2009 Andrei Alexandrescu 31 / 52

• What should find with ranges look like?

1. Return a range of one element (if found) or zero

elements (if not)?

2. Return the range before  the found element?

3. Return the range after  the found element?

• Correct answer: return the range starting with the found 

element (if any), empty if not found 

Page 45: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 45/66

Design Question

c 2009 Andrei Alexandrescu 31 / 52

• What should find with ranges look like?

1. Return a range of one element (if found) or zero

elements (if not)?

2. Return the range before  the found element?

3. Return the range after  the found element?

• Correct answer: return the range starting with the found 

element (if any), empty if not found Why?

Page 46: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 46/66

Find

c 2009 Andrei Alexandrescu 32 / 52

// Using ranges

template<class R, class T>

R find(R r, T value) {

for (; !r.empty(); r.popFront())

if (value == r.front()) break;

return r;

}

...

auto r = find(v.all(), value);if (!r.empty()) ...

Page 47: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 47/66

Elegant Specification

c 2009 Andrei Alexandrescu 33 / 52

template<class R, class T>R find(R r, T value);

“Reduces the range r from left until its front is

equal with value or r is exhausted.”

Page 48: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 48/66

Bidirectional Ranges

c 2009 Andrei Alexandrescu 34 / 52

template<class T> struct BidirRange {bool empty() const;

void popFront();

void popBack();T& front() const;

T& back() const;

};

Page 49: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 49/66

Reverse Iteration

c 2009 Andrei Alexandrescu 35 / 52

template<class R> struct Retro {

bool empty() const { return r.empty(); }

void popFront() { return r.popBack(); }

void popBack() { return r.popFront(); }E<R>::Type& front() const { return r.back(); }

E<R>::Type& back() const { return r.front(); }

R r;

};template<class R> Retro<R> retro(R r) {

return Retro<R>(r);

}template<class R> R retro(Retro<R> r) {

return r.r; // klever 

}

Page 50: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 50/66

How about find_end?

c 2009 Andrei Alexandrescu 36 / 52

template<class R, class T>

R find_end(R r, T value) {return retro(find(retro(r));

}

• No more need for rbegin, rend

• Containers define all which returns a range

• To iterate backwards: retro(cont.all())

Page 51: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 51/66

find_end with iterators sucks

c 2009 Andrei Alexandrescu 37 / 52

// find_end using reverse_iterator 

template<class It, class T>

It find_end(It b, It e, T value) {

It r = find(reverse_iterator<It>(e),

reverse_iterator<It>(b), value).base();

return r = = b ? e : - - r ;

}

• Crushing advantage of ranges: much terser code

• Easy composition because only one object needs to be

composed, not two in sync

Page 52: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 52/66

More composition opportunities

c 2009 Andrei Alexandrescu 38 / 52

• Chain: chain several ranges together

Elements are not copied!

Category of range is the weakest  of all ranges

• Zip: span several ranges in lockstep

Needs Tuple

• Stride: span a range several steps at once

Iterators can’t implement it!

• Radial: span a range in increasing distance from its

middle (or any other point)

Page 53: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 53/66

How about three-iterators functions?

c 2009 Andrei Alexandrescu 39 / 52

template<class It1, It2>

void copy(It1 begin, It1 end, It2 to);

template<class It>

void partial_sort(It begin, It mid, It end);

template<class It>

void rotate(It begin, It mid, It end);template<class It, class Pr>

It partition(It begin, It end, Pr pred);

template<class It, class Pr>It inplace_merge(It begin, It mid, It end);

Page 54: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 54/66

c 2009 Andrei Alexandrescu 40 / 52

“Where there’s hardship, there’s opportunity.”

 – I. Meade Etop

Page 55: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 55/66

3-legged algos ⇒ mixed-range algos

c 2009 Andrei Alexandrescu 41 / 52

template<class R1, class R2>

R2 copy(R1 r1, R2 r2);

• Spec: Copy r1 to r2, returns untouched portion of r2

vector<float> v;

list<int> s;

deque<double> d;copy(chain(v, s), d);

3 l d l i d l

Page 56: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 56/66

3-legged algos ⇒ mixed-range algos

c 2009 Andrei Alexandrescu 42 / 52

template<class R1, class R2>

void partial_sort(R1 r1, R2 r2);

• Spec: Partially sort the concatenation of r1 and r2such that the smallest elements end up in r1

• You can take a vector and a deque and put the smallestelements of both  in the array!

vector<double> v;deque<double> d;

partial_sort(v, d);

B i h ’

Page 57: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 57/66

But wait, there’s more

c 2009 Andrei Alexandrescu 43 / 52

vector<double> v1, v2;

deque<double> d;partial_sort(v1, chain(v2, d));

sort(chain(v1, v2, d));

• Algorithms can now operate on any mixture of ranges

seamlessly without any extra effort

• Try that  with iterators!

B t it th ’

Page 58: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 58/66

But wait, there’s even more

c 2009 Andrei Alexandrescu 44 / 52

vector<double> vd;

vector<string> vs;// sort the two in lockstep

sort(zip(vs, vd));

• Range combinators allow myriads of new uses

• Possible in theory with iterators, but the syntax would

explode (again)

O t t

Page 59: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 59/66

Output ranges

c 2009 Andrei Alexandrescu 45 / 52

• Freedom from pointer syntax allows supporting different

types

struct OutRange {

typedef Typelist<int, double, string> Types;void put(int);

void put(double);

void put(string);}

B k t i tdi t td t

Page 60: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 60/66

Back to copying stdin to stdout

c 2009 Andrei Alexandrescu 46 / 52

#include <...>

int main() {

copy(istream_range<string>(cin),ostream_range(cout, "\n"));

}

• Finally, a step forward: a one-liner that fits on one line1

• ostream_range does not need to specify string

1slide limitations notwithstanding

I fi it

Page 61: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 61/66

Infinite ranges

c 2009 Andrei Alexandrescu 47 / 52

• Notion of infinity becomes interesting with ranges

• Generators, random numbers, series, . . . are infinite

ranges

• Infinity is a trait distinct from the five classic categories;

any kind of range may be infinite

• Even a random-access range may be infinite!

• Statically knowing about infinity helps algorithms

h i

Page 62: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 62/66

has_size

c 2009 Andrei Alexandrescu 48 / 52

• Whether a range has an efficiently computed size  is

another independent trait

• (Index entry: list.size, endless debate on)

• Even an input range can have a known size, e.g.

take(100, rndgen)which takes 100 random

numbers

◦ take(100, r) has length 100 if r is infinite

◦ length min(100, r.size()) if r has knownlength

◦ unknown length if r is finite with unknown length

A Twist

Page 63: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 63/66

A Twist

c 2009 Andrei Alexandrescu 49 / 52

• Can <algorithm> be redone with ranges?

• D’s stdlib offers a superset of <algorithm> in modules

std.algorithm and std.range (google for them)

• Ranges pervade D: algorithms, lazy evaluation, random

numbers, higher-order functions,foreachstatement. . .

• Some opportunities not yet tapped—e.g. filters

(input/output ranges)

• Check “The Case for D” in Doctor Dobb’s Journal,

coming soon

Conclusion

Page 64: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 64/66

Conclusion

c 2009 Andrei Alexandrescu 50 / 52

• Ranges are a superior abstraction

• Better checking abilities (not perfect still)

• Easy composition

• Range-based design offers much more than a port of

iterator-based functions to ranges

• Exciting development taking STL one step further

Announcement

Page 65: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 65/66

Announcement

c 2009 Andrei Alexandrescu 51 / 52

Please note: “The D Programming

Language” soon to appear on Safari’sRough Cuts.

Andrei Alexandrescu Ph %@! D

Page 66: iterators-must-go

8/7/2019 iterators-must-go

http://slidepdf.com/reader/full/iterators-must-go 66/66

Andrei Alexandrescu, Ph %@! D

Please note: Andrei will soon be offering

training and consulting services. Contacthim for details.