vector_products.h

Go to the documentation of this file.
00001 /* -*- C++ -*- ------------------------------------------------------------
00002  
00003 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
00004 
00005 The Configurable Math Library (CML) is distributed under the terms of the
00006 Boost Software License, v1.0 (see cml/LICENSE for details).
00007 
00008  *-----------------------------------------------------------------------*/
00016 #ifndef vector_products_h
00017 #define vector_products_h
00018 
00019 #include <cml/core/cml_assert.h>
00020 #include <cml/et/scalar_promotions.h>
00021 #include <cml/et/size_checking.h>
00022 #include <cml/vector/vector_unroller.h>
00023 #include <cml/vector/vector_expr.h>
00024 #include <cml/matrix/matrix_expr.h>
00025 
00026 /* This is used below to create a more meaningful compile-time error when
00027  * dot() is not provided with vector or VectorExpr arguments:
00028  */
00029 struct dot_expects_vector_args_error;
00030 
00031 /* This is used below to create a more meaningful compile-time error when
00032  * perp_dot() is not provided with 2D vector or VectorExpr arguments:
00033  */
00034 struct perp_dot_expects_vector_args_error;
00035 struct perp_dot_expects_2D_vector_args_error;
00036 
00037 /* This is used below to create a more meaningful compile-time error when
00038  * outer() is not provided with vector or VectorExpr arguments:
00039  */
00040 struct outer_expects_vector_args_error;
00041 
00042 /* This is used below to create a more meaningful compile-time error when
00043  * cross() is not provided with 3D vector or VectorExpr arguments:
00044  */
00045 struct cross_expects_vector_args_error;
00046 struct cross_expects_3D_vector_args_error;
00047 
00048 
00049 namespace cml {
00050 namespace detail {
00051 
00052 template<typename LeftT, typename RightT>
00053 struct DotPromote
00054 {
00055     /* Shorthand: */
00056     typedef et::ExprTraits<LeftT> left_traits;
00057     typedef et::ExprTraits<RightT> right_traits;
00058     typedef typename left_traits::value_type left_value;
00059     typedef typename right_traits::value_type right_value;
00060 
00061     /* Deduce the promoted scalar type: */
00062     typedef et::OpMul<left_value, right_value> op_mul;
00063     typedef typename et::OpAdd<
00064         typename op_mul::value_type,
00065                  typename op_mul::value_type> op_add;
00066     typedef typename op_add::value_type promoted_scalar;
00067 };
00068 
00069 template<typename LeftT, typename RightT>
00070 struct CrossPromote
00071 {
00072     /* Shorthand: */
00073     typedef et::ExprTraits<LeftT> left_traits;
00074     typedef et::ExprTraits<RightT> right_traits;
00075     typedef typename left_traits::result_type left_type;
00076     typedef typename right_traits::result_type right_type;
00077 
00078     /* Deduce the matrix result type: */
00079     typedef typename et::VectorPromote<
00080         left_type,right_type>::temporary_type promoted_vector;
00081 };
00082 
00083 template<typename LeftT, typename RightT>
00084 struct OuterPromote
00085 {
00086     /* Shorthand: */
00087     typedef et::ExprTraits<LeftT> left_traits;
00088     typedef et::ExprTraits<RightT> right_traits;
00089     typedef typename left_traits::result_type left_type;
00090     typedef typename right_traits::result_type right_type;
00091 
00092     /* Deduce the matrix result type: */
00093     typedef typename et::MatrixPromote<
00094         left_type,right_type>::temporary_type promoted_matrix;
00095 };
00096 
00103 template<typename LeftT, typename RightT>
00104 inline typename DotPromote<LeftT,RightT>::promoted_scalar
00105 UnrollDot(const LeftT& left, const RightT& right, fixed_size_tag)
00106 {
00107     /* Shorthand: */
00108     typedef DotPromote<LeftT,RightT> dot_helper;
00109 
00110     /* Compile-type vector size check: */
00111     typedef typename et::GetCheckedSize<LeftT,RightT,fixed_size_tag>
00112         ::check_type check_sizes;
00113 
00114     /* Get the fixed array size using the helper: */
00115     enum { Len = check_sizes::array_size };
00116 
00117     /* Record the unroller type: */
00118     typedef typename dot_helper::op_mul op_mul;
00119     typedef typename dot_helper::op_add op_add;
00120     typedef typename et::detail::VectorAccumulateUnroller<
00121         op_add,op_mul,LeftT,RightT>::template
00122         Eval<0, Len-1, (Len <= CML_VECTOR_DOT_UNROLL_LIMIT)> Unroller;
00123     /* Note: Len is the array size, so Len-1 is the last element. */
00124 
00125     /* Now, call the unroller: */
00126     return Unroller()(left,right);
00127 }
00128 
00135 template<typename LeftT, typename RightT>
00136 inline typename DotPromote<LeftT,RightT>::promoted_scalar
00137 UnrollDot(const LeftT& left, const RightT& right, dynamic_size_tag)
00138 {
00139     /* Shorthand: */
00140     typedef DotPromote<LeftT,RightT> dot_helper;
00141     typedef et::ExprTraits<LeftT> left_traits;
00142     typedef et::ExprTraits<RightT> right_traits;
00143     typedef typename dot_helper::op_mul op_mul;
00144     typedef typename dot_helper::op_add op_add;
00145 
00146     /* Record the return type: */
00147     typedef typename dot_helper::promoted_scalar sum_type;
00148 
00149     /* Verify expression sizes: */
00150     const size_t N = et::CheckedSize(left,right,dynamic_size_tag());
00151 
00152     /* Initialize the sum. Left and right must be vector expressions, so
00153      * it's okay to use array notation here:
00154      */
00155     sum_type sum(op_mul().apply(left[0],right[0]));
00156     for(size_t i = 1; i < N; ++i) {
00157         /* XXX This might not be optimized properly by some compilers.
00158          * but to do anything else requires changing the requirements
00159          * of a scalar operator, or requires defining a new class of scalar
00160          * <op>= operators.
00161          */
00162         sum = op_add().apply(sum, op_mul().apply(left[i], right[i]));
00163         /* Note: we don't need get(), since both arguments are required to
00164          * be vector expressions.
00165          */
00166     }
00167     return sum;
00168 }
00169 
00171 template<typename VecT> inline void
00172 Require3D(const VecT&, fixed_size_tag) {
00173     CML_STATIC_REQUIRE_M(
00174             ((size_t)VecT::array_size == 3),
00175             cross_expects_3D_vector_args_error);
00176 }
00177 
00179 template<typename VecT> inline void
00180 Require3D(const VecT& v, dynamic_size_tag) {
00181     et::GetCheckedSize<VecT,VecT,dynamic_size_tag>()
00182         .equal_or_fail(v.size(),size_t(3));
00183 }
00184 
00186 template<typename VecT> inline void
00187 Require2D(const VecT& v, fixed_size_tag) {
00188     CML_STATIC_REQUIRE_M(
00189             ((size_t)VecT::array_size == 2),
00190             perp_dot_expects_2D_vector_args_error);
00191 }
00192 
00194 template<typename VecT> inline void
00195 Require2D(const VecT& v, dynamic_size_tag) {
00196     et::GetCheckedSize<VecT,VecT,dynamic_size_tag>()
00197         .equal_or_fail(v.size(),size_t(2));
00198 }
00199 
00200 } // namespace detail
00201 
00202 
00205 template<typename LeftT, typename RightT>
00206 inline typename detail::DotPromote<LeftT,RightT>::promoted_scalar
00207 dot(const LeftT& left, const RightT& right)
00208 {
00209     /* Shorthand: */
00210     typedef detail::DotPromote<LeftT,RightT> dot_helper;
00211     typedef et::ExprTraits<LeftT> left_traits;
00212     typedef et::ExprTraits<RightT> right_traits;
00213     typedef typename left_traits::result_type left_type;
00214     typedef typename right_traits::result_type right_type;
00215     typedef typename left_traits::size_tag left_size;
00216     typedef typename right_traits::size_tag right_size;
00217 
00218     /* dot() requires vector expressions: */
00219     CML_STATIC_REQUIRE_M(
00220             (et::VectorExpressions<LeftT,RightT>::is_true),
00221             dot_expects_vector_args_error);
00222     /* Note: parens are required here so that the preprocessor ignores the
00223      * commas:
00224      */
00225 
00226     /* Figure out the unroller to use (fixed or dynamic): */
00227     typedef typename et::VectorPromote<
00228         left_type, right_type>::temporary_type promoted_vector;
00229     typedef typename promoted_vector::size_tag size_tag;
00230 
00231     /* Call unroller: */
00232     return detail::UnrollDot(left,right,size_tag());
00233 }
00234 
00237 template<typename LeftT, typename RightT>
00238 inline typename detail::DotPromote<LeftT,RightT>::promoted_scalar
00239 perp_dot(const LeftT& left, const RightT& right)
00240 {
00241     /* Shorthand: */
00242     typedef et::ExprTraits<LeftT> left_traits;
00243     typedef et::ExprTraits<RightT> right_traits;
00244     typedef typename left_traits::result_tag left_result;
00245     typedef typename right_traits::result_tag right_result;
00246 
00247     /* perp_dot() requires vector expressions: */
00248     CML_STATIC_REQUIRE_M(
00249         (same_type<left_result, et::vector_result_tag>::is_true
00250          && same_type<right_result, et::vector_result_tag>::is_true),
00251         perp_dot_expects_vector_args_error);
00252     /* Note: parens are required here so that the preprocessor ignores the
00253      * commas.
00254      */
00255 
00256     /* Make sure arguments are 2D vectors: */
00257     detail::Require2D(left, typename left_traits::size_tag());
00258     detail::Require2D(right, typename right_traits::size_tag());
00259 
00260     /* Get result type: */
00261     typedef typename detail::DotPromote<
00262         LeftT,RightT>::promoted_scalar result_type;
00263 
00264     /* Compute and return: */
00265     return result_type(left[0]*right[1]-left[1]*right[0]);
00266 }
00267 
00268 template<typename LeftT, typename RightT>
00269 inline typename detail::CrossPromote<LeftT,RightT>::promoted_vector
00270 cross(const LeftT& left, const RightT& right)
00271 {
00272     /* Shorthand: */
00273     typedef et::ExprTraits<LeftT> left_traits;
00274     typedef et::ExprTraits<RightT> right_traits;
00275     typedef typename left_traits::result_tag left_result;
00276     typedef typename right_traits::result_tag right_result;
00277 
00278     /* outer() requires vector expressions: */
00279     CML_STATIC_REQUIRE_M(
00280         (same_type<left_result, et::vector_result_tag>::is_true
00281          && same_type<right_result, et::vector_result_tag>::is_true),
00282         cross_expects_vector_args_error);
00283     /* Note: parens are required here so that the preprocessor ignores the
00284      * commas.
00285      */
00286 
00287     /* Make sure arguments are 3D vectors: */
00288     detail::Require3D(left, typename left_traits::size_tag());
00289     detail::Require3D(right, typename right_traits::size_tag());
00290 
00291     /* Get result type: */
00292     typedef typename detail::CrossPromote<
00293         LeftT,RightT>::promoted_vector result_type;
00294 
00295     /* Now, compute and return the cross product: */
00296     result_type result(
00297             left[1]*right[2] - left[2]*right[1],
00298             left[2]*right[0] - left[0]*right[2],
00299             left[0]*right[1] - left[1]*right[0]
00300             );
00301     return result;
00302 }
00303 
00310 template < class VecT_1, class VecT_2, class VecT_3 >
00311 typename detail::DotPromote<
00312     VecT_1, typename detail::CrossPromote< VecT_2, VecT_3 >::promoted_vector
00313 >::promoted_scalar
00314 triple_product(const VecT_1& v1, const VecT_2& v2, const VecT_3& v3) {
00315     return dot(v1,cross(v2,v3));
00316 }
00317 
00318 template<typename LeftT, typename RightT>
00319 inline typename detail::OuterPromote<LeftT,RightT>::promoted_matrix
00320 outer(const LeftT& left, const RightT& right)
00321 {
00322     /* Shorthand: */
00323     typedef et::ExprTraits<LeftT> left_traits;
00324     typedef et::ExprTraits<RightT> right_traits;
00325     typedef typename left_traits::result_tag left_result;
00326     typedef typename right_traits::result_tag right_result;
00327 
00328     /* outer() requires vector expressions: */
00329     CML_STATIC_REQUIRE_M(
00330         (same_type<left_result, et::vector_result_tag>::is_true
00331          && same_type<right_result, et::vector_result_tag>::is_true),
00332         dot_expects_vector_args_error);
00333     /* Note: parens are required here so that the preprocessor ignores the
00334      * commas.
00335      */
00336 
00337     /* Create a matrix with the right size (resize() is a no-op for
00338      * fixed-size matrices):
00339      */
00340     typename detail::OuterPromote<LeftT,RightT>::promoted_matrix C;
00341     cml::et::detail::Resize(C, left.size(), right.size());
00342 
00343     /* Now, compute the outer product: */
00344     for(size_t i = 0; i < left.size(); ++i) {
00345         for(size_t j = 0; j < right.size(); ++j) {
00346             C(i,j) = left[i]*right[j];
00347             /* Note: both arguments are vectors, so array notation
00348              * is okay here.
00349              */
00350         }
00351     }
00352 
00353     return C;
00354 }
00355 
00356 } // namespace cml
00357 
00358 #endif
00359 
00360 // -------------------------------------------------------------------------
00361 // vim:ft=cpp

Generated on Sat Jul 18 19:35:35 2009 for CML 1.0 by  doxygen 1.5.9