Constructor for a :class:`BasisKet` for a :class:`SpinSpace`

    For a half-integer spin system::

        >>> hs = SpinSpace('s', spin=(3, 2))
        >>> assert SpinBasisKet(1, 2, hs=hs) == BasisKet("+1/2", hs=hs)

    For an integer spin system::

        >>> hs = SpinSpace('s', spin=1)
        >>> assert SpinBasisKet(1, hs=hs) == BasisKet("+1", hs=hs)

    Note that ``BasisKet(1, hs=hs)`` with an integer index (which would
    hypothetically refer to ``BasisKet("0", hs=hs)`` is not allowed for spin
    systems::

        >>> BasisKet(1, hs=hs)
        Traceback (most recent call last):
            ...
        TypeError: label_or_index must be an instance of one of str, SpinIndex; not int

    Raises:
        TypeError: if `hs` is not a :class:`SpinSpace` or the wrong number of
            positional arguments is given
        ValueError: if any of the positional arguments are out range for the
            given `hs`

def SpinBasisKet(*numer_denom, hs):
    """Constructor for a :class:`BasisKet` for a :class:`SpinSpace`

    For a half-integer spin system::

        >>> hs = SpinSpace('s', spin=(3, 2))
        >>> assert SpinBasisKet(1, 2, hs=hs) == BasisKet("+1/2", hs=hs)

    For an integer spin system::

        >>> hs = SpinSpace('s', spin=1)
        >>> assert SpinBasisKet(1, hs=hs) == BasisKet("+1", hs=hs)

    Note that ``BasisKet(1, hs=hs)`` with an integer index (which would
    hypothetically refer to ``BasisKet("0", hs=hs)`` is not allowed for spin
    systems::

        >>> BasisKet(1, hs=hs)
        Traceback (most recent call last):
            ...
        TypeError: label_or_index must be an instance of one of str, SpinIndex; not int

    Raises:
        TypeError: if `hs` is not a :class:`SpinSpace` or the wrong number of
            positional arguments is given
        ValueError: if any of the positional arguments are out range for the
            given `hs`
    """
    try:
        spin_numer, spin_denom = hs.spin.as_numer_denom()
    except AttributeError:
        raise TypeError(
            "hs=%s for SpinBasisKet must be a SpinSpace instance" % hs)
    assert spin_denom in (1, 2)
    if spin_denom == 1:  # integer spin
        if len(numer_denom) != 1:
            raise TypeError(
                "SpinBasisKet requires exactly one positional argument for an "
                "integer-spin Hilbert space")
        numer = numer_denom[0]
        if numer < -spin_numer or numer > spin_numer:
            raise ValueError(
                "spin quantum number %s must be in range (%s, %s)"
                % (numer, -spin_numer, spin_numer))
        label = str(numer)
        if numer > 0:
            label = "+" + label
        return BasisKet(label, hs=hs)
    else:  # half-integer spin
        if len(numer_denom) != 2:
            raise TypeError(
                "SpinBasisKet requires exactly two positional arguments for a "
                "half-integer-spin Hilbert space")
        numer, denom = numer_denom
        numer = int(numer)
        denom = int(denom)
        if denom != 2:
            raise ValueError(
                "The second positional argument (denominator of the spin "
                "quantum number) must be 2, not %s" % denom)
        if numer < -spin_numer or numer > spin_numer:
            raise ValueError(
                "spin quantum number %s/%s must be in range (%s/2, %s/2)"
                % (numer, denom, -spin_numer, spin_numer))
        label = str(numer)
        if numer > 0:
            label = "+" + label
        label = label + "/2"
        return BasisKet(label, hs=hs)