§
    ƒØjú”  ã                  óR  — d dl mZ d dlmZ d dlZd dlZd dlZd dlmZ d dl	m
Z
 d dl	mZ d dl	mZ d dlmZ  G d	„ d
ej        ¦  «        Z G d„ de¦  «        Z G d„ dej        ¦  «        Z G d„ d¦  «        Z G d„ dej        ¦  «        Zedk    rd dl	Z	 e	j        e¦  «         dS dS )é    )Úannotations)ÚCounterN)Úcast)Úbase)Úexceptions21)Úmetadata)Úhasherc                  ó   — e Zd ZdS )ÚAlignerExceptionN©Ú__name__Ú
__module__Ú__qualname__© ó    ú“/Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/music21/alpha/analysis/aligner.pyr   r      ó   € € € € € Ø€Dr   r   c                  ó   — e Zd ZdS )ÚAlignmentTracebackExceptionNr   r   r   r   r   r      r   r   r   c                  ó8   — e Zd ZdZdZdZdZdZed„ ¦   «         Z	dS )Ú	ChangeOpsa  
    >>> ins = alpha.analysis.aligner.ChangeOps.Insertion
    >>> ins.color
    'green'

    >>> deletion = alpha.analysis.aligner.ChangeOps.Deletion
    >>> deletion.color
    'red'

    >>> subs = alpha.analysis.aligner.ChangeOps.Substitution
    >>> subs.color
    'purple'

    >>> noChange = alpha.analysis.aligner.ChangeOps.NoChange
    >>> noChange.color is None
    True
    r   é   é   é   c                ó*   — dddd dœ}|| j                  S )NÚgreenÚredÚpurple)r   r   r   r   )Úvalue)ÚselfÚ	colorDicts     r   ÚcolorzChangeOps.color8   s    € à E¨h¸4Ð@Ð@ˆ	Ø˜œÔ$Ð$r   N)
r   r   r   Ú__doc__Ú	InsertionÚDeletionÚSubstitutionÚNoChangeÚpropertyr"   r   r   r   r   r   !   sM   € € € € € ðð ð" €IØ€HØ€LØ€Hàð%ð %ñ „Xð%ð %ð %r   r   c                  óp   — e Zd ZdZdd„Zd„ Zd„ Zd„ Zd„ Zd	„ Z	d
„ Z
d„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zdd„ZdS )ÚStreamAligneraa  
    Stream Aligner is a dumb object that takes in two streams and forces them to align
    without any thought to any external variables

    These terms are associated with the Target stream are:
    - n, the number of rows in the distance matrix, the left-most column of the matrix
    - i, the index into rows in the distance matrix
    - the first element of tuple

    These terms are associated with the Source stream are:
    - m, the number of columns in the distance matrix, the top-most row of the matrix
    - j, the index into columns in the distance matrix
    - the second element of tuple
    NFc                óÚ   — || _         || _        d | _        |€|                      ¦   «         }|| _        || _        g | _        d| _        d| _        d| _	        d | _
        d | _        d | _        d S )Nr   )ÚtargetStreamÚsourceStreamÚdistanceMatrixÚgetDefaultHasherr	   Ú	preHashedÚchangesÚsimilarityScoreÚnÚmÚhashedTargetStreamÚhashedSourceStreamÚchangesCount)r    r,   r-   Úhasher_funcr0   s        r   Ú__init__zStreamAligner.__init__N   s|   € Ø(ˆÔØ(ˆÔà"ˆÔàÐØ×/Ò/Ñ1Ô1ˆKà!ˆŒØ"ˆŒàˆŒØ ˆÔð ˆŒØˆŒà"&ˆÔØ"&ˆÔØ ˆÔÐÐr   c                óH   — t          j        ¦   «         }d|_        d|_        |S )a¯  
        returns a default hasher.Hasher object
        that does not hashOffset or include the reference.

        called by __init__ if no hasher is passed in.

        >>> sa = alpha.analysis.aligner.StreamAligner()
        >>> h = sa.getDefaultHasher()
        >>> h
        <music21.alpha.analysis.hasher.Hasher object at 0x1068cf6a0>
        >>> h.hashOffset
        False
        >>> h.includeReference
        True
        FT)r	   ÚHasherÚ
hashOffsetÚincludeReference)r    Úhs     r   r/   zStreamAligner.getDefaultHashere   s#   € õ" ŒM‰OŒOˆØˆŒØ!ˆÔØˆr   c                ó¦   — |                       ¦   «          |                      ¦   «          |                      ¦   «          |                      ¦   «          d S )N)ÚmakeHashedStreamsÚsetupDistanceMatrixÚpopulateDistanceMatrixÚcalculateChangesList©r    s    r   ÚalignzStreamAligner.align{   sP   € Ø×ÒÑ Ô Ð Ø× Ò Ñ"Ô"Ð"Ø×#Ò#Ñ%Ô%Ð%Ø×!Ò!Ñ#Ô#Ð#Ð#Ð#r   c                óØ   — | j         sJ| j                             | j        ¦  «        | _        | j                             | j        ¦  «        | _        dS | j        | _        | j        | _        dS )a)  
        Hashes streams if not pre-hashed

        >>> tStream = stream.Stream()
        >>> sStream = stream.Stream()

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        >>> tStream.append([note1, note2])
        >>> sStream.append([note3, note4])
        >>> sa1 = alpha.analysis.aligner.StreamAligner(tStream, sStream)

        >>> h = alpha.analysis.hasher.Hasher()
        >>> h.includeReference = True

        >>> toBeHashedTarStream = stream.Stream()
        >>> toBeHashedSouStream = stream.Stream()

        >>> note5 = note.Note('A4')
        >>> note6 = note.Note('B4')
        >>> note7 = note.Note('A4')
        >>> note8 = note.Note('G4')

        >>> toBeHashedTarStream.append([note5, note6])
        >>> toBeHashedSouStream.append([note7, note8])
        >>> hashedTarStr = h.hashStream(toBeHashedTarStream)
        >>> hashedSouStr = h.hashStream(toBeHashedSouStream)
        >>> sa2 = alpha.analysis.aligner.StreamAligner(
        ...             hashedTarStr, hashedSouStr, preHashed=True)

        >>> sa2.makeHashedStreams()
        >>> sa1.makeHashedStreams()

        >>> sa1.hashedTargetStream
        [NoteHashWithReference(Pitch=60, Duration=1.0),
         NoteHashWithReference(Pitch=62, Duration=1.0)]
        >>> sa1.hashedSourceStream
        [NoteHashWithReference(Pitch=60, Duration=1.0),
         NoteHashWithReference(Pitch=64, Duration=1.0)]

        >>> sa2.hashedTargetStream
        [NoteHashWithReference(Pitch=69, Duration=1.0, Offset=0.0),
         NoteHashWithReference(Pitch=71, Duration=1.0, Offset=1.0)]
        >>> sa2.hashedSourceStream
        [NoteHashWithReference(Pitch=69, Duration=1.0, Offset=0.0),
         NoteHashWithReference(Pitch=67, Duration=1.0, Offset=1.0)]

        N)r0   r	   Ú
