#ifndef ARRAYOPERATION
#define ARRAYOPERATION

#include "ElementContainerMatrix.hh"
#include "MlfHeader.hh"
#include <cmath>
#include <limits>

////////////////////////////////////////
// ArrayOperation
////////////////////////////////////////

//! Class to perform array oeprations
/*! For example
    Calculate(ECA, EC, "+") carries out ECA(i) + EC for each index.
    Calculate(ECM, std::vector< std::vector<Double> > , "/" ) carries out ECM(i, j) / V[i][j] for each index.

    Common flow of "Calculate" method
    1. Validate operator
    2. Compare size of operands
    3. Check the keys of ElementContainer is set
    4. Performe calculation

    "Calculate" returns true, if calculation successfully ends.
    It returns false, if either validation fails.
*/
class ArrayOperation
{
private:
    bool ValidateKey(const ElementContainer& ec) const;
    //!< Check keys of an ElementContainer
    /*!< Return true, if it is set keys.
     *
     *   @param ec (ElementContainer)
     *   @retval true: Keys are set
     */

    bool ValidateKey(const ElementContainerArray& eca, bool isLeftOperand=true) const;
    //!< Check keys of an ElementContainerArray
    bool ValidateKey(const ElementContainerMatrix& ecm, bool isLeftOperand=true) const;
    //!< Check keys of an ElementContainerArray

    bool ValidateOperator(std::string op) const;
    //!< Check std::string of operator
    /*!< Return true, if it is registerd as operators.
     */

    void TakeoverMask(ElementContainer& ec_l, ElementContainer& ec_r);
    //!< Takeover mask value by ORing
    void TakeoverMask(ElementContainerArray& eca_l, ElementContainerArray& eca_r);
    //!< Takeover mask value by ORing
    void TakeoverMask(ElementContainerMatrix& ecm_l, ElementContainerMatrix& ecm_r);
    //!< Takeover mask value by ORing

protected:
    std::string message_tag;
    std::vector<std::string> v_op; // Registered operators
    void _Calculate(ElementContainerArray& eca, std::string op, const Double d);
    void _Calculate(ElementContainerArray& eca, std::string op, const std::vector<Double>& vd);
    void _Calculate(ElementContainerArray& eca, std::string op, ElementContainer& ec);
    void _Calculate(ElementContainerArray& eca_l, std::string op, ElementContainerArray& eca_r);

public:
    ArrayOperation();

    bool Calculate(ElementContainerArray& eca_l, std::string op, const Double d);
    //!< Apply an operation to each EC with a right operand
    bool Calculate(ElementContainerArray& eca_l, std::string op, const std::vector<Double>& vd);
    //!< Apply an operation to each EC with a same index element of a right operand
    bool Calculate(ElementContainerArray& eca_l, std::string op, ElementContainer& ec);
    //!< Apply an operation to each EC with a right operand
    bool Calculate(ElementContainerArray& eca_l, std::string op, ElementContainerArray& eca_r);
    //!< Apply an operation to each EC with a same index element of a right operand
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, const Double d);
    //!< Apply an operation to each EC with a right operand
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, const std::vector<Double>& vd, bool direction=true);
    //!< Apply an operation to each EC with a same index element of a right operand
    /*!< Calculate(ECM, vd, "+", true) calclulates ECM(j, i) + vd[i] for each index.
     *   Calculate(ECM, vd, "+", false) calclulates ECM(i, j) + vd[i] for each index.
     */
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, ElementContainer& ec);
    //!< Apply an operation to each EC with a right operand
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, ElementContainerArray& eca_r, bool direction=true);
    //!< Apply an operation to each EC with a same index element of a right operand
    /*!< Calculate(ECM, ECA, "+", true) calclulates ECM(j, i) + ECA(i) for each index.
     *   Calculate(ECM, ECA, "+", false) calclulates ECM(i, j) + ECA(i) for each index.
     */
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, ElementContainerMatrix& ecm_r);
    //!< Apply an operation to each EC with a same index element of a right operand
    bool Calculate(ElementContainerMatrix& ecm_l, std::string op, const std::vector< std::vector<Double> >& vvd);
    //!< Apply an operation to each EC with a same index element of a right operand

};
#endif
