content: #
  # Copyright 2023 EA Authors.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    https://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  from sys import stderr
  
  def time() -> float:
      return _C.hs_time() / 1e9
  
  def time_ns() -> int:
      return _C.hs_time()
  
  def monotonic() -> float:
      return _C.hs_time_monotonic() / 1e9
  
  def monotonic_ns() -> int:
      return _C.hs_time_monotonic()
  
  def perf_counter() -> float:
      return _C.hs_time_highres() / 1e9
  
  def perf_counter_ns() -> int:
      return _C.hs_time_highres()
  
  def sleep(secs: float):
      if secs < 0:
          raise ValueError("sleep length must be non-negative")
      _C.hs_sleep(secs)
  
  class TimeInterval:
      """
      Utility class for timing Seq code
      """
  
      start: int
      msg: str
  
      def __init__(self):
          self.start = _C.hs_time()
          self.msg = ""
  
      def __enter__(self):
          self.start = _C.hs_time()
  
      def __exit__(self):
          print(self.report(self.msg), file=stderr)
  
      def report(self, msg="", memory=False) -> str:
          msg = f"{'Block' if not self.msg else self.msg} took {self.elapsed()}s"
          # if memory:
          # msg = f'{msg} ({_C.memory()} MB)'
          return msg
  
      def elapsed(self) -> float:
          return float(_C.hs_time() - self.start) / 1e9
  
      def tick(self, msg, memory=False):
          ret = self.report(msg)
          self.start = _C.hs_time()
  
  def timing(msg: str = "") -> TimeInterval:
      """
      Example usage:
  
      .. code-block:: python
  
          from time import timing
          with timing('foo function'):
              foo()  # prints runtime of foo
      """
      return TimeInterval(0, msg)
  
  @tuple
  class struct_time:
      _year: i16
      _yday: i16
      _sec: i8
      _min: i8
      _hour: i8
      _mday: i8
      _mon: i8
      _wday: i8
      _isdst: i8
  
      # (sunday=0) --> (monday=0)
      def _wday_adjust_monday_start(wday: int) -> int:
          x = wday - 1
          if x < 0:
              x = 6
          return x
  
      # (monday=0) --> (sunday=0)
      def _wday_adjust_sunday_start(wday: int) -> int:
          x = wday + 1
          if x > 6:
              x = 0
          return x
  
      def __new__(
          year: int,
          mon: int,
          mday: int,
          hour: int,
          min: int,
          sec: int,
          wday: int,
          yday: int,
          isdst: int,
      ) -> struct_time:
          return struct_time(
              i16(year - 1900),
              i16(yday - 1),
              i8(sec),
              i8(min),
              i8(hour),
              i8(mday),
              i8(mon - 1),
              i8(struct_time._wday_adjust_sunday_start(wday)),
              i8(isdst),
          )
  
      @property
      def tm_year(self) -> int:
          return int(self._year) + 1900
  
      @property
      def tm_yday(self) -> int:
          return int(self._yday) + 1
  
      @property
      def tm_sec(self) -> int:
          return int(self._sec)
  
      @property
      def tm_min(self) -> int:
          return int(self._min)
  
      @property
      def tm_hour(self) -> int:
          return int(self._hour)
  
      @property
      def tm_mday(self) -> int:
          return int(self._mday)
  
      @property
      def tm_mon(self) -> int:
          return int(self._mon) + 1
  
      @property
      def tm_wday(self) -> int:
          return struct_time._wday_adjust_monday_start(int(self._wday))
  
      @property
      def tm_isdst(self) -> int:
          return int(self._isdst)
  
  
  def localtime(secs: int = -1) -> struct_time:
      tm = struct_time()
      worked = _C.hs_localtime(secs, __ptr__(tm).as_byte())
      if not worked:
          raise OSError("localtime failed")
      return tm
  
  
  def gmtime(secs: int = -1) -> struct_time:
      tm = struct_time()
      worked = _C.hs_gmtime(secs, __ptr__(tm).as_byte())
      if not worked:
          raise OSError("localtime failed")
      return tm
  
  
  def mktime(t) -> int:
      if isinstance(t, struct_time):
          return _C.hs_mktime(__ptr__(t).as_byte())
      else:
          tm = struct_time(*t)
          return _C.hs_mktime(__ptr__(tm).as_byte())
  
  # pytime.h funcs
  
  _ROUND_HALF_EVEN = 0
  _ROUND_CEILING = 1
  _ROUND_FLOOR = 2
  _ROUND_UP = 3
  
  _MIN = 0x8000000000000000
  _MAX = 0x7FFFFFFFFFFFFFFF
  
  def _overflow():
      raise OverflowError("timestamp too large")
  
  def _add(t1: int, t2: int) -> int:
      if t2 > 0 and t1 > _MAX - t2:
          return _MAX
      elif t2 < 0 and t1 < _MIN - t2:
          return _MIN
      else:
          return t1 + t2
  
  def _mul_check_overflow(a: int, b: int) -> bool:
      if b != 0:
          # assert b > 0
          return (a < _MIN // b) or (_MAX // b < a)
      else:
          return False
  
  def _mul(t: int, k: int) -> int:
      # assert k >= 0
      if _mul_check_overflow(t, k):
          return _MAX if t >= 0 else _MIN
      else:
          return t * k
  
  def _muldiv(ticks: int, mul: int, div: int) -> int:
      intpart = ticks / div
      ticks %= div
      remaining = _mul(ticks, mul) // div
      return _add(_mul(intpart, mul), remaining)
  
  def _round_half_even(x: float) -> float:
      from math import fabs
  
      rounded = x.__round__()
      if fabs(x - rounded) == 0.5:
          rounded = 2.0 * (x / 2.0).__round__()
      return rounded
  
  def _round(x: float, mode: int) -> float:
      d = x
      if mode == _ROUND_HALF_EVEN:
          d = _round_half_even(d)
      elif mode == _ROUND_CEILING:
          d = d.__ceil__()
      elif mode == _ROUND_FLOOR:
          d = d.__floor__()
      elif mode == _ROUND_UP:
          d = d.__ceil__() if d >= 0 else d.__floor__()
      return d
  
  def _double_to_denominator(d: float, idenominator: int, mode: int) -> Tuple[int, int]:
      from math import modf
  
      denominator = float(idenominator)
      floatpart, intpart = modf(d)
  
      floatpart *= denominator
      floatpart = _round(floatpart, mode)
      if floatpart >= denominator:
          floatpart -= denominator
          intpart += 1.0
      elif floatpart < 0.0:
          floatpart += denominator
          intpart -= 1.0
      # assert 0.0 <= floatpart < denominator
  
      if intpart < _MIN or intpart > _MAX:
          _overflow()
  
      sec = int(intpart)
      numerator = int(floatpart)
      # assert 0 <= numerator < idenominator
      return sec, numerator
  
  def _time_to_timespec(t: float, mode: int) -> Tuple[int, int]:
      return _double_to_denominator(t, 1000000000, mode)
  
  def _time_to_timeval(t: float, mode: int) -> Tuple[int, int]:
      return _double_to_denominator(t, 1000000, mode)
  