Get the Miller index of a plane from a list of site coordinates.

        A minimum of 3 sets of coordinates are required. If more than 3 sets of
        coordinates are given, the best plane that minimises the distance to all
        points will be calculated.

        Args:
            coords (iterable): A list or numpy array of coordinates. Can be
                cartesian or fractional coordinates. If more than three sets of
                coordinates are provided, the best plane that minimises the
                distance to all sites will be calculated.
            coords_are_cartesian (bool, optional): Whether the coordinates are
                in cartesian space. If using fractional coordinates set to
                False.
            round_dp (int, optional): The number of decimal places to round the
                miller index to.
            verbose (bool, optional): Whether to print warnings.

        Returns:
            (tuple): The Miller index.

def get_miller_index_from_coords(
        self,
        coords: Vector3Like,
        coords_are_cartesian: bool = True,
        round_dp: int = 4,
        verbose: bool = True,
    ) -> Tuple[int, int, int]:
        """
        Get the Miller index of a plane from a list of site coordinates.

        A minimum of 3 sets of coordinates are required. If more than 3 sets of
        coordinates are given, the best plane that minimises the distance to all
        points will be calculated.

        Args:
            coords (iterable): A list or numpy array of coordinates. Can be
                cartesian or fractional coordinates. If more than three sets of
                coordinates are provided, the best plane that minimises the
                distance to all sites will be calculated.
            coords_are_cartesian (bool, optional): Whether the coordinates are
                in cartesian space. If using fractional coordinates set to
                False.
            round_dp (int, optional): The number of decimal places to round the
                miller index to.
            verbose (bool, optional): Whether to print warnings.

        Returns:
            (tuple): The Miller index.
        """
        if coords_are_cartesian:
            coords = [self.get_fractional_coords(c) for c in coords]

        coords = np.asarray(coords)
        g = coords.sum(axis=0) / coords.shape[0]

        # run singular value decomposition
        _, _, vh = np.linalg.svd(coords - g)

        # get unitary normal vector
        u_norm = vh[2, :]
        return get_integer_index(u_norm, round_dp=round_dp, verbose=verbose)