hkBattousai
hkBattousai

Reputation: 10931

Changing the nested for loop order dynamically

I have a class which represents a special file format. It stores discretized data of a 3D space. I want to print the data on the screen. But the problem is, I have to print the data in the given arbitrary order.

My class is something like this:

class MyFile
{
    public:
        // ...
        enum SEQUENCE
        {
            SEQ_I       = 200,
            SEQ_J       = 201,
            SEQ_K       = 202
        };
        enum INCREMENT
        {
            PLUS        = 300,
            MINUS       = 301
        };
        double GetVariable(uint64_t i, uint64_t j, uint64_t k) const;
        std::vector<SEQUENCE>  Sequences;
        std::vector<INCREMENT> Increments;
        friend std::ostream & operator<<(std::ostream & os, const MyFile & mf);
        // ...
    private:
        // ...
        std::vector<double> Data;
        // ...
}

I use it like this:

MyFile myfile;
// ...
// Load some data to "myfile".
// ...
myfile.Sequences.push_back(MyFile::SEQUENCE::SEQ_J);
myfile.Sequences.push_back(MyFile::SEQUENCE::SEQ_I);
myfile.Sequences.push_back(MyFile::SEQUENCE::SEQ_K);
myfile.Increments.push_back(MyFile::INCREMENT::MINUS);
myfile.Increments.push_back(MyFile::INCREMENT::MINUS);
myfile.Increments.push_back(MyFile::INCREMENT::PLUS);
std::cout << myfile;

And the mentioned for loop is in this method:

std::ostream & operator<<(std::ostream & os, const MyFile & mf)
{
    // The loop variables.
    uint64_t    InnerMin            = 0;
    uint64_t    MiddlMin            = 0;    // I spelled it "middl" to make
    uint64_t    OuterMin            = 0;    // the all variable names same length.
    uint64_t    InnerMax            = 0;
    uint64_t    MiddlMax            = 0;
    uint64_t    OuterMax            = 0;
    uint64_t    InnerIndex          = 0;
    uint64_t    MiddlIndex          = 0;
    uint64_t    OuterIndex          = 0;
    uint64_t    *i                  = &InnerIndex;
    uint64_t    *j                  = &MiddlIndex;
    uint64_t    *k                  = &OuterIndex;
    int8_t      InnerIncrement      = +1;
    int8_t      MiddlIncrement      = +1;
    int8_t      OuterIncrement      = +1;

    // Find out the variable of the out-most loop.
    switch (mf.Sequences[0])
    {
        default:
        case MyFile::SEQUENCE::SEQ_I:
            i           = &OuterIndex;
            OuterMin    = mf.IndexIMin;
            OuterMax    = mf.IndexIMax;
            break;
        case MyFile::SEQUENCE::SEQ_J:
            j           = &OuterIndex;
            OuterMin    = mf.IndexJMin;
            OuterMax    = mf.IndexJMax;
            break;
        case MyFile::SEQUENCE::SEQ_K:
            k           = &OuterIndex;
            OuterMin    = mf.IndexKMin;
            OuterMax    = mf.IndexKMax;
            break;
    }
    break;
    switch (mf.Increments[0])
    {
        case MyFile::INCREMENT::PLUS:
            OuterIncrement = +1;
            break;
        case MyFile::INCREMENT::MINUS:
            OuterIncrement = -1;
            break;
    }
    break;

    // The middle loop.
    switch (mf.Sequences[1])
    {
        case MyFile::SEQUENCE::SEQ_I:
            i           = &MiddlIndex;
            MiddlMin    = mf.IndexIMin;
            MiddlMax    = mf.IndexIMax;
            break;
        default:
        case MyFile::SEQUENCE::SEQ_J:
            j           = &MiddlIndex;
            MiddlMin    = mf.IndexJMin;
            MiddlMax    = mf.IndexJMax;
            break;
        case MyFile::SEQUENCE::SEQ_K:
            k           = &MiddlIndex;
            MiddlMin    = mf.IndexKMin;
            MiddlMax    = mf.IndexKMax;
            break;
    }
    break;
    switch (mf.Increments[1])
    {
        case MyFile::INCREMENT::PLUS:
            MiddlIncrement = +1;
            break;
        case MyFile::INCREMENT::MINUS:
            MiddlIncrement = -1;
            break;
    }

    // The inner loop.
    switch (mf.Sequences[2])
    {
        case MyFile::SEQUENCE::SEQ_I:
            i           = &InnerIndex;
            InnerMin    = mf.IndexIMin;
            InnerMax    = mf.IndexIMax;
            break;
        case MyFile::SEQUENCE::SEQ_J:
            j           = &InnerIndex;
            InnerMin    = mf.IndexJMin;
            InnerMax    = mf.IndexJMax;
            break;
        default:
        case MyFile::SEQUENCE::SEQ_K:
            k           = &InnerIndex;
            InnerMin    = mf.IndexKMin;
            InnerMax    = mf.IndexKMax;
            break;
    }
    break;
    switch (mf.Increments[2])                   // The third (inner) increment.
    {
        case MyFile::INCREMENT::PLUS:
            InnerIncrement = +1;
            break;
        case MyFile::INCREMENT::MINUS:
            InnerIncrement = -1;
            break;
    }

    // Run the loop.
    for             (uint64_t OuterIndex=OuterMin; OuterIndex<=OuterMax; OuterIndex += OuterIncrement)
    {
        for         (uint64_t MiddlIndex=MiddlMin; MiddlIndex<=MiddlMax; MiddlIndex += MiddlIncrement)
        {
            for     (uint64_t InnerIndex=InnerMin; InnerIndex<=InnerMax; InnerIndex += InnerIncrement)
            {
                os << GetVariable(*i, *j, *k) << "\t";
            }
        }
        os << std::endl;
    }

    return os;
}

Is this the correct way of manipulating the loop order? I find the way I implemented is too long and complex. Is there a better way of doing this?

Upvotes: 0

Views: 94

Answers (1)

Tadeusz Kopec for Ukraine
Tadeusz Kopec for Ukraine

Reputation: 12413

The easiest way to improve your code would be to create a helper function getNextCoordinate that takes a tuple (triple) of coordinates and order, returning next tuple in given order. Plus function getInitialCoordinates that gives initial coordinates for given order.

Upvotes: 1

Related Questions