hashStreamr,   r5   r-   r6   rD   s    r   r@   zStreamAligner.makeHashedStreams�   si   € ðh Œ~ð 	8Ø&*¤k×&<Ò&<¸TÔ=NÑ&OÔ&OˆDÔ#Ø&*¤k×&<Ò&<¸TÔ=NÑ&OÔ&OˆDÔ#Ð#Ð#ð '+Ô&7ˆDÔ#Ø&*Ô&7ˆDÔ#Ð#Ð#r   c                ó°  — | j         s|                      ¦   «          t          | j         ¦  «        | _        t          | j        ¦  «        | _        | j        dk    rt          d¦  «        ‚| j        dk    rt          d¦  «        ‚dt          j        v rt          d¦  «        ‚ddl	}| 
                    | j        dz   | j        dz   ft          ¬¦  «        | _        dS )	aí  
        Creates a distance matrix of the right size after hashing

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        Test for streams of length 3 and 4

        >>> target0 = converter.parse('tinyNotation: C4 D C E')
        >>> source0 = converter.parse('tinyNotation: C4 D C')

        >>> sa0 = alpha.analysis.aligner.StreamAligner(target0, source0)
        >>> sa0.setupDistanceMatrix()
        >>> sa0.distanceMatrix.size
        20
        >>> sa0.distanceMatrix.shape
        (5, 4)

        Test for empty target stream

        >>> target1 = stream.Stream()
        >>> source1 = stream.Stream()
        >>> source1.append(note1)
        >>> sa1 = alpha.analysis.aligner.StreamAligner(target1, source1)
        >>> sa1.makeHashedStreams()
        >>> sa1.setupDistanceMatrix()
        Traceback (most recent call last):
        music21.alpha.analysis.aligner.AlignerException:
        Cannot perform alignment with empty target stream.

        Test for empty source stream

        >>> target2 = stream.Stream()
        >>> source2 = stream.Stream()
        >>> target2.append(note3)
        >>> sa2 = alpha.analysis.aligner.StreamAligner(target2, source2)
        >>> sa2.makeHashedStreams()
        >>> sa2.setupDistanceMatrix()
        Traceback (most recent call last):
        music21.alpha.analysis.aligner.AlignerException:
            Cannot perform alignment with empty source stream.

        r   z2Cannot perform alignment with empty target stream.z2Cannot perform alignment with empty source stream.Únumpyz!Cannot run Aligner without numpy.Nr   )Údtype)r5   r@   Úlenr3   r6   r4   r   r   Ú_missingImportrI   ÚzerosÚintr.   )r    Únps     r   rA   z!StreamAligner.setupDistanceMatrix½   sÔ   € ð\ Ô&ð 	%Ø×"Ò"Ñ$Ô$Ð$å�TÔ,Ñ-Ô-ˆŒÝ�TÔ,Ñ-Ô-ˆŒàŒ6�QŠ;ˆ;Ý"Ð#WÑXÔXÐXàŒ6�QŠ;ˆ;Ý"Ð#WÑXÔXÐXð •dÔ)Ð)Ð)Ý"Ð#FÑGÔGÐGØÐÐÐà Ÿhšh¨¬°©
°D´F¸Q±JÐ'?Ås˜hÑKÔKˆÔÐÐr   c                ó2  — |                       | j        d         ¦  «        }|                      | j        d         ¦  «        }t          d| j        dz   ¦  «        D ])}| j        |dz
           d         |z   | j        |         d<   Œ*t          d| j        dz   ¦  «        D ])}| j        d         |dz
           |z   | j        d         |<   Œ*t          d| j        dz   ¦  «        D ]¹}t          d| j        dz   ¦  «        D ]ž}|                      | j        |dz
           | j        |dz
           ¦  «        }| j        |dz
           |         |z   | j        |         |dz
           |z   | j        |dz
           |dz
           |z   g}t          |¦  «        | j        |         |<   ŒŸŒºdS )a¼  
        Sets up the distance matrix for back-tracing

        >>> note1 = note.Note('C#4')
        >>> note2 = note.Note('C4')

        Test 1: similar streams

        >>> targetA = stream.Stream()
        >>> sourceA = stream.Stream()
        >>> targetA.append([note1, note2])
        >>> sourceA.append([note1, note2])
        >>> saA = alpha.analysis.aligner.StreamAligner(targetA, sourceA)
        >>> saA.makeHashedStreams()
        >>> saA.setupDistanceMatrix()
        >>> saA.populateDistanceMatrix()
        >>> saA.distanceMatrix
        array([[0, 2, 4],
               [2, 0, 2],
               [4, 2, 0]])

        Second Test

        >>> targetB = stream.Stream()
        >>> sourceB = stream.Stream()
        >>> targetB.append([note1, note2])
        >>> sourceB.append(note1)
        >>> saB = alpha.analysis.aligner.StreamAligner(targetB, sourceB)
        >>> saB.makeHashedStreams()
        >>> saB.setupDistanceMatrix()
        >>> saB.populateDistanceMatrix()
        >>> saB.distanceMatrix
        array([[0, 2],
               [2, 0],
               [4, 2]])

        Third Test

        >>> note3 = note.Note('D5')
        >>> note3.quarterLength = 3
        >>> note4 = note.Note('E3')
        >>> targetC = stream.Stream()
        >>> sourceC = stream.Stream()
        >>> targetC.append([note1, note2, note4])
        >>> sourceC.append([note3, note1, note4])
        >>> saC = alpha.analysis.aligner.StreamAligner(targetC, sourceC)
        >>> saC.makeHashedStreams()
        >>> saC.setupDistanceMatrix()
        >>> saC.populateDistanceMatrix()
        >>> saC.distanceMatrix
        array([[0, 2, 4, 6],
           [2, 2, 2, 4],
           [4, 4, 3, 3],
           [6, 6, 5, 3]])

        r   r   N)
