// Boost.Units - A C++ library for zero-overhead dimensional analysis and | |
// unit/quantity manipulation and conversion | |
// | |
// Copyright (C) 2003-2008 Matthias Christian Schabel | |
// Copyright (C) 2008 Steven Watanabe | |
// | |
// 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) | |
#ifndef BOOST_UNITS_DETAIL_LINEAR_ALGEBRA_HPP | |
#define BOOST_UNITS_DETAIL_LINEAR_ALGEBRA_HPP | |
#include <boost/units/static_rational.hpp> | |
#include <boost/mpl/next.hpp> | |
#include <boost/mpl/arithmetic.hpp> | |
#include <boost/mpl/and.hpp> | |
#include <boost/mpl/assert.hpp> | |
#include <boost/units/dim.hpp> | |
#include <boost/units/dimensionless_type.hpp> | |
#include <boost/units/static_rational.hpp> | |
#include <boost/units/detail/dimension_list.hpp> | |
#include <boost/units/detail/sort.hpp> | |
namespace boost { | |
namespace units { | |
namespace detail { | |
// typedef list<rational> equation; | |
template<int N> | |
struct eliminate_from_pair_of_equations_impl; | |
template<class E1, class E2> | |
struct eliminate_from_pair_of_equations; | |
template<int N> | |
struct elimination_impl; | |
template<bool is_zero, bool element_is_last> | |
struct elimination_skip_leading_zeros_impl; | |
template<class Equation, class Vars> | |
struct substitute; | |
template<int N> | |
struct substitute_impl; | |
template<bool is_end> | |
struct solve_impl; | |
template<class T> | |
struct solve; | |
template<int N> | |
struct check_extra_equations_impl; | |
template<int N> | |
struct normalize_units_impl; | |
struct inconsistent {}; | |
// generally useful utilies. | |
template<int N> | |
struct divide_equation { | |
template<class Begin, class Divisor> | |
struct apply { | |
typedef list<typename mpl::divides<typename Begin::item, Divisor>::type, typename divide_equation<N - 1>::template apply<typename Begin::next, Divisor>::type> type; | |
}; | |
}; | |
template<> | |
struct divide_equation<0> { | |
template<class Begin, class Divisor> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
// eliminate_from_pair_of_equations takes a pair of | |
// equations and eliminates the first variable. | |
// | |
// equation eliminate_from_pair_of_equations(equation l1, equation l2) { | |
// rational x1 = l1.front(); | |
// rational x2 = l2.front(); | |
// return(transform(pop_front(l1), pop_front(l2), _1 * x2 - _2 * x1)); | |
// } | |
template<int N> | |
struct eliminate_from_pair_of_equations_impl { | |
template<class Begin1, class Begin2, class X1, class X2> | |
struct apply { | |
typedef list< | |
typename mpl::minus< | |
typename mpl::times<typename Begin1::item, X2>::type, | |
typename mpl::times<typename Begin2::item, X1>::type | |
>::type, | |
typename eliminate_from_pair_of_equations_impl<N - 1>::template apply< | |
typename Begin1::next, | |
typename Begin2::next, | |
X1, | |
X2 | |
>::type | |
> type; | |
}; | |
}; | |
template<> | |
struct eliminate_from_pair_of_equations_impl<0> { | |
template<class Begin1, class Begin2, class X1, class X2> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<class E1, class E2> | |
struct eliminate_from_pair_of_equations { | |
typedef E1 begin1; | |
typedef E2 begin2; | |
typedef typename eliminate_from_pair_of_equations_impl<(E1::size::value - 1)>::template apply< | |
typename begin1::next, | |
typename begin2::next, | |
typename begin1::item, | |
typename begin2::item | |
>::type type; | |
}; | |
// Stage 1. Determine which dimensions should | |
// have dummy base units. For this purpose | |
// row reduce the matrix. | |
template<int N> | |
struct make_zero_vector { | |
typedef list<static_rational<0>, typename make_zero_vector<N - 1>::type> type; | |
}; | |
template<> | |
struct make_zero_vector<0> { | |
typedef dimensionless_type type; | |
}; | |
template<int Column, int TotalColumns> | |
struct create_row_of_identity { | |
typedef list<static_rational<0>, typename create_row_of_identity<Column - 1, TotalColumns - 1>::type> type; | |
}; | |
template<int TotalColumns> | |
struct create_row_of_identity<0, TotalColumns> { | |
typedef list<static_rational<1>, typename make_zero_vector<TotalColumns - 1>::type> type; | |
}; | |
template<int Column> | |
struct create_row_of_identity<Column, 0> { | |
// error | |
}; | |
template<int RemainingRows> | |
struct determine_extra_equations_impl; | |
template<bool first_is_zero, bool is_last> | |
struct determine_extra_equations_skip_zeros_impl; | |
// not the last row and not zero. | |
template<> | |
struct determine_extra_equations_skip_zeros_impl<false, false> { | |
template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result> | |
struct apply { | |
// remove the equation being eliminated against from the set of equations. | |
typedef typename determine_extra_equations_impl<RemainingRows - 1>::template apply<typename RowsBegin::next, typename RowsBegin::item>::type next_equations; | |
// since this column was present, strip it out. | |
typedef Result type; | |
}; | |
}; | |
// the last row but not zero. | |
template<> | |
struct determine_extra_equations_skip_zeros_impl<false, true> { | |
template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result> | |
struct apply { | |
// remove this equation. | |
typedef dimensionless_type next_equations; | |
// since this column was present, strip it out. | |
typedef Result type; | |
}; | |
}; | |
// the first columns is zero but it is not the last column. | |
// continue with the same loop. | |
template<> | |
struct determine_extra_equations_skip_zeros_impl<true, false> { | |
template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result> | |
struct apply { | |
typedef typename RowsBegin::next::item next_row; | |
typedef typename determine_extra_equations_skip_zeros_impl< | |
next_row::item::Numerator == 0, | |
RemainingRows == 2 // the next one will be the last. | |
>::template apply< | |
typename RowsBegin::next, | |
RemainingRows - 1, | |
CurrentColumn, | |
TotalColumns, | |
Result | |
> next; | |
typedef list<typename RowsBegin::item::next, typename next::next_equations> next_equations; | |
typedef typename next::type type; | |
}; | |
}; | |
// all the elements in this column are zero. | |
template<> | |
struct determine_extra_equations_skip_zeros_impl<true, true> { | |
template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result> | |
struct apply { | |
typedef list<typename RowsBegin::item::next, dimensionless_type> next_equations; | |
typedef list<typename create_row_of_identity<CurrentColumn, TotalColumns>::type, Result> type; | |
}; | |
}; | |
template<int RemainingRows> | |
struct determine_extra_equations_impl { | |
template<class RowsBegin, class EliminateAgainst> | |
struct apply { | |
typedef list< | |
typename eliminate_from_pair_of_equations<typename RowsBegin::item, EliminateAgainst>::type, | |
typename determine_extra_equations_impl<RemainingRows-1>::template apply<typename RowsBegin::next, EliminateAgainst>::type | |
> type; | |
}; | |
}; | |
template<> | |
struct determine_extra_equations_impl<0> { | |
template<class RowsBegin, class EliminateAgainst> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<int RemainingColumns, bool is_done> | |
struct determine_extra_equations { | |
template<class RowsBegin, int TotalColumns, class Result> | |
struct apply { | |
typedef typename RowsBegin::item top_row; | |
typedef typename determine_extra_equations_skip_zeros_impl< | |
top_row::item::Numerator == 0, | |
RowsBegin::item::size::value == 1 | |
>::template apply< | |
RowsBegin, | |
RowsBegin::size::value, | |
TotalColumns - RemainingColumns, | |
TotalColumns, | |
Result | |
> column_info; | |
typedef typename determine_extra_equations< | |
RemainingColumns - 1, | |
column_info::next_equations::size::value == 0 | |
>::template apply< | |
typename column_info::next_equations, | |
TotalColumns, | |
typename column_info::type | |
>::type type; | |
}; | |
}; | |
template<int RemainingColumns> | |
struct determine_extra_equations<RemainingColumns, true> { | |
template<class RowsBegin, int TotalColumns, class Result> | |
struct apply { | |
typedef typename determine_extra_equations<RemainingColumns - 1, true>::template apply< | |
RowsBegin, | |
TotalColumns, | |
list<typename create_row_of_identity<TotalColumns - RemainingColumns, TotalColumns>::type, Result> | |
>::type type; | |
}; | |
}; | |
template<> | |
struct determine_extra_equations<0, true> { | |
template<class RowsBegin, int TotalColumns, class Result> | |
struct apply { | |
typedef Result type; | |
}; | |
}; | |
// Stage 2 | |
// invert the matrix using Gauss-Jordan elimination | |
template<bool is_zero, bool is_last> | |
struct invert_strip_leading_zeroes; | |
template<int N> | |
struct invert_handle_after_pivot_row; | |
// When processing column N, none of the first N rows | |
// can be the pivot column. | |
template<int N> | |
struct invert_handle_inital_rows { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename invert_handle_inital_rows<N - 1>::template apply< | |
typename RowsBegin::next, | |
typename IdentityBegin::next | |
> next; | |
typedef typename RowsBegin::item current_row; | |
typedef typename IdentityBegin::item current_identity_row; | |
typedef typename next::pivot_row pivot_row; | |
typedef typename next::identity_pivot_row identity_pivot_row; | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_row::size::value) - 1>::template apply< | |
typename current_row::next, | |
pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::new_matrix | |
> new_matrix; | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply< | |
current_identity_row, | |
identity_pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::identity_result | |
> identity_result; | |
}; | |
}; | |
// This handles the switch to searching for a pivot column. | |
// The pivot row will be propagated up in the typedefs | |
// pivot_row and identity_pivot_row. It is inserted here. | |
template<> | |
struct invert_handle_inital_rows<0> { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename RowsBegin::item current_row; | |
typedef typename invert_strip_leading_zeroes< | |
(current_row::item::Numerator == 0), | |
(RowsBegin::size::value == 1) | |
>::template apply< | |
RowsBegin, | |
IdentityBegin | |
> next; | |
// results | |
typedef list<typename next::pivot_row, typename next::new_matrix> new_matrix; | |
typedef list<typename next::identity_pivot_row, typename next::identity_result> identity_result; | |
typedef typename next::pivot_row pivot_row; | |
typedef typename next::identity_pivot_row identity_pivot_row; | |
}; | |
}; | |
// The first internal element which is not zero. | |
template<> | |
struct invert_strip_leading_zeroes<false, false> { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename RowsBegin::item current_row; | |
typedef typename current_row::item current_value; | |
typedef typename divide_equation<(current_row::size::value - 1)>::template apply<typename current_row::next, current_value>::type new_equation; | |
typedef typename divide_equation<(IdentityBegin::item::size::value)>::template apply<typename IdentityBegin::item, current_value>::type transformed_identity_equation; | |
typedef typename invert_handle_after_pivot_row<(RowsBegin::size::value - 1)>::template apply< | |
typename RowsBegin::next, | |
typename IdentityBegin::next, | |
new_equation, | |
transformed_identity_equation | |
> next; | |
// results | |
// Note that we don't add the pivot row to the | |
// results here, because it needs to propagated up | |
// to the diagonal. | |
typedef typename next::new_matrix new_matrix; | |
typedef typename next::identity_result identity_result; | |
typedef new_equation pivot_row; | |
typedef transformed_identity_equation identity_pivot_row; | |
}; | |
}; | |
// The one and only non-zero element--at the end | |
template<> | |
struct invert_strip_leading_zeroes<false, true> { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename RowsBegin::item current_row; | |
typedef typename current_row::item current_value; | |
typedef typename divide_equation<(current_row::size::value - 1)>::template apply<typename current_row::next, current_value>::type new_equation; | |
typedef typename divide_equation<(IdentityBegin::item::size::value)>::template apply<typename IdentityBegin::item, current_value>::type transformed_identity_equation; | |
// results | |
// Note that we don't add the pivot row to the | |
// results here, because it needs to propagated up | |
// to the diagonal. | |
typedef dimensionless_type identity_result; | |
typedef dimensionless_type new_matrix; | |
typedef new_equation pivot_row; | |
typedef transformed_identity_equation identity_pivot_row; | |
}; | |
}; | |
// One of the initial zeroes | |
template<> | |
struct invert_strip_leading_zeroes<true, false> { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename RowsBegin::item current_row; | |
typedef typename RowsBegin::next::item next_row; | |
typedef typename invert_strip_leading_zeroes< | |
next_row::item::Numerator == 0, | |
RowsBegin::size::value == 2 | |
>::template apply< | |
typename RowsBegin::next, | |
typename IdentityBegin::next | |
> next; | |
typedef typename IdentityBegin::item current_identity_row; | |
// these are propagated up. | |
typedef typename next::pivot_row pivot_row; | |
typedef typename next::identity_pivot_row identity_pivot_row; | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_row::size::value - 1)>::template apply< | |
typename current_row::next, | |
pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::new_matrix | |
> new_matrix; | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply< | |
current_identity_row, | |
identity_pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::identity_result | |
> identity_result; | |
}; | |
}; | |
// the last element, and is zero. | |
// Should never happen. | |
template<> | |
struct invert_strip_leading_zeroes<true, true> { | |
}; | |
template<int N> | |
struct invert_handle_after_pivot_row { | |
template<class RowsBegin, class IdentityBegin, class MatrixPivot, class IdentityPivot> | |
struct apply { | |
typedef typename invert_handle_after_pivot_row<N - 1>::template apply< | |
typename RowsBegin::next, | |
typename IdentityBegin::next, | |
MatrixPivot, | |
IdentityPivot | |
> next; | |
typedef typename RowsBegin::item current_row; | |
typedef typename IdentityBegin::item current_identity_row; | |
typedef MatrixPivot pivot_row; | |
typedef IdentityPivot identity_pivot_row; | |
// results | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_row::size::value - 1)>::template apply< | |
typename current_row::next, | |
pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::new_matrix | |
> new_matrix; | |
typedef list< | |
typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply< | |
current_identity_row, | |
identity_pivot_row, | |
typename current_row::item, | |
static_rational<1> | |
>::type, | |
typename next::identity_result | |
> identity_result; | |
}; | |
}; | |
template<> | |
struct invert_handle_after_pivot_row<0> { | |
template<class RowsBegin, class IdentityBegin, class MatrixPivot, class IdentityPivot> | |
struct apply { | |
typedef dimensionless_type new_matrix; | |
typedef dimensionless_type identity_result; | |
}; | |
}; | |
template<int N> | |
struct invert_impl { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef typename invert_handle_inital_rows<RowsBegin::size::value - N>::template apply<RowsBegin, IdentityBegin> process_column; | |
typedef typename invert_impl<N - 1>::template apply< | |
typename process_column::new_matrix, | |
typename process_column::identity_result | |
>::type type; | |
}; | |
}; | |
template<> | |
struct invert_impl<0> { | |
template<class RowsBegin, class IdentityBegin> | |
struct apply { | |
typedef IdentityBegin type; | |
}; | |
}; | |
template<int N> | |
struct make_identity { | |
template<int Size> | |
struct apply { | |
typedef list<typename create_row_of_identity<Size - N, Size>::type, typename make_identity<N - 1>::template apply<Size>::type> type; | |
}; | |
}; | |
template<> | |
struct make_identity<0> { | |
template<int Size> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<class Matrix> | |
struct make_square_and_invert { | |
typedef typename Matrix::item top_row; | |
typedef typename determine_extra_equations<(top_row::size::value), false>::template apply< | |
Matrix, // RowsBegin | |
top_row::size::value, // TotalColumns | |
Matrix // Result | |
>::type invertible; | |
typedef typename invert_impl<invertible::size::value>::template apply< | |
invertible, | |
typename make_identity<invertible::size::value>::template apply<invertible::size::value>::type | |
>::type type; | |
}; | |
// find_base_dimensions takes a list of | |
// base_units and returns a sorted list | |
// of all the base_dimensions they use. | |
// | |
// list<base_dimension> find_base_dimensions(list<base_unit> l) { | |
// set<base_dimension> dimensions; | |
// for_each(base_unit unit : l) { | |
// for_each(dim d : unit.dimension_type) { | |
// dimensions = insert(dimensions, d.tag_type); | |
// } | |
// } | |
// return(sort(dimensions, _1 > _2, front_inserter(list<base_dimension>()))); | |
// } | |
typedef char set_no; | |
struct set_yes { set_no dummy[2]; }; | |
template<class T> | |
struct wrap {}; | |
struct set_end { | |
static set_no lookup(...); | |
typedef mpl::long_<0> size; | |
}; | |
template<class T, class Next> | |
struct set : Next { | |
using Next::lookup; | |
static set_yes lookup(wrap<T>*); | |
typedef T item; | |
typedef Next next; | |
typedef typename mpl::next<typename Next::size>::type size; | |
}; | |
template<bool has_key> | |
struct set_insert; | |
template<> | |
struct set_insert<true> { | |
template<class Set, class T> | |
struct apply { | |
typedef Set type; | |
}; | |
}; | |
template<> | |
struct set_insert<false> { | |
template<class Set, class T> | |
struct apply { | |
typedef set<T, Set> type; | |
}; | |
}; | |
template<class Set, class T> | |
struct has_key { | |
static const long size = sizeof(Set::lookup((wrap<T>*)0)); | |
static const bool value = (size == sizeof(set_yes)); | |
}; | |
template<int N> | |
struct find_base_dimensions_impl_impl { | |
template<class Begin, class S> | |
struct apply { | |
typedef typename find_base_dimensions_impl_impl<N-1>::template apply< | |
typename Begin::next, | |
S | |
>::type next; | |
typedef typename set_insert< | |
(has_key<next, typename Begin::item::tag_type>::value) | |
>::template apply< | |
next, | |
typename Begin::item::tag_type | |
>::type type; | |
}; | |
}; | |
template<> | |
struct find_base_dimensions_impl_impl<0> { | |
template<class Begin, class S> | |
struct apply { | |
typedef S type; | |
}; | |
}; | |
template<int N> | |
struct find_base_dimensions_impl { | |
template<class Begin> | |
struct apply { | |
typedef typename find_base_dimensions_impl_impl<(Begin::item::dimension_type::size::value)>::template apply< | |
typename Begin::item::dimension_type, | |
typename find_base_dimensions_impl<N-1>::template apply<typename Begin::next>::type | |
>::type type; | |
}; | |
}; | |
template<> | |
struct find_base_dimensions_impl<0> { | |
template<class Begin> | |
struct apply { | |
typedef set_end type; | |
}; | |
}; | |
template<class T> | |
struct find_base_dimensions { | |
typedef typename insertion_sort< | |
typename find_base_dimensions_impl< | |
(T::size::value) | |
>::template apply<T>::type | |
>::type type; | |
}; | |
// calculate_base_dimension_coefficients finds | |
// the coefficients corresponding to the first | |
// base_dimension in each of the dimension_lists. | |
// It returns two values. The first result | |
// is a list of the coefficients. The second | |
// is a list with all the incremented iterators. | |
// When we encounter a base_dimension that is | |
// missing from a dimension_list, we do not | |
// increment the iterator and we set the | |
// coefficient to zero. | |
template<bool has_dimension> | |
struct calculate_base_dimension_coefficients_func; | |
template<> | |
struct calculate_base_dimension_coefficients_func<true> { | |
template<class T> | |
struct apply { | |
typedef typename T::item::value_type type; | |
typedef typename T::next next; | |
}; | |
}; | |
template<> | |
struct calculate_base_dimension_coefficients_func<false> { | |
template<class T> | |
struct apply { | |
typedef static_rational<0> type; | |
typedef T next; | |
}; | |
}; | |
// begins_with_dimension returns true iff its first | |
// parameter is a valid iterator which yields its | |
// second parameter when dereferenced. | |
template<class Iterator> | |
struct begins_with_dimension { | |
template<class Dim> | |
struct apply : | |
boost::is_same< | |
Dim, | |
typename Iterator::item::tag_type | |
> {}; | |
}; | |
template<> | |
struct begins_with_dimension<dimensionless_type> { | |
template<class Dim> | |
struct apply : mpl::false_ {}; | |
}; | |
template<int N> | |
struct calculate_base_dimension_coefficients_impl { | |
template<class BaseUnitDimensions,class Dim,class T> | |
struct apply { | |
typedef typename calculate_base_dimension_coefficients_func< | |
begins_with_dimension<typename BaseUnitDimensions::item>::template apply< | |
Dim | |
>::value | |
>::template apply< | |
typename BaseUnitDimensions::item | |
> result; | |
typedef typename calculate_base_dimension_coefficients_impl<N-1>::template apply< | |
typename BaseUnitDimensions::next, | |
Dim, | |
list<typename result::type, T> | |
> next_; | |
typedef typename next_::type type; | |
typedef list<typename result::next, typename next_::next> next; | |
}; | |
}; | |
template<> | |
struct calculate_base_dimension_coefficients_impl<0> { | |
template<class Begin, class BaseUnitDimensions, class T> | |
struct apply { | |
typedef T type; | |
typedef dimensionless_type next; | |
}; | |
}; | |
// add_zeroes pushs N zeroes onto the | |
// front of a list. | |
// | |
// list<rational> add_zeroes(list<rational> l, int N) { | |
// if(N == 0) { | |
// return(l); | |
// } else { | |
// return(push_front(add_zeroes(l, N-1), 0)); | |
// } | |
// } | |
template<int N> | |
struct add_zeroes_impl { | |
// If you get an error here and your base units are | |
// in fact linearly independent, please report it. | |
BOOST_MPL_ASSERT_MSG((N > 0), base_units_are_probably_not_linearly_independent, (void)); | |
template<class T> | |
struct apply { | |
typedef list< | |
static_rational<0>, | |
typename add_zeroes_impl<N-1>::template apply<T>::type | |
> type; | |
}; | |
}; | |
template<> | |
struct add_zeroes_impl<0> { | |
template<class T> | |
struct apply { | |
typedef T type; | |
}; | |
}; | |
// expand_dimensions finds the exponents of | |
// a set of dimensions in a dimension_list. | |
// the second parameter is assumed to be | |
// a superset of the base_dimensions of | |
// the first parameter. | |
// | |
// list<rational> expand_dimensions(dimension_list, list<base_dimension>); | |
template<int N> | |
struct expand_dimensions { | |
template<class Begin, class DimensionIterator> | |
struct apply { | |
typedef typename calculate_base_dimension_coefficients_func< | |
begins_with_dimension<DimensionIterator>::template apply<typename Begin::item>::value | |
>::template apply<DimensionIterator> result; | |
typedef list< | |
typename result::type, | |
typename expand_dimensions<N-1>::template apply<typename Begin::next, typename result::next>::type | |
> type; | |
}; | |
}; | |
template<> | |
struct expand_dimensions<0> { | |
template<class Begin, class DimensionIterator> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<int N> | |
struct create_unit_matrix { | |
template<class Begin, class Dimensions> | |
struct apply { | |
typedef typename create_unit_matrix<N - 1>::template apply<typename Begin::next, Dimensions>::type next; | |
typedef list<typename expand_dimensions<Dimensions::size::value>::template apply<Dimensions, typename Begin::item::dimension_type>::type, next> type; | |
}; | |
}; | |
template<> | |
struct create_unit_matrix<0> { | |
template<class Begin, class Dimensions> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<class T> | |
struct normalize_units { | |
typedef typename find_base_dimensions<T>::type dimensions; | |
typedef typename create_unit_matrix<(T::size::value)>::template apply< | |
T, | |
dimensions | |
>::type matrix; | |
typedef typename make_square_and_invert<matrix>::type type; | |
static const long extra = (type::size::value) - (T::size::value); | |
}; | |
// multiply_add_units computes M x V | |
// where M is a matrix and V is a horizontal | |
// vector | |
// | |
// list<rational> multiply_add_units(list<list<rational> >, list<rational>); | |
template<int N> | |
struct multiply_add_units_impl { | |
template<class Begin1, class Begin2 ,class X> | |
struct apply { | |
typedef list< | |
typename mpl::plus< | |
typename mpl::times< | |
typename Begin2::item, | |
X | |
>::type, | |
typename Begin1::item | |
>::type, | |
typename multiply_add_units_impl<N-1>::template apply< | |
typename Begin1::next, | |
typename Begin2::next, | |
X | |
>::type | |
> type; | |
}; | |
}; | |
template<> | |
struct multiply_add_units_impl<0> { | |
template<class Begin1, class Begin2 ,class X> | |
struct apply { | |
typedef dimensionless_type type; | |
}; | |
}; | |
template<int N> | |
struct multiply_add_units { | |
template<class Begin1, class Begin2> | |
struct apply { | |
typedef typename multiply_add_units_impl< | |
(Begin2::item::size::value) | |
>::template apply< | |
typename multiply_add_units<N-1>::template apply< | |
typename Begin1::next, | |
typename Begin2::next | |
>::type, | |
typename Begin2::item, | |
typename Begin1::item | |
>::type type; | |
}; | |
}; | |
template<> | |
struct multiply_add_units<1> { | |
template<class Begin1, class Begin2> | |
struct apply { | |
typedef typename add_zeroes_impl< | |
(Begin2::item::size::value) | |
>::template apply<dimensionless_type>::type type1; | |
typedef typename multiply_add_units_impl< | |
(Begin2::item::size::value) | |
>::template apply< | |
type1, | |
typename Begin2::item, | |
typename Begin1::item | |
>::type type; | |
}; | |
}; | |
// strip_zeroes erases the first N elements of a list if | |
// they are all zero, otherwise returns inconsistent | |
// | |
// list strip_zeroes(list l, int N) { | |
// if(N == 0) { | |
// return(l); | |
// } else if(l.front == 0) { | |
// return(strip_zeroes(pop_front(l), N-1)); | |
// } else { | |
// return(inconsistent); | |
// } | |
// } | |
template<int N> | |
struct strip_zeroes_impl; | |
template<class T> | |
struct strip_zeroes_func { | |
template<class L, int N> | |
struct apply { | |
typedef inconsistent type; | |
}; | |
}; | |
template<> | |
struct strip_zeroes_func<static_rational<0> > { | |
template<class L, int N> | |
struct apply { | |
typedef typename strip_zeroes_impl<N-1>::template apply<typename L::next>::type type; | |
}; | |
}; | |
template<int N> | |
struct strip_zeroes_impl { | |
template<class T> | |
struct apply { | |
typedef typename strip_zeroes_func<typename T::item>::template apply<T, N>::type type; | |
}; | |
}; | |
template<> | |
struct strip_zeroes_impl<0> { | |
template<class T> | |
struct apply { | |
typedef T type; | |
}; | |
}; | |
// Given a list of base_units, computes the | |
// exponents of each base unit for a given | |
// dimension. | |
// | |
// list<rational> calculate_base_unit_exponents(list<base_unit> units, dimension_list dimensions); | |
template<class T> | |
struct is_base_dimension_unit { | |
typedef mpl::false_ type; | |
typedef void base_dimension_type; | |
}; | |
template<class T> | |
struct is_base_dimension_unit<list<dim<T, static_rational<1> >, dimensionless_type> > { | |
typedef mpl::true_ type; | |
typedef T base_dimension_type; | |
}; | |
template<int N> | |
struct is_simple_system_impl { | |
template<class Begin, class Prev> | |
struct apply { | |
typedef is_base_dimension_unit<typename Begin::item::dimension_type> test; | |
typedef mpl::and_< | |
typename test::type, | |
mpl::less<Prev, typename test::base_dimension_type>, | |
typename is_simple_system_impl<N-1>::template apply< | |
typename Begin::next, | |
typename test::base_dimension_type | |
> | |
> type; | |
static const bool value = (type::value); | |
}; | |
}; | |
template<> | |
struct is_simple_system_impl<0> { | |
template<class Begin, class Prev> | |
struct apply : mpl::true_ { | |
}; | |
}; | |
template<class T> | |
struct is_simple_system { | |
typedef T Begin; | |
typedef is_base_dimension_unit<typename Begin::item::dimension_type> test; | |
typedef typename mpl::and_< | |
typename test::type, | |
typename is_simple_system_impl< | |
T::size::value - 1 | |
>::template apply< | |
typename Begin::next::type, | |
typename test::base_dimension_type | |
> | |
>::type type; | |
static const bool value = type::value; | |
}; | |
template<bool> | |
struct calculate_base_unit_exponents_impl; | |
template<> | |
struct calculate_base_unit_exponents_impl<true> { | |
template<class T, class Dimensions> | |
struct apply { | |
typedef typename expand_dimensions<(T::size::value)>::template apply< | |
typename find_base_dimensions<T>::type, | |
Dimensions | |
>::type type; | |
}; | |
}; | |
template<> | |
struct calculate_base_unit_exponents_impl<false> { | |
template<class T, class Dimensions> | |
struct apply { | |
// find the units that correspond to each base dimension | |
typedef normalize_units<T> base_solutions; | |
// pad the dimension with zeroes so it can just be a | |
// list of numbers, making the multiplication easy | |
// e.g. if the arguments are list<pound, foot> and | |
// list<mass,time^-2> then this step will | |
// yield list<0,1,-2> | |
typedef typename expand_dimensions<(base_solutions::dimensions::size::value)>::template apply< | |
typename base_solutions::dimensions, | |
Dimensions | |
>::type dimensions; | |
// take the unit corresponding to each base unit | |
// multiply each of its exponents by the exponent | |
// of the base_dimension in the result and sum. | |
typedef typename multiply_add_units<dimensions::size::value>::template apply< | |
dimensions, | |
typename base_solutions::type | |
>::type units; | |
// Now, verify that the dummy units really | |
// cancel out and remove them. | |
typedef typename strip_zeroes_impl<base_solutions::extra>::template apply<units>::type type; | |
}; | |
}; | |
template<class T, class Dimensions> | |
struct calculate_base_unit_exponents { | |
typedef typename calculate_base_unit_exponents_impl<is_simple_system<T>::value>::template apply<T, Dimensions>::type type; | |
}; | |
} // namespace detail | |
} // namespace units | |
} // namespace boost | |
#endif |