C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

standard vs. hand crafted loops
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Guest






PostPosted: Tue May 09, 2006 1:21 pm    Post subject: standard vs. hand crafted loops Reply with quote



I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

for example: count length of each string wrapped into another object
(MyData):

=======================================================
class MyData // some object
{
std::string m_s;
public:
MyData( const std::string &s ) : m_s(s) {}
size_t len() const { return m_s.length(); }
};

class legngth // helper for for_each
{
size_t cnt; // accumulate result
public:
legngth(){ cnt = 0;} // initialize

void operator ()( const MyData &d) { cnt += d.len(); }

operator size_t() { return cnt; } // return the result
};


int _tmain(int argc, _TCHAR* argv[])
{
std::vector<MyData> vec;
vec.push_back(MyData("a"));
vec.push_back(MyData("b"));
vec.push_back(MyData("c"));

//
// count length for all objects of "vec"
//
size_t l = 0;

// standard algorithm
l = std::for_each( vec.begin(), vec.end(), legngth() );

l = 0;
// hand crafted loop
for( std::vector<MyData>::iterator i=vec.begin(); i < vec.end();
++i)
l += i->len();

return 0;
}

=======================================================


Using for_each it takes 10 lines and auxiliary Function object. Compare
"for" loop it is only 3 lines. Such example looks as very common case,
it is typical to perform two or more operations on container. Create
function object looks as extra useless work.

Is there any better way to write such loops in general?

Thanks, Pavel


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Daniel T.
Guest





PostPosted: Wed May 10, 2006 12:21 am    Post subject: Re: standard vs. hand crafted loops Reply with quote



In article <1147114512.131883.144670 (AT) v46g2000cwv (DOT) googlegroups.com>,
pavel.turbin (AT) gmail (DOT) com wrote:

Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

for example: count length of each string wrapped into another object
(MyData):

=======================================================
class MyData // some object
{
std::string m_s;
public:
MyData( const std::string &s ) : m_s(s) {}
size_t len() const { return m_s.length(); }
};

class legngth // helper for for_each
{
size_t cnt; // accumulate result
public:
legngth(){ cnt = 0;} // initialize

void operator ()( const MyData &d) { cnt += d.len(); }

operator size_t() { return cnt; } // return the result
};


int _tmain(int argc, _TCHAR* argv[])
{
std::vector<MyData> vec;
vec.push_back(MyData("a"));
vec.push_back(MyData("b"));
vec.push_back(MyData("c"));

//
// count length for all objects of "vec"
//
size_t l = 0;

// standard algorithm
l = std::for_each( vec.begin(), vec.end(), legngth() );

l = 0;
// hand crafted loop
for( std::vector<MyData>::iterator i=vec.begin(); i < vec.end();
++i)
l += i->len();

return 0;
}

=======================================================


Using for_each it takes 10 lines and auxiliary Function object. Compare
"for" loop it is only 3 lines. Such example looks as very common case,
it is typical to perform two or more operations on container. Create
function object looks as extra useless work.

Is there any better way to write such loops in general?

First, accumulate would be a better choice than for_each:

class MyData {
public:
size_t len() const;
};

size_t add_length( size_t lhs, MyData rhs ) {
return lhs + rhs.len();
}

int main()
{
vector<MyData> vec;
// load up vec
size_t l = accumulate( vec.begin(), vec.end(), 0, &add_length );
}

Using the proper algorithm reduces the size (as in number of lines) to
the same as the for loop, but I suspect you still won't like it.

I suspect that you have no problem using strlen so I will assume that it
isn't the function call you have a problem with.

No, most people seem to have a problem with the functor. They don't mind
using library functions but only if they don't need to customize them at
all. So for example, 'find' and 'count' are used often but 'find_if' and
'count_if' are shunned... Use 'copy'? Sure, but 'transform'? No way.


What are some arguments for using the algorithm over the loop?

(a) A temporary variable can be removed from your mainline function.
With the loop, you need a temp to hold the accumulation, but using
accumulate or even for_each, that temp is naturally buried in a smaller
scope which reduces the chance of error.

