






from __future__ import annotations





def 						__UpperCAmelCase      (					a_							,					a_							,					a_):
							assert n > 1 and a > 0 and greatest_common_divisor(a_							,					a_) == 1
							((snake_case_)      ,		(snake_case_)      ,		(snake_case_))           =							extended_gcd(a_							,					a_)  # Implemented below
							snake_case_           =							(b * s) % n
							return x





def 						__UpperCAmelCase      (					a_							,					a_):
							((snake_case_)      ,		(snake_case_))           =							extended_euclid(a_							,					a_)  # Implemented below
							if b < 0:
														snake_case_           =							(b % n + n) % n
							return b





def 						__UpperCAmelCase      (					a_							,					a_							,					a_):
							snake_case_           =							invert_modulo(a_							,					a_)
							snake_case_           =							(b * s) % n
							return x





def 						__UpperCAmelCase      (					a_							,					a_):
							assert a >= 0 and b >= 0

							if b == 0:
														snake_case_      ,		snake_case_      ,		snake_case_           =							a, 1, 0
							else:
														((snake_case_)      ,		(snake_case_)      ,		(snake_case_))           =							extended_gcd(a_							,					a % b)
														snake_case_           =							q
														snake_case_           =							p - q * (a // b)

							assert a % d == 0 and b % d == 0
							assert d == a * x + b * y

							return (d, x, y)





def 						__UpperCAmelCase      (					a_							,					a_):
							if b == 0:
														return (1, 0)
							((snake_case_)      ,		(snake_case_))           =							extended_euclid(a_							,					a % b)
							snake_case_           =							a // b
							return (y, x - k * y)





def 						__UpperCAmelCase      (					a_							,					a_):
							if a < b:
														snake_case_      ,		snake_case_           =							b, a

							while a % b != 0:
														snake_case_      ,		snake_case_           =							b, a % b

							return b


if __name__ == "__main__":
	from doctest import testmod

	testmod(name="modular_division", verbose=True)
	testmod(name="modular_division2", verbose=True)
	testmod(name="invert_modulo", verbose=True)
	testmod(name="extended_gcd", verbose=True)
	testmod(name="extended_euclid", verbose=True)
	testmod(name="greatest_common_divisor", verbose=True)













from collections.abc import Callable
from math import log
from os import name, system
from random import gauss, seed
from typing import TypeVar





def 						__UpperCAmelCase      (					a_							,					a_							,					a_):
							seed(1)
							return [gauss(a_							,					a_) for _ in range(a_)]





def 						__UpperCAmelCase      (					a_							,					a_):
							return [k for k in range(a_) for _ in range(instance_count[k])]





def 						__UpperCAmelCase      (					a_							,					a_):
							return sum(a_) / instance_count





def 						__UpperCAmelCase      (					a_							,					a_):
							return instance_count / total_count





def 						__UpperCAmelCase      (					a_							,					a_							,					a_):
							snake_case_           =							[]  # An empty list to store all squared differences
							# iterate over number of elements in items
							for i in range(len(a_)):
														# for loop iterates over number of elements in inner layer of items
														for j in range(len(items[i])):
																					# appending squared differences to 'squared_diff' list
																					squared_diff.append((items[i][j] - means[i]) ** 2)

    # one divided by (the number of all instances - number of classes) multiplied by
    # sum of all squared differences
							snake_case_           =							len(a_)  # Number of classes in dataset
							return 1 / (total_count - n_classes) * sum(a_)





def 						__UpperCAmelCase      (					a_							,					a_							,					a_							,					a_):
							snake_case_           =							[]
							# for loop iterates over number of elements in list
							for i in range(len(a_)):
														# for loop iterates over number of inner items of each element
														for j in range(len(x_items[i])):
																					snake_case_           =							[]  # to store all discriminant values of each item as a list
																					# for loop iterates over number of classes we have in our dataset
																					for k in range(len(a_)):
																												# appending values of discriminants for each class to 'temp' list
																												temp.append(
																												    x_items[i][j] * (means[k] / variance)
																												    - (means[k] ** 2 / (2 * variance))
																												    + log(probabilities[k]))
																					# appending discriminant values of each item to 'results' list
																					results.append(a_)

							return [result.index(max(a_)) for result in results]





def 						__UpperCAmelCase      (					a_							,					a_):
							snake_case_           =							sum(1 for i, j in zip(a_							,					a_) if i == j)
							# percentage of accuracy equals to number of correct predictions divided by number
							# of all data and multiplied by 100
							return (correct / len(a_)) * 1_00


lowercase  							= TypeVar("num")





def 						__UpperCAmelCase      (					a_							,					a_							,					a_							,					a_ = lambda a_: True							,					a_ = None							,					):
							while True:
														try:
																					snake_case_           =							input_type(input(a_).strip() or default)
																					if condition(a_):
																												return user_input
																					else:
																												print(f'''{user_input}: {err_msg}''')
																												continue
														except ValueError:
																					print(
																					    f'''{user_input}: Incorrect input type, expected {input_type.__name__!r}''')





def 						__UpperCAmelCase      (					):
							while True:
														print(' Linear Discriminant Analysis '.center(50							,					'*'))
														print('*' * 50							,					'\n')
														print('First of all we should specify the number of classes that')
														print('we want to generate as training dataset')
														# Trying to get number of classes
														snake_case_           =							valid_input(
														    input_type=a_							,					condition=lambda a_: x > 0							,					input_msg='Enter the number of classes (Data Groupings): '							,					err_msg='Number of classes should be positive!'							,					)

														print('-' * 1_00)

														# Trying to get the value of standard deviation
														snake_case_           =							valid_input(
														    input_type=a_							,					condition=lambda a_: x >= 0							,					input_msg=(
														        'Enter the value of standard deviation'
														        '(Default value is 1.0 for all classes): '
														    )							,					err_msg='Standard deviation should not be negative!'							,					default='1.0'							,					)

														print('-' * 1_00)

														# Trying to get number of instances in classes and theirs means to generate
														# dataset
														snake_case_           =							[]  # An empty list to store instance counts of classes in dataset
														for i in range(a_):
																					snake_case_           =							valid_input(
																					    input_type=a_							,					condition=lambda a_: x > 0							,					input_msg=(f'''Enter The number of instances for class_{i+1}: ''')							,					err_msg='Number of instances should be positive!'							,					)
																					counts.append(a_)
														print('-' * 1_00)

														# An empty list to store values of user-entered means of classes
														snake_case_           =							[]
														for a in range(a_):
																					snake_case_           =							valid_input(
																					    input_type=a_							,					input_msg=(f'''Enter the value of mean for class_{a+1}: ''')							,					err_msg='This is an invalid value.'							,					)
																					user_means.append(a_)
														print('-' * 1_00)

														print('Standard deviation: '							,					a_)
														# print out the number of instances in classes in separated line
														for i, count in enumerate(a_							,					1):
																					print(f'''Number of instances in class_{i} is: {count}''')
														print('-' * 1_00)

														# print out mean values of classes separated line
														for i, user_mean in enumerate(a_							,					1):
																					print(f'''Mean of class_{i} is: {user_mean}''')
														print('-' * 1_00)

														# Generating training dataset drawn from gaussian distribution
														snake_case_           =							[
														    gaussian_distribution(user_means[j]							,					a_							,					counts[j])
														    for j in range(a_)
														]
														print('Generated Normal Distribution: \n'							,					a_)
														print('-' * 1_00)

														# Generating Ys to detecting corresponding classes
														snake_case_           =							y_generator(a_							,					a_)
														print('Generated Corresponding Ys: \n'							,					a_)
														print('-' * 1_00)

														# Calculating the value of actual mean for each class
														snake_case_           =							[calculate_mean(counts[k]							,					x[k]) for k in range(a_)]
														# for loop iterates over number of elements in 'actual_means' list and print
														# out them in separated line
														for i, actual_mean in enumerate(a_							,					1):
																					print(f'''Actual(Real) mean of class_{i} is: {actual_mean}''')
														print('-' * 1_00)

														# Calculating the value of probabilities for each class
														snake_case_           =							[
														    calculate_probabilities(counts[i]							,					sum(a_)) for i in range(a_)
														]

														# for loop iterates over number of elements in 'probabilities' list and print
														# out them in separated line
														for i, probability in enumerate(a_							,					1):
																					print(f'''Probability of class_{i} is: {probability}''')
														print('-' * 1_00)

														# Calculating the values of variance for each class
														snake_case_           =							calculate_variance(a_							,					a_							,					sum(a_))
														print('Variance: '							,					a_)
														print('-' * 1_00)

														# Predicting Y values
														# storing predicted Y values in 'pre_indexes' variable
														snake_case_           =							predict_y_values(a_							,					a_							,					a_							,					a_)
														print('-' * 1_00)

														# Calculating Accuracy of the model
														print(f'''Accuracy: {accuracy(a_							,					a_)}''')
														print('-' * 1_00)
														print(' DONE '.center(1_00							,					'+'))

														if input('Press any key to restart or \'q\' for quit: ').strip().lower() == "q":
																					print('\n' + 'GoodBye!'.center(1_00							,					'-') + '\n')
																					break
														system('cls' if name == 'nt' else 'clear')  # noqa: S605


if __name__ == "__main__":
	main()





