






'''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       __UpperCAmelCase						(  lowerCamelCase_)							->     List[Tuple[int, ...]]:
		UpperCamelCase__				:					int					=					[]
		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       __UpperCAmelCase						(  lowerCamelCase_     ,      lowerCamelCase_)							->     Tuple[int, ...]:
		UpperCamelCase__				:					int					=					[]
		for d in reversed(lowerCamelCase_):
				idx.append(flat_idx % d)
				UpperCamelCase__				:					Any					=					flat_idx // d

		return tuple(reversed(lowerCamelCase_))







@torch.jit.ignore
def       __UpperCAmelCase						(  lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_ = None     ,      lowerCamelCase_ = 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(lowerCamelCase_) -> None:
				UpperCamelCase__				:					Tuple					=					True
				for i in range(len(lowerCamelCase_)):
						UpperCamelCase__				:					List[Any]					=					-1 * (i + 1)
						l[reversed_idx] &= tally
						UpperCamelCase__				:					Optional[Any]					=					l[reversed_idx]

		if start_edges is None:
				UpperCamelCase__				:					int					=					[s == 0 for s in start]
				reduce_edge_list(lowerCamelCase_)
		if end_edges is None:
				UpperCamelCase__				:					List[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),)]

		UpperCamelCase__				:					List[Tuple[slice, ...]]					=					[]
		UpperCamelCase__				:					List[slice]					=					[]

		# 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

		UpperCamelCase__				:					Tuple[slice, ...]					=					tuple(lowerCamelCase_)
		UpperCamelCase__				:					Dict					=					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

				UpperCamelCase__				:					str					=					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

				UpperCamelCase__				:					Optional[int]					=					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())
				UpperCamelCase__				:					Dict					=					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       __UpperCAmelCase						(  lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_)							->     torch.Tensor:
		UpperCamelCase__				:					List[Any]					=					t.shape[:no_batch_dims]
		UpperCamelCase__				:					Optional[int]					=					list(_flat_idx_to_idx(lowerCamelCase_     ,      lowerCamelCase_))
		# _get_minimal_slice_set is inclusive
		UpperCamelCase__				:					Dict					=					list(_flat_idx_to_idx(flat_end - 1     ,      lowerCamelCase_))

		# Get an ordered list of slices to perform
		UpperCamelCase__				:					int					=					_get_minimal_slice_set(
		    lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_     ,      )

		UpperCamelCase__				:					List[Any]					=					[t[s] for s in slices]

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







def       __UpperCAmelCase						(  lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_     ,      lowerCamelCase_ = False     ,      lowerCamelCase_ = None     ,      lowerCamelCase_ = False     ,      )							->     Any:
		if not (len(lowerCamelCase_) > 0):
				raise ValueError('Must provide at least one input')

		UpperCamelCase__				:					int					=					[shape[:no_batch_dims] for shape in _fetch_dims(lowerCamelCase_)]
		UpperCamelCase__				:					int					=					tuple([max(lowerCamelCase_) for s in zip(*lowerCamelCase_)])

		def _prep_inputs(lowerCamelCase_) -> torch.Tensor:
				if not low_mem:
						if not sum(t.shape[:no_batch_dims]) == no_batch_dims:
								UpperCamelCase__				:					List[Any]					=					t.expand(orig_batch_dims + t.shape[no_batch_dims:])
						UpperCamelCase__				:					Optional[int]					=					t.reshape(-1     ,      *t.shape[no_batch_dims:])
				else:
						UpperCamelCase__				:					Optional[int]					=					t.expand(orig_batch_dims + t.shape[no_batch_dims:])
				return t

		UpperCamelCase__				:					Dict[str, Any]					=					tensor_tree_map(_prep_inputs     ,      lowerCamelCase_)
		UpperCamelCase__				:					int					=					None
		if _out is not None:
				UpperCamelCase__				:					Optional[int]					=					tensor_tree_map(lambda lowerCamelCase_: t.view([-1] + list(t.shape[no_batch_dims:]))     ,      _out)

		UpperCamelCase__				:					Dict					=					1
		for d in orig_batch_dims:
				flat_batch_dim *= d

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

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

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

				UpperCamelCase__				:					Dict[str, Any]					=					tensor_tree_map(lowerCamelCase_     ,      lowerCamelCase_)

				# Run the layer on the chunk
				UpperCamelCase__				:					List[Any]					=					layer(**lowerCamelCase_)

				# Allocate space for the output
				if out is None:
						UpperCamelCase__				:					Optional[int]					=					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_     ,      lowerCamelCase_) -> 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:
														UpperCamelCase__				:					List[str]					=					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:
										UpperCamelCase__				:					int					=					xa
				elif isinstance(lowerCamelCase_     ,      torch.Tensor):
						if _add_into_out:
								out[i : i + chunk_size] += output_chunk
						else:
								UpperCamelCase__				:					Dict					=					output_chunk
				else:
						raise ValueError('Not supported')

				i += chunk_size

		UpperCamelCase__				:					int					=					tensor_tree_map(lambda lowerCamelCase_: t.view(orig_batch_dims + t.shape[1:])     ,      lowerCamelCase_)

		return out







class       __lowercase					:

	def __init__(      self   :						List[str]  ,					UpperCAmelCase_   :						int = 512  ,					):
			UpperCamelCase__				:					str					=					max_chunk_size
			UpperCamelCase__				:					Optional[int]					=					None
			UpperCamelCase__				:					Optional[tuple]					=					None

	def      __UpperCamelCase							(      self   :						str  ,					UpperCAmelCase_   :						Callable  ,					UpperCAmelCase_   :						tuple  ,					UpperCAmelCase_   :						int):
			logging.info('Tuning chunk size...')

			if min_chunk_size >= self.max_chunk_size:
					return min_chunk_size

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

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

			UpperCamelCase__				:					Tuple					=					0
			UpperCamelCase__				:					Dict					=					len(UpperCAmelCase_) - 1
			while i > min_viable_chunk_size_index:
					UpperCamelCase__				:					Optional[int]					=					test_chunk_size(candidates[i])
					if not viable:
							UpperCamelCase__				:					Tuple					=					(min_viable_chunk_size_index + i) // 2
					else:
							UpperCamelCase__				:					Optional[int]					=					i
							UpperCamelCase__				:					Dict					=					(i + len(UpperCAmelCase_) - 1) // 2

			return candidates[min_viable_chunk_size_index]

	def      __UpperCamelCase							(      self   :						Any  ,					UpperCAmelCase_   :						Iterable  ,					UpperCAmelCase_   :						Iterable):
			UpperCamelCase__				:					List[str]					=					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__				:					Union[str, Any]					=					[v for _, v in sorted(aa.items()  ,					key=lambda UpperCAmelCase_: x[0])]
							UpperCamelCase__				:					str					=					[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   :						List[Any]  ,					UpperCAmelCase_   :						Callable  ,					UpperCAmelCase_   :						tuple  ,					UpperCAmelCase_   :						int  ,					):
			UpperCamelCase__				:					List[Any]					=					True
			UpperCamelCase__				:					tuple					=					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__				:					Union[str, Any]					=					self._compare_arg_caches(self.cached_arg_data  ,					UpperCAmelCase_)
			else:
					# Otherwise, we can reuse the precomputed value
					UpperCamelCase__				:					Optional[int]					=					False

			if not consistent:
					UpperCamelCase__				:					Tuple					=					self._determine_favorable_chunk_size(
					    UpperCAmelCase_  ,					UpperCAmelCase_  ,					UpperCAmelCase_  ,					)
					UpperCamelCase__				:					Optional[Any]					=					arg_data

			assert self.cached_chunk_size is not None

			return self.cached_chunk_size












'''simple docstring'''




import argparse
import struct
import unittest







class       __lowercase					:

	def __init__(      self   :						Tuple  ,					UpperCAmelCase_   :						bytes):
			UpperCamelCase__				:					Dict					=					data

			# Initialize hash values
			UpperCamelCase__				:					Any					=					[
			    0X6A_09E_667,
			    0XBB_67A_E85,
			    0X3C_6EF_372,
			    0XA5_4FF_53A,
			    0X51_0E5_27F,
			    0X9B_056_88C,
			    0X1F_83D_9AB,
			    0X5B_E0C_D19,
			]

			# Initialize round constants
			UpperCamelCase__				:					List[Any]					=					[
			    0X42_8A2_F98,
			    0X71_374_491,
			    0XB5_C0F_BCF,
			    0XE9_B5D_BA5,
			    0X39_56C_25B,
			    0X59_F11_1F1,
			    0X92_3F8_2A4,
			    0XAB_1C5_ED5,
			    0XD8_07A_A98,
			    0X12_835_B01,
			    0X24_318_5BE,
			    0X55_0C7_DC3,
			    0X72_BE5_D74,
			    0X80_DEB_1FE,
			    0X9B_DC0_6A7,
			    0XC1_9BF_174,
			    0XE4_9B6_9C1,
			    0XEF_BE4_786,
			    0X0F_C19_DC6,
			    0X24_0CA_1CC,
			    0X2D_E92_C6F,
			    0X4A_748_4AA,
			    0X5C_B0A_9DC,
			    0X76_F98_8DA,
			    0X98_3E5_152,
			    0XA8_31C_66D,
			    0XB0_032_7C8,
			    0XBF_597_FC7,
			    0XC6_E00_BF3,
			    0XD5_A79_147,
			    0X06_CA6_351,
			    0X14_292_967,
			    0X27_B70_A85,
			    0X2E_1B2_138,
			    0X4D_2C6_DFC,
			    0X53_380_D13,
			    0X65_0A7_354,
			    0X76_6A0_ABB,
			    0X81_C2C_92E,
			    0X92_722_C85,
			    0XA2_BFE_8A1,
			    0XA8_1A6_64B,
			    0XC2_4B8_B70,
			    0XC7_6C5_1A3,
			    0XD1_92E_819,
			    0XD6_990_624,
			    0XF4_0E3_585,
			    0X10_6AA_070,
			    0X19_A4C_116,
			    0X1E_376_C08,
			    0X27_487_74C,
			    0X34_B0B_CB5,
			    0X39_1C0_CB3,
			    0X4E_D8A_A4A,
			    0X5B_9CC_A4F,
			    0X68_2E6_FF3,
			    0X74_8F8_2EE,
			    0X78_A56_36F,
			    0X84_C87_814,
			    0X8C_C70_208,
			    0X90_BEF_FFA,
			    0XA4_506_CEB,
			    0XBE_F9A_3F7,
			    0XC6_717_8F2,
			]

			UpperCamelCase__				:					Tuple					=					self.preprocessing(self.data)
			self.final_hash()

	@staticmethod
	def      __UpperCamelCase							(      UpperCAmelCase_   :						bytes):
			UpperCamelCase__				:					List[Any]					=					B'\x80' + (B'\x00' * (63 - (len(UpperCAmelCase_) + 8) % 64))
			UpperCamelCase__				:					List[Any]					=					struct.pack('>Q'  ,					(len(UpperCAmelCase_) * 8))
			return data + padding + big_endian_integer

	def      __UpperCamelCase							(      self   :						Union[str, Any]):
			# Convert into blocks of 64 bytes
			UpperCamelCase__				:					int					=					[
			    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__				:					Tuple					=					list(struct.unpack('>16L'  ,					UpperCAmelCase_))
					# add 48 0-ed integers
					words += [0] * 48

					UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__				:					str					=					self.hashes

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

									UpperCamelCase__				:					int					=					(
									    words[index - 16] + sa + words[index - 7] + sa
									) % 0X100_000_000

							# Compression
							UpperCamelCase__				:					Optional[Any]					=					self.ror(UpperCAmelCase_  ,					6) ^ self.ror(UpperCAmelCase_  ,					11) ^ self.ror(UpperCAmelCase_  ,					25)
							UpperCamelCase__				:					List[str]					=					(e & f) ^ ((~e & 0XFF_FFF_FFF) & g)
							UpperCamelCase__				:					List[Any]					=					(
							    h + sa + ch + self.round_constants[index] + words[index]
							) % 0X100_000_000
							UpperCamelCase__				:					List[str]					=					self.ror(UpperCAmelCase_  ,					2) ^ self.ror(UpperCAmelCase_  ,					13) ^ self.ror(UpperCAmelCase_  ,					22)
							UpperCamelCase__				:					Dict					=					(a & b) ^ (a & c) ^ (b & c)
							UpperCamelCase__				:					List[str]					=					(sa + maj) % 0X100_000_000

							UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__,	UpperCamelCase__				:					Tuple					=					(
							    g,
							    f,
							    e,
							    ((d + tempa) % 0X100_000_000),
							    c,
							    b,
							    a,
							    ((tempa + tempa) % 0X100_000_000),
							)

					UpperCamelCase__				:					List[Any]					=					[a, b, c, d, e, f, g, h]

					# Modify final values
					UpperCamelCase__				:					Optional[Any]					=					[
					    ((element + mutated_hash_values[index]) % 0X100_000_000)
					    for index, element in enumerate(self.hashes)
					]

			UpperCamelCase__				:					Any					=					''.join([hex(UpperCAmelCase_)[2:].zfill(8) for value in self.hashes])





	def      __UpperCamelCase							(      self   :						int  ,					UpperCAmelCase_   :						int  ,					UpperCAmelCase_   :						int):
			return 0XFF_FFF_FFF & (value << (32 - rotations)) | (value >> rotations)







class       __lowercase					(unittest.TestCase    ):

	def      __UpperCamelCase							(      self   :						int):
			import hashlib

			UpperCamelCase__				:					str					=					bytes('Test String'  ,					'utf-8')
			self.assertEqual(SHAaaa(UpperCAmelCase_).hash  ,					hashlib.shaaaa(UpperCAmelCase_).hexdigest())







def       __UpperCAmelCase						(  )							->     None:
		import doctest

		doctest.testmod()

		UpperCamelCase__				:					Union[str, Any]					=					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__				:					List[str]					=					parser.parse_args()

		UpperCamelCase__				:					str					=					args.input_string

		# hash input should be a bytestring
		if args.input_file:
				with open(args.input_file     ,      'rb') as f:
						UpperCamelCase__				:					Any					=					f.read()
		else:
				UpperCamelCase__				:					List[Any]					=					bytes(lowerCamelCase_     ,      'utf-8')

		print(SHAaaa(lowerCamelCase_).hash)


if __name__ == "__main__":
		main()




