import argparse
import logging
import os
from pathlib import Path
from typing import Any, Dict

import pytorch_lightning as pl
from pytorch_lightning.utilities import rank_zero_info

from transformers import (
    AdamW,
    AutoConfig,
    AutoModel,
    AutoModelForPreTraining,
    AutoModelForQuestionAnswering,
    AutoModelForSeqaSeqLM,
    AutoModelForSequenceClassification,
    AutoModelForTokenClassification,
    AutoModelWithLMHead,
    AutoTokenizer,
    PretrainedConfig,
    PreTrainedTokenizer,
)
from transformers.optimization import (
    Adafactor,
    get_cosine_schedule_with_warmup,
    get_cosine_with_hard_restarts_schedule_with_warmup,
    get_linear_schedule_with_warmup,
    get_polynomial_decay_schedule_with_warmup,
)
from transformers.utils.versions import require_version


a_					      = logging.getLogger(__name__)

require_version('pytorch_lightning>=1.0.4')

a_					      = {
    'base': AutoModel,
    'sequence-classification': AutoModelForSequenceClassification,
    'question-answering': AutoModelForQuestionAnswering,
    'pretraining': AutoModelForPreTraining,
    'token-classification': AutoModelForTokenClassification,
    'language-modeling': AutoModelWithLMHead,
    'summarization': AutoModelForSeqaSeqLM,
    'translation': AutoModelForSeqaSeqLM,
}


# update this and the import above to support new schedulers from transformers.optimization
a_					      = {
    'linear': get_linear_schedule_with_warmup,
    'cosine': get_cosine_schedule_with_warmup,
    'cosine_w_restarts': get_cosine_with_hard_restarts_schedule_with_warmup,
    'polynomial': get_polynomial_decay_schedule_with_warmup,
    # '': get_constant_schedule,             # not supported for now
    # '': get_constant_schedule_with_warmup, # not supported for now
}
a_					      = sorted(arg_to_scheduler.keys())
a_					      = '{' + ', '.join(arg_to_scheduler_choices) + '}'





