Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2007-07-23 04:16:35


Tom Brinkman wrote:
> I've modified the "simple_moving_average_functor" according to your
> suggestions of using the boost::circular_buffer container. (see
> below)
>
> I'm still unclear about how to incorporate the offsets, "start" and
> "stop". It might be easier if you illustrate this by modifying the
> function "operator()". I'm not shur how to proceed.

Hey, no fair! You volunteered! OK, see below. :-)

> Also, what should I do with the "finalize()" function. Its required
> that I implement this part of the interface

Actually, it's not. Like I said, range_run_storage::for_each() requires
TernaryFunction, and finalize() is not part of that concept.

, but its not needed to
> calculate the moving_average.

Actually, it is. Because after the last run, the rolling average will
peter out, and this petering will need to be calculated after for_each()
has returned. That's why so many of time_series' function objects have
finalize() methods---to do any clean-up and calculate any petering out.

   Yet, it requires me to return a value.
> Which value should I return?

When I've needed a finalizer, I've found it to be handy if it returns
the internally stored output inserter.

Anyway, I've attached my first crack at this problem. It's a little
tricky, and it's not 100% there yet, but it should get you going in the
right direction.

HTH,

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com
The Astoria Seminar ==> http://www.astoriaseminar.com

///////////////////////////////////////////////////////////////////////////////
// Copyright 2006 Eric Niebler. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_CB_TEST // BUGBUG why is this needed? Bug in circular_bummer (sic).

#include <iostream>
#include <boost/time_series/ordered_inserter.hpp>
#include <boost/time_series/piecewise_constant_series.hpp>
#include <boost/range_run_storage/algorithm/for_each.hpp>
#include <boost/circular_buffer.hpp>

using namespace boost;
namespace ts = time_series;
namespace rrs = range_run_storage;
namespace seq = sequence;

template<typename Out, typename Series>
struct simple_moving_average_functor
{
    typedef ts::concepts::TimeSeries<Series> series_concept;
    typedef typename series_concept::value_type value_type;
    typedef typename series_concept::offset_type offset_type;
    typedef typename numeric::functional::average<value_type, value_type>::result_type average_type;

    simple_moving_average_functor(Out const &out, Series const &series, std::size_t periods)
      : out_(out)
      , zero_(implicit_cast<value_type const &>(rrs::zero(series)))
      , total_(0)
      , offset_(-ts::inf)
      , buffer_(periods)
    {
        BOOST_ASSERT(periods > 0);
    }

    simple_moving_average_functor(simple_moving_average_functor const &that)
      : out_(that.out_)
      , zero_(that.zero_)
      , total_(that.total_)
      , offset_(that.offset_)
      , buffer_(that.buffer_)
    {
        this->buffer_.set_capacity(that.buffer_.capacity());
    }

    void operator ()(value_type const &value, offset_type start, offset_type stop)
    {
        if(static_cast<std::size_t>(start - this->offset_) > this->buffer_.capacity())
        {
            for(std::size_t i = 0; i < this->buffer_.capacity(); ++i)
            {
                this->push_back_(this->zero_);
            }
            this->offset_ = start;
        }
        else
        {
            while(this->offset_ < start)
            {
                this->push_back_(this->zero_);
            }
        }

        if(static_cast<std::size_t>(stop - start) > this->buffer_.capacity())
        {
            for(std::size_t i = 0; i < this->buffer_.capacity(); ++i)
            {
                this->push_back_(value);
            }
            this->out_(value, this->offset_, stop);
            this->offset_ = stop;
        }
        else
        {
            while(this->offset_ < stop)
            {
                this->push_back_(value);
            }
        }
    }

    Out const &finalize()
    {
        if(this->offset_ != ts::inf)
        {
            for(std::size_t i = 0; i < this->buffer_.capacity(); ++i)
            {
                this->push_back_(this->zero_);
            }
        }

        return this->out_;
    }

private:
    void push_back_(value_type const &value)
    {
        if(this->buffer_.full())
        {
            this->total_ -= this->buffer_[0];
            this->total_ += value;
            this->out_(numeric::average(this->total_, this->buffer_.capacity()), this->offset_);
        }
        else
        {
            this->total_ += value;
        }

        this->buffer_.push_back(value);
        ++this->offset_;
    }

    Out out_;
    value_type zero_;
    value_type total_;
    offset_type offset_;
    circular_buffer<average_type> buffer_;
};

int main()
{
    ts::piecewise_constant_series<int> pwc1;
    ts::make_ordered_inserter(pwc1)
        (-42, -ts::inf, -56)
        (-10, -10, 0)
        (10, 1, 10)
        (42, 56, ts::inf)
    .commit();
   
    ts::piecewise_constant_series<double> pwc2;
    ts::ordered_inserter<ts::piecewise_constant_series<double> > out(pwc2);

    simple_moving_average_functor<
        ts::ordered_inserter<ts::piecewise_constant_series<double> >
      , ts::piecewise_constant_series<int>
> fun(out, pwc1, 2); // window size == 2

    rrs::for_each(pwc1, fun).finalize();

    out.commit();

    std::cout << pwc1 << std::endl;
    std::cout << pwc2 << std::endl;

    return 0;
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk