I have implemented Lagrange polynomials, i.e.
\$l_j(x) := \prod_{\begin{smallmatrix}0\le m\le k\\ m\neq j\end{smallmatrix}}\frac{x-x_m}{x_j-x_m} = \frac{(x-x_0)}{(x_j-x_0)} \cdots \frac{(x-x_{j-1})}{(x_j-x_{j-1})} \frac{(x-x_{j+1})}{(x_j-x_{j+1})} \cdots \frac{(x-x_k)}{(x_j-x_k)}\$
where the product is supposed to be expanded at compile time. The \$x_m\$ are chosen to be evenly spaced on the interval \$[-1, 1]\$.
I've used boost fusion for this, mainly because it seemed (and turned out to be) helpful, but I'm not entirely sure if it's the best choice (out of the multiple TMP libraries in boost and elsewhere).
Generally, I'm new to such an extensive use of types and templates, so general advice in this regard is appreciated. And now, without further ado:
#include <iostream>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/include/accumulate.hpp>
namespace fusion = boost::fusion;
template <int Order, int NodeId>
struct Location
{
static constexpr double coord = 2.0 * NodeId / Order - 1.0;
};
template <class BaseNode, class OtherNode>
struct Term
{
static double F(double x)
{
return (x - OtherNode::coord) / (BaseNode::coord - OtherNode::coord);
}
};
template <int Order, int BaseNodeId, int NodeCounter = Order>
struct RemainingNodes
{
using tail = typename RemainingNodes<Order, BaseNodeId, NodeCounter - 1>::list;
using currentNode = Location<Order, NodeCounter>;
using full =
typename fusion::result_of::as_vector<typename fusion::result_of::push_back<tail, currentNode>::type>::type;
using list = typename std::conditional<BaseNodeId == NodeCounter, tail, full>::type;
};
template <int Order, int BaseNodeId>
struct RemainingNodes<Order, BaseNodeId, 0>
{
using list = typename boost::fusion::vector<Location<Order, 0>>;
};
template <int Order>
struct RemainingNodes<Order, 0, 0>
{
using list = typename boost::fusion::vector<>;
};
template <class BaseNode>
struct GetTerm
{
template <class OtherNode>
Term<BaseNode, OtherNode> operator()(OtherNode);
};
template <int Order, int BaseNodeId>
struct Terms
{
using Nodes = typename RemainingNodes<Order, BaseNodeId>::list;
using BaseNode = Location<Order, BaseNodeId>;
using transformed_list = typename fusion::result_of::transform<Nodes, GetTerm<BaseNode>>::type;
using value = typename fusion::result_of::as_vector<transformed_list>::type;
};
struct evaluate
{
double _x;
evaluate(double x)
: _x(x)
{
}
template <class _Term>
double operator()(double product, const _Term&) const
{
return product * _Term::F(_x);
}
};
template <int Order, int BaseNodeId>
struct LagrangePolynomial
{
using _Terms = Terms<Order, BaseNodeId>;
static double F(double x)
{
return boost::fusion::accumulate(typename _Terms::value(), 1.0, evaluate(x));
}
};
int main()
{
using secondOrder0 = LagrangePolynomial<2, 0>;
std::cout << "2nd order Lagrange polynomial of 1st node, evaluated at -1, 0, 0.5, 1: ";
std::cout << secondOrder0::F(-1.0) << " " << secondOrder0::F(0.0) << " " << secondOrder0::F(0.5) << " "
<< secondOrder0::F(1.0) << std::endl;
return 0;
}