(b) Cyclomatic Complexity can be reduced in your program. You end up
with functions that have fewer decisions made which reduces the chance
of bugs as well.

(c) With even moderate reuse, the total line count may be reduced. If
every loop body in your program is different, then the total line count
probably won't go down by using algorithms, but if even a few of them
are the same, you might see significant line count reduction.

(d) Algorithms often do a better job of communicating intent. When you
are finding, counting, accumulating, or transforming, why not make that
explicit in the code by using a standard algorithm. Readers of your code
will like not having to bury their heads into the body of a loop looking
for possible breaks or continues and wondering what the loop is for.

Of course, if you see *none* of these benefits at a particular place in
a particular program, then by all means use a loop instead.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Jerry Coffin
Guest





PostPosted: Wed May 10, 2006 1:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote



In article <1147114512.131883.144670
@v46g2000cwv.googlegroups.com>, pavel.turbin (AT) gmail (DOT) com
says...
Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

[ ... ]

#include <numeric>
#include <vector>
#include <algorithm>
#include <functional>


class MyData // some object
{
std::string m_s;
public:
MyData( const std::string &s ) : m_s(s) {}

// Definitely cheating here!
operator size_t() const { return m_s.length(); }

// used by the loop, but not the algorithm.
size_t len() const { return m_s.length(); }
};

int main(int argc, char* argv[])
{
std::vector<MyData> vec;
vec.push_back(MyData("a"));
vec.push_back(MyData("b"));
vec.push_back(MyData("c"));

// The short code using an algorithm:
size_t total;
std::accumulate(vec.begin(), vec.end(), total);

// hand-written loop is longer:
total = 0;
for( std::vector<MyData>::iterator i=vec.begin();
i < vec.end();++i)
total += i->len();

return 0;
}

Quote:
Using for_each it takes 10 lines and auxiliary Function object. Compare
"for" loop it is only 3 lines. Such example looks as very common case,
it is typical to perform two or more operations on container. Create
function object looks as extra useless work.

I rarely use std::for_each. In nearly every case,
something else works better. In this case I'll admit I
cheater -- the standard way would be to use mem_fun and
bind1st (or some such combination) to get accumulate to
call the memory function. Boost has a couple of
possibilities that should probably be handy as well.

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Carl Barron
Guest





PostPosted: Wed May 10, 2006 7:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

In article <1147114512.131883.144670 (AT) v46g2000cwv (DOT) googlegroups.com>,
<pavel.turbin (AT) gmail (DOT) com> wrote:

Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

for example: count length of each string wrapped into another object
(MyData):

I can't write anything more concise than the hand code loop using

just the standard library using std::accumulate which is more like what
you are doing it requires a binary functor,

This functor can be created inline with accumulate using either
boost::bind, or phoenix v1 below.

the binary functor can be created via boost[tr1] bind like this:

#include <iostream>
#include <ostream>
#include <boost/bind.hpp>
#include <vector>
#include <numeric>
#include <functional>

int main()
{
std::vector<std::string> data;
data.push_back("one");
data.push_back("four");
typedef std::string::size_type size_type;
size_type total = std::accumulate
(
data.begin(),
data.end(),
size_type(0),
boost::bind
(
std::plus<double>(),
_1,
boost::bind
(
&std::string::size,
_2
)
)
);
std::cout << "7 == "<< total << '\n';
}

or using phoenix 1.x in boost spirit library but it is independent of
spirit the functor can be created as:

#include <iostream>
#include <ostream>
#include <boost/spirit/phoenix.hpp>
#include <vector>
#include <numeric>

using namespace phoenix;

int main()
{
std::vector<std::string> data;
data.push_back("one");
data.push_back("four");
typedef std::string::size_type size_type;
size_type total = std::accumulate
(
data.begin(),
data.end(),
size_type(0),
arg1 + bind(&std::string::size)(arg2)
);
std::cout << "7 == "<< total << '\n';
}
the loop could be written as:
total = std::accumulate(data.begin(),data.end(),size_type(0),
arg1 + bind(&std::string::size)(arg2) );

