Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) StateObserver

 

STLQt CreatorLubuntu

 

StateObserver is a class for state observer.

Technical facts

 

 

 

 

 

 

./CppStateObserver/CppStateObserver.pri

 

INCLUDEPATH += \
    ../../Classes/CppStateObserver

SOURCES += \
    ../../Classes/CppStateObserver/alphabetafilter.cpp \
    ../../Classes/CppStateObserver/alphabetagammafilter.cpp \
    ../../Classes/CppStateObserver/alphafilter.cpp \
    ../../Classes/CppStateObserver/floatingpointstateobserver.cpp \
    ../../Classes/CppStateObserver/integeralphafilter.cpp \
    ../../Classes/CppStateObserver/integerstateobserver.cpp \
    ../../Classes/CppStateObserver/integersymmetricalphafilter.cpp \
    ../../Classes/CppStateObserver/multialphafilter.cpp \
    ../../Classes/CppStateObserver/multiintegerstateobserver.cpp

HEADERS  += \
    ../../Classes/CppStateObserver/alphabetafilter.h \
    ../../Classes/CppStateObserver/alphabetagammafilter.h \
    ../../Classes/CppStateObserver/alphafilter.h \
    ../../Classes/CppStateObserver/floatingpointstateobserver.h \
    ../../Classes/CppStateObserver/integeralphafilter.h \
    ../../Classes/CppStateObserver/integerstateobserver.h \
    ../../Classes/CppStateObserver/integersymmetricalphafilter.h \
    ../../Classes/CppStateObserver/multialphafilter.h \
    ../../Classes/CppStateObserver/multiintegerstateobserver.h \
    ../../Classes/CppStateObserver/stateobserverfwd.h

OTHER_FILES += \
    ../../Classes/CppStateObserver/Licence.txt

 

 

 

 

 

./CppStateObserver/alphabetafilter.h

 

#ifndef ALPHABETAFILTER_H
#define ALPHABETAFILTER_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "floatingpointstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

///An alpha beta filter
struct AlphaBetaFilter : public FloatingPointStateObserver
{
  AlphaBetaFilter(
    const double alpha = 0.1 ,
    const double beta  = 0.01,
    const double dt    = 1.0
  );

  ///Get the current state estimate
  double GetEstimate() const { return m_output; }

  ///Supply a measurement, which will update the state estimate
  void Update(const double measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  const double m_alpha;
  const double m_beta;
  const double m_dt;

  double m_slope;
  double m_output;
};

} //~namespace ribi

#endif // ALPHABETAFILTER_H

 

 

 

 

 

./CppStateObserver/alphabetafilter.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "alphabetafilter.h"

#include <cassert>

#pragma GCC diagnostic pop

ribi::AlphaBetaFilter::AlphaBetaFilter(
  const double alpha,
  const double beta,
  const double dt)
  : m_alpha(alpha),
    m_beta(beta),
    m_dt(dt),
    m_slope(0.0),
    m_output(0.0)
{
  assert(m_alpha >= 0.0 && m_alpha <  1.0);
  assert(m_beta  >= 0.0 && m_beta  <= 2.0); //beta > 1.0 amplifies noise
  assert(4.0 - (2.0 * m_alpha - m_beta) > 0.0);
  assert(m_dt != 0.0);
}


void ribi::AlphaBetaFilter::Update(const double measurement)
{
  const double output_predicted = m_output + ( m_slope * m_dt );
  const double prediction_error = measurement - output_predicted;
  const double output_corrected = output_predicted + (m_alpha * prediction_error);
  m_output = output_corrected;
  m_slope += (m_beta * prediction_error / m_dt);
}

std::string ribi::AlphaBetaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::AlphaBetaFilter::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2013-05-25: version 1.0: initial version");
  v.push_back("2013-06-18: version 1.1: derive from FloatingPointStateObserver");
  return v;
}

 

 

 

 

 

./CppStateObserver/alphabetagammafilter.h

 

#ifndef ALPHABETAGAMMAFILTER_H
#define ALPHABETAGAMMAFILTER_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "floatingpointstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

