"""simple docstring"""





import logging
import math
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union

import torch

from .tensor_utils import tensor_tree_map, tree_map





def 							a_	( _lowerCAmelCase    :					Union[dict, list, tuple, torch.Tensor]	):





       '''simple docstring'''

       lowercase__     :						Union[str, Any]         =      []
       if isinstance(_lowerCamelCase  ,					_lowerCamelCase	):
              for v in tree.values():
                     shapes.extend(_fetch_dims(_lowerCamelCase	)	)
       elif isinstance(_lowerCamelCase  ,					(list, tuple)	):
              for t in tree:
                     shapes.extend(_fetch_dims(_lowerCamelCase	)	)
       elif isinstance(_lowerCamelCase  ,					torch.Tensor	):
              shapes.append(tree.shape	)
       else:
              raise ValueError('Not supported'	)

       return shapes





@torch.jit.ignore
def 							a_	( _lowerCAmelCase    :					int  ,					_lowerCAmelCase    :					Tuple[int, ...]	):





       '''simple docstring'''

       lowercase__     :						Any         =      []
       for d in reversed(_lowerCamelCase	):
              idx.append(flat_idx % d	)
              lowercase__     :						Optional[int]         =      flat_idx // d

       return tuple(reversed(_lowerCamelCase	)	)





@torch.jit.ignore
def 							a_	( _lowerCAmelCase    :					Sequence[int]  ,					_lowerCAmelCase    :					Sequence[int]  ,					_lowerCAmelCase    :					Sequence[int]  ,					_lowerCAmelCase    :					Optional[Sequence[bool]] = None  ,					_lowerCAmelCase    :					Optional[Sequence[bool]] = None  ,					):





       '''simple docstring'''

       def reduce_edge_list(_lowerCAmelCase    :					List[bool]	) -> None:
              lowercase__     :						Optional[int]         =      True
              for i in range(len(_lowerCamelCase	)	):
                     lowercase__     :						int         =      -1 * (i + 1)
                     l[reversed_idx] &= tally
                     lowercase__     :						List[str]         =      l[reversed_idx]

       if start_edges is None:
              lowercase__     :						List[str]         =      [s == 0 for s in start]
              reduce_edge_list(_lowerCamelCase	)
       if end_edges is None:
              lowercase__     :						str         =      [e == (d - 1) for e, d in zip(_lowerCamelCase  ,					_lowerCamelCase	)]
              reduce_edge_list(_lowerCamelCase	)

       # Base cases. Either start/end are empty and we're done, or the final,
       # one-dimensional tensor can be simply sliced
       if len(_lowerCamelCase	) == 0:
              return [()]
       elif len(_lowerCamelCase	) == 1:
              return [(slice(start[0]  ,					end[0] + 1	),)]

       lowercase__     :						Optional[Any]         =      []
       lowercase__     :						List[Any]         =      []

       # Dimensions common to start and end can be selected directly
       for s, e in zip(_lowerCamelCase  ,					_lowerCamelCase	):
              if s == e:
                     path_list.append(slice(_lowerCamelCase  ,					s + 1	)	)
              else:
                     break

       lowercase__     :						Optional[Any]         =      tuple(_lowerCamelCase	)
       lowercase__     :						int         =      len(_lowerCamelCase	)

       # start == end, and we're done
       if divergence_idx == len(_lowerCamelCase	):
              return [path]

       def upper() -> Tuple[Tuple[slice, ...], ...]:
              assert start_edges is not None
              assert end_edges is not None

              lowercase__     :						Tuple         =      start[divergence_idx]
              return tuple(
                  path + (slice(_lowerCamelCase  ,					sdi + 1	),) + s
                  for s in _get_minimal_slice_set(
                      start[divergence_idx + 1 :]  ,					[d - 1 for d in dims[divergence_idx + 1 :]]  ,					dims[divergence_idx + 1 :]  ,					start_edges=start_edges[divergence_idx + 1 :]  ,					end_edges=[True for _ in end_edges[divergence_idx + 1 :]]  ,					)	)

       def lower() -> Tuple[Tuple[slice, ...], ...]:
              assert start_edges is not None
              assert end_edges is not None

              lowercase__     :						Optional[Any]         =      end[divergence_idx]
              return tuple(
                  path + (slice(_lowerCamelCase  ,					edi + 1	),) + s
                  for s in _get_minimal_slice_set(
                      [0 for _ in start[divergence_idx + 1 :]]  ,					end[divergence_idx + 1 :]  ,					dims[divergence_idx + 1 :]  ,					start_edges=[True for _ in start_edges[divergence_idx + 1 :]]  ,					end_edges=end_edges[divergence_idx + 1 :]  ,					)	)

       # If both start and end are at the edges of the subtree rooted at
       # divergence_idx, we can just select the whole subtree at once
       if start_edges[divergence_idx] and end_edges[divergence_idx]:
              slices.append(path + (slice(start[divergence_idx]  ,					end[divergence_idx] + 1	),)	)
       # If just start is at the edge, we can grab almost all of the subtree,
       # treating only the ragged bottom edge as an edge case
       elif start_edges[divergence_idx]:
              slices.append(path + (slice(start[divergence_idx]  ,					end[divergence_idx]	),)	)
              slices.extend(lower()	)
       # Analogous to the previous case, but the top is ragged this time
       elif end_edges[divergence_idx]:
              slices.extend(upper()	)
              slices.append(path + (slice(start[divergence_idx] + 1  ,					end[divergence_idx] + 1	),)	)
       # If both sides of the range are ragged, we need to handle both sides
       # separately. If there's contiguous meat in between them, we can index it
       # in one big chunk
       else:
              slices.extend(upper()	)
              lowercase__     :						Optional[int]         =      end[divergence_idx] - start[divergence_idx]
              if middle_ground > 1:
                     slices.append(path + (slice(start[divergence_idx] + 1  ,					end[divergence_idx]	),)	)
              slices.extend(lower()	)

       return slices





