
"""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		(       snake_case   :Union[dict, list, tuple, torch.Tensor]	)   ->			List[Tuple[int, ...]]:
 __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		(       snake_case   :int       ,      snake_case   :Tuple[int, ...]	)   ->			Tuple[int, ...]:
 __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		(       snake_case   :Sequence[int]       ,      snake_case   :Sequence[int]       ,      snake_case   :Sequence[int]       ,      snake_case   :Optional[Sequence[bool]] = None       ,      snake_case   :Optional[Sequence[bool]] = None       ,      )   ->			List[Tuple[slice, ...]]:

 # start_edges and end_edges both indicate whether, starting from any given
 # dimension, the start/end index is at the top/bottom edge of the
 # corresponding tensor, modeled as a tree
 def reduce_edge_list(snake_case   :List[bool]	) -> 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		(       snake_case   :torch.Tensor       ,      snake_case   :int       ,      snake_case   :int       ,      snake_case   :int	)   ->			torch.Tensor:
 __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		(       snake_case   :Callable       ,      snake_case   :Dict[str, Any]       ,      snake_case   :int       ,      snake_case   :int       ,      snake_case   :bool = False       ,      snake_case   :Any = None       ,      snake_case   :bool = False       ,      )   ->			Any:
 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(snake_case   :torch.Tensor	) -> 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 snake_case	: 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(snake_case   :torch.Tensor	) -> 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 snake_case	: 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(snake_case   :dict       ,      snake_case   :dict	) -> 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 snake_case	: t.view(orig_batch_dims + t.shape[1:]	)       ,      snake_case	)

 return out





class __lowerCAmelCase	:





     def __init__(			self			,  __UpperCAmelCase = 512			,  ):

      '''simple docstring'''




      __UpperCamelCase       						= max_chunk_size
      __UpperCamelCase       						= None
      __UpperCamelCase       						= None





     def 		UpperCAmelCase      (			self			,  __UpperCAmelCase			,  __UpperCAmelCase			,  __UpperCAmelCase   ):

      '''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(__UpperCAmelCase   ) -> bool:
       try:
        with torch.no_grad():
         fn(*__UpperCAmelCase			,  chunk_size=__UpperCAmelCase   )
        return True
       except RuntimeError:
        return False

      __UpperCamelCase       						= 0
      __UpperCamelCase       						= len(__UpperCAmelCase   ) - 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(__UpperCAmelCase   ) - 1) // 2

      return candidates[min_viable_chunk_size_index]





     def 		UpperCAmelCase      (			self			,  __UpperCAmelCase			,  __UpperCAmelCase   ):

      '''simple docstring'''




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

      return consistent







     def 		UpperCAmelCase      (			self			,  __UpperCAmelCase			,  __UpperCAmelCase			,  __UpperCAmelCase			,  ):

      '''simple docstring'''




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

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

      assert self.cached_chunk_size is not None

      return self.cached_chunk_size





"""simple docstring"""

import argparse
import struct
import unittest

class __lowerCAmelCase	:





     def __init__(			self			,  __UpperCAmelCase   ):

      '''simple docstring'''




      __UpperCamelCase       						= data

      # Initialize hash values
      __UpperCamelCase       						= [
          0x6a_09_e6_67,
          0xbb_67_ae_85,
          0x3c_6e_f3_72,
          0xa5_4f_f5_3a,
          0x51_0e_52_7f,
          0x9b_05_68_8c,
          0x1f_83_d9_ab,
          0x5b_e0_cd_19,
      ]

      # Initialize round constants
      __UpperCamelCase       						= [
          0x42_8a_2f_98,
          0x71_37_44_91,
          0xb5_c0_fb_cf,
          0xe9_b5_db_a5,
          0x39_56_c2_5b,
          0x59_f1_11_f1,
          0x92_3f_82_a4,
          0xab_1c_5e_d5,
          0xd8_07_aa_98,
          0x12_83_5b_01,
          0x24_31_85_be,
          0x55_0c_7d_c3,
          0x72_be_5d_74,
          0x80_de_b1_fe,
          0x9b_dc_06_a7,
          0xc1_9b_f1_74,
          0xe4_9b_69_c1,
          0xef_be_47_86,
          0x0f_c1_9d_c6,
          0x24_0c_a1_cc,
          0x2d_e9_2c_6f,
          0x4a_74_84_aa,
          0x5c_b0_a9_dc,
          0x76_f9_88_da,
          0x98_3e_51_52,
          0xa8_31_c6_6d,
          0xb0_03_27_c8,
          0xbf_59_7f_c7,
          0xc6_e0_0b_f3,
          0xd5_a7_91_47,
          0x06_ca_63_51,
          0x14_29_29_67,
          0x27_b7_0a_85,
          0x2e_1b_21_38,
          0x4d_2c_6d_fc,
          0x53_38_0d_13,
          0x65_0a_73_54,
          0x76_6a_0a_bb,
          0x81_c2_c9_2e,
          0x92_72_2c_85,
          0xa2_bf_e8_a1,
          0xa8_1a_66_4b,
          0xc2_4b_8b_70,
          0xc7_6c_51_a3,
          0xd1_92_e8_19,
          0xd6_99_06_24,
          0xf4_0e_35_85,
          0x10_6a_a0_70,
          0x19_a4_c1_16,
          0x1e_37_6c_08,
          0x27_48_77_4c,
          0x34_b0_bc_b5,
          0x39_1c_0c_b3,
          0x4e_d8_aa_4a,
          0x5b_9c_ca_4f,
          0x68_2e_6f_f3,
          0x74_8f_82_ee,
          0x78_a5_63_6f,
          0x84_c8_78_14,
          0x8c_c7_02_08,
          0x90_be_ff_fa,
          0xa4_50_6c_eb,
          0xbe_f9_a3_f7,
          0xc6_71_78_f2,
      ]

      __UpperCamelCase       						= self.preprocessing(self.data   )
      self.final_hash()





     @staticmethod
     def 		UpperCAmelCase      (			__UpperCAmelCase   ):

      '''simple docstring'''




      __UpperCamelCase       						= b'\x80' + (b'\x00' * (63 - (len(__UpperCAmelCase   ) + 8) % 64))
      __UpperCamelCase       						= struct.pack('>Q'			,  (len(__UpperCAmelCase   ) * 8)   )
      return data + padding + big_endian_integer





     def 		UpperCAmelCase      (			self   ):

      '''simple docstring'''




      __UpperCamelCase       						= [
          self.preprocessed_data[x : x + 64]
          for x in range(0			,  len(self.preprocessed_data   )			,  64   )
      ]

      for block in self.blocks:
       # Convert the given block into a list of 4 byte integers
       __UpperCamelCase       						= list(struct.unpack('>16L'			,  __UpperCAmelCase   )   )
       # add 48 0-ed integers
       words += [0] * 48

       __UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase       						= self.hashes

       for index in range(0			,  64   ):
        if index > 15:
         # modify the zero-ed indexes at the end of the array
         __UpperCamelCase       						= (
             self.ror(words[index - 15]			,  7   )
             ^ self.ror(words[index - 15]			,  18   )
             ^ (words[index - 15] >> 3)
         )
         __UpperCamelCase       						= (
             self.ror(words[index - 2]			,  17   )
             ^ self.ror(words[index - 2]			,  19   )
             ^ (words[index - 2] >> 10)
         )

         __UpperCamelCase       						= (
             words[index - 16] + sa + words[index - 7] + sa
         ) % 0x1_00_00_00_00

        # Compression
        __UpperCamelCase       						= self.ror(__UpperCAmelCase			,  6   ) ^ self.ror(__UpperCAmelCase			,  11   ) ^ self.ror(__UpperCAmelCase			,  25   )
        __UpperCamelCase       						= (e & f) ^ ((~e & 0xff_ff_ff_ff) & g)
        __UpperCamelCase       						= (
            h + sa + ch + self.round_constants[index] + words[index]
        ) % 0x1_00_00_00_00
        __UpperCamelCase       						= self.ror(__UpperCAmelCase			,  2   ) ^ self.ror(__UpperCAmelCase			,  13   ) ^ self.ror(__UpperCAmelCase			,  22   )
        __UpperCamelCase       						= (a & b) ^ (a & c) ^ (b & c)
        __UpperCamelCase       						= (sa + maj) % 0x1_00_00_00_00

        __UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase						,			__UpperCamelCase       						= (
            g,
            f,
            e,
            ((d + tempa) % 0x1_00_00_00_00),
            c,
            b,
            a,
            ((tempa + tempa) % 0x1_00_00_00_00),
        )

       __UpperCamelCase       						= [a, b, c, d, e, f, g, h]

       # Modify final values
       __UpperCamelCase       						= [
           ((element + mutated_hash_values[index]) % 0x1_00_00_00_00)
           for index, element in enumerate(self.hashes   )
       ]

      __UpperCamelCase       						= ''.join([hex(__UpperCAmelCase   )[2:].zfill(8   ) for value in self.hashes]   )







     def 		UpperCAmelCase      (			self			,  __UpperCAmelCase			,  __UpperCAmelCase   ):

      '''simple docstring'''




      return 0xff_ff_ff_ff & (value << (32 - rotations)) | (value >> rotations)


class __lowerCAmelCase	(   unittest.TestCase    ):





     def 		UpperCAmelCase      (			self   ):

      '''simple docstring'''




      import hashlib

      __UpperCamelCase       						= bytes('Test String'			,  'utf-8'   )
      self.assertEqual(SHAaaa(__UpperCAmelCase   ).hash			,  hashlib.shaaaa(__UpperCAmelCase   ).hexdigest()   )






def 							A		(       )   ->			None:
 import doctest

 doctest.testmod()

 __UpperCamelCase       						= argparse.ArgumentParser()
 parser.add_argument(
     '-s'       ,      '--string'       ,      dest='input_string'       ,      default='Hello World!! Welcome to Cryptography'       ,      help='Hash the string'       ,      )
 parser.add_argument(
     '-f'       ,      '--file'       ,      dest='input_file'       ,      help='Hash contents of a file'	)

 __UpperCamelCase       						= parser.parse_args()

 __UpperCamelCase       						= args.input_string

 # hash input should be a bytestring
 if args.input_file:
  with open(args.input_file       ,      'rb'	) as f:
   __UpperCamelCase       						= f.read()
 else:
  __UpperCamelCase       						= bytes(snake_case       ,      'utf-8'	)

 print(SHAaaa(snake_case	).hash	)


if __name__ == "__main__":
      main()



