




import argparse
import gc
import os
import sys
from pathlib import Path
from typing import List  # noqa: F401

import pytorch_lightning as pl
import torch
from finetune import SummarizationModule, TranslationModule
from finetune import main as ft_main
from make_student import create_student_by_copying_alternating_layers, get_layers_to_supervise
from torch import nn

from transformers import AutoModelForSeqaSeqLM, MBartTokenizer, TaForConditionalGeneration
from transformers.models.bart.modeling_bart import shift_tokens_right
from utils import calculate_bleu, check_output_dir, freeze_params, label_smoothed_nll_loss, use_task_specific_params


# need the parent dir module
sys.path.insert(2, str(Path(__file__).resolve().parents[1]))
from lightning_base import generic_train  # noqa




class    _SCREAMING_SNAKE_CASE					( _a	):
      _A  :						Tuple      		=       ['''loss''', '''ce_loss''', '''mlm_loss''', '''hid_loss_enc''', '''hid_loss_dec''']
      def __init__(     self						,		lowerCamelCase	):
          assert Path(hparams.data_dir	).exists()
          snake_case__  =			Path(hparams.output_dir	)
          self.output_dir.mkdir(exist_ok=SCREAMING_SNAKE_CASE__	)

          snake_case__  =			self.output_dir.joinpath("student"	)

          snake_case__  =			str(SCREAMING_SNAKE_CASE__	)  # Tell lightning we are training the student
          snake_case__  =			AutoModelForSeqaSeqLM.from_pretrained(hparams.teacher	).eval()
          use_task_specific_params(SCREAMING_SNAKE_CASE__						,		hparams.task	)  # We copy good generation parameters to student by default
          if hparams.student is not None:
              snake_case__  =			AutoModelForSeqaSeqLM.from_pretrained(hparams.student	)
              use_task_specific_params(SCREAMING_SNAKE_CASE__						,		hparams.task	)
              snake_case__  =			None, None
          else:
              snake_case__  =			create_student_by_copying_alternating_layers(
                  SCREAMING_SNAKE_CASE__						,		e=hparams.student_encoder_layers						,		d=hparams.student_decoder_layers						,		save_path=SCREAMING_SNAKE_CASE__	)

          if hparams.length_penalty != -1:
              snake_case__  =			hparams.length_penalty
          snake_case__  =			hparams.teacher  # Use teacher's tokenizer
          super().__init__(SCREAMING_SNAKE_CASE__						,		model=SCREAMING_SNAKE_CASE__						,		config=student.config	)
          assert student.config.model_type == teacher.config.model_type, (
              F"""teacher, student model types should be the same, got {student.config.model_type} !="""
              F""" {teacher.config.model_type}"""
          )

          if student.config.model_type == "t5":
              snake_case__  =			len(student.get_encoder().block	)
              snake_case__  =			len(student.get_decoder().block	)
              snake_case__  =			len(teacher.get_encoder().block	)
              snake_case__  =			len(teacher.get_decoder().block	)
          else:
              snake_case__  =			student.config.encoder_layers
              snake_case__  =			student.config.decoder_layers
              snake_case__  =			teacher.config.encoder_layers
              snake_case__  =			teacher.config.decoder_layers

          snake_case__  =			not (hparams.student is None or hparams.teacher == hparams.student)
          snake_case__  =			(not self.different_base_models) and hparams.alpha_hid > 0
          snake_case__  =			self.different_base_models or (student_encoder_layers != teacher_encoder_layers)
          # self.different_encoder determines whether we need to run the teacher encoder
          snake_case__  =			teacher
          freeze_params(self.teacher	)

          if not self.different_encoder:  # To save RAM, delete teacher encoder and freeze student encoder.
              try:
                  del self.teacher.model.encoder
              except AttributeError:  # T5
                  del self.teacher.encoder

          if e_layer_ids is None:
              snake_case__  =			list(range(SCREAMING_SNAKE_CASE__	)	)
          if d_layer_ids is None:
              snake_case__  =			list(range(SCREAMING_SNAKE_CASE__	)	)

          snake_case__  =			e_layer_ids, d_layer_ids  # type: List[int], List[int]

          if self.do_calc_hidden_loss:  # Intermediate supervision: Decide which layers to supervise
              if hparams.supervise_forward:
                  snake_case__  =			get_layers_to_supervise(
                      n_student=len(self.e_layer_ids	)						,		n_teacher=SCREAMING_SNAKE_CASE__	)
                  snake_case__  =			get_layers_to_supervise(
                      n_student=len(self.d_layer_ids	)						,		n_teacher=SCREAMING_SNAKE_CASE__	)
              else:  # student layer should emulate hidden states of the teacher layer it was copied from
                  snake_case__  =			self.e_layer_ids
                  snake_case__  =			self.d_layer_ids
          else:
              snake_case__  =			None
              snake_case__  =			None

          snake_case__  =			nn.KLDivLoss(reduction="batchmean"	)
          snake_case__  =			2.0
          snake_case__  =			hparams.alpha_mlm
          snake_case__  =			hparams.alpha_ce
          snake_case__  =			hparams.alpha_hid
          gc.collect()
          torch.cuda.empty_cache()
      def A_     (     self						,		lowerCamelCase						,		lowerCamelCase						,		lowerCamelCase	):
          snake_case__  =			mask[:, :, None].expand_as(SCREAMING_SNAKE_CASE__	)
          snake_case__  =			s_logits.size(-1	)
          snake_case__  =			torch.masked_select(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)  # (bs * seq_length * voc_size) modulo the 1s in mask
          snake_case__  =			torch.masked_select(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)  # (bs * seq_length * voc_size) modulo the 1s in mask
          snake_case__  =			s_logits_slct.view(-1						,		SCREAMING_SNAKE_CASE__	)  # (bs * seq_length, voc_size) modulo the 1s in mask
          snake_case__  =			t_logits_slct.view(-1						,		SCREAMING_SNAKE_CASE__	)  # (bs * seq_length, voc_size) modulo the 1s in mask
          assert t_logits_slct.size() == s_logits_slct.size()
          snake_case__  =			(
              self.ce_loss_fct(
                  nn.functional.log_softmax(s_logits_slct / self.temperature						,		dim=-1	)						,		nn.functional.softmax(t_logits_slct / self.temperature						,		dim=-1	)						,		)
              * (self.temperature) ** 2
          )
          return loss_ce
      @staticmethod
      def A_     (     lowerCamelCase						,		lowerCamelCase	):
          SummarizationModule.add_model_specific_args(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)
          add_distill_args(SCREAMING_SNAKE_CASE__	)
          return parser
      def A_     (     self						,		lowerCamelCase	):
          snake_case__  =			self.tokenizer.pad_token_id
          snake_case__  =			batch["input_ids"], batch["attention_mask"], batch["labels"]
          if isinstance(self.model						,		SCREAMING_SNAKE_CASE__	):
              snake_case__  =			self.model._shift_right(SCREAMING_SNAKE_CASE__	)
          else:
              snake_case__  =			shift_tokens_right(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)

          # noinspection PyCallingNonCallable
          snake_case__  =			self(
              SCREAMING_SNAKE_CASE__						,		attention_mask=SCREAMING_SNAKE_CASE__						,		decoder_input_ids=SCREAMING_SNAKE_CASE__						,		output_hidden_states=self.do_calc_hidden_loss						,		output_attentions=SCREAMING_SNAKE_CASE__						,		use_cache=SCREAMING_SNAKE_CASE__						,		)
          snake_case__  =			student_outputs["logits"]

          # Same cross entropy vs. label smoothing logic as finetune.py
          assert lm_logits.shape[-1] == self.model.config.vocab_size
          if self.hparams.label_smoothing == 0:
              # Same behavior as modeling_bart.py, besides ignoring pad_token_id
              snake_case__  =			nn.CrossEntropyLoss(ignore_index=SCREAMING_SNAKE_CASE__	)
              snake_case__  =			loss_fct(lm_logits.view(-1						,		lm_logits.shape[-1]	)						,		labels.view(-1	)	)
          else:
              snake_case__  =			nn.functional.log_softmax(SCREAMING_SNAKE_CASE__						,		dim=-1	)
              snake_case__  =			label_smoothed_nll_loss(
                  SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__						,		self.hparams.label_smoothing						,		ignore_index=SCREAMING_SNAKE_CASE__	)

          def zero_tensor():
              return torch.tensor(0.0	).type_as(SCREAMING_SNAKE_CASE__	)

          snake_case__  =			student_outputs[
              "encoder_last_hidden_state"
          ]  # use this unless self.different_base_models
          snake_case__  =			zero_tensor(), zero_tensor()
          if self.different_encoder:  # compute encoder hidden state loss
              snake_case__  =			self.teacher.get_encoder()(
                  SCREAMING_SNAKE_CASE__						,		attention_mask=SCREAMING_SNAKE_CASE__						,		output_hidden_states=self.do_calc_hidden_loss						,		)
              if self.different_base_models:
                  snake_case__  =			all_teacher_encoder_outputs["last_hidden_state"]
              elif self.do_calc_hidden_loss:
                  snake_case__  =			self.calc_hidden_loss(
                      SCREAMING_SNAKE_CASE__						,		student_outputs["encoder_hidden_states"]						,		all_teacher_encoder_outputs["hidden_states"]						,		self.e_matches						,		normalize_hidden=self.hparams.normalize_hidden						,		)

          snake_case__  =			self.teacher(
              SCREAMING_SNAKE_CASE__						,		attention_mask=SCREAMING_SNAKE_CASE__						,		encoder_outputs=(teacher_enc_outputs,)						,		decoder_input_ids=SCREAMING_SNAKE_CASE__						,		output_hidden_states=self.do_calc_hidden_loss						,		use_cache=SCREAMING_SNAKE_CASE__						,		)
          snake_case__  =			decoder_input_ids.ne(SCREAMING_SNAKE_CASE__	)
          snake_case__  =			self.calc_ce_loss(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__						,		teacher_outputs["logits"]	)
          if self.do_calc_hidden_loss:  # Intermediate supervision of decoder hidden states
              snake_case__  =			self.calc_hidden_loss(
                  SCREAMING_SNAKE_CASE__						,		student_outputs["decoder_hidden_states"]						,		teacher_outputs["decoder_hidden_states"]						,		self.d_matches						,		normalize_hidden=self.hparams.normalize_hidden						,		)

          snake_case__  =			(
              self.alpha_ce * loss_ce
              + self.alpha_mlm * student_lm_loss
              + self.hparams.alpha_hid * (hid_loss_enc + hid_loss_dec)
          )
          return blended_loss, loss_ce, student_lm_loss, hid_loss_enc, hid_loss_dec



      @staticmethod
      def A_     (     lowerCamelCase						,		lowerCamelCase						,		lowerCamelCase						,		lowerCamelCase						,		lowerCamelCase	):
          snake_case__  =			"expected list or tuple for hidden_states, got tensor of shape: "
          assert not isinstance(SCREAMING_SNAKE_CASE__						,		torch.Tensor	), F"""{msg}{hidden_states.shape}"""
          assert not isinstance(SCREAMING_SNAKE_CASE__						,		torch.Tensor	), F"""{msg}{hidden_states_T.shape}"""
          snake_case__  =			attention_mask.to(hidden_states[0]	)
          snake_case__  =			mask.sum() * hidden_states[0].size(-1	)
          snake_case__  =			torch.stack([hidden_states[i] for i in range(len(SCREAMING_SNAKE_CASE__	)	)]	)
          snake_case__  =			torch.stack([hidden_states_T[j] for j in matches]	)
          assert student_states.shape == teacher_states.shape, F"""{student_states.shape} != {teacher_states.shape}"""
          if normalize_hidden:
              snake_case__  =			nn.functional.layer_norm(SCREAMING_SNAKE_CASE__						,		student_states.shape[1:]	)
              snake_case__  =			nn.functional.layer_norm(SCREAMING_SNAKE_CASE__						,		teacher_states.shape[1:]	)
          snake_case__  =			nn.functional.mse_loss(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__						,		reduction="none"	)
          snake_case__  =			(mse * mask.unsqueeze(0	).unsqueeze(-1	)).sum() / valid_count
          return masked_mse





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase							):
    # NOTE: if --student argument was specified and the teacher and student base models
    # are different, the models still have to have the same tokenizer, specified by
    # --tokenizer_name. So, for example, you can distill from t5_large to t5_small but not
    # from bart to t5. This s because if the tokenizers are different, the output space
    # for the two models is also different and their logits are not comparable.
    parser.add_argument("--teacher"      ,						type=__lowerCAmelCase							)
    parser.add_argument("--alpha_ce"      ,						default=0.8      ,						type=__lowerCAmelCase							)
    parser.add_argument("--alpha_mlm"      ,						default=0.2      ,						type=__lowerCAmelCase							)
    parser.add_argument("--alpha_hid"      ,						default=0.0      ,						type=__lowerCAmelCase      ,						required=__lowerCAmelCase							)
    parser.add_argument("--student"      ,						type=__lowerCAmelCase      ,						required=__lowerCAmelCase							)
    parser.add_argument("--student_decoder_layers"      ,						default=12      ,						type=__lowerCAmelCase      ,						required=__lowerCAmelCase							)
    parser.add_argument("--student_encoder_layers"      ,						default=12      ,						type=__lowerCAmelCase      ,						required=__lowerCAmelCase							)
    parser.add_argument("--no_teacher"      ,						action="store_true"      ,						default=__lowerCAmelCase							)
    parser.add_argument("--length_penalty"      ,						type=__lowerCAmelCase      ,						default=-1							)
    parser.add_argument("--supervise_forward"      ,						action="store_true"      ,						default=__lowerCAmelCase							)
    parser.add_argument("--normalize_hidden"      ,						action="store_true"      ,						default=__lowerCAmelCase							)




