docs: def all_connected_components(i,j):
      '''Associate each label in i with a component #
      
      This function finds all connected components given an array of
      associations between labels i and j using a depth-first search.
      
      i & j give the edges of the graph. The first step of the algorithm makes
      bidirectional edges, (i->j and j<-i), so it's best to only send the
      edges in one direction (although the algorithm can withstand duplicates).
      
      returns a label for each vertex up to the maximum named vertex in i.
      '''
      if len(i) == 0:
          return i
      i1 = np.hstack((i,j))
      j1 = np.hstack((j,i))
      order = np.lexsort((j1,i1))
      i=np.ascontiguousarray(i1[order],np.uint32)
      j=np.ascontiguousarray(j1[order],np.uint32)
      #
      # Get indexes and counts of edges per vertex
      #
      counts = np.ascontiguousarray(np.bincount(i.astype(int)),np.uint32)
      indexes = np.ascontiguousarray(np.cumsum(counts)-counts,np.uint32)
      #
      # This stores the lowest index # during the algorithm - the first
      # vertex to be labeled in a connected component.
      #
      labels = np.zeros(len(counts), np.uint32)
      _all_connected_components(i,j,indexes,counts,labels)
      return labels