docs: def lru_cache(maxsize=128, typed=False):
      """
      Least-recently-used cache decorator, which is a backport of the same
      function in Python >= 3.2.
  
      If *maxsize* is set to None, the LRU features are disabled and the cache
      can grow without bound.
  
      If *typed* is True, arguments of different types will be cached separately.
      For example, f(3.0) and f(3) will be treated as distinct calls with
      distinct results.
  
      Arguments to the cached function must be hashable.
  
      View the cache statistics named tuple (hits, misses, maxsize, currsize)
      with f.cache_info().  Clear the cache and statistics with f.cache_clear().
      Access the underlying function with f.__wrapped__.
  
      See:  http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
      """
  
      # Users should only access the lru_cache through its public API:
      #       cache_info, cache_clear, and f.__wrapped__
      # The internals of the lru_cache are encapsulated for thread safety and
      # to allow the implementation to change (including a possible C version).
  
      # Early detection of an erroneous call to @lru_cache without any arguments
      # resulting in the inner function being passed to maxsize instead of an
      # integer or None.
      if maxsize is not None and not isinstance(maxsize, int):
          raise TypeError('Expected maxsize to be an integer or None')
  
      # Constants shared by all lru cache instances:
      sentinel = object()          # unique object used to signal cache misses
      make_key = _make_key         # build a key from the function arguments
      PREV, NEXT, KEY, RESULT = 0, 1, 2, 3   # names for the link fields
  
      def decorating_function(user_function):
          cache = {}
          hits = [0]
          misses = [0]
          full = [False]
          cache_get = cache.get    # bound method to lookup a key or return None
          lock = RLock()           # because linkedlist updates aren't threadsafe
          root = []                # root of the circular doubly linked list
          root[:] = [root, root, None, None]     # initialize by pointing to self
          r = [root]
  
          if maxsize == 0:
  
              def wrapper(*args, **kwds):
                  # No caching -- just a statistics update after a successful call
                  result = user_function(*args, **kwds)
                  misses[0] += 1
                  return result
  
          elif maxsize is None:
  
              def wrapper(*args, **kwds):
                  # Simple caching without ordering or size limit
                  key = make_key(args, kwds, typed)
                  result = cache_get(key, sentinel)
                  if result is not sentinel:
                      hits[0] += 1
                      return result
                  result = user_function(*args, **kwds)
                  cache[key] = result
                  misses[0] += 1
                  return result
  
          else:
              def wrapper(*args, **kwds):
                  # Size limited caching that tracks accesses by recency
                  key = make_key(args, kwds, typed)
                  with lock:
                      link = cache_get(key)
                      if link is not None:
                          # Move the link to the front of the circular queue
                          link_prev, link_next, _key, result = link
                          link_prev[NEXT] = link_next
                          link_next[PREV] = link_prev
                          last = r[0][PREV]
                          last[NEXT] = r[0][PREV] = link
                          link[PREV] = last
                          link[NEXT] = r[0]
                          hits[0] += 1
                          return result
                  result = user_function(*args, **kwds)
                  with lock:
                      if key in cache:
                          # Getting here means that this same key was added to the
                          # cache while the lock was released.  Since the link
                          # update is already done, we need only return the
                          # computed result and update the count of misses.
                          pass
                      elif full[0]:
                          # Use the old root to store the new key and result.
                          oldroot = r[0]
                          oldroot[KEY] = key
                          oldroot[RESULT] = result
                          # Empty the oldest link and make it the new root.
                          # Keep a reference to the old key and old result to
                          # prevent their ref counts from going to zero during the
                          # update. That will prevent potentially arbitrary object
                          # clean-up code (i.e. __del__) from running while we're
                          # still adjusting the links.
                          r[0] = oldroot[NEXT]
                          oldkey = r[0][KEY]
                          oldresult = r[0][RESULT]
                          r[0][KEY] = r[0][RESULT] = None
                          # Now update the cache dictionary.
                          del cache[oldkey]
                          # Save the potentially reentrant cache[key] assignment
                          # for last, after the root and links have been put in
                          # a consistent state.
                          cache[key] = oldroot
                      else:
                          # Put result in a new link at the front of the queue.
                          last = r[0][PREV]
                          link = [last, r[0], key, result]
                          last[NEXT] = r[0][PREV] = cache[key] = link
                          full[0] = (len(cache) >= maxsize)
                      misses[0] += 1
                  return result
  
          def cache_info():
              """Report cache statistics"""
              with lock:
                  return _CacheInfo(hits[0], misses[0], maxsize, len(cache))
  
          def cache_clear():
              """Clear the cache and cache statistics"""
              with lock:
                  cache.clear()
                  root[:] = [root, root, None, None]
                  r[0] = root
                  hits[0] = 0
                  misses[0] = 0
                  full[0] = False
  
          wrapper.cache_info = cache_info
          wrapper.cache_clear = cache_clear
          return update_wrapper(wrapper, user_function)
  
      return decorating_function