# Path: monty/io.py
# def reverse_readfile(filename: Union[str, Path]) -> Generator[str, str, None]:
#     """
#     A much faster reverse read of file by using Python's mmap to generate a
#     memory-mapped file. It is slower for very small files than
#     reverse_readline, but at least 2x faster for large files (the primary use
#     of such a method).
# 
#     Args:
#         filename (str):
#             Name of file to read.
# 
#     Yields:
#         Lines from the file in reverse order.
#     """
#     try:
#         with zopen(filename, "rb") as f:
#             if isinstance(f, (gzip.GzipFile, bz2.BZ2File)):
#                 for line in reversed(f.readlines()):
#                     yield line.decode("utf-8").rstrip()
#             else:
#                 fm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
#                 n = len(fm)
#                 while n > 0:
#                     i = fm.rfind(b"\n", 0, n)
#                     yield fm[i + 1 : n].decode("utf-8").strip("\n")
#                     n = i
#     except ValueError:
#         return
# 
# def zopen(filename: Union[str, Path], *args, **kwargs) -> IO:
#     r"""
#     This function wraps around the bz2, gzip, lzma, xz and standard python's open
#     function to deal intelligently with bzipped, gzipped or standard text
#     files.
# 
#     Args:
#         filename (str/Path): filename or pathlib.Path.
#         *args: Standard args for python open(..). E.g., 'r' for read, 'w' for
#             write.
#         **kwargs: Standard kwargs for python open(..).
# 
#     Returns:
#         File-like object. Supports with context.
#     """
#     if Path is not None and isinstance(filename, Path):
#         filename = str(filename)
# 
#     name, ext = os.path.splitext(filename)
#     ext = ext.upper()
#     if ext == ".BZ2":
#         return bz2.open(filename, *args, **kwargs)
#     if ext in (".GZ", ".Z"):
#         return gzip.open(filename, *args, **kwargs)
#     if ext in (".XZ", ".LZMA"):
#         return lzma.open(filename, *args, **kwargs)
#     return open(filename, *args, **kwargs)  # pylint: disable=R1732


import collections
import re
from monty.io import reverse_readfile, zopen
"""
Helpful regex based functions. E.g., grepping.
"""




def regrep(filename, patterns, reverse=False, terminate_on_match=False, postprocess=str):
    r"""
    A powerful regular expression version of grep.

    Args:
        filename (str): Filename to grep.
        patterns (dict): A dict of patterns, e.g.,
            {"energy": r"energy\\(sigma->0\\)\\s+=\\s+([\\d\\-\\.]+)"}.
        reverse (bool): Read files in reverse. Defaults to false. Useful for
            large files, especially when used with terminate_on_match.
        terminate_on_match (bool): Whether to terminate when there is at
            least one match in each key in pattern.
        postprocess (callable): A post processing function to convert all

# Path: monty/io.py
# def reverse_readfile(filename: Union[str, Path]) -> Generator[str, str, None]:
#     """
#     A much faster reverse read of file by using Python's mmap to generate a
#     memory-mapped file. It is slower for very small files than
#     reverse_readline, but at least 2x faster for large files (the primary use
#     of such a method).
# 
#     Args:
#         filename (str):
#             Name of file to read.
# 
#     Yields:
#         Lines from the file in reverse order.
#     """
#     try:
#         with zopen(filename, "rb") as f:
#             if isinstance(f, (gzip.GzipFile, bz2.BZ2File)):
#                 for line in reversed(f.readlines()):
#                     yield line.decode("utf-8").rstrip()
#             else:
#                 fm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
#                 n = len(fm)
#                 while n > 0:
#                     i = fm.rfind(b"\n", 0, n)
#                     yield fm[i + 1 : n].decode("utf-8").strip("\n")
#                     n = i
#     except ValueError:
#         return
# 
# def zopen(filename: Union[str, Path], *args, **kwargs) -> IO:
#     r"""
#     This function wraps around the bz2, gzip, lzma, xz and standard python's open
#     function to deal intelligently with bzipped, gzipped or standard text
#     files.
# 
#     Args:
#         filename (str/Path): filename or pathlib.Path.
#         *args: Standard args for python open(..). E.g., 'r' for read, 'w' for
#             write.
#         **kwargs: Standard kwargs for python open(..).
# 
#     Returns:
#         File-like object. Supports with context.
#     """
#     if Path is not None and isinstance(filename, Path):
#         filename = str(filename)
# 
#     name, ext = os.path.splitext(filename)
#     ext = ext.upper()
#     if ext == ".BZ2":
#         return bz2.open(filename, *args, **kwargs)
#     if ext in (".GZ", ".Z"):
#         return gzip.open(filename, *args, **kwargs)
#     if ext in (".XZ", ".LZMA"):
#         return lzma.open(filename, *args, **kwargs)
#     return open(filename, *args, **kwargs)  # pylint: disable=R1732


# Path: monty/re.py
import collections
import re
from monty.io import reverse_readfile, zopen

"""
Helpful regex based functions. E.g., grepping.
"""




def regrep(filename, patterns, reverse=False, terminate_on_match=False, postprocess=str):
    r"""
    A powerful regular expression version of grep.

    Args:
        filename (str): Filename to grep.
        patterns (dict): A dict of patterns, e.g.,
            {"energy": r"energy\\(sigma->0\\)\\s+=\\s+([\\d\\-\\.]+)"}.
        reverse (bool): Read files in reverse. Defaults to false. Useful for
            large files, especially when used with terminate_on_match.
        terminate_on_match (bool): Whether to terminate when there is at
            least one match in each key in pattern.
        postprocess (callable): A post processing function to convert all

            matches. Defaults to str, i.e., no change.