///An alpha beta gamma filter
struct AlphaBetaGammaFilter : public FloatingPointStateObserver
{
  AlphaBetaGammaFilter(
    const double alpha = 0.1 ,
    const double beta  = 0.01,
    const double gamma = 0.001,
    const double dt    = 1.0
  );

  ///Get the current state estimate
  double GetEstimate() const { return m_position; }

  ///Supply a measurement, which will update the state estimate
  void Update(const double measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  double m_acceleration;
  const double m_alpha;
  const double m_beta;
  const double m_dt;
  const double m_gamma;
  double m_position;
  double m_velocity;
};

} //~namespace ribi

#endif // ALPHABETAGAMMAFILTER_H

 

 

 

 

 

./CppStateObserver/alphabetagammafilter.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "alphabetagammafilter.h"

#include <cassert>

#pragma GCC diagnostic pop

ribi::AlphaBetaGammaFilter::AlphaBetaGammaFilter(
  const double alpha,
  const double beta,
  const double gamma,
  const double dt)
  : m_acceleration(0.0),
    m_alpha(alpha),
    m_beta(beta),
    m_dt(dt),
    m_gamma(gamma),
    m_position(0.0),
    m_velocity(0.0)
{
  assert(m_alpha >= 0.0 && m_alpha <  1.0);
  assert(m_beta  >= 0.0 && m_beta  <= 2.0); //beta > 1.0 amplifies noise
  assert(4.0 - (2.0 * m_alpha - m_beta) > 0.0);
  assert(m_dt != 0.0);
}


void ribi::AlphaBetaGammaFilter::Update(const double measurement)
{
  const double output_predicted = m_position + ( m_velocity * m_dt );
  const double prediction_error = measurement - output_predicted;
  m_position = output_predicted + (m_alpha * prediction_error);
  m_velocity += (m_beta * prediction_error / m_dt);
  m_acceleration += (m_gamma * 2.0 * prediction_error / (m_dt * m_dt) );
}

std::string ribi::AlphaBetaGammaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::AlphaBetaGammaFilter::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2013-05-25: version 1.0: initial version");
  v.push_back("2013-06-18: version 1.1: derive from FloatingPointStateObserver");
  return v;
}

 

 

 

 

 

./CppStateObserver/alphafilter.h

 

#ifndef ALPHAFILTER_H
#define ALPHAFILTER_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "floatingpointstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

///An alpha filter as described on [1]
///An alpha filter is equivalant to a low-pass filter (also called high-cut filter, or treble cut filter) [2]
/// [1] http://en.wikipedia.org/wiki/Alpha_beta_filter#The_alpha_filter
/// [2] http://en.wikipedia.org/wiki/Low-pass_filter
struct AlphaFilter : public FloatingPointStateObserver
{
  AlphaFilter(
    const double alpha = 0.1,
    const double dt    = 1.0
  );

  ///Get the current state estimate
  double GetEstimate() const { return m_output; }

  ///Supply a measurement, which will update the state estimate
  void Update(const double measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  ///The response to noise measurements
  ///  * m_alpha = 1.0: follow measurements
  ///  * m_alpha = 0.9: low inertia: noise in measurements in decreased only slightly
  ///  * m_alpha = 0.1: high inertia: noise in measurements in decreased considerably
  ///  * m_alpha = 0.0: ignore measurements
  ///
  ///For a low-pass filter:
  ///
  ///  m_alpha = m_dt / (RC + m_dt)
  ///
  ///Where
  ///  * m_dt: time step (sec)
  ///  * R: resistance (ohm)
  ///  * C: capacitance (Farad)
  const double m_alpha;

  ///The time constant
  const double m_dt;

  ///The current estimate of the state observed
  double m_output;
};

///An alpha filter can easily be converted to the following state transition matrix:
///
///[x_new] = [x_current] * [1.0 - alpha] + [ alpha ] [ input ] + [ noise ]
///  #1          #2             #3            #4         #5         #6
///
///#1: The new state vector
///#2: The current state vector
///#3: The state transition matrix
///#4: The control matrix
///#5: The input vector
///#6: The process noise vector
///
///The alpha filter is supplied as an example in the tool KalmanFilterer

} //~namespace ribi

#endif // ALPHAFILTER_H

 

 

 

 

 

