/* -*- C++ -*- ------------------------------------------------------------
 
Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/

The Configurable Math Library (CML) is distributed under the terms of the
Boost Software License, v1.0 (see cml/LICENSE for details).

 *-----------------------------------------------------------------------*/
/** @file
 *  @brief
 *
 * @todo The matrix and matrix order operators could probably be combined
 * into a single templated implementation, since the only thing that is
 * different is the access method.
 */

#ifndef matrix_comparison_h
#define matrix_comparison_h

#include <cml/core/cml_assert.h>
#include <cml/et/size_checking.h>
#include <cml/et/scalar_ops.h>

/* This is used below to create a more meaningful compile-time error when
 * matrix_comparison is not provided with matrix or MatrixExpr arguments:
 */
struct matrix_comparison_expects_matrix_args_error;

#define CML_MAT_MAT_ORDER(_order_, _op_, _OpT_)                         \
template<typename E1, class AT1, typename L1,                           \
         typename E2, class AT2, typename L2, typename BO>              \
inline bool                                                             \
_op_ (                                                                  \
        const matrix<E1,AT1,BO,L1>& left,                               \
        const matrix<E2,AT2,BO,L2>& right)                              \
{                                                                       \
    return detail::matrix_##_order_ (left, right, _OpT_ <E1,E2>());     \
}

#define CML_MAT_MATXPR_ORDER(_order_, _op_, _OpT_)                      \
template<typename E, class AT, typename BO, typename L, class XprT>     \
inline bool                                                             \
_op_ (                                                                  \
        const matrix<E,AT,BO,L>& left,                                  \
        MATXPR_ARG_TYPE right)                                          \
{                                                                       \
    return detail::matrix_##_order_ (left, right,                       \
            _OpT_ <E, typename XprT::value_type>());                    \
}

#define CML_MATXPR_MAT_ORDER(_order_, _op_, _OpT_)                      \
template<class XprT, typename E, class AT, typename BO, typename L>     \
inline bool                                                             \
_op_ (                                                                  \
        MATXPR_ARG_TYPE left,                                           \
        const matrix<E,AT,BO,L>& right)                                 \
{                                                                       \
    return detail::matrix_##_order_ (left, right,                       \
            _OpT_ <typename XprT::value_type, E>());                    \
}

#define CML_MATXPR_MATXPR_ORDER(_order_, _op_, _OpT_)                   \
template<class XprT1, class XprT2>                                      \
inline bool                                                             \
_op_ (                                                                  \
        MATXPR_ARG_TYPE_N(1) left,                                      \
        MATXPR_ARG_TYPE_N(2) right)                                     \
{                                                                       \
    return detail::matrix_##_order_ (left, right,                       \
            _OpT_ <                                                     \
                typename XprT1::value_type,                             \
                typename XprT2::value_type>());                         \
}


