

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     lowerCAmelCase__							(		SCREAMING_SNAKE_CASE_:							Union[dict, list, tuple, torch.Tensor]					)	->					List[Tuple[int, ...]]:




				'''simple docstring'''





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

				return shapes



@torch.jit.ignore
def     lowerCAmelCase__							(		SCREAMING_SNAKE_CASE_:							int		,				SCREAMING_SNAKE_CASE_:							Tuple[int, ...]					)	->					Tuple[int, ...]:




				'''simple docstring'''





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

				return tuple(reversed(SCREAMING_SNAKE_CASE_					)					)



@torch.jit.ignore
def     lowerCAmelCase__							(		SCREAMING_SNAKE_CASE_:							Sequence[int]		,				SCREAMING_SNAKE_CASE_:							Sequence[int]		,				SCREAMING_SNAKE_CASE_:							Sequence[int]		,				SCREAMING_SNAKE_CASE_:							Optional[Sequence[bool]] = None		,				SCREAMING_SNAKE_CASE_:							Optional[Sequence[bool]] = None		,				)	->					List[Tuple[slice, ...]]:




				'''simple docstring'''





				def reduce_edge_list(SCREAMING_SNAKE_CASE_:							List[bool]					) -> None:
								A__						=	True
								for i in range(len(SCREAMING_SNAKE_CASE_					)					):
												A__						=	-1 * (i + 1)
												l[reversed_idx] &= tally
												A__						=	l[reversed_idx]

				if start_edges is None:
								A__						=	[s == 0 for s in start]
								reduce_edge_list(SCREAMING_SNAKE_CASE_					)
				if end_edges is None:
								A__						=	[e == (d - 1) for e, d in zip(SCREAMING_SNAKE_CASE_		,				SCREAMING_SNAKE_CASE_					)]
								reduce_edge_list(SCREAMING_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(SCREAMING_SNAKE_CASE_					) == 0:
								return [()]
				elif len(SCREAMING_SNAKE_CASE_					) == 1:
								return [(slice(start[0]		,				end[0] + 1					),)]

				A__						=	[]
				A__						=	[]

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

				A__						=	tuple(SCREAMING_SNAKE_CASE_					)
				A__						=	len(SCREAMING_SNAKE_CASE_					)

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

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

								A__						=	start[divergence_idx]
								return tuple(
								    path + (slice(SCREAMING_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

								A__						=	end[divergence_idx]
								return tuple(
								    path + (slice(SCREAMING_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()					)
								A__						=	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     lowerCAmelCase__							(		SCREAMING_SNAKE_CASE_:							torch.Tensor		,				SCREAMING_SNAKE_CASE_:							int		,				SCREAMING_SNAKE_CASE_:							int		,				SCREAMING_SNAKE_CASE_:							int					)	->					torch.Tensor:




				'''simple docstring'''





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

				# Get an ordered list of slices to perform
				A__						=	_get_minimal_slice_set(
				    SCREAMING_SNAKE_CASE_		,				SCREAMING_SNAKE_CASE_		,				SCREAMING_SNAKE_CASE_		,				)

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

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



def     lowerCAmelCase__							(		SCREAMING_SNAKE_CASE_:							Callable		,				SCREAMING_SNAKE_CASE_:							Dict[str, Any]		,				SCREAMING_SNAKE_CASE_:							int		,				SCREAMING_SNAKE_CASE_:							int		,				SCREAMING_SNAKE_CASE_:							bool = False		,				SCREAMING_SNAKE_CASE_:							Any = None		,				SCREAMING_SNAKE_CASE_:							bool = False		,				)	->					Any:




				'''simple docstring'''





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

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

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

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

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

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

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

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

								A__						=	tensor_tree_map(SCREAMING_SNAKE_CASE_		,				SCREAMING_SNAKE_CASE_					)

								# Run the layer on the chunk
								A__						=	layer(**SCREAMING_SNAKE_CASE_					)

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

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

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

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

								i += chunk_size

				A__						=	tensor_tree_map(lambda SCREAMING_SNAKE_CASE_					: t.view(orig_batch_dims + t.shape[1:]					)		,				SCREAMING_SNAKE_CASE_					)

				return out





class     a__  :
			"""simple docstring"""







			def __init__(			self					,					lowercase = 512					,					)     ->  Any:
							'''simple docstring'''






							A__						=	max_chunk_size
							A__						=	None
							A__						=	None


			def    UpperCamelCase						(			self					,					lowercase					,					lowercase					,					lowercase				)     ->  int:
							'''simple docstring'''






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

							if min_chunk_size >= self.max_chunk_size:
											return min_chunk_size

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

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

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

							return candidates[min_viable_chunk_size_index]


			def    UpperCamelCase						(			self					,					lowercase					,					lowercase				)     ->  bool:
							'''simple docstring'''






							A__						=	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				):
															A__						=	[v for _, v in sorted(aa.items()					,					key=lambda lowercase				: x[0]				)]
															A__						=	[v for _, v in sorted(aa.items()					,					key=lambda lowercase				: x[0]				)]
															consistent &= self._compare_arg_caches(lowercase					,					lowercase				)
											else:
															consistent &= aa == aa

							return consistent


			def    UpperCamelCase						(			self					,					lowercase					,					lowercase					,					lowercase					,					)     ->  int:
							'''simple docstring'''






							A__						=	True
							A__						=	tree_map(lambda lowercase				: 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				)
											A__						=	self._compare_arg_caches(self.cached_arg_data					,					lowercase				)
							else:
											# Otherwise, we can reuse the precomputed value
											A__						=	False

							if not consistent:
											A__						=	self._determine_favorable_chunk_size(
											    lowercase					,					lowercase					,					lowercase					,					)
											A__						=	arg_data

							assert self.cached_chunk_size is not None

							return self.cached_chunk_size






'''simple docstring'''


def 				__lowerCamelCase				(   A__    )       ->	list:






   """simple docstring"""
   UpperCamelCase					=  len(A__    )
   for i in range(1    ,						A__    ):
      UpperCamelCase					=  collection[i]
      UpperCamelCase					=  0
      UpperCamelCase					=  i - 1

      while low <= high:
         UpperCamelCase					=  (low + high) // 2
         if val < collection[mid]:
            UpperCamelCase					=  mid - 1
         else:
            UpperCamelCase					=  mid + 1
      for j in range(A__    ,						A__    ,						-1    ):
         UpperCamelCase					=  collection[j - 1]
      UpperCamelCase					=  val
   return collection


if __name__ == "__main__":
   _lowerCamelCase	:       int      	=	input("Enter numbers separated by a comma:\n").strip()
   _lowerCamelCase	:       Union[str, Any]      	=	[int(item) for item in user_input.split(",")]
   print(binary_insertion_sort(unsorted))