Ú
insertCostr6   Ú
deleteCostÚranger3   r.   r4   ÚsubstitutionCostr5   Úmin)r    rQ   rR   ÚiÚjÚ	substCostÚpreviousValuess          r   rB   z$StreamAligner.populateDistanceMatrixþ   sÕ  € ðv —_’_ TÔ%<¸QÔ%?Ñ@Ô@ˆ
Ø—_’_ TÔ%<¸QÔ%?Ñ@Ô@ˆ
õ �q˜$œ& 1™*Ñ%Ô%ð 	Sð 	SˆAØ(,Ô(;¸AÀ¹EÔ(BÀ1Ô(EÈ
Ñ(RˆDÔ Ô" 1Ñ%Ð%õ �q˜$œ& 1™*Ñ%Ô%ð 	Sð 	SˆAØ(,Ô(;¸AÔ(>¸qÀ1¹uÔ(EÈ
Ñ(RˆDÔ Ô" 1Ñ%Ð%õ �q˜$œ& 1™*Ñ%Ô%ð 		@ð 		@ˆAÝ˜1˜dœf q™jÑ)Ô)ð @ð @�Ø ×1Ò1°$Ô2IÈ!ÈaÉ%Ô2PØ26Ô2IÈ!ÈaÉ%Ô2PñRô R�	ð #'Ô"5°a¸!±eÔ"<¸QÔ"?À*Ñ"LØ"&Ô"5°aÔ"8¸¸Q¹Ô"?À*Ñ"LØ"&Ô"5°a¸!±eÔ"<¸QÀ¹UÔ"CÀiÑ"Oð"Q�õ -0°Ñ,?Ô,?�Ô# AÔ& qÑ)Ð)ð@ð		@ð 		@r   c                ó$  — |dk    r#t          | j        |dz
           |         ¦  «        nd}|dk    r#t          | j        |         |dz
           ¦  «        nd}|dk    r,|dk    r&t          | j        |dz
           |dz
           ¦  «        nd}|||g}|S )a�  
        i and j are current row and column indices in self.distanceMatrix
        returns all possible moves (0 up to 3)
        vertical, horizontal, diagonal costs of adjacent entries in self.distMatrix

        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        >>> target.append([note1, note2, note3, note4])
        >>> source.append([note1, note2, note3])
        >>> sa = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa.makeHashedStreams()
        >>> sa.setupDistanceMatrix()
        >>> for i in range(4+1):
        ...     for j in range(3+1):
        ...         sa.distanceMatrix[i][j] = i * j

        >>> sa.distanceMatrix
        array([[ 0,  0,  0,  0],
               [ 0,  1,  2,  3],
               [ 0,  2,  4,  6],
               [ 0,  3,  6,  9],
               [ 0,  4,  8, 12]])

        >>> sa.getPossibleMovesFromLocation(0, 0)
        [None, None, None]

        >>> sa.getPossibleMovesFromLocation(1, 1)
        [0, 0, 0]

        >>> sa.getPossibleMovesFromLocation(4, 3)
        [9, 8, 6]

        >>> sa.getPossibleMovesFromLocation(2, 2)
        [2, 2, 1]

        >>> sa.getPossibleMovesFromLocation(0, 2)
        [None, 0, None]

        >>> sa.getPossibleMovesFromLocation(3, 0)
        [0, None, None]

        r   N)rN   r.   )r    rV   rW   ÚverticalCostÚhorizontalCostÚdiagonalCostÚpossibleMovess          r   ÚgetPossibleMovesFromLocationz*StreamAligner.getPossibleMovesFromLocationP  s¦   € ðd >?À!ºV¸V•s˜4Ô.¨q°1©uÔ5°aÔ8Ñ9Ô9Ð9ÈˆØ?@ÀAºv¸v�˜TÔ0°Ô3°A¸±EÔ:Ñ;Ô;Ð;È4ˆØBCÀqÂ&À&ÈQÐRSÊVÈV•s˜4Ô.¨q°1©uÔ5°a¸!±eÔ<Ñ=Ô=Ð=ÐZ^ˆà% ~°|ÐDˆØÐr   c                óz  — |                       ||¦  «        }|d         €#|d         €t          d¦  «        ‚t          j        S |d         €t          j        S | j        |         |         }t          t          |¦  «        t          j	        d¦  «        ¬¦  «        \  }}||k    rt          j
        S t          |¦  «        S )a„  
        Insert, Delete, Substitution, No Change = range(4)

        return the direction that traceback moves
        0: vertical movement, insertion
        1: horizontal movement, deletion
        2: diagonal movement, substitution
        3: diagonal movement, no change

        raises a ValueError if i == 0 and j == 0.
        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        >>> target.append([note1, note2, note3, note4])
        >>> source.append([note1, note2, note3])

        >>> sa = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa.makeHashedStreams()
        >>> sa.setupDistanceMatrix()
        >>> sa.populateDistanceMatrix()
        >>> sa.distanceMatrix
        array([[0, 2, 4, 6],
               [2, 0, 2, 4],
               [4, 2, 0, 2],
               [6, 4, 2, 0],
               [8, 6, 4, 2]])

        >>> sa.getOpFromLocation(4, 3)
        <ChangeOps.Insertion: 0>

        >>> sa.getOpFromLocation(2, 2)
        <ChangeOps.NoChange: 3>

        >>> sa.getOpFromLocation(0, 2)
        <ChangeOps.Deletion: 1>

        >>> sa.distanceMatrix[0][0] = 1
        >>> sa.distanceMatrix
        array([[1, 2, 4, 6],
               [2, 0, 2, 4],
               [4, 2, 0, 2],
               [6, 4, 2, 0],
               [8, 6, 4, 2]])

        >>> sa.getOpFromLocation(1, 1)
        <ChangeOps.Substitution: 2>

        >>> sa.getOpFromLocation(0, 0)
        Traceback (most recent call last):
        ValueError: No movement possible from the origin
        r   Nr   z$No movement possible from the origin)Úkey)r_   Ú
