Project: 037_On_Conformal_Isometry_of_Grid_Cells_Learning_Dista | Function: gridnessScore (iclr)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Computes the gridness score to quantify the hexagonal periodic patterns in grid cell firing maps. This implements the evaluation metric used to validate that the conformal isometry hypothesis leads to hexagonal grid patterns.

**KEY TERMS:** {
  "autoCorr": "spatial autocorrelation",
  "crossCorr": "rotational cross-correlation",
  "G": "gridness score",
  "maxima": "maximum at 30°, 90°, 150°",
  "minima": "minimum at 60°, 120°"
}

**CONTEXT CODE:**     yedges = np.linspace(-arenaDiam, arenaDiam, int(precision*2 + 1))
    X, Y = np.meshgrid(xedges, yedges)

    corr = ma.masked_array(correlate2d(rateMap, rateMap, mode='full'), mask = np.sqrt(X**2 + Y**2) > arenaDiam)

    return corr, xedges, yedges


def compute_angle(v1, v2):
    norm1 = np.sqrt(np.sum(v1 ** 2))
    norm2 = np.sqrt(np.sum(v2 ** 2))
    return math.acos(np.sum(v1 * v2) / (norm1 * norm2))


def compute_scale_orientation(autoCorr):
    size = autoCorr.shape[0]
    idx = np.argsort(-autoCorr.reshape([-1]))
    idx_x, idx_y = idx // size, idx % size
    peak = np.zeros(shape=(6, 2))
    count = 0
    iter = 0

    while count < 6:
        add = True
        for i in range(count):
            if autoCorr.mask[idx_x[iter], idx_y[iter]]:
                add = False
            elif np.sqrt((peak[i, 0] - idx_x[iter]) ** 2 + (peak[i, 1] - idx_y[iter]) ** 2) < 10.0:
                add = False
        if add:
            peak[count, 0], peak[count, 1] = idx_x[iter], idx_y[iter]
            count = count + 1
        iter = iter + 1
        assert iter < len(idx)

    center = [(size - 1) / 2, (size - 1) / 2]
    distance = np.sqrt((center[0] - peak[:, 0]) ** 2 + (center[1] - peak[:, 1]) ** 2)
    interval_length = 1.0 / ((size - 1) / 2)
    distance = distance * interval_length
    peaks = (peak - center) * interval_length

    scale = np.median(distance)

    angle = np.zeros(shape=[6])
    for i in range(6):
        angle[i] = compute_angle(peak[i] - center, np.array([-1, 0]))
    orientation = np.min(angle)
    return scale, orientation, peaks


def gridnessScore(rateMap, arenaDiam, h, corr_cutRmin):
    '''Calculate gridness score of a spatial firing rate map.

    Parameters
    ----------
    rateMap : np.ndarray
        Spatial firing rate map.
    arenaDiam : float
        The diameter of the arena.
    h : float
        Precision of the spatial firing rate map.

    Returns
    -------
    G : float
        Gridness score.
    crossCorr : np.ndarray
        An array containing cross correlation values of the rotated
        autocorrelations, with the original autocorrelation.
    angles : np.ndarray
        An array of angles corresponding to the `crossCorr` array.


    Notes
    -----
    This function computes gridness score accoring to [1]_. The auto
    correlation of the firing rate map is rotated in 3 degree steps. The
    resulting gridness score is the difference between a minimum of cross
    correlations at 60 and 90 degrees, and a maximum of cross correlations at
    30, 90 and 150 degrees.

    The center of the auto correlation map (given by corr_cutRmin) is removed
    from the map.

    References
    ----------
    .. [1] Hafting, T. et al., 2005. Microstructure of a spatial map in the
       entorhinal cortex. Nature, 436(7052), pp.801-806.
    '''
    raise NotImplementedError("Function implementation has been masked for testing")
def occupancy_prob_dist(arena, pos):
    '''Calculate a probability distribution for animal positions in an arena.

    Parameters
    ----------
    arena : :class:`~gridcells.core.arena.Arena`
        Arena the animal was running in.
    pos : :class:`~gridcells.core.common.Position2D`
        Positions of the animal.

    Returns
    -------
    dist : numpy.ndarray
        Probability distribution for the positional data, given the
        discretisation of the arena. The first dimension is the y axis, the
        second dimension is the x axis. The shape of the distribution is equal
        to the number of items in the discretised edges of the arena.
    '''
    edges = arena.getDiscretisation()
    dx = arena.getDiscretisationSteps()
    xedges = np.hstack((edges.x, [edges.x[-1] + dx.x]))
    yedges = np.hstack((edges.y, [edges.y[-1] + dx.y]))

    H, _, _ = np.histogram2d(pos.x, pos.y, bins=[xedges, yedges], normed=False)
    return (H / len(pos)).T


Complete the function implementation replacing "raise NotImplementedError".