--- /dev/null
+#ifndef __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__
+#define __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__
+
+#include "nncc/core/ADT/kernel/Shape.h"
+
+namespace nncc
+{
+namespace core
+{
+namespace ADT
+{
+namespace kernel
+{
+
+class IndexEnumerator
+{
+public:
+ explicit IndexEnumerator(const Shape &shape);
+
+public:
+ IndexEnumerator(IndexEnumerator &&) = delete;
+ IndexEnumerator(const IndexEnumerator &) = delete;
+
+public:
+ bool valid(void) const;
+
+public:
+ uint32_t count(void) const;
+ uint32_t depth(void) const;
+ uint32_t height(void) const;
+ uint32_t width(void) const;
+
+public:
+ void advance(void);
+
+private:
+ // Store max and current offset for count/depth/height/width
+ //
+ // NOTE Here explicit array is used instead of kernel::Shape to make
+ // a room for improvement such as enumeration order (NHWC, NCHW)
+ // support
+ uint32_t _max[4];
+ uint32_t _cur[4];
+
+private:
+ uint32_t _cursor;
+};
+
+} // namespace kernel
+} // namespace ADT
+} // namespace core
+} // namespace nncc
+
+#endif // __NNCC_CORE_ADT_KERNEL_INDEX_ENUMERATOR_H__
--- /dev/null
+#include "nncc/core/ADT/kernel/IndexEnumerator.h"
+
+#include <cassert>
+#include <algorithm>
+
+namespace nncc
+{
+namespace core
+{
+namespace ADT
+{
+namespace kernel
+{
+
+IndexEnumerator::IndexEnumerator(const Shape &shape) : _cursor(0)
+{
+ _max[0] = shape.width();
+ _max[1] = shape.height();
+ _max[2] = shape.depth();
+ _max[3] = shape.count();
+
+ std::fill(_cur, _cur + 4, 0);
+
+ // NOTE Null dimension should NOT exist
+ assert(std::find(_max, _max + 4, 0) == (_max + 4));
+}
+
+bool IndexEnumerator::valid(void) const { return _cursor < 4; }
+
+uint32_t IndexEnumerator::count(void) const { return _cur[3]; }
+uint32_t IndexEnumerator::depth(void) const { return _cur[2]; }
+uint32_t IndexEnumerator::height(void) const { return _cur[1]; }
+uint32_t IndexEnumerator::width(void) const { return _cur[0]; }
+
+void IndexEnumerator::advance(void)
+{
+ while (_cursor < 4)
+ {
+ if (_cur[_cursor] + 1 < _max[_cursor])
+ {
+ break;
+ }
+
+ ++_cursor;
+ }
+
+ if (_cursor == 4)
+ {
+ return;
+ }
+
+ // Increment index
+ _cur[_cursor] += 1;
+
+ // Reset indices for lower dimensions
+ for (uint32_t head = 0; head < _cursor; ++head)
+ {
+ _cur[head] = 0;
+ }
+
+ // Reset cursor
+ _cursor = 0;
+}
+
+} // namespace kernel
+} // namespace ADT
+} // namespace core
+} // namespace nncc
--- /dev/null
+#include "nncc/core/ADT/kernel/IndexEnumerator.h"
+
+#include <vector>
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+using nncc::core::ADT::kernel::Shape;
+using nncc::core::ADT::kernel::IndexEnumerator;
+
+TEST(ADT_KERNEL_INDEX_ENUMERATOR, iterate_full_range)
+{
+ const uint32_t N = 2;
+ const uint32_t C = 3;
+ const uint32_t H = 4;
+ const uint32_t W = 5;
+
+ const Shape shape{N, C, H, W};
+
+ std::vector<uint32_t> count;
+ count.resize(N * C * H * W, 0);
+
+ for (IndexEnumerator e{shape}; e.valid(); e.advance())
+ {
+ const uint32_t offset = ((e.count() * C + e.depth()) * H + e.height()) * W + e.width();
+ count.at(offset) += 1;
+ }
+
+ ASSERT_TRUE(std::all_of(count.begin(), count.end(), [](uint32_t n) { return n == 1; }));
+}