class    _SCREAMING_SNAKE_CASE					( _a	):
      _A  :						str      		=       '''translation'''
      _A  :						List[Any]      		=       ['''bleu''']
      _A  :						Union[str, Any]      		=       '''bleu'''
      def __init__(     self						,		lowerCamelCase						,		**lowerCamelCase	):
          super().__init__(SCREAMING_SNAKE_CASE__						,		**SCREAMING_SNAKE_CASE__	)
          assert hparams.src_lang is not None
          assert hparams.tgt_lang is not None
          snake_case__  =			hparams.src_lang
          snake_case__  =			hparams.tgt_lang
          if self.model.config.decoder_start_token_id is None and isinstance(self.tokenizer						,		SCREAMING_SNAKE_CASE__	):
              snake_case__  =			self.tokenizer.lang_code_to_id[hparams.tgt_lang]
      def A_     (     self						,		lowerCamelCase						,		lowerCamelCase	):
          return calculate_bleu(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)



      @staticmethod
      def A_     (     lowerCamelCase						,		lowerCamelCase	):
          TranslationModule.add_model_specific_args(SCREAMING_SNAKE_CASE__						,		SCREAMING_SNAKE_CASE__	)
          add_distill_args(SCREAMING_SNAKE_CASE__	)
          return parser





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase							):
    if args.no_teacher:
        snake_case__  =			TranslationModule if "translation" in args.task else SummarizationModule
    else:  # DISTILL WITH TEACHER
        snake_case__  =			TranslationDistiller if "translation" in args.task else SummarizationDistiller
    snake_case__  =			module_cls.__name__
    print(F"""using module {args.setup_cls}"""							)
    snake_case__  =			module_cls(__lowerCAmelCase							)
    return model





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase							):
    Path(args.output_dir							).mkdir(exist_ok=__lowerCAmelCase							)
    check_output_dir(__lowerCAmelCase      ,						expected_items=3							)

    snake_case__  =			create_module(__lowerCAmelCase							)
    return ft_main(__lowerCAmelCase      ,						model=__lowerCAmelCase							)