which is almost as short and almost as readable as the hand coded loop.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Greg Herlihy
Guest





PostPosted: Wed May 10, 2006 7:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

pavel.turbin (AT) gmail (DOT) com wrote:
Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

for example: count length of each string wrapped into another object
(MyData):

=======================================================
class MyData // some object
{
std::string m_s;
public:
MyData( const std::string &s ) : m_s(s) {}
size_t len() const { return m_s.length(); }
};

class legngth // helper for for_each
{
size_t cnt; // accumulate result
public:
legngth(){ cnt = 0;} // initialize

void operator ()( const MyData &d) { cnt += d.len(); }

operator size_t() { return cnt; } // return the result
};


int _tmain(int argc, _TCHAR* argv[])
{
std::vector<MyData> vec;
vec.push_back(MyData("a"));
vec.push_back(MyData("b"));
vec.push_back(MyData("c"));

//
// count length for all objects of "vec"
//
size_t l = 0;

// standard algorithm
l = std::for_each( vec.begin(), vec.end(), legngth() );

l = 0;
// hand crafted loop
for( std::vector<MyData>::iterator i=vec.begin(); i < vec.end();
++i)
l += i->len();

return 0;
}

=======================================================


Using for_each it takes 10 lines and auxiliary Function object.
Compare
"for" loop it is only 3 lines. Such example looks as very common case,
it is typical to perform two or more operations on container. Create
function object looks as extra useless work.

Is there any better way to write such loops in general?

It is possible to reduce the loop to a single line without having to
write any supporting code:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <tr1/functional>
#include <tr1/utility>

using std::plus;
using std::tr1::bind;
using std::tr1::placeholders::_1;
using std::tr1::placeholders::_2;

class MyData
{
std::string m_s;
public:
MyData( const std::string &s ) : m_s(s) {}
size_t len() const { return m_s.length(); }
};

int main()
{
std::vector<MyData> vec;

vec.push_back(MyData("a"));
vec.push_back(MyData("bc"));
vec.push_back(MyData("cde"));

int length = 0;

length = std::accumulate( vec.begin(), vec.end(), length,
bind( plus<int>(),
_1,
bind(&MyData::len, _2)));

std::cout << "length is " << length << "\n";
}

Program Output:
length is 6

And did I mention "concise"? No, I did not - and just as well because
unfortunately the accumulate loop is not particularly concise. Nor is
this code probably as clear as we would prefer it to be. And while
using lambda expressions is one approach to improve its readability, I
think built-in container support support for a generic, iterator
adapter would make for a more simple, more accessible solution:

// fictional implementation

int length = 0;

length = std::accumulate( vec.begin<&MyData::len>(),
vec.end<&MyData::len>(),
length); // not possible currently

In this hypothetical example a container's begin() and end() methods
would have function template overloads parameterized by member function
pointer of the contained class. The template versions of these method
would return an iterator adaptor such that dereferencing the iterator
returns - not the contained itemi itself - but the result of invoking
the member function upon the contained item.

In this way it would be possible to iterate over the "lengths" (or any
other accessible property) of each of the MyData items in the container
and to do so without resorting to baroque expressions. Actually, I
think this idea might be worthwhile proposal to enchance container
classes - depending of course on the necessary support for some kind of
iterator adaptor being present as well.

Greg


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Guest






PostPosted: Wed May 10, 2006 9:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

Daniel T. wrote:
Quote:
First, accumulate would be a better choice than for_each:

class MyData {
public:
size_t len() const;
};

size_t add_length( size_t lhs, MyData rhs ) {
return lhs + rhs.len();
}

int main()
{
vector<MyData> vec;
// load up vec
size_t l = accumulate( vec.begin(), vec.end(), 0, &add_length );
}

Using the proper algorithm reduces the size (as in number of lines) to
the same as the for loop, but I suspect you still won't like it.

By use of the bind-library from boost we can also get rid of add_length
and compose the function object at the call-site:

const size_t l = accumulate( vec.begin(), vec.end(), 0,
bind(plus<size_t>(), _1, bind(&MyData::len, _2)) );


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Bjorn Reese
Guest





