# Path: opt_einsum/helpers.py
# @overload
# def compute_size_by_dict(indices: Iterable[int], idx_dict: List[int]) -> int:
#     ...
# 
# def flop_count(
#     idx_contraction: Collection[str],
#     inner: bool,
#     num_terms: int,
#     size_dictionary: Dict[str, int],
# ) -> int:
#     """
#     Computes the number of FLOPS in the contraction.
# 
#     Parameters
#     ----------
#     idx_contraction : iterable
#         The indices involved in the contraction
#     inner : bool
#         Does this contraction require an inner product?
#     num_terms : int
#         The number of terms in a contraction
#     size_dictionary : dict
#         The size of each of the indices in idx_contraction
# 
#     Returns
#     -------
#     flop_count : int
#         The total number of FLOPS required for the contraction.
# 
#     Examples
#     --------
# 
#     >>> flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5})
#     30
# 
#     >>> flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5})
#     60
# 
#     """
# 
#     overall_size = compute_size_by_dict(idx_contraction, size_dictionary)
#     op_factor = max(1, num_terms - 1)
#     if inner:
#         op_factor += 1
# 
#     return overall_size * op_factor
# 
# Path: opt_einsum/typing.py



import functools
import heapq
import itertools
import operator
import random
import re
import numpy as np
from collections import Counter, OrderedDict, defaultdict
from typing import Any, Callable
from typing import Counter as CounterType
from typing import Dict, FrozenSet, Generator, List, Optional, Sequence, Set, Tuple, Union
from .helpers import compute_size_by_dict, flop_count
from .typing import ArrayIndexType, PathType
    from .path_random import random_greedy_128
    i: int,
    j: int,
    size_dict: Dict[str, int],
) -> Tuple[FrozenSet[str], int]:
    """
    Calculate the resulting indices and flops for a potential pairwise
    contraction - used in the recursive (optimal/branch) algorithms.

    **Parameters:**

    - **inputs** - *(tuple[frozenset[str]])* The indices of each tensor in this contraction, note this includes
        tensors unavailable to contract as static single assignment is used ->
        contracted tensors are not removed from the list.
    - **output** - *(frozenset[str])* The set of output indices for the whole contraction.
    - **remaining** - *(frozenset[int])* The set of indices (corresponding to ``inputs``) of tensors still available to contract.
    - **i** - *(int)* Index of potential tensor to contract.
    - **j** - *(int)* Index of potential tensor to contract.
    - **size_dict : dict[str, int] )* Size mapping of all the indices.

    **Returns:**

    - **k12** - *(frozenset)* The resulting indices of the potential tensor.
    - **cost** - *(int)* Estimated flop count of operation.
    """
    k1, k2 = inputs[i], inputs[j]
    either = k1 | k2
    shared = k1 & k2
    keep = frozenset.union(output, *map(inputs.__getitem__, remaining - {i, j}))

    k12 = either & keep

# Path: opt_einsum/helpers.py
# @overload
# def compute_size_by_dict(indices: Iterable[int], idx_dict: List[int]) -> int:
#     ...
# 
# def flop_count(
#     idx_contraction: Collection[str],
#     inner: bool,
#     num_terms: int,
#     size_dictionary: Dict[str, int],
# ) -> int:
#     """
#     Computes the number of FLOPS in the contraction.
# 
#     Parameters
#     ----------
#     idx_contraction : iterable
#         The indices involved in the contraction
#     inner : bool
#         Does this contraction require an inner product?
#     num_terms : int
#         The number of terms in a contraction
#     size_dictionary : dict
#         The size of each of the indices in idx_contraction
# 
#     Returns
#     -------
#     flop_count : int
#         The total number of FLOPS required for the contraction.
# 
#     Examples
#     --------
# 
#     >>> flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5})
#     30
# 
#     >>> flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5})
#     60
# 
#     """
# 
#     overall_size = compute_size_by_dict(idx_contraction, size_dictionary)
#     op_factor = max(1, num_terms - 1)
#     if inner:
#         op_factor += 1
# 
#     return overall_size * op_factor
# 
# Path: opt_einsum/typing.py



# Path: opt_einsum/paths.py
import functools
import heapq
import itertools
import operator
import random
import re
import numpy as np
from collections import Counter, OrderedDict, defaultdict
from typing import Any, Callable
from typing import Counter as CounterType
from typing import Dict, FrozenSet, Generator, List, Optional, Sequence, Set, Tuple, Union
from .helpers import compute_size_by_dict, flop_count
from .typing import ArrayIndexType, PathType
    from .path_random import random_greedy_128

    i: int,
    j: int,
    size_dict: Dict[str, int],
) -> Tuple[FrozenSet[str], int]:
    """
    Calculate the resulting indices and flops for a potential pairwise
    contraction - used in the recursive (optimal/branch) algorithms.

    **Parameters:**

    - **inputs** - *(tuple[frozenset[str]])* The indices of each tensor in this contraction, note this includes
        tensors unavailable to contract as static single assignment is used ->
        contracted tensors are not removed from the list.
    - **output** - *(frozenset[str])* The set of output indices for the whole contraction.
    - **remaining** - *(frozenset[int])* The set of indices (corresponding to ``inputs``) of tensors still available to contract.
    - **i** - *(int)* Index of potential tensor to contract.
    - **j** - *(int)* Index of potential tensor to contract.
    - **size_dict : dict[str, int] )* Size mapping of all the indices.

    **Returns:**

    - **k12** - *(frozenset)* The resulting indices of the potential tensor.
    - **cost** - *(int)* Estimated flop count of operation.
    """
    k1, k2 = inputs[i], inputs[j]
    either = k1 | k2
    shared = k1 & k2
    keep = frozenset.union(output, *map(inputs.__getitem__, remaining - {i, j}))

    k12 = either & keep

    cost = flop_count(either, bool(shared - keep), 2, size_dict)