import fractions


def harmonics(h1, h2, fund=1, reverse=False):
    """
    Returns the harmonic series ratios between harmonic number
    h1 to h2 inclusive.

    If 0 < h1 < h2 then the ratios will produce the overtone series, 
    otherwise (0 > h1 > h2) will produce undertones.  If reverse is
    False then the overtone ratios are ascending and undertones are
    decending.

    Parameters
    ----------
    h1 : int
        The starting harmonic. If positive, overtones are produced
        otherwise negative undertones are produced.
    h2 : int
        The ending harmonic (inclusive). If positive it must be
        equal to or greater than h1. If negativ it must be equal
        to or less than h1.
    fund : int | float
        The fundamental of the series, defaults to 1. If fund is an int
        then exact Faction harmonics are produced. If fund is a float
        then the series will be floats as well.
    reverse : bool
        If true then overtones are returned reversed order.
    
    Returns
    -------
    The list of harmonic ratios between h1 and h2 inclusive.
    """

    if not isinstance(h1, int):
        TypeError(f"not an integer harmonic number: {h1}.")
    if not isinstance(h2, int):
        TypeError(f"not an integer harmonic number: {h2}.")
    utones = False
    harms = []
    if (0 < h1 < h2):
        pass
    elif (0 > h1 > h2):
        utones = True
    else:
        raise ValueError(f"harmonic numbers out of order: {h1} {h2}.")
    h1 = fractions.Fraction(abs(h1),1)
    h2 = fractions.Fraction(abs(h2),1)
    h = h1
    f = None
    while h <= h2:
        # utones collect the reciprocal
        f = fund/(h/h1) if utones else fund*(h/h1)
        harms.append(f)
        h += 1
    if reverse:
        harms.reverse()
    return harms