
    rhRP                        S r SSKJr  SSKrSSKJr  SSKJr  SSKJ	r	   " S S\R                  5      r " S	 S
\5      r " S S\R                  5      r\\4r\S:X  a  SSKr\R$                  " 5         gg)z
Internal data structures for timespan collections.

This is an implementation detail of the TimespanTree class.  Most music21 users
can happily ignore this module.
    )annotationsN)core)Music21Object)	SortTuplec                     ^  \ rS rSr% SrSrSSSSSS	.rS
\S'   SU 4S jjrS r	\
S 5       r\
S 5       rSS jrS rSrU =r$ )ElementNode   a  
A node containing a single element, which is aware of the element's
endTime and index within a stream, as well as the endTimes and indices of the
elements to the left and right of it.

Here's an element node that is at first is no different from an AVL node, except in
representation:

>>> n = note.Note('C4')
>>> n.duration.quarterLength = 2.0
>>> n.offset = 4.0

>>> elNode = tree.node.ElementNode(n.sortTuple(), n)
>>> elNode
<ElementNode: Start:4.0 <0.20...> Indices:(l:-1 *-1* r:-1) Payload:<music21.note.Note C>>

>>> avlNode = tree.core.AVLNode(4.0, n)
>>> avlNode
<AVLNode: Start:4.0 Height:0 L:None R:None>

But now let's give n a stream context where it is element 1 in the measure.

>>> m = stream.Measure()
>>> r = note.Rest(type='whole')
>>> m.insert(0.0, r)
>>> m.insert(4.0, n)
>>> n2 = note.Note('F#4')
>>> n2.duration.quarterLength = 3.0
>>> m.insert(6.0, n2)

Let's create an ElementNode object for each object and attach them to elNode:

>>> rElNode = tree.node.ElementNode(r.sortTuple(), r)
>>> n2ElNode = tree.node.ElementNode(n2.sortTuple(), n2)
>>> elNode.leftChild = rElNode
>>> elNode.update()
>>> elNode.rightChild = n2ElNode
>>> elNode.update()

Everything so far could be done w/ AVLNodes, which are not music21 specific.
But now we'll add some specific features, based on the information from the Stream.

>>> m[1] is n
True
>>> m.highestTime
9.0
>>> m.offset
0.0


>>> rElNode.payloadElementIndex = 0
>>> elNode.payloadElementIndex = 1
>>> n2ElNode.payloadElementIndex = 2

>>> elNode.updateIndices()
>>> elNode.updateEndTimes()


Now let's look at the ElementNode:

>>> elNode
<ElementNode: Start:4.0 <0.20...> Indices:(l:0 *1* r:3) Payload:<music21.note.Note C>>

elNode is at 4.0.  With it and underneath it are nodes from 0 <= nodes < 3.  This
seemingly screwy way of counting is exactly slice notation: [0:3].

So if we wanted to get a certain index number we would know that index 0 is in the
leftChild's payload, index 2 is in the rightChild's payload, and index 1 is this node's
payload.  This information is easy to compute for ElementNodes, since each node holds
exactly one element (since the position here is a sortTuple which is unique); for its
subclass, OffsetNode, where multiple elements can be in a node, this will become
much more important and harder to work with.

Here is how you can actually get this information.

>>> elNode.subtreeElementsStartIndex
0
>>> elNode.subtreeElementsStopIndex
3

If they've never been set then they return -1, but the .updateIndices() call above
set these number for the children also. So they return the node's own index and one above:

>>> n2ElNode.subtreeElementsStartIndex
2
>>> n2ElNode.subtreeElementsStopIndex
3

Now here is the thing that actually makes trees based on ElementNodes useful.  Each node
knows the lowest and highest endTime as well as offset among the nodes below it:

>>> elNode.lowestPosition.offset
0.0
>>> elNode.endTimeLow
4.0
>>> elNode.highestPosition.offset
6.0
>>> elNode.endTimeHigh
9.0

