






import unittest

from transformers import XLMConfig, is_torch_available
from transformers.testing_utils import require_torch, slow, torch_device

from ...generation.test_utils import GenerationTesterMixin
from ...test_configuration_common import ConfigTester
from ...test_modeling_common import ModelTesterMixin, ids_tensor, random_attention_mask
from ...test_pipeline_mixin import PipelineTesterMixin


if is_torch_available():
     import torch

     from transformers import (
         XLMForMultipleChoice,
         XLMForQuestionAnswering,
         XLMForQuestionAnsweringSimple,
         XLMForSequenceClassification,
         XLMForTokenClassification,
         XLMModel,
         XLMWithLMHeadModel,
     )
     from transformers.models.xlm.modeling_xlm import XLM_PRETRAINED_MODEL_ARCHIVE_LIST






class 		lowercase_					:






      '''simple docstring'''




      def __init__(      self      :						List[Any] ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Tuple=13 ,					__UpperCAmelCase      :						List[str]=7 ,					__UpperCAmelCase      :						str=True ,					__UpperCAmelCase      :						Union[str, Any]=True ,					__UpperCAmelCase      :						List[Any]=True ,					__UpperCAmelCase      :						Dict=True ,					__UpperCAmelCase      :						Union[str, Any]=True ,					__UpperCAmelCase      :						int=False ,					__UpperCAmelCase      :						str=False ,					__UpperCAmelCase      :						Optional[Any]=False ,					__UpperCAmelCase      :						List[str]=2 ,					__UpperCAmelCase      :						int=99 ,					__UpperCAmelCase      :						str=0 ,					__UpperCAmelCase      :						Dict=32 ,					__UpperCAmelCase      :						str=5 ,					__UpperCAmelCase      :						Any=4 ,					__UpperCAmelCase      :						Optional[Any]=0.1 ,					__UpperCAmelCase      :						List[str]=0.1 ,					__UpperCAmelCase      :						Union[str, Any]=512 ,					__UpperCAmelCase      :						Any=2 ,					__UpperCAmelCase      :						List[Any]=0.02 ,					__UpperCAmelCase      :						Any=2 ,					__UpperCAmelCase      :						str=4 ,					__UpperCAmelCase      :						Any="last" ,					__UpperCAmelCase      :						str=True ,					__UpperCAmelCase      :						Union[str, Any]=None ,					__UpperCAmelCase      :						Tuple=0 ,					)    ->List[str]:




       """simple docstring"""


       a		  =      parent
       a		  =      batch_size
       a		  =      seq_length
       a		  =      is_training
       a		  =      use_input_lengths
       a		  =      use_token_type_ids
       a		  =      use_labels
       a		  =      gelu_activation
       a		  =      sinusoidal_embeddings
       a		  =      causal
       a		  =      asm
       a		  =      n_langs
       a		  =      vocab_size
       a		  =      n_special
       a		  =      hidden_size
       a		  =      num_hidden_layers
       a		  =      num_attention_heads
       a		  =      hidden_dropout_prob
       a		  =      attention_probs_dropout_prob
       a		  =      max_position_embeddings
       a		  =      type_sequence_label_size
       a		  =      initializer_range
       a		  =      num_labels
       a		  =      num_choices
       a		  =      summary_type
       a		  =      use_proj
       a		  =      scope
       a		  =      bos_token_id







      def       __lowerCAmelCase					(      self      :						int  )    ->Dict:




       """simple docstring"""


       a		  =      ids_tensor([self.batch_size, self.seq_length] ,					self.vocab_size  )
       a		  =      random_attention_mask([self.batch_size, self.seq_length]  )

       a		  =      None
       if self.use_input_lengths:
        a		  =      (
            ids_tensor([self.batch_size] ,					vocab_size=2  ) + self.seq_length - 2
        )  # small variation of seq_length

       a		  =      None
       if self.use_token_type_ids:
        a		  =      ids_tensor([self.batch_size, self.seq_length] ,					self.n_langs  )

       a		  =      None
       a		  =      None
       a		  =      None
       if self.use_labels:
        a		  =      ids_tensor([self.batch_size] ,					self.type_sequence_label_size  )
        a		  =      ids_tensor([self.batch_size, self.seq_length] ,					self.num_labels  )
        a		  =      ids_tensor([self.batch_size] ,					2  ).float()
        a		  =      ids_tensor([self.batch_size] ,					self.num_choices  )

       a		  =      self.get_config()

       return (
           config,
           input_ids,
           token_type_ids,
           input_lengths,
           sequence_labels,
           token_labels,
           is_impossible_labels,
           choice_labels,
           input_mask,
       )







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




       """simple docstring"""


       return XLMConfig(
           vocab_size=self.vocab_size ,					n_special=self.n_special ,					emb_dim=self.hidden_size ,					n_layers=self.num_hidden_layers ,					n_heads=self.num_attention_heads ,					dropout=self.hidden_dropout_prob ,					attention_dropout=self.attention_probs_dropout_prob ,					gelu_activation=self.gelu_activation ,					sinusoidal_embeddings=self.sinusoidal_embeddings ,					asm=self.asm ,					causal=self.causal ,					n_langs=self.n_langs ,					max_position_embeddings=self.max_position_embeddings ,					initializer_range=self.initializer_range ,					summary_type=self.summary_type ,					use_proj=self.use_proj ,					num_labels=self.num_labels ,					bos_token_id=self.bos_token_id ,					)







      def       __lowerCAmelCase					(      self      :						Optional[Any] ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						str ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						Optional[int] ,					)    ->List[str]:




       """simple docstring"""


       a		  =      XLMModel(config=__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()
       a		  =      model(__UpperCAmelCase ,					lengths=__UpperCAmelCase ,					langs=__UpperCAmelCase  )
       a		  =      model(__UpperCAmelCase ,					langs=__UpperCAmelCase  )
       a		  =      model(__UpperCAmelCase  )
       self.parent.assertEqual(result.last_hidden_state.shape ,					(self.batch_size, self.seq_length, self.hidden_size)  )







      def       __lowerCAmelCase					(      self      :						Dict ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						str ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Optional[Any] ,					)    ->Any:




       """simple docstring"""


       a		  =      XLMWithLMHeadModel(__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()

       a		  =      model(__UpperCAmelCase ,					token_type_ids=__UpperCAmelCase ,					labels=__UpperCAmelCase  )
       self.parent.assertEqual(result.loss.shape ,					()  )
       self.parent.assertEqual(result.logits.shape ,					(self.batch_size, self.seq_length, self.vocab_size)  )







      def       __lowerCAmelCase					(      self      :						List[str] ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						str ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Dict ,					)    ->Optional[Any]:




       """simple docstring"""


       a		  =      XLMForQuestionAnsweringSimple(__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()

       a		  =      model(__UpperCAmelCase  )

       a		  =      model(__UpperCAmelCase ,					start_positions=__UpperCAmelCase ,					end_positions=__UpperCAmelCase  )
       a		  =      outputs
       self.parent.assertEqual(result.start_logits.shape ,					(self.batch_size, self.seq_length)  )
       self.parent.assertEqual(result.end_logits.shape ,					(self.batch_size, self.seq_length)  )







      def       __lowerCAmelCase					(      self      :						List[str] ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						List[str] ,					)    ->List[Any]:




       """simple docstring"""


       a		  =      XLMForQuestionAnswering(__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()

       a		  =      model(__UpperCAmelCase  )

       a		  =      model(
           __UpperCAmelCase ,					start_positions=__UpperCAmelCase ,					end_positions=__UpperCAmelCase ,					cls_index=__UpperCAmelCase ,					is_impossible=__UpperCAmelCase ,					p_mask=__UpperCAmelCase ,					)

       a		  =      model(
           __UpperCAmelCase ,					start_positions=__UpperCAmelCase ,					end_positions=__UpperCAmelCase ,					cls_index=__UpperCAmelCase ,					is_impossible=__UpperCAmelCase ,					)

       ((a)  ,     )		  =      result_with_labels.to_tuple()

       a		  =      model(__UpperCAmelCase ,					start_positions=__UpperCAmelCase ,					end_positions=__UpperCAmelCase  )

       ((a)  ,     )		  =      result_with_labels.to_tuple()

       self.parent.assertEqual(result_with_labels.loss.shape ,					()  )
       self.parent.assertEqual(result.start_top_log_probs.shape ,					(self.batch_size, model.config.start_n_top)  )
       self.parent.assertEqual(result.start_top_index.shape ,					(self.batch_size, model.config.start_n_top)  )
       self.parent.assertEqual(
           result.end_top_log_probs.shape ,					(self.batch_size, model.config.start_n_top * model.config.end_n_top)  )
       self.parent.assertEqual(
           result.end_top_index.shape ,					(self.batch_size, model.config.start_n_top * model.config.end_n_top)  )
       self.parent.assertEqual(result.cls_logits.shape ,					(self.batch_size,)  )







      def       __lowerCAmelCase					(      self      :						Any ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						Optional[int] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						Optional[Any] ,					)    ->Dict:




       """simple docstring"""


       a		  =      XLMForSequenceClassification(__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()

       a		  =      model(__UpperCAmelCase  )
       a		  =      model(__UpperCAmelCase ,					labels=__UpperCAmelCase  )
       self.parent.assertEqual(result.loss.shape ,					()  )
       self.parent.assertEqual(result.logits.shape ,					(self.batch_size, self.type_sequence_label_size)  )







      def       __lowerCAmelCase					(      self      :						List[str] ,					__UpperCAmelCase      :						Any ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						str ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						str ,					)    ->Optional[Any]:




       """simple docstring"""


       a		  =      self.num_labels
       a		  =      XLMForTokenClassification(__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()

       a		  =      model(__UpperCAmelCase ,					attention_mask=__UpperCAmelCase ,					labels=__UpperCAmelCase  )
       self.parent.assertEqual(result.logits.shape ,					(self.batch_size, self.seq_length, self.num_labels)  )







      def       __lowerCAmelCase					(      self      :						List[Any] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						Optional[int] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Union[str, Any] ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						List[str] ,					)    ->str:




       """simple docstring"""


       a		  =      self.num_choices
       a		  =      XLMForMultipleChoice(config=__UpperCAmelCase  )
       model.to(__UpperCAmelCase  )
       model.eval()
       a		  =      input_ids.unsqueeze(1  ).expand(-1 ,					self.num_choices ,					-1  ).contiguous()
       a		  =      token_type_ids.unsqueeze(1  ).expand(-1 ,					self.num_choices ,					-1  ).contiguous()
       a		  =      input_mask.unsqueeze(1  ).expand(-1 ,					self.num_choices ,					-1  ).contiguous()
       a		  =      model(
           __UpperCAmelCase ,					attention_mask=__UpperCAmelCase ,					token_type_ids=__UpperCAmelCase ,					labels=__UpperCAmelCase ,					)
       self.parent.assertEqual(result.logits.shape ,					(self.batch_size, self.num_choices)  )







      def       __lowerCAmelCase					(      self      :						Optional[Any]  )    ->Tuple:




       """simple docstring"""


       a		  =      self.prepare_config_and_inputs()
       (
           (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     (
           a
       )  ,     
       )		  =      config_and_inputs
       a		  =      {'''input_ids''': input_ids, '''token_type_ids''': token_type_ids, '''lengths''': input_lengths}
       return config, inputs_dict






@require_torch
class 		lowercase_					(	lowercase ,  lowercase ,  lowercase ,  unittest.TestCase   ):






      '''simple docstring'''




      __snake_case       =  (
          (
              XLMModel,
              XLMWithLMHeadModel,
              XLMForQuestionAnswering,
              XLMForSequenceClassification,
              XLMForQuestionAnsweringSimple,
              XLMForTokenClassification,
              XLMForMultipleChoice,
          )
          if is_torch_available()
          else ()
      )
      __snake_case       =  (
          (XLMWithLMHeadModel,) if is_torch_available() else ()
      )  # TODO (PVP): Check other models whether language generation is also applicable
      __snake_case       =  (
          {
              '''feature-extraction''': XLMModel,
              '''fill-mask''': XLMWithLMHeadModel,
              '''question-answering''': XLMForQuestionAnsweringSimple,
              '''text-classification''': XLMForSequenceClassification,
              '''text-generation''': XLMWithLMHeadModel,
              '''token-classification''': XLMForTokenClassification,
              '''zero-shot''': XLMForSequenceClassification,
          }
          if is_torch_available()
          else {}
      )







      def       __lowerCAmelCase					(      self      :						int ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						int ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Tuple ,					__UpperCAmelCase      :						List[Any]  )    ->int:




       """simple docstring"""


       if (
           pipeline_test_casse_name == "QAPipelineTests"
           and tokenizer_name is not None
           and not tokenizer_name.endswith('''Fast'''  )
       ):
        # `QAPipelineTests` fails for a few models when the slower tokenizer are used.
        # (The slower tokenizers were never used for pipeline tests before the pipeline testing rework)
        # TODO: check (and possibly fix) the `QAPipelineTests` with slower tokenizer
        return True

       return False







      def       __lowerCAmelCase					(      self      :						Union[str, Any] ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						Any ,					__UpperCAmelCase      :						List[str]=False  )    ->int:




       """simple docstring"""


       a		  =      super()._prepare_for_class(__UpperCAmelCase ,					__UpperCAmelCase ,					return_labels=__UpperCAmelCase  )

       if return_labels:
        if model_class.__name__ == "XLMForQuestionAnswering":
         a		  =      torch.zeros(
             self.model_tester.batch_size ,					dtype=torch.long ,					device=__UpperCAmelCase  )
         a		  =      torch.zeros(
             self.model_tester.batch_size ,					dtype=torch.long ,					device=__UpperCAmelCase  )

       return inputs_dict







      def       __lowerCAmelCase					(      self      :						int  )    ->Any:




       """simple docstring"""


       a		  =      XLMModelTester(self  )
       a		  =      ConfigTester(self ,					config_class=__UpperCAmelCase ,					emb_dim=37  )







      def       __lowerCAmelCase					(      self      :						Dict  )    ->Dict:




       """simple docstring"""


       self.config_tester.run_common_tests()







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




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_model(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						str  )    ->int:




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_lm_head(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						Tuple  )    ->Dict:




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_simple_qa(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						Any  )    ->Dict:




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_qa(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						Tuple  )    ->Tuple:




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_sequence_classif(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						List[Any]  )    ->Optional[Any]:




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_token_classif(*__UpperCAmelCase  )







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




       """simple docstring"""


       a		  =      self.model_tester.prepare_config_and_inputs()
       self.model_tester.create_and_check_xlm_for_multiple_choice(*__UpperCAmelCase  )







      def       __lowerCAmelCase					(      self      :						List[str] ,					__UpperCAmelCase      :						Any ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						str ,					__UpperCAmelCase      :						Optional[int] ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						int=False ,					__UpperCAmelCase      :						int=1  )    ->Dict:




       """simple docstring"""


       self.assertIsInstance(__UpperCAmelCase ,					__UpperCAmelCase  )
       self.assertListEqual(
           [isinstance(__UpperCAmelCase ,					__UpperCAmelCase  ) for iter_attentions in attentions] ,					[True] * len(__UpperCAmelCase  )  )
       self.assertEqual(len(__UpperCAmelCase  ) ,					(max_length - min_length) * num_beam_groups  )

       for idx, iter_attentions in enumerate(__UpperCAmelCase  ):
        # adds PAD dummy token
        a		  =      min_length + idx + 1
        a		  =      min_length + idx + 1

        a		  =      (
            batch_size * num_beam_groups,
            config.num_attention_heads,
            tgt_len,
            src_len,
        )
        # check attn size
        self.assertListEqual(
            [layer_attention.shape for layer_attention in iter_attentions] ,					[expected_shape] * len(__UpperCAmelCase  )  )







      def       __lowerCAmelCase					(      self      :						Optional[Any] ,					__UpperCAmelCase      :						Optional[Any] ,					__UpperCAmelCase      :						List[Any] ,					__UpperCAmelCase      :						Optional[int] ,					__UpperCAmelCase      :						List[str] ,					__UpperCAmelCase      :						Dict ,					__UpperCAmelCase      :						Optional[int]=False ,					__UpperCAmelCase      :						int=1  )    ->Dict:




       """simple docstring"""


       self.assertIsInstance(__UpperCAmelCase ,					__UpperCAmelCase  )
       self.assertListEqual(
           [isinstance(__UpperCAmelCase ,					__UpperCAmelCase  ) for iter_hidden_states in hidden_states] ,					[True] * len(__UpperCAmelCase  ) ,					)
       self.assertEqual(len(__UpperCAmelCase  ) ,					(max_length - min_length) * num_beam_groups  )

       for idx, iter_hidden_states in enumerate(__UpperCAmelCase  ):
        # adds PAD dummy token
        a		  =      min_length + idx + 1
        a		  =      (batch_size * num_beam_groups, seq_len, config.hidden_size)
        # check hidden size
        self.assertListEqual(
            [layer_hidden_states.shape for layer_hidden_states in iter_hidden_states] ,					[expected_shape] * len(__UpperCAmelCase  ) ,					)
       pass







      @slow
      def       __lowerCAmelCase					(      self      :						str  )    ->Tuple:




       """simple docstring"""


       for model_name in XLM_PRETRAINED_MODEL_ARCHIVE_LIST[:1]:
        a		  =      XLMModel.from_pretrained(__UpperCAmelCase  )
        self.assertIsNotNone(__UpperCAmelCase  )






@require_torch
class 		lowercase_					(	unittest.TestCase   ):






      '''simple docstring'''




      @slow
      def       __lowerCAmelCase					(      self      :						Optional[int]  )    ->int:




       """simple docstring"""


       a		  =      XLMWithLMHeadModel.from_pretrained('''xlm-mlm-en-2048'''  )
       model.to(__UpperCAmelCase  )
       a		  =      torch.tensor([[14, 447]] ,					dtype=torch.long ,					device=__UpperCAmelCase  )  # the president
       a		  =      [
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
           14,
           447,
       ]  # the president the president the president the president the president the president the president the president the president the president
       # TODO(PVP): this and other input_ids I tried for generation give pretty bad results. Not sure why. Model might just not be made for auto-regressive inference
       a		  =      model.generate(__UpperCAmelCase ,					do_sample=__UpperCAmelCase  )
       self.assertListEqual(output_ids[0].cpu().numpy().tolist() ,					__UpperCAmelCase  )










from math import factorial

UpperCAmelCase__				   =		{str(digit): factorial(digit) for digit in range(10)}
def 						_a    (      a :int )							->			int:
 if not isinstance(a	,							a ):
  raise TypeError('''Parameter number must be int''' )

 if number < 0:
  raise ValueError('''Parameter number must be greater than or equal to 0''' )

 # Converts number in string to iterate on its digits and adds its factorial.
 return sum(DIGIT_FACTORIAL[digit] for digit in str(a ) )
def 						_a    (      a :int = 60	,							a :int = 1_000_000 )							->			int:

 if not isinstance(a	,							a ) or not isinstance(a	,							a ):
  raise TypeError('''Parameters chain_length and number_limit must be int''' )

 if chain_length <= 0 or number_limit <= 0:
  raise ValueError(
      '''Parameters chain_length and number_limit must be greater than 0''' )

 # the counter for the chains with the exact desired length
 a		  =      0
 # the cached sizes of the previous chains
 a		  =      {}

 for start_chain_element in range(1	,							a ):
  # The temporary set will contain the elements of the chain
  a		  =      set()
  a		  =      0

  # Stop computing the chain when you find a cached size, a repeating item or the
  # length is greater then the desired one.
  a		  =      start_chain_element
  while (
      chain_element not in chain_sets_lengths
      and chain_element not in chain_set
      and chain_set_length <= chain_length
  ):
   chain_set.add(a )
   chain_set_length += 1
   a		  =      digit_factorial_sum(a )

  if chain_element in chain_sets_lengths:
   chain_set_length += chain_sets_lengths[chain_element]

  a		  =      chain_set_length

  # If chain contains the exact amount of elements increase the counter
  if chain_set_length == chain_length:
   chains_counter += 1

 return chains_counter


if __name__ == "__main__":
     import doctest

     doctest.testmod()
     print(f"""{solution()}""")


