
    rh                   t   S r SSKJr  SSKJr  SSKJr  SSK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  SS
KJr  SSKJr  SSKJr  \R&                  " S5      r " S S\R*                  5      r " S S\R*                  5      r\\\-  \\S-  \S-  \\S-  4   rS rS r " S S\R>                  5      r  " S S\RB                  5      r" " S S\RB                  \RF                  5      r$ " S S\R>                  5      r% " S S5      r& " S S\&5      r'\&\$\"/r(\)S :X  a  SSKr\RT                  " 5         gg)!a  
An IntervalNetwork defines a scale or harmonic unit as a (weighted)
digraph, or directed graph, where pitches are nodes and intervals are
edges. Nodes, however, are not stored; instead, an ordered list of edges
(Intervals) is provided as an archetype of adjacent nodes.

IntervalNetworks are unlike conventional graphs in that each graph must
define a low and high terminus. These points are used to create a cyclic
graph and are treated as point of cyclical overlap.

IntervalNetwork permits the definition of conventional octave repeating
scales or harmonies (abstract chords), non-octave repeating scales and
chords, and ordered interval sequences that might move in multiple
directions.

A scale or harmony may be composed of one or more IntervalNetwork objects.

Both nodes and edges can be weighted to suggest tonics, dominants,
finals, or other attributes of the network.

Changed in v8: nodeId and nodeName standardized.  TERMINUS and DIRECTION
are now Enums.
    )annotations)OrderedDict)SequenceN)common)environment)exceptions21)interval)note)pitch)prebasezscale.intervalNetworkc                  ,    \ rS rSrSrSrSrS rS rSr	g)	Terminus5   zI
One of the two Termini of a scale, either Terminus.LOW or
Terminus.HIGH
terminusLowterminusHighc                     SU R                   -   $ Nz	Terminus.nameselfs    W/home/james-whalen/.local/lib/python3.13/site-packages/music21/scale/intervalNetwork.py__repr__Terminus.__repr__=       TYY&&    c                     SU R                   -   $ r   r   r   s    r   __str__Terminus.__str__@   r   r    N)
__name__
__module____qualname____firstlineno____doc__LOWHIGHr   r   __static_attributes__r    r   r   r   r   5   s     CD''r   r   c                  0    \ rS rSrSrSrSrSrS rS r	Sr
g	)
	DirectionC   zx
