





import argparse
import struct
import unittest

class 					A     :



      def __init__(       self  ,	lowercase_       )   -> List[str]:



             '''simple docstring'''

             _snake_case   :				Optional[int]  					=    data

             # Initialize hash values
             _snake_case   :				Tuple  					=    [
                 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
             _snake_case   :				str  					=    [
                 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,
             ]

             _snake_case   :				List[str]  					=    self.preprocessing(self.data       )
             self.final_hash()



      @staticmethod
      def  __a			(       lowercase_       )   -> Optional[Any]:



             '''simple docstring'''

             _snake_case   :				List[str]  					=    b'''\x80''' + (b'''\x00''' * (63 - (len(__snake_case       ) + 8) % 64))
             _snake_case   :				List[str]  					=    struct.pack('''>Q'''  ,	(len(__snake_case       ) * 8)       )
             return data + padding + big_endian_integer



      def  __a			(       self       )   -> Optional[Any]:



             '''simple docstring'''

             _snake_case   :				List[str]  					=    [
                 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
                    _snake_case   :				Optional[Any]  					=    list(struct.unpack('''>16L'''  ,	__snake_case       )       )
                    # add 48 0-ed integers
                    words += [0] * 48

                    _snake_case					,     _snake_case					,     _snake_case					,     _snake_case					,     _snake_case					,     _snake_case					,     _snake_case					,     _snake_case   :				Union[str, Any]  					=    self.hashes

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

                                  _snake_case   :				Optional[Any]  					=    (
                                      words[index - 16] + sa + words[index - 7] + sa
                                  ) % 0x100_000_000

                           # Compression
                           _snake_case   :				str  					=    self.ror(__snake_case  ,	6       ) ^ self.ror(__snake_case  ,	11       ) ^ self.ror(__snake_case  ,	25       )
                           _snake_case   :				str  					=    (e & f) ^ ((~e & 0xFF_FFF_FFF) & g)
                           _snake_case   :				str  					=    (
                               h + sa + ch + self.round_constants[index] + words[index]
                           ) % 0x100_000_000
                           _snake_case   :				Tuple  					=    self.ror(__snake_case  ,	2       ) ^ self.ror(__snake_case  ,	13       ) ^ self.ror(__snake_case  ,	22       )
                           _snake_case   :				Union[str, Any]  					=    (a & b) ^ (a & c) ^ (b & c)
                           _snake_case   :				Optional[int]  					=    (sa + maj) % 0x100_000_000

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

                    _snake_case   :				Dict  					=    [a, b, c, d, e, f, g, h]

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

             _snake_case   :				List[str]  					=    ''''''.join([hex(__snake_case       )[2:].zfill(8       ) for value in self.hashes]       )



      def  __a			(       self  ,	lowercase_  ,	lowercase_       )   -> List[str]:



             '''simple docstring'''

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


class 					A     (unittest.TestCase					):



      def  __a			(       self       )   -> Union[str, Any]:



             '''simple docstring'''

             import hashlib

             _snake_case   :				Optional[int]  					=    bytes('''Test String'''  ,	'''utf-8'''       )
             self.assertEqual(SHAaaa(__snake_case       ).hash  ,	hashlib.shaaaa(__snake_case       ).hexdigest()       )




def     A_      (	)      ->    None:
       import doctest

       doctest.testmod()

       _snake_case   :				List[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'''	)

       _snake_case   :				Optional[Any]  					=    parser.parse_args()

       _snake_case   :				List[str]  					=    args.input_string

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

       print(SHAaaa(_UpperCAmelCase	).hash	)


if __name__ == "__main__":
   main()













# Author: OMKAR PATHAK, Nwachukwu Chidiebere

# Use a Python dictionary to construct the graph.
from __future__ import annotations

from pprint import pformat
from typing import Generic, TypeVar

a__      :     Optional[int]         =		TypeVar('T')





class UpperCAmelCase_				( Generic[T] ):


  def __init__(							self ,__snake_case = True ):



       """simple docstring"""





       A_          =       {}  # dictionary of lists
       A_          =       directed


  def 						__UpperCAmelCase     (							self ,__snake_case ,__snake_case ):



       """simple docstring"""





       if not self.directed:  # For undirected graphs
            # if both source vertex and destination vertex are both present in the
            # adjacency list, add destination vertex to source vertex list of adjacent
            # vertices and add source vertex to destination vertex list of adjacent
            # vertices.
            if source_vertex in self.adj_list and destination_vertex in self.adj_list:
                 self.adj_list[source_vertex].append(__snake_case )
                 self.adj_list[destination_vertex].append(__snake_case )
            # if only source vertex is present in adjacency list, add destination vertex
            # to source vertex list of adjacent vertices, then create a new vertex with
            # destination vertex as key and assign a list containing the source vertex
            # as it's first adjacent vertex.
            elif source_vertex in self.adj_list:
                 self.adj_list[source_vertex].append(__snake_case )
                 A_          =       [source_vertex]
            # if only destination vertex is present in adjacency list, add source vertex
            # to destination vertex list of adjacent vertices, then create a new vertex
            # with source vertex as key and assign a list containing the source vertex
            # as it's first adjacent vertex.
            elif destination_vertex in self.adj_list:
                 self.adj_list[destination_vertex].append(__snake_case )
                 A_          =       [destination_vertex]
            # if both source vertex and destination vertex are not present in adjacency
            # list, create a new vertex with source vertex as key and assign a list
            # containing the destination vertex as it's first adjacent vertex also
            # create a new vertex with destination vertex as key and assign a list
            # containing the source vertex as it's first adjacent vertex.
            else:
                 A_          =       [destination_vertex]
                 A_          =       [source_vertex]
       else:  # For directed graphs
            # if both source vertex and destination vertex are present in adjacency
            # list, add destination vertex to source vertex list of adjacent vertices.
            if source_vertex in self.adj_list and destination_vertex in self.adj_list:
                 self.adj_list[source_vertex].append(__snake_case )
            # if only source vertex is present in adjacency list, add destination
            # vertex to source vertex list of adjacent vertices and create a new vertex
            # with destination vertex as key, which has no adjacent vertex
            elif source_vertex in self.adj_list:
                 self.adj_list[source_vertex].append(__snake_case )
                 A_          =       []
            # if only destination vertex is present in adjacency list, create a new
            # vertex with source vertex as key and assign a list containing destination
            # vertex as first adjacent vertex
            elif destination_vertex in self.adj_list:
                 A_          =       [destination_vertex]
            # if both source vertex and destination vertex are not present in adjacency
            # list, create a new vertex with source vertex as key and a list containing
            # destination vertex as it's first adjacent vertex. Then create a new vertex
            # with destination vertex as key, which has no adjacent vertex
            else:
                 A_          =       [destination_vertex]
                 A_          =       []

       return self




  def __repr__(							self ):



       """simple docstring"""





       return pformat(self.adj_list )



