





from __future__ import annotations

UpperCAmelCase_					:   List[Any] 							=						tuple[int, int, int]
UpperCAmelCase_					:   Union[str, Any] 							=						tuple[str, str, str]


# used alphabet --------------------------
# from string.ascii_uppercase
UpperCAmelCase_					:   Any 							=						"""ABCDEFGHIJKLMNOPQRSTUVWXYZ"""

# -------------------------- default selection --------------------------
# rotors --------------------------
UpperCAmelCase_					:   List[str] 							=						"""EGZWVONAHDCLFQMSIPJBYUKXTR"""
UpperCAmelCase_					:   Tuple 							=						"""FOBHMDKEXQNRAULPGSJVTYICZW"""
UpperCAmelCase_					:   Optional[int] 							=						"""ZJXESIUQLHAVRMDOYGTNFWPBKC"""
# reflector --------------------------
UpperCAmelCase_					:   str 							=						{
    """A""": """N""",
    """N""": """A""",
    """B""": """O""",
    """O""": """B""",
    """C""": """P""",
    """P""": """C""",
    """D""": """Q""",
    """Q""": """D""",
    """E""": """R""",
    """R""": """E""",
    """F""": """S""",
    """S""": """F""",
    """G""": """T""",
    """T""": """G""",
    """H""": """U""",
    """U""": """H""",
    """I""": """V""",
    """V""": """I""",
    """J""": """W""",
    """W""": """J""",
    """K""": """X""",
    """X""": """K""",
    """L""": """Y""",
    """Y""": """L""",
    """M""": """Z""",
    """Z""": """M""",
}

# -------------------------- extra rotors --------------------------
UpperCAmelCase_					:   Any 							=						"""RMDJXFUWGISLHVTCQNKYPBEZOA"""
UpperCAmelCase_					:   List[str] 							=						"""SGLCPQWZHKXAREONTFBVIYJUDM"""
UpperCAmelCase_					:   Dict 							=						"""HVSICLTYKQUBXDWAJZOMFGPREN"""
UpperCAmelCase_					:   Union[str, Any] 							=						"""RZWQHFMVDBKICJLNTUXAGYPSOE"""
UpperCAmelCase_					:   Any 							=						"""LFKIJODBEGAMQPXVUHYSTCZRWN"""
UpperCAmelCase_					:   Optional[Any] 							=						"""KOAEGVDHXPQZMLFTYWJNBRCIUS"""






def 							_lowerCAmelCase (  _a   :					RotorPositionT					,   _a   :					RotorSelectionT					,   _a   :					str			)	->			tuple[RotorPositionT, RotorSelectionT, dict[str, str]]:
     # Checks if there are 3 unique rotors

     if (unique_rotsel := len(set(_a			)			)) < 3:
          lowerCAmelCase_					:       Union[str, Any]    =		F'Please use 3 unique rotors (not {unique_rotsel})'
          raise Exception(_a			)

     # Checks if rotor positions are valid
     lowerCAmelCase_   ,  lowerCAmelCase_   ,  lowerCAmelCase_					:       Optional[Any]    =		rotpos
     if not 0 < rotorposa <= len(_a			):
          lowerCAmelCase_					:       Tuple    =		F'First rotor position is not within range of 1..26 ({rotorposa}'
          raise ValueError(_a			)
     if not 0 < rotorposa <= len(_a			):
          lowerCAmelCase_					:       Dict    =		F'Second rotor position is not within range of 1..26 ({rotorposa})'
          raise ValueError(_a			)
     if not 0 < rotorposa <= len(_a			):
          lowerCAmelCase_					:       Optional[Any]    =		F'Third rotor position is not within range of 1..26 ({rotorposa})'
          raise ValueError(_a			)

     # Validates string and returns dict
     lowerCAmelCase_					:       Any    =		_plugboard(_a			)

     return rotpos, rotsel, pbdict






