





from __future__ import annotations

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

__UpperCAmelCase							=   TypeVar('T')
__UpperCAmelCase							=   TypeVar('U')

class 		__a				(						Generic[T, U]   ):



       def __init__(  self						: Optional[int]       ,      UpperCAmelCase						: int       ,      UpperCAmelCase						: Any      ):
           lowerCAmelCase_       :						List[Any]							= key
           lowerCAmelCase_       :						Union[str, Any]							= val
           lowerCAmelCase_       :						DoubleLinkedListNode[T, U] | None							= None
           lowerCAmelCase_       :						DoubleLinkedListNode[T, U] | None							= None



       def __repr__(  self						: Any      ):
           return (
               F'Node: key: {self.key}, val: {self.val}, '
               F'has next: {bool(self.next      )}, has prev: {bool(self.prev      )}'
           )






class 		__a				(						Generic[T, U]   ):



       def __init__(  self						: str      ):
           lowerCAmelCase_       :						DoubleLinkedListNode[T, U]							= DoubleLinkedListNode(UpperCamelCase_       ,      UpperCamelCase_      )
           lowerCAmelCase_       :						DoubleLinkedListNode[T, U]							= DoubleLinkedListNode(UpperCamelCase_       ,      UpperCamelCase_      )
           lowerCAmelCase_       :						Optional[Any]							= self.rear, self.head



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



       def 			A				(  self						: int       ,      UpperCAmelCase						: List[Any]      ):
           lowerCAmelCase_       :						int							= self.rear.prev

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

           lowerCAmelCase_       :						List[str]							= node
           lowerCAmelCase_       :						int							= previous
           lowerCAmelCase_       :						List[str]							= node
           lowerCAmelCase_       :						List[Any]							= self.rear



       def 			A				(  self						: List[Any]       ,      UpperCAmelCase						: List[Any]      ):

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

           lowerCAmelCase_       :						Any							= node.next
           lowerCAmelCase_       :						Dict							= node.prev
           lowerCAmelCase_       :						Optional[int]							= None
           lowerCAmelCase_       :						Optional[Any]							= None
           return node






class 		__a				(						Generic[T, U]   ):
       __snake_case    : dict[Callable[[T], U], LRUCache[T, U]]   =		{}



       def __init__(  self						: Dict       ,      UpperCAmelCase						: int      ):
           lowerCAmelCase_       :						DoubleLinkedList[T, U]							= DoubleLinkedList()
           lowerCAmelCase_       :						int							= capacity
           lowerCAmelCase_       :						Optional[int]							= 0
           lowerCAmelCase_       :						Union[str, Any]							= 0
           lowerCAmelCase_       :						Optional[Any]							= 0
           lowerCAmelCase_       :						dict[T, DoubleLinkedListNode[T, U]]							= {}



       def __repr__(  self						: List[str]      ):
           return (
               F'CacheInfo(hits={self.hits}, misses={self.miss}, '
               F'capacity={self.capacity}, current size={self.num_keys})'
           )



       def __contains__(  self						: str       ,      UpperCAmelCase						: Dict      ):
           return key in self.cache



       def 			A				(  self						: int       ,      UpperCAmelCase						: Union[str, Any]      ):
           # Note: pythonic interface would throw KeyError rather than return None

           if key in self.cache:
               self.hits += 1
               lowerCAmelCase_       :						DoubleLinkedListNode[T, U]							= self.cache[key]
               lowerCAmelCase_       :						Union[str, Any]							= 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(UpperCamelCase_      )
               return node.val
           self.miss += 1
           return None



       def 			A				(  self						: Optional[int]       ,      UpperCAmelCase						: Any       ,      UpperCAmelCase						: int      ):

           if key not in self.cache:
               if self.num_keys >= self.capacity:
                   # delete first node (oldest) when over capacity
                   lowerCAmelCase_       :						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(UpperCamelCase_      ) 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
               lowerCAmelCase_       :						Tuple							= DoubleLinkedListNode(UpperCamelCase_       ,      UpperCamelCase_      )
               self.list.add(self.cache[key]      )
               self.num_keys += 1

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



       @classmethod
       def 			A				(  cls						: List[Any]       ,      UpperCAmelCase						: int = 1_28      ):

           def cache_decorator_inner(UpperCAmelCase						: Optional[Any]      ) -> Callable[..., U]:
               def cache_decorator_wrapper(*UpperCAmelCase						: Tuple      ) -> U:
                   if func not in cls.decorator_function_to_instance_map:
                       lowerCAmelCase_       :						List[str]							= LRUCache(UpperCamelCase_      )

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

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

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

               return cache_decorator_wrapper

           return cache_decorator_inner


if __name__ == "__main__":
  import doctest

  doctest.testmod()




"""simple docstring"""



UpperCamelCase__          =	{
    'meter': 'm',
    'kilometer': 'km',
    'megametre': 'Mm',
    'gigametre': 'Gm',
    'terametre': 'Tm',
    'petametre': 'Pm',
    'exametre': 'Em',
    'zettametre': 'Zm',
    'yottametre': 'Ym',
}
# Exponent of the factor(meter)
UpperCamelCase__          =	{
    'm': 0,
    'km': 3,
    'Mm': 6,
    'Gm': 9,
    'Tm': 12,
    'Pm': 15,
    'Em': 18,
    'Zm': 21,
    'Ym': 24,
}
def 					lowerCamelCase   (     _snake_case    ,_snake_case    ,_snake_case ):
    UpperCAmelCase__	:      int     							=					from_type.lower().strip('s' )
    UpperCAmelCase__	:      List[Any]     							=					to_type.lower().strip('s' )

    UpperCAmelCase__	:      Optional[int]     							=					UNIT_SYMBOL.get(_snake_case    ,_snake_case )
    UpperCAmelCase__	:      Optional[Any]     							=					UNIT_SYMBOL.get(_snake_case    ,_snake_case )

    if from_sanitized not in METRIC_CONVERSION:
        UpperCAmelCase__	:      Optional[int]     							=					(
            F'''Invalid \'from_type\' value: {from_type!r}.\n'''
            F'''Conversion abbreviations are: {', '.join(_snake_case )}'''
        )
        raise ValueError(_snake_case )
    if to_sanitized not in METRIC_CONVERSION:
        UpperCAmelCase__	:      int     							=					(
            F'''Invalid \'to_type\' value: {to_type!r}.\n'''
            F'''Conversion abbreviations are: {', '.join(_snake_case )}'''
        )
        raise ValueError(_snake_case )
    UpperCAmelCase__	:      str     							=					METRIC_CONVERSION[from_sanitized]
    UpperCAmelCase__	:      Optional[int]     							=					METRIC_CONVERSION[to_sanitized]
    UpperCAmelCase__	:      int     							=					1

    if from_exponent > to_exponent:
        UpperCAmelCase__	:      List[str]     							=					from_exponent - to_exponent
    else:
        UpperCAmelCase__	:      int     							=					-(to_exponent - from_exponent)

    return value * pow(10    ,_snake_case )


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

    testmod()