ValueErrorr   r%   r$   r.   rU   Ú	enumerateÚoperatorÚ
itemgetterr'   )r    rV   rW   r^   ÚcurrentCostÚminIndexÚ
minNewCosts          r   ÚgetOpFromLocationzStreamAligner.getOpFromLocation‰  s»   € ðr ×9Ò9¸!¸QÑ?Ô?ˆà˜ÔÐ#Ø˜QÔÐ'Ý Ð!GÑHÔHÐHÝÔ%Ð%Ø˜1ÔÐ%ÝÔ&Ð&àÔ)¨!Ô,¨QÔ/ˆÝ"¥9¨]Ñ#;Ô#;ÅÔATÐUVÑAWÔAWÐXÑXÔXÑˆ�*Ø˜*Ò$Ð$ÝÔ%Ð%å˜XÑ&Ô&Ð&r   c                ó.   — t          |j        ¦  «        }|S )a�  
        Cost of inserting an extra hashed item.
        For now, it's just the size of the keys of the NoteHashWithReference

        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        >>> target.append([note1, note2, note3, note4])
        >>> source.append([note1, note2, note3])

        This is a StreamAligner with default hasher settings

        >>> sa0 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa0.align()
        >>> tup0 = sa0.hashedTargetStream[0]
        >>> sa0.insertCost(tup0)
        2

        This is a StreamAligner with a modified hasher that doesn't hash pitch at all

        >>> sa1 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa1.hasher.hashPitch = False
        >>> sa1.align()
        >>> tup1 = sa1.hashedTargetStream[0]
        >>> sa1.insertCost(tup1)
        1

        This is a StreamAligner with a modified hasher that hashes 3 additional properties

        >>> sa2 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa2.hasher.hashOctave = True
        >>> sa2.hasher.hashIntervalFromLastNote = True
        >>> sa2.hasher.hashIsAccidental = True
        >>> sa2.align()
        >>> tup2 = sa2.hashedTargetStream[0]
        >>> sa2.insertCost(tup2)
        5
        ©rK   ÚhashItemsKeys©r    ÚtupÚkeyDictSizes      r   rQ   zStreamAligner.insertCostÒ  ó   € õX ˜#Ô+Ñ,Ô,ˆØÐr   c                ó.   — t          |j        ¦  «        }|S )aœ  
        Cost of deleting an extra hashed item.
        For now, it's just the size of the keys of the NoteHashWithReference

        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('C4')
        >>> note2 = note.Note('D4')
        >>> note3 = note.Note('C4')
        >>> note4 = note.Note('E4')

        >>> target.append([note1, note2, note3, note4])
        >>> source.append([note1, note2, note3])

        This is a StreamAligner with default hasher settings

        >>> sa0 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa0.align()
        >>> tup0 = sa0.hashedSourceStream[0]
        >>> sa0.deleteCost(tup0)
        2

        This is a StreamAligner with a modified hasher that doesn't hash pitch at all

        >>> sa1 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa1.hasher.hashPitch = False
        >>> sa1.align()
        >>> tup1 = sa1.hashedSourceStream[0]
        >>> sa1.deleteCost(tup1)
        1

        This is a StreamAligner with a modified hasher that hashes 3 additional properties

        >>> sa2 = alpha.analysis.aligner.StreamAligner(target, source)
        >>> sa2.hasher.hashOctave = True
        >>> sa2.hasher.hashIntervalFromLastNote = True
        >>> sa2.hasher.hashIsAccidental = True
        >>> sa2.align()
        >>> tup2 = sa2.hashedSourceStream[0]
        >>> sa2.deleteCost(tup2)
        5
        rk   rm   s      r   rR   zStreamAligner.deleteCost  rp   r   c                ó”   — |                       ||¦  «        rdS t          |j        ¦  «        }|                      ||¦  «        }||z  }|S )a0
  
        Finds the cost of substituting the targetTup with the sourceTup.
        For now, it's just an interpolation of how many things they have in common

        Example: equality testing, both streams made from same note
        targetA will not have the same reference as sourceA
        but their hashes will be equal, which makes for their hashed objects to be
        able to be equal.

        >>> note1 = note.Note('C4')
        >>> targetA = stream.Stream()
        >>> sourceA = stream.Stream()
        >>> targetA.append(note1)
        >>> sourceA.append(note1)
        >>> targetA == sourceA
        False

        >>> saA = alpha.analysis.aligner.StreamAligner(targetA, sourceA)
        >>> saA.align()
        >>> hashedItem1A = saA.hashedTargetStream[0]
        >>> hashedItem2A = saA.hashedSourceStream[0]
        >>> print(hashedItem1A)
        NoteHashWithReference(Pitch=60, Duration=1.0)

        >>> print(hashedItem2A)
        NoteHashWithReference(Pitch=60, Duration=1.0)

        >>> saA.tupleEqualityWithoutReference(hashedItem1A, hashedItem2A)
        True
        >>> saA.substitutionCost(hashedItem1A, hashedItem2A)
        0

        >>> note2 = note.Note('D4')
        >>> targetB = stream.Stream()
        >>> sourceB = stream.Stream()
        >>> targetB.append(note1)
        >>> sourceB.append(note2)
        >>> saB = alpha.analysis.aligner.StreamAligner(targetB, sourceB)
        >>> saB.align()
        >>> hashedItem1B = saB.hashedTargetStream[0]
        >>> hashedItem2B = saB.hashedSourceStream[0]

        hashed items only differ in 1 spot

        >>> print(hashedItem1B)
        NoteHashWithReference(Pitch=60, Duration=1.0)

        >>> print(hashedItem2B)
        NoteHashWithReference(Pitch=62, Duration=1.0)

        >>> saB.substitutionCost(hashedItem1B, hashedItem2B)
        1

        >>> note3 = note.Note('E4')
        >>> note4 = note.Note('E#4')
        >>> note4.duration = duration.Duration('half')
        >>> targetC = stream.Stream()
        >>> sourceC = stream.Stream()
        >>> targetC.append(note3)
        >>> sourceC.append(note4)
        >>> saC = alpha.analysis.aligner.StreamAligner(targetC, sourceC)
        >>> saC.align()
        >>> hashedItem1C = saC.hashedTargetStream[0]
        >>> hashedItem2C = saC.hashedSourceStream[0]

        hashed items should differ in 2 spots

        >>> print(hashedItem1C)
        NoteHashWithReference(Pitch=64, Duration=1.0)

        >>> print(hashedItem2C)
        NoteHashWithReference(Pitch=65, Duration=2.0)

        >>> saC.substitutionCost(hashedItem1C, hashedItem2C)
        2
        r   )ÚtupleEqualityWithoutReferencerK   rl   ÚcalculateNumSimilarities)r    Ú	targetTupÚ	sourceTupÚtotalPossibleDifferencesÚnumSimilaritiesInTuples        r   rT   zStreamAligner.substitutionCost0  s[   € ðZ ×-Ò-¨i¸ÑCÔCð 	Ø�1å#& yÔ'>Ñ#?Ô#?Ð Ø!%×!>Ò!>¸yÈ)Ñ!TÔ!TÐØ Ð$:Ñ:Ð Ø'Ð'r   c                ól   — d}|j         D ])}t          ||¦  «        t          ||¦  «        k    r|dz  }Œ*|S )aý  
        Returns the number of attributes that two tuples have that are the same

        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('D1')
        >>> target.append([note1])
        >>> source.append([note1])
        >>> sa = alpha.analysis.aligner.StreamAligner(target, source)

        >>> from collections import namedtuple
        >>> NoteHash = namedtuple('NoteHash', ['Pitch', 'Duration'])
        >>> nh1 = NoteHash(60, 4)
        >>> nhwr1 = alpha.analysis.hasher.NoteHashWithReference(nh1)
        >>> nhwr1.reference = note.Note('C4')
        >>> nhwr1
        NoteHashWithReference(Pitch=60, Duration=4)

        >>> nh2 = NoteHash(60, 4)
        >>> nhwr2 = alpha.analysis.hasher.NoteHashWithReference(nh2)
        >>> nhwr2.reference = note.Note('C4')
        >>> nhwr2
        NoteHashWithReference(Pitch=60, Duration=4)

        >>> sa.calculateNumSimilarities(nhwr1, nhwr2)
        2

        >>> nh3 = NoteHash(61, 4)
        >>> nhwr3 = alpha.analysis.hasher.NoteHashWithReference(nh3)
        >>> nhwr3.reference = note.Note('C#4')
        >>> nhwr3
        NoteHashWithReference(Pitch=61, Duration=4)

        >>> sa.calculateNumSimilarities(nhwr1, nhwr3)
        1

        >>> nh4 = NoteHash(59, 1)
        >>> nhwr4 = alpha.analysis.hasher.NoteHashWithReference(nh4)
        >>> nhwr4.reference = note.Note('B3')
        >>> nhwr4
        NoteHashWithReference(Pitch=59, Duration=1)

        >>> sa.calculateNumSimilarities(nhwr2, nhwr4)
        0
        r   r   ©rl   Úgetattr)r    ru   rv   ÚcountÚvals        r   rt   z&StreamAligner.calculateNumSimilarities…  sJ   € ð` ˆØÔ*ð 	ð 	ˆCÝ�y #Ñ&Ô&­'°)¸SÑ*AÔ*AÒAÐAØ˜‘
�øØˆr   c                ód   — |j         D ]'}t          ||¦  «        t          ||¦  «        k    r dS Œ(dS )aA  
        Returns whether two hashed items have the same attributes,
        even though their references are different?

        >>> target = stream.Stream()
        >>> source = stream.Stream()

        >>> note1 = note.Note('D1')
        >>> target.append([note1])
        >>> source.append([note1])
        >>> sa = alpha.analysis.aligner.StreamAligner(target, source)

        >>> from collections import namedtuple
        >>> NoteHash = namedtuple('NoteHash', ['Pitch', 'Duration'])
        >>> nh1 = NoteHash(60, 4)
        >>> nhwr1 = alpha.analysis.hasher.NoteHashWithReference(nh1)
        >>> nhwr1.reference = note.Note('C4')
        >>> nhwr1
        NoteHashWithReference(Pitch=60, Duration=4)

        >>> nh2 = NoteHash(60, 4)
        >>> nhwr2 = alpha.analysis.hasher.NoteHashWithReference(nh2)
        >>> nhwr2.reference = note.Note('B#3')
        >>> nhwr2
        NoteHashWithReference(Pitch=60, Duration=4)

        >>> sa.tupleEqualityWithoutReference(nhwr1, nhwr2)
        True

        This is a very difference has

        >>> nh3 = NoteHash(61, 4)
        >>> nhwr3 = alpha.analysis.hasher.NoteHashWithReference(nh3)
        >>> nhwr3.reference = note.Note('C#4')
        >>> nhwr3
        NoteHashWithReference(Pitch=61, Duration=4)

        >>> sa.tupleEqualityWithoutReference(nhwr1, nhwr3)
        False

        FTrz   )r    Útup1Útup2r}   s       r   rs   z+StreamAligner.tupleEqualityWithoutReference»  sG   € ðT Ô%ð 	ð 	ˆCÝ�t˜SÑ!Ô!¥W¨T°3Ñ%7Ô%7Ò7Ð7Ø�u�uð 8àˆtr   c                ó’  — | j         }| j        }|dk    s|dk    r½|                      ||¦  «        }| j        |dz
           j        }| j        |dz
           j        }|||f}| j                             d|¦  «         |t          j	        k    r|dz  }n;|t          j
        k    r|dz  }n%|t          j        k    r|dz  }|dz  }n
|dz  }|dz  }|dk    °·|dk    °½|dk    r|dk    rt          d¦  «        ‚t          d„ | j        D ¦   «         ¦  «        | _        | j        t          j                 t!          | j        ¦  «        z  | _        dS )añ  
        Traverses through self.distanceMatrix from the bottom right corner to top left looking at
        bestOp at every move to determine which change was most likely at any point. Compiles
        the list of changes in self.changes. Also calculates some metrics like self.similarityScore
        and self.changesCount.

        >>> note1 = note.Note('C#4')
        >>> note2 = note.Note('C4')

        test 1: one insertion, one no change. Target stream has one more note than
        source stream, so source stream needs an insertion to match target stream.
        should be 0.5 similarity between the two

        >>> targetA = stream.Stream()
        >>> sourceA = stream.Stream()
        >>> targetA.append([note1, note2])
        >>> sourceA.append(note1)
        >>> saA = alpha.analysis.aligner.StreamAligner(targetA, sourceA)
        >>> saA.makeHashedStreams()
        >>> saA.setupDistanceMatrix()
        >>> saA.populateDistanceMatrix()
        >>> saA.calculateChangesList()
        >>> saA.changesCount[alpha.analysis.aligner.ChangeOps.Insertion]
        1
        >>> saA.changesCount[alpha.analysis.aligner.ChangeOps.NoChange]
        1
        >>> saA.similarityScore
        0.5

        test 2: one deletion, one no change. Target stream has one fewer note than
        source stream, so source stream needs a deletion to match target stream.
        should be 0.5 similarity between the two

        >>> targetB = stream.Stream()
        >>> sourceB = stream.Stream()
        >>> targetB.append(note1)
        >>> sourceB.append([note1, note2])
        >>> saB = alpha.analysis.aligner.StreamAligner(targetB, sourceB)
        >>> saB.makeHashedStreams()
        >>> saB.setupDistanceMatrix()
        >>> saB.populateDistanceMatrix()
        >>> saB.calculateChangesList()
        >>> saB.changesCount[alpha.analysis.aligner.ChangeOps.Deletion]
        1
        >>> saB.changesCount[alpha.analysis.aligner.ChangeOps.NoChange]
        1
        >>> saB.similarityScore
        0.5

        test 3: no changes

        >>> targetC = stream.Stream()
        >>> sourceC = stream.Stream()
        >>> targetC.append([note1, note2])
        >>> sourceC.append([note1, note2])
        >>> saC = alpha.analysis.aligner.StreamAligner(targetC, sourceC)
        >>> saC.makeHashedStreams()
        >>> saC.setupDistanceMatrix()
        >>> saC.populateDistanceMatrix()
        >>> saC.calculateChangesList()
        >>> saC.changesCount[alpha.analysis.aligner.ChangeOps.NoChange]
        2
        >>> saC.similarityScore
        1.0

        test 4: 1 no change, 1 substitution

        >>> targetD = stream.Stream()
        >>> sourceD = stream.Stream()
        >>> note3 = note.Note('C4')
        >>> note3.quarterLength = 2  # same pitch and offset as note2
        >>> targetD.append([note1, note2])
        >>> sourceD.append([note1, note3])
        >>> saD = alpha.analysis.aligner.StreamAligner(targetD, sourceD)
        >>> saD.makeHashedStreams()
        >>> saD.setupDistanceMatrix()
        >>> saD.populateDistanceMatrix()
        >>> saD.calculateChangesList()
        >>> saD.changesCount[alpha.analysis.aligner.ChangeOps.Substitution]
        1
        >>> saD.changesCount[alpha.analysis.aligner.ChangeOps.NoChange]
        1
        >>> saD.similarityScore
        0.5

        r   r   z0Traceback of best alignment did not end properlyc              3  ó&   K  — | ]}|d          V — ŒdS )r   Nr   )Ú.0Úelems     r   ú	<genexpr>z5StreamAligner.calculateChangesList.<locals>.<genexpr>_  s&   è è € Ð#EÐ#E° D¨¤GÐ#EÐ#EÐ#EÐ#EÐ#EÐ#Er   N)r3   r4   ri   r5   Ú	referencer6   r1   Úinsertr   r$   r%   r&   r   r   r7   r'   rK   r2   )r    rV   rW   ÚbestOpÚtargetStreamReferenceÚsourceStreamReferenceÚopTuples          r   rC   z"StreamAligner.calculateChangesListê  sr  € ðn ŒFˆØŒFˆØ�1Šfˆf˜˜Qš˜ð ×+Ò+¨A¨qÑ1Ô1ˆFØ$(Ô$;¸AÀ¹EÔ$BÔ$LÐ!Ø$(Ô$;¸AÀ¹EÔ$BÔ$LÐ!Ø,Ð.CÀVÐLˆGØŒL×Ò  7Ñ+Ô+Ð+ð
 �Ô,Ò,Ð,Ø�Q‘��à�9Ô-Ò-Ð-Ø�Q‘��à�9Ô1Ò1Ð1Ø�Q‘�Ø�Q‘��ð �Q‘�Ø�Q‘�ð1 �1Šfˆf˜˜Qš˜ð2 �Š6ˆ6�a˜1’f�fÝ-Ð.`ÑaÔaÐaå#Ð#EÐ#E¸¼Ð#EÑ#EÔ#EÑEÔEˆÔØ#Ô0µÔ1CÔDÅsÈ4Ì<ÑGXÔGXÑXˆÔÐÐr   c                ó  — t          | j        ¦  «        D ]f\  }\  }}}|t          j        k    rŒ|j        |j        _        |                     |¦  «         |j        |j        _        |                     |¦  «         Œgt          j        ¦   «         | j	        _        t          j        ¦   «         | j
        _        dt          | j	        j        ¦  «        z   | j	        j        _        dt          | j	        j        ¦  «        z   | j
        j        _        | j	        j        j        | j	        j        _        | j
        j        j        | j
        j        _        |r4| j	                             ¦   «          | j
                             ¦   «          dS dS )zŽ
        Visual and debugging feature to display which notes are changed.
        Will open in MuseScore, unless show is set to False
        zTarget zSource N)rc   r1   r   r'   r"   ÚstyleÚaddLyricr   ÚMetadatar,   r-   ÚstrÚidÚtitleÚmovementNameÚshow)r    r”   ÚidxÚmidiNoteRefÚ