def 							_lowerCAmelCase (  _a   :					str			)	->			dict[str, str]:

     # tests the input string if it
     # a) is type string
     # b) has even length (so pairs can be made)
     if not isinstance(_a					,   _a			):
          lowerCAmelCase_					:       List[Any]    =		F'Plugboard setting isn\'t type string ({type(_a			)})'
          raise TypeError(_a			)
     elif len(_a			) % 2 != 0:
          lowerCAmelCase_					:       Any    =		F'Odd number of symbols ({len(_a			)})'
          raise Exception(_a			)
     elif pbstring == "":
          return {}

     pbstring.replace(""" """					,   """"""			)

     # Checks if all characters are unique
     lowerCAmelCase_					:       Any    =		set()
     for i in pbstring:
          if i not in abc:
               lowerCAmelCase_					:       Optional[int]    =		F'\'{i}\' not in list of symbols'
               raise Exception(_a			)
          elif i in tmppbl:
               lowerCAmelCase_					:       str    =		F'Duplicate symbol ({i})'
               raise Exception(_a			)
          else:
               tmppbl.add(_a			)
     del tmppbl

     # Created the dictionary
     lowerCAmelCase_					:       Tuple    =		{}
     for j in range(0					,   len(_a			) - 1					,   2			):
          lowerCAmelCase_					:       List[Any]    =		pbstring[j + 1]
          lowerCAmelCase_					:       str    =		pbstring[j]

     return pb






def 							_lowerCAmelCase (  _a   :					str					,   _a   :					RotorPositionT					,   _a   :					RotorSelectionT = (rotora, rotora, rotora)					,   _a   :					str = ""					,   )	->			str:
     lowerCAmelCase_					:       Any    =		text.upper()
     lowerCAmelCase_   ,  lowerCAmelCase_   ,  lowerCAmelCase_					:       Any    =		_validator(
         _a					,   _a					,   plugb.upper()			)

     lowerCAmelCase_   ,  lowerCAmelCase_   ,  lowerCAmelCase_					:       str    =		rotor_position
     lowerCAmelCase_   ,  lowerCAmelCase_   ,  lowerCAmelCase_					:       Dict    =		rotor_selection
     rotorposa -= 1
     rotorposa -= 1
     rotorposa -= 1

     lowerCAmelCase_					:       Dict    =		[]

     # encryption/decryption process --------------------------
     for symbol in text:
          if symbol in abc:
               # 1st plugboard --------------------------
               if symbol in plugboard:
                    lowerCAmelCase_					:       Any    =		plugboard[symbol]

               # rotor ra --------------------------
               lowerCAmelCase_					:       List[str]    =		abc.index(_a			) + rotorposa
               lowerCAmelCase_					:       List[Any]    =		rotora[index % len(_a			)]

               # rotor rb --------------------------
               lowerCAmelCase_					:       Union[str, Any]    =		abc.index(_a			) + rotorposa
               lowerCAmelCase_					:       Optional[Any]    =		rotora[index % len(_a			)]

               # rotor rc --------------------------
               lowerCAmelCase_					:       Any    =		abc.index(_a			) + rotorposa
               lowerCAmelCase_					:       Union[str, Any]    =		rotora[index % len(_a			)]

               # reflector --------------------------
               # this is the reason you don't need another machine to decipher

               lowerCAmelCase_					:       Dict    =		reflector[symbol]

               # 2nd rotors
               lowerCAmelCase_					:       Optional[int]    =		abc[rotora.index(_a			) - rotorposa]
               lowerCAmelCase_					:       List[Any]    =		abc[rotora.index(_a			) - rotorposa]
               lowerCAmelCase_					:       str    =		abc[rotora.index(_a			) - rotorposa]

               # 2nd plugboard
               if symbol in plugboard:
                    lowerCAmelCase_					:       int    =		plugboard[symbol]

               # moves/resets rotor positions
               rotorposa += 1
               if rotorposa >= len(_a			):
                    lowerCAmelCase_					:       str    =		0
                    rotorposa += 1
               if rotorposa >= len(_a			):
                    lowerCAmelCase_					:       Optional[int]    =		0
                    rotorposa += 1
               if rotorposa >= len(_a			):
                    lowerCAmelCase_					:       Tuple    =		0

        # else:
        #    pass
        #    Error could be also raised
        #    raise ValueError(
        #       'Invalid symbol('+repr(symbol)+')')
          result.append(_a			)

     return "".join(_a			)


if __name__ == "__main__":
      UpperCAmelCase_					:   List[str] 							=						"""This is my Python script that emulates the Enigma machine from WWII."""
      UpperCAmelCase_					:   Optional[Any] 							=						(1, 1, 1)
      UpperCAmelCase_					:   str 							=						"""pictures"""
      UpperCAmelCase_					:   Union[str, Any] 							=						(rotora, rotora, rotora)
      UpperCAmelCase_					:   Optional[int] 							=						enigma(message, rotor_pos, rotor_sel, pb)

      print("""Encrypted message:""", en)
      print("""Decrypted message:""", enigma(en, rotor_pos, rotor_sel, pb))