./CppStateObserver/alphafilter.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "alphafilter.h"

#include <cassert>
#pragma GCC diagnostic pop

ribi::AlphaFilter::AlphaFilter(
  const double alpha,
  const double dt)
  : m_alpha(alpha),
    m_dt(dt),
    m_output(0.0)
{
  //assert(m_alpha >= 0.0 && m_alpha <  1.0);
  //assert(m_beta  >= 0.0 && m_beta  <= 2.0); //beta > 1.0 amplifies noise
  //assert(4.0 - (2.0 * m_alpha - m_beta) > 0.0);
  assert(m_dt != 0.0);
}


void ribi::AlphaFilter::Update(const double measurement)
{
  const double difference = measurement - m_output;
  m_output += m_alpha * difference;
}

std::string ribi::AlphaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::AlphaFilter::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2013-05-25: version 1.0: initial version");
  v.push_back("2013-06-18: version 1.1: derive from FloatingPointStateObserver");
  return v;
}

 

 

 

 

 

./CppStateObserver/floatingpointstateobserver.h

 

#ifndef FLOATINGPOINTSTATEOBSERVER_H
#define FLOATINGPOINTSTATEOBSERVER_H

#include <string>
#include <vector>

namespace ribi {

///An observer for floating point values:
///The measurements it works must be floating point,
///The estimates it gives will be floating point
struct FloatingPointStateObserver
{
  ///ABC must have public virtual destructor
  // * Herb Sutter, Andrei Alexandrescu. C++ coding standards: 101 rules, guidelines, and best practices.
  //   ISBN: 0-32-111358-6. Item 50: 'Make base class destructors public and virtual, or protected and nonvirtual'
  virtual ~FloatingPointStateObserver() {}

  ///Get the current state estimate
  virtual double GetEstimate() const = 0;

  ///Supply a measurement, which will update the state estimate
  virtual void Update(const double measurement) = 0;

  ///Obtain the version of this class
  static std::string GetVersion();

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory();
};

} //~namespace ribi

#endif // FLOATINGPOINTSTATEOBSERVER_H

 

 

 

 

 

./CppStateObserver/floatingpointstateobserver.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "floatingpointstateobserver.h"
#pragma GCC diagnostic pop

std::string ribi::FloatingPointStateObserver::GetVersion()
{
  return "1.0";
}

std::vector<std::string> ribi::FloatingPointStateObserver::GetVersionHistory()
{
  std::vector<std::string> v;
  v.push_back("2013-06-18: version 1.0: initial version");
  return v;
}

 

 

 

 

 

./CppStateObserver/integeralphafilter.h

 

#ifndef INTEGERALPHAFILTER_H
#define INTEGERALPHAFILTER_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "integerstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

struct IntegerAlphaFilter : public IntegerStateObserver
{
  IntegerAlphaFilter(
    const int alpha,
    const int64_t value_active = 0);

  ///Get the current state estimate
  int64_t GetEstimate() const { return m_value_active; }

  ///Supply a measurement, which will update the state estimate
  void Update(const int64_t measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:

  ///The bitshift used for division
  int m_alpha;

  int64_t m_value_active;
};

} //~namespace ribi

#endif // INTEGERALPHAFILTER_H

 

 

 

 

 

./CppStateObserver/integeralphafilter.cpp

 

#include "integeralphafilter.h"

#include <cassert>
#include <cstdlib>

ribi::IntegerAlphaFilter::IntegerAlphaFilter(
  const int alpha,
  const int64_t value_active)
  : m_alpha(alpha),
    m_value_active(value_active)
{
  assert(m_alpha >=  0 && "A bitshift should not be done with negative values");
  assert(m_alpha <= 63 && "An int64_t can maximally be shifted 63 bits to the right");
}

void ribi::IntegerAlphaFilter::Update(const int64_t measurement)
{
  m_value_active += ((measurement - m_value_active) >> m_alpha);
}

std::string ribi::IntegerAlphaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::IntegerAlphaFilter::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2013-06-04: version 1.0: initial version");
  v.push_back("2013-06-18: version 1.1: refactoring of IntegerStateObserver");
  return v;
}

 

 

 

 

 