What's so great about this?  If you're doing a "getElementsByOffset" search, you
can know from this information whether there's going to be an element whose start or
end time will possibly match the offset span without needing to descend into the tree.

The last element in a Stream often doesn't has the highest endTime (think of different
voices, flat streams, etc.) so searches for offsets are often O(n) when
they could be O(log n) if the information were cached into a tree as this does.
)endTimeHigh
endTimeLowpayloadElementIndexsubtreeElementsStartIndexsubtreeElementsStopIndexzb
            The index in a stream of the element stored in the payload of this node.
            z]
            The highest endTime of any node in the subtree rooted on this node.
            z\
            The lowest endTime of any node in the subtree rooted on this node.
            z
            The lowest element index of an element in the payload of any node of the
            subtree rooted on this node.
            z
            The highest element index of an element in the payload of any node of the
            subtree rooted on this node.
            )r   r
   r   r   r   dict[str, str]	_DOC_ATTRc                j   > [         TU ]  X5        SU l        S U l        S U l        SU l        SU l        g N)super__init__r   r
   r   r   r   )selfpositionpayload	__class__s      K/home/james-whalen/.local/lib/python3.13/site-packages/music21/tree/node.pyr   ElementNode.__init__   s7    +#% )+&(*%    c                    U R                   n[        US5      (       a  UR                  5       nSR                  UU R                  U R
                  U R                  U R                  5      $ )N	shortReprz=<ElementNode: Start:{} Indices:(l:{} *{}* r:{}) Payload:{!r}>)r   hasattrr   formatr   r   r   r   )r   poss     r   __repr__ElementNode.__repr__   s[    mm3$$--/CNUU**$$))LL
 	
r   c                `    U R                   c  U R                  $ U R                   R                  $ )z>
Returns the lowest position in the tree rooted on this node.
)	leftChildr   lowestPositionr   s    r   r&   ElementNode.lowestPosition   s(    
 >>!== >>000r   c                `    U R                   c  U R                  $ U R                   R                  $ )z?
Returns the highest position in the tree rooted on this node.
)
rightChildr   highestPositionr'   s    r   r+   ElementNode.highestPosition   s(    
 ??"== ??222r   c                   U R                   bP  U R                   R                  US9  U R                   R                  U l        U R                   R                  U l        OUc  SU l        SU l        OXl        Xl        U R
                  c  SnOSnU R                  U-   U l        U R                  b?  U R                  R                  U R                  S9  U R                  R                  U l        gg)a  
Updates the payloadElementIndex, and the subtreeElementsStartIndex and
subtreeElementsStopIndex (and does so for all child nodes) by traversing
the tree structure.

Updates cached indices which keep
track of the index of the element stored at each node, and of the
minimum and maximum indices of the subtrees rooted at each node.

Called on rootNode of a tree that uses ElementNodes, such as ElementTree

parentStopIndex specifies the stop index of the parent node, if known.
It is used internally by the function.

Returns None.
NparentStopIndexr      )r%   updateIndicesr   r   r   r   r*   )r   r/   
payloadLens      r   r1   ElementNode.updateIndices   s    " >>%NN(((I'+~~'N'ND$-1^^-U-UD*$'(D$-.D*'6$-<*<<JJ(,(@(@:(M%??&OO))$:W:W)X,0OO,T,TD) 'r   c                F    U R                   R                  nUnU R                  nU(       a:  UR                  5         [        XR                  5      n[        X$R                  5      nU R                  nU(       a:  UR                  5         [        XR                  5      n[        X%R                  5      nXl        X l        g! [         aU    U R                  n[	        U[
        5      (       a  UR                  nX0R                   R                  R                  -   nUn GNf = f)z
Traverses the tree structure and updates cached maximum and minimum
endTime values for the subtrees rooted at each node.

Used internally by ElementTree.

Returns None.
N)r   endTimeAttributeErrorr   
isinstancer   offsetdurationquarterLengthr%   updateEndTimesminr   maxr
   r*   )r   r   r
   r!   r%   r*   s         r   r;   ElementNode.updateEndTimes   s    	%--J$K NN	$$&Z)=)=>Jk+@+@AK__
