#include "UtsusemiCalcContainers.hh"
//////////////////////////////////////////////////////////////////////
UtsusemiCalcContainers::
UtsusemiCalcContainers(){
    _NumOfMulTh = UtsusemiGetNumOfMulTh();
    st = new StringTools();
}

//////////////////////////////////////////////////////////////////////
UtsusemiCalcContainers::
~UtsusemiCalcContainers(){
    delete st;
}

//////////////////////////////////////////////////////////////////////
UInt4 UtsusemiCalcContainers::
CheckOperator( std::string ope ){
    if (ope=="+") return 1;
    if (ope=="-") return 2;
    if (ope=="*") return 3;
    if (ope=="/") return 4;
    return 0;
}
//////////////////////////////////////////////////////////////////////
ElementContainer UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainer* ec1, std::string ope,
                Double coef2, ElementContainer* ec2 ){
    ElementContainer ret;
    UInt4 i_ope = CheckOperator( ope );

    ElementContainer EC1 = ec1->Mul(coef1);
    ElementContainer EC2 = ec2->Mul(coef2);
    switch( i_ope ){
    case 1:
        ret = EC1 + EC2;
        break;
    case 2:
        ret = EC1 - EC2;
        break;
    case 3:
        ret = EC1 * EC2;
        break;
    case 4:
        ret = EC1 / EC2;
        break;

    default:
        UtsusemiError("CalcContainers >> operator is invalid.");
    }

    return ret;
}
//////////////////////////////////////////////////////////////////////
ElementContainerArray UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainerArray* eca1, std::string ope,
                Double coef2, ElementContainerArray* eca2 ){
    ElementContainerArray ret;
    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return ret;
    }
    if ( eca1->PutSize()!=eca2->PutSize() ){
        UtsusemiError("CalcContainers >> The Number of Containers in Arguments are different.");
        return ret;
    }
    for (UInt4 i=0;i<eca1->PutSize();i++){
        ElementContainer ec1 = eca1->Put(i);
        ElementContainer ec2 = eca2->Put(i);
        ec1.MulMySelf( coef1 );
        ec2.MulMySelf( coef2 );

        switch( i_ope ){
        case 1:
            ret.Add( ec1+ec2 );
            break;
        case 2:
            ret.Add( ec1-ec2 );
            break;
        case 3:
            ret.Add( ec1*ec2 );
            break;
        case 4:
            ret.Add( ec1/ec2 );
            break;
        }
    }
    return ret;
}

//////////////////////////////////////////////////////////////////////
ElementContainerArray UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainerArray* eca, std::string ope,
                Double coef2, ElementContainer* ec ){
    ElementContainerArray ret;
    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return ret;
    }

    ElementContainer ec2 = ec->MulMySelf( coef2 );

    for (UInt4 i=0;i<eca->PutSize();i++){
        ElementContainer ec1 = eca->Put(i);
        ec1.MulMySelf( coef1 );

        switch( i_ope ){
        case 1:
            ret.Add( ec1+ec2 );
            break;
        case 2:
            ret.Add( ec1-ec2 );
            break;
        case 3:
            ret.Add( ec1*ec2 );
            break;
        case 4:
            ret.Add( ec1/ec2 );
            break;
        }
    }
    return ret;
}

//////////////////////////////////////////////////////////////////////
ElementContainerMatrix UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainerMatrix* ecm1, std::string ope,
                Double coef2, ElementContainerMatrix* ecm2 ){
    ElementContainerMatrix ret;

    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return ret;
    }

    if (ecm1->PutSize()!=ecm2->PutSize()){
        UtsusemiError("CalcContainers >> The Number of Containers in Arguments are different.");
        return ret;
    }

    for (UInt4 i=0;i<ecm1->PutSize();i++){
        ElementContainerArray* eca1 = ecm1->PutPointer(i);
        ElementContainerArray* eca2 = ecm2->PutPointer(i);

        ElementContainerArray eca = CalcContainers( coef1, eca1, ope, coef2, eca2 );

        ret.Add( eca );
    }

    return ret;
}

//////////////////////////////////////////////////////////////////////
ElementContainerMatrix UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainerMatrix* ecm, std::string ope,
                Double coef2, ElementContainerArray* eca ){

    ElementContainerMatrix ret;

    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return ret;
    }

    if (ecm->PutSize()!=eca->PutSize()){
        UtsusemiError("CalcContainers >> The Number of Containers in Arguments are different.");
        return ret;
    }

    for (UInt4 i=0;i<ecm->PutSize();i++){
        ElementContainerArray* eca1 = ecm->PutPointer(i);
        ElementContainer* ec1 = eca->PutPointer(i);

        ElementContainerArray eca = CalcContainers( coef1, eca1, ope, coef2, ec1 );

        ret.Add( eca );
    }

    return ret;
}