class 	_lowercase		(      pl.LightningModule ):

 def __init__(				self			:							str     ,   snake_case			:							argparse.Namespace     ,   snake_case			:							List[Any]=None     ,   snake_case			:							Dict="base"     ,   snake_case			:							Dict=None     ,   snake_case			:							Dict=None     ,   snake_case			:							Any=None     ,   **snake_case			:							Optional[Any]     ,   )     ->		Any:

  """simple docstring"""





  super().__init__()
  # TODO: move to self.save_hyperparameters()
  # self.save_hyperparameters()
  # can also expand arguments into trainer signature for easier reading

  self.save_hyperparameters(snake_case    )
  UpperCamelCase_      :		List[str]				=      0
  UpperCamelCase_      :		List[Any]				=      Path(self.hparams.output_dir    )
  UpperCamelCase_      :		Optional[Any]				=      self.hparams.cache_dir if self.hparams.cache_dir else None
  if config is None:
   UpperCamelCase_      :		Dict				=      AutoConfig.from_pretrained(
       self.hparams.config_name if self.hparams.config_name else self.hparams.model_name_or_path     ,   **({'num_labels': num_labels} if num_labels is not None else {})     ,   cache_dir=snake_case     ,   **snake_case     ,   )
  else:
   UpperCamelCase_      :		PretrainedConfig				=      config

  UpperCamelCase_      :		Any				=      ('encoder_layerdrop', 'decoder_layerdrop', 'dropout', 'attention_dropout')
  for p in extra_model_params:
   if getattr(self.hparams     ,   snake_case     ,   snake_case    ):
    assert hasattr(self.config     ,   snake_case    ), f"model config doesn't have a `{p}` attribute"
    setattr(self.config     ,   snake_case     ,   getattr(self.hparams     ,   snake_case    )    )

  if tokenizer is None:
   UpperCamelCase_      :		Union[str, Any]				=      AutoTokenizer.from_pretrained(
       self.hparams.tokenizer_name if self.hparams.tokenizer_name else self.hparams.model_name_or_path     ,   cache_dir=snake_case     ,   )
  else:
   UpperCamelCase_      :		PreTrainedTokenizer				=      tokenizer
  UpperCamelCase_      :		List[str]				=      MODEL_MODES[mode]
  if model is None:
   UpperCamelCase_      :		Optional[Any]				=      self.model_type.from_pretrained(
       self.hparams.model_name_or_path     ,   from_tf=bool('.ckpt' in self.hparams.model_name_or_path    )     ,   config=self.config     ,   cache_dir=snake_case     ,   )
  else:
   UpperCamelCase_      :		str				=      model

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Any     ,   *snake_case			:							Any     ,   **snake_case			:							int    )     ->		Dict:

  """simple docstring"""





  UpperCamelCase_      :		str				=      self.model_type.from_pretrained(*snake_case     ,   **snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							int    )     ->		Tuple:

  """simple docstring"""





  UpperCamelCase_      :		List[str]				=      arg_to_scheduler[self.hparams.lr_scheduler]
  UpperCamelCase_      :		Any				=      get_schedule_func(
      self.opt     ,   num_warmup_steps=self.hparams.warmup_steps     ,   num_training_steps=self.total_steps()    )
  UpperCamelCase_      :		Tuple				=      {'scheduler': scheduler, 'interval': 'step', 'frequency': 1}
  return scheduler

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Optional[Any]    )     ->		Optional[int]:

  """simple docstring"""





  UpperCamelCase_      :		Union[str, Any]				=      self.model
  UpperCamelCase_      :		Optional[int]				=      ['bias', 'LayerNorm.weight']
  UpperCamelCase_      :		str				=      [
      {
          'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay    )],
          'weight_decay': self.hparams.weight_decay,
      },
      {
          'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay    )],
          'weight_decay': 0.0,
      },
  ]
  if self.hparams.adafactor:
   UpperCamelCase_      :		Union[str, Any]				=      Adafactor(
       snake_case     ,   lr=self.hparams.learning_rate     ,   scale_parameter=snake_case     ,   relative_step=snake_case    )

  else:
   UpperCamelCase_      :		List[Any]				=      AdamW(
       snake_case     ,   lr=self.hparams.learning_rate     ,   eps=self.hparams.adam_epsilon    )
  UpperCamelCase_      :		Optional[Any]				=      optimizer

  UpperCamelCase_      :		Tuple				=      self.get_lr_scheduler()

  return [optimizer], [scheduler]

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Dict     ,   snake_case			:							Any     ,   snake_case			:							Any    )     ->		Optional[Any]:

  """simple docstring"""





  return self.validation_step(snake_case     ,   snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							int     ,   snake_case			:							List[str]    )     ->		int:

  """simple docstring"""





  return self.validation_end(snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Optional[Any]    )     ->		int:

  """simple docstring"""





  UpperCamelCase_      :		Optional[int]				=      max(1     ,   self.hparams.gpus    )  # TODO: consider num_tpu_cores
  UpperCamelCase_      :		Dict				=      self.hparams.train_batch_size * self.hparams.accumulate_grad_batches * num_devices
  return (self.dataset_size / effective_batch_size) * self.hparams.max_epochs

 def 		SCREAMING_SNAKE_CASE__     (				self			:							List[str]     ,   snake_case			:							Dict    )     ->		str:

  """simple docstring"""





  if stage == "test":
   UpperCamelCase_      :		Any				=      len(self.test_dataloader().dataset    )
  else:
   UpperCamelCase_      :		int				=      self.get_dataloader('train'     ,   self.hparams.train_batch_size     ,   shuffle=snake_case    )
   UpperCamelCase_      :		Any				=      len(self.train_dataloader().dataset    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							List[Any]     ,   snake_case			:							str     ,   snake_case			:							int     ,   snake_case			:							bool = False    )     ->		Tuple:

  """simple docstring"""





  raise NotImplementedError('You must implement this for your task'    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Dict    )     ->		Optional[Any]:

  """simple docstring"""





  return self.train_loader

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Dict    )     ->		int:

  """simple docstring"""





  return self.get_dataloader('dev'     ,   self.hparams.eval_batch_size     ,   shuffle=snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Union[str, Any]    )     ->		List[Any]:

  """simple docstring"""





  return self.get_dataloader('test'     ,   self.hparams.eval_batch_size     ,   shuffle=snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							Tuple     ,   snake_case			:							int    )     ->		Union[str, Any]:

  """simple docstring"""





  return os.path.join(
      self.hparams.data_dir     ,   'cached_{}_{}_{}'.format(
          snake_case     ,   list(filter(snake_case     ,   self.hparams.model_name_or_path.split('/'    )    )    ).pop()     ,   str(self.hparams.max_seq_length    )     ,   )     ,   )

 @pl.utilities.rank_zero_only
 def 		SCREAMING_SNAKE_CASE__     (				self			:							List[Any]     ,   snake_case			:							Dict[str, Any]    )     ->		None:

  """simple docstring"""





  UpperCamelCase_      :		Tuple				=      self.output_dir.joinpath('best_tfmr'    )
  UpperCamelCase_      :		Optional[Any]				=      self.step_count
  self.model.save_pretrained(snake_case    )
  self.tokenizer.save_pretrained(snake_case    )


 @staticmethod
 def 		SCREAMING_SNAKE_CASE__     (				snake_case			:							List[str]     ,   snake_case			:							Optional[int]    )     ->		Optional[int]:

  """simple docstring"""





  parser.add_argument(
      '--model_name_or_path'     ,   default=snake_case     ,   type=snake_case     ,   required=snake_case     ,   help='Path to pretrained model or model identifier from huggingface.co/models'     ,   )
  parser.add_argument(
      '--config_name'     ,   default=''     ,   type=snake_case     ,   help='Pretrained config name or path if not the same as model_name'    )
  parser.add_argument(
      '--tokenizer_name'     ,   default=snake_case     ,   type=snake_case     ,   help='Pretrained tokenizer name or path if not the same as model_name'     ,   )
  parser.add_argument(
      '--cache_dir'     ,   default=''     ,   type=snake_case     ,   help='Where do you want to store the pre-trained models downloaded from huggingface.co'     ,   )
  parser.add_argument(
      '--encoder_layerdrop'     ,   type=snake_case     ,   help='Encoder layer dropout probability (Optional). Goes into model.config'     ,   )
  parser.add_argument(
      '--decoder_layerdrop'     ,   type=snake_case     ,   help='Decoder layer dropout probability (Optional). Goes into model.config'     ,   )
  parser.add_argument(
      '--dropout'     ,   type=snake_case     ,   help='Dropout probability (Optional). Goes into model.config'     ,   )
  parser.add_argument(
      '--attention_dropout'     ,   type=snake_case     ,   help='Attention dropout probability (Optional). Goes into model.config'     ,   )
  parser.add_argument('--learning_rate'     ,   default=5e-5     ,   type=snake_case     ,   help='The initial learning rate for Adam.'    )
  parser.add_argument(
      '--lr_scheduler'     ,   default='linear'     ,   choices=snake_case     ,   metavar=snake_case     ,   type=snake_case     ,   help='Learning rate scheduler'     ,   )
  parser.add_argument('--weight_decay'     ,   default=0.0     ,   type=snake_case     ,   help='Weight decay if we apply some.'    )
  parser.add_argument('--adam_epsilon'     ,   default=1e-8     ,   type=snake_case     ,   help='Epsilon for Adam optimizer.'    )
  parser.add_argument('--warmup_steps'     ,   default=0     ,   type=snake_case     ,   help='Linear warmup over warmup_steps.'    )
  parser.add_argument('--num_workers'     ,   default=4     ,   type=snake_case     ,   help='kwarg passed to DataLoader'    )
  parser.add_argument('--num_train_epochs'     ,   dest='max_epochs'     ,   default=3     ,   type=snake_case    )
  parser.add_argument('--train_batch_size'     ,   default=3_2     ,   type=snake_case    )
  parser.add_argument('--eval_batch_size'     ,   default=3_2     ,   type=snake_case    )
  parser.add_argument('--adafactor'     ,   action='store_true'    )





