Collect :class:`ValueScalar` and :class:`ScalarExpression` summands

    Example:
        >>> srepr(collect_scalar_summands(Scalar, (1, 2, 3), {}))
        'ScalarValue(6)'
        >>> collect_scalar_summands(Scalar, (1, 1, -1), {})
        One
        >>> collect_scalar_summands(Scalar, (1, -1), {})
        Zero

        >>> Psi = KetSymbol("Psi", hs=0)
        >>> Phi = KetSymbol("Phi", hs=0)
        >>> braket = BraKet.create(Psi, Phi)

        >>> collect_scalar_summands(Scalar, (1, braket, -1), {})
        <Psi|Phi>^(0)
        >>> collect_scalar_summands(Scalar, (1, 2 * braket, 2, 2 * braket), {})
        ((3, 4 * <Psi|Phi>^(0)), {})
        >>> collect_scalar_summands(Scalar, (2 * braket, -braket, -braket), {})
        Zero

def collect_scalar_summands(cls, ops, kwargs):
    """Collect :class:`ValueScalar` and :class:`ScalarExpression` summands

    Example:
        >>> srepr(collect_scalar_summands(Scalar, (1, 2, 3), {}))
        'ScalarValue(6)'
        >>> collect_scalar_summands(Scalar, (1, 1, -1), {})
        One
        >>> collect_scalar_summands(Scalar, (1, -1), {})
        Zero

        >>> Psi = KetSymbol("Psi", hs=0)
        >>> Phi = KetSymbol("Phi", hs=0)
        >>> braket = BraKet.create(Psi, Phi)

        >>> collect_scalar_summands(Scalar, (1, braket, -1), {})
        <Psi|Phi>^(0)
        >>> collect_scalar_summands(Scalar, (1, 2 * braket, 2, 2 * braket), {})
        ((3, 4 * <Psi|Phi>^(0)), {})
        >>> collect_scalar_summands(Scalar, (2 * braket, -braket, -braket), {})
        Zero
    """
    # This routine is required because there is no
    # "ScalarTimesQuantumExpression" for scalars: we have to extract
    # coefficiencts from ScalarTimes instead
    from qnet.algebra.core.scalar_algebra import (
        Zero, One, Scalar, ScalarTimes, ScalarValue)
    a_0 = Zero
    coeff_map = OrderedDict()
    for op in ops:
        if isinstance(op, ScalarValue) or isinstance(op, Scalar._val_types):
            a_0 += op
            continue
        elif isinstance(op, ScalarTimes):
            if isinstance(op.operands[0], ScalarValue):
                coeff = op.operands[0]
                term = op.operands[1]
                for sub_op in op.operands[2:]:
                    term *= sub_op
            else:
                coeff, term = One, op
        else:
            coeff, term = One, op
        if term in coeff_map:
            coeff_map[term] += coeff
        else:
            coeff_map[term] = coeff
    if a_0 == Zero:
        fops = []
    else:
        fops = [a_0]
    for (term, coeff) in coeff_map.items():
        op = coeff * term
        if not op.is_zero:
            fops.append(op)
    if len(fops) == 0:
        return cls._zero
    elif len(fops) == 1:
        return fops[0]
    else:
        return tuple(fops), kwargs