def localscale_neighbors_opposing_r_x(self, x, y, k, N, rho):
        """ 
        nearest neighbors from opposing classes with local scale computed
        based on D and B
        (class other than that of the example considered)

        x - data points
        y - class membership
        k - nearest neighbors to be considered from opposing class for 
            computing local scale
        rho - List of multipliers to be used when computing the graph with
            local scale. For a value r from this, an edge will be created when
            d(x_i, x_j) < r*local_scale_i*local_scale_j and i, j belong to
            opposing classes
        N - max number of nearest neighbors from opposing classes (user parameter)
        
        2 hop neighbors are then computed to complete the triangles and form
        simplices

        SIMILAR to localscale_neighbors_opposing_r but uses Cython for speedup
        This involves restricting the maximum number of neighbors for each point to N
        """

        numFilt = len(rho)
        nTriv = np.zeros(numFilt)
        m = x.shape[0]

        rows = np.ceil(float(numFilt) / 5)
        cols = 5

        # Compute opposing neighbors
        S, DN = self.opposing_neighbors(x, y, N)

        # Sigma array
        sigarr = np.sqrt(DN[:, k - 1].ravel())

        # Other inits
        C = -1 * np.ones((m, N + 1), dtype=np.int32)
        Pupd = np.zeros((m, m), dtype=np.int32)
        P = np.zeros((m, m), dtype=np.int32)
        inds = np.zeros(m, dtype=np.int32)

        #t1 = time()
        for j, r in enumerate(rho):
            # Re-initialize some arrays
            if j > 0:
                C.fill(-1)
                inds.fill(0)
                Pupd.fill(0)

            # Call Cython functions for updating P
            init_C_inds(C, inds)
            compute_C_closest(C, S, inds, DN, r, sigarr)
            update_P(C, inds, Pupd, 1)
            np.maximum(Pupd, Pupd.transpose(), out=Pupd)
            np.add(P, Pupd, out=P)

            nTriv[j] = np.sum(inds == 1)
            #sum(np.sum(C, axis = 0) == 1)

            if self.showComplexes:
                f = plt.figure(num=1000, figsize=(rows * 5, cols * 5))
                titl = "r = " + str(r)
                self.show_simplicial_complex(x, y, Pupd.astype(np.double), f,
                                             rows, cols, j + 1, titl)
            if self.saveComplexes:
                titl = "r = " + str(r)
                self.save_simplicial_complex(x, y, Pupd.astype(np.double),
                                             rows, cols, j + 1, titl)

        P = numFilt - P

        return P, nTriv, numFilt, C
    def localscale_neighbors_opposing_x(self, x, y, neighborsv, N):
        """ 
        k nearest neighbors from opposing classes 
        (class other than that of the example considered)

        x - data points
        y - class membership
        neighborsv - nearest neighbors to be considered from opposing class for 
                     computing local scale
        N - max number of nearest neighbors from opposing classes (user parameter)

        2 hop neighbors are then computed to complete the triangles and form
        simplices
        SIMILAR to localscale_neighbors_opposing but uses Cython for speedup
        This involves restricting the maximum number of neighbors for each point to N
        """

        numFilt = len(neighborsv)
        nTriv = np.zeros(numFilt)
        m = x.shape[0]

        rows = np.ceil(float(numFilt) / 5)
        cols = 5

        # Adjust N depending on max(neighborsv)
        N = int(np.maximum(N, np.max(neighborsv)))
        print("N adjusted to %d based on requested max nearest neighbors" %
              (N))

        # Compute opposing neighbors
        S, DN = self.opposing_neighbors(x, y, N)

        # Other inits
        C = -1 * np.ones((m, N + 1), dtype=np.int32)
        Pupd = np.zeros((m, m), dtype=np.int32)
        P = np.zeros((m, m), dtype=np.int32)
        inds = np.zeros(m, dtype=np.int32)

        for j, k in enumerate(neighborsv):
            # Sigma array
            sigarr = np.sqrt(DN[:, int(k) - 1].ravel())

            # Re-initialize some arrays
            if j > 0:
                C.fill(-1)
                inds.fill(0)
                Pupd.fill(0)

            # Call Cython functions for updating P
            init_C_inds(C, inds)
            compute_C_closest(C, S, inds, DN, 1.0, sigarr)
            update_P(C, inds, Pupd, 1)
            np.maximum(Pupd, Pupd.transpose(), out=Pupd)
            np.add(P, Pupd, out=P)

            nTriv[j] = np.sum(inds == 1)

            if self.showComplexes:
                f = plt.figure(num=1000, figsize=(rows * 5, cols * 5))
                titl = "k = " + str(neighborsv[j])
                self.show_simplicial_complex(x, y, Pupd.astype(np.double), f,
                                             rows, cols, j + 1, titl)

            if self.saveComplexes:
                titl = "r = " + str(neighborsv[j])
                self.save_simplicial_complex(x, y, Pupd.astype(np.double),
                                             rows, cols, j + 1, titl)

        P = numFilt - P

        return P, nTriv, numFilt