   }
 
   std::vector<unsigned int>
-  clustering_merged_results(std::vector<std::vector<unsigned int>> clusterings
-                          , unsigned int max_row) {
+  merge_results(std::vector<std::vector<unsigned int>> clusterings
+              , unsigned int max_row) {
     unsigned int n_results = clusterings.size();
+    if (n_results == 0) {
+      std::cerr << "error: there are no partial clustering results to merge!"
+                << std::endl;
+      exit(EXIT_FAILURE);
+    } else {
+      if (clusterings[0].size() == 0) {
+        std::cerr << "error: no sampling, nothing to merge"
+                  << std::endl;
+        exit(EXIT_FAILURE);
+      }
+    }
+    max_row = std::min(max_row
+                     , (unsigned int) clusterings[0].size());
+std::cout << "merge" << std::endl;
     for (unsigned int i=0; i < max_row; ++i) {
+      // collect start points, i.e. cluster assignemnts
+      // of all partial results
+std::cout << i << ": " << "collecting start_points" << std::endl;
       std::set<unsigned int> start_points;
       for (unsigned int j=0; j < n_results; ++j) {
         start_points.insert(clusterings[j][i]);
       }
-
-      //TODO remove 0 from start_points
-
-
-
-      //TODO: finish: follow start_points (id = i_state - 1), collect all
-      //              states and rebase them to min(state)
-      std::set<unsigned int> update_needed = start_points;
+      // not interested in zero-ed states (aka no assignment)
+      if (start_points.count(0) > 0) {
+        start_points.erase(0);
+      }
+std::cout << i << ": " << "following state trails" << std::endl;
+      // follow start_points (id = i_state-1), collect all
+      // states and rebase them to min(state)
+      std::set<unsigned int> need_update = start_points;
       for (unsigned int s: start_points) {
-        unsigned int id = s-1;
-        while (clusterings[0][id] != 0
-            && clusterings[0][id] != s) {
-          update_needed.insert(s);
-          s = clusterings[0][id] 
-
-        }
-
-
-      }
-    }
-    return clustering;
+std::cout << "start point: " << s << std::endl;
+        unsigned int s_old = 1;
+        while (s != 0 && s_old != s) {
+          s_old = s;
+          s = clusterings[0][s-1];
+std::cout << "   needs update: " << s << std::endl;
+          need_update.insert(s);
+        }
+      }
+std::cout << i << ": " << "update state names" << std::endl;
+      // std::set is guaranteed to be ordered!
+      unsigned int min_s = (*need_update.begin());
+      for (unsigned int s: need_update) {
+        clusterings[0][s-1] = min_s;
+      }
+    }
+    return clusterings[0];
   }
 
   std::vector<std::size_t>

    unsigned int gid = bid * bsize + tid + i_from;
    int comp_size = min(bsize, n_rows - offset);
    if (tid < comp_size) {
      // reference coordinates to cache
      for (unsigned int j=0; j < n_cols; ++j) {
        smem_coords[tid*n_cols+j] = sorted_coords[(tid+offset)*n_cols+j];
      }
      // reference state information to cache
      smem_cache[col_inter+tid] = clustering[tid+offset];
    }
    __syncthreads();
    if (gid < i_to) {
      unsigned int min_id = clustering[gid];
      for (unsigned int j=0; j < n_cols; ++j) {
        // load coordinates of current frame for re-use into shared memory
        smem_coords[(tid+bsize)*n_cols+j] = sorted_coords[gid*n_cols+j];
      }
      // previous state information to cache
      smem_cache[col_prev+tid] = clustering[gid];
      smem_cache[col_result+tid] = smem_cache[col_prev+tid];
      // compare current frame (tid) against reference block (i)

    }
    __syncthreads();
    ////////

    // update result for given frame
    if (gid < i_to) {