omrNoteRefÚchanges         r   ÚshowChangeszStreamAligner.showChangesb  sZ  € õ
 9BÀ$Ä,Ñ8OÔ8Oð 	)ð 	)Ñ4ˆSÑ3�; 
¨FØ�Ô+Ò+Ð+Øà*0¬,�Ô!Ô'Ø×$Ò$ SÑ)Ô)Ð)Ø)/¬�
Ô Ô&Ø×#Ò# CÑ(Ô(Ð(Ð(å%-Ô%6Ñ%8Ô%8ˆÔÔ"Ý%-Ô%6Ñ%8Ô%8ˆÔÔ"à+4µs¸4Ô;LÔ;OÑ7PÔ7PÑ+PˆÔÔ"Ô(Ø+4µs¸4Ô;LÔ;OÑ7PÔ7PÑ+PˆÔÔ"Ô(à26Ô2CÔ2LÔ2RˆÔÔ"Ô/Ø26Ô2CÔ2LÔ2RˆÔÔ"Ô/àð 	%ØÔ×"Ò"Ñ$Ô$Ð$ØÔ×"Ò"Ñ$Ô$Ð$Ð$Ð$ð	%ð 	%r   )NNNF)F)r   r   r   r#   r9   r/   rE   r@   rA   rB   r_   ri   rQ   rR   rT   rt   rs   rC   r™   r   r   r   r*   r*   >   s)  € € € € € ðð ð!ð !ð !ð !ð.ð ð ð,$ð $ð $ð:8ð :8ð :8ðx?Lð ?Lð ?LðBP@ð P@ð P@ðd7ð 7ð 7ðrG'ð G'ð G'ðR-ð -ð -ð^-ð -ð -ð^S(ð S(ð S(ðj4ð 4ð 4ðl-ð -ð -ð^vYð vYð vYðp%ð %ð %ð %ð %ð %r   r*   c                  óJ   — e Zd Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z	d„ Z
d	„ Zd
„ ZdS )ÚTestc                ó”  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     |¦  «         |                     |¦  «         t          ||¦  «        }|                     ¦   «          |                      |j	        d¦  «         dS )zI
        two streams of the same note should have 1.0 similarity
        r   ©Ústream©ÚnoteÚC4ç      ð?N©