@torch.jit.ignore
def 							a_	( _lowerCAmelCase    :					torch.Tensor  ,					_lowerCAmelCase    :					int  ,					_lowerCAmelCase    :					int  ,					_lowerCAmelCase    :					int	):





       '''simple docstring'''

       lowercase__     :						Dict         =      t.shape[:no_batch_dims]
       lowercase__     :						Union[str, Any]         =      list(_flat_idx_to_idx(_lowerCamelCase  ,					_lowerCamelCase	)	)
       # _get_minimal_slice_set is inclusive
       lowercase__     :						int         =      list(_flat_idx_to_idx(flat_end - 1  ,					_lowerCamelCase	)	)

       # Get an ordered list of slices to perform
       lowercase__     :						List[str]         =      _get_minimal_slice_set(
           _lowerCamelCase  ,					_lowerCamelCase  ,					_lowerCamelCase  ,					)

       lowercase__     :						Union[str, Any]         =      [t[s] for s in slices]

       return torch.cat([s.view((-1,) + t.shape[no_batch_dims:]	) for s in sliced_tensors]	)





def 							a_	( _lowerCAmelCase    :					Callable  ,					_lowerCAmelCase    :					Dict[str, Any]  ,					_lowerCAmelCase    :					int  ,					_lowerCAmelCase    :					int  ,					_lowerCAmelCase    :					bool = False  ,					_lowerCAmelCase    :					Any = None  ,					_lowerCAmelCase    :					bool = False  ,					):





       '''simple docstring'''

       if not (len(_lowerCamelCase	) > 0):
              raise ValueError('Must provide at least one input'	)

       lowercase__     :						List[Any]         =      [shape[:no_batch_dims] for shape in _fetch_dims(_lowerCamelCase	)]
       lowercase__     :						List[str]         =      tuple([max(_lowerCamelCase	) for s in zip(*_lowerCamelCase	)]	)

       def _prep_inputs(_lowerCAmelCase    :					torch.Tensor	) -> torch.Tensor:
              if not low_mem:
                     if not sum(t.shape[:no_batch_dims]	) == no_batch_dims:
                            lowercase__     :						str         =      t.expand(orig_batch_dims + t.shape[no_batch_dims:]	)
                     lowercase__     :						Optional[Any]         =      t.reshape(-1  ,					*t.shape[no_batch_dims:]	)
              else:
                     lowercase__     :						Dict         =      t.expand(orig_batch_dims + t.shape[no_batch_dims:]	)
              return t

       lowercase__     :						Dict         =      tensor_tree_map(_prep_inputs  ,					_lowerCamelCase	)
       lowercase__     :						Dict         =      None
       if _out is not None:
              lowercase__     :						Optional[Any]         =      tensor_tree_map(lambda _lowerCAmelCase	: t.view([-1] + list(t.shape[no_batch_dims:]	)	)  ,					_out	)

       lowercase__     :						List[Any]         =      1
       for d in orig_batch_dims:
              flat_batch_dim *= d

       lowercase__     :						int         =      flat_batch_dim // chunk_size + (flat_batch_dim % chunk_size != 0)

       def _select_chunk(_lowerCAmelCase    :					torch.Tensor	) -> torch.Tensor:
              return t[i : i + chunk_size] if t.shape[0] != 1 else t

       lowercase__     :						int         =      0
       lowercase__     :						Any         =      prepped_outputs
       for _ in range(_lowerCamelCase	):
              # Chunk the input
              if not low_mem:
                     lowercase__     :						List[str]         =      _select_chunk
              else:
                     lowercase__     :						int         =      partial(
                         _chunk_slice  ,					flat_start=_lowerCamelCase  ,					flat_end=min(_lowerCamelCase  ,					i + chunk_size	)  ,					no_batch_dims=len(_lowerCamelCase	)  ,					)

              lowercase__     :						List[str]         =      tensor_tree_map(_lowerCamelCase  ,					_lowerCamelCase	)

              # Run the layer on the chunk
              lowercase__     :						Optional[int]         =      layer(**_lowerCamelCase	)

              # Allocate space for the output
              if out is None:
                     lowercase__     :						List[Any]         =      tensor_tree_map(lambda _lowerCAmelCase	: t.new_zeros((flat_batch_dim,) + t.shape[1:]	)  ,					_lowerCamelCase	)

              # Put the chunk in its pre-allocated space
              if isinstance(_lowerCamelCase  ,					_lowerCamelCase	):

                     def assign(_lowerCAmelCase    :					dict  ,					_lowerCAmelCase    :					dict	) -> None:
                            for k, v in da.items():
                                   if isinstance(_lowerCamelCase  ,					_lowerCamelCase	):
                                          assign(_lowerCamelCase  ,					da[k]	)
                                   else:
                                          if _add_into_out:
                                                 v[i : i + chunk_size] += da[k]
                                          else:
                                                 lowercase__     :						Any         =      da[k]

                     assign(_lowerCamelCase  ,					_lowerCamelCase	)
              elif isinstance(_lowerCamelCase  ,					_lowerCamelCase	):
                     for xa, xa in zip(_lowerCamelCase  ,					_lowerCamelCase	):
                            if _add_into_out:
                                   xa[i : i + chunk_size] += xa
                            else:
                                   lowercase__     :						int         =      xa
              elif isinstance(_lowerCamelCase  ,					torch.Tensor	):
                     if _add_into_out:
                            out[i : i + chunk_size] += output_chunk
                     else:
                            lowercase__     :						List[Any]         =      output_chunk
              else:
                     raise ValueError('Not supported'	)

              i += chunk_size

       lowercase__     :						List[Any]         =      tensor_tree_map(lambda _lowerCAmelCase	: t.view(orig_batch_dims + t.shape[1:]	)  ,					_lowerCamelCase	)

       return out