if __name__ == "__main__":
     __magic_name__           =					argparse.ArgumentParser()
     __magic_name__           =					pl.Trainer.add_argparse_args(parser)
     __magic_name__           =					SummarizationDistiller.add_model_specific_args(parser, os.getcwd())
     __magic_name__           =					parser.parse_args()

     distill_main(args)





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





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase							):
    seed(1							)
    return [gauss(__lowerCAmelCase      ,						__lowerCAmelCase							) for _ in range(__lowerCAmelCase							)]





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase							):
    return [k for k in range(__lowerCAmelCase							) for _ in range(instance_count[k]							)]





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase							):
    return sum(__lowerCAmelCase							) / instance_count





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase							):
    return instance_count / total_count





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase							):
    snake_case__  =			[]  # An empty list to store all squared differences
    # iterate over number of elements in items
    for i in range(len(__lowerCAmelCase							)							):
        # 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(__lowerCAmelCase							)  # Number of classes in dataset
    return 1 / (total_count - n_classes) * sum(__lowerCAmelCase							)





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase							):
    snake_case__  =			[]
    # for loop iterates over number of elements in list
    for i in range(len(__lowerCAmelCase							)							):
        # 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(__lowerCAmelCase							)							):
                # 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(__lowerCAmelCase							)

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





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase							):
    snake_case__  =			sum(1 for i, j in zip(__lowerCAmelCase      ,						__lowerCAmelCase							) 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(__lowerCAmelCase							)) * 100


