# Path: opt_einsum/parser.py
# def alpha_canonicalize(equation: str) -> str:
#     """Alpha convert an equation in an order-independent canonical way.
# 
#     Examples
#     --------
#     >>> oe.parser.alpha_canonicalize("dcba")
#     'abcd'
# 
#     >>> oe.parser.alpha_canonicalize("Ĥěļļö")
#     'abccd'
#     """
#     rename: Dict[str, str] = {}
#     for name in equation:
#         if name in ".,->":
#             continue
#         if name not in rename:
#             rename[name] = get_symbol(len(rename))
#     return "".join(rename.get(x, x) for x in equation)
# 
# def parse_einsum_input(operands: Any) -> Tuple[str, str, List[ArrayType]]:
#     """
#     A reproduction of einsum c side einsum parsing in python.
# 
#     Returns
#     -------
#     input_strings : str
#         Parsed input strings
#     output_string : str
#         Parsed output string
#     operands : list of array_like
#         The operands to use in the numpy contraction
# 
#     Examples
#     --------
#     The operand list is simplified to reduce printing:
# 
#     >>> a = np.random.rand(4, 4)
#     >>> b = np.random.rand(4, 4, 4)
#     >>> parse_einsum_input(('...a,...a->...', a, b))
#     ('za,xza', 'xz', [a, b])
# 
#     >>> parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
#     ('za,xza', 'xz', [a, b])
#     """
# 
#     if len(operands) == 0:
#         raise ValueError("No input operands")
# 
#     if isinstance(operands[0], str):
#         subscripts = operands[0].replace(" ", "")
#         operands = [possibly_convert_to_numpy(x) for x in operands[1:]]
# 
#     else:
#         subscripts, operands = convert_interleaved_input(operands)
# 
#     # Check for proper "->"
#     if ("-" in subscripts) or (">" in subscripts):
#         invalid = (subscripts.count("-") > 1) or (subscripts.count(">") > 1)
#         if invalid or (subscripts.count("->") != 1):
#             raise ValueError("Subscripts can only contain one '->'.")
# 
#     # Parse ellipses
#     if "." in subscripts:
#         used = subscripts.replace(".", "").replace(",", "").replace("->", "")
#         ellipse_inds = "".join(gen_unused_symbols(used, max(len(x.shape) for x in operands)))
#         longest = 0
# 
#         # Do we have an output to account for?
#         if "->" in subscripts:
#             input_tmp, output_sub = subscripts.split("->")
#             split_subscripts = input_tmp.split(",")
#             out_sub = True
#         else:
#             split_subscripts = subscripts.split(",")
#             out_sub = False
# 
#         for num, sub in enumerate(split_subscripts):
#             if "." in sub:
#                 if (sub.count(".") != 3) or (sub.count("...") != 1):
#                     raise ValueError("Invalid Ellipses.")
# 
#                 # Take into account numerical values
#                 if operands[num].shape == ():
#                     ellipse_count = 0
#                 else:
#                     ellipse_count = max(len(operands[num].shape), 1) - (len(sub) - 3)
# 
#                 if ellipse_count > longest:
#                     longest = ellipse_count
# 
#                 if ellipse_count < 0:
#                     raise ValueError("Ellipses lengths do not match.")
#                 elif ellipse_count == 0:
#                     split_subscripts[num] = sub.replace("...", "")
#                 else:
#                     split_subscripts[num] = sub.replace("...", ellipse_inds[-ellipse_count:])
# 
#         subscripts = ",".join(split_subscripts)
# 
#         # Figure out output ellipses
#         if longest == 0:
#             out_ellipse = ""
#         else:
#             out_ellipse = ellipse_inds[-longest:]
# 
#         if out_sub:
#             subscripts += "->" + output_sub.replace("...", out_ellipse)
#         else:
#             # Special care for outputless ellipses
#             output_subscript = find_output_str(subscripts)
#             normal_inds = "".join(sorted(set(output_subscript) - set(out_ellipse)))
# 
#             subscripts += "->" + out_ellipse + normal_inds
# 
#     # Build output string if does not exist
#     if "->" in subscripts:
#         input_subscripts, output_subscript = subscripts.split("->")
#     else:
#         input_subscripts, output_subscript = subscripts, find_output_str(subscripts)
# 
#     # Make sure output subscripts are in the input
#     for char in output_subscript:
#         if char not in input_subscripts:
#             raise ValueError("Output character '{}' did not appear in the input".format(char))
# 
#     # Make sure number operands is equivalent to the number of terms
#     if len(input_subscripts.split(",")) != len(operands):
#         raise ValueError("Number of einsum subscripts must be equal to the " "number of operands.")
# 
#     return input_subscripts, output_subscript, operands
# 
# Path: opt_einsum/typing.py



import contextlib
import functools
import numbers
import threading
from collections import Counter, defaultdict
from typing import Any
from typing import Counter as CounterType
from typing import Dict, Generator, List, Optional, Tuple, Union
from .parser import alpha_canonicalize, parse_einsum_input
from .typing import ArrayType
"""
A module for sharing intermediates between contractions.

Copyright (c) 2018 Uber Technologies
"""



CacheKeyType = Union[Tuple[str, str, int, Tuple[int, ...]], Tuple[str, int]]