class 	_lowercase		(      pl.Callback ):

 def 		SCREAMING_SNAKE_CASE__     (				self			:							List[str]     ,   snake_case			:							Tuple     ,   snake_case			:							int    )     ->		int:

  """simple docstring"""





  if (
      trainer.is_global_zero and trainer.global_rank == 0
  ):  # we initialize the retriever only on master worker with RAY. In new pytorch-lightning accelorators are removed.
   pl_module.model.rag.retriever.init_retrieval()  # better to use hook functions.





class 	_lowercase		(      pl.Callback ):

 def 		SCREAMING_SNAKE_CASE__     (				self			:							str     ,   snake_case			:							Dict     ,   snake_case			:							Any    )     ->		List[Any]:

  """simple docstring"""





  UpperCamelCase_      :		Dict				=      trainer.lr_schedulers[0]['scheduler']
  UpperCamelCase_      :		Union[str, Any]				=      {f"lr_group_{i}": lr for i, lr in enumerate(lr_scheduler.get_lr()    )}
  pl_module.logger.log_metrics(snake_case    )

 def 		SCREAMING_SNAKE_CASE__     (				self			:							int     ,   snake_case			:							pl.Trainer     ,   snake_case			:							pl.LightningModule    )     ->		Optional[Any]:

  """simple docstring"""





  rank_zero_info('***** Validation results *****'    )
  UpperCamelCase_      :		Tuple				=      trainer.callback_metrics
  # Log results
  for key in sorted(snake_case    ):
   if key not in ["log", "progress_bar"]:
    rank_zero_info('{} = {}\n'.format(snake_case     ,   str(metrics[key]    )    )    )


 def 		SCREAMING_SNAKE_CASE__     (				self			:							List[str]     ,   snake_case			:							pl.Trainer     ,   snake_case			:							pl.LightningModule    )     ->		str:

  """simple docstring"""





  rank_zero_info('***** Test results *****'    )
  UpperCamelCase_      :		List[str]				=      trainer.callback_metrics
  # Log and save results to file
  UpperCamelCase_      :		Union[str, Any]				=      os.path.join(pl_module.hparams.output_dir     ,   'test_results.txt'    )
  with open(snake_case     ,   'w'    ) as writer:
   for key in sorted(snake_case    ):
    if key not in ["log", "progress_bar"]:
     rank_zero_info('{} = {}\n'.format(snake_case     ,   str(metrics[key]    )    )    )
     writer.write('{} = {}\n'.format(snake_case     ,   str(metrics[key]    )    )    )