PostPosted: Thu May 11, 2006 1:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

Daniel T. wrote:

Quote:
(d) Algorithms often do a better job of communicating intent. When you
are finding, counting, accumulating, or transforming, why not make that
explicit in the code by using a standard algorithm. Readers of your code
will like not having to bury their heads into the body of a loop looking
for possible breaks or continues and wondering what the loop is for.

I once did a study on how well programmers with different levels of
experience were at understanding for-loops, algorithms, and recursion.
I used stereotypical looping constructs such as finding, counting, and
replacing. None of the programmers had any recognizable difficulties in
understanding for-loops and algorithms. Only recursion caused problems.
So, for-loops are just fine.

--
mail1dotstofanetdotdk

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Vidar Hasfjord
Guest





PostPosted: Thu May 11, 2006 1:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

Quote:
I think built-in container support support for a generic, iterator
adapter would make for a more simple, more accessible solution:

I like the concept.

But the ideal here would be (although this may require *concepts*...
Smile:

size_t n = std::accumulate (vec, &MyData::len); // not possible
currently

In short, the std lib should make things simple. It is not enough to be
extensible.

Regards,
---
Vidar Hasfjord


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Daniel T.
Guest





PostPosted: Thu May 11, 2006 1:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

In article <1147246414.160487.63860 (AT) e56g2000cwe (DOT) googlegroups.com>,
adampetersen75 (AT) hotmail (DOT) com wrote:

Quote:
const size_t l = accumulate( vec.begin(), vec.end(), 0,
bind(plus<size_t>(), _1, bind(&MyData::len, _2)) );

Yes and the same thing could have been done if I introduced Nicolai
Josuttis' compose functions, but the OP wanted less code, not more.

Maybe I'm old school, but there is nothing I like better than a well
named function. I mean compare your code above with:

size_t total_length = accumulate( vec.begin(), vec.end(), 0,
&by_adding_lengths );

Yea, "accumulate... by_adding_lengths" you can't get much more readable
than that without putting the line in its own function. :-)

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Maxim Yegorushkin
Guest





PostPosted: Thu May 11, 2006 1:21 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

pavel.turbin (AT) gmail (DOT) com wrote:
Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

It has been discussed in great detail here:
http://groups.google.com/group/comp.lang.c++.moderated/msg/c18f73fb59799e30


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Thomas Richter
Guest





PostPosted: Thu May 11, 2006 11:25 pm    Post subject: Re: standard vs. hand crafted loops Reply with quote

pavel.turbin (AT) gmail (DOT) com wrote:
Quote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting larger
without any benefits for me.

This is not a new problem. My major concern on the STL algorithms is
that the job they perform is so trivial that you can really write
them up yourself, moreover you'd have to write more code for the
same thing and it doesn't read much simpler either. For more
complicated examples, you'd had to write your own algorithm right
away anyhow, so the benefit of STL algorithms is IMHO quite small.

However, the problem isn't unidentified. To a major degree this
failure is due to the lack of lambda expressions in C++, i.e. you
cannot write up an anonymous function as an argument to the STL
algorithm. As far as I know, there's an open proposal for C++0x to
fix exactly that, and I would believe that the standard algorithms
become much more useful if you don't have to write a custom functor
for even the most trivial algorithms.

Quote:
Using for_each it takes 10 lines and auxiliary Function object. Compare
"for" loop it is only 3 lines. Such example looks as very common case,
it is typical to perform two or more operations on container. Create
function object looks as extra useless work.

Is there any better way to write such loops in general?

Well, right now I would write this loop out explicitly. It is IMHO
much more readable than the STL version.

So long,
Thomas

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Carl Barron
Guest





PostPosted: Fri May 12, 2006 12:21 am    Post subject: Re: standard vs. hand crafted loops Reply with quote

In article <1147292097.993574.73540 (AT) v46g2000cwv (DOT) googlegroups.com>,
Vidar Hasfjord <vattilah-groups (AT) yahoo (DOT) co.uk> wrote:

Quote:
I think built-in container support support for a generic, iterator
adapter would make for a more simple, more accessible solution:

I like the concept.