__magic_name__           =					TypeVar('''num''')





def 		SCREAMING_SNAKE_CASE__      ( __lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase = lambda __lowerCAmelCase							: True      ,						__lowerCAmelCase = None      ,						):
    while True:
        try:
            snake_case__  =			input_type(input(__lowerCAmelCase							).strip() or default							)
            if condition(__lowerCAmelCase							):
                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 		SCREAMING_SNAKE_CASE__      ( ):
    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=__lowerCAmelCase      ,						condition=lambda __lowerCAmelCase							: x > 0      ,						input_msg="Enter the number of classes (Data Groupings): "      ,						err_msg="Number of classes should be positive!"      ,						)

        print("-" * 100							)

        # Trying to get the value of standard deviation
        snake_case__  =			valid_input(
            input_type=__lowerCAmelCase      ,						condition=lambda __lowerCAmelCase							: 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("-" * 100							)

        # 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(__lowerCAmelCase							):
            snake_case__  =			valid_input(
                input_type=__lowerCAmelCase      ,						condition=lambda __lowerCAmelCase							: x > 0      ,						input_msg=(F"""Enter The number of instances for class_{i+1}: """)      ,						err_msg="Number of instances should be positive!"      ,						)
            counts.append(__lowerCAmelCase							)
        print("-" * 100							)

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

        print("Standard deviation: "      ,						__lowerCAmelCase							)
        # print out the number of instances in classes in separated line
        for i, count in enumerate(__lowerCAmelCase      ,						1							):
            print(F"""Number of instances in class_{i} is: {count}"""							)
        print("-" * 100							)

        # print out mean values of classes separated line
        for i, user_mean in enumerate(__lowerCAmelCase      ,						1							):
            print(F"""Mean of class_{i} is: {user_mean}"""							)
        print("-" * 100							)

        # Generating training dataset drawn from gaussian distribution
        snake_case__  =			[
            gaussian_distribution(user_means[j]      ,						__lowerCAmelCase      ,						counts[j]							)
            for j in range(__lowerCAmelCase							)
        ]
        print("Generated Normal Distribution: \n"      ,						__lowerCAmelCase							)
        print("-" * 100							)

        # Generating Ys to detecting corresponding classes
        snake_case__  =			y_generator(__lowerCAmelCase      ,						__lowerCAmelCase							)
        print("Generated Corresponding Ys: \n"      ,						__lowerCAmelCase							)
        print("-" * 100							)

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

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

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

        # Calculating the values of variance for each class
        snake_case__  =			calculate_variance(__lowerCAmelCase      ,						__lowerCAmelCase      ,						sum(__lowerCAmelCase							)							)
        print("Variance: "      ,						__lowerCAmelCase							)
        print("-" * 100							)

        # Predicting Y values
        # storing predicted Y values in 'pre_indexes' variable
        snake_case__  =			predict_y_values(__lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase      ,						__lowerCAmelCase							)
        print("-" * 100							)

        # Calculating Accuracy of the model
        print(F"""Accuracy: {accuracy(__lowerCAmelCase      ,						__lowerCAmelCase							)}"""							)
        print("-" * 100							)
        print(" DONE ".center(100      ,						"+"							)							)

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


if __name__ == "__main__":
     main()