An enumerated Direction for a scale, either Direction.ASCENDING,
Direction.DESCENDING, or Direction.BI (bidirectional)
bi	ascending
descendingc                     SU R                   -   $ Nz
Direction.r   r   s    r   r   Direction.__repr__L       dii''r   c                     SU R                   -   $ r0   r   r   s    r   r   Direction.__str__O   r2   r   r    N)r!   r"   r#   r$   r%   BI	ASCENDING
DESCENDINGr   r   r(   r    r   r   r*   r*   C   s"     
BIJ((r   r*   c                4    X:  a  g[        X-
  5      S:  a  gg)z(
check if a > b or abs(a - b) < epsilon
Th㈵>Fabsabs     r   _gter?   W         	u	QUg	r   c                4    X:  a  g[        X-
  5      S:  a  gg)z(
check if a < b or abs(a - b) < epsilon
Tr9   Fr:   r<   s     r   _lterB   b   r@   r   c                      \ rS rSrSrg)EdgeExceptionm   r    Nr!   r"   r#   r$   r(   r    r   r   rD   rD   m       r   rD   c                      \ rS rSrSrS\R                  4 SS jjrS rS r	 S     SS jjr
S r S   SS	 jjr\SS
 j5       rSrg)Edgeq   a  
Abstraction of an Interval as an Edge.

Edges store an Interval object as well as a pathway direction specification.
The pathway is the route through the network from terminus to terminus,
and can either by ascending or descending.

For directed Edges, the direction of the Interval may be used to
suggest non-pitch ascending movements (even if the pathway direction is ascending).

Weight values, as well as other attributes, can be stored.

>>> i = interval.Interval('M3')
>>> e = scale.intervalNetwork.Edge(i)
>>> e.interval is i
True
>>> e.direction
Direction.BI

Return the stored Interval object

>>> i = interval.Interval('M3')
>>> e1 = scale.intervalNetwork.Edge(i, id=0)
>>> n1 = scale.intervalNetwork.Node(id=0, degree=0)
>>> n2 = scale.intervalNetwork.Node(id=1, degree=1)
>>> e1.addDirectedConnection(n1, n2, scale.Direction.ASCENDING)
>>> e1.interval
<music21.interval.Interval M3>

Return the direction of the Edge.

>>> i = interval.Interval('M3')
>>> e1 = scale.intervalNetwork.Edge(i, id=0)
>>> n1 = scale.intervalNetwork.Node(id=0, degree=0)
>>> n2 = scale.intervalNetwork.Node(id=1, degree=1)
>>> e1.addDirectedConnection(n1, n2, scale.Direction.ASCENDING)
>>> e1.direction
Direction.ASCENDING
Nc                    [        U[        5      (       a  [        R                  " U5      nOUnX@l        X0l        SU l        X l        / U l        g )N      ?)
isinstancestrr	   Interval	directionweightid_connections)r   intervalDatarR   rP   is        r   __init__Edge.__init__   sH     lC((!!,/AA+,$- FHr   c                l    [        XR                  5      =(       a    U R                  UR                  :H  $ )a  
>>> i1 = interval.Interval('M3')
>>> i2 = interval.Interval('M3')
>>> i3 = interval.Interval('m3')
>>> e1 = scale.intervalNetwork.Edge(i1)
>>> e2 = scale.intervalNetwork.Edge(i2)
>>> e3 = scale.intervalNetwork.Edge(i3)
>>> e1 == e2
True
>>> e1 == e3
False
)rM   	__class____dict__r   others     r   __eq__Edge.__eq__   s*     5..1 4MMU^^3	5r   c                h    U R                    SU R                  R                   SU R                  < 3$ )N )rP   r	   r   rS   r   s    r   _reprInternalEdge._reprInternal   s0    ..!4==#5#5"6a8I8I7LMMr   c                N   [        U[        [        45      (       a  UnOUR                  n[        U[        [        45      (       a  UnOUR                  nU R                  R                  XE45        U[        R                  [        R                  4;  a  [        S5      eX0l
        g)ao  
Provide two Node objects that are connected by this Edge,
in the direction from the first to the second.

When calling directly, a direction, either
ascending or descending, should be set here;
this will override whatever the interval is.
If None, this will not be set.

>>> i = interval.Interval('M3')
>>> e1 = scale.intervalNetwork.Edge(i, id=0)

>>> n1 = scale.intervalNetwork.Node(id=0, degree=0)
>>> n2 = scale.intervalNetwork.Node(id=1, degree=1)

>>> e1.addDirectedConnection(n1, n2, scale.Direction.ASCENDING)
>>> e1.connections
[(0, 1)]
>>> e1
<music21.scale.intervalNetwork.Edge Direction.ASCENDING M3 [(0, 1)]>
zmust request a directionN)rM   r   intrR   rS   appendr*   r6   r7   rD   rP   )r   node1node2rP   n1Idn2Ids         r   addDirectedConnectionEdge.addDirectedConnection   s    8 eh_--D88Deh_--D88D  $. Y00)2F2FGG :;;"r   c                    U R                  X[        R                  5        U R                  X![        R                  5        [        R                  U l        g)a  
Provide two Edge objects that pass through
this Node, in the direction from the first to the second.

>>> i = interval.Interval('M3')
>>> e1 = scale.intervalNetwork.Edge(i, id=0)
>>> n1 = scale.intervalNetwork.Node(id=scale.Terminus.LOW, degree=0)
>>> n2 = scale.intervalNetwork.Node(id=1, degree=1)

>>> e1.addBiDirectedConnections(n1, n2)
>>> e1.connections
[(Terminus.LOW, 1), (1, Terminus.LOW)]
>>> e1
<music21.scale.intervalNetwork.Edge Direction.BI M3
    [(Terminus.LOW, 1), (1, Terminus.LOW)]>
N)rj   r*   r6   r7   r5   rP   )r   rf   rg   s      r   addBiDirectedConnectionsEdge.addBiDirectedConnections   s;    $ 	""51D1DE""51E1EF"r   c                
   Uc  U R                   nU R                   U:X  a  U R                  $ U[        R                  :X  a9  U R                   [        R                  [        R
                  4;   a  [        S5      eU[        R                  [        R
                  4;   af  U R                   [        R                  :X  aH  U[        R                  :X  a  U R                  S   /$ U[        R
                  :X  a  U R                  S   /$ / $ )a  
Callable as a property (.connections) or as a method
(.getConnections(direction)):

Return a list of connections between Nodes, represented as pairs
of Node ids. If a direction is specified, and if the Edge is
directional, only the desired directed values will be returned.

>>> i = interval.Interval('M3')
>>> e1 = scale.intervalNetwork.Edge(i, id=0)
>>> n1 = scale.intervalNetwork.Node(id=scale.Terminus.LOW, degree=1)
>>> n2 = scale.intervalNetwork.Node(id=1, degree=2)

>>> e1.addBiDirectedConnections(n1, n2)
>>> e1.connections
[(Terminus.LOW, 1), (1, Terminus.LOW)]
>>> e1.getConnections(scale.Direction.ASCENDING)
[(Terminus.LOW, 1)]
>>> e1.getConnections(scale.Direction.DESCENDING)
[(1, Terminus.LOW)]
z3cannot request a bi direction from a mono directionr      )rP   rS   r*   r5   r6   r7   rD   )r   rP   s     r   getConnectionsEdge.getConnections  s    2 I >>Y&$$$ %NNy':':I<P<P&QQ UVV )--y/C/CDDNNill2
 I///))!,--i222))!,--	r   c                "    U R                  5       $ N)rq   r   s    r   connectionsEdge.connections9  s    ""$$r   )rS   rP   rR   r	   rQ   )rT   zinterval.Interval | strrt   )rf   Node | int | Terminusrg   rw   returnNone)rP   zNone | Directionrx   +list[tuple[int | Terminus, int | Terminus]])rx   rz   )r!   r"   r#   r$   r%   r*   r5   rV   r]   ra   rj   rm   rq   propertyru   r(   r    r   r   rI   rI   q   s    &V $<<H4H&5 N 	+# +# !+#
 
+#Z&0 %)2!2 
12j % %r   rI   c                  <    \ rS rSrSrSrS
SS jjrS rS rS r	Sr
g	)Nodei>  a4  
Abstraction of an unrealized Pitch Node.

The Node `id` is used to store connections in Edges and has no real meaning.

Terminal Nodes have special ids: Terminus.LOW, Terminus.HIGH

The Node `degree` is translated to scale degrees in various applications,
and is used to request a pitch from the network.

The `weight` attribute is used to probabilistically select between
multiple nodes when multiple nodes satisfy either a branching option in a pathway
or a request for a degree.

TODO: replace w/ NamedTuple; eliminate id, and have a terminus: low, high, None
rR   degreerQ   c                (    Xl         X l        X0l        g rt   r~   )r   rR   r   rQ   s       r   rV   Node.__init__S  s     !# "#r   c                ^    U R                   U R                  U R                  4n[        U5      $ rt   )rR   r   rQ   hash)r   	hashTuples     r   __hash__Node.__hash__^  s$    WWdkk4;;7	Ir   c                0    [        U 5      [        U5      :H  $ )a  
Nodes are equal if everything in the object.__slots__ is equal.

>>> n1 = scale.intervalNetwork.Node(id=3, degree=1)
>>> n2 = scale.intervalNetwork.Node(id=3, degree=1)
>>> n3 = scale.intervalNetwork.Node(id=2, degree=1)
>>> n1 == n2
True
>>> n1 == n3
False
>>> n2.weight = 2.0
>>> n1 == n2
False

>>> n4 = scale.intervalNetwork.Node(id=scale.Terminus.LOW, degree=1)
>>> n5 = scale.intervalNetwork.Node(id=scale.Terminus.LOW, degree=1)
>>> n4 == n5
True
)r   r[   s     r   r]   Node.__eq__b  s    ( DzT%[((r   c                "    SU R                   < 3$ )Nzid=rR   r   s    r   ra   Node._reprInternalx  s    TWWK  r   )r   rR   rQ   N)rL   )rR   zTerminus | intr   rd   rQ   float)r!   r"   r#   r$   r%   	__slots__rV   r   r]   ra   r(   r    r   r   r}   r}   >  s"      +I	$),!r   r}   c                      \ rS rSrSrg)IntervalNetworkExceptioni}  r    NrF   r    r   r   r   r   }  rG   r   r   c                     \ rS rSr% Sr    S? S@S jjrS rS rS@S jrS	 r	S
 r
S rS r\S 5       r\S 5       r\S 5       r\SAS j5       r\S 5       rSBSCS jjrS rS rSDS jrSSS. SES jjrS rS r\R6                  SS. SFS jjr\R:                  SSSS.         SGS jjrSS .             SHS! jjr   SISSS".         SJS# jjjr    SISSSSS$.       SKS% jjjr!SSS\R:                  SS4         SLS& jjr"SSS\R:                  SS4           SMS' jjr#SSS\R:                  SS4         SNS( jjr$  SO     SPS) jjr%  SO     SPS* jjr&SS+SS\R:                  S4         SQS, jjr'S- r(S. r)S/\R:                  SS0.         SRS1 jjr*\R:                  S4       SSS2 jjr+S/\R:                  S4     STS3 jjr,\R:                  SSSS4     SUS4 jjr-\.    SVS5 j5       r/  SW SXS7 jjr0S6SS\R:                  S4   SYS8 jjr1S9r2S:\3S;'      SZS< jr4      S[S= jr5S>r6g)\IntervalNetworki  a0  
A graph of undefined Pitch nodes connected by a defined,
ordered list of :class:`~music21.interval.Interval` objects as edges.

An `octaveDuplicating` boolean, if defined, can be used
to optimize pitch realization routines.

The `deterministic` boolean, if defined, can be used to declare that there
is no probabilistic or multi-pathway segments of this network.

The `pitchSimplification` method specifies how to simplify the pitches
if they spiral out into double and triple sharps, etc.  The default is
'maxAccidental' which specifies that each note can have at most one
accidental; double-flats and sharps are not allowed.  The other choices
are 'simplifyEnharmonic' (which also converts C-, F-, B#, and E# to
B, E, C, and F respectively, see :meth:`~music21.pitch.Pitch.simplifyEnharmonic`),
'mostCommon' (which adds to simplifyEnharmonic the requirement that the
most common accidental forms be used, so A# becomes B-, G- becomes
F#, etc. the only ambiguity allowed is that both G# and A- are acceptable),
and None (or 'none') which does not do any simplification.
FTc                    SU l         SU l        [        5       U l        [        5       U l        U(       a  U R                  U5        X l        X0l        X@l        [        5       U l	        [        5       U l
        g )Nr   )edgeIdCountnodeIdCountr   edgesnodesfillBiDirectedEdgesoctaveDuplicatingdeterministicpitchSimplification_ascendingCache_descendingCache)r   edgeListr   r   r   s        r   rV   IntervalNetwork.__init__  sq      7Bm
 7Bm
$$X. "3* $7  M 	 M 	r   c                    SU l         SU l        [        5       U l        [        5       U l        [        5       U l        [        5       U l        g)z'
Remove and reset all Nodes and Edges.
r   N)r   r   r   r   r   r   r   r   s    r   clearIntervalNetwork.clear  s:      ]
 ]
*} +r   c                    [        XR                  5      (       d  gS H  n[        X5      [        X5      :w  d  M    g   g)a  

>>> edgeList1 = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> edgeList2 = ['M2', 'M2', 'm2', 'M2', 'A3', 'm2']

>>> net1 = scale.intervalNetwork.IntervalNetwork()
>>> net1.fillBiDirectedEdges(edgeList1)

>>> net2 = scale.intervalNetwork.IntervalNetwork()
>>> net2.fillBiDirectedEdges(edgeList1)

>>> net3 = scale.intervalNetwork.IntervalNetwork()
>>> net3.fillBiDirectedEdges(edgeList2)

>>> net1 == net2
True
>>> net1 == net3
False
F)r   r   r   r   r   r   r   T)rM   rY   getattr)r   r\   attrs      r   r]   IntervalNetwork.__eq__  s>    , %00RDt"ge&::R r   c                N   U R                  5         Sn[        [        R                  US9nUS-  nX0R                  UR
                  '   Un[        U5       H  u  pVU[        U5      S-
  :  a1  [        U R                  US9nU =R                  S-  sl        US-  nUnO[        [        R                  US9n	U	nXR                  UR
                  '   [        X`R                  S9n
XR                  U
R
                  '   U =R                  S-  sl        U
R                  XH5        UnM     g)a  
Given an ordered list of bi-directed edges given as :class:`~music21.interval.Interval`
specifications, create and define appropriate Nodes. This
assumes that all edges are bi-directed and all edges are in order.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.nodes
OrderedDict()
>>> net.edges
OrderedDict()


>>> net.fillBiDirectedEdges(edgeList)
>>> net.nodes
OrderedDict([(Terminus.LOW, <music21.scale.intervalNetwork.Node id=Terminus.LOW>),
             (0, <music21.scale.intervalNetwork.Node id=0>),
             (1, <music21.scale.intervalNetwork.Node id=1>),
             ...
             (5, <music21.scale.intervalNetwork.Node id=5>),
             (Terminus.HIGH, <music21.scale.intervalNetwork.Node id=Terminus.HIGH>)])
>>> net.edges
OrderedDict([(0, <music21.scale.intervalNetwork.Edge Direction.BI M2
                    [(Terminus.LOW, 0), (0, Terminus.LOW)]>),
             (1, <music21.scale.intervalNetwork.Edge Direction.BI M2 [(0, 1), (1, 0)]>),
             (2, <music21.scale.intervalNetwork.Edge Direction.BI m2 [(1, 2), (2, 1)]>),
             ...
             (5, <music21.scale.intervalNetwork.Edge Direction.BI M2 [(4, 5), (5, 4)]>),
             (6, <music21.scale.intervalNetwork.Edge Direction.BI m2
                    [(5, Terminus.HIGH), (Terminus.HIGH, 5)]>)])

>>> [str(p) for p in net.realizePitch('g4')]
['G4', 'A4', 'B4', 'C5', 'D5', 'E5', 'F#5', 'G5']
>>> net.degreeMin, net.degreeMax
(1, 8)

Using another fill method creates a new network

>>> net.fillBiDirectedEdges(['M3', 'M3', 'M3'])
>>> [str(p) for p in net.realizePitch('g4')]
['G4', 'B4', 'D#5', 'G5']
>>> net.degreeMin, net.degreeMax
(1, 4)

>>> net.fillBiDirectedEdges([interval.Interval('M3'),
...                          interval.Interval('M3'),
...                          interval.Interval('M3')])
>>> [str(p) for p in net.realizePitch('c2')]
['C2', 'E2', 'G#2', 'B#2']
rp   rR   r   r   N)r   r}   r   r&   r   rR   	enumeratelenr   r'   rI   r   r   rm   )r   r   degreeCountnLow	nPreviousrU   eNamen
nFollowingnHighes              r   r   #IntervalNetwork.fillBiDirectedEdges  s   h 	

x||K8q"

477	!(+HA 3x=1$$D,,[A  A% q 
 kB"
 )3JJz}}% U//0A JJqtt!&&y="I/ ,r   c                   U R                  5         [        U5      [        U5      :w  a  [        S5      eSn[        [        R
                  US9nUS-  nX@R                  UR                  '   Un[        U5       H  u  pgU[        U5      S-
  :  a1  [        U R                  US9nU =R                  S-  sl	        US-  nUn	O[        [        R                  US9n
U
n	XR                  U	R                  '   [        XpR                  S9nXR                  UR                  '   U =R                  S-  sl        UR                  XY[        R                   S9  U	nM     SnU R                  [        R
                     nUS-  nUn[        U5       H  u  pgU[        U5      S-
  :  aI  [        U R                  US9nU =R                  S-  sl	        US-  nUn	XR                  U	R                  '   OU R                  [        R                     n
U
n	[        XpR                  S9nXR                  UR                  '   U =R                  S-  sl        UR                  X[        R"                  S9  U	nM     g)a  
Given two lists of edges, one for ascending :class:`~music21.interval.Interval`
objects and
another for  descending, construct appropriate Nodes and Edges.

Note that the descending :class:`~music21.interval.Interval` objects
should be given in ascending form.
z*cannot manage unequal sized directed edgesrp   r   r   rP   N)r   r   r   r}   r   r&   r   rR   r   r   r'   rI   r   r   rj   r*   r6   r7   )r   ascendingEdgeListdescendingEdgeListr   r   r   rU   r   r   r   r   r   s               r   fillDirectedEdges!IntervalNetwork.fillDirectedEdgesN  s7    	

  !S);%<<*+WXXx||K8q"

477	!"34HA 3()A--D,,[A  A% q 
kB"
 )3JJz}}% U//0A JJqtt!##I.7.A.A $ C #I/ 56 zz(,,'q	!"45HA 3)*Q..D,,[A  A% q 
,6

:==)

8==1"
 U//0A JJqtt! ##JYEYEY#Z"I- 6r   c                   U R                  5         U H;  n[        US   US   S9nSU;   a
  US   Ul        X@R                  UR                  '   M=     SnU H  n[        US   US9nUS    Hs  u  pn
U
[        R                  :X  a.  UR                  U R                  U   U R                  U	   5        MH  UR                  U R                  U   U R                  U	   U
S	9  Mu     XpR                  UR                  '   US
-  nM     g)aR  
Fill any arbitrary network given node and edge definitions.

Nodes must be defined by a dictionary of id and degree values.
There must be a terminusLow and terminusHigh id as string::

    nodes = ({'id': Terminus.LOW, 'degree': 1},
             {'id': 0, 'degree': 2},
             {'id': Terminus.HIGH, 'degree': 3},
            )

Edges must be defined by a dictionary of :class:`~music21.interval.Interval`
strings and connections. Values for `id` will be automatically assigned.
Each connection must define direction and pairs of valid node ids::

    edges = ({'interval': 'm2',
              'connections': ([Terminus.LOW, 0, Direction.BI],)
              },
             {'interval': 'M3',
              'connections': ([0, Terminus.HIGH, Direction.BI],)
              },
            )


>>> nodes = ({'id': scale.Terminus.LOW, 'degree': 1},
...          {'id': 0, 'degree': 2},
...          {'id': scale.Terminus.HIGH, 'degree': 3})
>>> edges = ({'interval': 'm2',
...           'connections': ([scale.Terminus.LOW, 0, scale.Direction.BI],)},
...          {'interval': 'M3',
...           'connections': ([0, scale.Terminus.HIGH, scale.Direction.BI],)},)

>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillArbitrary(nodes, edges)
>>> net.realizePitch('c4', 1)
[<music21.pitch.Pitch C4>, <music21.pitch.Pitch D-4>, <music21.pitch.Pitch F4>]
rR   r   r   rQ   r   r	   r   ru   r   rp   N)r   r}   rQ   r   rR   rI   r*   r5   rm   rj   r   )r   r   r   nDictr   eIdeDictr   nId1nId2rP   s              r   fillArbitraryIntervalNetwork.fillArbitrary  s    N 	

EdE(O<A5  ? JJqtt	  EU:&3/A).})=%I 	,..tzz$/?DAQR++DJJt,<,0JJt,<	 , S *>  !JJqtt1HC r   c                   [         R                  SS.SSS.SSS.SSS.SSS.SSS.SSS.SS	S.S	S	S.[         R                  S
S.4
nS[         R                  S[        R                  /4S.SSS[        R                  /4S.SSS[        R                  /4S.SSS[        R                  /4S.SSS[        R
                  /4S.SSS[        R
                  /4S.SS[         R                  [        R
                  /4S.S[         R                  S	[        R                  /4S.SS	S[        R                  /4S.SSS[        R                  /4S.4
nU R                  X5        SU l        SU l	        g)z
A convenience routine for testing a complex, bi-directional scale.

>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillMelodicMinor()
>>> [str(p) for p in net.realizePitch('c4')]
['C4', 'D4', 'E-4', 'F4', 'G4', 'A4', 'B4', 'C5']
rp   r   r                        M2)r	   ru   m2TN)
r   r&   r'   r*   r5   r6   r7   r   r   r   )r   r   r   s      r   fillMelodicMinor IntervalNetwork.fillMelodicMinor  s    !2Q'Q'Q'Q'Q'Q'Q'Q' 3 ##+<<ILL"A!C ##$a"6!8 ##$a"6!8 ##$a"6!8 ##$a)<)<"=!? ##$a)<)<"=!? ##$hmmY5H5H"I!K ##+==!Y5I5I"J!L ##$a)=)=">!@ ##$a)=)=">!@7@ 	5(!%!r   c                    [        [        [        U5      5      5      nU Vs/ s H  oDR                  PM     nn[        R
                  " X55      nX   X&   4$ s  snf )a  
Perform weighted random selection on a parallel list of
edges and corresponding nodes.

>>> n1 = scale.intervalNetwork.Node(id=1, degree=1, weight=1000000)
>>> n2 = scale.intervalNetwork.Node(id=2, degree=1, weight=1)
>>> e1 = scale.intervalNetwork.Edge(interval.Interval('m3'), id=1)
>>> e2 = scale.intervalNetwork.Edge(interval.Interval('m3'), id=2)
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> e, n = net.weightedSelection([e1, e2], [n1, n2])

Note: this may fail as there is a slight chance to get 2

>>> e.id
1
>>> n.id
1
)listranger   rQ   r   weightedSelection)r   r   r   iValuesr   weightsrU   s          r   r   !IntervalNetwork.weightedSelection  sQ    ( uSZ()%*+U88U+$$W6x!!	 ,s   Ac                    SnU R                   R                  5        H)  nUc  UR                  nM  [        XR                  5      nM+     U$ )z
Return the lowest degree value.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.degreeMin
1
N)r   valuesr   minr   xr   s      r   	degreeMinIntervalNetwork.degreeMin0  C     ""$AyHH88$	 %
 r   c                    SnU R                   R                  5        H)  nUc  UR                  nM  [        XR                  5      nM+     U$ )z
Return the largest degree value.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.degreeMax    # returns eight, as this is the last node
8
N)r   r   r   maxr   s      r   	degreeMaxIntervalNetwork.degreeMaxC  r   r   c                    SnU R                   R                  5        HA  u  p#U[        R                  :X  a  M  Uc  UR                  nM,  [        XR                  5      nMC     U$ )a  
Return the largest degree value that represents a pitch level
that is not a terminus of the scale.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.degreeMaxUnique
7
N)r   itemsr   r'   r   r   )r   r   nIdr   s       r   degreeMaxUniqueIntervalNetwork.degreeMaxUniqueV  sT     jj&&(FChmm#yHH88$ ) r   c                b    / nUR                  U R                  [        R                     5        U$ )a]  
Return a list of first Nodes, or Nodes that contain Terminus.LOW.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.terminusLowNodes
[<music21.scale.intervalNetwork.Node id=Terminus.LOW>]

Note that this list currently always has one element.
)re   r   r   r&   r   posts     r   terminusLowNodes IntervalNetwork.terminusLowNodesm  s'     DJJx||,-r   c                b    / nUR                  U R                  [        R                     5        U$ )a(  
Return a list of last Nodes, or Nodes that contain Terminus.HIGH.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.terminusHighNodes
[<music21.scale.intervalNetwork.Node id=Terminus.HIGH>]
)re   r   r   r'   r   s     r   terminusHighNodes!IntervalNetwork.terminusHighNodes  s'     DJJx}}-.r   c                (   [        5       nU R                  R                  5        Hi  u  p4U(       aO  U[        R                  :X  a+  U R                  [        R
                     R                  X#'   MK  UR                  X#'   M[  UR                  X#'   Mk     U$ )a.  
Return a dictionary of node-id, node-degree pairs.
The same degree may be given for each node

There may not be an unambiguous way to determine the degree.
Or, a degree may have different meanings when ascending or descending.

If `equateTermini` is True, the terminals will be given the same degree.
)r   r   r   r   r'   r&   r   )r   equateTerminir   r   r   s        r   getNodeDegreeDictionary'IntervalNetwork.getNodeDegreeDictionary  sj     0;}jj&&(FC(--' $

8<< 8 ? ?DI !DIHH	 ) r   c                *    U R                  5       nX!   $ )z
Given a strict node id (the .id attribute of the Node), return the degree.

There may not be an unambiguous way to determine the degree.
Or, a degree may have different meanings when ascending or descending.
)r   )r   r   nodeSteps      r   nodeIdToDegreeIntervalNetwork.nodeIdToDegree  s     //1}r   c                   / n[        U[        5      (       a  UnUR                  nOU R                  U   nU R                   Hm  nU R                  U   nUR
                   HK  u  pgXa:X  a  UR                  UR                  5          MF  Xq:X  d  M/  UR                  UR                  5          Mk     Mo     U(       d  [        SU5      eU$ )a  
Given a Node id, find all edges associated
with this node and report on their directions

>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillMelodicMinor()
>>> net.nodeIdToEdgeDirections(scale.Terminus.LOW)
[Direction.BI]
>>> net.nodeIdToEdgeDirections(0)
[Direction.BI, Direction.BI]
>>> net.nodeIdToEdgeDirections(6)
[Direction.ASCENDING, Direction.ASCENDING]
>>> net.nodeIdToEdgeDirections(5)
[Direction.DESCENDING, Direction.DESCENDING]

This node has bi-directional (from below),
ascending (to above), and descending (from above)
edge connections connections

>>> net.nodeIdToEdgeDirections(3)
[Direction.BI, Direction.ASCENDING, Direction.DESCENDING]
zfailed to match any edges)	rM   r}   rR   r   r   ru   re   rP   r   )r   r   
collectionnObjr   eObjr   ys           r   nodeIdToEdgeDirections&IntervalNetwork.nodeIdToEdgeDirections  s    . 
c4  D''C::c?D::C::c?D((8%%dnn5X%%dnn5 )  *+FMMr   c                l    Uc  [        S5      eU R                  nU R                  nX2-
  nUS-
  U-  U-   $ )a1  
Return the degree modulus degreeMax - degreeMin.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.degreeModulus(3)
3
>>> net.degreeModulus(8)
1
>>> net.degreeModulus(9)
2
>>> net.degreeModulus(0)
7
z%Degree of None given to degreeModulusrp   )r   r   r   )r   r   sMinsMax	spanCounts        r   degreeModulusIntervalNetwork.degreeModulus  sF      >*+RSS~~~~ K	 !y(D00r   )r   permitDegreeModulic                  [        U[        5      (       a  / nU R                  US9nUR                  5        H*  u  pgX:X  d  M  UR	                  U R
                  U   5        M,     U(       dU  U(       aN  UR                  5        H:  u  pgU R                  U5      U:X  d  M  UR	                  U R
                  U   5        M<     U$ [        U[        5      (       aA  U[        R                  :X  a  U R                  $ U[        R                  :X  a  U R                  $ g[        U[        5      (       a,  U R
                   H  nU R
                  U   nXL d  M  U/s  $    g[        U[        5      (       a  [        SU< S35      e[        SU 35      e)a  
The `nodeId` parameter may be a :class:`~music21.scale.intervalNetwork.Node` object,
a node degree (as a number), a terminus string, or a None (indicating Terminus.LOW).

Return a list of Node objects that match this identification.

If `equateTermini` is True, and the name given is a degree number,
then the first terminal will return both the first and last.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.nodeNameToNodes(1)[0]
<music21.scale.intervalNetwork.Node id=Terminus.LOW>
>>> net.nodeNameToNodes(scale.Terminus.HIGH)
[<music21.scale.intervalNetwork.Node id=Terminus.HIGH>]
>>> net.nodeNameToNodes(scale.Terminus.LOW)
[<music21.scale.intervalNetwork.Node id=Terminus.LOW>]

Test using a nodeStep, or an integer nodeName

>>> net.nodeNameToNodes(1)
[<music21.scale.intervalNetwork.Node id=Terminus.LOW>,
 <music21.scale.intervalNetwork.Node id=Terminus.HIGH>]
>>> net.nodeNameToNodes(1, equateTermini=False)
[<music21.scale.intervalNetwork.Node id=Terminus.LOW>]
>>> net.nodeNameToNodes(2)
[<music21.scale.intervalNetwork.Node id=0>]

With degree moduli, degree zero is the top-most non-terminal
(since terminals are redundant)

>>> net.nodeNameToNodes(0)
[<music21.scale.intervalNetwork.Node id=5>]
>>> net.nodeNameToNodes(-1)
[<music21.scale.intervalNetwork.Node id=4>]
>>> net.nodeNameToNodes(8)
[<music21.scale.intervalNetwork.Node id=Terminus.LOW>,
 <music21.scale.intervalNetwork.Node id=Terminus.HIGH>]
)r   zStrings like z are no longer valid nodeIds.zcannot filter by: N)rM   rd   r   r   re   r   r   r   r&   r   r'   r   r}   rN   r   )	r   nodeIdr   r  r   r   r   nStepr   s	            r   nodeNameToNodesIntervalNetwork.nodeNameToNodes  sQ   \ fc""D33+ 4 -H&nn.
?KK

30 / ."*.."2JC))&1U:DJJsO4 #3 K))%,,,8==(--- )%%zzJJsO;3J " $$*]6*Da+bcc*-?x+HIIr   c                   / n/ nUR                   nU[        R                  :X  a%  U[        R                  :X  a  [        R
                  nO8U[        R
                  :X  a$  U[        R                  :X  a  [        R                  nU R                   H`  nU R                  U   nUR                  U5      nU(       d  M,  U H.  u  pX:X  d  M  UR                  U5        UR                  U
5        M0     Mb     U(       d&  [        R                  SUSUSU/5        [        S5      eU Vs/ s H  oR                  U   PM     nnX<4$ s  snf )a  
Given a Node, get two lists, one of next Edges, and one of next Nodes,
searching all Edges to find all matches.

There may be more than one possibility. If so, the caller must look
at the Edges and determine which to use

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.nodeNameToNodes(1)[0]
<music21.scale.intervalNetwork.Node id=Terminus.LOW>
	nodeStartrP   postEdgezcould not find any edges)rR   r   r&   r*   r7   r'   r6   r   rq   re   environLocal
printDebugr   r   )r   r	  rP   r
  
postNodeIdsrcIdkr   pairssrcdstr   postNodes                r   getNextIntervalNetwork.getNextJ  s    
 HLL Y)2F2F%FMMEhmm#	Y5H5H(HLLEA

1A$$Y/E! <OOA&%%c* "  ##[)[)%/%; < ++EFF 0::zJJsOz:!! ;s   #E c                  U(       d  U$ UR                   U;  a  U$ XR                      S   nSnXE:X  a  SnOuU[        R                  :X  a'  U[        R                  [        R                  4;   a  SnO:U[        R                  [        R                  4;   a  U[        R                  :X  a  SnU(       a#  U R                  XR                      S   U5      nU$ U$ )zh
Return an altered pitch for given node, if an alteration is specified
in the alteredDegrees dictionary
rP   FTr	   )r   r*   r5   r6   r7   $transposePitchAndApplySimplification)r   alteredDegreesr   prP   directionSpecmatchpPosts           r   processAlteredNodes#IntervalNetwork.processAlteredNodes}  s     H88>)H&xx0=  %E 9<<'I$7$79M9M#NNEI//1E1EFF9<</E==xx(4a9ELr   NrP   r  c                   U(       d  U$ UR                   U;   a1  U R                  XBR                      S   R                  5       U5      nU$ U$ )zW
Given a node and alteredDegrees get the unaltered pitch, or return the current object
r	   )r   r  reverse)r   pitchObjnodeObjrP   r  r  s         r   getUnalteredPitch!IntervalNetwork.getUnalteredPitch  sO     O >>^+99~~.z:BBDhPAHr   rp   )rP   stepSizer  getNeighborc          
         Uc  [        S5      e[        U[        5      (       a  [        R                  " U5      nO[
        R                  " U5      nSn	U R                  UUUUUS9n
SnU
cc  US[        R                  [        R                  [        R                  4;   a/  SnU R                  UUUUS9u  pU[        R                  :X  a  Un
OUn
U R                  UUU R                  U
   R                  USSSS9nUR                   Ul        SnU R#                  U
5      nU(       a  UU;   a  UU   S	   R$                  nU(       a  U[        R                  :X  d  U(       dp  U[        R                  :X  a\  UR                   bN  UR'                  U5      U:  a9  U=R                   S
-  sl        UR                   b  UR'                  U5      U:  a  M9  O[UR                   bN  UR'                  U5      U:  a9  U=R                   S
-  sl        UR                   b  UR'                  U5      U:  a  M9  U R                  U
   n[)        U5       H  nU R+                  UU5      u  nn[-        U5      S
:  a"  U R/                  UU5      u  nnUR0                  nOUS   R0                  nUS   nU[        R                  :X  a  U R3                  UU5      nO U R3                  UR5                  5       U5      nU R7                  UUUUS9n	M     U	$ )aj  
Given a pitchReference, nodeName, and a pitch origin, return the next pitch.

The `nodeName` parameter may be a :class:`~music21.scale.intervalNetwork.Node` object,
a node degree, a Terminus Enum, or a None (indicating Terminus.LOW).


>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.nextPitch('g', 1, 'f#5', direction=scale.Direction.ASCENDING)
<music21.pitch.Pitch G5>
>>> net.nextPitch('g', 1, 'f#5', direction=scale.Direction.DESCENDING)
<music21.pitch.Pitch E5>

The `stepSize` parameter can be configured to permit different sized steps
in the specified direction.

>>> net.nextPitch('g', 1, 'f#5',
...               direction=scale.Direction.ASCENDING,
...               stepSize=2)
<music21.pitch.Pitch A5>

Altered degrees can be given to temporarily change the pitches returned
without affecting the network as a whole.

>>> alteredDegrees = {2: {'direction': scale.Direction.BI,
...                       'interval': interval.Interval('-a1')}}
>>> net.nextPitch('g', 1, 'g2',
...               direction=scale.Direction.ASCENDING,
...               alteredDegrees=alteredDegrees)
<music21.pitch.Pitch A-2>
>>> net.nextPitch('g', 1, 'a-2',
...               direction=scale.Direction.ASCENDING,
...               alteredDegrees=alteredDegrees)
<music21.pitch.Pitch B2>
Nz/No pitch origin for calling next on this pitch!)r  pitchTargetrP   r  FT)pitchReferencenodeNamer)  rP   )r*  r+  nodeDegreeTargetrP   minPitchmaxPitchr  r   r	   rp   r  r   r  rP   )	TypeErrorrM   rN   r   PitchcopydeepcopygetRelativeNodeIdr*   r6   r7   r5   getNeighborNodeIdsgetPitchFromNodeDegreer   r   octaver   	semitones	transposer   r  r   r   r	   r  r!  r  )r   r*  r+  pitchOriginrP   r&  r  r'  pitchOriginObjpCollectr  usedNeighborlowIdhighIdr  alterSemitonesr   r   rU   r
  r  r   intervalObjs                          r   	nextPitchIntervalNetwork.nextPitch  s   b MNNk3''"[[5N!]];7N ''/74B2;7E	 ( G ND)*=*=y?S?SU^UaUa#bbL 33>=E@N>G 4 IME i222 '')!ZZ/66 ( 
  "(( $$V,f6+F3J?IINkY-A-AA$i6I6I)I((&1;;~+F+WA ((&1;;~+F+W ((&1;;~+F+WA ((&1;;~+F+W JJv xA!%a!;Hh8}q --hA1jj&qk22QK I///==k1M==k>Q>Q>SUVW//~3434;D 0 FH' !0 r   )r!  c                   Ub  UR                   nOSnUb  UR                   nOSnUR                  UR                   UUUU4$ )z6
Return key for caching based on critical components.
N)nameWithOctaverR   )	r   r#  r*  r-  r.  includeFirstr!  minKeymaxKeys	            r   _getCacheKeyIntervalNetwork._getCacheKey\  sV     ,,FF,,FF

-- 	r   )r  fillMinMaxIfNonec                  [        U[        5      (       a  [        R                  " U5      nO[        R
                  " U5      n[        U[        5      (       a  UnO'Uc  U R                  S   nOU R                  U5      S   nUR                  c  UR                  Ul	        [        U[        5      (       a  [        R                  " U5      n[        U[        5      (       a  [        R                  " U5      nU(       a  Uc  Uc  U R                  UUUS9u  p4U R                  UU[        R                  US9nU R                  (       a2  U R!                  UUUUSS9nXR"                  ;   a  U R"                  U   $ OSnU R$                  (       a  Ub  UR'                  USSS9  Un	Un
U
n/ n/ nSnS	nX:  Ga  US
-  nSnUbP  [)        UR*                  UR*                  5      (       a+  Ub(  [-        UR*                  UR*                  5      (       a  SnOdUb+  [)        UR*                  UR*                  5      (       a  Uc  SnO6Ub+  [-        UR*                  UR*                  5      (       a  Uc  SnOUc  Uc  SnU(       a,  UR/                  U5        UR/                  U	R0                  5        Ub&  [)        U
R*                  UR*                  5      (       a  OU	R0                  [2        R4                  :X  a  Uc  OU R                  S   n	U R7                  U	[        R                  5      nUc  OUu  nn[9        U5      S
:  a"  U R;                  UU5      u  nn	UR<                  nOUS   R<                  nUS   n	U R?                  UU
5      n
U
nU RA                  UU	U
[        R                  S9nX:  a  GM  X:  a  [C        S5      eU R                  (       a  Ub  X4U R"                  U'   X4$ )a  
Given a reference pitch, realize upwards to a maximum pitch.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']

>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> (pitches, nodeKeys) = net.realizeAscending('c2', 1, 'c5', 'c6')
>>> [str(p) for p in pitches]
['C5', 'D5', 'E5', 'F5', 'G5', 'A5', 'B5', 'C6']
>>> nodeKeys
[Terminus.HIGH, 0, 1, 2, 3, 4, 5, Terminus.HIGH]

>>> net = scale.intervalNetwork.IntervalNetwork(octaveDuplicating=True)
>>> net.fillBiDirectedEdges(edgeList)
>>> (pitches, nodeKeys) = net.realizeAscending('c2', 1, 'c5', 'c6')
>>> [str(p) for p in pitches]
['C5', 'D5', 'E5', 'F5', 'G5', 'A5', 'B5', 'C6']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5, Terminus.HIGH]
Nr   r  r  F)rF  TminimizeinPlaced   rp   r/  zrCannot realize these pitches; is your scale well-formed? (especially check if you're giving notes without octaves))"rM   rN   r   r1  r2  r3  r}   r   r  r7  implicitOctaverealizeMinMaxr$  r*   r6   r   rI  r   r   transposeBelowTargetr?   psrB   re   rR   r   r'   r  r   r   r	   r  r  r   )r   r*  r  r-  r.  r  rK  r#  ckr   r  r<  r   r  attemptsmaxAttemptsappendPitch
nextBundler
  r  r   rA  s                         r   realizeAscending IntervalNetwork.realizeAscendingz  s   @ nc**"[[8N!]]>:N fd##G^++A.G**6215G   ($2$A$AN!h$${{8,Hh$${{8,H 0X5E!%!3!3N4;CQ "4 "SH //07:C:M:M?M 0 O ""7#1#+#+05	 # 7B
 )))++B// * B !!h&://4QU/V 
 $MHK$X[[(++66 ,X[[(++66"&8;;44&"&8;;44&"!h&6"H%!!!$$'#QTT8;;(?(? ttx}}$#))!,a)<)<=J !!+Hh8}q  --hA1jj&qk22QK99+qIAH//~3434;D;N;N 0 PHm $v "*[\ \
 ".'+'7D  $ r   )r  rF  rK  r!  c          	     L   Sn	[        U[        5      (       a  [        R                  " U5      n
O[        R
                  " U5      n
U
R                  c  SU
l        [        U[        5      (       a  UnO'Uc  U R                  S   nOU R                  U5      S   n[        U[        5      (       a  [        R                  " U5      nOUn[        U[        5      (       a  [        R                  " U5      nOUnU(       a  Uc  Uc  U R                  U
UUS9u  pU R                  U
U[        R                  US9n
U R                  (       a2  U R                  UU
UUUUS9n	XR                   ;   a  U R                   U	   $ U R"                  (       a  Ub  U
R%                  USSS9  UnU
nUn/ n/ nSn S	nUbP  ['        UR(                  UR(                  5      (       a+  Ub(  [+        UR(                  UR(                  5      (       a  SnOdUb+  ['        UR(                  UR(                  5      (       a  Uc  SnO6Ub+  [+        UR(                  UR(                  5      (       a  Uc  SnOUc  Uc  SnU(       a  U(       a  U(       a:  U(       a3  U(       a,  UR-                  U5        UR-                  UR.                  5        S	nUb  UR(                  UR(                  ::  a  GOUR.                  [0        R2                  :X  a  Uc  OU R4                  S   nUR.                  [0        R2                  :X  a  Uc  OU R7                  U[        R                  5      nUc  OUu  nn[9        U5      S
:  a"  U R;                  UU5      u  nnUR<                  nOUS   R<                  nUS   nU R?                  URA                  5       U5      nU RC                  UUU[        R                  S9nGM-  U(       a   URA                  5         URA                  5         U R                  (       a  U	b  UU4U R                   U	'   UU4$ )a  
Given a reference pitch, realize downward to a minimum.

If no minimum is given, the terminus is used.

If `includeFirst` is False, the starting (highest) pitch will not be included.

If `fillMinMaxIfNone` is True, a min and max will be artificially
derived from an ascending scale and used as min and max values.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.realizeDescending('c2', 1, 'c3')  # minimum is above ref
([], [])
>>> (pitches, nodeKeys) = net.realizeDescending('c3', 1, 'c2')
>>> [str(p) for p in pitches]
['C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5]
>>> (pitches, nodeKeys) = net.realizeDescending('c3', 1, 'c2', includeFirst=True)
>>> [str(p) for p in pitches]
['C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2', 'C3']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5, Terminus.LOW]

>>> (pitches, nodeKeys) = net.realizeDescending('a6', scale.Terminus.HIGH)
>>> [str(p) for p in pitches]
['A5', 'B5', 'C#6', 'D6', 'E6', 'F#6', 'G#6']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5]

>>> (pitches, nodeKeys) = net.realizeDescending('a6', scale.Terminus.HIGH,
...                                             includeFirst=True)
>>> [str(p) for p in pitches]
['A5', 'B5', 'C#6', 'D6', 'E6', 'F#6', 'G#6', 'A6']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5, Terminus.HIGH]

>>> net = scale.intervalNetwork.IntervalNetwork(octaveDuplicating=True)
>>> net.fillBiDirectedEdges(edgeList)
>>> (pitches, nodeKeys) = net.realizeDescending('c2', 1, 'c0', 'c1')
>>> [str(p) for p in pitches]
['C0', 'D0', 'E0', 'F0', 'G0', 'A0', 'B0']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5]
Nr   r   rM  r  )r-  r.  rF  r!  TrN  Frp   r/  )"rM   rN   r   r1  r2  r3  r7  r}   r   r  rS  r$  r*   r7   r   rI  r   r   transposeAboveTargetr?   rU  rB   re   rR   r   r&   r   r  r   r   r	   r  r!  r  )r   r*  r  r-  r.  r  rF  rK  r!  rV  pitchRefr#  minPitchObjmaxPitchObjr   r  r<  pre	preNodeIdisFirstrY  rZ  r
  r  r   rA  s                             r   realizeDescending!IntervalNetwork.realizeDescending  s   x nc**{{>2H}}^4H ??"HO fd##G^ ++A.G**6215G h$$++h/K"K h$$++h/K"K 38K'+'9'9(:AIW (: (Y$K ))(*14=4H4H9G * I ""7#+,7,70<+2 # %B ***,,R00 !!k&=))+d)S	K'QTT;>>22#/QTT;>>22")14400!)")14400!)"$)<"
 G\

8$  &G&144;>>+Attx||#&**1-ttx||#&a)=)=>J !!+Hh8}q  --hA1jj&qk22QK99+:M:M:OQRSA//~2323:C:N:N 0 PHo x KKM ".(+YD!!"%I~r   c           
     r   Uc  [        S5      e[        U[        5      (       a  [        R                  " U5      nO[
        R                  " U5      nUR                  c  UR                  Ul        [        U[        5      (       a  [        R                  " U5      n	OUn	[        U[        5      (       a  [        R                  " U5      n
OUn
SnU R                  (       a  SnU(       Gai  U[        R                  :X  a<  U R                  (       a  U	b  UR                  U	SS9  U R                  UUU	U
USS9u  pGOOU[        R                  :X  a=  U R                  (       a  U
b  UR                  U
SS9  U R!                  UUU	U
USSS9u  pGOU[        R"                  :X  Ga  [
        R                  " U5      n[
        R                  " U5      nU R                  (       a  U	b  UR                  U	SS9  U R                  UUU	U
US9u  nnU R                  (       a  U
b  UR                  U
SS9  U R!                  UUU	U
USS9u  nn/ n/ nS	nS	nS
nUS	:  a  US-  nU[%        U5      :  a6  UU   U;  a-  UR'                  UU   5        UR'                  UU   UU   45        US-  nU[%        U5      :  a6  UU   U;  a-  UR'                  UU   5        UR'                  UU   UU   45        US-  nU[%        U5      :  a  U[%        U5      :  a  OUS	:  a  M  / n/ nU H(  u  nnUR'                  U5        UR'                  U5        M*     OE[        SU< 35      eU R                  UUU	U
US9u  nnU R!                  UUU	U
USS9u  nnUU-   UU-   pU(       a(  [)        [+        U5      5      n[)        [+        U5      5      nX4$ )aB  
Realize the nodes of this network based on a pitch assigned to a
valid `nodeId`, where `nodeId` can be specified by integer
(starting from 1) or key (a tuple of origin, destination keys).

Without a min or max pitch, the given pitch reference is assigned
to the designated node, and then both ascends to the terminus and
descends to the terminus.

The `alteredDegrees` dictionary permits creating mappings between
node degree and direction and :class:`~music21.interval.Interval`
based transpositions.

Returns two lists, a list of pitches, and a list of Node keys.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> (pitches, nodeKeys) = net.realize('c2', 1, 'c2', 'c3')
>>> [str(p) for p in pitches]
['C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B2', 'C3']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5, Terminus.HIGH]

>>> alteredDegrees = {7: {'direction': scale.Direction.BI,
...                       'interval': interval.Interval('-a1')}}
>>> (pitches, nodeKeys) = net.realize('c2', 1, 'c2', 'c4', alteredDegrees=alteredDegrees)
>>> [str(p) for p in pitches]
['C2', 'D2', 'E2', 'F2', 'G2', 'A2', 'B-2', 'C3',
 'D3', 'E3', 'F3', 'G3', 'A3', 'B-3', 'C4']
>>> nodeKeys
[Terminus.LOW, 0, 1, 2, 3, 4, 5, Terminus.HIGH, 0, 1, 2, 3, 4, 5, Terminus.HIGH]
zpitchReference cannot be NoneFTrP  )r*  r  r-  r.  r  rK  r*  r  r-  r.  r  rF  rK  )r*  r  r-  r.  r  )r*  r  r-  r.  r  rF  r   i'  rp   z&cannot match direction specification: )r   rM   rN   r   r1  r2  r3  r7  rR  r   r*   r6   rT  r[  r7   r^  re  r5   r   re   r   reversed)r   r*  r  r-  r.  rP   r  r!  r_  r`  ra  directedRealizationmergedPitchesmergedNodespitchReferenceApitchReferenceBr   r  rb  rc  mergedfoundPitchesrU   jpreventPermanentRecursionr   r   s                              r   realizeIntervalNetwork.realize  s
   Z !*+JKKnc**{{>2H}}^4H ??"&55HO h$$++h/K"K h$$++h/K"K#!!"& I///))k.E11+t1L-1-B-B#+!((#1%) .C .+*{ i222))k.E11+t1L .2-C-C#+!((#1!%%) .D .+*{ ill*"&--"9"&--"9))k.E#88d8S $(#8#8@FBMBMHV	 $9 $X j ))k.E#88d8S "&!7!7?EALALGUEI "8 "KY !,0)/!3-2-3t9}a)D$++DG4tAw
1&>?FA3s8|Al(B$++CF3s1vy|&<=FA CI~!s3x- 0!3 !# "DAq!((+&&q) # /<YMJL L  $44H<B>I>IDR	  5  TD*
 "338;A=H=HCQAF 4 HNC *-tY5K; !-!89Mx45K))r   c           
     6    U R                  UUUUUUUS9nUS   $ )a  
Realize the native nodes of this network based on a pitch
assigned to a valid `nodeId`, where `nodeId` can be specified by integer
(starting from 1) or key (a tuple of origin, destination keys).

The nodeId, when a simple, linear network, can be used as a scale degree
value starting from one.


>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> [str(p) for p in net.realizePitch(pitch.Pitch('G3'))]
['G3', 'A3', 'B3', 'C4', 'D4', 'E4', 'F#4', 'G4']

G3 is the fifth (scale) degree

>>> [str(p) for p in net.realizePitch(pitch.Pitch('G3'), 5)]
['C3', 'D3', 'E3', 'F3', 'G3', 'A3', 'B3', 'C4']

G3 is the seventh (scale) degree

>>> [str(p) for p in net.realizePitch(pitch.Pitch('G3'), 7) ]
['A-2', 'B-2', 'C3', 'D-3', 'E-3', 'F3', 'G3', 'A-3']

>>> [str(p) for p in net.realizePitch(pitch.Pitch('f#3'), 1, 'f2', 'f3') ]
['E#2', 'F#2', 'G#2', 'A#2', 'B2', 'C#3', 'D#3', 'E#3']

>>> [str(p) for p in net.realizePitch(pitch.Pitch('a#2'), 7, 'c6', 'c7')]
['C#6', 'D#6', 'E6', 'F#6', 'G#6', 'A#6', 'B6']


Circle of fifths


>>> edgeList = ['P5'] * 6 + ['d6'] + ['P5'] * 5
>>> net5ths = scale.intervalNetwork.IntervalNetwork()
>>> net5ths.fillBiDirectedEdges(edgeList)
>>> [str(p) for p in net5ths.realizePitch(pitch.Pitch('C1'))]
['C1', 'G1', 'D2', 'A2', 'E3', 'B3', 'F#4', 'D-5', 'A-5', 'E-6', 'B-6', 'F7', 'C8']
>>> [str(p) for p in net5ths.realizePitch(pitch.Pitch('C2'))]
['C2', 'G2', 'D3', 'A3', 'E4', 'B4', 'F#5', 'D-6', 'A-6', 'E-7', 'B-7', 'F8', 'C9']

r*  r  r-  r.  rP   r  r!  r   )rt  )	r   r*  r  r-  r.  rP   r  r!  
componentss	            r   realizePitchIntervalNetwork.realizePitch  s9    l \\)) " 
 !}r   c           
         SnU R                  UUUUUUUS9S   n/ n	[        U5       HE  u  pU
[        U5      S-
  :  d  M  XS-      nU	R                  [        R
                  " X5      5        MG     U	$ )a  
Realize the sequence of intervals between the specified pitches, or the termini.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.realizeIntervals()
[<music21.interval.Interval M2>, <music21.interval.Interval M2>,
 <music21.interval.Interval m2>, <music21.interval.Interval M2>,
 <music21.interval.Interval M2>, <music21.interval.Interval M2>,
 <music21.interval.Interval m2>]
c4rw  r   rp   )rt  r   r   re   r	   rO   )r   r  r-  r.  rP   r  r!  r*  pListiListrU   p1p2s                r   realizeIntervals IntervalNetwork.realizeIntervals  s    2 N$*&.&.'0,:%,  . /01 u%EA3u:>!q5\X..r67 & r   c                t    U R                  UUUSS9S   nU R                  UUUSSS9S   nXT-   nUS   US   4$ )aC  
Realize the pitches of the 'natural' terminus of a network. This (presently)
must be done by ascending, and assumes only one valid terminus for both extremes.

This suggests that in practice termini should not be affected by directionality.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)
>>> net.realizeTermini(pitch.Pitch('G3'))
(<music21.pitch.Pitch G3>, <music21.pitch.Pitch G4>)
>>> net.realizeTermini(pitch.Pitch('a6'))
(<music21.pitch.Pitch A6>, <music21.pitch.Pitch A7>)
F)r*  r  r  rK  r   )r*  r  r  rF  rK  )r[  re  )r   r*  r  r  r   rb  rl  s          r   realizeTerminiIntervalNetwork.realizeTermini  s    0 $$))"	 % $ %&	'
 $$))" % $
 %&' 
 Qr!222r   c           
        U R                  UUUS9u  pEUR                  S5      nUR                  S5      nU R                  UUUUUSS9u  pgU R                  UUUUUSSS9u  p/ n
Sn[	        U5       H  u  pXl   nUS:X  a&  U[
        R                  [
        R                  4;   a  M5  U[
        R                  [
        R                  4;   a  USL a  U
R                  X45          OJU[
        R                  [
        R                  4;   a  USL a  SnU(       d  M  U
R                  X45        M     / nSn[	        U	5       H  u  pX   nUS:X  a&  U[
        R                  [
        R                  4;   a  M5  U[
        R                  [
        R                  4;   a  USL a  UR                  X45          OJU[
        R                  [
        R                  4;   a  USL a  SnU(       d  M  UR                  X45        M     US	   nUS   nX-    H?  u  pUR                  UR                  :  a  UnUR                  UR                  :  d  M=  UnMA     UU4$ )
a  
Realize the min and max pitches of the scale, or the min and max values
found between two termini.

This suggests that min and max might be beyond the terminus.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)

>>> net.realizeMinMax(pitch.Pitch('C4'))
(<music21.pitch.Pitch C4>, <music21.pitch.Pitch C6>)
>>> net.realizeMinMax(pitch.Pitch('B-5'))
(<music21.pitch.Pitch B-5>, <music21.pitch.Pitch B-7>)

Note that it might not always be two octaves apart

#  s = scale.AbstractDiatonicScale('major')
#  s._net.realizeMinMax(pitch.Pitch('D2'))
#  (<music21.pitch.Pitch D2>, <music21.pitch.Pitch D3>)
)r*  r  r     F)r*  r  r  r-  r.  rK  Tri  r   r  )
r  r9  r[  re  r   r   r&   r'   re   rU  )r   r*  r  r  lowhighr   r  rb  rc  	postPairscollectrU   r   r  prePairsr-  r.  s                     r   rS  IntervalNetwork.realizeMinMaxC  s<   8 ''~/57E ( G	
 mmC ~~b!00))" 1 $ //))" 0 $ 	
+FAAAv#(,,!>>x}}55'T/  !*x}}55'U:Jw  !* , 	*FAAAv#(,,!>>x}}55'T/)x}}55'U:Jw) +" 87*FAtthkk!tthkk!	 + !!r   )rp   c           	     .   U R                  UUUUUUS9u  pU V
s/ s H  oR                  U
5      PM     nn
/ n[        U5       HH  u  pU R                  X      nU R                  UR                  5      U;   d  M7  UR                  U5        MJ     U$ s  sn
f )au  
Realize the native nodes of this network based on
a pitch assigned to a valid `nodeId`, where `nodeId` can
be specified by integer (starting from 1) or key
(a tuple of origin, destination keys).

The `nodeDegreeTargets` specifies the degrees to be
included within the specified range.

Example: build a network of the Major scale:

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork()
>>> net.fillBiDirectedEdges(edgeList)

Now for every "scale" where G is the 3rd degree, give me the
tonic if that note is between C2 and C3.
(There's only one such note: E-2).

>>> net.realizePitchByDegree('G', 3, [1], 'c2', 'c3')
[<music21.pitch.Pitch E-2>]

But between c2 and f3 there are two, E-2 and E-3 (it doesn't matter that the G
which is scale degree 3 for E-3 is above F3):

>>> net.realizePitchByDegree('G', 3, [1], 'c2', 'f3')
[<music21.pitch.Pitch E-2>, <music21.pitch.Pitch E-3>]

Give us nodes 1, 2, and 5 for scales where G is node 5 (e.g., C major's dominant)
where any pitch is between C2 and F4

>>> pitchList = net.realizePitchByDegree('G', 5, [1, 2, 5], 'c2', 'f4')
>>> print(' '.join([str(p) for p in pitchList]))
C2 D2 G2 C3 D3 G3 C4 D4

There are no networks based on the major scale's edge-list where
with node 1 (i.e. "tonic") between C2 and F2 where
G is scale degree 7

>>> net.realizePitchByDegree('G', 7, [1], 'c2', 'f2')
[]
r*  r  r-  r.  rP   r  )rt  r   r   r   r   re   )r   r*  r  nodeDegreeTargetsr-  r.  rP   r  realizedPitchrealizedNodesnodeDegreeTargetsModulusr   rU   r  r   s                   r   realizePitchByDegree$IntervalNetwork.realizePitchByDegree  s    j '+ll)) '3 '+# DU#UCTa$6$6q$9CT #U m,DA

<?+A!!!((+/GGA -  $Vs   Bc           	     .   SSK nSnSnS nUR                  5       nU R                  R                  5        H  u  pgUR                  [
        R                  :X  a  SnSnOEUR                  [
        R                  :X  a  SnSnO"UR                  [
        R                  :X  a  SnSnUR                   H  u  pUR                  XX#S	9  M     M     [        5       n
[        U R                  R                  5       5      nUR                  US
9  U Hp  nU R                  U   nUR                   U
;  a  SXR                   '   XR                      UR                   4UR"                  U   S'   XR                   ==   S-  ss'   Mr     [$        R'                  SU
/5        U$ )z
Create a networkx graph from the raw Node representation.

Return a networks Graph object representing a realized version
of this IntervalNetwork if networkx is installed
r   Nrp   solidc                l    Sn[        U [        5      (       a  U R                  5       S:X  a  SnX4$ SnX4$ )z
return a two-tuple where the first element is -1 if 'Terminus.LOW',
0 if an int, and 1 if 'Terminus.HIGH' or another string, and
the second element is the value itself.
r   zTerminus.LOWr  rp   )rM   rN   upper)r=   	sortFirsts     r   &sortTerminusLowThenIntThenTerminusHighPIntervalNetwork.getNetworkxGraph.<locals>.sortTerminusLowThenIntThenTerminusHigh  sB     I!S!!779. "I >! !"I>!r   g?g333333?rL   )rQ   style)keyposzgot degree count)networkxMultiDiGraphr   r   rP   r*   r6   r7   r5   ru   add_edger   r   r   keyssortr   noder  r  )r   r  rQ   r  r  g
unused_eIdr   r  r  r   nKeysr   r   s                 r   getNetworkxGraph IntervalNetwork.getNetworkxGraph  s\    		" !!#!ZZ--/MJ{{i111	 4 44	,MM

3F
@ * 0 "mTZZ__&'

=
>C

3Axx{*()HH%"-hh"7!BAFF3K!Q&!  	!3[ ABr   c                ~    SSK Jn  UR                  R                  U R	                  5       S9nUR                  5         g)z
Given a method and keyword configuration arguments, create and display a plot.

Requires `networkx` to be installed.

* Changed in v8: other parameters were unused and removed.
r   )graph)networkxGraphN)music21r  
primitivesGraphNetworkxGraphr  process)r   keywordsr  r  s       r   plotIntervalNetwork.plot*	  s7    $ 	"////1 0 3			r   rU  )comparisonAttributerP   r  c          	        Uc  U R                   S   nOU R                  U5      S   n[        U[        5      (       a  [        R
                  " U5      nO.[        U[        R                  5      (       a  UR                  nOUnUR                  n	U	c  UR                  Ul        UR                  SSS9n
UR                  SSS9nU R                  UUU
UUUS9u  p/ n[        X5       H9  u  nn[        X5      [        X5      :X  d  M   UU;  d  M(  UR                  U5        M;     U	c  SUl        U(       d  g[        U5      S:X  a  US   $ [         R"                  " UU Vs/ s H  nU R$                  U   R&                  PM     sn5      $ s  snf )	a  
Given a reference pitch assigned to node id, determine the
relative node id of pitchTarget, even if displaced over multiple octaves

The `nodeId` parameter may be
a :class:`~music21.scale.intervalNetwork.Node` object, a node degree,
a terminus string, or a None (indicating Terminus.LOW).

Returns None if no match.

If `getNeighbor` is True, or direction, the nearest node will be returned.

If more than one node defines the same pitch, Node weights are used
to select a single node.


>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> net.getRelativeNodeId('a', 1, 'a4')
Terminus.LOW
>>> net.getRelativeNodeId('a', 1, 'b4')
0
>>> net.getRelativeNodeId('a', 1, 'c#4')
1
>>> net.getRelativeNodeId('a', 1, 'c4', comparisonAttribute='step')
1
>>> net.getRelativeNodeId('a', 1, 'c', comparisonAttribute='step')
1
>>> net.getRelativeNodeId('a', 1, 'b-4') is None
True
Nr   r  Frh  r  r-  r.  rP   r  rp   )r   r  rM   rN   r   r1  r
   Noter7  rR  r9  rt  zipr   re   r   r   r   r   rQ   )r   r*  r  r)  r  rP   r  r#  pitchTargetObj
saveOctaver-  r.  realizedPitchesrealizedNodesr   r  r  r   s                     r   r4  !IntervalNetwork.getRelativeNodeIdB	  s   X >++A.G**6215G
 k3''"[[5NTYY//(..N(N#**
$2$A$AN! "++C+?!++B+>)-n6=?G?G@IES *6 *U& +.+N'M<
 <}BCt+KK- ,O $(N!Y!^7N ++DKO,P4aTZZ]-A-A4,PR R,Ps   #F
c           	        Uc  U R                   S   nOU R                  U5      S   n[        U[        5      (       a  [        R
                  " U5      nOUnUR                  nUc  UR                  Ul        UR                  SSS9n	UR                  SSS9n
U R                  UUU	U
UUS9u  pSnSn[        X5       H)  u  nnUR                  UR                  :  a  UnX4s  $ UnM+     Uc  Xl        g)ad  
Given a reference pitch assigned to a node id, determine the node ids
that neighbor this pitch.

Returns None if an exact match.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> net.getNeighborNodeIds('c4', 1, 'b-')
(4, 5)
>>> net.getNeighborNodeIds('c4', 1, 'b')
(5, Terminus.HIGH)
Nr   r  Frh  r  r  )r   r  rM   rN   r   r1  r7  rR  r9  rt  r  rU  )r   r*  r+  r)  rP   r  r  r  savedOctaver-  r.  r  r  lowNeighborhighNeighborr  r  s                    r   r5  "IntervalNetwork.getNeighborNodeIds	  s   , **1-F))(3A6Fk3''"[[5N(N$++$2$A$AN! "++C+?!++B+>)-n6<?G?G@IES *6 *U& +.+N'M<  =#3#33+"00&K ,O $/!r   c           	     T    U R                  UUUUUUS9nUc  gU R                  U5      $ )a	  
Given a reference pitch assigned to node id,
determine the relative node degree of pitchTarget,
even if displaced over multiple octaves

Comparison Attribute determines what will be used to determine
equality.  Use `ps` (default) for post-tonal uses.  `name` for
tonal, and `step` for diatonic.


>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> [str(p) for p in net.realizePitch(pitch.Pitch('e-2')) ]
['E-2', 'F2', 'G2', 'A-2', 'B-2', 'C3', 'D3', 'E-3']

>>> net.getRelativeNodeDegree('e-2', 1, 'd3')  # if e- is tonic, what is d3
7

For an octave repeating network, the neither pitch's octave matters:

>>> net.getRelativeNodeDegree('e-', 1, 'd5')  # if e- is tonic, what is d3
7
>>> net.getRelativeNodeDegree('e-2', 1, 'd')  # if e- is tonic, what is d3
7

>>> net.getRelativeNodeDegree('e3', 1, 'd5') is None
True
>>> net.getRelativeNodeDegree('e3', 1, 'd5', comparisonAttribute='step')
7
>>> net.getRelativeNodeDegree('e3', 1, 'd', comparisonAttribute='step')
7

>>> net.getRelativeNodeDegree('e-3', 1, 'b-3')
5

>>> net.getRelativeNodeDegree('e-3', 1, 'e-5')
1
>>> net.getRelativeNodeDegree('e-2', 1, 'f3')
2
>>> net.getRelativeNodeDegree('e-3', 1, 'b6') is None
True

>>> net.getRelativeNodeDegree('e-3', 1, 'e-2')
1
>>> net.getRelativeNodeDegree('e-3', 1, 'd3')
7
>>> net.getRelativeNodeDegree('e-3', 1, 'e-3')
1
>>> net.getRelativeNodeDegree('e-3', 1, 'b-1')
5


>>> edgeList = ['p4', 'p4', 'p4']  # a non octave-repeating scale
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> [str(p) for p in net.realizePitch('f2')]
['F2', 'B-2', 'E-3', 'A-3']
>>> [str(p) for p in net.realizePitch('f2', 1, 'f2', 'f6')]
['F2', 'B-2', 'E-3', 'A-3', 'D-4', 'G-4', 'C-5', 'F-5', 'A5', 'D6']

>>> net.getRelativeNodeDegree('f2', 1, 'a-3')  # could be 4 or 1
1
>>> net.getRelativeNodeDegree('f2', 1, 'd-4')  # 2 is correct
2
>>> net.getRelativeNodeDegree('f2', 1, 'g-4')  # 3 is correct
3
>>> net.getRelativeNodeDegree('f2', 1, 'c-5')  # could be 4 or 1
1
>>> net.getRelativeNodeDegree('f2', 1, 'e--6')  # could be 4 or 1
1

>>> [str(p) for p in net.realizePitch('f6', 1, 'f2', 'f6')]
['G#2', 'C#3', 'F#3', 'B3', 'E4', 'A4', 'D5', 'G5', 'C6', 'F6']

>>> net.getRelativeNodeDegree('f6', 1, 'd5')
1
>>> net.getRelativeNodeDegree('f6', 1, 'g5')
2
>>> net.getRelativeNodeDegree('f6', 1, 'a4')
3
>>> net.getRelativeNodeDegree('f6', 1, 'e4')
2
>>> net.getRelativeNodeDegree('f6', 1, 'b3')
1

)r*  r  r)  r  r  rP   N)r4  r   )r   r*  r  r)  r  rP   r  r   s           r   getRelativeNodeDegree%IntervalNetwork.getRelativeNodeDegree	  sG    | $$)# 3) % ! ;&&s++r   c	           
        U R                  U5      n	Sn
U R                  USUS9n[        U5      S:X  a  US   n
OgU Vs/ s H  oR                  PM     sn[        R                  [        R
                  /:X  a  US   n
O#U H  nU R                  U5      nXN;   d  M  Un
  O   U
c  US   n
[        S5       H  nU R                  UU	S   UUUUS9u  nn[        U5       H  u  nnXR                  :X  a	  UU   s  s  $ U(       d  M'  U[        R
                  [        R                  4;   d  MM  U
R                  [        R
                  [        R                  4;   d  M}  UU   s  s  $    M     gs  snf )a|  
Given a reference pitch assigned to node id,
determine the pitch for the target node degree.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> [str(p) for p in net.realizePitch(pitch.Pitch('e-2')) ]
['E-2', 'F2', 'G2', 'A-2', 'B-2', 'C3', 'D3', 'E-3']
>>> net.getPitchFromNodeDegree('e4', 1, 1)
<music21.pitch.Pitch E4>
>>> net.getPitchFromNodeDegree('e4', 1, 7)  # seventh scale degree
<music21.pitch.Pitch D#5>
>>> net.getPitchFromNodeDegree('e4', 1, 8)
<music21.pitch.Pitch E4>
>>> net.getPitchFromNodeDegree('e4', 1, 9)
<music21.pitch.Pitch F#4>
>>> net.getPitchFromNodeDegree('e4', 1, 3, minPitch='c2', maxPitch='c3')
<music21.pitch.Pitch G#2>


This will always get the lowest pitch:

>>> net.getPitchFromNodeDegree('e4', 1, 3, minPitch='c2', maxPitch='c10')
<music21.pitch.Pitch G#2>

>>> net.fillMelodicMinor()
>>> net.getPitchFromNodeDegree('c', 1, 5)
<music21.pitch.Pitch G4>
>>> net.getPitchFromNodeDegree('c', 1, 6, scale.Direction.ASCENDING)
<music21.pitch.Pitch A4>
>>> net.getPitchFromNodeDegree('c', 1, 6, scale.Direction.DESCENDING)
<music21.pitch.Pitch A-4>
NT)r  r   rp   r   
   r  )
r  r   rR   r   r&   r'   r   r   rt  r   )r   r*  r+  r,  rP   r-  r.  r  r   nodeListForNamesnodeTargetIdnodeTargetIdListr   r   dirListunused_counterr  r  rU   s                      r   r6  &IntervalNetwork.getPitchFromNodeDegreeP
  sw   \  //9 //0@CG>K 0 M  A%+A.L,-,qdd,-(,,1NN+A.L'55c:
 '#&L (  ,A.L $BiN*.,,-'*!!#- +7 +/'M< $L13 //)(++ != ==*oo(--1NN,Q// 2 (; .s   E)c                "   [        U [        [        45      (       d2  [        U [        5      (       a  [        R
                  " U 5      nOU nU/nOX/ nU  HP  n[        U[        5      (       a'  UR                  [        R
                  " U5      5        M?  UR                  U5        MR     U(       d  [        S5      e[        U5       VVs/ s H  u  pEUR                  U4PM     nnnUR                  5         X&S   S      nX&S   S      nX'U4$ s  snnf )a  
Given a list or one pitch, check if all are pitch objects; convert if necessary.
Return a 3-tuple: a list of all pitches, the min value and the max value.

>>> Net = scale.intervalNetwork.IntervalNetwork
>>> Net.filterPitchList(['c#4', 'f5', 'd3'])
([<music21.pitch.Pitch C#4>, <music21.pitch.Pitch F5>, <music21.pitch.Pitch D3>],
 <music21.pitch.Pitch D3>,
 <music21.pitch.Pitch F5>)

A single string or pitch can be given.

>>> Net.filterPitchList('c#')
([<music21.pitch.Pitch C#>],
 <music21.pitch.Pitch C#>,
 <music21.pitch.Pitch C#>)

Empty lists raise value errors:

>>> Net.filterPitchList([])
Traceback (most recent call last):
ValueError: There must be at least one pitch given.

* Changed in v8: staticmethod.  Raise value error on empty
z'There must be at least one pitch given.r   rp   r  )rM   r   tuplerN   r   r1  re   
ValueErrorr   rU  r  )	r)  r"  	pitchListr  rU   r   sortListr-  r.  s	            r   filterPitchListIntervalNetwork.filterPitchList
  s    < +e}55+s++ ;;{3&!
I I a%%$$U[[^4$$Q'	 ! FGG 3<I2FG2FhaUXXqM2FGa[^,b\!_-H,, Hs   D
pitchClassc                x   Uc  U R                   S   nOU R                  U5      S   nU R                  U5      u  p6nU R                  UUUUUS9n/ n	/ n
U H`  nSnU H;  nSn[	        X5      [	        X5      :X  a  SnU(       d  M(  U	R                  U5        Sn  O   U(       a  MO  U
R                  U5        Mb     X4$ )a6  
Given one or more pitches in `pitchTarget`, return a
tuple of a list of matched pitches, and a list of unmatched pitches.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> [str(p) for p in net.realizePitch('e-2')]
['E-2', 'F2', 'G2', 'A-2', 'B-2', 'C3', 'D3', 'E-3']

>>> net.match('e-2', 1, 'c3')  # if e- is tonic, is 'c3' in the scale?
([<music21.pitch.Pitch C3>], [])

>>> net.match('e-2', 1, 'd3')
([<music21.pitch.Pitch D3>], [])

>>> net.match('e-2', 1, 'd#3')
([<music21.pitch.Pitch D#3>], [])

>>> net.match('e-2', 1, 'e3')
([], [<music21.pitch.Pitch E3>])

>>> pitchTarget = [pitch.Pitch('b-2'), pitch.Pitch('b2'), pitch.Pitch('c3')]
>>> net.match('e-2', 1, pitchTarget)
([<music21.pitch.Pitch B-2>, <music21.pitch.Pitch C3>], [<music21.pitch.Pitch B2>])

>>> pitchTarget = ['b-2', 'b2', 'c3', 'e-3', 'e#3', 'f2', 'e--2']
>>> (matched, unmatched) = net.match('e-2', 1, pitchTarget)
>>> [str(p) for p in matched]
['B-2', 'C3', 'E-3', 'E#3', 'F2', 'E--2']
>>> unmatched
[<music21.pitch.Pitch B2>]

r   rM  FT)r   r  r  ry  r   re   )r   r*  r  r)  r  r  r-  r.  nodesRealizedmatchednoMatchtargetfoundr  r  s                  r   r  IntervalNetwork.match  s    T >**1-F))&1!4F*.*>*>{*K'x )).*0*2*29G	 * I  "FE"12gf6ZZ E5NN6* E # 5v& " r   c	                :   Uc  U R                   S   nOU R                  U5      S   nU R                  UUUUUS9n	U R                  U5      u  p5n/ n
U	 HD  nSnU H  n[	        X5      [	        X5      :X  d  M  Sn  O   U(       a  M3  U
R                  U5        MF     U
$ )a  
Find all pitches in the realized scale that are not in the
pitch target network based on the comparison attribute.

>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)
>>> [str(p) for p in net.realizePitch('G3')]
['G3', 'A3', 'B3', 'C4', 'D4', 'E4', 'F#4', 'G4']
>>> net.findMissing('g', 1, ['g', 'a', 'b', 'd', 'f#'])
[<music21.pitch.Pitch C5>, <music21.pitch.Pitch E5>]
r   )r-  r.  r  FT)r   r  ry  r  r   re   )r   r*  r  r)  r  r-  r.  rP   r  r  r   r  r  r  s                 r   findMissingIntervalNetwork.findMissingR  s    , >**1-F))&1!4F )).*03;3;9G	 * I +/*>*>{*K'x #FE 12gf6ZZ E	 ! 5F# $ r   )CzC#zD-DzD#zE-EFzF#GzG#AzB-BzC-ztuple[str, ...]_SCALE_STARTSc           	     (   U R                   S   n/ nU R                   HH  nU R                  UUUUUS9u  pUR                  [	        U5      [
        R                  " U5      45        MJ     UR                  5         UR                  5         Ub  USU $ U$ )a  
Given a collection of pitches, test all transpositions of a realized
version of this network, and return the number of matches in each for
each pitch assigned to the first node.


>>> edgeList = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']
>>> net = scale.intervalNetwork.IntervalNetwork(edgeList)

a network built on G or D as

>>> net.find(['g', 'a', 'b', 'd', 'f#'])
[(5, <music21.pitch.Pitch G>), (5, <music21.pitch.Pitch D>),
 (4, <music21.pitch.Pitch A>), (4, <music21.pitch.Pitch C>)]

>>> net.find(['g', 'a', 'b', 'c', 'd', 'e', 'f#'])
[(7, <music21.pitch.Pitch G>), (6, <music21.pitch.Pitch D>),
 (6, <music21.pitch.Pitch C>), (5, <music21.pitch.Pitch A>)]

If resultsReturned is None then return every such scale.
r   )r  r  N)	r   r  r  re   r   r   r1  r  r!  )
r   r)  resultsReturnedr  r  r  r  r  r  unused_noMatchs
             r   findIntervalNetwork.find  s    4 &&q)
 ##A '+jj$7- '1 '/#G OOS\5;;q>:; $ 	&,_--Or   c                   U R                   nUS;   aC  [        US5      (       a  UR                  (       d  [        U[        R
                  5      (       a  SnUS:X  a  UR                  USS9nU$ UR                  U5      nUR                  (       aM  US:X  a  UR                  SS	9  U$ US:X  a  UR                  SSS
9  U$ US;   a   U$ [        SU S3S-   S-   5      eU$ )a  
transposes the pitch according to the given interval object and
uses the simplification of the `pitchSimplification` property
to simplify it afterwards.

>>> b = scale.intervalNetwork.IntervalNetwork()
>>> b.pitchSimplification  # default
'maxAccidental'
>>> i = interval.Interval('m2')
>>> p = pitch.Pitch('C4')
>>> allPitches = []
>>> for j in range(15):
...    p = b.transposePitchAndApplySimplification(i, p)
...    allPitches.append(p.nameWithOctave)
>>> allPitches
['D-4', 'D4', 'E-4', 'F-4', 'F4', 'G-4', 'G4', 'A-4', 'A4',
 'B-4', 'C-5', 'C5', 'D-5', 'D5', 'E-5']

>>> b.pitchSimplification = 'mostCommon'
>>> p = pitch.Pitch('C4')
>>> allPitches = []
>>> for j in range(15):
...    p = b.transposePitchAndApplySimplification(i, p)
...    allPitches.append(p.nameWithOctave)
>>> allPitches
['C#4', 'D4', 'E-4', 'E4', 'F4', 'F#4', 'G4', 'A-4', 'A4', 'B-4',
 'B4', 'C5', 'C#5', 'D5', 'E-5']

PitchSimplification can also be specified in the creation of the IntervalNetwork object

>>> b = scale.intervalNetwork.IntervalNetwork(pitchSimplification=None)
>>> p = pitch.Pitch('C4')
>>> allPitches = []
>>> for j in range(5):
...    p = b.transposePitchAndApplySimplification(i, p)
...    allPitches.append(p.nameWithOctave)
>>> allPitches
['D-4', 'E--4', 'F--4', 'G---4', 'A----4']

Note that beyond quadruple flats or sharps, pitchSimplification is automatic:

>>> p
<music21.pitch.Pitch A----4>
>>> b.transposePitchAndApplySimplification(i, p)
<music21.pitch.Pitch F#4>
)NnoneimplicitDiatonic
mostCommonmaxAccidentalrp   )r  simplifyEnharmonicTrh  )rP  r  z!unknown pitchSimplification type ,zG allowable values are "maxAccidental" (default), "simplifyEnharmonic", z!"mostCommon", or None (or "none"))
r   hasattrr  rM   r	   ChromaticIntervaltransposePitch
accidentalr  r   )r   rA  r"  r   r  s        r   r  4IntervalNetwork.transposePitchAndApplySimplification  s   f #66>1+'9::{?[?[X-G-GHH"./1..xq.IE   ..x8E&*>>,,T,:  )L8,,Td,K  )N: 	 3;<O;PPQRcd=>? ? r   )	r   r   r   r   r   r   r   r   r   )r    FTr  )r   z!Sequence[interval.Interval | str])rx   z
list[Node])T)r   bool)r   rd   rx   rd   )r  Node | int | Terminus | None)rx   pitch.Pitch)
r*  pitch.Pitch | strr+  r  r:  r   rP   r*   r'  zbool | Direction)r#  r}   r*  r  r-  pitch.Pitch | Noner.  r  rF  r  r!  zbool | Nonerx   CacheKey)NNN)
r*  r   r  r  r-  pitch.Pitch | str | Noner.  r  rx   z.tuple[list[pitch.Pitch], list[Terminus | int]])r*  r   r  r  r-  r  r.  r  )
r*  str | pitch.Pitchr  r  r-  r  r.  r  rP   r*   )r*  r  r  r  r-  r  r.  r  rP   r*   rx   zlist[pitch.Pitch])
r  r  r-  r  r.  r  rP   r*   rx   zlist[interval.Interval])NN)r*  r  r  r  rx   ztuple[pitch.Pitch, pitch.Pitch])
r*  r   r  r  r-  r  r.  r  rP   r*   )
r*  r   r  r  r)  zpitch.Pitch | note.Note | strr  rN   rP   r*   )r*  r   r+  r  r)  r   rP   r*   )r*  r   r)  r   rP   r*   )r*  r   r+  r  rP   r*   )r)  z7t.Union[list[str], list[pitch.Pitch], str, pitch.Pitch]rx   z2tuple[list[pitch.Pitch], pitch.Pitch, pitch.Pitch])r  N)r*  r   )r*  r   rP   r*   )r   r  N)rA  zinterval.Intervalr"  r  rx   r  )7r!   r"   r#   r$   r%   rV   r   r]   r   r   r   r   r   r{   r   r   r   r   r   r   r   r   r   r  r  r  r*   r5   r$  r6   rB  rI  r[  re  rt  ry  r  r  rS  r  r  r  r4  r5  r  r6  staticmethodr  r  r  r  __annotations__r  r  r(   r    r   r   r   r     s   . >@#(#%4	#:#J	.<T#lK#Z;z8"x"8  $  $  ,  "  ".+Z1F '++/	IJ 6IJV1"f)` ,, 
:  )22&*Y'Y )Y %	Y Y $YJ " $ #	
 #   
B *.)-)-c  c 'c  'c  '	c 
 'c  
6c P *.)-)-F F'F 'F '	F
 'FT 261515'0':':#E* /E*.E* /E* /	E*
 %E*T *.)-)-(22>'> '> '	>
 '> > 
>D *.)-)-(22(&( '( '	(
 ( 
!(Z *.	)3')3 ')3
 
))3\ *.	a"'a" 'a"
 
)a"L *.)-)-(22I'I 'I
 'I 'I IV7r< $((22dR'dR 'dR /	dR !dR dRV  )22;'; ); %	;
 ;D !(22i,'i, %	i, i,`  )22x0'x0 )x0
 x0z 6-L6-	;6- 6-x #/!J -J ` )5!!+4+>+>#'1$31  )1h&M?  !- 	0dJ&J J 
	Jr   r   c                      \ rS rSrSrSrg)BoundIntervalNetworki  zW
This class is kept only because of the ICMC Paper.  Just use IntervalNetwork instead.
r    N)r!   r"   r#   r$   r%   r(   r    r   r   r  r    s     	r   r  __main__)+r%   
__future__r   collectionsr   collections.abcr   r2  enumtypingtr  r   r   r   r	   r
   r   r   Environmentr  Enumr   r*   r  rd   rN   r  r  r?   rB   Music21ExceptionrD   ProtoM21ObjectrI   SlottedObjectMixinr}   r   r   r  
_DOC_ORDERr!   mainTestr    r   r   <module>r     s2  . # # $           &&'>?'tyy '(		 (  L#s4xT4d:<	L11 	J%7!! J%Z;!7!!6#<#< ;!~	|<< 	,x) x)vS	? 	 tT*
z r   