


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						(						_lowercase      ) ->      int:


     """simple docstring"""


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

     return shapes






@torch.jit.ignore
def 						_A						(						_lowercase ,   _lowercase      ) ->      List[Any]:


     """simple docstring"""


     __UpperCamelCase      =       []
     for d in reversed(snake_case__      ):
          idx.append(flat_idx % d      )
          __UpperCamelCase      =       flat_idx // d

     return tuple(reversed(snake_case__      )      )






@torch.jit.ignore
def 						_A						(						_lowercase ,   _lowercase ,   _lowercase ,   _lowercase = None ,   _lowercase = None ,   ) ->      Optional[int]:


     """simple docstring"""


     def reduce_edge_list(_lowercase      ) -> None:
          __UpperCamelCase      =       True
          for i in range(len(snake_case__      )      ):
               __UpperCamelCase      =       -1 * (i + 1)
               l[reversed_idx] &= tally
               __UpperCamelCase      =       l[reversed_idx]

     if start_edges is None:
          __UpperCamelCase      =       [s == 0 for s in start]
          reduce_edge_list(snake_case__      )
     if end_edges is None:
          __UpperCamelCase      =       [e == (d - 1) for e, d in zip(snake_case__ ,   snake_case__      )]
          reduce_edge_list(snake_case__      )

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

     __UpperCamelCase      =       []
     __UpperCamelCase      =       []

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

     __UpperCamelCase      =       tuple(snake_case__      )
     __UpperCamelCase      =       len(snake_case__      )

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

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

          __UpperCamelCase      =       start[divergence_idx]
          return tuple(
              path + (slice(snake_case__ ,   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

          __UpperCamelCase      =       end[divergence_idx]
          return tuple(
              path + (slice(snake_case__ ,   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()      )
          __UpperCamelCase      =       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						(						_lowercase ,   _lowercase ,   _lowercase ,   _lowercase      ) ->      Dict:


     """simple docstring"""


     __UpperCamelCase      =       t.shape[:no_batch_dims]
     __UpperCamelCase      =       list(_flat_idx_to_idx(snake_case__ ,   snake_case__      )      )
     # _get_minimal_slice_set is inclusive
     __UpperCamelCase      =       list(_flat_idx_to_idx(flat_end - 1 ,   snake_case__      )      )

     # Get an ordered list of slices to perform
     __UpperCamelCase      =       _get_minimal_slice_set(
         snake_case__ ,   snake_case__ ,   snake_case__ ,   )

     __UpperCamelCase      =       [t[s] for s in slices]

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






def 						_A						(						_lowercase ,   _lowercase ,   _lowercase ,   _lowercase ,   _lowercase = False ,   _lowercase = None ,   _lowercase = False ,   ) ->      Dict:


     """simple docstring"""


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

     __UpperCamelCase      =       [shape[:no_batch_dims] for shape in _fetch_dims(snake_case__      )]
     __UpperCamelCase      =       tuple([max(snake_case__      ) for s in zip(*snake_case__      )]      )

     def _prep_inputs(_lowercase      ) -> torch.Tensor:
          if not low_mem:
               if not sum(t.shape[:no_batch_dims]      ) == no_batch_dims:
                    __UpperCamelCase      =       t.expand(orig_batch_dims + t.shape[no_batch_dims:]      )
               __UpperCamelCase      =       t.reshape(-1 ,   *t.shape[no_batch_dims:]      )
          else:
               __UpperCamelCase      =       t.expand(orig_batch_dims + t.shape[no_batch_dims:]      )
          return t

     __UpperCamelCase      =       tensor_tree_map(_prep_inputs ,   snake_case__      )
     __UpperCamelCase      =       None
     if _out is not None:
          __UpperCamelCase      =       tensor_tree_map(lambda _lowercase      : t.view([-1] + list(t.shape[no_batch_dims:]      )      ) ,   _out      )

     __UpperCamelCase      =       1
     for d in orig_batch_dims:
          flat_batch_dim *= d

     __UpperCamelCase      =       flat_batch_dim // chunk_size + (flat_batch_dim % chunk_size != 0)

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

     __UpperCamelCase      =       0
     __UpperCamelCase      =       prepped_outputs
     for _ in range(snake_case__      ):
          # Chunk the input
          if not low_mem:
               __UpperCamelCase      =       _select_chunk
          else:
               __UpperCamelCase      =       partial(
                   _chunk_slice ,   flat_start=snake_case__ ,   flat_end=min(snake_case__ ,   i + chunk_size      ) ,   no_batch_dims=len(snake_case__      ) ,   )

          __UpperCamelCase      =       tensor_tree_map(snake_case__ ,   snake_case__      )

          # Run the layer on the chunk
          __UpperCamelCase      =       layer(**snake_case__      )

          # Allocate space for the output
          if out is None:
               __UpperCamelCase      =       tensor_tree_map(lambda _lowercase      : t.new_zeros((flat_batch_dim,) + t.shape[1:]      ) ,   snake_case__      )

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

               def assign(_lowercase ,   _lowercase      ) -> None:
                    for k, v in da.items():
                         if isinstance(snake_case__ ,   snake_case__      ):
                              assign(snake_case__ ,   da[k]      )
                         else:
                              if _add_into_out:
                                   v[i : i + chunk_size] += da[k]
                              else:
                                   __UpperCamelCase      =       da[k]

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

          i += chunk_size

     __UpperCamelCase      =       tensor_tree_map(lambda _lowercase      : t.view(orig_batch_dims + t.shape[1:]      ) ,   snake_case__      )

     return out



class      __lowerCamelCase       :







       def __init__(       self:      Union[str, Any],A_:      int = 512,):






            '''simple docstring'''




            __UpperCamelCase      =       max_chunk_size
            __UpperCamelCase      =       None
            __UpperCamelCase      =       None







       def 						snake_case_     (       self:      Dict,A_:      Callable,A_:      tuple,A_:      int ):






            '''simple docstring'''




            logging.info('Tuning chunk size...' )

            if min_chunk_size >= self.max_chunk_size:
                 return min_chunk_size

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

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

            __UpperCamelCase      =       0
            __UpperCamelCase      =       len(SCREAMING_SNAKE_CASE_ ) - 1
            while i > min_viable_chunk_size_index:
                 __UpperCamelCase      =       test_chunk_size(candidates[i] )
                 if not viable:
                      __UpperCamelCase      =       (min_viable_chunk_size_index + i) // 2
                 else:
                      __UpperCamelCase      =       i
                      __UpperCamelCase      =       (i + len(SCREAMING_SNAKE_CASE_ ) - 1) // 2

            return candidates[min_viable_chunk_size_index]







       def 						snake_case_     (       self:      Tuple,A_:      Iterable,A_:      Iterable ):






            '''simple docstring'''




            __UpperCamelCase      =       True
            for aa, aa in zip(SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_ ):
                 assert type(SCREAMING_SNAKE_CASE_ ) == type(SCREAMING_SNAKE_CASE_ )
                 if isinstance(SCREAMING_SNAKE_CASE_,(list, tuple) ):
                      consistent &= self._compare_arg_caches(SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_ )
                 elif isinstance(SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_ ):
                      __UpperCamelCase      =       [v for _, v in sorted(aa.items(),key=lambda A_ : x[0] )]
                      __UpperCamelCase      =       [v for _, v in sorted(aa.items(),key=lambda A_ : x[0] )]
                      consistent &= self._compare_arg_caches(SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_ )
                 else:
                      consistent &= aa == aa

            return consistent







       def 						snake_case_     (       self:      Tuple,A_:      Callable,A_:      tuple,A_:      int,):






            '''simple docstring'''




            __UpperCamelCase      =       True
            __UpperCamelCase      =       tree_map(lambda A_ : a.shape if isinstance(SCREAMING_SNAKE_CASE_,torch.Tensor ) else a,SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_ )
            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(SCREAMING_SNAKE_CASE_ )
                 __UpperCamelCase      =       self._compare_arg_caches(self.cached_arg_data,SCREAMING_SNAKE_CASE_ )
            else:
                 # Otherwise, we can reuse the precomputed value
                 __UpperCamelCase      =       False

            if not consistent:
                 __UpperCamelCase      =       self._determine_favorable_chunk_size(
                     SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_,SCREAMING_SNAKE_CASE_,)
                 __UpperCamelCase      =       arg_data

            assert self.cached_chunk_size is not None

            return self.cached_chunk_size




def 				a						(							snake_case__:					list					):


				'''simple docstring'''

				if len(snake_case__					) <= 1:
								return [tuple(snake_case__					)]

				lowercase_       =					[]

				def generate(snake_case__:					int						,   snake_case__:					list					):
								if k == 1:
												res.append(tuple(arr[:]					)					)
												return

								generate(k - 1						,   snake_case__					)

								for i in range(k - 1					):
												if k % 2 == 0:  # k is even
																lowercase_  ,	lowercase_       =					arr[k - 1], arr[i]
												else:  # k is odd
																lowercase_  ,	lowercase_       =					arr[k - 1], arr[0]
												generate(k - 1						,   snake_case__					)

				generate(len(snake_case__					)						,   snake_case__					)
				return res


if __name__ == "__main__":
						__a  			=					input('Enter numbers separated by a comma:\n').strip()
						__a  			=					[int(item) for item in user_input.split(',')]
						print(heaps(arr))