%%'Z)>)>?Jk+A+ABK$&'  	%--C#y))jj||44BBBJ$K	%s   C AD D N)__name__
__module____qualname____firstlineno____doc__	__slots__r   __annotations__r   r"   propertyr&   r+   r1   r;   __static_attributes____classcell__r   s   @r   r   r      s{    k\I &%!I~ ,+

 1 1 3 3#UJ' 'r   r   c                  h   ^  \ rS rSr% SrSrSSSS.rS\S	'   SU 4S
 jjrS r	SS jr
S rS rSrU =r$ )
OffsetNodei   aL  
A node representing zero, one, or many elements at an offset.  It has all the
power of an ElementNode but substantially more.

Here's an example of what it means and does:

>>> score = tree.makeExampleScore()
>>> sf = score.flatten()
>>> sf.show('text', addEndTimes=True)
{0.0 - 0.0} <music21.instrument.Instrument 'PartA: : '>
{0.0 - 0.0} <music21.instrument.Instrument 'PartB: : '>
{0.0 - 0.0} <music21.clef.BassClef>
{0.0 - 0.0} <music21.clef.BassClef>
{0.0 - 0.0} <music21.meter.TimeSignature 2/4>
{0.0 - 0.0} <music21.meter.TimeSignature 2/4>
{0.0 - 1.0} <music21.note.Note C>
{0.0 - 2.0} <music21.note.Note C#>
{1.0 - 2.0} <music21.note.Note D>
{2.0 - 3.0} <music21.note.Note E>
{2.0 - 4.0} <music21.note.Note G#>
{3.0 - 4.0} <music21.note.Note F>
{4.0 - 5.0} <music21.note.Note G>
{4.0 - 6.0} <music21.note.Note E#>
{5.0 - 6.0} <music21.note.Note A>
{6.0 - 7.0} <music21.note.Note B>
{6.0 - 8.0} <music21.note.Note D#>
{7.0 - 8.0} <music21.note.Note C>
{8.0 - 8.0} <music21.bar.Barline type=final>
{8.0 - 8.0} <music21.bar.Barline type=final>

>>> scoreTree = tree.fromStream.asTimespans(sf, flatten=False, classList=None)
>>> rn = scoreTree.rootNode

The RootNode here represents the starting position of the Note F at 3.0; It is the center
of the elements in the flat Stream.  Its index is 5 (that is, it's the sixth note in the
element list) and its offset is 3.0

>>> rn
<OffsetNode 3.0 Indices:0,11,12,20 Length:1>
>>> sf[11]
<music21.note.Note F>
>>> sf[11].offset
3.0

Thus, the indices of 0:5:6:12 indicate that the left-side of the node handles indices
from >= 0 to < 5; and the right-side of the node handles indices >= 6 and < 12, and this node
handles indices >= 5 and < 6.

The `Length: {1}` indicates that there is exactly one element at this location, that is,
the F.

The "payload" of the node, is just that element wrapped in a list wrapped in an
ElementTimespan or PitchedTimespan:

>>> rn.payload
[<PitchedTimespan (3.0 to 4.0) <music21.note.Note F>>]
>>> rn.payload[0].element
<music21.note.Note F>
>>> rn.payload[0].element is sf[11]
True


We can look at the leftChild of the root node to get some more interesting cases:

>>> left = rn.leftChild
>>> left
<OffsetNode 1.0 Indices:0,8,9,11 Length:1>

In the leftNode of the leftNode of the rootNode there are eight elements:
metadata and both notes that begin on offset 0.0:

>>> leftLeft = left.leftChild
>>> leftLeft
<OffsetNode 0.0 Indices:0,0,8,8 Length:8>