./CppStateObserver/integerstateobserver.h

 

#ifndef INTEGERSTATEOBSERVER_H
#define INTEGERSTATEOBSERVER_H

#include <cinttypes>
#include <string>
#include <vector>

namespace ribi {

///An observer for integer values:
///The measurements it works must be int,
///The estimates it gives will be int
struct IntegerStateObserver
{
  virtual ~IntegerStateObserver() noexcept {}

  ///Get the current state estimate
  virtual int64_t GetEstimate() const = 0;

  ///Supply a measurement, which will update the state estimate
  virtual void Update(const int64_t measurement) = 0;

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;
};

} //~namespace ribi

#endif // INTEGERSTATEOBSERVER_H

 

 

 

 

 

./CppStateObserver/integerstateobserver.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "integerstateobserver.h"

#include <boost/numeric/conversion/cast.hpp>
#pragma GCC diagnostic pop

std::string ribi::IntegerStateObserver::GetVersion() noexcept
{
  return "1.0";
}

std::vector<std::string> ribi::IntegerStateObserver::GetVersionHistory() noexcept
{
  return {
    "2013-06-04: version 1.0: initial version"
  };
}

 

 

 

 

 

./CppStateObserver/integersymmetricalphafilter.h

 

#ifndef INTEGERSYMMETRICALPHAFILTER_H
#define INTEGERSYMMETRICALPHAFILTER_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "integerstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

struct IntegerSymmetricalAlphaFilter : public IntegerStateObserver
{
  IntegerSymmetricalAlphaFilter(
    const int alpha,
    const int64_t value_active = 0);

  ///Get the current state estimate
  int64_t GetEstimate() const { return m_value_active; }

  ///Supply a measurement, which will update the state estimate
  void Update(const int64_t measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  ///The bitshift used for division
  int m_alpha;
  int64_t m_value_active;
};

} //~namespace ribi

#endif // INTEGERSYMMETRICALPHAFILTER_H

 

 

 

 

 

./CppStateObserver/integersymmetricalphafilter.cpp

 

#include "integersymmetricalphafilter.h"

#include <cassert>
#include <cstdlib>

ribi::IntegerSymmetricalAlphaFilter::IntegerSymmetricalAlphaFilter(
  const int alpha,
  const int64_t value_active)
  : m_alpha(alpha),
    m_value_active(value_active)
{
  assert(m_alpha >=  0 && "A bitshift should not be done with negative values");
  assert(m_alpha <= 63 && "An int64_t can maximally be shifted 63 bits to the right");
}

void ribi::IntegerSymmetricalAlphaFilter::Update(const int64_t measurement)
{
  const int64_t delta = ((measurement  - m_value_active) >> m_alpha);
  m_value_active += delta + (delta == 0 && measurement - m_value_active > 0 ? 1 : 0);
}

std::string ribi::IntegerSymmetricalAlphaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::IntegerSymmetricalAlphaFilter::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2013-06-04: version 1.0: initial version");
  v.push_back("2013-06-18: version 1.1: refactoring of IntegerStateObserver");
  return v;
}

 

 

 

 

 

./CppStateObserver/multialphafilter.h

 

#ifndef MULTIALPHAFILTER_H
#define MULTIALPHAFILTER_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <boost/shared_ptr.hpp>
#include "alphafilter.h"
#include "floatingpointstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

struct MultiAlphaFilter : public FloatingPointStateObserver
{
  MultiAlphaFilter(
    const std::vector<double> alphas,
    const double dt = 1.0
  );

  ///Get the current state estimate
  double GetEstimate() const;

  ///Supply a measurement, which will update the state estimate
  void Update(const double measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  std::vector<boost::shared_ptr<AlphaFilter> > m_filters;

  static const std::vector<boost::shared_ptr<AlphaFilter> > CreateFilters(
    const std::vector<double> alphas,
    const double dt);
};

} //~namespace ribi

#endif // MULTIALPHAFILTER_H

 

 

 

 

 

./CppStateObserver/multialphafilter.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "multialphafilter.h"

#include <cassert>


#pragma GCC diagnostic pop

