def METHOD_NAME(self, *point):
    """Get plain noise for a single point, without taking into account
    either octaves or tiling.
    """
    if len(point) != self.dimension:
        raise ValueError("Expected {} values, got {}".format(
            self.dimension, len(point)))
    # Build a list of the (min, max) bounds in each dimension
    grid_coords = []
    for coord in point:
        min_coord = math.floor(coord)
        max_coord = min_coord + 1
        grid_coords.append((min_coord, max_coord))
    # Compute the dot product of each gradient vector and the point's
    # distance from the corresponding grid point.  This gives you each
    # gradient's "influence" on the chosen point.
    dots = []
    for grid_point in product(*grid_coords):
        if grid_point not in self.gradient:
            self.gradient[grid_point] = self._generate_gradient()
        gradient = self.gradient[grid_point]
        dot = 0
        for i in range(self.dimension):
            dot += gradient[i] * (point[i] - grid_point[i])
        dots.append(dot)
    # Interpolate all those dot products together.  The interpolation is
    # done with smoothstep to smooth out the slope as you pass from one
    # grid cell into the next.
    # Due to the way product() works, dot products are ordered such that
    # the last dimension alternates: (..., min), (..., max), etc.  So we
    # can interpolate adjacent pairs to "collapse" that last dimension.  Then
    # the results will alternate in their second-to-last dimension, and so
    # forth, until we only have a single value left.
    dim = self.dimension
    while len(dots) > 1:
        dim -= 1
        s = smoothstep(point[dim] - grid_coords[dim][0])
        next_dots = []
        while dots:
            next_dots.append(lerp(s, dots.pop(0), dots.pop(0)))
        dots = next_dots
    return dots[0] * self.scale_factor