SparseBlockStructure3D createRegularDistribution3D (
        Box3D const& domain, plint numBlocksX, plint numBlocksY, plint numBlocksZ )
{
    SparseBlockStructure3D dataGeometry(domain);
    plint posX = domain.x0;
    for (plint iBlockX=0; iBlockX<numBlocksX; ++iBlockX) {
        plint lx = domain.getNx() / numBlocksX;
        if (iBlockX < domain.getNx()%numBlocksX) ++lx;
        plint posY = domain.y0;
        for (plint iBlockY=0; iBlockY<numBlocksY; ++iBlockY) {
            plint ly = domain.getNy() / numBlocksY;
            if (iBlockY < domain.getNy()%numBlocksY) ++ly;
            plint posZ = domain.z0;
            for (plint iBlockZ=0; iBlockZ<numBlocksZ; ++iBlockZ) {
                plint lz = domain.getNz() / numBlocksZ;
                if (iBlockZ < domain.getNz()%numBlocksZ) ++lz;
                dataGeometry.addBlock (
                        Box3D(posX, posX+lx-1, posY, posY+ly-1, posZ, posZ+lz-1),
                        dataGeometry.nextIncrementalId() );
                posZ += lz;
            }
            posY += ly;
        }
        posX += lx;
    }
    return dataGeometry;
}
SparseBlockStructure3D createRegularDistribution3D (
        Box3D const& domain, plint numProc)
{

    std::vector<plint> repartition = algorithm::evenRepartition(numProc, 3);
    std::vector<plint> newRepartition(3);
    if(domain.getNx()>domain.getNy()) {        // nx>ny
        if(domain.getNx()>domain.getNz()) {      // nx>nz
            newRepartition[0] = repartition[0];
            if (domain.getNy()>domain.getNz()) {   // ny>nz
                newRepartition[1] = repartition[1];
                newRepartition[2] = repartition[2];
            }
            else {                                 // nz>ny
                newRepartition[1] = repartition[2];
                newRepartition[2] = repartition[1];
            }
        }
        else {                                   // nz>nx
            newRepartition[2] = repartition[0];
            newRepartition[1] = repartition[2];
            newRepartition[0] = repartition[1];
        }
    }
    else {                                     // ny>nx
        if (domain.getNy()>domain.getNz()) {     // ny>nz
            newRepartition[1] = repartition[0];
            if (domain.getNx()>domain.getNz()) {   // nx>nz
                newRepartition[0] = repartition[1];
                newRepartition[2] = repartition[2];
            }
            else {                                 // nz>nx
                newRepartition[0] = repartition[2];
                newRepartition[2] = repartition[1];
            }
        }
        else {                                   // nz>ny
            newRepartition[2] = repartition[0];
            newRepartition[1] = repartition[1];
            newRepartition[0] = repartition[2];
        }
    }
    return createRegularDistribution3D (
                 domain,
                 newRepartition[0], newRepartition[1], newRepartition[2] );
}
void MultiContainerBlock3D::allocateBlocks() 
{
    for (pluint iBlock=0; iBlock<this->getLocalInfo().getBlocks().size(); ++iBlock)
    {
        plint blockId = this->getLocalInfo().getBlocks()[iBlock];
        SmartBulk3D bulk(this->getMultiBlockManagement(), blockId);
        Box3D envelope = bulk.computeEnvelope();
        AtomicContainerBlock3D* newBlock =
            new AtomicContainerBlock3D (
                    envelope.getNx(), envelope.getNy(), envelope.getNz() );
        newBlock -> setLocation(Dot3D(envelope.x0, envelope.y0, envelope.z0));
        blocks[blockId] = newBlock;
    }
}
plint Parallelizer3D::computeCost(std::vector<std::vector<Box3D> > const& originalBlocks, Box3D box){
    plint totalCost = 0;
    plint numLevels = originalBlocks.size();
    
    for (plint iLevel=(plint)originalBlocks.size()-1; iLevel>=0; --iLevel){
        // convert the box to the current level
        Box3D levelBox = global::getDefaultMultiScaleManager().scaleBox(box,iLevel-(numLevels-1));
        for (pluint iComp=0; iComp<originalBlocks[iLevel].size(); ++iComp){
            Box3D currentBox;
            if (intersect(originalBlocks[iLevel][iComp], levelBox, currentBox)){
                plint volume = currentBox.getNx()*currentBox.getNy()*currentBox.getNz();
                totalCost += (plint) util::twoToThePower(iLevel) * volume;
            }
        }
    }
    
    return totalCost;
}