Úmusic21rž   r    ÚStreamÚNoteÚappendr*   rE   ÚassertEqualr2   ©r    rž   r    ÚtargetÚsourceÚnote1Únote2Úsas           r   ÚtestSimpleStreamOneNotezTest.testSimpleStreamOneNote  s¿   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜$‘”ˆà�Š�eÑÔÐØ�Š�eÑÔÐå˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
à×Ò˜Ô+¨SÑ1Ô1Ð1Ð1Ð1r   c                ó¢  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }d|_        |                     |¦  «         |                     |¦  «         t          ||¦  «        }|                     ¦   «          |  	                    |j
        d¦  «         dS )	zO
        two streams of two different notes should have 0.0 similarity
        r   r�   rŸ   r¡   zC#4é   g        N)r¤   rž   r    r¥   r¦   ÚquarterLengthr§   r*   rE   r¨   r2   r©   s           r   Ú testSimpleStreamOneNoteDifferentz%Test.testSimpleStreamOneNoteDifferent”  sÉ   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜%Ñ Ô ˆØˆÔà�Š�eÑÔÐØ�Š�eÑÔÐå˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
à×Ò˜Ô+¨SÑ1Ô1Ð1Ð1Ð1r   c                óø  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     ||||g¦  «         |                     ||||g¦  «         t          ||¦  «        }	|	                     ¦   «          |                      |	j	        d¦  «         d	S )
zU
        two streams of the same notes should have 1.0 percentage similarity
        r   r�   rŸ   r¡   ÚD4ÚE4ÚF4r¢   Nr£   ©
