Как получить индекс элемента последовательности по индексу общих срезов?

Надумал написать итератор с возможностью изменения режима обхода непрерывной последовательности. То есть, я устанавливаю режим, передавая в соответствующий метод срез(std::gslicе, считаю так удобно) который принимает три аргумента: стартовый индекс и два объекта std::valarray<size_t>, представляющие размеры и шаги срезов соответственно. Для этого я написал базовый класс, который хранит некоторые данные, и, помимо других услуг, умеет выдать индекс элемента последовательности по индексу общих срезов(именно в реализации этого метода возникла трудность). Вот базовый класс с неправильной реализацией функции_члена get_slice_index:

class Base_matrix_iterator {    
private:
    const std::valarray<size_t>  sizes,  strides;
        //...
protected:
    size_t curr;
    const size_t start;
    //получить индекс элементa последовательности по индексу срезов
    size_t get_slice_index(size_t n) {
        //неправильный код              
        const size_t
            sz = sizes.size(),
            x = sizes[sz - 2],
            y = sizes[sz - 1],
            z = x * y;
        size_t pos = start;
            
        if (sz == 3 && n >= z) {
            pos += strides[0] * (n / z);
            n %= z;
        }
        if (n >= y) {
            pos += strides[sz - 2] * n / y;
            n %= y;
        }       
        else 
           pos += n * strides[sz - 1];
        return pos;         
    }
        
    //...
        
public:  
    /*ожидается общий срез с двумерными или трехмерными размерами, предназначенные
    для двухмерной и трехмерной матрицы соответственно
    std::gslice(старт, {количество строк, столбцов }, {шаг строк, столбцов})
    Если количество и шаги заданы как std::valarray<size_t>(3),
    то первые аргументы это количество таких матриц и шаги соответственно*/

    Base_matrix_iterator(const std::gslice& s)
        : sizes(s.size()), strides(s.stride()),
        start(s.start()), curr(0) {}
    size_t get_index() { return get_slice_index(curr); }
    //в дальнейшем конструктор и get_index будут
    //в защищенной части, поскольку они нужны только итераторам
    Base_matrix_iterator& operator++()
    {
        ++curr;
        return *this;
    }
    Base_matrix_iterator& operator +(const int off) {/*... */ }
    //наследник будет реализовать Tp& operator *() { return array[get_slice_index(curr)]; } и т.д.
    //...
};

Это означает, что:

Base_matrix_iterator It({ 0, {3, 2, 4}, {1, 10, 2} }),
    
for (int i = 0; i < 5; ++i) {
    for (int j = 0; j < 4; ++j) {
        cout << It.get_index() << ' ';
        ++It;
    }
    cout << endl;
}

должен выдать:

0 2 4 6     //старт==0 и 4 элемента (шаг == 2)
10 12 14 16  //вторая последовательность шаг == 10 от старта 
1 3 5 7     //и таких (матриц) 3 штуки, ( шаг== 1)
11 13 15 17
2 4 6 8
12 14 16 18

а следующий код:

Base_matrix_iterator  Iter2({ 1,{2, 3}, {7, 1} });
for (int i = 0; i < 2; ++i) {
    cout << '\n';
    for (int j = 0; j < 3; ++j) {
        cout << Iter2.get_index() << ' ';
        ++Iter2;
    }
}

должен выдать две последовательности по 3 элемента в каждой:

1 2 3   // старт== 1 и 3 элемента (шаг == 1)
8 9 10   //вторая последовательность шаг == 7 от старта 

Мой код выдает:

0 2 4 6
10 12 15 17
1 3 5 7
11 13 16 18
2 4 6 8
12 14 17 19

и

1 2 3
8 10 12

соответственно.

У меня что то заклинило, и я не могу разобраться в чем ошибка в реализации метода get_slice_index.

И, необязательный вопрос: "Не изобретаю ли я велосипед?.."

P.S. чтобы было понятно, еще раз про std::gslice

Cрез std::gslice(start, {x, y}, {sx, sy}) описывает матрицу размером[x][y] элементов массива array:

  • array[start ], array[start + sy], array[start + 2sy]... array[start + (y - 1) * sy]
  • еще таких же x - 1 последовательностей, только стартом теперь является start + i * sx, где 0 < i < x есть индекс строки, но .

Cрез std::gslice(start, {z, x, y}, {sz, sx, sy}) описывает z штук таких матриц(трехмерную матрицу), только start == start + j, где 0 <= j < z есть номер(индекс) матрицы. Словами, это z матриц шагом sz от начала матрицы, в каждой из них x строк, шагом sx от начала строк, в каждой строке sy элементов, шагом sy от начала строки

Я лишь хочу вычислить индекс элемента массива array по индексу полученной двумерной или трехмерной матрицы из его же элементов.


Ответы (1 шт):

Автор решения: AR Hovsepyan

Ошибка кроется в тонкостях:

  1. Когда выполняешь несколько действий в одном выражении, нужно не забывать о необходимых скобках. В выражении pos += strides[sz - 2] * n / y; я просто забыл скобки ставить вокруг n / y, поскольку сначала нужно знать на сколько раз n > y, а потом умножить на шаг среза.
  2. Дальше: поскольку, после взятия остатка n %= y;, n чаще будет иметь не нулевое значение, и нужно выполнить увеличение pos += n * strides[sz - 1]; тоже, то нужно else заменить на if(n < y) или в теле if (n >= y) добавить в конце эту операцию.

В итоге правильным кодом будет:

size_t get_slice_index(size_t n) {
    const size_t
        sz = sizes.size(),
        x = sizes[sz - 2],
        y = sizes[sz - 1],
        z = x * y;
    size_t pos = start;
        
    if (sz == 3 && n >= z) {
        pos += strides[0] * (n / z);
        n %= z;
    }       
    if (n >= y) {
        pos += strides[sz - 2] * (n / y);
        n %= y;
    }       
    if(n && n < y)
       pos += n * strides[sz - 1];
    return pos;         
}
→ Ссылка