class 	UpperCAmelCase_      :


      def __init__(  self						,     a = 5_1_2						,     )			->   str:
             lowercase__     :						Dict         =      max_chunk_size
             lowercase__     :						Union[str, Any]         =      None
             lowercase__     :						List[Any]         =      None


      def 						_UpperCAmelCase			(  self						,     a						,     a						,     a				)			->   int:
             logging.info('Tuning chunk size...'				)

             if min_chunk_size >= self.max_chunk_size:
                    return min_chunk_size

             lowercase__     :						int         =      [2**l for l in range(int(math.log(self.max_chunk_size						,     2				)				) + 1				)]
             lowercase__     :						Dict         =      [c for c in candidates if c > min_chunk_size]
             lowercase__     :						Tuple         =      [min_chunk_size] + candidates
             candidates[-1] += 4

             def test_chunk_size(a				) -> bool:
                    try:
                           with torch.no_grad():
                                  fn(*lowercase_						,     chunk_size=lowercase_				)
                           return True
                    except RuntimeError:
                           return False

             lowercase__     :						Any         =      0
             lowercase__     :						Dict         =      len(lowercase_				) - 1
             while i > min_viable_chunk_size_index:
                    lowercase__     :						Optional[Any]         =      test_chunk_size(candidates[i]				)
                    if not viable:
                           lowercase__     :						int         =      (min_viable_chunk_size_index + i) // 2
                    else:
                           lowercase__     :						int         =      i
                           lowercase__     :						List[str]         =      (i + len(lowercase_				) - 1) // 2

             return candidates[min_viable_chunk_size_index]


      def 						_UpperCAmelCase			(  self						,     a						,     a				)			->   bool:
             lowercase__     :						int         =      True
             for aa, aa in zip(lowercase_						,     lowercase_				):
                    assert type(lowercase_				) == type(lowercase_				)
                    if isinstance(lowercase_						,     (list, tuple)				):
                           consistent &= self._compare_arg_caches(lowercase_						,     lowercase_				)
                    elif isinstance(lowercase_						,     lowercase_				):
                           lowercase__     :						List[str]         =      [v for _, v in sorted(aa.items()						,     key=lambda a				: x[0]				)]
                           lowercase__     :						int         =      [v for _, v in sorted(aa.items()						,     key=lambda a				: x[0]				)]
                           consistent &= self._compare_arg_caches(lowercase_						,     lowercase_				)
                    else:
                           consistent &= aa == aa

             return consistent


      def 						_UpperCAmelCase			(  self						,     a						,     a						,     a						,     )			->   int:
             lowercase__     :						int         =      True
             lowercase__     :						Tuple         =      tree_map(lambda a				: a.shape if isinstance(lowercase_						,     torch.Tensor				) else a						,     lowercase_						,     lowercase_				)
             if self.cached_arg_data is not None:
                    # If args have changed shape/value, we need to re-tune
                    assert len(self.cached_arg_data				) == len(lowercase_				)
                    lowercase__     :						List[Any]         =      self._compare_arg_caches(self.cached_arg_data						,     lowercase_				)
             else:
                    # Otherwise, we can reuse the precomputed value
                    lowercase__     :						Dict         =      False

             if not consistent:
                    lowercase__     :						Optional[int]         =      self._determine_favorable_chunk_size(
                        lowercase_						,     lowercase_						,     lowercase_						,     )
                    lowercase__     :						Any         =      arg_data

             assert self.cached_chunk_size is not None

             return self.cached_chunk_size








