1 // Boost.Units - A C++ library for zero-overhead dimensional analysis and
2 // unit/quantity manipulation and conversion
4 // Copyright (C) 2003-2008 Matthias Christian Schabel
5 // Copyright (C) 2008 Steven Watanabe
7 // Distributed under the Boost Software License, Version 1.0. (See
8 // accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_UNITS_DETAIL_LINEAR_ALGEBRA_HPP
12 #define BOOST_UNITS_DETAIL_LINEAR_ALGEBRA_HPP
14 #include <boost/units/static_rational.hpp>
15 #include <boost/mpl/next.hpp>
16 #include <boost/mpl/arithmetic.hpp>
17 #include <boost/mpl/and.hpp>
18 #include <boost/mpl/assert.hpp>
20 #include <boost/units/dim.hpp>
21 #include <boost/units/dimensionless_type.hpp>
22 #include <boost/units/static_rational.hpp>
23 #include <boost/units/detail/dimension_list.hpp>
24 #include <boost/units/detail/sort.hpp>
32 // typedef list<rational> equation;
35 struct eliminate_from_pair_of_equations_impl;
37 template<class E1, class E2>
38 struct eliminate_from_pair_of_equations;
41 struct elimination_impl;
43 template<bool is_zero, bool element_is_last>
44 struct elimination_skip_leading_zeros_impl;
46 template<class Equation, class Vars>
50 struct substitute_impl;
59 struct check_extra_equations_impl;
62 struct normalize_units_impl;
64 struct inconsistent {};
66 // generally useful utilies.
69 struct divide_equation {
70 template<class Begin, class Divisor>
72 typedef list<typename mpl::divides<typename Begin::item, Divisor>::type, typename divide_equation<N - 1>::template apply<typename Begin::next, Divisor>::type> type;
77 struct divide_equation<0> {
78 template<class Begin, class Divisor>
80 typedef dimensionless_type type;
84 // eliminate_from_pair_of_equations takes a pair of
85 // equations and eliminates the first variable.
87 // equation eliminate_from_pair_of_equations(equation l1, equation l2) {
88 // rational x1 = l1.front();
89 // rational x2 = l2.front();
90 // return(transform(pop_front(l1), pop_front(l2), _1 * x2 - _2 * x1));
94 struct eliminate_from_pair_of_equations_impl {
95 template<class Begin1, class Begin2, class X1, class X2>
99 typename mpl::times<typename Begin1::item, X2>::type,
100 typename mpl::times<typename Begin2::item, X1>::type
102 typename eliminate_from_pair_of_equations_impl<N - 1>::template apply<
103 typename Begin1::next,
104 typename Begin2::next,
113 struct eliminate_from_pair_of_equations_impl<0> {
114 template<class Begin1, class Begin2, class X1, class X2>
116 typedef dimensionless_type type;
120 template<class E1, class E2>
121 struct eliminate_from_pair_of_equations {
124 typedef typename eliminate_from_pair_of_equations_impl<(E1::size::value - 1)>::template apply<
125 typename begin1::next,
126 typename begin2::next,
127 typename begin1::item,
128 typename begin2::item
134 // Stage 1. Determine which dimensions should
135 // have dummy base units. For this purpose
136 // row reduce the matrix.
139 struct make_zero_vector {
140 typedef list<static_rational<0>, typename make_zero_vector<N - 1>::type> type;
143 struct make_zero_vector<0> {
144 typedef dimensionless_type type;
147 template<int Column, int TotalColumns>
148 struct create_row_of_identity {
149 typedef list<static_rational<0>, typename create_row_of_identity<Column - 1, TotalColumns - 1>::type> type;
151 template<int TotalColumns>
152 struct create_row_of_identity<0, TotalColumns> {
153 typedef list<static_rational<1>, typename make_zero_vector<TotalColumns - 1>::type> type;
156 struct create_row_of_identity<Column, 0> {
160 template<int RemainingRows>
161 struct determine_extra_equations_impl;
163 template<bool first_is_zero, bool is_last>
164 struct determine_extra_equations_skip_zeros_impl;
166 // not the last row and not zero.
168 struct determine_extra_equations_skip_zeros_impl<false, false> {
169 template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result>
171 // remove the equation being eliminated against from the set of equations.
172 typedef typename determine_extra_equations_impl<RemainingRows - 1>::template apply<typename RowsBegin::next, typename RowsBegin::item>::type next_equations;
173 // since this column was present, strip it out.
178 // the last row but not zero.
180 struct determine_extra_equations_skip_zeros_impl<false, true> {
181 template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result>
183 // remove this equation.
184 typedef dimensionless_type next_equations;
185 // since this column was present, strip it out.
191 // the first columns is zero but it is not the last column.
192 // continue with the same loop.
194 struct determine_extra_equations_skip_zeros_impl<true, false> {
195 template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result>
197 typedef typename RowsBegin::next::item next_row;
198 typedef typename determine_extra_equations_skip_zeros_impl<
199 next_row::item::Numerator == 0,
200 RemainingRows == 2 // the next one will be the last.
202 typename RowsBegin::next,
208 typedef list<typename RowsBegin::item::next, typename next::next_equations> next_equations;
209 typedef typename next::type type;
213 // all the elements in this column are zero.
215 struct determine_extra_equations_skip_zeros_impl<true, true> {
216 template<class RowsBegin, int RemainingRows, int CurrentColumn, int TotalColumns, class Result>
218 typedef list<typename RowsBegin::item::next, dimensionless_type> next_equations;
219 typedef list<typename create_row_of_identity<CurrentColumn, TotalColumns>::type, Result> type;
223 template<int RemainingRows>
224 struct determine_extra_equations_impl {
225 template<class RowsBegin, class EliminateAgainst>
228 typename eliminate_from_pair_of_equations<typename RowsBegin::item, EliminateAgainst>::type,
229 typename determine_extra_equations_impl<RemainingRows-1>::template apply<typename RowsBegin::next, EliminateAgainst>::type
235 struct determine_extra_equations_impl<0> {
236 template<class RowsBegin, class EliminateAgainst>
238 typedef dimensionless_type type;
242 template<int RemainingColumns, bool is_done>
243 struct determine_extra_equations {
244 template<class RowsBegin, int TotalColumns, class Result>
246 typedef typename RowsBegin::item top_row;
247 typedef typename determine_extra_equations_skip_zeros_impl<
248 top_row::item::Numerator == 0,
249 RowsBegin::size::value == 1
252 RowsBegin::size::value,
253 TotalColumns - RemainingColumns,
257 typedef typename determine_extra_equations<
258 RemainingColumns - 1,
259 column_info::next_equations::size::value == 0
261 typename column_info::next_equations,
263 typename column_info::type
268 template<int RemainingColumns>
269 struct determine_extra_equations<RemainingColumns, true> {
270 template<class RowsBegin, int TotalColumns, class Result>
272 typedef typename determine_extra_equations<RemainingColumns - 1, true>::template apply<
275 list<typename create_row_of_identity<TotalColumns - RemainingColumns, TotalColumns>::type, Result>
281 struct determine_extra_equations<0, true> {
282 template<class RowsBegin, int TotalColumns, class Result>
289 // invert the matrix using Gauss-Jordan elimination
292 template<bool is_zero, bool is_last>
293 struct invert_strip_leading_zeroes;
296 struct invert_handle_after_pivot_row;
298 // When processing column N, none of the first N rows
299 // can be the pivot column.
301 struct invert_handle_inital_rows {
302 template<class RowsBegin, class IdentityBegin>
304 typedef typename invert_handle_inital_rows<N - 1>::template apply<
305 typename RowsBegin::next,
306 typename IdentityBegin::next
308 typedef typename RowsBegin::item current_row;
309 typedef typename IdentityBegin::item current_identity_row;
310 typedef typename next::pivot_row pivot_row;
311 typedef typename next::identity_pivot_row identity_pivot_row;
313 typename eliminate_from_pair_of_equations_impl<(current_row::size::value) - 1>::template apply<
314 typename current_row::next,
316 typename current_row::item,
319 typename next::new_matrix
322 typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply<
323 current_identity_row,
325 typename current_row::item,
328 typename next::identity_result
333 // This handles the switch to searching for a pivot column.
334 // The pivot row will be propagated up in the typedefs
335 // pivot_row and identity_pivot_row. It is inserted here.
337 struct invert_handle_inital_rows<0> {
338 template<class RowsBegin, class IdentityBegin>
340 typedef typename RowsBegin::item current_row;
341 typedef typename invert_strip_leading_zeroes<
342 (current_row::item::Numerator == 0),
343 (RowsBegin::size::value == 1)
349 typedef list<typename next::pivot_row, typename next::new_matrix> new_matrix;
350 typedef list<typename next::identity_pivot_row, typename next::identity_result> identity_result;
351 typedef typename next::pivot_row pivot_row;
352 typedef typename next::identity_pivot_row identity_pivot_row;
356 // The first internal element which is not zero.
358 struct invert_strip_leading_zeroes<false, false> {
359 template<class RowsBegin, class IdentityBegin>
361 typedef typename RowsBegin::item current_row;
362 typedef typename current_row::item current_value;
363 typedef typename divide_equation<(current_row::size::value - 1)>::template apply<typename current_row::next, current_value>::type new_equation;
364 typedef typename divide_equation<(IdentityBegin::item::size::value)>::template apply<typename IdentityBegin::item, current_value>::type transformed_identity_equation;
365 typedef typename invert_handle_after_pivot_row<(RowsBegin::size::value - 1)>::template apply<
366 typename RowsBegin::next,
367 typename IdentityBegin::next,
369 transformed_identity_equation
373 // Note that we don't add the pivot row to the
374 // results here, because it needs to propagated up
376 typedef typename next::new_matrix new_matrix;
377 typedef typename next::identity_result identity_result;
378 typedef new_equation pivot_row;
379 typedef transformed_identity_equation identity_pivot_row;
383 // The one and only non-zero element--at the end
385 struct invert_strip_leading_zeroes<false, true> {
386 template<class RowsBegin, class IdentityBegin>
388 typedef typename RowsBegin::item current_row;
389 typedef typename current_row::item current_value;
390 typedef typename divide_equation<(current_row::size::value - 1)>::template apply<typename current_row::next, current_value>::type new_equation;
391 typedef typename divide_equation<(IdentityBegin::item::size::value)>::template apply<typename IdentityBegin::item, current_value>::type transformed_identity_equation;
394 // Note that we don't add the pivot row to the
395 // results here, because it needs to propagated up
397 typedef dimensionless_type identity_result;
398 typedef dimensionless_type new_matrix;
399 typedef new_equation pivot_row;
400 typedef transformed_identity_equation identity_pivot_row;
404 // One of the initial zeroes
406 struct invert_strip_leading_zeroes<true, false> {
407 template<class RowsBegin, class IdentityBegin>
409 typedef typename RowsBegin::item current_row;
410 typedef typename RowsBegin::next::item next_row;
411 typedef typename invert_strip_leading_zeroes<
412 next_row::item::Numerator == 0,
413 RowsBegin::size::value == 2
415 typename RowsBegin::next,
416 typename IdentityBegin::next
418 typedef typename IdentityBegin::item current_identity_row;
419 // these are propagated up.
420 typedef typename next::pivot_row pivot_row;
421 typedef typename next::identity_pivot_row identity_pivot_row;
423 typename eliminate_from_pair_of_equations_impl<(current_row::size::value - 1)>::template apply<
424 typename current_row::next,
426 typename current_row::item,
429 typename next::new_matrix
432 typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply<
433 current_identity_row,
435 typename current_row::item,
438 typename next::identity_result
443 // the last element, and is zero.
444 // Should never happen.
446 struct invert_strip_leading_zeroes<true, true> {
450 struct invert_handle_after_pivot_row {
451 template<class RowsBegin, class IdentityBegin, class MatrixPivot, class IdentityPivot>
453 typedef typename invert_handle_after_pivot_row<N - 1>::template apply<
454 typename RowsBegin::next,
455 typename IdentityBegin::next,
459 typedef typename RowsBegin::item current_row;
460 typedef typename IdentityBegin::item current_identity_row;
461 typedef MatrixPivot pivot_row;
462 typedef IdentityPivot identity_pivot_row;
466 typename eliminate_from_pair_of_equations_impl<(current_row::size::value - 1)>::template apply<
467 typename current_row::next,
469 typename current_row::item,
472 typename next::new_matrix
475 typename eliminate_from_pair_of_equations_impl<(current_identity_row::size::value)>::template apply<
476 current_identity_row,
478 typename current_row::item,
481 typename next::identity_result
487 struct invert_handle_after_pivot_row<0> {
488 template<class RowsBegin, class IdentityBegin, class MatrixPivot, class IdentityPivot>
490 typedef dimensionless_type new_matrix;
491 typedef dimensionless_type identity_result;
497 template<class RowsBegin, class IdentityBegin>
499 typedef typename invert_handle_inital_rows<RowsBegin::size::value - N>::template apply<RowsBegin, IdentityBegin> process_column;
500 typedef typename invert_impl<N - 1>::template apply<
501 typename process_column::new_matrix,
502 typename process_column::identity_result
508 struct invert_impl<0> {
509 template<class RowsBegin, class IdentityBegin>
511 typedef IdentityBegin type;
516 struct make_identity {
519 typedef list<typename create_row_of_identity<Size - N, Size>::type, typename make_identity<N - 1>::template apply<Size>::type> type;
524 struct make_identity<0> {
527 typedef dimensionless_type type;
531 template<class Matrix>
532 struct make_square_and_invert {
533 typedef typename Matrix::item top_row;
534 typedef typename determine_extra_equations<(top_row::size::value), false>::template apply<
536 top_row::size::value, // TotalColumns
539 typedef typename invert_impl<invertible::size::value>::template apply<
541 typename make_identity<invertible::size::value>::template apply<invertible::size::value>::type
546 // find_base_dimensions takes a list of
547 // base_units and returns a sorted list
548 // of all the base_dimensions they use.
550 // list<base_dimension> find_base_dimensions(list<base_unit> l) {
551 // set<base_dimension> dimensions;
552 // for_each(base_unit unit : l) {
553 // for_each(dim d : unit.dimension_type) {
554 // dimensions = insert(dimensions, d.tag_type);
557 // return(sort(dimensions, _1 > _2, front_inserter(list<base_dimension>())));
561 struct set_yes { set_no dummy[2]; };
567 static set_no lookup(...);
568 typedef mpl::long_<0> size;
571 template<class T, class Next>
574 static set_yes lookup(wrap<T>*);
577 typedef typename mpl::next<typename Next::size>::type size;
580 template<bool has_key>
584 struct set_insert<true> {
585 template<class Set, class T>
592 struct set_insert<false> {
593 template<class Set, class T>
595 typedef set<T, Set> type;
599 template<class Set, class T>
601 static const long size = sizeof(Set::lookup((wrap<T>*)0));
602 static const bool value = (size == sizeof(set_yes));
606 struct find_base_dimensions_impl_impl {
607 template<class Begin, class S>
609 typedef typename find_base_dimensions_impl_impl<N-1>::template apply<
610 typename Begin::next,
614 typedef typename set_insert<
615 (has_key<next, typename Begin::item::tag_type>::value)
618 typename Begin::item::tag_type
624 struct find_base_dimensions_impl_impl<0> {
625 template<class Begin, class S>
632 struct find_base_dimensions_impl {
633 template<class Begin>
635 typedef typename find_base_dimensions_impl_impl<(Begin::item::dimension_type::size::value)>::template apply<
636 typename Begin::item::dimension_type,
637 typename find_base_dimensions_impl<N-1>::template apply<typename Begin::next>::type
643 struct find_base_dimensions_impl<0> {
644 template<class Begin>
646 typedef set_end type;
651 struct find_base_dimensions {
652 typedef typename insertion_sort<
653 typename find_base_dimensions_impl<
655 >::template apply<T>::type
659 // calculate_base_dimension_coefficients finds
660 // the coefficients corresponding to the first
661 // base_dimension in each of the dimension_lists.
662 // It returns two values. The first result
663 // is a list of the coefficients. The second
664 // is a list with all the incremented iterators.
665 // When we encounter a base_dimension that is
666 // missing from a dimension_list, we do not
667 // increment the iterator and we set the
668 // coefficient to zero.
670 template<bool has_dimension>
671 struct calculate_base_dimension_coefficients_func;
674 struct calculate_base_dimension_coefficients_func<true> {
677 typedef typename T::item::value_type type;
678 typedef typename T::next next;
683 struct calculate_base_dimension_coefficients_func<false> {
686 typedef static_rational<0> type;
691 // begins_with_dimension returns true iff its first
692 // parameter is a valid iterator which yields its
693 // second parameter when dereferenced.
695 template<class Iterator>
696 struct begins_with_dimension {
701 typename Iterator::item::tag_type
706 struct begins_with_dimension<dimensionless_type> {
708 struct apply : mpl::false_ {};
712 struct calculate_base_dimension_coefficients_impl {
713 template<class BaseUnitDimensions,class Dim,class T>
715 typedef typename calculate_base_dimension_coefficients_func<
716 begins_with_dimension<typename BaseUnitDimensions::item>::template apply<
720 typename BaseUnitDimensions::item
722 typedef typename calculate_base_dimension_coefficients_impl<N-1>::template apply<
723 typename BaseUnitDimensions::next,
725 list<typename result::type, T>
727 typedef typename next_::type type;
728 typedef list<typename result::next, typename next_::next> next;
733 struct calculate_base_dimension_coefficients_impl<0> {
734 template<class Begin, class BaseUnitDimensions, class T>
737 typedef dimensionless_type next;
741 // add_zeroes pushs N zeroes onto the
744 // list<rational> add_zeroes(list<rational> l, int N) {
748 // return(push_front(add_zeroes(l, N-1), 0));
753 struct add_zeroes_impl {
754 // If you get an error here and your base units are
755 // in fact linearly independent, please report it.
756 BOOST_MPL_ASSERT_MSG((N > 0), base_units_are_probably_not_linearly_independent, (void));
761 typename add_zeroes_impl<N-1>::template apply<T>::type
767 struct add_zeroes_impl<0> {
774 // expand_dimensions finds the exponents of
775 // a set of dimensions in a dimension_list.
776 // the second parameter is assumed to be
777 // a superset of the base_dimensions of
778 // the first parameter.
780 // list<rational> expand_dimensions(dimension_list, list<base_dimension>);
783 struct expand_dimensions {
784 template<class Begin, class DimensionIterator>
786 typedef typename calculate_base_dimension_coefficients_func<
787 begins_with_dimension<DimensionIterator>::template apply<typename Begin::item>::value
788 >::template apply<DimensionIterator> result;
790 typename result::type,
791 typename expand_dimensions<N-1>::template apply<typename Begin::next, typename result::next>::type
797 struct expand_dimensions<0> {
798 template<class Begin, class DimensionIterator>
800 typedef dimensionless_type type;
805 struct create_unit_matrix {
806 template<class Begin, class Dimensions>
808 typedef typename create_unit_matrix<N - 1>::template apply<typename Begin::next, Dimensions>::type next;
809 typedef list<typename expand_dimensions<Dimensions::size::value>::template apply<Dimensions, typename Begin::item::dimension_type>::type, next> type;
814 struct create_unit_matrix<0> {
815 template<class Begin, class Dimensions>
817 typedef dimensionless_type type;
822 struct normalize_units {
823 typedef typename find_base_dimensions<T>::type dimensions;
824 typedef typename create_unit_matrix<(T::size::value)>::template apply<
828 typedef typename make_square_and_invert<matrix>::type type;
829 static const long extra = (type::size::value) - (T::size::value);
832 // multiply_add_units computes M x V
833 // where M is a matrix and V is a horizontal
836 // list<rational> multiply_add_units(list<list<rational> >, list<rational>);
839 struct multiply_add_units_impl {
840 template<class Begin1, class Begin2 ,class X>
845 typename Begin2::item,
848 typename Begin1::item
850 typename multiply_add_units_impl<N-1>::template apply<
851 typename Begin1::next,
852 typename Begin2::next,
860 struct multiply_add_units_impl<0> {
861 template<class Begin1, class Begin2 ,class X>
863 typedef dimensionless_type type;
868 struct multiply_add_units {
869 template<class Begin1, class Begin2>
871 typedef typename multiply_add_units_impl<
872 (Begin2::item::size::value)
874 typename multiply_add_units<N-1>::template apply<
875 typename Begin1::next,
876 typename Begin2::next
878 typename Begin2::item,
879 typename Begin1::item
885 struct multiply_add_units<1> {
886 template<class Begin1, class Begin2>
888 typedef typename add_zeroes_impl<
889 (Begin2::item::size::value)
890 >::template apply<dimensionless_type>::type type1;
891 typedef typename multiply_add_units_impl<
892 (Begin2::item::size::value)
895 typename Begin2::item,
896 typename Begin1::item
902 // strip_zeroes erases the first N elements of a list if
903 // they are all zero, otherwise returns inconsistent
905 // list strip_zeroes(list l, int N) {
908 // } else if(l.front == 0) {
909 // return(strip_zeroes(pop_front(l), N-1));
911 // return(inconsistent);
916 struct strip_zeroes_impl;
919 struct strip_zeroes_func {
920 template<class L, int N>
922 typedef inconsistent type;
927 struct strip_zeroes_func<static_rational<0> > {
928 template<class L, int N>
930 typedef typename strip_zeroes_impl<N-1>::template apply<typename L::next>::type type;
935 struct strip_zeroes_impl {
938 typedef typename strip_zeroes_func<typename T::item>::template apply<T, N>::type type;
943 struct strip_zeroes_impl<0> {
950 // Given a list of base_units, computes the
951 // exponents of each base unit for a given
954 // list<rational> calculate_base_unit_exponents(list<base_unit> units, dimension_list dimensions);
957 struct is_base_dimension_unit {
958 typedef mpl::false_ type;
959 typedef void base_dimension_type;
962 struct is_base_dimension_unit<list<dim<T, static_rational<1> >, dimensionless_type> > {
963 typedef mpl::true_ type;
964 typedef T base_dimension_type;
968 struct is_simple_system_impl {
969 template<class Begin, class Prev>
971 typedef is_base_dimension_unit<typename Begin::item::dimension_type> test;
974 mpl::less<Prev, typename test::base_dimension_type>,
975 typename is_simple_system_impl<N-1>::template apply<
976 typename Begin::next,
977 typename test::base_dimension_type
980 static const bool value = (type::value);
985 struct is_simple_system_impl<0> {
986 template<class Begin, class Prev>
987 struct apply : mpl::true_ {
992 struct is_simple_system {
994 typedef is_base_dimension_unit<typename Begin::item::dimension_type> test;
995 typedef typename mpl::and_<
997 typename is_simple_system_impl<
1000 typename Begin::next::type,
1001 typename test::base_dimension_type
1004 static const bool value = type::value;
1008 struct calculate_base_unit_exponents_impl;
1011 struct calculate_base_unit_exponents_impl<true> {
1012 template<class T, class Dimensions>
1014 typedef typename expand_dimensions<(T::size::value)>::template apply<
1015 typename find_base_dimensions<T>::type,
1022 struct calculate_base_unit_exponents_impl<false> {
1023 template<class T, class Dimensions>
1025 // find the units that correspond to each base dimension
1026 typedef normalize_units<T> base_solutions;
1027 // pad the dimension with zeroes so it can just be a
1028 // list of numbers, making the multiplication easy
1029 // e.g. if the arguments are list<pound, foot> and
1030 // list<mass,time^-2> then this step will
1031 // yield list<0,1,-2>
1032 typedef typename expand_dimensions<(base_solutions::dimensions::size::value)>::template apply<
1033 typename base_solutions::dimensions,
1036 // take the unit corresponding to each base unit
1037 // multiply each of its exponents by the exponent
1038 // of the base_dimension in the result and sum.
1039 typedef typename multiply_add_units<dimensions::size::value>::template apply<
1041 typename base_solutions::type
1043 // Now, verify that the dummy units really
1044 // cancel out and remove them.
1045 typedef typename strip_zeroes_impl<base_solutions::extra>::template apply<units>::type type;
1049 template<class T, class Dimensions>
1050 struct calculate_base_unit_exponents {
1051 typedef typename calculate_base_unit_exponents_impl<is_simple_system<T>::value>::template apply<T, Dimensions>::type type;
1054 } // namespace detail
1056 } // namespace units
1058 } // namespace boost