readPack<partPointersToRead, NumberSegments, OriginalPointerType>
                    (oldRankSplit, rankIndicesPack[0]), 
                  avx2Helper::readPack<partPointersToRead, NumberSegments, OriginalPointerType>
                    (oldRankSplit, rankIndicesPack[1])  };
            
            __m256d mulPack[2] = 
                { _mm256_mul_pd(matValPack[0], rankPack[0]),
                  _mm256_mul_pd(matValPack[1], rankPack[1]) };
            resPack[0] = _mm256_add_pd(resPack[0], mulPack[0]);
            resPack[1] = _mm256_add_pd(resPack[1], mulPack[1]);
        }
        
        ConvertAvx result[2];
        result[0].avx = resPack[0];
        result[1].avx = resPack[1];
        
        #pragma unroll
        for (std::size_t i = 0; i < 4; ++i) {
            const OriginalPointerType newRank[2] = 
                {
                (static_cast<OriginalPointerType>(1) - dampingFactor)
                 / static_cast<OriginalPointerType>(n)
                + dampingFactor * (result[0].dbl[i] + correction), 
                
                (static_cast<OriginalPointerType>(1) - dampingFactor)
                 / static_cast<OriginalPointerType>(n)
                + dampingFactor * (result[1].dbl[i] + correction)
                };
            
            newRankSplit.template write<partPointersToWrite>(row + i, newRank[0]);
            newRankSplit.template write<partPointersToWrite>(row + i + 4, newRank[1]);
        }
    }
    
    //if (row!= rowAfterAvx) {
    //    std::cerr << "Mismatch! n = " << n << "; Row = " << row << ", expected: " << rowAfterAvx << "\n";
    //}
    
    // After the loop above, row should be:
    // row = rowAfterAvx
    #ifdef OMP_ENABLE_
    #pragma omp parallel for schedule(dynamic, 1)
    #endif
    //row must be the same as before!
    for (row = rowAfterAvx; row < n; ++row) {
        OriginalPointerType localRes = static_cast<OriginalPointerType>(0.0);
        for (IndexType i = 0; i < k; ++i) {
            const IndexType matIdx = i*n + row;
            const IndexType rankIdx = indices[matIdx];
            const OriginalPointerType matValue = valsSplit.template read<partPointersToRead>(matIdx);
            const OriginalPointerType rankValue = oldRankSplit.template read<partPointersToRead>(rankIdx);
            localRes += matValue * rankValue;
        }

        const OriginalPointerType newRank =
            (static_cast<OriginalPointerType>(1) - dampingFactor)
             / static_cast<OriginalPointerType>(n)
            + dampingFactor * (localRes + correction);
        newRankSplit.template write<partPointersToWrite>(row, newRank);
    }
#endif
}

};




template<MySegmentsNumberType NumberSegments, typename OriginalPointerType>
struct Avx2Kernels
{

union ConvertAvx {__m256d avx; double dbl[4]; };

using SplitPtr = SplitPointer<NumberSegments, OriginalPointerType>;

static constexpr std::size_t segmentsPerBlock = SplitPtr::segmentsPerMemoryBlock;

static constexpr std::size_t potentialChunk = ompHelper::blas1ChunkSize / segmentsPerBlock;

static constexpr int blockChunk = (1 < potentialChunk)? potentialChunk : 1;

#ifdef OMP_ENABLE_
#pragma omp declare reduction \
  (vecAdd:__m256d:omp_out=_mm256_add_pd(omp_out, omp_in)) \
  initializer(omp_priv=_mm256_setzero_pd())
#endif

template<MySegmentsNumberType SegmentsToUse, typename IndexType>
static void rwTest(IndexType n, SplitPtr rw, OriginalPointerType factor)
{
    static_assert(SegmentsToUse <= NumberSegments, "It is not possible to use more segments than are available!");
    const std::size_t avxUpperLimit = n - (4-1);
    IndexType idx = 0;
    #ifdef OMP_ENABLE_
    #pragma omp parallel for lastprivate(idx) schedule(static, ompHelper::blas1ChunkSize / 4)
    #endif
    for (idx = 0; idx < avxUpperLimit; idx += 4) {
        const __m256d factorPack = _mm256_set1_pd(factor);
        
        __m256d valPack = avx2Helper::readPack<SegmentsToUse, NumberSegments, OriginalPointerType>(rw, idx);
        __m256d multResPack = _mm256_mul_pd(valPack, factorPack);
        avx2Helper::writePack<SegmentsToUse, NumberSegments, OriginalPointerType>(rw, multResPack, idx);
    }
    //idx = n - (4 - (n % 4));
    for (; idx < n; idx += 1) {
        OriginalPointerType val = rw.template read<SegmentsToUse>(idx);
        rw.template write<SegmentsToUse>(idx, val * factor);
    }
}

//No need to vectorize because not runtime critical!
template<typename IndexType>
static void setVector(IndexType n, SplitPtr vec, OriginalPointerType initData)
{
    BasicKernels<NumberSegments, OriginalPointerType>::setVector(n, vec, initData);
}

template<MySegmentsNumberType SegmentsToUse, typename IndexType>
static OriginalPointerType norm1(IndexType n, const SplitPtr in)
{
    static_assert(SegmentsToUse <= NumberSegments, "It is not possible to use more segments than are available!");
    using std::abs;
    const std::size_t avxUpperLimit = n - (4-1);
    
    std::size_t i = 0;
    __m256d sumPack = _mm256_setzero_pd();
    
    #ifdef OMP_ENABLE_
    #pragma omp parallel for reduction(vecAdd:sumPack) lastprivate(i) schedule(static, ompHelper::blas1ChunkSize / 4)
    #endif
    for (i = 0; i < avxUpperLimit; i += 4) {
        //__m128i indices32 = _mm_setr_epi32(i, i+1, i+2, i+3);
        //__m256d valPack = avx2Helper::readPack<SegmentsToUse, NumberSegments, OriginalPointerType>(in, indices32);
        __m256d valPack = avx2Helper::readPack<SegmentsToUse, NumberSegments, OriginalPointerType>(in, i);
        __m256d absValPack = avx2Helper::absPack(valPack);
        sumPack = _mm256_add_pd(sumPack, absValPack);
    }
    ConvertAvx result;
    result.avx = sumPack;
    
    OriginalPointerType sum = result.dbl[0] + result.dbl[1] + result.dbl[2] + result.dbl[3];
    
    for (; i < n; i += 1) {
        sum += abs(in.template read<SegmentsToUse>(i));
    }
    return sum;
}


template<MySegmentsNumberType SegmentsToUse, typename IndexType>
static OriginalPointerType norm1Diff(IndexType n, const SplitPtr in1,
    const SplitPtr in2)
{
    static_assert(SegmentsToUse <= NumberSegments, "It is not possible to use more segments than are available!");
    using std::abs;
    const std::size_t avxUpperLimit = n - (4-1);
    
    std::size_t i = 0;
    __m256d diffPack = _mm256_setzero_pd();
    
    #ifdef OMP_ENABLE_
    #pragma omp parallel for reduction(vecAdd:diffPack) lastprivate(i) schedule(static, ompHelper::blas1ChunkSize / 4)
    #endif
    for (i = 0; i < avxUpperLimit; i += 4) {
        __m256