//////////////////////////////////////////////////////////////////////
ElementContainerMatrix UtsusemiCalcContainers::
CalcContainers( Double coef1, ElementContainerMatrix* ecm, std::string ope,
                Double coef2, ElementContainer* ec ){

    ElementContainerMatrix ret;

    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return ret;
    }

    for (UInt4 i=0;i<ecm->PutSize();i++){
        ElementContainerArray* eca1 = ecm->PutPointer(i);

        ElementContainerArray eca = CalcContainers( coef1, eca1, ope, coef2, ec );

        ret.Add( eca );
    }

    return ret;
}
//////////////////////////////////////////////////////////////////////
bool UtsusemiCalcContainers::
CalcContainers( ElementContainerMatrix* ret_ecm,
                Double coef1, ElementContainerMatrix* ecm1, std::string ope,
                Double coef2, ElementContainerMatrix* ecm2 ){

    if (ecm1->PutSize()!=ecm2->PutSize()){
        UtsusemiError("CalcContainers >> ECM sizes are dirrerent");
        return false;
    }

    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return false;
    }

    UInt4 ecm_size=ecm1->PutSize();
    ret_ecm->Allocate(ecm_size);

#ifdef MULTH
    omp_set_num_threads( _NumOfMulTh );
#endif

    for (UInt4 i=0; i<ecm_size; i++){
        if (((*ecm1)(i)->PutSize())!=((*ecm2)(i)->PutSize())){
            UtsusemiError("CalcContainers >> ECM("+st->UInt4ToString(i)+") sizes are different");
            return false;
        }
        UInt4 eca_size=(*ecm1)(i)->PutSize();
        ElementContainerArray* ret_eca = new ElementContainerArray((*ecm1)(i)->PutHeader());
        ret_eca->Allocate(eca_size);

        for( UInt4 j=0; j<eca_size; j++ ){
            ElementContainer* ec1 = (*ecm1)(i,j);
            ElementContainer* ec2 = (*ecm2)(i,j);
            std::vector<Double>* x_ec1 = (*ec1)( ec1->PutXKey() );
            std::vector<Double>* x_ec2 = (*ec2)( ec2->PutXKey() );
            if ((x_ec1->size())!=(x_ec2->size())){
                UtsusemiError("CalcContainers >> ECM("+st->UInt4ToString(i)+","+st->UInt4ToString(j)+") sizes are different");
                return false;
            }

            std::vector<Double>* y_ec1 = (*ec1)( ec1->PutYKey() );
            std::vector<Double>* y_ec2 = (*ec2)( ec2->PutYKey() );
            std::vector<Double>* e_ec1 = (*ec1)( ec1->PutEKey() );
            std::vector<Double>* e_ec2 = (*ec2)( ec2->PutEKey() );

            ElementContainer* ret_ec = new ElementContainer( ec1->PutHeader() );
            std::vector<Double> ret_x( x_ec1->size(),0 );
            copy( x_ec1->begin(), x_ec1->end(), ret_x.begin() );

            std::vector<Double> ret_y( x_ec1->size()-1,0 );
            std::vector<Double> ret_e( x_ec1->size()-1,0 );

            std::string xkey=ec1->PutXKey();
            std::string ykey=ec1->PutYKey();
            std::string ekey=ec1->PutEKey();
            ret_ec->Add( xkey, ret_x, ec1->PutUnit(xkey) );
            ret_ec->Add( ykey, ret_y, ec1->PutUnit(ykey) );
            ret_ec->Add( ekey, ret_e, ec1->PutUnit(ekey) );
            ret_ec->SetKeys( xkey, ykey, ekey );

            std::vector<std::string> key_list = ec1->PutKeys();
            for (UInt4 l=0; l<key_list.size(); l++){
                std::string a_key = key_list[l];
                if ((a_key!=xkey)&&(a_key!=ykey)&&(a_key!=ekey)){
                    std::vector<Double> tmp_v = ec1->Put( a_key );
                    std::string tmp_u = ec1->PutUnit( a_key );
                    ret_ec->Add( a_key, tmp_v, tmp_u );
                }
            }

            std::vector<double>* yy=(*ret_ec)( ykey );
            std::vector<double>* ee=(*ret_ec)( ekey );

            switch( i_ope ){
            case 1:
                for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                    yy->at(k) = (y_ec1->at(k))*coef1 + (y_ec2->at(k))*coef2;
                    ee->at(k) = sqrt( ((e_ec1->at(k))*coef1)*((e_ec1->at(k))*coef1) + ((e_ec2->at(k))*coef2)*((e_ec2->at(k))*coef2) );
                    if ((e_ec1->at(k) < 0.0) || (e_ec2->at(k) < 0.0)) ee->at(k) *= -1.0;
                }
                break;
            case 2:
                for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                    yy->at(k) = (y_ec1->at(k))*coef1 - (y_ec2->at(k))*coef2;
                    ee->at(k) = sqrt( ((e_ec1->at(k))*coef1)*((e_ec1->at(k))*coef1) + ((e_ec2->at(k))*coef2)*((e_ec2->at(k))*coef2) );
                    if ((e_ec1->at(k) < 0.0) || (e_ec2->at(k) < 0.0)) ee->at(k) *= -1.0;
                }
                break;
            case 3:
                for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                    double ly = (y_ec1->at(k))*coef1;
                    double ry = (y_ec2->at(k))*coef2;
                    double le = (e_ec1->at(k))*coef1;
                    double re = (e_ec2->at(k))*coef2;
                    yy->at(k) = ly * ry;
                    ee->at(k) = sqrt( (ly*re)*(ly*re) + (ry*le)*(ry*le) );
                    if ((le < 0.0) || (re < 0.0)) ee->at(k) *= -1.0;
                }
                break;
            case 4:
                for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                    double ly = (y_ec1->at(k))*coef1;
                    double ry = (y_ec2->at(k))*coef2;
                    double le = (e_ec1->at(k))*coef1;
                    double re = (e_ec2->at(k))*coef2;
                    if (ry==0.0){
                        yy->at(k) = 0.0;
                        ee->at(k) = -1.0;
                    }else{
                        yy->at(k) = ly / ry;
                        ee->at(k) = (1.0/ry/ry)*sqrt( (ly*re)*(ly*re) + (ry*le)*(ry*le) );
                        if ((le < 0.0) || (re < 0.0)) ee->at(k) *= -1.0;
                    }
                }
                break;

            default:
                UtsusemiError("CalcContainers >> operator is invalid.");
            }

            ret_eca->Set(j, ret_ec);
        }
        ret_ecm->Set(i, ret_eca);
    }

    return true;
}
//////////////////////////////////////////////////////////////////////
bool UtsusemiCalcContainers::
CalcContainers( ElementContainerMatrix* ret_ecm,
                Double coef1, ElementContainerMatrix* ecm1, Double pow1, std::string ope,
                Double coef2, ElementContainerMatrix* ecm2, Double pow2 ){
    if ((pow1 <= 0.0) && (pow2 <= 0.0)){
        UtsusemiError("CalcContainers >> argument pow1 and pow2 must be over 0.0.");
        return false;
    }
    ElementContainerMatrix* ecm1p = new ElementContainerMatrix( ecm1 -> Pow(pow1) );
    ElementContainerMatrix* ecm2p = new ElementContainerMatrix( ecm2 -> Pow(pow2) );
    bool ret = CalcContainers(ret_ecm, coef1, ecm1p, ope, coef2, ecm2p);
    delete ecm1p;
    delete ecm2p;
    return ret;
}

