import copy
import threading
from typing import Generic, TypeVar, Optional, Callable


_T = TypeVar("_T")
_U = TypeVar("_U")


class Atomic(Generic[_T]):
    def __init__(self, initial_value: _T):
        self._lock = threading.Lock()
        self._v = initial_value

    def get(self) -> _T:
        return copy.deepcopy(self._v)

    def set(self, v: _T) -> None:
        with self._lock:
            self._v = copy.deepcopy(v)

    def apply(self, f: Callable[[_T], _U]) -> _U:
        with self._lock:
            return f(self._v)


class NullableAtomic(Generic[_T]):
    def __init__(self, initial_value: Optional[_T] = None):
        self._lock = threading.Lock()
        self._v = initial_value

    def pop(self) -> Optional[_T]:
        with self._lock:
            temp =  self._v
            self._v = None
            return temp
    
    def get_copy(self) -> Optional[_T]:
        with self._lock:
            return copy.deepcopy(self._v)

    def set(self, v: _T) -> None:
        with self._lock:
            self._v = copy.deepcopy(v)

    def clear(self) -> None:
        with self._lock:
            self._v = None

    def apply(self, f: Callable[[Optional[_T]], _U]) -> _U:
        with self._lock:
            return f(self._v)