// FunctionID 4: PCHfun
void PCHfun(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
//declare variables
    double *U, *V, *theta, *u, *Rotation;
    double *Family;
    int family, rotation;
    unsigned int n;
    
//figure out dimensions
    n = (unsigned int)mxGetM(prhs[2]);
    
//associate inputs
    Family = mxGetPr(prhs[1]);
    family = (int) *Family;
    theta = mxGetPr(prhs[4]);
    
//associate outputs
    plhs[0] = mxCreateDoubleMatrix(n,1,mxREAL);
    u = mxGetPr(plhs[0]);
    
    if (nrhs==6 && !mxIsEmpty(prhs[5]))
    {
        mxArray *U_in, *V_in;
        U_in = mxDuplicateArray(prhs[2]);
        V_in = mxDuplicateArray(prhs[3]);
        U = mxGetPr(U_in);
        V = mxGetPr(V_in);
        Rotation = mxGetPr(prhs[5]);
        rotation = (int) *Rotation;
        
        PairCopulaHfun(family,rotation,theta,U,V,u,n);
        
    }
    else
    {
        U = mxGetPr(prhs[2]);
        V = mxGetPr(prhs[3]);
        
        PairCopulaHfun(family,theta,U,V,u,n);
    }
    
    
    return;
    
}
double PairCopulaHfun(int family, const double theta0, const double theta1, double U, double V)
{
    double u;
    std::vector<double> theta(2);
    theta[0] = theta0;
    theta[1] = theta1;
    PairCopulaHfun(family, &theta[0], &U, &V, &u, 1);
    return u;
}
void VineCopulaStructureSelect(double *bounds, int type, double *Structure, double *Families, double *Rotations, std::vector<double>& Thetas, double *U, int d, unsigned int n, int StructuringRule, double *familyset, int m)
{
    std::vector<int> families((d-1)*d/2);
    std::vector<int> rotations((d-1)*d/2);
    std::vector<double> thetas;
    
    int i,j,k,I;
    
    switch (type)
    {
        case 0: // C-Vine
        {
            std::vector<int> structure(d);
            
            for (i=0;i<d;i++)
            {
                structure[i] = i;
            }
            
            switch (StructuringRule)
            {
                case 0: // Maximizing the sum of absolute Kendall's tau in every tree
                {
                    I = UpdateStructure(structure, 0, U, d, d, n);
                    
                    std::vector<double> V(n*d);
                    
                    for (i=0;i<(int)n;i++)
                    {
                        V[i] = U[I*n+i];
                    }
                    
                    for (j=0;j<I;j++)
                    {
                        for (i=0;i<(int)n;i++)
                        {
                            V[(j+1)*n+i] = U[j*n+i];
                        }
                    }
                    
                    for (j=I+1;j<d;j++)
                    {
                        for (i=0;i<(int)n;i++)
                        {
                            V[j*n+i] = U[j*n+i];
                        }
                    }
                    
                    TreeSelect(bounds,type, &families[0], thetas, &rotations[0], &V[0], d, n, familyset, m);
                    
                    GetPseudoObs(&families[0], thetas, &rotations[0], &V[0], d, n);
                    
                    for (k=1;k<d-2;k++)
                    {
                        I = UpdateStructure(structure, 0, &V[n], d, d-k, n);
                        
                        for (i=0;i<(int)n;i++)
                        {
                            V[i] = V[(I+1)*n+i];
                        }
                        
                        for (j=I+1;j<d-k;j++)
                        {
                            for (i=0;i<(int)n;i++)
                            {
                                V[j*n+i] = V[(j+1)*n+i];
                            }
                        }
                        
                        TreeSelect(bounds,type, &families[d*k-k*(k+1)/2], thetas, &rotations[d*k-k*(k+1)/2], &V[0], d-k, n, familyset, m);
                        
                        GetPseudoObs(&families[d*k-k*(k+1)/2], thetas, &rotations[d*k-k*(k+1)/2], &V[0], d-k, n);
                    }
                    
                    TreeSelect(bounds,type, &families[d*(d-1)/2-1], thetas, &rotations[d*(d-1)/2-1], &V[n], 2, n, familyset, m);
                    break;
                }
// case 2: Choose the most dependent variable as the root node and list the other variables by their dependence to the root node in increasing order (Nikoloulopoulos et al. 2012, p.3665)
// case 3: Choose the most dependent variable as the root node and list the other variables sequentially by choosing the variable which is least dependent with the previously selcted one (Nikoloulopoulos et al. 2012, p.3665)
// case 1: Choose the most dependent variable as the root node and list the other variables by their dependence to the root node in decreasing order (Nikoloulopoulos et al. 2012, p.3665)
                default:
                {
                    I = UpdateStructure(structure, StructuringRule, U, d, d, n);
                    
                    std::vector<double> V(n*d);
                    
                    for (j=0;j<d;j++)
                    {
                        for (i=0;i<(int)n;i++)
                        {
                            V[j*n+i] = U[structure[j]*n+i];
                        }
                    }
                    
                    TreeSelect(bounds,type, &families[0], thetas, &rotations[0], &V[0], d, n, familyset, m);
                    
                    GetPseudoObs(&families[0], thetas, &rotations[0], &V[0], d, n);
                    
                    
                    for (k=1;k<d-2;k++)
                    {
                        TreeSelect(bounds,type, &families[d*k-k*(k+1)/2], thetas, &rotations[d*k-k*(k+1)/2], &V[k*n], d-k, n, familyset, m);
                        
                        GetPseudoObs(&families[d*k-k*(k+1)/2], thetas, &rotations[d*k-k*(k+1)/2], &V[k*n], d-k, n);
                    }
                    
                    TreeSelect(bounds,type, &families[d*(d-1)/2-1], thetas, &rotations[d*(d-1)/2-1], &V[(d-2)*n], 2, n, familyset, m);
                }
            }
            
            
            Thetas.resize(thetas.size());
            
            switch (StructuringRule)
            {
                case 0:
                {
                    std::vector<int> NumbParams((d-1)*d/2+1);
                    int J=0;
                    
                    for (i=0;i<(d-1)*d/2;i++)
                    {
                        switch((int) families[i]){
                            case 0:
                            {
                                NumbParams[i+1] = NumbParams[i];
                                break;
                            }
                            case 18:
                            {
                                NumbParams[i+1] = NumbParams[i] +3;
                                break;
                            }
                            case 3: case 4: case 5: case 6: case 12: case 16: case 17: case 19:
                            {
                                NumbParams[i+1] = NumbParams[i] +2;
                                break;
                            }
                            default:
                            {
                                NumbParams[i+1] = NumbParams[i] +1;
                            }
                            
                        }
                    }
                    std::vector<std::pair<int, int> > R(d-1);
                    std::vector<int> Ranks(d-1);
                    
                    for (k=0;k<d-1;k++)
                    {
                        // Computing ranks
                        for (i=0;i<d-k-1;i++)
                        {
                            R[i] = std::make_pair(structure[i+1+k],i);
                        }
                        
                        std::sort(&R[0],&R[d-k-1]);
                        
                        for (i=0;i<d-k-1;i++)
                        {
                            Ranks[R[i].second] = i;
                        }
                        
                        for (i=0;i<d-k-1;i++)
                        {
                            Families[d*k-k*(k+1)/2+i] = (double) families[d*k-k*(k+1)/2+Ranks[i]];
                            Rotations[d*k-k*(k+1)/2+i] = (double) rotations[d*k-k*(k+1)/2+Ranks[i]];
                            switch(NumbParams[d*k-k*(k+1)/2+Ranks[i]+1]-NumbParams[d*k-k*(k+1)/2+Ranks[i]]){
                                case 0:
                                {
                                    break;
                                }
                                case 1:
                                {
                                    Thetas[J] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]];
                                    J++;
                                    break;
                                }
                                case 2:
                                {
                                    Thetas[J] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]];
                                    Thetas[J+1] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]+1];
                                    J = J+2;
                                    break;
                                }
                                default:
                                {
                                    Thetas[J] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]];
                                    Thetas[J+1] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]+1];
                                    Thetas[J+2] = thetas[NumbParams[d*k-k*(k+1)/2+Ranks[i]]+2];
                                    J = J+3;
                                }
                            }
                        }
                    }
                    break;
                }
                default:
                {
                    for (i=0;i<(d-1)*d/2;i++)
                    {
                        Families[i] = (double) families[i];
                        Rotations[i] = (double) rotations[i];
                    }
                    
                    for (i=0;i<(int) thetas.size();i++)
                    {
                        Thetas[i] = thetas[i];
                    }
                    
                }
            }
            
            for (i=0;i<d;i++)
            {
                Structure[i] = (double) structure[i];
            }
            break;
        }
        case 1: // D-Vine
        {
            std::vector<double> V((d-2)*n);
            std::vector<double> H((d-2)*n);
            std::vector<double> U1(n),V1(n);
            
            TreeSelect(bounds,type, &families[0], thetas, &rotations[0], &U[0], d, n, familyset, m);
            
            int J =0;
            
            for (i=0;i<d-1;i++)
            {
                if (rotations[i]>0)
                {
                    Rotate_Obs(&U[i*n],&U[(i+1)*n],&U1[0],&V1[0],rotations[i],n);
                    if (i<d-2) {
                        PairCopulaHfun_Rotated_Obs(families[i], rotations[i], &thetas[J], &U1[0], &V1[0], &H[i*n], n);
                    }
                    if (i>0) {
                        PairCopulaVfun_Rotated_Obs(families[i], rotations[i], &thetas[J], &U1[0], &V1[0], &V[(i-1)*n], n);
                    }
                }
                else
                {
                    if (i<d-2) {
                        PairCopulaHfun(families[i], &thetas[J], &U[i*n], &U[(i+1)*n], &H[i*n], n);
                    }
                    if (i>0) {
                        PairCopulaVfun(families[i], &thetas[J], &U[i*n], &U[(i+1)*n], &V[(i-1)*n], n);
                    }
                }
                switch(families[i]){
                    case 0:
                    {
                        break;
                    }
                    case 18:
                    {
                        J += 3;
                        break;
                    }
                    case 3: case 4: case 5: case 6: case 12: case 16: case 17: case 19:
                    {
                        J += 2;
                        break;
                    }
                    default:
                    {
                        J += 1;
                    }
                }
            }

            
            for (k=1;k<d-2;k++)
            {
                TreeSelect(bounds,type, &families[d*k-k*(k+1)/2], thetas, &rotations[d*k-k*(k+1)/2], &H[0], &V[0], d-k, n, familyset, m);
                
                for (i=0;i<d-k-1;i++)
                {
                    if (rotations[d*k-k*(k+1)/2+i]>0)
                    {
                        Rotate_Obs(&H[i*n],&V[i*n],&U1[0],&V1[0],rotations[d*k-k*(k+1)/2+i],n);
                        if (i>0) {
                            PairCopulaVfun_Rotated_Obs(families[d*k-k*(k+1)/2+i], rotations[d*k-k*(k+1)/2+i], &thetas[J], &U1[0], &V1[0], &V[(i-1)*n], n);
                        }
                        if (i<d-k-2) {
                            PairCopulaHfun_Rotated_Obs(families[d*k-k*(k+1)/2+i], rotations[d*k-k*(k+1)/2+i], &thetas[J], &U1[0], &V1[0], &H[i*n], n);
                        }
                    }
                    else
                    {
                        if (i>0) {
                            PairCopulaVfun(families[d*k-k*(k+1)/2+i], &thetas[J], &H[i*n], &V[i*n], &V[(i-1)*n], n);
                        }
                        if (i<d-k-2) {
                            PairCopulaHfun(families[d*k-k*(k+1)/2+i], &thetas[J], &H[i*n], &V[i*n], &H[i*n], n);
                        }
                    }
                    switch(families[d*k-k*(k+1)/2+i]){
                        case 0:
                        {
                            break;
                        }
                        case 18:
                        {
                            J += 3;
                            break;
                        }
                        case 3: case 4: case 5: case 6: case 12: case 16: case 17: case 19:
                        {
                            J += 2;
                            break;
                        }
                        default:
                        {
                            J += 1;
                        }
                    }
                }
            }

            TreeSelect(bounds,type, &families[d*(d-1)/2-1], thetas, &rotations[d*(d-1)/2-1], &H[0], &V[0], 2, n, familyset, m);
            
            Thetas.resize(thetas.size());
            
            for (i=0;i<(d-1)*d/2;i++)
            {
                Families[i] = (double) families[i];
                Rotations[i] = (double) rotations[i];
            }
            
            for (i=0;i<(int) thetas.size();i++)
            {
                Thetas[i] = thetas[i];
            }
        }
    }
    
    return;
}
    T operator()(const T& x)
	{
        return PairCopulaHfun(family,theta0,theta1,theta2,x,V) - U;
    }
double PairCopulaHfun(int family, const double theta, double U, double V)
{
    double u;
    PairCopulaHfun(family, &theta, &U, &V, &u, 1);
    return u;
}