text: """
  Conversion utilities for mapping memory, disk, core declarations from strings to numbers and vice versa.
  Also contains general conversion functions
  """
  
  import math
  from typing import Optional, SupportsInt, Tuple
  
  # See https://en.wikipedia.org/wiki/Binary_prefix
  BINARY_PREFIXES = ['ki', 'mi', 'gi', 'ti', 'pi', 'ei', 'kib', 'mib', 'gib', 'tib', 'pib', 'eib']
  DECIMAL_PREFIXES = ['b', 'k', 'm', 'g', 't', 'p', 'e', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb']
  VALID_PREFIXES = BINARY_PREFIXES + DECIMAL_PREFIXES
  
  
  def bytes_in_unit(unit: str = 'B') -> int:
      num_bytes = 1
      if unit.lower() in ['ki', 'kib']:
          num_bytes = 1 << 10
      if unit.lower() in ['mi', 'mib']:
          num_bytes = 1 << 20
      if unit.lower() in ['gi', 'gib']:
          num_bytes = 1 << 30
      if unit.lower() in ['ti', 'tib']:
          num_bytes = 1 << 40
      if unit.lower() in ['pi', 'pib']:
          num_bytes = 1 << 50
      if unit.lower() in ['ei', 'eib']:
          num_bytes = 1 << 60
  
      if unit.lower() in ['k', 'kb']:
          num_bytes = 1000
      if unit.lower() in ['m', 'mb']:
          num_bytes = 1000 ** 2
      if unit.lower() in ['g', 'gb']:
          num_bytes = 1000 ** 3
      if unit.lower() in ['t', 'tb']:
          num_bytes = 1000 ** 4
      if unit.lower() in ['p', 'pb']:
          num_bytes = 1000 ** 5
      if unit.lower() in ['e', 'eb']:
          num_bytes = 1000 ** 6
      return num_bytes
  
  
  def convert_units(num: float,
                    src_unit: str,
                    dst_unit: str = 'B') -> float:
      """Returns a float representing the converted input in dst_units."""
      assert src_unit.lower() in VALID_PREFIXES, f"{src_unit} not a valid unit, valid units are {VALID_PREFIXES}."
      assert dst_unit.lower() in VALID_PREFIXES, f"{dst_unit} not a valid unit, valid units are {VALID_PREFIXES}."
      return (num * bytes_in_unit(src_unit)) / bytes_in_unit(dst_unit)
  
  
  def parse_memory_string(string: str) -> Tuple[float, str]:
      """
      Given a string representation of some memory (i.e. '1024 Mib'), return the
      number and unit.
      """
      for i, character in enumerate(string):
          # find the first character of the unit
          if character not in '0123456789.-_ ':
              units = string[i:].strip()
              assert units.lower() in VALID_PREFIXES, f"{units} not a valid unit, valid units are {VALID_PREFIXES}."
              return float(string[:i]), units
      return float(string), 'b'
  
  
  def human2bytes(string: str) -> int:
      """
      Given a string representation of some memory (i.e. '1024 Mib'), return the
      integer number of bytes.
      """
      value, unit = parse_memory_string(string)
      return int(convert_units(value, src_unit=unit, dst_unit='b'))
  
  
  def bytes2human(n: SupportsInt) -> str:
      """Return a binary value as a human readable string with units."""
      n = int(n)
      if n < 0:
          raise ValueError("n < 0")
      elif n < 1:
          return '0 b'
  
      power_level = math.floor(math.log(n, 1024))
      units = ('b', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei')
  
      unit = units[power_level if power_level < len(units) else -1]
      value = convert_units(n, "b", unit)
      return f'{value:.1f} {unit}'
  
  #General Conversions
  
  def hms_duration_to_seconds(hms: str) -> float: 
      """
      Parses a given time string in hours:minutes:seconds, 
      returns an equivalent total seconds value
      """
      vals_to_convert = hms.split(':')
      seconds = 0.0
      
      for val in vals_to_convert:
          if(float(val) < 0): 
              raise ValueError("Invalid Time, negative value")
  
      if(len(vals_to_convert) != 3):
          raise ValueError("Invalid amount of fields, function takes input in 'hh:mm:ss'")
  
      seconds += float(vals_to_convert[0]) * 60 * 60 
      seconds += float(vals_to_convert[1]) * 60 
      seconds += float(vals_to_convert[2]) 
  
      return seconds