def      __lowercase  (		lowerCamelCase      :  List[Any]						,						lowerCamelCase      :  Dict			):
 #  To allow all pl args uncomment the following line
 #  parser = pl.Trainer.add_argparse_args(parser)
 parser.add_argument(
     '--output_dir'						,						default=lowerCamelCase						,						type=lowerCamelCase						,						required=lowerCamelCase						,						help='The output directory where the model predictions and checkpoints will be written.'						,						)
 parser.add_argument(
     '--fp16'						,						action='store_true'						,						help='Whether to use 16-bit (mixed) precision (through NVIDIA apex) instead of 32-bit'						,						)

 parser.add_argument(
     '--fp16_opt_level'						,						type=lowerCamelCase						,						default='O2'						,						help=(
         'For fp16: Apex AMP optimization level selected in [\'O0\', \'O1\', \'O2\', and \'O3\'].'
         'See details at https://nvidia.github.io/apex/amp.html'
     )						,						)
 parser.add_argument('--n_tpu_cores'						,						dest='tpu_cores'						,						type=lowerCamelCase			)
 parser.add_argument('--max_grad_norm'						,						dest='gradient_clip_val'						,						default=1.0						,						type=lowerCamelCase						,						help='Max gradient norm'			)
 parser.add_argument('--do_train'						,						action='store_true'						,						help='Whether to run training.'			)
 parser.add_argument('--do_predict'						,						action='store_true'						,						help='Whether to run predictions on the test set.'			)
 parser.add_argument(
     '--gradient_accumulation_steps'						,						dest='accumulate_grad_batches'						,						type=lowerCamelCase						,						default=1						,						help='Number of updates steps to accumulate before performing a backward/update pass.'						,						)
 parser.add_argument('--seed'						,						type=lowerCamelCase						,						default=42						,						help='random seed for initialization'			)
 parser.add_argument(
     '--data_dir'						,						default=lowerCamelCase						,						type=lowerCamelCase						,						required=lowerCamelCase						,						help='The input data dir. Should contain the training files for the CoNLL-2003 NER task.'						,						)






