inline std::string LanguageC<Base>::linearIndexPattern2String(const LinearIndexPattern& lip,
                                                              const IndexDclrOperationNode<Base>& index) {
    long dy = lip.getLinearSlopeDy();
    long dx = lip.getLinearSlopeDx();
    long b = lip.getLinearConstantTerm();
    long xOffset = lip.getXOffset();

    std::stringstream ss;
    if (dy != 0) {
        if (xOffset != 0) {
            ss << "(";
        }
        ss << (*index.getName());
        if (xOffset != 0) {
            ss << " - " << xOffset << ")";
        }

        if (dx != 1) {
            ss << " / " << dx;
        }
        if (dy != 1) {
            ss << " * " << dy;
        }
    } else if (b == 0) {
        ss << "0"; // when dy == 0 and b == 0
    }

    if (b != 0) {
        if (dy != 0)
            ss << " + ";
        ss << b;
    }
    return ss.str();
}
    static inline Plane2DIndexPattern* detectPlane2D(const std::map<size_t, std::map<size_t, size_t> >& x2y2z) {
        /**
         * try to fit a combination of two patterns:
         *  z = fStart(x) + flit(y);
         */

        if (x2y2z.size() == 1) {
            // only one x -> fit z to y
            const std::map<size_t, size_t>& y2z = x2y2z.begin()->second;
            return new Plane2DIndexPattern(nullptr, IndexPattern::detect(y2z));
        }

        // perhaps there is always only one y
        size_t y = x2y2z.begin()->second.begin()->first;
        std::map<size_t, size_t> x2z;
        std::map<size_t, std::map<size_t, size_t> >::const_iterator itx2y2z;
        for (itx2y2z = x2y2z.begin(); itx2y2z != x2y2z.end(); ++itx2y2z) {
            size_t x = itx2y2z->first;
            const std::map<size_t, size_t>& y2z = itx2y2z->second;

            if (y2z.size() != 1 ||
                    y != y2z.begin()->first) {
                x2z.clear(); // not always the same y
                break;
            }

            size_t z = y2z.begin()->second;
            x2z[x] = z;
        }

        if (!x2z.empty()) {
            return new Plane2DIndexPattern(IndexPattern::detect(x2z), nullptr);
        }

        /**
         * try to fit a combination of two patterns:
         *  z = fStart(x) + flit(y);
         */
        std::map<size_t, size_t> x2zStart;
        std::map<size_t, size_t> y2zOffset;

        for (itx2y2z = x2y2z.begin(); itx2y2z != x2y2z.end(); ++itx2y2z) {
            size_t x = itx2y2z->first;
            const std::map<size_t, size_t>& y2z = itx2y2z->second;

            size_t zFirst = y2z.begin()->second;
            x2zStart[x] = zFirst;

            std::map<size_t, size_t>::const_iterator ity2z;

            for (ity2z = y2z.begin(); ity2z != y2z.end(); ++ity2z) {
                size_t y = ity2z->first;
                size_t offset = ity2z->second - zFirst;
                std::map<size_t, size_t>::const_iterator itY2zOffset = y2zOffset.find(y);
                if (itY2zOffset == y2zOffset.end()) {
                    y2zOffset[y] = offset;
                } else if (itY2zOffset->second != offset) {
                    return nullptr; // does not fit the pattern
                }
            }
        }

        /**
         * try to detect a pattern for the initial iteration index based on x
         */
        std::unique_ptr<IndexPattern> fx;

        std::map<size_t, IndexPattern*> startSections = SectionedIndexPattern::detectLinearSections(x2zStart, 2);
        if (startSections.empty()) {
            return nullptr; // does not fit the pattern
        }

        // detected a pattern for the first z based on x
        if (startSections.size() == 1) {
            fx = std::unique_ptr<IndexPattern> (startSections.begin()->second);
        } else {
            fx = std::unique_ptr<IndexPattern> (new SectionedIndexPattern(startSections));
        }

        /**
         * try to detect a pattern for the following iterations
         * based on the local loop index (local index != model index)
         */
        std::map<size_t, IndexPattern*> sections = SectionedIndexPattern::detectLinearSections(y2zOffset, 2);
        if (sections.empty()) {
            return nullptr; // does not fit the pattern
        }

        // detected a pattern for the z offset based on y
        std::unique_ptr<IndexPattern> fy;
        if (sections.size() == 1) {
            fy = std::unique_ptr<IndexPattern> (sections.begin()->second);
        } else {
            fy = std::unique_ptr<IndexPattern> (new SectionedIndexPattern(sections));
        }

        // simplify when both patterns are constant
        if (fx->getType() == IndexPatternType::Linear && fy->getType() == IndexPatternType::Linear) {
            LinearIndexPattern* ipx = static_cast<LinearIndexPattern*> (fx.get());
            LinearIndexPattern* ipy = static_cast<LinearIndexPattern*> (fy.get());

            if (ipx->getLinearSlopeDy() == 0 && ipy->getLinearSlopeDy() == 0) {
                /**
                 * only need to keep one
                 */
                ipx->setLinearConstantTerm(ipx->getLinearConstantTerm() + ipy->getLinearConstantTerm());
                fy.reset();
            }
        }

        return new Plane2DIndexPattern(fx.release(), fy.release());
    }