




'''simple docstring'''

import mpmath  # for roots of unity
import numpy as np



class 		SCREAMING_SNAKE_CASE__  :





			def __init__(      self						:    Tuple      ,		a_						:    Optional[int]=None      ,		a_						:    int=None							):



						"""simple docstring"""




						__snake_case			=		list(poly_a or [0]							)[:]
						__snake_case			=		list(poly_b or [0]							)[:]

						# Remove leading zero coefficients
						while self.polyA[-1] == 0:
									self.polyA.pop()
						__snake_case			=		len(self.polyA							)

						while self.polyB[-1] == 0:
									self.polyB.pop()
						__snake_case			=		len(self.polyB							)

						# Add 0 to make lengths equal a power of 2
						__snake_case			=		int(
						    2 ** np.ceil(np.loga(len(self.polyA							) + len(self.polyB							) - 1							)							)							)

						while len(self.polyA							) < self.c_max_length:
									self.polyA.append(0							)
						while len(self.polyB							) < self.c_max_length:
									self.polyB.append(0							)
						# A complex root used for the fourier transform
						__snake_case			=		complex(mpmath.root(x=1      ,		n=self.c_max_length      ,		k=1							)							)

						# The product
						__snake_case			=		self.__multiply()





			def 							A (      self						:    Any      ,		a_						:    Optional[Any]							):



						"""simple docstring"""




						__snake_case			=		[[x] for x in self.polyA] if which == "A" else [[x] for x in self.polyB]
						# Corner case
						if len(a_							) <= 1:
									return dft[0]
						#
						__snake_case			=		self.c_max_length // 2
						while next_ncol > 0:
									__snake_case			=		[[] for i in range(a_							)]
									__snake_case			=		self.root**next_ncol

									# First half of next step
									__snake_case			=		1
									for j in range(self.c_max_length // (next_ncol * 2)							):
												for i in range(a_							):
															new_dft[i].append(dft[i][j] + current_root * dft[i + next_ncol][j]							)
												current_root *= root
									# Second half of next step
									__snake_case			=		1
									for j in range(self.c_max_length // (next_ncol * 2)							):
												for i in range(a_							):
															new_dft[i].append(dft[i][j] - current_root * dft[i + next_ncol][j]							)
												current_root *= root
									# Update
									__snake_case			=		new_dft
									__snake_case			=		next_ncol // 2
						return dft[0]





			def 							A (      self						:    Union[str, Any]							):



						"""simple docstring"""




						__snake_case			=		self.__dft("A"							)
						__snake_case			=		self.__dft("B"							)
						__snake_case			=		[[dft_a[i] * dft_b[i] for i in range(self.c_max_length							)]]
						del dft_a
						del dft_b

						# Corner Case
						if len(inverce_c[0]							) <= 1:
									return inverce_c[0]
						# Inverse DFT
						__snake_case			=		2
						while next_ncol <= self.c_max_length:
									__snake_case			=		[[] for i in range(a_							)]
									__snake_case			=		self.root ** (next_ncol // 2)
									__snake_case			=		1
									# First half of next step
									for j in range(self.c_max_length // next_ncol							):
												for i in range(next_ncol // 2							):
															# Even positions
															new_inverse_c[i].append(
															    (
															        inverce_c[i][j]
															        + inverce_c[i][j + self.c_max_length // next_ncol]
															    )
															    / 2							)
															# Odd positions
															new_inverse_c[i + next_ncol // 2].append(
															    (
															        inverce_c[i][j]
															        - inverce_c[i][j + self.c_max_length // next_ncol]
															    )
															    / (2 * current_root)							)
												current_root *= root
									# Update
									__snake_case			=		new_inverse_c
									next_ncol *= 2
						# Unpack
						__snake_case			=		[round(x[0].real      ,		8							) + round(x[0].imag      ,		8							) * 1j for x in inverce_c]

						# Remove leading 0's
						while inverce_c[-1] == 0:
									inverce_c.pop()
						return inverce_c






			def __str__(      self						:    Optional[int]							):



						"""simple docstring"""




						__snake_case			=		"A = " + " + ".join(
						    f'''{coef}*x^{i}''' for coef, i in enumerate(self.polyA[: self.len_A]							)							)
						__snake_case			=		"B = " + " + ".join(
						    f'''{coef}*x^{i}''' for coef, i in enumerate(self.polyB[: self.len_B]							)							)
						__snake_case			=		"A*B = " + " + ".join(
						    f'''{coef}*x^{i}''' for coef, i in enumerate(self.product							)							)

						return f'''{a}\n{b}\n{c}'''


# Unit tests
if __name__ == "__main__":
						import doctest

						doctest.testmod()












def      A__       (    SCREAMING_SNAKE_CASE_   :      int				,	SCREAMING_SNAKE_CASE_   :      int						)  ->  str:






   """simple docstring"""







   if a < 0 or b < 0:
      raise ValueError('''the value of both inputs must be positive'''						)

   _UpperCAmelCase           =   str(bin(SCREAMING_SNAKE_CASE_						)						)[2:]  # remove the leading "0b"
   _UpperCAmelCase           =   str(bin(SCREAMING_SNAKE_CASE_						)						)[2:]  # remove the leading "0b"

   _UpperCAmelCase           =   max(len(SCREAMING_SNAKE_CASE_						)				,	len(SCREAMING_SNAKE_CASE_						)						)

   return "0b" + "".join(
       str(int(char_a == '''1''' and char_b == '''1'''						)						)
       for char_a, char_b in zip(a_binary.zfill(SCREAMING_SNAKE_CASE_						)				,	b_binary.zfill(SCREAMING_SNAKE_CASE_						)						)						)


if __name__ == "__main__":
   import doctest

   doctest.testmod()