



'''simple docstring'''





from __future__ import annotations

from collections.abc import Callable
from typing import Generic, TypeVar

__a          =		TypeVar("T")
__a          =		TypeVar("U")







class     UpperCAmelCase_       (       Generic[T, U]	):
    """simple docstring"""







    def __init__(    self					:     Any ,     snake_case_					:     T | None ,     snake_case_					:     U | None						):
          snake_case__  :    Dict    						=      key
          snake_case__  :    Tuple    						=      val
          snake_case__  :    DoubleLinkedListNode[T, U] | None    						=      None
          snake_case__  :    DoubleLinkedListNode[T, U] | None    						=      None




    def __repr__(    self					:     int						):
          return (
              f"Node: key: {self.key}, val: {self.val}, "
              f"has next: {bool(self.next						)}, has prev: {bool(self.prev						)}"
          )







class     UpperCAmelCase_       (       Generic[T, U]	):
    """simple docstring"""







    def __init__(    self					:     Tuple						):
          snake_case__  :    DoubleLinkedListNode[T, U]    						=      DoubleLinkedListNode(snake_case_ ,     snake_case_						)
          snake_case__  :    DoubleLinkedListNode[T, U]    						=      DoubleLinkedListNode(snake_case_ ,     snake_case_						)
          snake_case__						,				snake_case__  :    Tuple    						=      self.rear, self.head




    def __repr__(    self					:     List[str]						):
          snake_case__  :    Dict    						=      ["""DoubleLinkedList"""]
          snake_case__  :    Any    						=      self.head
          while node.next is not None:
                rep.append(str(snake_case_						)						)
                snake_case__  :    List[str]    						=      node.next
          rep.append(str(self.rear						)						)
          return ",\n    ".join(snake_case_						)




    def lowerCamelCase       (    self					:     Optional[int] ,     snake_case_					:     DoubleLinkedListNode[T, U]						):
          snake_case__  :    List[str]    						=      self.rear.prev

          # All nodes other than self.head are guaranteed to have non-None previous
          assert previous is not None

          snake_case__  :    Tuple    						=      node
          snake_case__  :    Optional[Any]    						=      previous
          snake_case__  :    int    						=      node
          snake_case__  :    List[str]    						=      self.rear




    def lowerCamelCase       (    self					:     List[str] ,     snake_case_					:     DoubleLinkedListNode[T, U]						):

          if node.prev is None or node.next is None:
                return None

          snake_case__  :    Optional[int]    						=      node.next
          snake_case__  :    List[str]    						=      node.prev
          snake_case__  :    Optional[int]    						=      None
          snake_case__  :    Optional[Any]    						=      None
          return node







class     UpperCAmelCase_       (       Generic[T, U]	):
    """simple docstring"""







    lowercase					=		{}




    def __init__(    self					:     str ,     snake_case_					:     int						):
          snake_case__  :    DoubleLinkedList[T, U]    						=      DoubleLinkedList()
          snake_case__  :    Tuple    						=      capacity
          snake_case__  :    Dict    						=      0
          snake_case__  :    Dict    						=      0
          snake_case__  :    List[Any]    						=      0
          snake_case__  :    dict[T, DoubleLinkedListNode[T, U]]    						=      {}




    def __repr__(    self					:     Tuple						):
          return (
              f"CacheInfo(hits={self.hits}, misses={self.miss}, "
              f"capacity={self.capacity}, current size={self.num_keys})"
          )




    def __contains__(    self					:     Optional[int] ,     snake_case_					:     T						):
          return key in self.cache




    def lowerCamelCase       (    self					:     Dict ,     snake_case_					:     T						):
          # Note: pythonic interface would throw KeyError rather than return None

          if key in self.cache:
                self.hits += 1
                snake_case__  :    DoubleLinkedListNode[T, U]    						=      self.cache[key]
                snake_case__  :    List[str]    						=      self.list.remove(self.cache[key]						)
                assert node == value_node

                # node is guaranteed not None because it is in self.cache
                assert node is not None
                self.list.add(snake_case_						)
                return node.val
          self.miss += 1
          return None




    def lowerCamelCase       (    self					:     List[str] ,     snake_case_					:     T ,     snake_case_					:     U						):

          if key not in self.cache:
                if self.num_keys >= self.capacity:
                      # delete first node (oldest) when over capacity
                      snake_case__  :    Tuple    						=      self.list.head.next

                      # guaranteed to have a non-None first node when num_keys > 0
                      # explain to type checker via assertions
                      assert first_node is not None
                      assert first_node.key is not None
                      assert (
                          self.list.remove(snake_case_						) is not None
                      )  # node guaranteed to be in list assert node.key is not None

                      del self.cache[first_node.key]
                      self.num_keys -= 1
                snake_case__  :    Optional[int]    						=      DoubleLinkedListNode(snake_case_ ,     snake_case_						)
                self.list.add(self.cache[key]						)
                self.num_keys += 1

          else:
                # bump node to the end of the list, update value
                snake_case__  :    List[Any]    						=      self.list.remove(self.cache[key]						)
                assert node is not None  # node guaranteed to be in list
                snake_case__  :    int    						=      value
                self.list.add(snake_case_						)




    @classmethod
    def lowerCamelCase       (    cls					:     Dict ,     snake_case_					:     int = 128						):

          def cache_decorator_inner(snake_case_					:     Callable[[T], U]						) -> Callable[..., U]:
                def cache_decorator_wrapper(*snake_case_					:     T						) -> U:
                      if func not in cls.decorator_function_to_instance_map:
                            snake_case__  :    Union[str, Any]    						=      LRUCache(snake_case_						)

                      snake_case__  :    str    						=      cls.decorator_function_to_instance_map[func].get(args[0]						)
                      if result is None:
                            snake_case__  :    Any    						=      func(*snake_case_						)
                            cls.decorator_function_to_instance_map[func].put(args[0] ,     snake_case_						)
                      return result

                def cache_info() -> LRUCache[T, U]:
                      return cls.decorator_function_to_instance_map[func]

                setattr(snake_case_ ,     """cache_info""" ,     snake_case_						)  # noqa: B010

                return cache_decorator_wrapper

          return cache_decorator_inner


if __name__ == "__main__":
       import doctest

       doctest.testmod()








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()}""")


