You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
1060 lines
32 KiB
1060 lines
32 KiB
// 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
|
|
|