r    rž   r    rª   r«   r¬   r­   Únote3Únote4r®   s
             r   ÚtestSameSimpleStreamzTest.testSameSimpleStreamª  sï   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆà�Š�u˜e U¨EÐ2Ñ3Ô3Ð3Ø�Š�u˜e U¨EÐ2Ñ3Ô3Ð3å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
à×Ò˜Ô+¨SÑ1Ô1Ð1Ð1Ð1r   c                óô  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     |||g¦  «         |                     |||g¦  «         t          ||¦  «        }	|	                     ¦   «          |                      |	j	        d¦  «         dS )	zN
        two streams of the 2/3 same notes should have 2/3 similarity
        r   r�   rŸ   r¡   zD#4zD-4gUUUUUUå?Nr£   r¸   s
             r   ÚtestSameSimpleStream2zTest.testSameSimpleStream2Á  sï   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜%Ñ Ô ˆØ—	’	˜%Ñ Ô ˆØ—	’	˜$‘”ˆà�Š�u˜e UÐ+Ñ,Ô,Ð,Ø�Š�u˜e UÐ+Ñ,Ô,Ð,å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
à×Ò˜Ô+¨UÑ3Ô3Ð3Ð3Ð3r   c                ó"  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }	|                     ||||g¦  «         |                     ||||	g¦  «         t          ||¦  «        }
|
                     ¦   «          |                      |
j	        d	¦  «         d
S )z_
        two streams with just 1 note different should have 0.75 percentage similarity
        r   r�   rŸ   r¡   rµ   r¶   r·   ÚG4ç      è?Nr£   )r    rž   r    rª   r«   r¬   r­   r¹   rº   Únote5r®   s              r   ÚtestSameOneOffStreamzTest.testSameOneOffStreamØ  sþ   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆà�Š�u˜e U¨EÐ2Ñ3Ô3Ð3Ø�Š�u˜e U¨EÐ2Ñ3Ô3Ð3å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
à×Ò˜Ô+¨TÑ2Ô2Ð2Ð2Ð2r   c                ó  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     ||||g¦  «         |                     |||g¦  «         t          ||¦  «        }	|	                     ¦   «          |	                     ¦   «          |  	                    |	j
        d¦  «         d	S )
zv
        two streams, both the same, but one has an extra note should
        have 0.75 percentage similarity
        r   r�   rŸ   r¡   rµ   r¶   r·   rÀ   N)r¤   rž   r    r¥   r¦   r§   r*   rE   r™   r¨   r2   r¸   s
             r   ÚtestOneOffDeletionStreamzTest.testOneOffDeletionStreamð  sý   € ð
 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆØ—	’	˜$‘”ˆà�Š�u˜e U¨EÐ2Ñ3Ô3Ð3Ø�Š�u˜e UÐ+Ñ,Ô,Ð,å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
Ø
�ŠÑÔÐà×Ò˜Ô+¨TÑ2Ô2Ð2Ð2Ð2r   c                ón  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     g d¢¦  «        }|                     |¦  «         |                     |¦  «         t          ||¦  «        }|                     ¦   «          |                      |j	        d¦  «         dS )z6
        two streams, one with explicit chord
        r   r�   )Úchord)ÚE3r¡   r¿   r¢   N)
