


"""simple docstring"""







from __future__ import annotations

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

a__    :							Tuple            =					TypeVar('''T''')
a__    :							int            =					TypeVar('''U''')




class 			UpperCamelCase_     (			Generic[T, U]):

	"""simple docstring"""





	def __init__(			self      :    List[Any]      ,      UpperCAmelCase__      :    T | None      ,      UpperCAmelCase__      :    U | None       )	->     Dict:
						__SCREAMING_SNAKE_CASE						       =    key
						__SCREAMING_SNAKE_CASE						       =    val
						__SCREAMING_SNAKE_CASE						       =    None
						__SCREAMING_SNAKE_CASE						       =    None




	def __repr__(			self      :    Optional[Any]       )	->     str:
						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      :    str       )	->     None:
						__SCREAMING_SNAKE_CASE						       =    DoubleLinkedListNode(UpperCAmelCase__      ,      UpperCAmelCase__       )
						__SCREAMING_SNAKE_CASE						       =    DoubleLinkedListNode(UpperCAmelCase__      ,      UpperCAmelCase__       )
						__SCREAMING_SNAKE_CASE       ,  __SCREAMING_SNAKE_CASE						       =    self.rear, self.head




	def __repr__(			self      :    Optional[Any]       )	->     str:
						__SCREAMING_SNAKE_CASE						       =    ["DoubleLinkedList"]
						__SCREAMING_SNAKE_CASE						       =    self.head
						while node.next is not None:
											rep.append(str(UpperCAmelCase__       )       )
											__SCREAMING_SNAKE_CASE						       =    node.next
						rep.append(str(self.rear       )       )
						return ",\n    ".join(UpperCAmelCase__       )




	def   UpperCAmelCase_				(			self      :    Optional[int]      ,      UpperCAmelCase__      :    DoubleLinkedListNode[T, U]       )	->     None:
						__SCREAMING_SNAKE_CASE						       =    self.rear.prev

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

						__SCREAMING_SNAKE_CASE						       =    node
						__SCREAMING_SNAKE_CASE						       =    previous
						__SCREAMING_SNAKE_CASE						       =    node
						__SCREAMING_SNAKE_CASE						       =    self.rear




	def   UpperCAmelCase_				(			self      :    Tuple      ,      UpperCAmelCase__      :    DoubleLinkedListNode[T, U]       )	->     DoubleLinkedListNode[T, U] | None:

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

						__SCREAMING_SNAKE_CASE						       =    node.next
						__SCREAMING_SNAKE_CASE						       =    node.prev
						__SCREAMING_SNAKE_CASE						       =    None
						__SCREAMING_SNAKE_CASE						       =    None
						return node




class 			UpperCamelCase_     (			Generic[T, U]):

	"""simple docstring"""





	snake_case__    :  dict[Callable[[T], U], LRUCache[T, U]]    =      {}




	def __init__(			self      :    Any      ,      UpperCAmelCase__      :    int       )	->     Union[str, Any]:
						__SCREAMING_SNAKE_CASE						       =    DoubleLinkedList()
						__SCREAMING_SNAKE_CASE						       =    capacity
						__SCREAMING_SNAKE_CASE						       =    0
						__SCREAMING_SNAKE_CASE						       =    0
						__SCREAMING_SNAKE_CASE						       =    0
						__SCREAMING_SNAKE_CASE						       =    {}




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




	def __contains__(			self      :    Tuple      ,      UpperCAmelCase__      :    T       )	->     bool:
						return key in self.cache




	def   UpperCAmelCase_				(			self      :    List[str]      ,      UpperCAmelCase__      :    T       )	->     U | None:
						# Note: pythonic interface would throw KeyError rather than return None

						if key in self.cache:
											self.hits += 1
											__SCREAMING_SNAKE_CASE						       =    self.cache[key]
											__SCREAMING_SNAKE_CASE						       =    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   UpperCAmelCase_				(			self      :    Tuple      ,      UpperCAmelCase__      :    T      ,      UpperCAmelCase__      :    U       )	->     None:

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

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




	@classmethod
	def   UpperCAmelCase_				(			cls      :    Union[str, Any]      ,      UpperCAmelCase__      :    int = 1_2_8       )	->     Callable[[Callable[[T], U]], Callable[..., U]]:

						def cache_decorator_inner(UpperCAmelCase__      :    Callable[[T], U]       ) -> Callable[..., U]:
											def cache_decorator_wrapper(*UpperCAmelCase__      :    T       ) -> U:
																if func not in cls.decorator_function_to_instance_map:
																					__SCREAMING_SNAKE_CASE						       =    LRUCache(UpperCAmelCase__       )

																__SCREAMING_SNAKE_CASE						       =    cls.decorator_function_to_instance_map[func].get(args[0]       )
																if result is None:
																					__SCREAMING_SNAKE_CASE						       =    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"""







from __future__ import annotations

import requests




def        UpperCAmelCase__	(lowerCAmelCase_       ):




					'''simple docstring'''


					__SCREAMING_SNAKE_CASE						       =    f"""https://hacker-news.firebaseio.com/v0/item/{story_id}.json?print=pretty"""
					return requests.get(lowerCAmelCase_       ).json()




def        UpperCAmelCase__	(lowerCAmelCase_ = 10       ):




					'''simple docstring'''


					__SCREAMING_SNAKE_CASE						       =    "https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty"
					__SCREAMING_SNAKE_CASE						       =    requests.get(lowerCAmelCase_       ).json()[:max_stories]
					return [get_hackernews_story(lowerCAmelCase_       ) for story_id in story_ids]




def        UpperCAmelCase__	(lowerCAmelCase_ = 10       ):




					'''simple docstring'''


					__SCREAMING_SNAKE_CASE						       =    hackernews_top_stories(lowerCAmelCase_       )
					return "\n".join("* [{title}]({url})".format(**lowerCAmelCase_       ) for story in stories       )


if __name__ == "__main__":
		print(hackernews_top_stories_as_markdown())