>>> leftLeft.payload
[<ElementTimespan (0.0 to 0.0) <music21.instrument.Instrument 'PartA: : '>>,
 <ElementTimespan (0.0 to 0.0) <music21.instrument.Instrument 'PartB: : '>>,
 <ElementTimespan (0.0 to 0.0) <music21.clef.BassClef>>,
 <ElementTimespan (0.0 to 0.0) <music21.clef.BassClef>>,
 <ElementTimespan (0.0 to 0.0) <music21.meter.TimeSignature 2/4>>,
 <ElementTimespan (0.0 to 0.0) <music21.meter.TimeSignature 2/4>>,
 <PitchedTimespan (0.0 to 1.0) <music21.note.Note C>>,
 <PitchedTimespan (0.0 to 2.0) <music21.note.Note C#>>]

The Indices:0,0,8,8 indicates that `leftLeft` has neither left nor right children

>>> leftLeft.leftChild is None
True
>>> leftLeft.rightChild is None
True

What makes an OffsetNode more interesting than other AWL Nodes is that it is aware of
the fact that it might have objects that end at different times, such as the zero-length
metadata and the 2.0 length half note

>>> leftLeft.endTimeLow
0.0
>>> leftLeft.endTimeHigh
2.0
)payloadElementsStartIndexpayloadElementsStopIndexa  
            The contents of the node at this point.  Usually a list of ElementTimespans
            or PitchedTimespans.

            >>> score = tree.makeExampleScore()
            >>> scoreTree = tree.fromStream.asTimespans(score, flatten=True,
            ...                  classList=(note.Note, chord.Chord))
            >>> print(scoreTree.rootNode.debug())
            <OffsetNode 3.0 Indices:0,5,6,12 Length:1>
                L: <OffsetNode 1.0 Indices:0,2,3,5 Length:1>
                    L: <OffsetNode 0.0 Indices:0,0,2,2 Length:2>
                    R: <OffsetNode 2.0 Indices:3,3,5,5 Length:2>
                R: <OffsetNode 5.0 Indices:6,8,9,12 Length:1>
                    L: <OffsetNode 4.0 Indices:6,6,8,8 Length:2>
                    R: <OffsetNode 6.0 Indices:9,9,11,12 Length:2>
                        R: <OffsetNode 7.0 Indices:11,11,12,12 Length:1>

            >>> scoreTree.rootNode.payload
            [<PitchedTimespan (3.0 to 4.0) <music21.note.Note F>>]

            >>> scoreTree.rootNode.leftChild.payload
            [<PitchedTimespan (1.0 to 2.0) <music21.note.Note D>>]

            >>> for x in scoreTree.rootNode.leftChild.rightChild.payload:
            ...     x
            ...
            <PitchedTimespan (2.0 to 3.0) <music21.note.Note E>>
            <PitchedTimespan (2.0 to 4.0) <music21.note.Note G#>>

            >>> scoreTree.rootNode.rightChild.payload
            [<PitchedTimespan (5.0 to 6.0) <music21.note.Note A>>]
            z
            The timespan start index (i.e., the first x where s[x] is found in this Node's payload)
            of only those timespans stored in the payload of this node.
            z
            The timespan stop index (i.e., the last x where s[x] is found in this Node's payload)
            of only those timespans stored in the payload of this node.
            )r   rM   rN   r   r   c                N   > [         TU ]  U5        / U l        SU l        SU l        g r   )r   r   r   rM   rN   )r   r8   r   r   s      r   r   OffsetNode.__init__  s'     )+&(*%r   c           
         U R                   nU R                  nU R                  nU R                  nSU R                   S3nUSU SU SU SU S3	-  nUS[        U R                  5       S3-  nU$ )Nz<OffsetNode  zIndices:,zLength:>)r   rM   rN   r   r   lenr   )r   subStartpayStartpayEndsubEndmsgs         r   r"   OffsetNode.__repr__  s    1111....T]]O1-(1XJaxqBBT\\*+1--
r   c                   U R                   bP  U R                   R                  US9  U R                   R                  U l        U R                   R                  U l        OUc  SU l        SU l        OXl        Xl        U R                  [        U R                  5      -   U l        U R                  U l        U R                  b?  U R                  R                  U R                  S9  U R                  R                  U l        gg)a  
Updates the payloadElementsStartIndex, the payloadElementsStopIndex
and the subtreeElementsStartIndex and
subtreeElementsStopIndex (and does so for all child nodes) by traversing
the tree structure.