# Path: opt_einsum/parser.py
# def alpha_canonicalize(equation: str) -> str:
#     """Alpha convert an equation in an order-independent canonical way.
# 
#     Examples
#     --------
#     >>> oe.parser.alpha_canonicalize("dcba")
#     'abcd'
# 
#     >>> oe.parser.alpha_canonicalize("Ĥěļļö")
#     'abccd'
#     """
#     rename: Dict[str, str] = {}
#     for name in equation:
#         if name in ".,->":
#             continue
#         if name not in rename:
#             rename[name] = get_symbol(len(rename))
#     return "".join(rename.get(x, x) for x in equation)
# 
# def parse_einsum_input(operands: Any) -> Tuple[str, str, List[ArrayType]]:
#     """
#     A reproduction of einsum c side einsum parsing in python.
# 
#     Returns
#     -------
#     input_strings : str
#         Parsed input strings
#     output_string : str
#         Parsed output string
#     operands : list of array_like
#         The operands to use in the numpy contraction
# 
#     Examples
#     --------
#     The operand list is simplified to reduce printing:
# 
#     >>> a = np.random.rand(4, 4)
#     >>> b = np.random.rand(4, 4, 4)
#     >>> parse_einsum_input(('...a,...a->...', a, b))
#     ('za,xza', 'xz', [a, b])
# 
#     >>> parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
#     ('za,xza', 'xz', [a, b])
#     """
# 
#     if len(operands) == 0:
#         raise ValueError("No input operands")
# 
#     if isinstance(operands[0], str):
#         subscripts = operands[0].replace(" ", "")
#         operands = [possibly_convert_to_numpy(x) for x in operands[1:]]
# 
#     else:
#         subscripts, operands = convert_interleaved_input(operands)
# 
#     # Check for proper "->"
#     if ("-" in subscripts) or (">" in subscripts):
#         invalid = (subscripts.count("-") > 1) or (subscripts.count(">") > 1)
#         if invalid or (subscripts.count("->") != 1):
#             raise ValueError("Subscripts can only contain one '->'.")
# 
#     # Parse ellipses
#     if "." in subscripts:
#         used = subscripts.replace(".", "").replace(",", "").replace("->", "")
#         ellipse_inds = "".join(gen_unused_symbols(used, max(len(x.shape) for x in operands)))
#         longest = 0
# 
#         # Do we have an output to account for?
#         if "->" in subscripts:
#             input_tmp, output_sub = subscripts.split("->")
#             split_subscripts = input_tmp.split(",")
#             out_sub = True
#         else:
#             split_subscripts = subscripts.split(",")
#             out_sub = False
# 
#         for num, sub in enumerate(split_subscripts):
#             if "." in sub:
#                 if (sub.count(".") != 3) or (sub.count("...") != 1):
#                     raise ValueError("Invalid Ellipses.")
# 
#                 # Take into account numerical values
#                 if operands[num].shape == ():
#                     ellipse_count = 0
#                 else:
#                     ellipse_count = max(len(operands[num].shape), 1) - (len(sub) - 3)
# 
#                 if ellipse_count > longest:
#                     longest = ellipse_count
# 
#                 if ellipse_count < 0:
#                     raise ValueError("Ellipses lengths do not match.")
#                 elif ellipse_count == 0:
#                     split_subscripts[num] = sub.replace("...", "")
#                 else:
#                     split_subscripts[num] = sub.replace("...", ellipse_inds[-ellipse_count:])
# 
#         subscripts = ",".join(split_subscripts)
# 
#         # Figure out output ellipses
#         if longest == 0:
#             out_ellipse = ""
#         else:
#             out_ellipse = ellipse_inds[-longest:]
# 
#         if out_sub:
#             subscripts += "->" + output_sub.replace("...", out_ellipse)
#         else:
#             # Special care for outputless ellipses
#             output_subscript = find_output_str(subscripts)
#             normal_inds = "".join(sorted(set(output_subscript) - set(out_ellipse)))
# 
#             subscripts += "->" + out_ellipse + normal_inds
# 
#     # Build output string if does not exist
#     if "->" in subscripts:
#         input_subscripts, output_subscript = subscripts.split("->")
#     else:
#         input_subscripts, output_subscript = subscripts, find_output_str(subscripts)
# 
#     # Make sure output subscripts are in the input
#     for char in output_subscript:
#         if char not in input_subscripts:
#             raise ValueError("Output character '{}' did not appear in the input".format(char))
# 
#     # Make sure number operands is equivalent to the number of terms
#     if len(input_subscripts.split(",")) != len(operands):
#         raise ValueError("Number of einsum subscripts must be equal to the " "number of operands.")
# 
#     return input_subscripts, output_subscript, operands
# 
# Path: opt_einsum/typing.py



# Path: opt_einsum/sharing.py
import contextlib
import functools
import numbers
import threading
from collections import Counter, defaultdict
from typing import Any
from typing import Counter as CounterType
from typing import Dict, Generator, List, Optional, Tuple, Union
from .parser import alpha_canonicalize, parse_einsum_input
from .typing import ArrayType

"""
A module for sharing intermediates between contractions.

Copyright (c) 2018 Uber Technologies
"""



CacheKeyType = Union[Tuple[str, str, int, Tuple[int, ...]], Tuple[str, int]]

CacheType = Dict[CacheKeyType, ArrayType]