//////////////////////////////////////////////////////////////////////
bool UtsusemiCalcContainers::
CalcContainers( ElementContainerArray* ret_eca,
                Double coef1, ElementContainerArray* eca1, std::string ope,
                Double coef2, ElementContainerArray* eca2 ){

    if ((eca1->PutSize())!=(eca2->PutSize())){
        UtsusemiError("CalcContainers >> ECA sizes are different");
        return false;
    }
    UInt4 i_ope = CheckOperator( ope );
    if (i_ope==0){
        UtsusemiError("CalcContainers >> operator is invalid.");
        return false;
    }

    UInt4 eca_size=eca1->PutSize();

    ret_eca->Allocate(eca_size);

    for( UInt4 i=0; i<eca_size; i++ ){
        ElementContainer* ec1 = (*eca1)(i);
        ElementContainer* ec2 = (*eca2)(i);
        std::vector<Double>* x_ec1 = (*ec1)( ec1->PutXKey() );
        std::vector<Double>* x_ec2 = (*ec2)( ec2->PutXKey() );
        if ((x_ec1->size())!=(x_ec2->size())){
            UtsusemiError("CalcContainers >> ECA("+st->UInt4ToString(i)+") sizes are different");
            return false;
        }

        std::vector<Double>* y_ec1 = (*ec1)( ec1->PutYKey() );
        std::vector<Double>* y_ec2 = (*ec2)( ec2->PutYKey() );
        std::vector<Double>* e_ec1 = (*ec1)( ec1->PutEKey() );
        std::vector<Double>* e_ec2 = (*ec2)( ec2->PutEKey() );

        ElementContainer* ret_ec = new ElementContainer( ec1->PutHeader() );
        std::vector<Double> ret_x( x_ec1->size(),0 );
        copy( x_ec1->begin(), x_ec1->end(), ret_x.begin() );

        std::vector<Double> ret_y( x_ec1->size()-1,0 );
        std::vector<Double> ret_e( x_ec1->size()-1,0 );

        std::string xkey=ec1->PutXKey();
        std::string ykey=ec1->PutYKey();
        std::string ekey=ec1->PutEKey();
        ret_ec->Add( xkey, ret_x, ec1->PutUnit(xkey) );
        ret_ec->Add( ykey, ret_y, ec1->PutUnit(ykey) );
        ret_ec->Add( ekey, ret_e, ec1->PutUnit(ekey) );
        ret_ec->SetKeys( xkey, ykey, ekey );

        std::vector<double>* yy=(*ret_ec)( ykey );
        std::vector<double>* ee=(*ret_ec)( ekey );

        switch( i_ope ){
        case 1:
            for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                yy->at(k) = (y_ec1->at(k))*coef1 + (y_ec2->at(k))*coef2;
                ee->at(k) = sqrt( ((e_ec1->at(k))*coef1)*((e_ec1->at(k))*coef1) + ((e_ec2->at(k))*coef2)*((e_ec2->at(k))*coef2) );
                if ((e_ec1->at(k) < 0.0) || (e_ec2->at(k) < 0.0)) ee->at(k) *= -1.0;
            }
            break;
        case 2:
            for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                yy->at(k) = (y_ec1->at(k))*coef1 - (y_ec2->at(k))*coef2;
                ee->at(k) = sqrt( ((e_ec1->at(k))*coef1)*((e_ec1->at(k))*coef1) + ((e_ec2->at(k))*coef2)*((e_ec2->at(k))*coef2) );
                if ((e_ec1->at(k) < 0.0) || (e_ec2->at(k) < 0.0)) ee->at(k) *= -1.0;
            }
            break;
        case 3:
            for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                double ly = (y_ec1->at(k))*coef1;
                double ry = (y_ec2->at(k))*coef2;
                double le = (e_ec1->at(k))*coef1;
                double re = (e_ec2->at(k))*coef2;
                yy->at(k) = ly * ry;
                ee->at(k) = sqrt( (ly*re)*(ly*re) + (ry*le)*(ry*le) );
                if ((le < 0.0) || (re < 0.0)) ee->at(k) *= -1.0;
            }
            break;
        case 4:
            for (UInt4 k=0;k<(x_ec1->size()-1);k++){
                double ly = (y_ec1->at(k))*coef1;
                double ry = (y_ec2->at(k))*coef2;
                double le = (e_ec1->at(k))*coef1;
                double re = (e_ec2->at(k))*coef2;
                if (ry==0.0){
                    yy->at(k) = 0.0;
                    ee->at(k) = -1.0;
                }else{
                    yy->at(k) = ly / ry;
                    ee->at(k) = (1.0/ry/ry)*sqrt( (ly*re)*(ly*re) + (ry*le)*(ry*le) );
                    if ((le < 0.0) || (re < 0.0)) ee->at(k) *= -1.0;
                }
            }
            break;

        default:
            UtsusemiError("CalcContainers >> operator is invalid.");
        }

        ret_eca->Set(i, ret_ec);
    }

    return true;
}
//////////////////////////////////////////////////////////////////////
bool UtsusemiCalcContainers::
CalcContainers( ElementContainerArray* ret_eca,
                Double coef1, ElementContainerArray* eca1, Double pow1, std::string ope,
                Double coef2, ElementContainerArray* eca2, Double pow2 ){
    if ((pow1 <= 0.0) && (pow2 <= 0.0)){
        UtsusemiError("CalcContainers >> argument pow1 and pow2 must be over 0.0.");
        return false;
    }
    ElementContainerArray* eca1p = new ElementContainerArray( eca1 -> Pow(pow1) );
    ElementContainerArray* eca2p = new ElementContainerArray( eca2 -> Pow(pow2) );
    bool ret = CalcContainers(ret_eca, coef1, eca1p, ope, coef2, eca2p);
    delete eca1p;
    delete eca2p;
    return ret;
}
