

'''simple docstring'''
from math import pi, sqrt, tan




def 	UpperCamelCase   (       lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if side_length < 0:
    raise ValueError('''surface_area_cube() only accepts non-negative values'''	)
  return 6 * side_length**2




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if length < 0 or breadth < 0 or height < 0:
    raise ValueError('''surface_area_cuboid() only accepts non-negative values'''	)
  return 2 * ((length * breadth) + (breadth * height) + (length * height))




def 	UpperCamelCase   (       lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius < 0:
    raise ValueError('''surface_area_sphere() only accepts non-negative values'''	)
  return 4 * pi * radius**2




def 	UpperCamelCase   (       lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius < 0:
    raise ValueError('''surface_area_hemisphere() only accepts non-negative values'''	)
  return 3 * pi * radius**2




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius < 0 or height < 0:
    raise ValueError('''surface_area_cone() only accepts non-negative values'''	)
  return pi * radius * (radius + (height**2 + radius**2) ** 0.5)




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius_a < 0 or radius_a < 0 or height < 0:
    raise ValueError(
        '''surface_area_conical_frustum() only accepts non-negative values'''	)
  lowercase									=(height**2 + (radius_a - radius_a) ** 2) ** 0.5
  return pi * ((slant_height * (radius_a + radius_a)) + radius_a**2 + radius_a**2)




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius < 0 or height < 0:
    raise ValueError('''surface_area_cylinder() only accepts non-negative values'''	)
  return 2 * pi * radius * (height + radius)




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if torus_radius < 0 or tube_radius < 0:
    raise ValueError('''surface_area_torus() only accepts non-negative values'''	)
  if torus_radius < tube_radius:
    raise ValueError(
        '''surface_area_torus() does not support spindle or self intersecting tori'''	)
  return 4 * pow(lowercase_     ,					2	) * torus_radius * tube_radius




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if length < 0 or width < 0:
    raise ValueError('''area_rectangle() only accepts non-negative values'''	)
  return length * width




def 	UpperCamelCase   (       lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if side_length < 0:
    raise ValueError('''area_square() only accepts non-negative values'''	)
  return side_length**2




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if base < 0 or height < 0:
    raise ValueError('''area_triangle() only accepts non-negative values'''	)
  return (base * height) / 2




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if sidea < 0 or sidea < 0 or sidea < 0:
    raise ValueError('''area_triangle_three_sides() only accepts non-negative values'''	)
  elif sidea + sidea < sidea or sidea + sidea < sidea or sidea + sidea < sidea:
    raise ValueError('''Given three sides do not form a triangle'''	)
  lowercase									=(sidea + sidea + sidea) / 2
  lowercase									=sqrt(
      semi_perimeter
      * (semi_perimeter - sidea)
      * (semi_perimeter - sidea)
      * (semi_perimeter - sidea)	)
  return area




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if base < 0 or height < 0:
    raise ValueError('''area_parallelogram() only accepts non-negative values'''	)
  return base * height




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if basea < 0 or basea < 0 or height < 0:
    raise ValueError('''area_trapezium() only accepts non-negative values'''	)
  return 1 / 2 * (basea + basea) * height




def 	UpperCamelCase   (       lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius < 0:
    raise ValueError('''area_circle() only accepts non-negative values'''	)
  return pi * radius**2




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if radius_x < 0 or radius_y < 0:
    raise ValueError('''area_ellipse() only accepts non-negative values'''	)
  return pi * radius_x * radius_y




def 	UpperCamelCase   (       lowercase_						:    float     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if diagonal_a < 0 or diagonal_a < 0:
    raise ValueError('''area_rhombus() only accepts non-negative values'''	)
  return 1 / 2 * diagonal_a * diagonal_a




def 	UpperCamelCase   (       lowercase_						:    int     ,					lowercase_						:    float	)		->	float:




  '''simple docstring'''



  if not isinstance(lowercase_     ,					lowercase_	) or sides < 3:
    raise ValueError(
        '''area_reg_polygon() only accepts integers greater than or \
equal to three as number of sides'''	)
  elif length < 0:
    raise ValueError(
        '''area_reg_polygon() only accepts non-negative values as \
length of a side'''	)
  return (sides * length**2) / (4 * tan(pi / sides	))
  return (sides * length**2) / (4 * tan(pi / sides	))


if __name__ == "__main__":
     import doctest

     doctest.testmod(verbose=True)  # verbose so we can see methods missing tests

     print('''[DEMO] Areas of various geometric shapes: \n''')
     print(F"""Rectangle: {area_rectangle(10, 20) = }""")
     print(F"""Square: {area_square(10) = }""")
     print(F"""Triangle: {area_triangle(10, 10) = }""")
     print(F"""Triangle: {area_triangle_three_sides(5, 12, 13) = }""")
     print(F"""Parallelogram: {area_parallelogram(10, 20) = }""")
     print(F"""Rhombus: {area_rhombus(10, 20) = }""")
     print(F"""Trapezium: {area_trapezium(10, 20, 30) = }""")
     print(F"""Circle: {area_circle(20) = }""")
     print(F"""Ellipse: {area_ellipse(10, 20) = }""")
     print('''\nSurface Areas of various geometric shapes: \n''')
     print(F"""Cube: {surface_area_cube(20) = }""")
     print(F"""Cuboid: {surface_area_cuboid(10, 20, 30) = }""")
     print(F"""Sphere: {surface_area_sphere(20) = }""")
     print(F"""Hemisphere: {surface_area_hemisphere(20) = }""")
     print(F"""Cone: {surface_area_cone(10, 20) = }""")
     print(F"""Conical Frustum: {surface_area_conical_frustum(10, 20, 30) = }""")
     print(F"""Cylinder: {surface_area_cylinder(10, 20) = }""")
     print(F"""Torus: {surface_area_torus(20, 10) = }""")
     print(F"""Equilateral Triangle: {area_reg_polygon(3, 10) = }""")
     print(F"""Square: {area_reg_polygon(4, 10) = }""")
     print(F"""Reqular Pentagon: {area_reg_polygon(5, 10) = }""")





'''simple docstring'''

from __future__ import annotations






def 				a  (     UpperCamelCase_    :    str      ,					UpperCamelCase_    :    list[str] | None = None      ,					UpperCamelCase_    :    dict[str, float] | None = None      ,					UpperCamelCase_    :    bool = False      ,					)       ->	tuple[int, float, str]:
		snake_case__	     =cipher_alphabet or [chr(UpperCamelCase_			) for i in range(97      ,					123			)]

		# If the argument is None or the user provided an empty dictionary
		if not frequencies_dict:
				# Frequencies of letters in the english language (how much they show up)
				snake_case__	     ={
				    'a': 0.0_8_4_9_7,
				    'b': 0.0_1_4_9_2,
				    'c': 0.0_2_2_0_2,
				    'd': 0.0_4_2_5_3,
				    'e': 0.1_1_1_6_2,
				    'f': 0.0_2_2_2_8,
				    'g': 0.0_2_0_1_5,
				    'h': 0.0_6_0_9_4,
				    'i': 0.0_7_5_4_6,
				    'j': 0.0_0_1_5_3,
				    'k': 0.0_1_2_9_2,
				    'l': 0.0_4_0_2_5,
				    'm': 0.0_2_4_0_6,
				    'n': 0.0_6_7_4_9,
				    'o': 0.0_7_5_0_7,
				    'p': 0.0_1_9_2_9,
				    'q': 0.0_0_0_9_5,
				    'r': 0.0_7_5_8_7,
				    's': 0.0_6_3_2_7,
				    't': 0.0_9_3_5_6,
				    'u': 0.0_2_7_5_8,
				    'v': 0.0_0_9_7_8,
				    'w': 0.0_2_5_6_0,
				    'x': 0.0_0_1_5_0,
				    'y': 0.0_1_9_9_4,
				    'z': 0.0_0_0_7_7,
				}
		else:
				# Custom frequencies dictionary
				snake_case__	     =frequencies_dict

		if not case_sensitive:
				snake_case__	     =ciphertext.lower()

		# Chi squared statistic values
		snake_case__	     ={}

		# cycle through all of the shifts
		for shift in range(len(UpperCamelCase_			)			):
				snake_case__	     =''

				# decrypt the message with the shift
				for letter in ciphertext:
						try:
								# Try to index the letter in the alphabet
								snake_case__	     =(alphabet_letters.index(letter.lower()			) - shift) % len(
								    UpperCamelCase_			)
								decrypted_with_shift += (
								    alphabet_letters[new_key].upper()
								    if case_sensitive and letter.isupper()
								    else alphabet_letters[new_key]
								)
						except ValueError:
								# Append the character if it isn't in the alphabet
								decrypted_with_shift += letter

				snake_case__	     =0.0

				# Loop through each letter in the decoded message with the shift
				for letter in decrypted_with_shift:
						if case_sensitive:
								snake_case__	     =letter.lower()
								if letter in frequencies:
										# Get the amount of times the letter occurs in the message
										snake_case__	     =decrypted_with_shift.lower().count(UpperCamelCase_			)

										# Get the excepcted amount of times the letter should appear based
										# on letter frequencies
										snake_case__	     =frequencies[letter] * occurrences

										# Complete the chi squared statistic formula
										snake_case__	     =((occurrences - expected) ** 2) / expected

										# Add the margin of error to the total chi squared statistic
										chi_squared_statistic += chi_letter_value
						else:
								if letter.lower() in frequencies:
										# Get the amount of times the letter occurs in the message
										snake_case__	     =decrypted_with_shift.count(UpperCamelCase_			)

										# Get the excepcted amount of times the letter should appear based
										# on letter frequencies
										snake_case__	     =frequencies[letter] * occurrences

										# Complete the chi squared statistic formula
										snake_case__	     =((occurrences - expected) ** 2) / expected

										# Add the margin of error to the total chi squared statistic
										chi_squared_statistic += chi_letter_value

        # Add the data to the chi_squared_statistic_values dictionary
				snake_case__	     =(
				    chi_squared_statistic,
				    decrypted_with_shift,
				)

		# Get the most likely cipher by finding the cipher with the smallest chi squared
		# statistic
		def chi_squared_statistic_values_sorting_key(UpperCamelCase_    :    int			) -> tuple[float, str]:
				return chi_squared_statistic_values[key]

		snake_case__	     =min(
		    UpperCamelCase_      ,					key=UpperCamelCase_      ,					)

		# Get all the data from the most likely cipher (key, decoded message)
		(
		    (
		    snake_case__
		)     ,	(
		    snake_case__
		)     ,	
		)	     =chi_squared_statistic_values[most_likely_cipher]

		# Return the data on the most likely shift
		return (
		    most_likely_cipher,
		    most_likely_cipher_chi_squared_value,
		    decoded_most_likely_cipher,
		)