namespace cml {
namespace detail {

/** Matrix strict weak ordering relationship.
 *
 * OpT must implement a strict weak order on the matrix element type.
 * operator< and operator> on integer and floating-point types are
 * examples.
 */
template<typename LeftT, typename RightT, typename OpT>
inline bool
matrix_weak_order(const LeftT& left, const RightT& right, OpT)
{
    /* Shorthand: */
    typedef et::ExprTraits<LeftT> left_traits;
    typedef et::ExprTraits<RightT> right_traits;

    /* matrix_comparison() requires matrix expressions: */
    CML_STATIC_REQUIRE_M(
            (et::MatrixExpressions<LeftT,RightT>::is_true),
            matrix_comparison_expects_matrix_args_error);
    /* Note: parens are required here so that the preprocessor ignores the
     * commas:
     */

    typedef typename et::MatrixPromote<
        typename left_traits::result_type,
        typename right_traits::result_type
    >::type result_type;
    typedef typename result_type::size_tag size_tag;

    /* Verify expression size: */
    matrix_size N = et::CheckedSize(left,right,size_tag());
    for(ssize_t i = 0; i < N.first; ++ i) {
        for(ssize_t j = 0; j < N.second; ++ j) {
            if(OpT().apply(
                        left_traits().get(left,i,j),
                        right_traits().get(right,i,j)
                        ))
            {
                /* If weak order (a < b) is satisfied, return true: */
                return true;
            } else if(OpT().apply(
                        right_traits().get(right,i,j),
                        left_traits().get(left,i,j)
                        ))
            {
                /* If !(b < a), then return false: */
                return false;
            } else {

                /* Have !(a < b) && !(b < a) <=> (a >= b && b >= a)
                 * <=> (a == b).  so need to test next element:
                 */
                continue;
            }
        }
    }
    /* XXX Can this be unrolled in any reasonable way? */

    /* If we get here, then left == right: */
    return false;
}

/** Matrix total order relationship.
 *
 * OpT must implement a total order on the matrix element type.  operator<=
 * and operator>= on integer and floating-point types are examples.
 */
template<typename LeftT, typename RightT, typename OpT>
inline bool
matrix_total_order(const LeftT& left, const RightT& right, OpT)
{
    /* Shorthand: */
    typedef et::ExprTraits<LeftT> left_traits;
    typedef et::ExprTraits<RightT> right_traits;

    /* matrix_comparison() requires matrix expressions: */
    CML_STATIC_REQUIRE_M(
            (et::MatrixExpressions<LeftT,RightT>::is_true),
            matrix_comparison_expects_matrix_args_error);
    /* Note: parens are required here so that the preprocessor ignores the
     * commas:
     */

    typedef typename et::MatrixPromote<
        typename left_traits::result_type,
        typename right_traits::result_type
    >::type result_type;
    typedef typename result_type::size_tag size_tag;

    /* Verify expression size: */
    matrix_size N = et::CheckedSize(left,right,size_tag());
    for(size_t i = 0; i < N.first; ++ i) {
        for(size_t j = 0; j < N.second; ++ j) {

            /* Test total order: */
            if(OpT().apply(
                        left_traits().get(left,i,j),
                        right_traits().get(right,i,j)
                        ))
            {
                /* Automatically true if weak order (a <= b) && !(b <= a)
                 * <=> (a <= b) && (b > a) <=> (a < b) is satisfied:
                 */
                if(!OpT().apply(
                            right_traits().get(right,i,j),
                            left_traits().get(left,i,j)
                            ))
                    return true;

                /* Otherwise, have equality (a <= b) && (b <= a), so
                 * continue to next element:
                 */
                else
                    continue;

            } else {

                /* Total order isn't satisfied (a > b), so return false: */
                return false;
            }
        }
    }
    /* XXX Can this be unrolled in any reasonable way? */

    /* Total (==) or weak (<) order was satisfied, so return true: */
    return true;
}

}

/* XXX There is a better way to handle these with operator traits... */

CML_MAT_MAT_ORDER(       total_order, operator==, et::OpEqual)
CML_MATXPR_MAT_ORDER(    total_order, operator==, et::OpEqual)
CML_MAT_MATXPR_ORDER(    total_order, operator==, et::OpEqual)
CML_MATXPR_MATXPR_ORDER( total_order, operator==, et::OpEqual)

CML_MAT_MAT_ORDER(       weak_order, operator!=, et::OpNotEqual)
CML_MATXPR_MAT_ORDER(    weak_order, operator!=, et::OpNotEqual)
CML_MAT_MATXPR_ORDER(    weak_order, operator!=, et::OpNotEqual)
CML_MATXPR_MATXPR_ORDER( weak_order, operator!=, et::OpNotEqual)

CML_MAT_MAT_ORDER(       weak_order, operator<, et::OpLess)
CML_MATXPR_MAT_ORDER(    weak_order, operator<, et::OpLess)
CML_MAT_MATXPR_ORDER(    weak_order, operator<, et::OpLess)
CML_MATXPR_MATXPR_ORDER( weak_order, operator<, et::OpLess)

CML_MAT_MAT_ORDER(       weak_order, operator>, et::OpGreater)
CML_MATXPR_MAT_ORDER(    weak_order, operator>, et::OpGreater)
CML_MAT_MATXPR_ORDER(    weak_order, operator>, et::OpGreater)
CML_MATXPR_MATXPR_ORDER( weak_order, operator>, et::OpGreater)

CML_MAT_MAT_ORDER(       total_order, operator<=, et::OpLessEqual)
CML_MATXPR_MAT_ORDER(    total_order, operator<=, et::OpLessEqual)
CML_MAT_MATXPR_ORDER(    total_order, operator<=, et::OpLessEqual)
CML_MATXPR_MATXPR_ORDER( total_order, operator<=, et::OpLessEqual)

CML_MAT_MAT_ORDER(       total_order, operator>=, et::OpGreaterEqual)
CML_MATXPR_MAT_ORDER(    total_order, operator>=, et::OpGreaterEqual)
CML_MAT_MATXPR_ORDER(    total_order, operator>=, et::OpGreaterEqual)
CML_MATXPR_MATXPR_ORDER( total_order, operator>=, et::OpGreaterEqual)

} // namespace cml

#endif

// -------------------------------------------------------------------------
// vim:ft=cpp