r¤   rž   rÆ   r¥   ÚChordr§   r*   rE   r¨   r2   )r    rž   rÆ   rª   r«   ÚcMajorr®   s          r   ÚtestChordSimilarityStreamzTest.testChordSimilarityStream	  s¹   € ð 	#Ð"Ð"Ð"Ð"Ð"Ø!Ð!Ð!Ð!Ð!Ð!à—’‘”ˆØ—’‘”ˆà—’Ð/Ð/Ð/Ñ0Ô0ˆØ�Š�fÑÔÐØ�Š�fÑÔÐå˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
Ø×Ò˜Ô+¨SÑ1Ô1Ð1Ð1Ð1r   c                ó¦  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }	|                     d¦  «        }
|                     d¦  «        }|                     ||||g¦  «         |                     ||	|
g¦  «         t          ||¦  «        }|                     ¦   «          |                     ¦   «          | 	                    |j
        d         d         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         | 	                    |j
        d         d	         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         d
S )z¡
        Given two streams:

        MIDI is `C C C B`
        OMR is `C C C`

        Therefore, there needs to be an insertion to get from OMR to MIDI
        r   r�   rŸ   r¡   ÚB3r   r   Ú3r   N©r¤   rž   r    r¥   r¦   r§   r*   rE   r™   ÚgetElementByIdr1   r‘   ÚassertIsNotNoner   r¨   r�   r"   Úlyric©r    rž   r    rª   r«   ÚnoteC1ÚnoteC2ÚnoteC3ÚnoteC4ÚnoteC5ÚnoteC6ÚnoteBr®   Ún0Ún1s                  r   ÚtestShowInsertionzTest.testShowInsertion  s  € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—	’	˜$‘”ˆà�Š�v˜v v¨uÐ5Ñ6Ô6Ð6Ø�Š�v˜v vÐ.Ñ/Ô/Ð/å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
Ø
�ŠÑÔÐà×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ1Ô1Ð1Ø×Ò˜œ 3Ñ'Ô'Ð'à×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ1Ô1Ð1Ø×Ò˜œ 3Ñ'Ô'Ð'Ð'Ð'r   c                ó¦  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }	|                     d¦  «        }
|                     d¦  «        }|                     |||g¦  «         |                     ||	|
|g¦  «         t          ||¦  «        }|                     ¦   «          |                     ¦   «          | 	                    |j
        d         d         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         | 	                    |j
        d         d	         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         d
S )z¡
        Given two streams:

        MIDI is `C C C`

        OMR is `C C C B`

        Therefore, there needs to be a deletion to get from OMR to MIDI.
        r   r�   rŸ   r¡   rÌ   r   r   rÍ   r   NrÎ   rÒ   s                  r   ÚtestShowDeletionzTest.testShowDeletionE  s  € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—	’	˜$‘”ˆà�Š�v˜v vÐ.Ñ/Ô/Ð/Ø�Š�v˜v v¨uÐ5Ñ6Ô6Ð6å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
Ø
�ŠÑÔÐà×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ/Ô/Ð/Ø×Ò˜œ 3Ñ'Ô'Ð'à×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ/Ô/Ð/Ø×Ò˜œ 3Ñ'Ô'Ð'Ð'Ð'r   c                óz  — ddl m} ddl m} |                     ¦   «         }|                     ¦   «         }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }|                     d¦  «        }	|                     d¦  «        }
|                     |||g¦  «         |                     ||	|
g¦  «         t          ||¦  «        }|                     ¦   «          |                     ¦   «          | 	                    |j
        d         d         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         | 	                    |j
        d         d	         j        ¦  «        }|                      |¦  «         t          |j        |¦  «        }|                      |j        j        d¦  «         |                      |j        d¦  «         d
S )zš
        two streams:
        MIDI is `C C C`
        OMR is `C C B`

        Therefore, there needs to be a substitution to get from OMR to MIDI
        r   r�   rŸ   r¡   rÌ   r   r   Ú2r   NrÎ   )r    rž   r    rª   r«   rÓ   rÔ   rÕ   rÖ   r×   rÙ   r®   rÚ   rÛ   s                 r   ÚtestShowSubstitutionzTest.testShowSubstitutionp  sò  € ð 	#Ð"Ð"Ð"Ð"Ð"Ø Ð Ð Ð Ð Ð à—’‘”ˆØ—’‘”ˆà—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—’˜4‘”ˆØ—	’	˜$‘”ˆà�Š�v˜v vÐ.Ñ/Ô/Ð/Ø�Š�v˜v uÐ-Ñ.Ô.Ð.å˜6 6Ñ*Ô*ˆØ
�Š‰
Œ
ˆ
Ø
�ŠÑÔÐð ×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ2Ô2Ð2Ø×Ò˜œ 3Ñ'Ô'Ð'à×"Ò" 2¤:¨a¤=°Ô#3Ô#6Ñ7Ô7ˆØ×Ò˜RÑ Ô Ð Ý�$”)˜RÑ Ô ˆØ×Ò˜œœ¨Ñ2Ô2Ð2Ø×Ò˜œ 3Ñ'Ô'Ð'Ð'Ð'r   N)r   r   r   r¯   r³   r»   r½   rÂ   rÄ   rÊ   rÜ   rÞ   rá   r   r   r   r›   r›   ~  s¬   € € € € € ð2ð 2ð 2ð*2ð 2ð 2ð,2ð 2ð 2ð.4ð 4ð 4ð.3ð 3ð 3ð03ð 3ð 3ð22ð 2ð 2ð$((ð ((ð ((ðT)(ð )(ð )(ðV'(ð '(ð '(ð '(ð '(r   r›   Ú__main__)Ú
__future__r   Úcollectionsr   Úenumrd   ÚunittestÚtypingr   r¤   r   r   r   Úmusic21.alpha.analysisr	   ÚMusic21Exceptionr   r   ÚIntEnumr   r*   ÚTestCaser›   r   ÚmainTestr   r   r   ú<module>rí      sÂ  ðð #Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð Ð Ð Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð à Ð Ð Ð Ð Ð Ø  Ð  Ð  Ð  Ð  Ð  Ø Ð Ð Ð Ð Ð à )Ð )Ð )Ð )Ð )Ð )ð	ð 	ð 	ð 	ð 	�|Ô4ñ 	ô 	ð 	ð	ð 	ð 	ð 	ð 	Ð"2ñ 	ô 	ð 	ð%ð %ð %ð %ð %�”ñ %ô %ð %ð:}%ð }%ð }%ð }%ð }%ñ }%ô }%ð }%ð@Y(ð Y(ð Y(ð Y(ð Y(ˆ8Ôñ Y(ô Y(ð Y(ðx ˆzÒÐØ€N€N€NØ€GÔ�TÑÔÐÐÐð Ðr   