import re


def md_changes(seq, md_tag):
    """Recreates the reference sequence of a given alignment to the extent that the
    MD tag can represent.

    Note:
        Used in conjunction with `cigar_changes` to recreate the
        complete reference sequence

    Args:
        seq (str): aligned segment sequence
        md_tag (str): MD tag for associated sequence

    Returns:
        ref_seq (str): a version of the aligned segment's reference sequence given \
            the changes reflected in the MD tag

    Raises:
        ValueError: if MD tag is None

    Example:
        >>> md_changes('CTTATATTGGCCTT', '3C4AT4')
        'CTTCTATTATCCTT'

    """
    if md_tag is None:
        raise ValueError('No MD tag found or given for sequence')
    ref_seq = ''
    last_md_pos = 0
    for mo in re.finditer(r'(?P<matches>\d+)|(?P<del>\^\w+?(?=\d))|(?P<sub>\w)', md_tag):
        mo_group_dict = mo.groupdict()
        if mo_group_dict['matches'] is not None:
            matches = int(mo_group_dict['matches'])
            ref_seq += seq[last_md_pos:last_md_pos + matches]
            last_md_pos += matches
        elif mo_group_dict['del'] is not None:
            deletion = mo_group_dict['del']
            ref_seq += deletion[1:]
        elif mo_group_dict['sub'] is not None:
            substitution = mo_group_dict['sub']
            ref_seq += substitution
            last_md_pos += 1
        else:
            pass
    return ref_seq