from typing import Union


def is_downstream(pfaf_id_a: Union[int, str], pfaf_id_b: Union[int, str]) -> bool:
    """Calculate if pfaf_id_b is downstream of pfaf_id_a

    Implemented as in https://en.wikipedia.org/wiki/Pfafstetter_Coding_System#Properties
    Works even if pfaf_id_a and pfaf_id_b are at different levels.

    :param pfaf_id_a: first Pfafstetter id (upstream)
    :param pfaf_id_b: second Pfafstetter id (downstream)
    :return: `True` if pfaf_id_b is downstream of pfaf_id_a, `False` otherwise or if a == b
    """
    if str(pfaf_id_a) == str(pfaf_id_b):
        # Basin is not downstream of itself.
        return False
    n = 0
    for c1, c2 in zip(str(pfaf_id_a), str(pfaf_id_b)):
        if c1 == c2:
            n += 1
        else:
            break
    # First n digits are the same.
    # In case where b is shorter than a (e.g. b is level 1, a is level 2), only compare the matching digits up to
    # the length of b.
    min_len_a_b = min(len(str(pfaf_id_a)), len(str(pfaf_id_b)))
    if int(str(pfaf_id_b)[n:min_len_a_b]) < int(str(pfaf_id_a)[n:min_len_a_b]):
        # If any remaining digits in b are even it is not downstream.
        for d in [int(c) for c in str(pfaf_id_b)[n:]]:
            if d % 2 == 0 and d != 0:
                return False
        return True
    return False