def      __lowercase  (		lowerCamelCase      :  BaseTransformer						,						lowerCamelCase      :  argparse.Namespace						,						lowerCamelCase      :  Union[str, Any]=None						,						lowerCamelCase      :  int=True						,						lowerCamelCase      :  Dict=None						,						lowerCamelCase      :  str=[]						,						lowerCamelCase      :  List[Any]=None						,						lowerCamelCase      :  List[Any]=None						,						**lowerCamelCase      :  List[str]						,						):
 pl.seed_everything(args.seed			)

 # init model
 UpperCamelCase_      :		Optional[Any]				=      Path(model.hparams.output_dir			)
 odir.mkdir(exist_ok=lowerCamelCase			)

 # add custom checkpoints
 if checkpoint_callback is None:
  UpperCamelCase_      :		Union[str, Any]				=      pl.callbacks.ModelCheckpoint(
      filepath=args.output_dir						,						prefix='checkpoint'						,						monitor='val_loss'						,						mode='min'						,						save_top_k=1			)
 if early_stopping_callback:
  extra_callbacks.append(lowerCamelCase			)
 if logging_callback is None:
  UpperCamelCase_      :		Tuple				=      LoggingCallback()

 UpperCamelCase_      :		Optional[Any]				=      {}

 # TODO: remove with PyTorch 1.6 since pl uses native amp
 if args.fpaa:
  UpperCamelCase_      :		Any				=      16
  # train_params["amp_level"] = args.fp16_opt_level

 if args.gpus > 1:
  UpperCamelCase_      :		Union[str, Any]				=      'auto'  # "ddp"
  UpperCamelCase_      :		str				=      'ddp'

 UpperCamelCase_      :		Optional[int]				=      args.accumulate_grad_batches
 UpperCamelCase_      :		int				=      None  # extra_train_kwargs.get("profiler", None) #get unwanted logs
 UpperCamelCase_      :		Any				=      'auto'

 UpperCamelCase_      :		Optional[int]				=      pl.Trainer.from_argparse_args(
     lowerCamelCase						,						weights_summary=lowerCamelCase						,						callbacks=[logging_callback] + extra_callbacks + [checkpoint_callback] + [InitCallback()]						,						logger=lowerCamelCase						,						**lowerCamelCase						,						)

 if args.do_train:
  trainer.fit(lowerCamelCase			)

 return trainer




def      __lowercase  (		lowerCamelCase      :  int = 998001			):
 UpperCamelCase_      :		int				=      0
 for i in range(999						,						99						,						-1			):  # 3 digit numbers range from 999 down to 100
  for j in range(999						,						99						,						-1			):
   UpperCamelCase_      :		str				=      str(i * j			)
   if product_string == product_string[::-1] and i * j < n:
    UpperCamelCase_      :		str				=      max(lowerCamelCase						,						i * j			)
 return answer


if __name__ == "__main__":
   print(F"""{solution() = }""")