But the ideal here would be (although this may require *concepts*...
Smile:

size_t n = std::accumulate (vec, &MyData::len); // not possible
currently

If you don't like the use of expression libs to perform the

composition then boost::transform_iteraotr can create a sequence using
std::mem_fun_ref the simpler accumulate using operator +. It takes
more
typing but

template <class Out,class Cont,class R,class T>
Out do_accumulate(Cont const &c, R (T::*f)())
{
return std::accumulate
(
boost::make_transform_iterator(c.begin(),std::mem_fun_ref(f)),
boost::make_transform_iterator(c.end(),std::mem_fun_ref(f),
Out(0)
);
}
could be written and used like
n = do_accumulate<size_t>(vec,&MyData::len);
It does not need to be added to a standard library, I don't want a
java sized standard library!!

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Robert Mabee
Guest





PostPosted: Fri May 12, 2006 12:21 am    Post subject: Re: standard vs. hand crafted loops Reply with quote

Bjorn Reese wrote:
Quote:
I once did a study on how well programmers with different levels of
experience were at understanding for-loops, algorithms, and recursion.
I used stereotypical looping constructs such as finding, counting, and
replacing. None of the programmers had any recognizable difficulties in
understanding for-loops and algorithms. Only recursion caused problems.
So, for-loops are just fine.

After many years of hunting bugs in code written by professionals

(including me) I hold the opposite view of for loops. The problem
seems to be that the highly general for loop of C can hide small
deviations from the usual idiom that result in slightly broken code.
For example, the conditional expression might be miscoded as
i <= Nelem
or i < lastValid
This hides well because the for loop is verbose but not (usually)
worth inspecting closely, so we see the expected pattern even when
it isn't there.

If I were setting standards for a large outfit today, I'd require
use of for_each and standardized macros so the intent is clearly
stated, and remaining hand-coded loops would stand out as needing
careful reading.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Daniel T.
Guest





PostPosted: Fri May 12, 2006 12:21 am    Post subject: Re: standard vs. hand crafted loops Reply with quote

In article <446234a5$0$11673$ba624c82 (AT) nntp02 (DOT) dk.telia.net>,
Bjorn Reese <breese (AT) see (DOT) signature> wrote:

Quote:
Daniel T. wrote:

(d) Algorithms often do a better job of communicating intent. When you
are finding, counting, accumulating, or transforming, why not make that
explicit in the code by using a standard algorithm. Readers of your code
will like not having to bury their heads into the body of a loop looking
for possible breaks or continues and wondering what the loop is for.

I once did a study on how well programmers with different levels of
experience were at understanding for-loops, algorithms, and recursion.
I used stereotypical looping constructs such as finding, counting, and
replacing. None of the programmers had any recognizable difficulties in
understanding for-loops and algorithms. Only recursion caused problems.
So, for-loops are just fine.

Note, I didn't claim that standard algorithms are *always* easer to
read. Obviously, if you find that in some special case the loop is as
easy to read *and* you are deriving no other benefits from the use of an
algorithm, then it really doesn't matter which you use. I'd say that is
a very rare case.

Now, I realize that you were not arguing against the use of standard
algorithms in general but were merely saying that one of the several
reasons for using them isn't very powerful on its own. I will be happy
to accept that.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Daniel T.
Guest





PostPosted: Fri May 12, 2006 4:21 am    Post subject: Re: standard vs. hand crafted loops Reply with quote

In article <4cgdq7F15t510U1 (AT) news (DOT) dfncis.de>,
Thomas Richter <thor (AT) math (DOT) TU-Berlin.DE> wrote:

Quote:
pavel.turbin (AT) gmail (DOT) com wrote:
I've seen many times advise to use standard loops e.g. for_each,
instead of iterator and for loop. I was trying to follow this hint.
But, it looks it involves extra complexity and the code getting
larger
without any benefits for me.

This is not a new problem. My major concern on the STL algorithms is
that the job they perform is so trivial that you can really write
them up yourself,

Do you also argue against using strlen and strcpy because, "the job they
perform is so trivial that you can really write them up yourself"?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.