'''simple docstring'''

from string import ascii_uppercase

__lowerCAmelCase				:      Union[str, Any]						      ={str(ord(c) - 55): c for c in ascii_uppercase}





def 		UpperCamelCase							(				_lowerCamelCase   :   int		,			_lowerCamelCase   :   int			):
    if isinstance(_lowerCamelCase		,			_lowerCamelCase			):
        raise TypeError("int() can't convert non-string with explicit base"			)
    if num < 0:
        raise ValueError("parameter must be positive int"			)
    if isinstance(_lowerCamelCase		,			_lowerCamelCase			):
        raise TypeError("'str' object cannot be interpreted as an integer"			)
    if isinstance(_lowerCamelCase		,			_lowerCamelCase			):
        raise TypeError("'float' object cannot be interpreted as an integer"			)
    if base in (0, 1):
        raise ValueError("base must be >= 2"			)
    if base > 36:
        raise ValueError("base must be <= 36"			)
    A__							=     ""
    A__							=     0
    A__							=     0
    while div != 1:
        A__,      A__							=     divmod(_lowerCamelCase		,			_lowerCamelCase			)
        if base >= 11 and 9 < mod < 36:
            A__							=     ALPHABET_VALUES[str(_lowerCamelCase			)]
        else:
            A__							=     str(_lowerCamelCase			)
        new_value += actual_value
        A__							=     num // base
        A__							=     div
        if div == 0:
            return str(new_value[::-1]			)
        elif div == 1:
            new_value += str(_lowerCamelCase			)
            return str(new_value[::-1]			)

    return new_value[::-1]


if __name__ == "__main__":
  import doctest

  doctest.testmod()

  for base in range(2, 37):
    for num in range(1000):
      assert int(decimal_to_any(num, base), base) == num, (
          num,
          base,
          decimal_to_any(num, base),
          int(decimal_to_any(num, base), base),
      )



