//  [python]
def _sample_wedge_points(self, diff_mask, diff_wedges_ind, num_samples):
        r"""

        Samples equally spaced candidate diffraction points on the candidate
        wedges.

        The distance between two points is the cumulative length of the
        candidate wedges divided by ``num_samples``, i.e., the density of
        samples is the same for all wedges.

        The `num_samples` dimension of the output tensors is in general slighly
        smaller than input `num_samples` because of roundings.

        Input
        ------
        diff_mask : [num_tx, num_samples], tf.bool
            Mask set to False for invalid samples

        diff_wedges_ind : [num_candidate_wedges], int
            Candidate wedges indices

        num_samples : int
            Number of samples to shoot

        Output
        ------
        diff_mask : [num_tx, num_samples], tf.bool
            Mask set to False for invalid wedges

        diff_wedges_ind : [num_samples], tf.int
            Indices of the wedges that interacted with the diffracted paths

        diff_ells : [num_samples], tf.float
            Positions of the diffraction points on the wedges.
            These positions are given as an offset from the wedges origins.

        diff_vertex : [num_samples, 3], tf.float
            Positions of the diffracted points in the GCS

        diff_num_samples_per_wedge : [num_samples], tf.int
            For each sample, total mumber of samples that were sampled on the
            same wedge
        """

        zero_dot_five = tf.cast(0.5, self._rdtype)

        # [num_candidate_wedges]
        wedges_length = tf.gather(self._wedges_length, diff_wedges_ind)
        # Total length of the wedges
        # ()
        wedges_total_length = tf.reduce_sum(wedges_length)
        # Spacing between the samples
        # ()
        delta_ell = wedges_total_length/tf.cast(num_samples,self._rdtype)
        # Number of samples for each wedge
        # [num_candidate_wedges]
        samples_per_wedge = tf.math.divide_no_nan(wedges_length, delta_ell)
        samples_per_wedge = tf.cast(tf.floor(samples_per_wedge), tf.int32)
        # Maximum number of samples for a wedge
        # tf.maximum() required for the case where samples_per_wedge is empty
        # ()
        max_samples_per_wedge = tf.maximum(tf.reduce_max(samples_per_wedge), 0)
        # Sequence used to build the equally spaced samples on the wedges
        # [max_samples_per_wedge]
        cseq = tf.cumsum(tf.ones([max_samples_per_wedge], dtype=tf.int32)) - 1
        # [1, max_samples_per_wedge]
        cseq = tf.expand_dims(cseq, axis=0)
        # [num_candidate_wedges, 1]
        samples_per_wedge_ = tf.expand_dims(samples_per_wedge, axis=1)
        # [num_candidate_wedges, max_samples_per_wedge]
        ells_i = tf.where(cseq < samples_per_wedge_, cseq,
                          max_samples_per_wedge)
        # Compute the relative offset of the diffraction point on the wedge
        # [num_candidate_wedges, max_samples_per_wedge]
        ells = (tf.cast(ells_i, self._rdtype) + zero_dot_five)*delta_ell
        # [num_candidate_wedges x max_samples_per_wedge]
        ells_i = tf.reshape(ells_i, [-1])
        ells = tf.reshape(ells, [-1])
        # Extract only relevant indices
        # [num_samples]. Smaller but close than input num_samples in general
        # because of previous floor() op
        ells = tf.gather(ells, tf.where(ells_i < max_samples_per_wedge))[:,0]

        # Compute the corresponding points coordinates in the GCS
        # Wedges origin
        # [num_candidate_wedges, 3]
        origins = tf.gather(self._wedges_origin, diff_wedges_ind)
        # Wedges directions
        # [num_candidate_wedges, 3]
        e_hat = tf.gather(self._wedges_e_hat, diff_wedges_ind)
        # Match each sample to the corresponding wedge origin and vector
        # First, generate the indices for the gather op
        # ()
        num_candidate_wedges = diff_wedges_ind.shape[0]
        # [num_candidate_wedges]
        gather_ind = tf.range(num_candidate_wedges)
        gather_ind = tf.expand_dims(gather_ind, axis=1)
        # [num_candidate_wedges, max_samples_per_wedge]
        gather_ind = tf.where(cseq < samples_per_wedge_, gather_ind,
                              num_candidate_wedges)
        # [num_candidate_wedges x max_samples_per_wedge]
        gather_ind = tf.reshape(gather_ind, [-1])
        # [num_samples]
        gather_ind = tf.gather(gather_ind,
                               tf.where(ells_i < max_samples_per_wedge))[:,0]
        # [num_samples, 3]
        origins = tf.gather(origins, gather_ind, axis=0)
        e_hat = tf.gather(e_hat, gather_ind, axis=0)
        # [num_samples]
        diff_wedges_ind = tf.gather(diff_wedges_ind, gather_ind, axis=0)
        # [num_tx, num_samples]
        diff_mask = tf.gather(diff_mask, gather_ind, axis=1)
        # Positions of the diffracted points in the GCS
        # [num_samples, 3]
        diff_points = origins + tf.expand_dims(ells, axis=1)*e_hat
        # Number of samples per wedge
        # [num_samples]
        samples_per_wedge = tf.gather(samples_per_wedge, gather_ind, axis=0)

        return diff_mask, diff_wedges_ind, ells, diff_points, samples_per_wedge