def 							_lowerCAmelCase (  _a   :					int					,   _a   :					bool = False			)	->			bool:
     if n == 2:
          return True
     if not n % 2 or n < 2:
          return False
     if n > 5 and n % 10 not in (1, 3, 7, 9):  # can quickly check last digit
          return False
     if n > 3_31_70_44_06_46_79_88_73_85_96_19_81 and not allow_probable:
          raise ValueError(
              """Warning: upper bound of deterministic test is exceeded. """
              """Pass allow_probable=True to allow probabilistic test. """
              """A return value of True indicates a probable prime."""			)
     # array bounds provided by analysis
     lowerCAmelCase_					:       Optional[int]    =		[
         20_47,
         1_37_36_53,
         25_32_60_01,
         32_15_03_17_51,
         2_15_23_02_89_87_47,
         3_47_47_49_66_03_83,
         3_41_55_00_71_72_83_21,
         1,
         3_82_51_23_05_65_46_41_30_51,
         1,
         1,
         31_86_65_85_78_34_03_11_51_16_74_61,
         3_31_70_44_06_46_79_88_73_85_96_19_81,
     ]

     lowerCAmelCase_					:       Dict    =		[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
     for idx, _p in enumerate(_a					,   1			):
          if n < _p:
               # then we have our last prime to check
               lowerCAmelCase_					:       str    =		primes[:idx]
               break
     lowerCAmelCase_   ,  lowerCAmelCase_					:       Optional[Any]    =		n - 1, 0
     # break up n -1 into a power of 2 (s) and
     # remaining odd component
     # essentially, solve for d * 2 ** s == n - 1
     while d % 2 == 0:
          d //= 2
          s += 1
     for prime in plist:
          lowerCAmelCase_					:       Tuple    =		False
          for r in range(_a			):
               lowerCAmelCase_					:       Tuple    =		pow(_a					,   d * 2**r					,   _a			)
               # see article for analysis explanation for m
               if (r == 0 and m == 1) or ((m + 1) % n == 0):
                    lowerCAmelCase_					:       List[Any]    =		True
                    # this loop will not determine compositeness
                    break
          if pr:
               continue
          # if pr is False, then the above loop never evaluated to true,
          # and the n MUST be composite
          return False
     return True






def 							_lowerCAmelCase (  )	->			None:
     assert not miller_rabin(5_61			)
     assert miller_rabin(5_63			)
     # 2047

     assert not miller_rabin(83_82_01			)
     assert miller_rabin(83_82_07			)
     # 1_373_653

     assert not miller_rabin(17_31_60_01			)
     assert miller_rabin(17_31_60_17			)
     # 25_326_001

     assert not miller_rabin(30_78_38_66_41			)
     assert miller_rabin(30_78_38_66_53			)
     # 3_215_031_751

     assert not miller_rabin(1_71_30_45_57_48_01			)
     assert miller_rabin(1_71_30_45_57_48_19			)
     # 2_152_302_898_747

     assert not miller_rabin(2_77_97_99_72_83_07			)
     assert miller_rabin(2_77_97_99_72_83_27			)
     # 3_474_749_660_383

     assert not miller_rabin(1_13_85_00_23_90_94_41			)
     assert miller_rabin(1_13_85_00_23_90_95_27			)
     # 341_550_071_728_321

     assert not miller_rabin(1_27_50_41_01_88_48_80_43_51			)
     assert miller_rabin(1_27_50_41_01_88_48_80_43_91			)
     # 3_825_123_056_546_413_051

     assert not miller_rabin(7_96_66_46_44_58_50_77_87_79_18_67			)
     assert miller_rabin(7_96_66_46_44_58_50_77_87_79_19_51			)
     # 318_665_857_834_031_151_167_461

     assert not miller_rabin(55_28_40_67_74_46_64_78_97_66_03_33			)
     assert miller_rabin(55_28_40_67_74_46_64_78_97_66_03_59			)
     # 3_317_044_064_679_887_385_961_981
     # upper limit for probabilistic test


if __name__ == "__main__":
      test_miller_rabin()
