docs: def deci2sexa(deci, pre=3, trunc=False, lower=None, upper=None,
                b=False, upper_trim=False):
      """Returns the sexagesimal representation of a decimal number.
  
      Parameters
      ----------
      deci : float
          Decimal number to be converted into sexagesimal. If `lower` and
          `upper` are given then the number is normalized to the given
          range before converting to sexagesimal.
      pre : int
          The last part of the sexagesimal number is rounded to these
          many decimal places. This can be negative. Default is 3.
      trunc : bool
          If True then the last part of the sexagesimal number is
          truncated and not rounded to `pre` decimal places. Default is
          False.
      lower : int
          Lower bound of range to which number is to be normalized.
      upper : int
          Upper bound of range to which number is to be normalized.
      b : bool
          Affects type of normalization. See docstring for `normalize`.
      upper_trim : bool
          If `lower` and `upper` are given and this is True, then if the
          first part of the sexagesimal number is equal to `upper`, it is
          replaced with `lower` (value used is int(lower)). This converts numbers
          such as "24 00 00.000" to "00 00 00.000". Default value is False.
  
      Returns
      -------
      s : 4 element tuple; (int, int, int, float)
          A tuple of sign and the three parts of the sexagesimal
          number. Sign is 1 for positive and -1 for negative values. The
          sign applies to the whole angle and not to any single part,
          i.e., all parts are positive and the sign multiplies the
          angle. The first and second parts of the sexagesimal number are
          integers and the last part is a float.
  
      Notes
      -----
      The given decimal number `deci` is converted into a sexagesimal
      number. The last part of the sexagesimal number is rounded to `pre`
      number of decimal points. If `trunc == True` then instead of
      rounding, the last part is truncated.
  
      If `lower` and `upper` are given then the number is normalized to
      the given range before converting into sexagesimal format. The `b`
      argument determines the type of normalization. See docstring of the
      `normalize` function for details.
  
      If `upper_trim` is True then, if after convertion to sexagesimal
      the first part is equal to `upper`, it is replaced with `lower` (value used
      is int(lower)). This is useful in cases where numbers such as "24 00 00.00"
      needs to be converted into "00 00 00.00"
  
      The returned sign, first element of tuple, applies to the whole
      number and not just to a single part.
  
      Examples
      --------
      >>> deci2sexa(-11.2345678)
      (-1, 11, 14, 4.444)
      >>> deci2sexa(-11.2345678, pre=5)
      (-1, 11, 14, 4.44408)
      >>> deci2sexa(-11.2345678, pre=4)
      (-1, 11, 14, 4.4441)
      >>> deci2sexa(-11.2345678, pre=4, trunc=True)
      (-1, 11, 14, 4.444)
  
      >>> deci2sexa(-11.2345678, pre=1)
      (-1, 11, 14, 4.4)
      >>> deci2sexa(-11.2345678, pre=0)
      (-1, 11, 14, 4.0)
      >>> deci2sexa(-11.2345678, pre=-1)
      (-1, 11, 14, 0.0)
  
      >>> x = 23+59/60.0+59.99999/3600.0
  
      To 3 decimal places, this number is 24 or 0 hours.
  
      >>> deci2sexa(x, pre=3, lower=0, upper=24, upper_trim=True)
      (1, 0, 0, 0.0)
      >>> deci2sexa(x, pre=3, lower=0, upper=24, upper_trim=False)
      (1, 24, 0, 0.0)
  
      To 5 decimal places, we get back the full value.
  
      >>> deci2sexa(x, pre=5, lower=0, upper=24, upper_trim=True)
      (1, 23, 59, 59.99999)
  
      """
      if lower is not None and upper is not None:
          deci = normalize(deci, lower=lower, upper=upper, b=b)
  
      sign = 1
      if deci < 0:
          deci = abs(deci)
          sign = -1
  
      hd, f1 = divmod(deci, 1)
      mm, f2 = divmod(f1 * 60.0, 1)
      sf = f2 * 60.0
  
      # Find the seconds part to required precision.
      fp = 10 ** pre
      if trunc:
          ss, _ = divmod(sf * fp, 1)
      else:
          ss = round(sf * fp, 0)
  
      ss = int(ss)
  
      # If ss is 60 to given precision then update mm, and if necessary
      # hd.
      if ss == 60 * fp:
          mm += 1
          ss = 0
      if mm == 60:
          hd += 1
          mm = 0
  
      hd = int(hd)
      mm = int(mm)
      if lower is not None and upper is not None and upper_trim:
          # For example 24h0m0s => 0h0m0s.
          if hd == upper:
              hd = int(lower)
  
      if hd == 0 and mm == 0 and ss == 0:
          sign = 1
  
      ss /= float(fp)
      # hd and mm parts are integer values but of type float
      return (sign, hd, mm, ss)