ribi::MultiAlphaFilter::MultiAlphaFilter(
  const std::vector<double> alphas,
  const double dt)
  : m_filters(CreateFilters(alphas,dt))
{

}

const std::vector<boost::shared_ptr<ribi::AlphaFilter> > ribi::MultiAlphaFilter::CreateFilters(
  const std::vector<double> alphas,
  const double dt)
{
  std::vector<boost::shared_ptr<AlphaFilter> > v;

  for(const double alpha: alphas)
  {
    boost::shared_ptr<AlphaFilter> filter(new AlphaFilter(alpha,dt));
    assert(filter);
    v.push_back(filter);
  }
  return v;
}

double ribi::MultiAlphaFilter::GetEstimate() const
{
  return m_filters.back()->GetEstimate();
}

void ribi::MultiAlphaFilter::Update(double measurement)
{
  const std::size_t sz = m_filters.size();
  for (std::size_t i = 0; i!=sz; ++i)
  {
    assert(i < m_filters.size() );
    assert(m_filters[i]);
    m_filters[i]->Update(measurement); //One's output is the next one's output
    measurement = m_filters[i]->GetEstimate();
  }
}

std::string ribi::MultiAlphaFilter::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::MultiAlphaFilter::GetVersionHistory() noexcept
{
  return {
    "2013-05-25: version 1.0: initial version",
    "2013-06-18: version 1.1: derive from FloatingPointStateObserver"
  };
}

 

 

 

 

 

./CppStateObserver/multiintegerstateobserver.h

 

#ifndef MULTIINTEGERSTATEOBSERVER_H
#define MULTIINTEGERSTATEOBSERVER_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <boost/shared_ptr.hpp>
#include "integeralphafilter.h"
#include "integerstateobserver.h"
#pragma GCC diagnostic pop

namespace ribi {

struct MultiIntegerStateObserver : public IntegerStateObserver
{
  MultiIntegerStateObserver(
    std::vector<boost::shared_ptr<IntegerStateObserver> >& filters
  );

  ///Get the current state estimate
  int64_t GetEstimate() const;

  ///Supply a measurement, which will update the state estimate
  void Update(const int64_t measurement);

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  std::vector<boost::shared_ptr<IntegerStateObserver> > m_filters;
};

} //~namespace ribi

#endif // MULTIINTEGERSTATEOBSERVER_H

 

 

 

 

 

./CppStateObserver/multiintegerstateobserver.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "multiintegerstateobserver.h"

#include <cassert>


#pragma GCC diagnostic pop

ribi::MultiIntegerStateObserver::MultiIntegerStateObserver(
  std::vector<boost::shared_ptr<IntegerStateObserver> >& filters)
  : m_filters(filters)
{

}

int64_t ribi::MultiIntegerStateObserver::GetEstimate() const
{
  return m_filters.back()->GetEstimate();
}

void ribi::MultiIntegerStateObserver::Update(int64_t measurement)
{
  const std::size_t sz = m_filters.size();
  for (std::size_t i = 0; i!=sz; ++i)
  {
    assert(i < m_filters.size() );
    assert(m_filters[i]);
    m_filters[i]->Update(measurement); //One's output is the next one's output
    measurement = m_filters[i]->GetEstimate(); //One's output is the next one's output
  }
}

std::string ribi::MultiIntegerStateObserver::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::MultiIntegerStateObserver::GetVersionHistory() noexcept
{
  return {
    "2013-06-04: version 1.0: initial version",
    "2013-06-18: version 1.1: derive from IntegerStateObserver"
  };
}

 

 

 

 

 

./CppStateObserver/stateobserverfwd.h

 

#ifndef STATEOBSERVERFWD_H
#define STATEOBSERVERFWD_H

namespace ribi {

struct AlphaFilter;
struct AlphaBetaFilter;
struct AlphaBetaGammaFilter;
struct FloatingPointStateObserver;
struct IntegerStateObserver;
struct IntegerAlphaFilter;
struct IntegerSmartAlphaFilter;
struct IntegerSymmetricalAlphaFilter;
struct MultiIntegerStateObserver;
struct MultiAlphaFilter;

} //~namespace ribi

#endif // STATEOBSERVERFWD_H

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml