
    rht                    P   S r SSKJr  SSKrSSKJr  SSKrSSKrSSK	J
r
  SSKJr  SSKJr  SSKJrJrJr  SS	KJr  SS
KJr  SSKJrJr  SSKJrJr  \R8                  (       a  SSKJr   " S S\
5      r " S S\R@                  5      r!\"S:X  a  SSKr\RF                  " \!5        gg)a  
the Stream Core Mixin handles the core attributes of streams that
should be thought of almost as private values and not used except
by advanced programmers who need the highest speed in programming.

Nothing here promises to be stable.  The music21 team can make
any changes here for efficiency reasons while being considered
backwards compatible so long as the public methods that call these
methods remain stable.

All functions here will eventually begin with `.core`.
    )annotationsN)Fraction)Music21Object)OffsetSpecial)opFrac)OffsetQLOffsetQLSpecial
M21ObjType)spanner)tree)StreamExceptionImmutableStreamException)StreamIteratorRecursiveIterator)Streamc                  J  ^  \ rS rSrSrSU 4S jjrSSS.     SS jjrSS.   S S	 jjrSSS
.     S!S jjrSSSSS.         S"S jjr	SSS.   S#S jjr
S rSS.S jrS$S jr\S 5       rSSS.S jrS rSSSSS.S jrSSSSS.         S%S jjrSrU =r$ )&
StreamCore-   z
Core aspects of a Stream's behavior.  Any of these can change at any time.
Users are encouraged only to create stream.Stream objects.
c                l   > [         TU ]  " S0 UD6  0 U l        / U l        / U l        SU l        SU l        g )NT )super__init___offsetDict	_elements_endElementsisSortedisFlat)selfkeywords	__class__s     M/home/james-whalen/.local/lib/python3.13/site-packages/music21/stream/core.pyr   StreamCore.__init__2   s@    $8$
 NP /1 24    FT)
ignoreSortsetActiveSitec                  SnU(       d}  U R                   SL an  U R                  nXa:  a  SnOZXa:X  aU  U R                  (       d  SnOAU R                  S   R                  5       nUR                  5       R	                  US9nXx:  a  SnU R                  U[        U5      SUS9  UR                  R                  U 5        U R                  R                  U5        U$ )a  
N.B. -- a "core" method, not to be used by general users.  Run .insert() instead.

A faster way of inserting elements that performs no checks,
just insertion.

Only be used in contexts that we know we have a proper, single Music21Object.
Best for usage when taking objects in a known Stream and creating a new Stream

When using this method, the caller is responsible for calling Stream.coreElementsChanged
after all operations are completed.

Do not mix coreInsert with coreAppend operations.

Returns boolean if the Stream (assuming it was sorted before) is still guaranteed
to be sorted.  (False doesn't mean that it's not sorted, just that we can't guarantee it.)
If you don't care and plan to sort the stream later, then use `ignoreSort=True`.
FT)offset
addElementr%   )
r   highestTimer   	sortTuplemodifycoreSetElementOffsetfloatsitesaddappend)	r   r(   elementr$   r%   storeSortedhthighestSortTuplethisSortTuples	            r!   
coreInsertStreamCore.coreInsertH   s    < 
 }}$%%;"&K\>>&*+/>>"+=+G+G+I((/(9(9(;(B(B&(B(Q+;*.K!!&M'	 	" 	
 	$ 	g&r#   )r%   c               &   U R                   nU R                  XSS9  UR                  R                  U 5        U(       a  U R	                  U5        U R
                  R                  U5        U R                  X1R                  R                  -   5        g)aB  
N.B. -- a "core" method, not to be used by general users.  Run .append() instead.

Low level appending; like `coreInsert` does not error check,
determine elements changed, or similar operations.

When using this method, the caller is responsible for calling
Stream.coreElementsChanged after all operations are completed.
Tr*   N)
r+   r.   r0   r1   coreSelfActiveSiter   r2   _setHighestTimedurationquarterLength)r   r3   r%   r5   s       r!   
coreAppendStreamCore.coreAppend   sv    " !!'$!?$##G,g&
 	R"2"2"@"@@Ar#   r)   c               ,    [        U5      n[	        U5      nU(       d!  XPR
                  ;  a  [        SU SU  S35      eX!4U R
                  U'   U(       a  U R                  U5        gg! [         a    U[        ;  a  [        SU< SU 35      e Nf = f)aN  
Sets the Offset for an element, very quickly.
Caller is responsible for calling :meth:`~music21.stream.core.coreElementsChanged`
afterward.

>>> s = stream.Stream()
>>> s.id = 'Stream1'
>>> n = note.Note('B-4')
>>> s.insert(10, n)
>>> n.offset
10.0
>>> s.coreSetElementOffset(n, 20.0)
>>> n.offset
20.0
>>> n.getOffsetBySite(s)
20.0
zCannot set offset to z for z"Cannot set the offset for element z, not in Stream .N)r   	TypeErrorr   r   idr   r<   )r   r3   r(   r*   r%   idEls         r!   r.   StreamCore.coreSetElementOffset   s    6	XF^F
 '{d*:*::!4WI=MdVSTUW W"(!2##G,   	X]*%(=fZuWI&VWW +	Xs   A* *&BBN)updateIsFlatclearIsSortedmemo	keepIndexc                  [        U SS5      (       d  [        S5      eUc  / n[        U 5      U;   a  gUR                  [        U 5      5        U R                  bW  U R                  R
                  nUS;   a;  [        R                  " SU R                  R                  5      nUR                  5         U R                   H  nUR                  US9  M     U(       a  SU l        U(       a4  SU l        U R                   H  nUR                  (       d  M  SU l          O   U R                   (       aS  Sn	U(       a  S	U R                   ;   a  U R                   S	   n	U R                  5         U(       a  U	b  XR                   S	'   gggg)
a-  
NB -- a "core" stream method that is not necessary for most users.

This method is called automatically any time the elements in the Stream are changed.
However, it may be called manually in case sites or other advanced features of an
element have been modified.  It was previously a private method and for most users
should still be treated as such.

The various arguments permit optimizing the clearing of cached data in situations
when completely dropping all cached data is excessive.

>>> a = stream.Stream()
>>> a.isFlat
True

Here we manipulate the private `._elements` storage (which generally shouldn't
be done) using coreAppend and thus need to call `.coreElementsChanged` directly.

>>> a.coreAppend(stream.Stream())
>>> a.isFlat  # this is wrong.
True

>>> a.coreElementsChanged()
>>> a.isFlat
False
_mutableTzBcoreElementsChanged should not be triggered on an immutable streamN)flatsemiflatzmusic21.stream.Stream)rJ   Findex)getattrr   rE   r2   _derivationmethodtcastorigin
clearCacher0   coreElementsChangedr   r   r   isStream_cache)
r   rH   rI   rJ   rK   sdmrV   
livingSitee
indexCaches
             r!   rX   StreamCore.coreElementsChanged   sT   F tZ..*T  <Dd8tBtH '""))C**23&&9P9=9I9I9P9P3R!!#
 **J***5 % !DMDK^^ :::"'DK $ ;;JW3![[1
 OOZ3'1G$ 4y r#   )recursedeepc               
   SSK Jn  U(       a  [        R                  " U 5      nO[        R                  " U 5      nXR                  l        U(       a1  U(       a*  [        XTR                  5      (       a  UR                  USS9  U$ )a[  
*This is a Core method that most users will not need to use.*

Make a copy of this stream with the proper derivation set.

>>> s = stream.Stream()
>>> n = note.Note()
>>> s.append(n)
>>> s2 = s.coreCopyAsDerivation('exampleCopy')
>>> s2.derivation.method
'exampleCopy'
>>> s2.derivation.origin is s
True
>>> s2[0].derivation.method
'exampleCopy'
r   )streamT)r`   )	music21rc   copydeepcopy
derivationrS   
isinstancer   setDerivationMethod)r   
methodNamer`   ra   rc   posts         r!   coreCopyAsDerivationStreamCore.coreCopyAsDerivation2  sZ    ( 	#==&D99T?D!+t
4 ? ?$$Z$>r#   c                    U R                    H  n[        U5      U:X  d  M  Us  $    U R                   H  n[        U5      U:X  d  M  Us  $    g)a  
NB -- a "core" stream method that is not necessary for most users.

Low-level tool to get an element based only on the object id.

This is not the same as getElementById, which refers to the id
attribute which may be manually set and not unique.

However, some implementations of python will reuse object locations, sometimes
quickly, so don't keep these around.

Used by spanner and variant.

>>> s = stream.Stream()
>>> n1 = note.Note('g')
>>> n2 = note.Note('g#')
>>> s.append(n1)
>>> s.coreGetElementByMemoryLocation(id(n1)) is n1
True
>>> s.coreGetElementByMemoryLocation(id(n2)) is None
True
>>> b = bar.Barline()
>>> s.storeAtEnd(b)
>>> s.coreGetElementByMemoryLocation(id(b)) is b
True
N)r   rE   r   )r   objIdr]   s      r!   coreGetElementByMemoryLocation)StreamCore.coreGetElementByMemoryLocationR  sI    : A!u~   ""A!u~ # r#   )checkRedundancyc                  XL a  [        S5      e[        U[        5      (       d6  [        U[        5      (       a  [        S5      e[        SU< S3S-   S-   5      eU(       a  [	        U5      nX0R
                  ;   ai  U R                  U R                  4 H@  nU H7  nXQL d  M	  [        SU< S[	        U5       S	3S
U < S[	        U 5       S3-   5      e   MB     U R
                  U	 UR                  5         g)a  
Before adding an element, this method performs
important checks on that element.

Used by:

  - :meth:`~music21.stream.Stream.insert`
  - :meth:`~music21.stream.Stream.append`
  - :meth:`~music21.stream.Stream.storeAtEnd`
  - `Stream.__init__()`

Returns None or raises a StreamException

>>> s = stream.Stream()
>>> s.coreGuardBeforeAddElement(s)
Traceback (most recent call last):
music21.exceptions21.StreamException: this Stream cannot be contained within itself

>>> s.append(s.iter())
Traceback (most recent call last):
music21.exceptions21.StreamException: cannot insert StreamIterator into a Stream
Iterate over it instead (User's Guide chs. 6 and 26)

>>> s.insert(4, 3.14159)
Traceback (most recent call last):
music21.exceptions21.StreamException: The object you tried to add to
the Stream, 3.14159, is not a Music21Object.  Use an ElementWrapper
object if this is what you intend.

z-this Stream cannot be contained within itselfz_cannot insert StreamIterator into a Stream
Iterate over it instead (User's Guide chs. 6 and 26)z+The object you tried to add to the Stream, z, z6is not a Music21Object.  Use an ElementWrapper object zif this is what you intend.zthe object (z, id()= z!is already found in this Stream ()N)	r   rh   r   r   rE   r   r   r   purgeLocations)r   r3   rr   	idElementsearch_place	eInStreams         r!   coreGuardBeforeAddElement$StreamCore.coreGuardBeforeAddElementx  s   > ?!"QRR'=11'>22%KL L "=g[KJK/01 1 7I,,, &*^^T5F5F$GL%1	$/"1".wkGQ O$EdXWUWX\U]T^^_"`!a#  &2 %H $$Y/
 	 r#   c                    U R                  U[        R                  SS9  UR                  R	                  U 5        U(       a  U R                  U5        U R                  R                  U5        g)z
NB -- this is a "core" method.  General users should use .storeAtEnd() instead.

Core method for adding end elements.
To be called by other methods.
Tr;   N)r.   r   AT_ENDr0   r1   r<   r   r2   )r   r3   r%   s      r!   coreStoreAtEndStreamCore.coreStoreAtEnd  sV     	!!'=+?+?D!Q$##G,  )r#   c                    SU R                   ;  d  U R                   S   cK  U R                  [        R                  4SS9n[        R                  " [        U5      5      U R                   S'   U R                   S   $ )zJ
A low-level object for Spanner management. This is a read-only property.
spannerBundleF)classFilterrestoreActiveSites)rZ   r`   r   SpannerSpannerBundlelist)r   spannerss     r!   r   StreamCore.spannerBundle  sd    
 $++-_1M1U||0BW\|]H+2+@+@h+PDKK({{?++r#   flatten	classListc                  [        [        U=(       d    S5      U45      nS[        U5      -   nX@R                  ;  d  U R                  U   c-  [        R
                  R                  U UUS9nXPR                  U'   U R                  U   $ )a  
Convert stream to a :class:`~music21.tree.trees.TimespanTree` instance, a
highly optimized data structure for searching through elements and
offsets.

>>> score = tree.makeExampleScore()
>>> scoreTree = score.asTimespans()
>>> print(scoreTree)
<TimespanTree {20} (0.0 to 8.0) <music21.stream.Score exampleScore>>
    <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.instrument.Instrument 'PartA: : '>>
    <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.instrument.Instrument 'PartB: : '>>
    <PitchedTimespan (0.0 to 1.0) <music21.note.Note C>>
    <PitchedTimespan (0.0 to 2.0) <music21.note.Note C#>>
    <PitchedTimespan (1.0 to 2.0) <music21.note.Note D>>
    <PitchedTimespan (2.0 to 3.0) <music21.note.Note E>>
    <PitchedTimespan (2.0 to 4.0) <music21.note.Note G#>>
    <PitchedTimespan (3.0 to 4.0) <music21.note.Note F>>
    <PitchedTimespan (4.0 to 5.0) <music21.note.Note G>>
    <PitchedTimespan (4.0 to 6.0) <music21.note.Note E#>>
    <PitchedTimespan (5.0 to 6.0) <music21.note.Note A>>
    <PitchedTimespan (6.0 to 7.0) <music21.note.Note B>>
    <PitchedTimespan (6.0 to 8.0) <music21.note.Note D#>>
    <PitchedTimespan (7.0 to 8.0) <music21.note.Note C>>
    <ElementTimespan (8.0 to 8.0) <music21.bar.Barline type=final>>
    <ElementTimespan (8.0 to 8.0) <music21.bar.Barline type=final>>
r   timespanTreer   )hashtuplestrrZ   r   
fromStreamasTimespans)r   r   r   hashedAttributescacheKeyhashedTimespanTrees         r!   r   StreamCore.asTimespans  s    >  yB!7 AB!C(8$99;;&$++h*?*G!%!<!<TELGP "= "R %7KK!{{8$$r#   c                    Xl         g)z}
Set the activeSite of el to be self.

Override for SpannerStorage, VariantStorage, which should never
become the activeSite
N)
activeSite)r   els     r!   r<   StreamCore.coreSelfActiveSite  s	     r#   r   r   useTimespansgroupOffsetsc               r   [         R                  (       a  [        U [        5      (       d   e[	        [        U=(       d    S5      UUU45      nS[        U5      -   nX`R                  ;  d  U R                  U   c/  [        R                  R                  U UUUUS9nXpR                  U'   U R                  U   $ )a  
Returns an elementTree of the score, using exact positioning.

See tree.fromStream.asTree() for more details.

>>> score = tree.makeExampleScore()
>>> scoreTree = score.asTree(flatten=True)
>>> scoreTree
<ElementTree {20} (0.0 <0.-25...> to 8.0) <music21.stream.Score exampleScore>>
r   elementTreer   )rT   TYPE_CHECKINGrh   r   r   r   r   rZ   r   r   asTree)r   r   r   r   r   r   r   hashedElementTrees           r!   r   StreamCore.asTree  s     ??dF++++yB!7")".".!0 1 !3'7#88;;&$++h*?*G $ 6 6t?FAJDPDP	 !7 !R
 %6KK!{{8$$r#   )r`   requireAllPresentinsertconstrainingSpannerBundlec                  U R                   nUSL a  U R                  5       nOU R                  5       n/ n[        U5       Hs  nUR	                  5        H\  n	X;   a  M
  X;   a  M  Ub  X;  a  M  U(       a)  Sn
U	R                  5        H  nX;  d  M
  Sn
  O   U
SL a  MK  UR                  U	5        M^     Mu     USL a  U$ U(       a*  U H  n	U R                  SU	5        M     U R                  SS9  g)a  
find all spanners that are referenced by elements in the
(recursed if recurse=True) stream and either inserts them in the Stream
(if insert is True) or returns them if insert is False.

If requireAllPresent is True (default) then only those spanners whose complete
spanned elements are in the Stream are returned.

Because spanners are stored weakly in .sites this is only guaranteed to find
the spanners in cases where the spanner is in another stream that is still active.

Here's a little helper function since we'll make the same Stream several times,
with two slurred notes, but without the slur itself.  Python's garbage collection
will get rid of the slur if we do not prevent it

>>> preventGarbageCollection = []
>>> def getStream():
...    s = stream.Stream()
...    n = note.Note('C')
...    m = note.Note('D')
...    sl = spanner.Slur(n, m)
...    preventGarbageCollection.append(sl)
...    s.append([n, m])
...    return s

Okay now we have a Stream with two slurred notes, but without the slur.
`coreGatherMissingSpanners()` will put it in at the beginning.

>>> s = getStream()
>>> s.show('text')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note D>
>>> s.coreGatherMissingSpanners()
>>> s.show('text')
{0.0} <music21.note.Note C>
{0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>
{1.0} <music21.note.Note D>

Now, the same Stream, but insert is False, so it will return a list of
Spanners that should be inserted, rather than inserting them.

>>> s = getStream()
>>> spList = s.coreGatherMissingSpanners(insert=False)
>>> spList
[<music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>]
>>> s.show('text')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note D>


Now we'll remove the second note so not all elements of the Slur
are present. This, by default, will not insert the Slur:

>>> s = getStream()
>>> s.remove(s[-1])
>>> s.show('text')
{0.0} <music21.note.Note C>
>>> s.coreGatherMissingSpanners()
>>> s.show('text')
{0.0} <music21.note.Note C>

But with `requireAllPresent=False`, the spanner appears!

>>> s.coreGatherMissingSpanners(requireAllPresent=False)
>>> s.show('text')
{0.0} <music21.note.Note C>
{0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>

With `recurse=False`, then spanners are not gathered inside the inner
stream:

>>> part = stream.Part()
>>> s = getStream()
>>> part.insert(0, s)
>>> part.coreGatherMissingSpanners(recurse=False)
>>> part.show('text')
{0.0} <music21.stream.Stream 0x104935b00>
    {0.0} <music21.note.Note C>
    {1.0} <music21.note.Note D>


But the default acts with recursion:

>>> part.coreGatherMissingSpanners()
>>> part.show('text')
{0.0} <music21.stream.Stream 0x104935b00>
    {0.0} <music21.note.Note C>
    {1.0} <music21.note.Note D>
{0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>


Spanners already in the stream are not put there again:

>>> s = getStream()
>>> sl = s.notes.first().getSpannerSites()[0]
>>> sl
<music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>
>>> s.insert(0, sl)
>>> s.coreGatherMissingSpanners()
>>> s.show('text')
{0.0} <music21.note.Note C>
{0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>
{1.0} <music21.note.Note D>

Also does not happen with recursion.

>>> part = stream.Part()
>>> s = getStream()
>>> sl = s.notes.first().getSpannerSites()[0]
>>> s.insert(0, sl)
>>> part.insert(0, s)
>>> part.coreGatherMissingSpanners()
>>> part.show('text')
{0.0} <music21.stream.Stream 0x104935b00>
    {0.0} <music21.note.Note C>
    {0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>
    {1.0} <music21.note.Note D>

If `constrainingSpannerBundle` is set then only spanners also present in
that spannerBundle are added.  This can be useful, for instance, in restoring
spanners from an excerpt that might already have spanners removed.  In
Jacob Tyler Walls's brilliant phrasing, it prevents regrowing zombie spanners
that you thought you had killed.

Here we will constrain only to spanners also present in another Stream:

>>> s = getStream()
>>> s2 = stream.Stream()
>>> s.coreGatherMissingSpanners(constrainingSpannerBundle=s2.spannerBundle)
>>> s.show('text')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Note D>

Now with the same constraint, but we will put the Slur into the other stream.

>>> sl = s.notes.first().getSpannerSites()[0]
>>> s2.insert(0, sl)
>>> s.coreGatherMissingSpanners(constrainingSpannerBundle=s2.spannerBundle)
>>> s.show('text')
{0.0} <music21.note.Note C>
{0.0} <music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>
{1.0} <music21.note.Note D>
TNFr   )rH   )	r   r`   iterr   getSpannerSitesgetSpannedElementsr2   r8   rX   )r   r`   r   r   r   sbsItercollectListr   spallFoundspannedElements               r!   coreGatherMissingSpanners$StreamCore.coreGatherMissingSpanners  s    n d?LLNEIIKEu+B((*8$,8R=`$#H*,*?*?*A)6',H! +B  5( ""2& + $ U?!2& "$$%$8r#   )r   r   r   r   r   )returnNone)r(   r   r3   r   r   bool)r3   r   r   r   )r3   r   r(   z&int | float | Fraction | OffsetSpecialr   r   )
rH   r   rI   r   rJ   zlist[int] | NonerK   r   r   r   )r   r
   rj   r   r   r
   )T)
r`   r   r   r   r   r   r   zspanner.SpannerBundle | Noner   zlist[spanner.Spanner] | None)__name__
__module____qualname____firstlineno____doc__r   r8   r@   r.   rX   rl   rp   rz   r~   propertyr   r   r<   r   r   __static_attributes____classcell__)r    s   @r!   r   r   -   s   6 == = 
=F 	BB
 
BL (-(- 1(- 
(-Z ""#]2 ]2 	]2
 ]2 ]2 
]2D &*"&), ,6@#L EI ?!B*  , , &*T &%P !&EX] %< "&@Dx x  	x
 x $>x 
$x xr#   r   c                      \ rS rSrSrg)Testi  r   N)r   r   r   r   r   r   r#   r!   r   r     s    r#   r   __main__)$r   
__future__r   re   	fractionsr   typingrT   unittestmusic21.baser   music21.common.enumsr   music21.common.numberToolsr   music21.common.typesr   r	   r
   rd   r   r   music21.exceptions21r   r   music21.stream.iteratorr   r   r   music21.streamr   r   TestCaser   r   mainTestr   r#   r!   <module>r      s    #     & . - F F   J E ??%j
 j
j	8 	 zT r#   