void applyProcessingFunctional(DotProcessingFunctional2D* functional,
                               DotList2D const& dotList,
                               std::vector<AtomicBlock2D*> atomicBlocks)
{
    executeDataProcessor( DotProcessorGenerator2D(functional, dotList),
                          atomicBlocks );
}
void executeDataProcessor( ReductiveDataProcessorGenerator3D& generator,
                           MultiBlock3D& object )
{
    std::vector<MultiBlock3D*> objects(1);
    objects[0] = &object;
    executeDataProcessor(generator, objects);
}
void executeDataProcessor( ReductiveDataProcessorGenerator3D& generator,
                           std::vector<MultiGrid3D*> multiGrids,
                           plint referenceLevel )
{
    if(multiGrids.empty()) return;
    plint numLevels = (plint)multiGrids[0]->getNumLevels();
    std::vector<int> dimensionsX, dimensionsT;
    generator.getDimensionsX(dimensionsX);
    generator.getDimensionsT(dimensionsT);
    std::vector<ReductiveDataProcessorGenerator3D*> localGenerators(numLevels);
    std::vector<BlockStatistics*> localStatistics(numLevels);
    for (plint iLevel=0; iLevel<numLevels; ++iLevel) {
        int dxScale = (int)referenceLevel - (int)iLevel;
        int dtScale = dxScale;  // TODO: here, we assume convective scaling; general case could be considered.
        localGenerators[iLevel] = generator.clone();
        
        plint boxRescaleFactor = util::roundToInt(util::twoToThePowerPlint(std::abs(referenceLevel-iLevel)));
        if (dxScale < 0)
            generator.divide(boxRescaleFactor);  
        else
            generator.multiply(boxRescaleFactor);
        std::vector<MultiBlock3D*> localBlocks(multiGrids.size());
        for (plint iBlock=0; iBlock<(plint)localBlocks.size(); ++iBlock) {
            localBlocks[iBlock] = &multiGrids[iBlock]->getComponent(iLevel);
        }
        executeDataProcessor(*localGenerators[iLevel], localBlocks);
        std::vector<double> scales(dimensionsX.size());
        for (pluint iScale=0; iScale<scales.size(); ++iScale) {
            scales[iScale] = scaleToReference(dxScale, dimensionsX[iScale], dtScale, dimensionsT[iScale]);
        }
        localGenerators[iLevel]->getStatistics().rescale(scales);
        localStatistics[iLevel] = &(localGenerators[iLevel]->getStatistics());
    }
    combine(localStatistics, generator.getStatistics());
}
/// Execute a data processor over several MultiGrid3D
void executeDataProcessor( DataProcessorGenerator3D const& generator,
                           std::vector<MultiGrid3D*> multiGrids,
                           plint referenceLevel )
{
    if(multiGrids.empty()) return;
    
    for (plint iLevel=0; iLevel<(plint)multiGrids[0]->getNumLevels(); ++iLevel) {
        std::auto_ptr<DataProcessorGenerator3D> localGenerator(generator.clone());
        int dxScale = (int)referenceLevel - (int)iLevel;
        int dtScale = dxScale;  // TODO: here, we assume convective scaling; general case should be considered.
        
        plint boxRescaleFactor = util::roundToInt(util::twoToThePowerPlint(std::abs(referenceLevel-iLevel)));
        if (dxScale < 0) // if we go to a coarser grid
            localGenerator->divide(boxRescaleFactor);  
        else  // otherwise we go to a finer grid
            localGenerator->multiply(boxRescaleFactor);
        
        localGenerator->setscale(dxScale,dtScale);
        
        std::vector<MultiBlock3D*> localBlocks(multiGrids.size());
        for (plint iBlock=0; iBlock<(plint)localBlocks.size(); ++iBlock) {
            localBlocks[iBlock] =  &multiGrids[iBlock]->getComponent(iLevel);
        }
        executeDataProcessor(*localGenerator, localBlocks);
    }
}
/// Execute a data processor over a single multiGrid3D
void executeDataProcessor( DataProcessorGenerator3D const& generator,
                           MultiGrid3D& object, plint referenceLevel )
{
    std::vector<MultiGrid3D*> blocks;
    blocks.push_back(&object);
    executeDataProcessor(generator,blocks,referenceLevel);
}
void executeDataProcessor( DataProcessorGenerator2D const& generator,
                           MultiBlock2D& object )
{
    std::vector<MultiBlock2D*> objects(1);
    objects[0] = &object;
    executeDataProcessor(generator, objects);
}
void executeDataProcessor( DataProcessorGenerator3D const& generator,
                           MultiBlock3D& object1, MultiBlock3D& object2 )
{
    std::vector<MultiBlock3D*> objects(2);
    objects[0] = &object1;
    objects[1] = &object2;
    executeDataProcessor(generator, objects);
}
void executeDataProcessor( ReductiveDataProcessorGenerator2D& generator,
                           MultiBlock2D& object1, MultiBlock2D& object2 )
{
    std::vector<MultiBlock2D*> objects(2);
    objects[0] = &object1;
    objects[1] = &object2;
    executeDataProcessor(generator, objects);
}
void executeDataProcessor( ReductiveDataProcessorGenerator3D& generator,
                           MultiGrid3D& object1, MultiGrid3D& object2,
                           plint referenceLevel )
{
    std::vector<MultiGrid3D*> blocks;
    blocks.push_back(&object1);
    blocks.push_back(&object2);
    executeDataProcessor(generator,blocks,referenceLevel);
}
void applyProcessingFunctional(ReductiveBoxProcessingFunctional2D& functional,
                               Box2D domain, std::vector<AtomicBlock2D*> atomicBlocks)
{
    // Let the generator get off with a clone of the functional (because the generator
    //   owns its functional, whereas we still need our copy afterwards to get at the
    //   reducted values).
    ReductiveBoxProcessorGenerator2D generator(functional.clone(), domain);
    executeDataProcessor(generator, atomicBlocks);
    // Recover reducted values from the generator's functional.
    functional.getStatistics() = generator.getFunctional().getStatistics();
}
void applyProcessingFunctional(BoundedBoxProcessingFunctional2D* functional,
                               Box2D domain, std::vector<AtomicBlock2D*> atomicBlocks,
                               plint boundaryWidth )
{
    std::vector<BoxProcessorGenerator2D*> generators;
    functional -> getGenerators(domain, boundaryWidth, generators);
    delete functional;
    for (pluint iGen=0; iGen<generators.size(); ++iGen) {
        executeDataProcessor( *generators[iGen], atomicBlocks );
        delete generators[iGen];
    }
}
void applyProcessingFunctional(BoundedReductiveBoxProcessingFunctional2D& functional,
                               Box2D domain, std::vector<AtomicBlock2D*> atomicBlocks,
                               plint boundaryWidth )
{
    std::vector<ReductiveBoxProcessorGenerator2D*> generators;
    functional.getGenerators(domain, boundaryWidth, generators);
    std::vector<BlockStatistics const*> individualStatistics(generators.size());
    for (pluint iGen=0; iGen<generators.size(); ++iGen) {
        executeDataProcessor( *generators[iGen], atomicBlocks );
        individualStatistics[iGen] = &(generators[iGen]->getStatistics());
    }
    SerialCombinedStatistics().combine(individualStatistics, functional.getStatistics());
    for (pluint iGen=0; iGen<generators.size(); ++iGen) {
        delete generators[iGen];
    }
}
void applyProcessingFunctional(BoxProcessingFunctional2D* functional,
                               Box2D domain, std::vector<AtomicBlock2D*> atomicBlocks)
{
    executeDataProcessor( BoxProcessorGenerator2D(functional, domain),
                          atomicBlocks );
}