Updates cached indices which keep
track of the index of the element stored at each node, and of the
minimum and maximum indices of the subtrees rooted at each node.

Called on rootNode of a tree that uses OffsetNodes, such as OffsetTree
or TimespanTree

Returns None.
Nr.   r   )	r%   r1   r   rM   r   rU   r   rN   r*   )r   r/   s     r   r1   OffsetNode.updateIndices  s      >>%NN(((I-1^^-T-TD*-1^^-U-UD*$-.D*-.D*-<*-<*(,(F(FT\\IZ(Z%(,(E(E%??&OO))$:W:W)X,0OO,T,TD) 'r   c                    [        S U R                   5       5      n[        S U R                   5       5      nU R
                  nU(       a:  UR                  5         [        XR                  5      n[        X#R                  5      nU R                  nU(       a:  UR                  5         [        XR                  5      n[        X$R                  5      nXl        X l        g! [         aV    U R                  [        S U R                   5       5      -   nU R                  [        S U R                   5       5      -   n GNf = f)z
Traverses the tree structure and updates cached maximum and minimum
endTime values for the subtrees rooted at each node.

Used internally by OffsetTree.

Returns None
c              3  8   #    U  H  oR                   v   M     g 7fr?   r5   .0xs     r   	<genexpr>,OffsetNode.updateEndTimes.<locals>.<genexpr>  s     =1YY   c              3  8   #    U  H  oR                   v   M     g 7fr?   r`   ra   s     r   rd   re     s     >Aiirf   c              3  L   #    U  H  oR                   R                  v   M     g 7fr?   r9   r:   ra   s     r   rd   re     s     ,\|!ZZ-E-E|   "$c              3  L   #    U  H  oR                   R                  v   M     g 7fr?   ri   ra   s     r   rd   re     s     -]P\1jj.F.FP\rj   N)
r<   r   r=   r6   r   r%   r;   r   r
   r*   )r   r   r
   r%   r*   s        r   r;   OffsetNode.updateEndTimes  s    	^===J>>>K
 NN	$$&Z)=)=>Jk+@+@AK__
%%'Z)>)>?Jk+A+ABK$&!  	^,\t||,\)\\J--#-]PTP\P\-]*]]K	^s   8C! !AE Ec                    / nU R                    Hg  n[        U[        5      (       a4  UR                  U R                  UR
                  R                  -   5        ML  UR                  UR                  5        Mi     U$ )a  
returns a (potentially unsorted) list of all the end times for all TimeSpans or
Elements in the payload.  Does not trust el.endTime because it might refer to a
different offset.  Rather, it takes the position and adds it to the
duration.quarterLength.

>>> offsetNode = tree.node.OffsetNode(40)
>>> n = note.Note()
>>> offsetNode.payload.append(n)
>>> ts = tree.spans.Timespan(40, 44)
>>> offsetNode.payload.append(ts)
>>> offsetNode.payloadEndTimes()
[41.0, 44.0]
)r   r7   r   appendr   r9   r:   r5   )r   outEndTimestsOrEls      r   payloadEndTimesOffsetNode.payloadEndTimes  s_     llF&-00""4==6??3P3P#PQ""6>>2	 #
 r   )r
   r   r   rM   rN   r   r   r?   )r@   rA   rB   rC   rD   rE   r   rF   r   r"   r1   r;   rq   rH   rI   rJ   s   @r   rL   rL      sQ    dPI@&%K)!I~ )V+	U@'< r   rL   c                      \ rS rSrSrg)Testi&   N)r@   rA   rB   rC   rH   ru   r   r   rt   rt   &  s    r   rt   __main__)rD   
__future__r   unittestmusic21.treer   music21.baser   music21.sortingr   AVLNoder   rL   TestCasert   
_DOC_ORDERr@   music21mainTestru   r   r   <module>r      sy    #   & %@'$,, @'HA AL	8 	 :&
 z r   