DSPython  00.03.03 — 25 juin 2012
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
knots.py
Aller à la documentation de ce fichier.
1 #!/usr/bin/env python
2 # -*- coding: latin-1 -*-
3 ##\package DSPython.knots Théorie des \htmlonly nœuds\endhtmlonly
4 #
5 # Cf. \htmlonly <a href="http://www.opimedia.be/Bruno_Marchal/index.htm#Liens" target="_blank">
6 # <tt>http://www.opimedia.be/Bruno_Marchal/index.htm#Liens</tt></a>\endhtmlonly
7 
8 ##\file
9 # Théorie des \htmlonly n&oelig;uds\endhtmlonly
10 #
11 # Cf. \htmlonly <a href="http://www.opimedia.be/Bruno_Marchal/index.htm#Liens" target="_blank">
12 # <tt>http://www.opimedia.be/Bruno_Marchal/index.htm#Liens</tt></a>\endhtmlonly
13 
14 # (c) Olivier Pirson --- DragonSoft
15 # http://www.opimedia.be/DS/
16 # Débuté le 3 mars 2007
17 ####################################
18 from __future__ import print_function
19 
20 ## Date du dernier changement pour ce module
21 VERSION = 'knots --- 2010 April 12'
22 
23 import copy, functools, numbers
24 
25 import DSPython
26 import DSPython.natural as natural
27 import DSPython.polynomial as polynomial
28 
29 
30 
31 # ############
32 # Constantes #
33 ##############
34 ## Caractère pour un espacement vide
35 EMPTY = ' '
36 
37 ## Caractère pour un brin à gauche
38 LEFT = ')'
39 
40 ## Caractère pour un brin à droite
41 RIGHT = '('
42 
43 ## Caractère pour un brin en haut
44 TOP = '^'
45 
46 ## Caractère pour un brin en bas
47 BOTTOM = '_'
48 
49 ## Caractère pour un brin diagonal montant
50 UP = '/'
51 
52 ## Caractère pour un brin diagonal descendant
53 DOWN = '\\'
54 
55 ##\brief Caractère pour deux brins verticaux
56 # (notamment l'état non labellisé d'un croisement CROSSUP A ou CROSSDOWN B)
57 VERT = '"'
58 
59 ##\brief Caractère pour deux brins horizontaux
60 # (notamment l'état non labellisé d'un croisement CROSSUP B ou CROSSDOWN A
61 HORI = '='
62 
63 ## Caractère pour un croisement dont le brin diagonal montant passe au-dessus
64 CROSSUP = '%'
65 
66 ## Caractère pour un croisement dont le brin diagonal descendant passe au-dessus
67 CROSSDOWN = '&'
68 
69 
70 ## Caractère pour l'état labellisé A d'un croisement CROSSUP
71 CROSSUPA = 'A'
72 
73 ## Caractère pour l'état labellisé B d'un croisement CROSSUP
74 CROSSUPB = 'B'
75 
76 ## Caractère pour l'état labellisé A d'un croisement CROSSDOWN
77 CROSSDOWNA = 'a'
78 
79 ## Caractère pour l'état labellisé B d'un croisement CROSSDOWN
80 CROSSDOWNB = 'b'
81 
82 
83 ##\brief Caractère pour un croisement non "orienté"
84 # (croisement dans l'univers du \htmlonly n&oelig;ud\endhtmlonly)
85 CROSS = 'x'
86 
87 
88 ## Caractères permis pour constituer un \htmlonly n&oelig;ud\endhtmlonly
89 KNOTCHARS = EMPTY + LEFT + RIGHT + TOP + BOTTOM + UP + DOWN + VERT + HORI + CROSSUP + CROSSDOWN
90 
91 ## Caractères permis pour constituer un état labellisé
92 LABELSTATECHARS = EMPTY + LEFT + RIGHT + TOP + BOTTOM + UP + DOWN + VERT + HORI \
93  + CROSSUPA + CROSSUPB + CROSSDOWNA + CROSSDOWNB
94 
95 ## Caractères permis pour constituer un état non labellisé
96 STATECHARS = EMPTY + LEFT + RIGHT + TOP + BOTTOM + UP + DOWN + VERT + HORI
97 
98 ## Caractères permis pour constituer l'univers d'un \htmlonly n&oelig;ud\endhtmlonly
99 UNIVERSECHARS = EMPTY + LEFT + RIGHT + TOP + BOTTOM + UP + DOWN + VERT + HORI + CROSS
100 
101 
102 ##\brief <a class="relative" href="piecesknots.png" target="_blank">Caractères permis</a>
103 # (<a class="relative" href="piecesknots.eps" target="_blank">.eps</a>) pour constituer un espace
104 # \htmlonly <a class="relative" href="piecesknots.png" target="_blank"><img
105 # src="piecesknots_th.png" border="0" align="top" alt="[piecesknots_th.png]"></a>\endhtmlonly
106 SPACECHARS = KNOTCHARS + CROSSUPA + CROSSUPB + CROSSDOWNA + CROSSDOWNB + CROSS
107 
108 
109 ##\brief Dictionnaire qui pour chaque type d'élément
110 # contient la description des bouts de \htmlonly n&oelig;ud\endhtmlonly dans le coin considéré :
111 # None si le coin est vide,
112 # sinon un couple (x, y) avec x et y == -1, 0 ou 1
113 # indiquant la direction du bout de \htmlonly n&oelig;ud\endhtmlonly
114 CORNERS = {}
115 CORNERS[EMPTY] = ((None, None),
116  (None, None))
117 CORNERS[LEFT] = (((0, 1), None),
118  ((0, -1), None))
119 CORNERS[RIGHT] = ((None, (0, 1)),
120  (None, (0, -1)))
121 CORNERS[TOP] = (((1, 0), (-1, 0)),
122  (None, None))
123 CORNERS[BOTTOM] = ((None, None),
124  ((1, 0), (-1, 0)))
125 CORNERS[UP] = ((None, (-1, 1)),
126  ((1, -1), None))
127 CORNERS[DOWN] = (((1, 1), None),
128  (None, (-1, -1)))
129 CORNERS[CROSS] = (((1, 1), (-1, 1)),
130  ((1, -1), (-1, -1)))
131 CORNERS[CROSSUP] = CORNERS[CROSS]
132 CORNERS[CROSSDOWN] = CORNERS[CROSS]
133 
134 CORNERS[VERT] = (((0, 1), (0, 1)),
135  ((0, -1), (0, -1)))
136 CORNERS[HORI] = (((1, 0), (-1, 0)),
137  ((1, 0), (-1, 0)))
138 CORNERS[CROSSUPA] = CORNERS[VERT]
139 CORNERS[CROSSUPB] = CORNERS[HORI]
140 CORNERS[CROSSDOWNA] = CORNERS[HORI]
141 CORNERS[CROSSDOWNB] = CORNERS[VERT]
142 
143 
144 ##\brief Dictionnaire de dictionnaires contenant
145 # les premiers \htmlonly n&oelig;uds\endhtmlonly premiers 0<sub>1</sub>,
146 # 3<sub>1</sub>, 4<sub>1</sub>, 5<sub>1</sub>, 5<sub>2</sub>,
147 # 6<sub>1</sub>, 6<sub>2</sub>, 6<sub>3</sub>, 7<sub>1</sub>
148 PRIMEKNOTS = {}
149 PRIMEKNOTS[0] = {}
150 PRIMEKNOTS[0][1] = ['()']
151 PRIMEKNOTS[3] = {}
152 PRIMEKNOTS[3][1] = [' /%\\',
153  '(( &',
154  ' \\%/']
155 PRIMEKNOTS[4] = {}
156 PRIMEKNOTS[4][1] = [' _',
157  ' /% \\',
158  '(( &&',
159  ' \\% /',
160  ' ^']
161 PRIMEKNOTS[5] = {}
162 PRIMEKNOTS[5][1] = [' _',
163  ' /% %\\',
164  '(( ^ ))',
165  ' \\%%%/']
166 PRIMEKNOTS[5][2] = [' _',
167  '/% \\',
168  '& &&',
169  '\\% /',
170  ' ^']
171 PRIMEKNOTS[6] = {}
172 PRIMEKNOTS[6][1] = [' ___',
173  ' /% \\',
174  '(( &&&&',
175  ' \\% /',
176  ' ^^^']
177 PRIMEKNOTS[6][2] = [' _',
178  ' /_\\',
179  ' & &',
180  '( % )',
181  ' & &',
182  ' \\%/']
183 PRIMEKNOTS[6][2] = [' _',
184  ' /% %\\',
185  '(( & &',
186  ' \\% %/',
187  ' ^']
188 PRIMEKNOTS[6][3] = [' _',
189  ' /& &\\',
190  '(( % %',
191  '( & ^/',
192  ' \\& /',
193  ' ^']
194 PRIMEKNOTS[7] = {}
195 PRIMEKNOTS[7][1] = [' _____',
196  '/ \\',
197  '%%%%%%%',
198  '\\ /',
199  ' ^^^^^']
200 
201 
202 ##\brief \htmlonly N&oelig;ud\endhtmlonly borroméen
203 # (un lien de 3 \htmlonly n&oelig;uds\endhtmlonly entremêlés)
204 BORROMEAN = [' /&\\',
205  ' //_\\\\',
206  '( & & )',
207  ' % % %',
208  ' \\^ ^/',
209  ' ^^^']
210 
211 
212 
213 # ###########
214 # Fonctions #
215 #############
216 ## Modifie l'espace s en lui ajoutant l'espace a à droite
217 def addtoright(s, a):
218  """Modifie l'espace s en lui ajoutant l'espace a à droite
219 
220  Pre: s: space
221  a: space
222 
223  Result: None
224 
225  O(s) = ..."""
226  assert space_is(s), s
227  assert space_is(a), a
228 
229  m = 0 # plus grande longueur
230  for y in range(min(len(s), len(a))):
231  m = max(m, len(s[y]))
232  for y in range(min(len(s), len(a))):
233  s[y] += EMPTY*(m - len(s[y])) + a[y]
234  if len(s) < len(a):
235  for y in range(len(s), len(a)):
236  s.append(EMPTY*m + a[y])
237 
238 
239 ## Tous les cycles de l'espace s sont fermés ?
240 def closed_is(s):
241  """Renvoie True si tous les cycles de l'espace s sont fermés,
242  False sinon
243 
244  Pre: s: space
245 
246  Result: boolean
247 
248  O(s) = ..."""
249  assert space_is(s), s
250 
251  prev = '' # avant-première ligne vide
252  for line in s + ['']: # avec une après-dernière ligne vide
253  line_src = line + EMPTY # ajoute un après-dernier élément vide
254  if len(prev) > len(line):
255  line += EMPTY * (len(prev) - len(line))
256  elif len(prev) < len(line):
257  prev += EMPTY * (len(line) - len(prev))
258  prev_c = EMPTY # avant-premier élément vide pour la ligne précédente
259  line_c = EMPTY # avant-premier élément vide pour la ligne courante
260  for i in range(len(line)):
261  # prev_c | prev[i]
262  # -------- --------- le coin courant
263  # line_c | line[i]
264  nb = ((CORNERS[prev_c][1][1] != None) + (CORNERS[prev[i]][1][0] != None)
265  + (CORNERS[line_c][0][1] != None) + (CORNERS[line[i]][0][0] != None))
266  if (nb != 0) and (nb != 2):
267  assert nb == 1, (i, nb, line)
268 
269  return False
270  prev_c = prev[i]
271  line_c = line[i]
272  prev = line_src
273  return True
274 
275 
276 ## s est de type knots ?
277 def knots_is(s):
278  """Renvoie True si l'espace s est de type knots
279  (un espace vide ou contenant des noeuds),
280  False sinon
281 
282  Pre: s: space
283 
284  Result: boolean
285 
286  O(s) = ..."""
287  assert space_is(s), s
288 
289  return links_is(s)#???
290 
291 
292 ## Renvoie la liste ordonnée des combinaisons possibles de labels A et B pour nb croisements
293 def labels_list(nb):
294  """Renvoie la liste ordonnée des combinaisons possibles de labels A et B
295  pour nb croisements
296  (Si nb == 0 alors renvoie ['']
297  Si nb == 1 alors renvoie ['A', 'B']
298  Si nb == 2 alors renvoie ['AA', 'AB', 'BA', 'BB']
299  Si nb == 2 alors renvoie ['AAA', 'AAB', 'ABA', 'ABB',
300  'BAA', 'BAB', 'BBA', 'BBB']
301  ...)
302 
303  Pre: nb: naturel
304 
305  Result: liste de 2**n string de 'A' et 'B' de longueur n
306 
307  O(nb) = ..."""
308  assert DSPython.natural_is(nb), nb
309 
310  if nb > 0:
311  l = []
312  for s in labels_list(nb - 1):
313  l.append(s + 'A')
314  l.append(s + 'B')
315  return l
316  else:
317  return ['']
318 
319 
320 ## s est de type labelstates ?
322  """Renvoie True si l'espace s est de type labelstates
323  (un espace vide ou contenant des états labellisés de noeuds),
324  False sinon
325 
326  Pre: s: space
327 
328  Result: boolean
329 
330  O(s) = ..."""
331  assert space_is(s), s
332 
333  if not isinstance(s, list):
334  return False
335  for line in s:
336  if not isinstance(line, str):
337  return False
338  for c in line:
339  if c not in LABELSTATECHARS:
340  return False
341  return True
342 
343 
344 ## s est de type links ?
345 def links_is(s):
346  """Renvoie True si l'espace s est de type links
347  (un espace vide ou contenant des liens),
348  False sinon
349 
350  Pre: s: space
351 
352  Result: boolean
353 
354  O(s) = ..."""
355  assert space_is(s), s
356 
357  if not isinstance(s, list):
358  return False
359  for line in s:
360  if not isinstance(line, str):
361  return False
362  for c in line:
363  if c not in KNOTCHARS:
364  return False
365  return True
366 
367 
368 ##\brief Modifie l'espace s pour que toutes les lignes aient même longueur
369 # en ajoutant des add à droite
370 def make_egalwidth(s, add=EMPTY):
371  """ Modifie l'espace s pour que toutes les lignes aient même longueur
372  en ajoutant des add à droite
373  !!! Le résultat n'est pas forcément un space
374 
375  Pre: s: space
376  add: caractère de SPACECHARS
377 
378  Result: None
379 
380  O(s) = ..."""
381  assert space_is(s), s
382  assert isinstance(add, str), add
383  assert len(add) == 1, add
384  assert add in SPACECHARS, add
385 
386  m = functools.reduce(lambda m, line: max(m, len(line)), s, 0) # plus grande longueur
387  for y in range(len(s)):
388  s[y] += add*(m - len(s[y]))
389 
390 
391 ## Renvoie l'image miroir de l'espace s
392 def mirror(s):
393  """Renvoie l'image miroir de l'espace s
394 
395  Pre: s: space
396 
397  Result: space
398 
399  O(s) = ..."""
400  assert space_is(s), s
401 
402  CONV = {}
403  CONV[EMPTY] = EMPTY
404  CONV[LEFT] = LEFT
405  CONV[RIGHT] = RIGHT
406  CONV[TOP] = TOP
407  CONV[BOTTOM] = BOTTOM
408  CONV[UP] = UP
409  CONV[DOWN] = DOWN
410  CONV[VERT] = HORI
411  CONV[HORI] = VERT
412  CONV[CROSSUP] = CROSSDOWN
413  CONV[CROSSDOWN] = CROSSUP
414  CONV[CROSSUPA] = CROSSDOWNA
415  CONV[CROSSDOWNA] = CROSSUPA
416  CONV[CROSSUPB] = CROSSDOWNB
417  CONV[CROSSDOWNB] = CROSSUPB
418  CONV[CROSS] = CROSS
419 
420  r = []
421  for line in s:
422  new_line = ''
423  for c in line:
424  new_line += CONV[c]
425  r.append(new_line)
426 
427  assert space_is(r), r
428 
429  return r
430 
431 
432 ## Renvoie le nombre d'états A (croisements up ou down) de l'espace d'états labellisés s
433 def nb_A(s):
434  """Renvoie le nombre d'états A (croisements up ou down)
435  de l'espace d'états labellisés s
436 
437  Pre: s: labelstates
438 
439  Result: naturel
440 
441  O(s) = ..."""
442  assert labelstates_is(s), s
443 
444  return nb_items(s, CROSSUPA + CROSSDOWNA)
445 
446 
447 ## Renvoie le nombre d'états B (croisements up ou down) de l'espace d'états labellisés s
448 def nb_B(s):
449  """Renvoie le nombre d'états B (croisements up ou down)
450  de l'espace d'états labellisés s
451 
452  Pre: s: labelstates
453 
454  Result: naturel
455 
456  O(s) = ..."""
457  assert labelstates_is(s), s
458 
459  return nb_items(s, CROSSUPB + CROSSDOWNB)
460 
461 
462 ##\brief Renvoie le nombre de croisements (up, down ou non "orientés")
463 # et d'états de croisements labellisés de l'espace s
464 def nb_cross(s):
465  """Renvoie le nombre de croisements (up, down ou non "orientés")
466  et d'états de croisements labellisés de l'espace s
467 
468  Pre: s: space
469 
470  Result: naturel
471 
472  O(s) = ..."""
473  assert space_is(s), s
474 
475  return nb_items(s, CROSSUP + CROSSDOWN + CROSSUPA + CROSSUPB + CROSSDOWNA + CROSSDOWNB + CROSS)
476 
477 
478 ## Renvoie (nombre de cycles, nombre de cycles ouverts) de l'espace s
479 def nb_cycle(s):
480  """Renvoie (nombre de cycles, nombre de cycles ouverts) de l'espace s
481 
482  Pre: s: space
483 
484  Result: (naturel, naturel)
485 
486  O(s) = ..."""
487  assert space_is(s), s
488 
489  return Corners(s).nb_cycle()
490 
491 
492 ## Renvoie le nombre d'éléments de l'espace s qui sont dans items
493 def nb_items(s, items):
494  """Renvoie le nombre d'éléments de l'espace s qui sont dans items
495 
496  Pre: s: space
497  items: string de caractères appartenant à SPACECHARS
498 
499  Result: naturel
500 
501  O(s) = ..."""
502  assert space_is(s), s
503  assert isinstance(items, str), items
504  if __debug__:
505  for c in items:
506  assert c in SPACECHARS, (c, items)
507 
508  nb = 0
509  if items != '':
510  for line in s:
511  for c in line:
512  if c in items:
513  nb += 1
514  return nb
515 
516 
517 ## Renvoie le polynôme invariant de Kauffman pour l'espace de \htmlonly n&oelig;uds\endhtmlonly s
519  """Renvoie le polynôme invariant de Kauffman pour l'espace de noeuds s,
520  c.-à-d. (-A^(-3))^writhe(s) * poly_Kauffman_A(s)
521  (invariant pour les isotopies :
522  mouvements 0, I, II et III de Reidemeister)
523 
524  Pre: s: knots non vide
525 
526  Result: Polynomial
527 
528  O(s) = ..."""
529  assert knots_is(s), s
530  if __debug__:
531  nb = nb_cycle(s)
532  assert nb[0] > 0, nb[0]
533  assert nb[1] == 0, nb[1]
534 
535  p = poly_Kauffman_A(s)
536  w = writhe(s)
538  if w%2 == 0:
539  for k in p:
540  r[polynomial.Term((k[0] - 3*w, ))] = p[k]
541  else:
542  for k in p:
543  r[polynomial.Term((k[0] - 3*w, ))] = -p[k]
544  return r
545 
546 
547 ## Renvoie le polynôme A de Kauffman pour l'espace de \htmlonly n&oelig;uds\endhtmlonly s
549  """Renvoie le polynôme A de Kauffman pour l'espace de noeuds s,
550  c.-à-d. le polynôme ABd de Kauffman avec B = A^-1 et d = -A^2 - A^-2
551  (invariant pour les isotopies régulières :
552  mouvements 0, II et III de Reidemeister)
553 
554  Pre: s: knots non vide
555 
556  Result: Polynomial
557 
558  O(s) = ..."""
559  assert knots_is(s), s
560  if __debug__:
561  nb = nb_cycle(s)
562  assert nb[0] > 0, nb[0]
563  assert nb[1] == 0, nb[1]
564 
565  p = polynomial.Polynomial(varsstr='A')
566  for labels in labels_list(nb_cross(s)):
567  st = to_labelstates(s, labels)
568  d_e = nb_cycle(st)[0] - 1
569  e = nb_A(st) - nb_B(st) - d_e*2
570  sign = (1 if d_e%2 == 0
571  else -1)
572  for i in range(d_e + 1):
573  p.__iadd__(polynomial.Term((e + i*4, )), coef=natural.binom(d_e, i)*sign)
574  return p
575 
576 
577 ## Renvoie le polynôme ABd de Kauffman pour l'espace de \htmlonly n&oelig;uds\endhtmlonly s
579  """Renvoie le polynôme ABd de Kauffman pour l'espace de noeuds s
580  = somme sur tous les états labellisés de s
581  du produit des labels A et B
582  et de d^(nombre de cycles fermés - 1) de l'état
583 
584  Pre: s: knots
585 
586  Result: Polynomial
587 
588  O(s) = ..."""
589  assert knots_is(s), s
590  assert nb_cycle(s)[1] == 0, nb_cycle(s)[1]
591 
592  p = polynomial.Polynomial(varsstr='ABd')
593  for labels in labels_list(nb_cross(s)):
594  st = to_labelstates(s, labels)
595  p += polynomial.Term((nb_A(st), nb_B(st), nb_cycle(st)[0] - 1))
596  return p
597 
598 
599 ## Renvoie le \htmlonly n&oelig;ud\endhtmlonly premier n<sub>1</sub>
601  """Renvoie le noeud premier n_1
602 
603  Pre: n: naturel pair >= 4
604 
605  Result: knots
606 
607  O(n) = ..."""
608  assert n >= 4, n
609 
610  n -= 3
611  k = [' ' + '_'*n,
612  ' /%' + ' '*n + '\\',
613  '(( &' + '&'*n,
614  ' \\%' + ' '*n + '/',
615  ' ' + '^'*n]
616  return k
617 
618 
619 ## Renvoie l'espace lu dans le fichier f
620 def read(f):
621  """Renvoie l'espace lu dans le fichier f
622 
623  Pre: f: fichier (texte) ouvert en lecture
624 
625  Result: list de string (qui n'est pas forcément un space !!!)
626 
627  O(f) = ..."""
628 # assert isinstance(f, file), type(f)
629 
630  l = f.read().splitlines()
631  r = []
632  for s in l:
633  if s != '':
634  r.append(s)
635  return r
636 
637 
638 ## Renvoie l'espace s avec une rotation de nb*90° dans le sens anti-horloger
639 def rotate(s, nb=1):
640  """Renvoie l'espace s avec une rotation de nb*90°
641  dans le sens anti-horloger
642 
643  Pre: s: space
644  nb: Integral
645 
646  Result: space
647 
648  O(s) = ..."""
649  assert space_is(s), s
650  assert isinstance(nb, numbers.Integral), nb
651 
652  CONV = {}
653  CONV[EMPTY] = EMPTY
654  CONV[LEFT] = BOTTOM
655  CONV[RIGHT] = TOP
656  CONV[TOP] = LEFT
657  CONV[BOTTOM] = RIGHT
658  CONV[UP] = DOWN
659  CONV[DOWN] = UP
660  CONV[VERT] = HORI
661  CONV[HORI] = VERT
662  CONV[CROSSUP] = CROSSDOWN
663  CONV[CROSSDOWN] = CROSSUP
664  CONV[CROSSUPA] = CROSSDOWNA
665  CONV[CROSSDOWNA] = CROSSUPA
666  CONV[CROSSUPB] = CROSSDOWNB
667  CONV[CROSSDOWNB] = CROSSUPB
668  CONV[CROSS] = CROSS
669 
670  nb %= 4
671  m = functools.reduce(lambda m, line: max(m, len(line)), s, 0) # plus grande longueur
672  while nb > 0:
673  r = [''] * m # m colonnes donne m lignes
674  for y in range(len(s)):
675  line = s[y] + EMPTY*(m - len(s[y]))
676  for x in range(len(line)):
677  r[m - 1 - x] += CONV[line[x]]
678 
679  assert space_is(r), r
680 
681  m = len(s)
682  s = r
683  nb -= 1
684  return s
685 
686 
687 ## s est de type space ?
688 def space_is(s):
689  """Renvoie True si s est de type space
690  (un espace vide ou contenant des noeuds,
691  états de noeuds (labellisés ou non) et univers de noeuds),
692  False sinon
693 
694  Pre: s: object quelconque
695 
696  Result: boolean
697 
698  O(s) = ..."""
699  if not isinstance(s, list):
700  return False
701  prev = '' # avant-première ligne vide
702  for line in s + ['']: # avec une après-dernière ligne vide
703  if not isinstance(line, str):
704  return False
705  line_src = line + EMPTY # ajoute un après-dernier élément vide
706  if len(prev) > len(line):
707  line += EMPTY * (len(prev) - len(line))
708  elif len(prev) < len(line):
709  prev += EMPTY * (len(line) - len(prev))
710  prev_c = EMPTY # avant-premier élément vide pour la ligne précédente
711  line_c = EMPTY # avant-premier élément vide pour la ligne courante
712  if line_c not in SPACECHARS:
713  return False
714  for i in range(len(line)):
715  if line[i] not in SPACECHARS:
716  return False
717  # prev_c | prev[i]
718  # -------- --------- le coin courant
719  # line_c | line[i]
720  nb = ((CORNERS[prev_c][1][1] != None) + (CORNERS[prev[i]][1][0] != None)
721  + (CORNERS[line_c][0][1] != None) + (CORNERS[line[i]][0][0] != None))
722  if nb > 2:
723  return False
724  prev_c = prev[i]
725  line_c = line[i]
726  prev = line_src
727  return True
728 
729 
730 ## s est de type states ?
731 def states_is(s):
732  """Renvoie True si l'espace s est de type states
733  (un espace vide ou contenant des états non labellisés de noeuds),
734  False sinon
735 
736  Pre: s: space
737 
738  Result: boolean
739 
740  O(s) = ..."""
741  assert space_is(s), s
742 
743  if not isinstance(s, list):
744  return False
745  for line in s:
746  if not isinstance(line, str):
747  return False
748  for c in line:
749  if c not in STATECHARS:
750  return False
751  return True
752 
753 
754 ## Renvoie l'état labellisé de l'espace s de \htmlonly n&oelig;uds\endhtmlonly ou d'états labellisés
755 def to_labelstates(s, labels=''):
756  """Renvoie l'état labellisé de l'espace s de noeuds ou d'états labellisés
757  Le parcours de s se fait de la gauche vers la droite,
758  du haut vers le bas.
759  Chaque croisement est alors remplacé par son état labellisé
760  en fonction du 'A' ou 'B' dans labels.
761  Lorsque les caractères de labels sont épuisés,
762  c'est l'état A qui est choisi.
763 
764  Pre: s: knots ou labelstates
765  (ou space mélange des deux,
766  mais ne contenant pas d'état non labellisé
767  ou de croisement non "orienté")
768  labels: string de 'A' et 'B'
769 
770  Result: labelstates
771 
772  O(s) = ..."""
773  assert space_is(s), s
774 
775  new_s = []
776  for line in s:
777  new_line = ''
778  for c in line:
779  assert c != CROSS
780 
781  if c in CROSSUP + CROSSUPA + CROSSUPB :
782  new_line += (CROSSUPA if (labels == '') or (labels[0] == 'A')
783  else CROSSUPB)
784  labels = labels[1:]
785  elif c in CROSSDOWN + CROSSDOWNA + CROSSDOWNB:
786  new_line += (CROSSDOWNA if (labels == '') or (labels[0] == 'A')
787  else CROSSDOWNB)
788  labels = labels[1:]
789  else:
790  new_line += c
791  new_s.append(new_line)
792  return new_s
793 
794 
795 ##\brief Renvoie un string contenant le code L<sup>A</sup>T<sub>E</sub>X
796 # (pour le module Knots.sty) de la représentation de l'espace s
797 
798 ## Module L<sup>A</sup>T<sub>E</sub>X :
799 # <a class="relative" href="latex/Knots.sty" target="_blank">Knots.sty</a>\n
800 # Documentation et exemples :
801 # <a class="relative" href="latex/Knots.tex" target="_blank">Knots.tex</a>,
802 # <a class="relative" href="latex/Knots.ps" target="_blank">Knots.ps</a>
803 def to_LaTeX(s, length=None, shape=None, grid=None):
804  """Renvoie un string contenant le code LaTeX
805  (pour le module Knots.sty :
806  http://www.opimedia.be/DS/DSPython/)
807  de la représentation de l'espace s
808  length : taille du quadrillage
809  (si None alors utilise la valeur de Knots.sty : par défaut 20)
810  shape : détermine la forme des arrondis
811  (si None alors utilise la valeur de Knots.sty : par défaut 5)
812  grid : Si True alors affiche le quadrillage, sinon pas
813  (si None
814  alors utilise la valeur de Knots.sty : par défaut False)
815 
816  Pre: s: space
817  length: None ou naturel > 0
818  shape: None ou naturel > 0
819  grid: None ou boolean
820 
821  Result: string
822 
823  O(s, ...) = ..."""
824  assert space_is(s), s
825  assert length == None or DSPython.natural_is(length), length
826  assert length == None or length > 0, length
827  assert shape == None or DSPython.natural_is(shape), shape
828  assert shape == None or shape > 0, shape
829 
830  r = ['\\Knots{{{0}}}{{{1}}}{{%\n'
831  .format(functools.reduce(lambda m, line: max(m, len(line)), s, 0),
832  len(s))]
833 
834  if length != None:
835  r.append(' \\setcounter{{Knotslen}}{0}%\n'.format(length))
836  if shape != None:
837  r.append(' \\setcounter{{Knotsshape}}{{\\theKnotslen/{0}}}%\n'.format(shape))
838  if grid != None:
839  r.append(' \\Kgridtrue\n' if grid
840  else ' \\Kgridfalse\n')
841 
842  CONV = {} # dictionnaire pour convertir les éléments de la grille en leur équivalant LaTeX
843  CONV[EMPTY] = r'\e'
844  CONV[LEFT] = r'\)'
845  CONV[RIGHT] = r'\('
846  CONV[TOP] = r'\^'
847  CONV[BOTTOM] = r'\_'
848  CONV[UP] = r'\/'
849  CONV[DOWN] = r'\\'
850  CONV[VERT] = r'\''
851  CONV[HORI] = r'\='
852  CONV[CROSSUP] = r'\%'
853  CONV[CROSSDOWN] = r'\&'
854  CONV[CROSSUPA] = r'\A'
855  CONV[CROSSUPB] = r'\B'
856  CONV[CROSSDOWNA] = r'\a'
857  CONV[CROSSDOWNB] = r'\b'
858  CONV[CROSS] = r'\x'
859 
860  for line in s:
861  r.append(' ')
862  for c in line:
863  r.append(CONV[c])
864  r += '\\n\n'
865  r.append('}')
866  return ''.join(r)
867 
868 
869 ## Renvoie un string contenant le code PostScript de la représentation de l'espace s
870 def to_PS(s, length=30, margin=10, shape=5, grid=False):
871  """Renvoie un string contenant le code PostScript
872  de la représentation de l'espace s
873  length : taille du quadrillage
874  margin : taille des marges
875  shape : détermine la forme des arrondis
876  grid : Si True alors affiche le quadrillage, sinon pas
877 
878  Pre: s: space
879  length: naturel > 0
880  margin: naturel
881  shape: réel != 0
882  grid: boolean
883 
884  Result: string
885 
886  O(s, ...) = ..."""
887  assert space_is(s), s
888  assert DSPython.natural_is(length), length
889  assert length > 0, length
890  assert DSPython.natural_is(margin), margin
891  assert isinstance(shape, numbers.Real), shape
892  assert shape != 0, shape
893 
894  grid = ('true' if grid
895  else 'false')
896 
897  r = ["""%!PS-Adobe-3.0 EPSF-3.0
898 %%BoundingBox: 0 0 {0} {1}
899 """.format(functools.reduce(lambda m, line: max(m, len(line)), s, 0)*length + margin*2,
900  len(s)*length + margin*2),
901  """%%Creator: DSPython --- http://www.opimedia.be/DS/DSPython/
902 %%EndComments
903 
904 % Procedures
905 %%%%%%%%%%%%%
906 /bottom {
907  grid { lightbox } if
908  currentpoint
909  /y exch def
910  /x exch def
911  x y
912  x y shape add
913  x len add y shape add
914  x len add y curveto
915  stroke
916 } def
917 
918 /cross {
919  grid { lightbox } if
920  len len rlineto
921  0 len neg rmoveto
922  len neg len rlineto
923  stroke
924 } def
925 
926 /crossdown {
927  grid { lightbox } if
928  len 1.9 mul 5 div dup rlineto
929  len 1.2 mul 5 div dup rmoveto
930  len 1.9 mul 5 div dup rlineto
931  0 len neg rmoveto
932  len neg len rlineto
933  stroke
934 } def
935 
936 /crossdownA {
937  grid { lightbox } if
938  currentpoint
939  gsave
940  .7 setgray
941  .3 setlinewidth
942  len 2 div shape .8 mul rmoveto
943  0 len shape 1.6 mul sub rlineto
944  stroke
945  grestore
946  hori
947  moveto
948  len size sub 2 div dup len sub rmoveto
949  (A) show
950 } def
951 
952 /crossdownB {
953  grid { lightbox } if
954  currentpoint
955  gsave
956  .7 setgray
957  .3 setlinewidth
958  shape .8 mul len 2 div rmoveto
959  len shape 1.6 mul sub 0 rlineto
960  stroke
961  grestore
962  vert
963  moveto
964  len size sub 2 div dup exch len sub exch rmoveto
965  (B) show
966 } def
967 
968 /crossup {
969  grid { lightbox } if
970  len len rlineto
971  0 len neg rmoveto
972  len 1.9 mul 5 div neg dup neg rlineto
973  len 1.2 mul 5 div neg dup neg rmoveto
974  len 1.9 mul 5 div neg dup neg rlineto
975  stroke
976 } def
977 
978 /crossupA {
979  grid { lightbox } if
980  currentpoint
981  gsave
982  .7 setgray
983  .3 setlinewidth
984  shape .8 mul len 2 div rmoveto
985  len shape 1.6 mul sub 0 rlineto
986  stroke
987  grestore
988  vert
989  moveto
990  len size sub 2 div dup exch len sub exch rmoveto
991  (A) show
992 } def
993 
994 /crossupB {
995  grid { lightbox } if
996  currentpoint
997  gsave
998  .7 setgray
999  .3 setlinewidth
1000  len 2 div shape .8 mul rmoveto
1001  0 len shape 1.6 mul sub rlineto
1002  stroke
1003  grestore
1004  hori
1005  moveto
1006  len size sub 2 div dup len sub rmoveto
1007  (B) show
1008 } def
1009 
1010 /down {
1011  grid { lightbox } if
1012  0 len rmoveto
1013  len len neg rlineto
1014  stroke
1015 } def
1016 
1017 /frame {
1018  /h exch def
1019  /w exch def
1020  w 0 rlineto
1021  0 h rlineto
1022  w neg 0 rlineto
1023  0 h neg rlineto
1024 } def
1025 
1026 /hori {
1027  grid { lightbox } if
1028  currentpoint
1029  bottom
1030  moveto
1031  top
1032 } def
1033 
1034 /left {
1035  grid { lightbox } if
1036  currentpoint
1037  /y exch def
1038  /x exch def
1039  x y
1040  x shape add y
1041  x shape add y len add
1042  x y len add curveto
1043  stroke
1044 } def
1045 
1046 /lightbox {
1047  gsave
1048  .7 setgray
1049  .3 setlinewidth
1050  len len frame
1051  stroke
1052  grestore
1053 } def
1054 
1055 /right {
1056  grid { lightbox } if
1057  len 0 rmoveto
1058  currentpoint
1059  /y exch def
1060  /x exch def
1061  x y
1062  x shape sub y
1063  x shape sub y len add
1064  x y len add curveto
1065  stroke
1066 } def
1067 
1068 /top {
1069  grid { lightbox } if
1070  0 len rmoveto
1071  currentpoint
1072  /y exch def
1073  /x exch def
1074  x y
1075  x y shape sub
1076  x len add y shape sub
1077  x len add y curveto
1078  stroke
1079 } def
1080 
1081 /up {
1082  grid { lightbox } if
1083  len len rlineto
1084  stroke
1085 } def
1086 
1087 /vert {
1088  grid { lightbox } if
1089  currentpoint
1090  left
1091  moveto
1092  right
1093 } def
1094 
1095 % Variables globales
1096 %%%%%%%%%%%%%%%%%%%%%
1097 """,
1098  """/grid {0} def % si true alors affiche les cadres en gris, sinon ne les affiche pas
1099 /len {1} def % taille des cadres
1100 /shape len {2} div def % taille pour l'arrondi
1101 """.format(grid, length, shape),
1102  """/size len 2 div 1.5 div def % taille de la police de caracteres
1103 
1104 % Main
1105 %%%%%%%
1106 /Times-Roman findfont
1107 size 1.5 mul scalefont
1108 setfont
1109 1 setlinecap
1110 
1111 """]
1112 
1113  CONV = {} # dictionnaire pour convertir les éléments de la grille en leur équivalant PostScript
1114  CONV[EMPTY] = 'grid { lightbox } if'
1115  CONV[LEFT] = 'left'
1116  CONV[RIGHT] = 'right'
1117  CONV[TOP] = 'top'
1118  CONV[BOTTOM] = 'bottom'
1119  CONV[UP] = 'up'
1120  CONV[DOWN] = 'down'
1121  CONV[VERT] = 'vert'
1122  CONV[HORI] = 'hori'
1123  CONV[CROSSUP] = 'crossup'
1124  CONV[CROSSDOWN] = 'crossdown'
1125  CONV[CROSSUPA] = 'crossupA'
1126  CONV[CROSSUPB] = 'crossupB'
1127  CONV[CROSSDOWNA] = 'crossdownA'
1128  CONV[CROSSDOWNB] = 'crossdownB'
1129  CONV[CROSS] = 'cross'
1130 
1131  y = len(s)*length + margin
1132  for line in s:
1133  y -= length
1134  x = margin
1135  for c in line:
1136  r.append('{0} {1} moveto '.format(x, y) + CONV[c] + '\n')
1137  x += length
1138  r += '\n'
1139  r.append("""showpage
1140 %%EOF""")
1141  return ''.join(r)
1142 
1143 
1144 ##\brief Renvoie l'état non labellisé de l'espace s de \htmlonly n&oelig;uds\endhtmlonly
1145 # ou d'états (labellisés ou non)
1146 def to_states(s, labels=''):
1147  """Renvoie l'état non labellisé de l'espace s
1148  de noeuds ou d'états (labellisés ou non).
1149  Le parcours de s se fait de la gauche vers la droite,
1150  du haut vers le bas.
1151  Chaque croisement est alors remplacé par son état non labellisé
1152  en fonction du 'A' ou 'B' dans labels.
1153  Lorsque les caractères de labels sont épuisés,
1154  c'est l'état A qui est choisi.
1155 
1156  Pre: s: noeuds, labelstates ou states
1157  (ou space mélange des trois,
1158  mais ne contenant pas de croisement non "orienté")
1159  labels: string de 'A' et 'B'
1160 
1161  Result: states
1162 
1163  O(s) = ..."""
1164  assert space_is(s), s
1165 
1166  new_s = []
1167  for line in s:
1168  new_line = ''
1169  for c in line:
1170  assert c != CROSS
1171 
1172  if c in CROSSUP + CROSSUPA + CROSSUPB :
1173  new_line += (VERT if (labels == '') or (labels[0] == 'A')
1174  else HORI)
1175  labels = labels[1:]
1176  elif c in CROSSDOWN + CROSSDOWNA + CROSSDOWNB:
1177  new_line += (HORI if (labels == '') or (labels[0] == 'A')
1178  else VERT)
1179  labels = labels[1:]
1180  else:
1181  new_line += c
1182  new_s.append(new_line)
1183  return new_s
1184 
1185 
1186 ## Renvoie l'espace s dans un string avec retour à la ligne
1187 def to_str(s):
1188  """Renvoie l'espace s dans un string avec retour à la ligne
1189 
1190  Pre: s: space
1191 
1192  Result: string
1193 
1194  O(s) = ..."""
1195  assert space_is(s), s
1196 
1197  return '\n'.join(s)
1198 
1199 
1200 ## Renvoie l'univers des \htmlonly n&oelig;uds\endhtmlonly de l'espace s
1202  """Renvoie l'univers des noeuds de l'espace s
1203 
1204  Pre: s: space
1205 
1206  Result: universes
1207 
1208  O(s) = ..."""
1209  assert space_is(s), s
1210 
1211  new_s = []
1212  for line in s:
1213  new_line = ''
1214  for c in line:
1215  new_line += (CROSS if c in CROSSUP + CROSSDOWN + CROSSUPA + CROSSUPB
1216  + CROSSDOWNA + CROSSDOWNB
1217  else c)
1218  new_s.append(new_line)
1219  return new_s
1220 
1221 
1222 ## s est de type universes ?
1224  """Renvoie True si l'espace s est de type universes
1225  (un espace vide ou contenant des univers de noeud),
1226  False sinon
1227 
1228  Pre: s: space
1229 
1230  Result: boolean
1231 
1232  O(s) = ..."""
1233  assert space_is(s), s
1234 
1235  if not isinstance(s, list):
1236  return False
1237  for line in s:
1238  if not isinstance(line, str):
1239  return False
1240  for c in line:
1241  if c not in UNIVERSECHARS:
1242  return False
1243  return True
1244 
1245 
1246 ## Écrit l'espace s dans le fichier f
1247 def write(f, s):
1248  """Écrit l'espace s dans le fichier f
1249 
1250  Pre: f: fichier (texte) ouvert en écriture
1251  s: space
1252 
1253  Result: None
1254 
1255  O(f, s) = ..."""
1256 # assert isinstance(f, file), type(f)
1257  assert space_is(s), s
1258 
1259  f.writelines([l + '\n' for l in s])
1260 
1261 
1262 ## Renvoie le degré de torsion du space s
1263 def writhe(s):
1264  """Renvoie le degré de torsion du space s,
1265  c.-à-d. la somme des degrés de torsion pour tous ses croisements
1266 
1267  Pre: s: knots dont tous les croisements sont dans 1 ou 2 cycles fermés
1268 
1269  Result: Integral
1270 
1271  O(s) = ..."""
1272  assert knots_is(s), s
1273 
1274  a = Corners(s)
1275  w = 0
1276  for y in range(len(s)):
1277  line = s[y]
1278  for x in range(len(line)):
1279  c = line[x]
1280  if (c == CROSSUP) or (c == CROSSDOWN):
1281  w += a.writhe_cross(x, y, c == CROSSUP)
1282  return w
1283 
1284 
1285 ## Renvoie le degré de torsion du croisement "orienté" placé en (x, y) du space s
1286 def writhe_cross(s, x, y):
1287  """Renvoie le degré de torsion du croisement "orienté"
1288  placé en (x, y) du space s.
1289  Si les deux brins du croisement
1290  font partie de 2 cycles différents alors renvoie 0,
1291  sinon renvoie -1 ou 1.
1292 
1293  Pre: s: space tel que s[y][x] == CROSSUP ou CROSSDOWN
1294  dans 1 ou 2 cycles fermés
1295  x: natural
1296  y: natural
1297 
1298  Result: -1, 0 ou 1
1299 
1300  O(s) = ..."""
1301  assert space_is(s), s
1302  assert DSPython.natural_is(x), x
1303  assert DSPython.natural_is(y), y
1304  assert s[y][x] == CROSSUP or s[y][x] == CROSSDOWN, (x, y, s[y][x])
1305 
1306  return Corners(s).writhe_cross(x, y, s[y][x] == CROSSUP)
1307 
1308 
1309 
1310 # ########
1311 # Classe #
1312 ##########
1313 ##\brief Tableau (list de list) "des coins" d'un space.
1314 # Pour chaque coin, un list de couples (x, y) avec x et y == -1, 0 ou 1.
1315 class Corners(list):
1316  """Tableau (list de list) "des coins" d'un space.
1317  Pour chaque coin, un list de couples (x, y) avec x et y == -1, 0 ou 1"""
1318 
1319  ## Initialise le Corners à partir du space value
1320  def __init__(self, value):
1321  """Initialise le Corners à partir du space value
1322 
1323  Pre: value: space
1324 
1325  Result: None
1326 
1327  O() = ..."""
1328  assert space_is(value), value
1329 
1330  nb = functools.reduce(lambda m, line: max(m, len(line)),
1331  value, 0) + 1 # plus grande longueur + 1
1332  list.__init__(self, [None] * (len(value) + 1)) # tableau de (len(value) + 1) lignes
1333 
1334  # Première ligne du Corners initialisée avec nb éléments list
1335  self[0] = [None] * nb
1336  for x in range(nb):
1337  self[0][x] = []
1338 
1339  # Parcourt chaque ligne du space et construit le Corners
1340  for y in range(len(value)):
1341  # Ligne suivante du Corners initialisée avec nb éléments list
1342  self[y + 1] = [None] * nb
1343  for x in range(nb):
1344  self[y + 1][x] = []
1345 
1346  # Pour chaque élément de la ligne du space
1347  line = value[y]
1348  for x in range(len(line)):
1349  c = CORNERS[line[x]]
1350  if c[0][0] != None:
1351  self[y][x].append(c[0][0])
1352  if c[0][1] != None:
1353  self[y][x + 1].append(c[0][1])
1354  if c[1][0] != None:
1355  self[y + 1][x].append(c[1][0])
1356  if c[1][1] != None:
1357  self[y + 1][x + 1].append(c[1][1])
1358 
1359 
1360  ##\brief Si le coin (x, y) est vide ou passe par un cycle fermé alors renvoie (x, y),
1361  # sinon renvoie les coordonnées d'une extrémité du cycle ouvert passant par (x, y)
1362  def extremity(self, x, y):
1363  """Si le coin (x, y) est vide ou passe par un cycle fermé
1364  alors renvoie (x, y),
1365  sinon renvoie les coordonnées d'une extrémité
1366  du cycle ouvert passant par (x, y)
1367 
1368  Pre: x: naturel
1369  y: naturel
1370 
1371  Result: (naturel, naturel)
1372 
1373  O(s) = ..."""
1374  assert DSPython.natural_is(x), x
1375  assert DSPython.natural_is(y), y
1376 
1377  # Fait le tour du cycle s'il est fermé, sinon s'arrête sur une extrémité
1378  add_x, add_y = self[y][x][0]
1379  moving_x = x + add_x
1380  moving_y = y + add_y
1381  while (moving_x != x or moving_y != y) and len(self[moving_y][moving_x]) != 1:
1382  assert len(self[moving_y][moving_x]) == 2, \
1383  (x, y, moving_x, moving_y, self[moving_y][moving_x])
1384 
1385  add_x, add_y = (self[moving_y][moving_x][
1386  # self[moving_y][moving_x][0] est le "brin réciproque"
1387  1 if ((self[moving_y][moving_x][0][0] == -add_x)
1388  and (self[moving_y][moving_x][0][1] == -add_y))
1389  # self[moving_y][moving_x][0] n'est pas le "brin réciproque"
1390  else 0])
1391  moving_x += add_x
1392  moving_y += add_y
1393  x = moving_x
1394  y = moving_y
1395  return (x, y)
1396 
1397 
1398  ## Renvoie (nombre de cycles, nombre de cycles ouverts) du Corners
1399  def nb_cycle(self):
1400  """Renvoie (nombre de cycles, nombre de cycles ouverts) du Corners
1401 
1402  Result: (naturel, naturel)
1403 
1404  O(s) = ..."""
1405  a = copy.deepcopy(self)
1406  # Parcourt le Corners en comptabilisant les cycles et les cycles ouverts
1407  nb = 0 # nombre de cycles
1408  nb_open = 0 # nombre de cycles ouverts
1409  for y in range(len(a)):
1410  for x in range(len(a[y])):
1411  if a[y][x] == []:
1412  continue
1413  (x, y) = a.extremity(x, y)
1414 
1415  assert a[y][x] != [], (x, y)
1416 
1417  # Suit le cycle jusqu'à avoir fait le tour ou qu'il se termine,
1418  # en enlevant les brins parcourus
1419  add_x, add_y = a[y][x][0]
1420  a[y][x] = a[y][x][1:]
1421  moving_x = x + add_x
1422  moving_y = y + add_y
1423  i = a[moving_y][moving_x].index((-add_x, -add_y)) # cherche le "brin réciproque"
1424  a[moving_y][moving_x] = a[moving_y][moving_x][:i] + a[moving_y][moving_x][i + 1:]
1425  while (moving_x != x or moving_y != y) and a[moving_y][moving_x] != []:
1426  add_x, add_y = a[moving_y][moving_x][0]
1427  a[moving_y][moving_x] = a[moving_y][moving_x][1:]
1428  moving_x += add_x
1429  moving_y += add_y
1430  i = a[moving_y][moving_x].index((-add_x, -add_y)) # cherche le "brin réciproque"
1431  a[moving_y][moving_x] = (a[moving_y][moving_x][:i]
1432  + a[moving_y][moving_x][i + 1:])
1433  if (moving_x == x) and (moving_y == y): # on a fait un tour complet
1434  nb += 1
1435  else: # on est arrivé sur une extrémité
1436  nb_open += 1
1437 
1438  assert a[y][x] == [], (x, y, a[y][x])
1439 
1440  return (nb, nb_open)
1441 
1442 
1443  ## Renvoie le degré de torsion du croisement "orienté" placé entre (x, y) et (x + 1, y + 1)
1444  def writhe_cross(self, x, y, crossup):
1445  """Renvoie le degré de torsion du croisement "orienté"
1446  placé entre (x, y) et (x + 1, y + 1).
1447  Si les deux brins du croisement font partie
1448  de 2 cycles différents alors renvoie 0,
1449  sinon renvoie -1 ou 1.
1450 
1451  Pre: l'élément entre (x, y) et (x + 1, y + 1)
1452  est un CROSSUP si crossup == True
1453  ou un CROSSDOWN si crossup == False,
1454  faisant partie de 1 ou de 2 cycles fermés
1455  x: natural
1456  y: natural
1457  crossup: boolean
1458 
1459  Result: -1, 0 ou 1
1460 
1461  O(s) = ..."""
1462 
1463  assert DSPython.natural_is(x), x
1464  assert DSPython.natural_is(y), y
1465  assert self.extremity(x, y) == (x, y), (self.extremity(x, y), (x, y))
1466 
1467  # Fait un presque tour du cycle jusqu'à retomber sur le croisement
1468  add_x, add_y = self[y][x][# le mouvement self[y][x][0] traverse le croisement
1469  1 if self[y][x][0] == (1, 1)
1470  # le mouvement self[y][x][0] quitte le croisement
1471  else 0]
1472  moving_x = x + add_x
1473  moving_y = y + add_y
1474  while not (0 <= moving_x - x <= 1 and 0 <= moving_y - y <= 1):
1475  assert len(self[moving_y][moving_x]) == 2, \
1476  (x, y, moving_x, moving_y, self[moving_y][moving_x])
1477 
1478  add_x, add_y = self[moving_y][moving_x][
1479  # self[moving_y][moving_x][0] est le "brin réciproque"
1480  1 if ((self[moving_y][moving_x][0][0] == -add_x)
1481  and (self[moving_y][moving_x][0][1] == -add_y))
1482  # self[moving_y][moving_x][0] n'est pas le "brin réciproque"
1483  else 0]
1484  moving_x += add_x
1485  moving_y += add_y
1486  add_x = moving_x - x
1487  add_y = moving_y - y
1488 
1489  assert add_x == 0 or add_x == 1, add_x
1490  assert add_y == 0 or add_y == 1, add_y
1491  assert add_x == 1 or add_y == 1, (add_x, add_y)
1492 
1493  if (add_x == 0) or (add_y == 0): # le croisement fait partie d'un seul cycle
1494  if crossup:
1495  return (1 if add_x == 0
1496  else -1)
1497  else:
1498  return (-1 if add_x == 0
1499  else 1)
1500  else: # le croisement fait partie de deux cycles
1501  return 0
1502 
1503 
1504 
1505 # ######\cond MAINTEST
1506 # Main #
1507 ########
1508 if __name__ == '__main__':
1509  def main_test():
1510  """Test du module"""
1511  import os, sys, tempfile
1512 
1513  try:
1514  if not 'profile' in dir():
1515  import psyco
1516  psyco.full()
1517  except ImportError:
1518  pass
1519 
1520  import DSPython.debug as debug
1521 
1522  debug.test_begin(VERSION, __debug__)
1523 
1524  print("SPACECHARS == '{0}'".format(SPACECHARS)); sys.stdout.flush()
1525  for c in SPACECHARS:
1526  assert 32 <= ord(c) <= 127, c
1527 
1528  print("KNOTCHARS == '{0}'".format(KNOTCHARS)); sys.stdout.flush()
1529  print("LABELSTATECHARS == '{0}'".format(LABELSTATECHARS)); sys.stdout.flush()
1530  print("STATECHARS == '{0}'".format(STATECHARS)); sys.stdout.flush()
1531  print("UNIVERSECHARS == '{0}'".format(UNIVERSECHARS)); sys.stdout.flush()
1532 
1533 
1534  print('CORNERS...', end=''); sys.stdout.flush()
1535  assert len(CORNERS) == len(SPACECHARS), (len(CORNERS), len(SPACECHARS))
1536  for i in SPACECHARS:
1537  assert isinstance(CORNERS[i], tuple)
1538  assert len(CORNERS[i]) == 2, (i, CORNERS[i])
1539  nb = 0
1540  for y in range(2):
1541  assert len(CORNERS[i][y]) == 2, (i, y, CORNERS[i][y])
1542  for x in range(2):
1543  c = CORNERS[i][y][x]
1544  assert c == None or len(c) == 2, (i, y, x, c)
1545  if c != None:
1546  assert c != (0, 0), (i, y, x, c)
1547  t = CORNERS[i][y + c[1]][x + c[0]]
1548  assert t[0] == -c[0], (i, y, x, c, t)
1549  assert t[1] == -c[1], (i, y, x, c, t)
1550  nb += 1
1551  if i == EMPTY:
1552  assert nb == 0, (i, nb, CORNERS[i])
1553  elif i in (LEFT, RIGHT, TOP, BOTTOM, UP, DOWN):
1554  assert nb == 2, (i, nb, CORNERS[i])
1555  else:
1556  assert nb == 4, (i, nb, CORNERS[i])
1557  print('ok'); sys.stdout.flush()
1558 
1559 
1560  print('PRIMEKNOTS...', end=''); sys.stdout.flush()
1561  assert isinstance(PRIMEKNOTS, dict)
1562  assert len(PRIMEKNOTS) == 6, len(PRIMEKNOTS)
1563  for nb in PRIMEKNOTS:
1564  assert len(PRIMEKNOTS[nb]) >0, (nb, len(PRIMEKNOTS[nb]))
1565  for i in PRIMEKNOTS[nb]:
1566  assert knots_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
1567  assert closed_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
1568  assert nb_cycle(PRIMEKNOTS[nb][i]) == (1, 0), \
1569  (nb, i, nb_cycle(PRIMEKNOTS[nb][i]), PRIMEKNOTS[nb][i])
1570  print('ok'); sys.stdout.flush()
1571 
1572 
1573  print('BORROMEAN...', end=''); sys.stdout.flush()
1574  assert knots_is(BORROMEAN), BORROMEAN
1575  assert closed_is(BORROMEAN), BORROMEAN
1576  assert nb_cycle(BORROMEAN) == (3, 0), nb_cycle(BORROMEAN)
1577  print('ok'); sys.stdout.flush()
1578 
1579 
1580  print()
1581  print('addtoright()...', end=''); sys.stdout.flush()
1582  s = []
1583  assert addtoright(s, []) == None
1584  assert s == [], s
1585  s = [' _', '( )', '^ ^']
1586  assert addtoright(s, ['_', '%', '%', '^']) == None
1587  assert s == [' _ _', '( )%', '^ ^%', ' ^'], s
1588  assert not space_is(s), s
1589  for nb in PRIMEKNOTS:
1590  for i in PRIMEKNOTS[nb]:
1591  s = []
1592  assert addtoright(s, PRIMEKNOTS[nb][i]) == None, (nb, i)
1593  assert s == PRIMEKNOTS[nb][i], (nb, i, s)
1594  s = ['']
1595  assert addtoright(s, PRIMEKNOTS[nb][i]) == None, (nb, i)
1596  assert s == PRIMEKNOTS[nb][i], (nb, i, s)
1597  s = []
1598  s += PRIMEKNOTS[nb][i]
1599  assert addtoright(s, []) == None, (nb, i)
1600  assert s == PRIMEKNOTS[nb][i], (nb, i, s)
1601  s = []
1602  s += PRIMEKNOTS[nb][i]
1603  assert addtoright(s, ['']) == None, (nb, i)
1604  assert s == PRIMEKNOTS[nb][i], (nb, i, s)
1605  print('ok'); sys.stdout.flush()
1606 
1607 
1608  print('closed_is()...', end=''); sys.stdout.flush()
1609  assert closed_is([])
1610  assert closed_is([EMPTY, ''])
1611  assert closed_is(['()'])
1612  assert closed_is(['(%&)'])
1613  assert closed_is(['(&&)'])
1614  assert not closed_is([')('])
1615  assert closed_is([' _',
1616  '( )',
1617  ' ^'])
1618  assert not closed_is([' _',
1619  '( )',
1620  ' _'])
1621  assert closed_is([' _',
1622  '( )',
1623  '(_)'])
1624  assert not closed_is([' _',
1625  '( )',
1626  '(_('])
1627  for c in SPACECHARS:
1628  if c == EMPTY:
1629  assert closed_is([c]), c
1630  else:
1631  assert not closed_is([c]), c
1632  for nb in PRIMEKNOTS:
1633  for i in PRIMEKNOTS[nb]:
1634  assert closed_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
1635  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1636  assert closed_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
1637  (nb, i, to_labelstates(PRIMEKNOTS[nb][i], labels))
1638  assert closed_is(to_states(PRIMEKNOTS[nb][i], labels)), \
1639  (nb, i, to_states(PRIMEKNOTS[nb][i], labels))
1640  assert closed_is(to_universes(PRIMEKNOTS[nb][i])), \
1641  (nb, i, to_universes(PRIMEKNOTS[nb][i]))
1642  print('ok'); sys.stdout.flush()
1643 
1644 
1645  print('knots_is()...', end=''); sys.stdout.flush()
1646  assert knots_is([])
1647  assert knots_is([EMPTY, ''])
1648  for c in KNOTCHARS:
1649  assert knots_is([c])
1650  for i in range(256):
1651  c = chr(i)
1652  if c in KNOTCHARS:
1653  assert knots_is([c])
1654  for nb in PRIMEKNOTS:
1655  for i in PRIMEKNOTS[nb]:
1656  assert knots_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
1657  if nb > 0:
1658  for i in PRIMEKNOTS[nb]:
1659  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1660  assert not knots_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
1661  (nb, i, labels, PRIMEKNOTS[nb][i],
1662  to_labelstates(PRIMEKNOTS[nb][i], labels))
1663  assert knots_is(to_states(PRIMEKNOTS[nb][i], labels)), \
1664  (nb, i, labels, PRIMEKNOTS[nb][i],
1665  to_states(PRIMEKNOTS[nb][i], labels))
1666  assert not knots_is(to_universes(PRIMEKNOTS[nb][i])), \
1667  (nb, i, PRIMEKNOTS[nb][i], to_universes(PRIMEKNOTS[nb][i]))
1668  print('ok'); sys.stdout.flush()
1669 
1670 
1671  print('labels_list()...', end=''); sys.stdout.flush()
1672  assert labels_list(0) == [''], labels_list(0)
1673  assert labels_list(1) == ['A', 'B'], labels_list(1)
1674  assert labels_list(2) == ['AA', 'AB', 'BA', 'BB'], labels_list(2)
1675  assert labels_list(3) == ['AAA', 'AAB', 'ABA', 'ABB', 'BAA', 'BAB', 'BBA', 'BBB'], \
1676  labels_list(3)
1677  for n in range(10):
1678  l = labels_list(n)
1679  assert len(l) == 2**n, (n, len(l))
1680  for s in l:
1681  assert len(s) == n, (n, len(s))
1682  assert s.count('A') + s.count('B') == n, (n, s.count('A'), s.count('B'))
1683  print('ok'); sys.stdout.flush()
1684 
1685 
1686  print('labelstates_is()...', end=''); sys.stdout.flush()
1687  assert labelstates_is([])
1688  assert labelstates_is([EMPTY, ''])
1689  for c in LABELSTATECHARS:
1690  assert labelstates_is([c])
1691  for i in range(256):
1692  c = chr(i)
1693  if c in LABELSTATECHARS:
1694  assert labelstates_is([c])
1695  for nb in PRIMEKNOTS:
1696  for i in PRIMEKNOTS[nb]:
1697  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1698  assert labelstates_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
1699  (nb, i, PRIMEKNOTS[nb][i],
1700  labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1701  if nb > 0:
1702  for i in PRIMEKNOTS[nb]:
1703  assert not labelstates_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
1704  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1705  assert labelstates_is(to_states(PRIMEKNOTS[nb][i], labels)), \
1706  (nb, i, PRIMEKNOTS[nb][i],
1707  labels, to_states(PRIMEKNOTS[nb][i], labels))
1708  assert not labelstates_is(to_universes(PRIMEKNOTS[nb][i])), \
1709  (nb, i, to_universes(PRIMEKNOTS[nb][i]))
1710  print('ok'); sys.stdout.flush()
1711 
1712 
1713  print('links_is()...', end=''); sys.stdout.flush()
1714  print('???', end='')
1715  print('ok'); sys.stdout.flush()
1716 
1717 
1718  print('make_egalwidth()...', end=''); sys.stdout.flush()
1719  s = []
1720  assert make_egalwidth(s) == None
1721  assert s == [], s
1722  s = ['']
1723  assert make_egalwidth(s) == None
1724  assert s == [''], s
1725  s = ['(%)']
1726  assert make_egalwidth(s) == None
1727  assert s == ['(%)'], s
1728  s = ['(%%)', '', '(%)']
1729  assert make_egalwidth(s) == None
1730  assert s == ['(%%)', ' ', '(%) '], s
1731  s = ['', '()', '', '(%)']
1732  assert make_egalwidth(s) == None
1733  assert s == [' ', '() ', ' ', '(%)'], s
1734  s = ['', '()', '', '(%)']
1735  assert make_egalwidth(s, UP) == None
1736  assert s == ['///', '()/', '///', '(%)'], s
1737  print('ok'); sys.stdout.flush()
1738 
1739 
1740  print('mirror()...', end=''); sys.stdout.flush()
1741  assert mirror([]) == [], mirror([])
1742  assert mirror(['']) == [''], mirror([''])
1743  assert mirror([LEFT]) == [LEFT], mirror([LEFT])
1744  assert mirror(['()']) == ['()'], mirror(['()'])
1745  assert mirror(['/^^', ')']) == ['/^^', ')'], mirror(['/^^', ')'])
1746  assert mirror(['(%)']) == ['(&)'], mirror(['(%)'])
1747  assert mirror(['(', '%', ')']) == ['(', '&', ')'], mirror(['(', '%', ')'])
1748  for nb in PRIMEKNOTS:
1749  for i in PRIMEKNOTS[nb]:
1750  assert mirror(mirror(PRIMEKNOTS[nb][i])) == PRIMEKNOTS[nb][i], \
1751  (nb, i, mirror(mirror(PRIMEKNOTS[nb][i])))
1752  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1753  assert to_states(mirror(PRIMEKNOTS[nb][i]), labels) \
1754  == mirror(to_states(PRIMEKNOTS[nb][i], labels)), \
1755  (nb, i, to_states(PRIMEKNOTS[nb][i], labels),
1756  mirror(to_states(PRIMEKNOTS[nb][i]), labels))
1757  assert to_labelstates(mirror(PRIMEKNOTS[nb][i]), labels) \
1758  == mirror(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
1759  (nb, i, to_labelstates(PRIMEKNOTS[nb][i], labels),
1760  mirror(to_labelstates(PRIMEKNOTS[nb][i]), labels))
1761  assert to_universes(mirror(PRIMEKNOTS[nb][i])) \
1762  == mirror(to_universes(PRIMEKNOTS[nb][i])), \
1763  (nb, i, to_universes(mirror(PRIMEKNOTS[nb][i])),
1764  mirror(to_universes(PRIMEKNOTS[nb][i])))
1765  print('ok'); sys.stdout.flush()
1766 
1767 
1768  print('nb_A()...', end=''); sys.stdout.flush()
1769  assert nb_A([]) == 0, nb_A([])
1770  assert nb_A([CROSSUPA, '']) == 1, nb_A([CROSSUPA, ''])
1771  assert nb_A([CROSSDOWNA, '']) == 1, nb_A([c, CROSSDOWNA])
1772  for c in LABELSTATECHARS.replace(CROSSUPA, EMPTY).replace(CROSSDOWNA, EMPTY):
1773  assert nb_A([c, '']) == 0, (c, nb_A([c, '']))
1774  for nb in PRIMEKNOTS:
1775  for i in PRIMEKNOTS[nb]:
1776  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1777  assert nb_A(to_labelstates(PRIMEKNOTS[nb][i], labels)) == labels.count('A'), \
1778  (nb, i, PRIMEKNOTS[nb][i],
1779  nb_A(to_labelstates(PRIMEKNOTS[nb][i], labels)),
1780  labels.count('A'), labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1781  assert nb_A(to_labelstates(PRIMEKNOTS[nb][i], labels)) \
1782  == nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSUPA) \
1783  + nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels) , CROSSDOWNA), \
1784  (nb, i, PRIMEKNOTS[nb][i],
1785  nb_A(to_labelstates(PRIMEKNOTS[nb][i], labels)),
1786  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSUPA),
1787  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSDOWNA),
1788  labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1789  print('ok'); sys.stdout.flush()
1790 
1791 
1792  print('nb_B()...', end=''); sys.stdout.flush()
1793  assert nb_B([]) == 0, nb_B([])
1794  assert nb_B([CROSSUPB, '']) == 1, nb_B([CROSSUPB, ''])
1795  assert nb_B([CROSSDOWNB, '']) == 1, nb_B([CROSSDOWNB, ''])
1796  for c in LABELSTATECHARS.replace(CROSSUPB, EMPTY).replace(CROSSDOWNB, EMPTY):
1797  assert nb_B([c, '']) == 0, (c, nb_B([c, '']))
1798  for nb in PRIMEKNOTS:
1799  for i in PRIMEKNOTS[nb]:
1800  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1801  assert nb_B(to_labelstates(PRIMEKNOTS[nb][i], labels)) == labels.count('B'), \
1802  (nb, i, PRIMEKNOTS[nb][i],
1803  nb_B(to_labelstates(PRIMEKNOTS[nb][i], labels)),
1804  labels.count('B'), labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1805  assert nb_B(to_labelstates(PRIMEKNOTS[nb][i], labels)) \
1806  == nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSUPB) \
1807  + nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels) , CROSSDOWNB), \
1808  (nb, i, PRIMEKNOTS[nb][i],
1809  nb_B(to_labelstates(PRIMEKNOTS[nb][i], labels)),
1810  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSUPB),
1811  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels), CROSSDOWNB),
1812  labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1813  print('ok'); sys.stdout.flush()
1814 
1815 
1816  print('nb_cross()...', end=''); sys.stdout.flush()
1817  assert nb_cross([]) == 0, nb_cross([])
1818  assert nb_cross([VERT, '']) == 0, nb_cross([VERT, ''])
1819  assert nb_cross([HORI, '']) == 0, nb_cross([HORI, ''])
1820  assert nb_cross([CROSSUP, '']) == 1, nb_cross([CROSSUP, ''])
1821  assert nb_cross([CROSSDOWN, '']) == 1, nb_cross([CROSSDOWN, ''])
1822  assert nb_cross([CROSSUPA, '']) == 1, nb_cross([CROSSUPA, ''])
1823  assert nb_cross([CROSSUPB, '']) == 1, nb_cross([CROSSUPB, ''])
1824  assert nb_cross([CROSSDOWNA, '']) == 1, nb_cross([CROSSDOWNA, ''])
1825  assert nb_cross([CROSSDOWNB, '']) == 1, nb_cross([CROSSDOWNB, ''])
1826  assert nb_cross([CROSS, '']) == 1, nb_cross([CROSS, ''])
1827  for c in KNOTCHARS.replace(CROSSUP, EMPTY).replace(CROSSDOWN, EMPTY):
1828  assert nb_cross([c, '']) == 0, (c, nb_cross([c, '']))
1829  for nb in PRIMEKNOTS:
1830  for i in PRIMEKNOTS[nb]:
1831  assert nb_cross(PRIMEKNOTS[nb][i]) == nb, \
1832  (nb, i, nb_cross(PRIMEKNOTS[nb][i]), PRIMEKNOTS[nb][i])
1833  assert nb_cross(PRIMEKNOTS[nb][i]) \
1834  == nb_items(PRIMEKNOTS[nb][i], CROSSUP) \
1835  + nb_items(PRIMEKNOTS[nb][i], CROSSDOWN), \
1836  (nb, i, nb_cross(PRIMEKNOTS[nb][i]),
1837  nb_items(PRIMEKNOTS[nb][i], CROSSUP),
1838  nb_items(PRIMEKNOTS[nb][i], CROSSDOWN),
1839  PRIMEKNOTS[nb][i])
1840  assert nb_cross(to_universes(PRIMEKNOTS[nb][i])) == nb, \
1841  (nb, i, to_universes(nb_cross(PRIMEKNOTS[nb][i])),
1842  to_universes(PRIMEKNOTS[nb][i]))
1843  assert nb_cross(to_universes(PRIMEKNOTS[nb][i])) \
1844  == nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSS), \
1845  (nb, i, to_universes(nb_cross(PRIMEKNOTS[nb][i])),
1846  nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSS),
1847  to_universes(PRIMEKNOTS[nb][i]))
1848  assert nb_cross(to_states(PRIMEKNOTS[nb][i])) <= nb, \
1849  (nb, i, to_states(nb_cross(PRIMEKNOTS[nb][i])),
1850  to_universes(PRIMEKNOTS[nb][i]))
1851  print('ok'); sys.stdout.flush()
1852 
1853 
1854  print('nb_cycle()...', end=''); sys.stdout.flush()
1855  assert nb_cycle([]) == (0, 0), nb_cycle([])
1856  assert nb_cycle([EMPTY, '']) == (0, 0), nb_cycle([EMPTY, ''])
1857  assert nb_cycle(['()']) == (1, 0), nb_cycle(['()'])
1858  assert nb_cycle([')(']) == (0, 2), nb_cycle([')('])
1859  assert nb_cycle([' _',
1860  '( )',
1861  ' ^']) == (1, 0), nb_cycle([' _', '( )', ' ^'])
1862  assert nb_cycle(['_',
1863  ' )',
1864  '_']) == (0, 2), nb_cycle(['_', ' )', '_'])
1865  assert nb_cycle([' _',
1866  '( )',
1867  ' _']) == (0, 2), nb_cycle([' _', '( )', ' _'])
1868  assert nb_cycle([' _ _',
1869  '( )( )',
1870  ' _ ^']) == (1, 2), nb_cycle([' _ _', '( )( )', ' _ ^'])
1871  assert nb_cycle([' _ _ _',
1872  ' ( )( )%',
1873  '() _ ^ ^']) == (3, 2), \
1874  nb_cycle([' _ _ _', ' ( )( )%', '() _ ^ ^'])
1875  for c in KNOTCHARS.replace(VERT,
1876  EMPTY).replace(HORI,
1877  EMPTY).replace(CROSSUP,
1878  EMPTY).replace(CROSSDOWN, EMPTY):
1879  if c == EMPTY:
1880  assert nb_cycle([c]) == (0, 0), (c, nb_cycle([c]))
1881  else:
1882  assert nb_cycle([c]) == (0, 1), (c, nb_cycle([c]))
1883  for c in VERT + HORI + CROSSUP + CROSSDOWN + CROSSUPA + CROSSUPB + CROSSDOWNA + CROSSDOWNB:
1884  assert nb_cycle([c]) == (0, 2), (c, nb_cycle([c]))
1885  for nb in PRIMEKNOTS:
1886  for i in PRIMEKNOTS[nb]:
1887  assert nb_cycle(PRIMEKNOTS[nb][i]) == (1, 0), \
1888  (nb, i, nb_cycle(PRIMEKNOTS[nb][i]), PRIMEKNOTS[nb][i])
1889  assert nb_cycle(to_universes(PRIMEKNOTS[nb][i])) == (1, 0), \
1890  (nb, i, nb_cycle(to_universes(PRIMEKNOTS[nb][i])),
1891  to_universes(PRIMEKNOTS[nb][i]))
1892  print('ok'); sys.stdout.flush()
1893 
1894 
1895  print('nb_items()...', end=''); sys.stdout.flush()
1896  assert nb_items([], EMPTY) == 0, nb_items([], EMPTY)
1897  assert nb_items([EMPTY, ''], EMPTY) == 1, nb_items([EMPTY, ''], EMPTY)
1898  for c in SPACECHARS:
1899  assert nb_items([c, ''], c) == 1, (c, nb_items([c, ''], c))
1900  for nb in PRIMEKNOTS:
1901  for i in PRIMEKNOTS[nb]:
1902  assert nb_items(PRIMEKNOTS[nb][i], '') == 0, \
1903  (nb, i, nb_items(PRIMEKNOTS[nb][i], ''), PRIMEKNOTS[nb][i])
1904  assert nb_items(PRIMEKNOTS[nb][i], CROSSUP + CROSSDOWN) == nb, \
1905  (nb, i, nb_items(PRIMEKNOTS[nb][i], CROSSUP + CROSSDOWN), PRIMEKNOTS[nb][i])
1906  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
1907  assert nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels),
1908  CROSSUPA + CROSSDOWNA) == labels.count('A'), \
1909  (nb, i, PRIMEKNOTS[nb][i],
1910  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels),
1911  CROSSUPA + CROSSDOWNA),
1912  labels.count('A'), labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1913  assert nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels),
1914  CROSSUPB + CROSSDOWNB) == labels.count('B'), \
1915  (nb, i, PRIMEKNOTS[nb][i],
1916  nb_items(to_labelstates(PRIMEKNOTS[nb][i], labels),
1917  CROSSUPB + CROSSDOWNB),
1918  labels.count('B'), labels, to_labelstates(PRIMEKNOTS[nb][i], labels))
1919  assert nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSS) == nb, \
1920  (nb, i, PRIMEKNOTS[nb][i], nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSS),
1921  to_universes(PRIMEKNOTS[nb][i]))
1922  assert nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSSUP + CROSSDOWN) == 0, \
1923  (nb, i, PRIMEKNOTS[nb][i],
1924  nb_items(to_universes(PRIMEKNOTS[nb][i]), CROSSUP + CROSSDOWN),
1925  to_universes(PRIMEKNOTS[nb][i]))
1926  print('ok'); sys.stdout.flush()
1927 
1928 
1929  print('poly_Kauffman()...', end=''); sys.stdout.flush()
1930  p = polynomial.Polynomial(polynomial.Term((0, )), coef=1, varsstr='A')
1931  assert poly_Kauffman(['()']) == p, (poly_Kauffman(['()']), p)
1932  assert poly_Kauffman(['()',
1933  ' ']) == p, (poly_Kauffman(['()', ' ']), p)
1934  assert poly_Kauffman([' _',
1935  '( )',
1936  ' ^']) == p, (poly_Kauffman([' _', '( )', ' ^'], p))
1937  assert poly_Kauffman(['(%)']) == p, (poly_Kauffman(['(%)']), p)
1938  assert poly_Kauffman(['(&)']) == p, (poly_Kauffman(['(&)']), p)
1939 
1940  p = polynomial.Polynomial({polynomial.Term((4, )): 1,
1941  polynomial.Term((12, )): 1,
1942  polynomial.Term((16, )): -1}, varsstr='A')
1943  assert poly_Kauffman(PRIMEKNOTS[3][1]) == p, (poly_Kauffman(PRIMEKNOTS[3][1]), p)
1944  assert p.eval((-1)) == 1, p.eval((-1))
1945 
1946  p = polynomial.Polynomial({polynomial.Term((-16, )): -1,
1947  polynomial.Term((-12, )): 1,
1948  polynomial.Term((-4, )): 1}, varsstr='A')
1949  assert poly_Kauffman([' _ _',
1950  '( & )',
1951  ' % %',
1952  '( ^ )',
1953  ' ^^^']) == p, \
1954  (poly_Kauffman([' _ _',
1955  '( & )',
1956  ' % %',
1957  '( ^ )',
1958  ' ^^^']), p)
1959  assert poly_Kauffman([' _ _',
1960  '( & )',
1961  ' % %',
1962  '( ^ %)',
1963  ' ^^^']) == p, \
1964  (poly_Kauffman([' _ _',
1965  '( & )',
1966  ' % %',
1967  '( ^ %)',
1968  ' ^^^']), p)
1969  assert p.eval((-1)) == 1, p.eval((-1))
1970 
1971  p = polynomial.Polynomial({polynomial.Term((-8, )): 1,
1972  polynomial.Term((-4, )): -1,
1973  polynomial.Term((0, )): 1,
1974  polynomial.Term((4, )): -1,
1975  polynomial.Term((8, )): 1}, varsstr='A')
1976  assert poly_Kauffman(PRIMEKNOTS[4][1]) == p, (poly_Kauffman(PRIMEKNOTS[4][1]), p)
1977  assert p.eval((-1)) == 1, p.eval((-1))
1978 
1979  for nb in PRIMEKNOTS:
1980  for i in PRIMEKNOTS[nb]:
1981  assert poly_Kauffman(PRIMEKNOTS[nb][i]).eval((-1)) == 1, \
1982  poly_Kauffman(PRIMEKNOTS[nb][i]).eval((-1))
1983 
1984  # Lien de Hopf
1985  p = polynomial.Polynomial({polynomial.Term((-4, )):- 1,
1986  polynomial.Term((4, )): -1}, varsstr='A')
1987  assert poly_Kauffman(['/%\\',
1988  '\\%/']) == p, (poly_Kauffman(['/%\\', '\\%/']), p)
1989  assert poly_Kauffman(['/%^%)',
1990  '\\%/']) == p, (poly_Kauffman(['/%^%)', '\\%/']), p)
1991  assert poly_Kauffman(['/&\\',
1992  '\\&/']) == p, (poly_Kauffman(['/&\\', '\\&/']), p)
1993  assert poly_Kauffman(['/&^%)',
1994  '\\&/']) == p, (poly_Kauffman(['/&^%)', '\\&/']), p)
1995  assert p.eval((-1)) == -2, p.eval((-1))
1996 
1997  # Deux noeuds triviaux superposés
1998  p = polynomial.Polynomial({polynomial.Term((-2, )): -1,
1999  polynomial.Term((2, )): -1}, varsstr='A')
2000  assert poly_Kauffman(['/%\\',
2001  '\\&/']) == p, (poly_Kauffman(['/%\\', '\\&/']), p)
2002  assert poly_Kauffman(['/%^%)',
2003  '\\&/']) == p, (poly_Kauffman(['/%^%)', '\\&/']), p)
2004  assert p.eval((-1)) == -2, p.eval((-1))
2005 
2006  # k noeuds triviaux séparés
2007  p = polynomial.Polynomial({polynomial.Term((-2, )): -1,
2008  polynomial.Term((2, )): -1}, varsstr='A')
2009  assert poly_Kauffman(['()'*2]) == p, (poly_Kauffman(['()'*2]), p)
2010  assert poly_Kauffman(['()',
2011  ' ']*2) == p, (poly_Kauffman(['()', ' ']*2), p)
2012  assert poly_Kauffman(['(%)'*2]) == p, (poly_Kauffman(['(%)'*2]), p)
2013  assert poly_Kauffman(['(%%)'*2]) == p, (poly_Kauffman(['(%%)'*2]), p)
2014  assert poly_Kauffman(['(%&)'*2]) == p, (poly_Kauffman(['(%&)'*2]), p)
2015 
2016  p = polynomial.Polynomial({polynomial.Term((-4, )): 1,
2017  polynomial.Term((0, )): 2,
2018  polynomial.Term((4, )): 1,}, varsstr='A')
2019  assert poly_Kauffman(['()'*3]) == p, (poly_Kauffman(['()'*3]), p)
2020  assert poly_Kauffman(['()',
2021  ' ']*3) == p, (poly_Kauffman(['()', ' ']*3), p)
2022  assert poly_Kauffman(['(%)'*3]) == p, (poly_Kauffman(['(%)'*3]), p)
2023  assert poly_Kauffman(['(%%)'*3]) == p, (poly_Kauffman(['(%%)'*3]), p)
2024  assert poly_Kauffman(['(%&)'*3]) == p, (poly_Kauffman(['(%&)'*3]), p)
2025 
2026  for k in range(1, 10 if debug.assertspeed >= debug.ASSERT_NORMAL else 5):
2027  assert poly_Kauffman(['()'*k]).eval(-1) == (-2)**(k - 1), \
2028  (k, poly_Kauffman(['()'*k]).eval(-1), (-2)**(k - 1))
2029  assert poly_Kauffman(['()',
2030  ' ']*k).eval(-1) == (-2)**(k - 1), \
2031  (k, poly_Kauffman(['()', ' ']*k).eval(-1), (-2)**(k - 1))
2032  assert poly_Kauffman(['(%)'*k]).eval(-1) == (-2)**(k - 1), \
2033  (k, poly_Kauffman(['(%)'*k]).eval(-1), (-2)**(k - 1))
2034  if debug.assertspeed < debug.ASSERT_NORMAL:
2035  print(debug.assertspeed_str(), end='')
2036  print('ok'); sys.stdout.flush()
2037 
2038 
2039  print('poly_Kauffman_A()...', end=''); sys.stdout.flush()
2040  p = polynomial.Polynomial(polynomial.Term((0, )), varsstr='A')
2041  assert poly_Kauffman_A(['()']) == p, (poly_Kauffman_A(['()']), p)
2042  assert poly_Kauffman_A(['()',
2043  ' ']) == p, (poly_Kauffman_A(['()', ' ']), p)
2044  assert poly_Kauffman_A([' _',
2045  '( )',
2046  ' ^']) == p, (poly_Kauffman_A([' _', '( )', ' ^'], p))
2047  assert p.eval((-1)) == 1, p.eval((-1))
2048 
2049  p = polynomial.Polynomial({polynomial.Term((-5, )): -1,
2050  polynomial.Term((3, )): -1,
2051  polynomial.Term((7, )): 1}, varsstr='A')
2052  assert poly_Kauffman_A(PRIMEKNOTS[3][1]) == p, (poly_Kauffman_A(PRIMEKNOTS[3][1]), p)
2053  assert p.eval((-1)) == 1, p.eval((-1))
2054 
2055  p = polynomial.Polynomial({polynomial.Term((-7, )): 1,
2056  polynomial.Term((-3, )): -1,
2057  polynomial.Term((5, )): -1}, varsstr='A')
2058  assert poly_Kauffman_A([' _ _',
2059  '( & )',
2060  ' % %',
2061  '( ^ )',
2062  ' ^^^']) == p, \
2063  (poly_Kauffman_A([' _ _',
2064  '( & )',
2065  ' % %',
2066  '( ^ )',
2067  ' ^^^']), p)
2068  assert p.eval((-1)) == 1, p.eval((-1))
2069 
2070  p = polynomial.Polynomial({polynomial.Term((-8, )): 1,
2071  polynomial.Term((-4, )): -1,
2072  polynomial.Term((0, )): 1,
2073  polynomial.Term((4, )): -1,
2074  polynomial.Term((8, )): 1}, varsstr='A')
2075  assert poly_Kauffman_A(PRIMEKNOTS[4][1]) == p, (poly_Kauffman_A(PRIMEKNOTS[4][1]), p)
2076  assert p.eval((-1)) == 1, p.eval((-1))
2077 
2078  for nb in PRIMEKNOTS:
2079  for i in PRIMEKNOTS[nb]:
2080  assert poly_Kauffman_A(PRIMEKNOTS[nb][i]).eval((-1)) == 1, \
2081  poly_Kauffman_A(PRIMEKNOTS[nb][i]).eval((-1))
2082 
2083  # Lien de Hopf
2084  p = polynomial.Polynomial({polynomial.Term((-4, )):- 1,
2085  polynomial.Term((4, )): -1}, varsstr='A')
2086  assert poly_Kauffman_A(['/%\\',
2087  '\\%/']) == p, (poly_Kauffman_A(['/%\\', '\\%/']), p)
2088  assert poly_Kauffman_A(['/&\\',
2089  '\\&/']) == p, (poly_Kauffman_A(['/&\\', '\\&/']), p)
2090  assert p.eval((-1)) == -2, p.eval((-1))
2091 
2092  # Deux noeuds triviaux superposés
2093  p = polynomial.Polynomial({polynomial.Term((-2, )): -1,
2094  polynomial.Term((2, )): -1}, varsstr='A')
2095  assert poly_Kauffman_A(['/%\\',
2096  '\\&/']) == p, (poly_Kauffman_A(['/%\\', '\\&/']), p)
2097  assert p.eval((-1)) == -2, p.eval((-1))
2098 
2099  # k noeuds triviaux séparés
2100  p = polynomial.Polynomial({polynomial.Term((-2, )): -1,
2101  polynomial.Term((2, )): -1}, varsstr='A')
2102  assert poly_Kauffman_A(['()'*2]) == p, (poly_Kauffman_A(['()'*2]), p)
2103  assert poly_Kauffman_A(['()',
2104  ' ']*2) == p, (poly_Kauffman_A(['()', ' ']*2), p)
2105 
2106  p = polynomial.Polynomial({polynomial.Term((-4, )): 1,
2107  polynomial.Term((0, )): 2,
2108  polynomial.Term((4, )): 1,}, varsstr='A')
2109  assert poly_Kauffman_A(['()'*3]) == p, (poly_Kauffman_A(['()'*3]), p)
2110  assert poly_Kauffman_A(['()',
2111  ' ']*3) == p, (poly_Kauffman_A(['()', ' ']*3), p)
2112 
2113  for k in range(1, 10):
2114  assert poly_Kauffman_A(['()'*k]).eval(-1) == (-2)**(k - 1), \
2115  (k, poly_Kauffman_A(['()'*k]).eval(-1), (-2)**(k - 1))
2116  assert poly_Kauffman_A(['()',
2117  ' ']*k).eval(-1) == (-2)**(k - 1), \
2118  (k, poly_Kauffman_A(['()', ' ']*k).eval(-1), (-2)**(k - 1))
2119  print('ok'); sys.stdout.flush()
2120 
2121 
2122  print('poly_Kauffman_ABd()...', end=''); sys.stdout.flush()
2123  p = polynomial.Polynomial(polynomial.Term((0, 0, -1)), varsstr='ABd')
2124  assert poly_Kauffman_ABd([]) == p, (poly_Kauffman_ABd([]), p)
2125  assert p.eval((-1, -1, -2)) == -.5, p.eval((-1, -1, -2))
2126 
2127  p = polynomial.Polynomial(polynomial.Term((0, 0, 0)), varsstr='ABd')
2128  assert poly_Kauffman_ABd(['()']) == p, (poly_Kauffman_ABd(['()']), p)
2129  assert poly_Kauffman_ABd([' _',
2130  '( )',
2131  ' ^']) == p, (poly_Kauffman_ABd([' _', '( )', ' ^'], p))
2132  assert p.eval((-1, -1, -2)) == 1, p.eval((-1, -1, -2))
2133 
2134  p = polynomial.Polynomial({polynomial.Term((0, 3, 1)): 1,
2135  polynomial.Term((1, 2, 0)): 3,
2136  polynomial.Term((2, 1, 1)): 3,
2137  polynomial.Term((3, 0, 2)): 1}, varsstr='ABd')
2138  assert poly_Kauffman_ABd(PRIMEKNOTS[3][1]) == p, (poly_Kauffman_ABd(PRIMEKNOTS[3][1]), p)
2139  assert p.eval((-1, -1, -2)) == 1, p.eval((-1, -1, -2))
2140 
2141  p = polynomial.Polynomial({polynomial.Term((3, 0, 1)): 1,
2142  polynomial.Term((2, 1, 0)): 3,
2143  polynomial.Term((1, 2, 1)): 3,
2144  polynomial.Term((0, 3, 2)): 1}, varsstr='ABd')
2145  assert poly_Kauffman_ABd([' _ _',
2146  '( & )',
2147  ' % %',
2148  '( ^ )',
2149  ' ^^^']) == p, \
2150  (poly_Kauffman_ABd([' _ _',
2151  '( & )',
2152  ' % %',
2153  '( ^ )',
2154  ' ^^^']), p)
2155  assert p.eval((-1, -1, -2)) == 1, p.eval((-1, -1, -2))
2156 
2157  p = polynomial.Polynomial({polynomial.Term((0, 4, 2)): 1,
2158  polynomial.Term((1, 3, 1)): 4,
2159  polynomial.Term((2, 2, 0)): 5,
2160  polynomial.Term((2, 2, 2)): 1,
2161  polynomial.Term((3, 1, 1)): 4,
2162  polynomial.Term((4, 0, 2)): 1}, varsstr='ABd')
2163  assert poly_Kauffman_ABd(PRIMEKNOTS[4][1]) == p, (poly_Kauffman_ABd(PRIMEKNOTS[4][1]), p)
2164  assert p.eval((-1, -1, -2)) == 1, p.eval((-1, -1, -2))
2165 
2166  for nb in PRIMEKNOTS:
2167  for i in PRIMEKNOTS[nb]:
2168  assert poly_Kauffman_ABd(PRIMEKNOTS[nb][i]).eval((-1, -1, -2)) == 1, \
2169  poly_Kauffman_ABd(PRIMEKNOTS[nb][i]).eval((-1, -1, -2))
2170 
2171  # Lien de Hopf
2172  p = polynomial.Polynomial({polynomial.Term((0, 2, 1)): 1,
2173  polynomial.Term((1, 1, 0)): 2,
2174  polynomial.Term((2, 0, 1)): 1}, varsstr='ABd')
2175  assert poly_Kauffman_ABd(['/%\\',
2176  '\\%/']) == p, (poly_Kauffman_ABd(['/%\\', '\\%/']), p)
2177  assert poly_Kauffman_ABd(['/&\\',
2178  '\\&/']) == p, (poly_Kauffman_ABd(['/&\\', '\\&/']), p)
2179  assert p.eval((-1, -1, -2)) == -2, p.eval((-1, -1, -2))
2180 
2181  # Deux noeuds triviaux superposés
2182  p = polynomial.Polynomial({polynomial.Term((0, 2, 0)): 1,
2183  polynomial.Term((1, 1, 1)): 2,
2184  polynomial.Term((2, 0, 0)): 1}, varsstr='ABd')
2185  assert poly_Kauffman_ABd(['/%\\',
2186  '\\&/']) == p, (poly_Kauffman_ABd(['/%\\', '\\&/']), p)
2187  assert p.eval((-1, -1, -2)) == -2, p.eval((-1, -1, -2))
2188 
2189  # k noeuds triviaux séparés
2190  for k in range(1, 10):
2191  p = polynomial.Polynomial({polynomial.Term((0, 0, k - 1)): 1}, varsstr='ABd')
2192  assert poly_Kauffman_ABd(['()'*k]) == p, (k, poly_Kauffman_ABd(['()'*k]), p)
2193  assert poly_Kauffman_ABd(['()',
2194  ' ']*k) == p, (k, poly_Kauffman_ABd(['()', ' ']*k), p)
2195  assert p.eval((-1, -1, -2)) == (-2)**(k - 1), (k, p.eval((-1, -1, -2), (-2)**(k - 1)))
2196  print('ok'); sys.stdout.flush()
2197 
2198 
2199  print('primeknots_even_1()...', end=''); sys.stdout.flush()
2200  assert primeknots_even_1(4) == PRIMEKNOTS[4][1], (primeknots_even_1(4), PRIMEKNOTS[4][1])
2201  assert primeknots_even_1(6) == PRIMEKNOTS[6][1], (primeknots_even_1(6), PRIMEKNOTS[6][1])
2202  for n in range(4, 50, 2):
2203  assert knots_is(primeknots_even_1(n)), (n, primeknots_even_1(n))
2204  assert closed_is(primeknots_even_1(n)), (n, primeknots_even_1(n))
2205  assert nb_cycle(primeknots_even_1(n)) == (1, 0), (n, nb_cycle(primeknots_even_1(n)))
2207  print('???', end='')
2208  print('ok'); sys.stdout.flush()
2209 
2210 
2211  print('read()...', end=''); sys.stdout.flush()
2212  for nb in PRIMEKNOTS:
2213  for i in PRIMEKNOTS[nb]:
2214  f = tempfile.NamedTemporaryFile(mode='w', suffix='.txt',
2215  prefix='DSPython_test_read_knots_{0}_{1}__'
2216  .format(nb, i),
2217  delete=False)
2218  write(f, PRIMEKNOTS[nb][i])
2219  f.close()
2220 
2221  f = open(f.name)
2222  s = read(f)
2223  f.close()
2224  os.remove(f.name)
2225  assert s == PRIMEKNOTS[nb][i], (nb, i, PRIMEKNOTS[nb][i], s)
2226  print('ok'); sys.stdout.flush()
2227 
2228 
2229  print('rotate()...', end=''); sys.stdout.flush()
2230  assert rotate([]) == [], rotate([])
2231  assert rotate(['']) == [], rotate([''])
2232  assert rotate(['', '']) == [], rotate(['', ''])
2233  assert rotate([LEFT]) == [BOTTOM], rotate([LEFT])
2234  assert rotate(['()']) == ['_', '^'], rotate(['()'])
2235  assert rotate(['/^^', ')']) == [') ', ') ', '\\_'], rotate(['/^^', ')'])
2236  assert rotate(['/^^', '', ')']) == [') ', ') ', '\\ _'], rotate(['/^^', '', ')'])
2237  assert rotate(['/^^', ')'], 2) == [' (', '__/'], rotate(['/^^', ')'], 2)
2238  assert rotate(['/^^', ')'], 3) == ['^\\', ' (', ' ('], rotate(['/^^', ')'], 3)
2239  assert rotate(['/^^', ')'], 4) == ['/^^', ')'], rotate(['/^^', ')'], 4)
2240  assert rotate(['/^^', ')'], -1) == ['^\\', ' (', ' ('], rotate(['/^^', ')'], -1)
2241  assert rotate(['/^^', ')'], -2) == [' (', '__/'], rotate(['/^^', ')'], -2)
2242  assert rotate(['/^^', ')'], -3) == [') ', ') ', '\\_'], rotate(['/^^', ')'], -3)
2243  for nb in PRIMEKNOTS:
2244  for i in PRIMEKNOTS[nb]:
2245  assert rotate(PRIMEKNOTS[nb][i]) == rotate(PRIMEKNOTS[nb][i], -3), \
2246  (nb, i, rotate(PRIMEKNOTS[nb][i]), rotate(PRIMEKNOTS[nb][i], -3))
2247  assert rotate(PRIMEKNOTS[nb][i], 2) == rotate(PRIMEKNOTS[nb][i], -2), \
2248  (nb, i, rotate(PRIMEKNOTS[nb][i], 2), rotate(PRIMEKNOTS[nb][i], -2))
2249  assert rotate(PRIMEKNOTS[nb][i], 3) == rotate(PRIMEKNOTS[nb][i], -1), \
2250  (nb, i, rotate(PRIMEKNOTS[nb][i], 3), rotate(PRIMEKNOTS[nb][i], -1))
2251  assert rotate(PRIMEKNOTS[nb][i], 4) == PRIMEKNOTS[nb][i], \
2252  (nb, i, rotate(PRIMEKNOTS[nb][i]), 4)
2253  assert rotate(PRIMEKNOTS[nb][i], 12) == PRIMEKNOTS[nb][i], \
2254  (nb, i, rotate(PRIMEKNOTS[nb][i]), 12)
2255  assert rotate(PRIMEKNOTS[nb][i], -4) == PRIMEKNOTS[nb][i], \
2256  (nb, i, rotate(PRIMEKNOTS[nb][i]), -4)
2257 
2258  s_egalwidth = []
2259  s_egalwidth += PRIMEKNOTS[nb][i]
2260  make_egalwidth(s_egalwidth)
2261  s = []
2262  s += PRIMEKNOTS[nb][i]
2263  s = rotate(s, 0)
2264  assert s == PRIMEKNOTS[nb][i], (nb, i, s_egalwidth)
2265  s = rotate(s, 12)
2266  assert s == PRIMEKNOTS[nb][i], (nb, i, s_egalwidth)
2267  s = rotate(s, -12)
2268  assert s == PRIMEKNOTS[nb][i], (nb, i, s_egalwidth)
2269  s = rotate(s)
2270  s = rotate(s, 3)
2271  assert s == s_egalwidth, (nb, i, s_egalwidth)
2272  s = []
2273  s += PRIMEKNOTS[nb][i]
2274  s = rotate(s, 2)
2275  s = rotate(s, 2)
2276  assert s == s_egalwidth, (nb, i, s_egalwidth)
2277  s = []
2278  s += PRIMEKNOTS[nb][i]
2279  s = rotate(s, -1)
2280  t = []
2281  t += PRIMEKNOTS[nb][i]
2282  t = rotate(t, 3)
2283  assert s == t, (nb, i, s, t)
2284 
2285  for labels in ['A'*nb, 'B'*nb]:
2286  assert to_labelstates(rotate(PRIMEKNOTS[nb][i]), labels) \
2287  == rotate(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
2288  (nb, i, to_labelstates(rotate(PRIMEKNOTS[nb][i]), labels),
2289  rotate(to_labelstates(PRIMEKNOTS[nb][i], labels)))
2290  assert to_states(rotate(PRIMEKNOTS[nb][i]), labels) \
2291  == rotate(to_states(PRIMEKNOTS[nb][i], labels)), \
2292  (nb, i, to_states(rotate(PRIMEKNOTS[nb][i]), labels),
2293  rotate(to_states(PRIMEKNOTS[nb][i], labels)))
2294  assert to_universes(rotate(PRIMEKNOTS[nb][i])) \
2295  == rotate(to_universes(PRIMEKNOTS[nb][i])), \
2296  (nb, i, to_universes(rotate(PRIMEKNOTS[nb][i])),
2297  rotate(to_universes(PRIMEKNOTS[nb][i])))
2298  print('ok'); sys.stdout.flush()
2299 
2300 
2301  print('space_is()...', end=''); sys.stdout.flush()
2302  assert space_is([])
2303  assert not space_is(())
2304  assert space_is([EMPTY, ''])
2305  assert not space_is([EMPTY, 666, ''])
2306  assert space_is([' _',
2307  '( )',
2308  ' ^'])
2309  assert not space_is([' _',
2310  '( )',
2311  ' ^)'])
2312  for c in SPACECHARS:
2313  assert space_is([c])
2314  for i in range(256):
2315  c = chr(i)
2316  if c in SPACECHARS:
2317  assert space_is([c])
2318  else:
2319  assert not space_is([c])
2320  for nb in PRIMEKNOTS:
2321  for i in PRIMEKNOTS[nb]:
2322  assert space_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
2323  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2324  assert space_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
2325  (nb, i, to_labelstates(PRIMEKNOTS[nb][i], labels))
2326  assert space_is(to_states(PRIMEKNOTS[nb][i], labels)), \
2327  (nb, i, to_states(PRIMEKNOTS[nb][i], labels))
2328  assert space_is(to_universes(PRIMEKNOTS[nb][i])), \
2329  (nb, i, to_universes(PRIMEKNOTS[nb][i]))
2330  print('ok'); sys.stdout.flush()
2331 
2332 
2333  print('states_is()...', end=''); sys.stdout.flush()
2334  assert states_is([])
2335  assert states_is([EMPTY, ''])
2336  for c in STATECHARS:
2337  assert states_is([c])
2338  for i in range(256):
2339  c = chr(i)
2340  if c in STATECHARS:
2341  assert states_is([c])
2342  for nb in PRIMEKNOTS:
2343  for i in PRIMEKNOTS[nb]:
2344  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2345  assert states_is(to_states(PRIMEKNOTS[nb][i], labels)), \
2346  (nb, i, PRIMEKNOTS[nb][i], labels, to_states(PRIMEKNOTS[nb][i]))
2347  if nb > 0:
2348  for i in PRIMEKNOTS[nb]:
2349  assert not states_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
2350  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2351  assert not states_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
2352  (nb, i, PRIMEKNOTS[nb][i], labels,
2353  to_labelstates(PRIMEKNOTS[nb][i], labels))
2354  assert not states_is(to_universes(PRIMEKNOTS[nb][i])), \
2355  (nb, i, PRIMEKNOTS[nb][i], to_universes(PRIMEKNOTS[nb][i]))
2356  print('ok'); sys.stdout.flush()
2357 
2358 
2359  print('to_labelstates()...', end=''); sys.stdout.flush()
2360  assert to_labelstates([]) == [], to_labelstates([])
2361  assert to_labelstates([CROSSUP, '']) == [CROSSUPA, ''], to_labelstates([CROSSUP, ''])
2362  assert to_labelstates([CROSSUP, ''], 'B') == [CROSSUPB, ''], \
2363  to_labelstates([CROSSUP, ''], 'B')
2364  assert to_labelstates([CROSSDOWN, '']) == [CROSSDOWNA, ''], to_labelstates([CROSSDOWN, ''])
2365  assert to_labelstates([CROSSDOWN, ''], 'B') == [CROSSDOWNB, ''], \
2366  to_labelstates([CROSSDOWN, ''], 'B')
2367  assert to_labelstates([CROSSUPA, '']) == [CROSSUPA, ''], to_labelstates([CROSSUPA, ''])
2368  assert to_labelstates([CROSSUPA, ''], 'B') == [CROSSUPB, ''], \
2369  to_labelstates([CROSSUPA, ''], 'B')
2370  assert to_labelstates([CROSSDOWNA, '']) == [CROSSDOWNA, ''], \
2371  to_labelstates([CROSSDOWNA, ''])
2372  assert to_labelstates([CROSSDOWNA, ''], 'B') == [CROSSDOWNB, ''], \
2373  to_labelstates([CROSSDOWNA, ''], 'B')
2374  for c in KNOTCHARS.replace(CROSSUP, EMPTY).replace(CROSSDOWN, EMPTY):
2375  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2376  assert to_labelstates([c, ''], labels) == [c, ''], \
2377  (c, labels, to_labelstates([c, ''], labels))
2378  assert to_labelstates(PRIMEKNOTS[0][1]) == PRIMEKNOTS[0][1], \
2379  to_labelstates(PRIMEKNOTS[0][1])
2380  assert to_labelstates(PRIMEKNOTS[3][1]) == [' /A\\', '(( a', ' \\A/'], \
2381  to_labelstates(PRIMEKNOTS[3][1])
2382  assert to_labelstates(PRIMEKNOTS[3][1], 'B') == [' /B\\', '(( a', ' \\A/'], \
2383  to_labelstates(PRIMEKNOTS[3][1], 'B')
2384  assert to_labelstates(PRIMEKNOTS[3][1], 'AAA') == [' /A\\', '(( a', ' \\A/'], \
2385  to_labelstates(PRIMEKNOTS[3][1], 'AAA')
2386  assert to_labelstates(PRIMEKNOTS[3][1], 'AAB') == [' /A\\', '(( a', ' \\B/'], \
2387  to_labelstates(PRIMEKNOTS[3][1], 'AAB')
2388  assert to_labelstates(PRIMEKNOTS[3][1], 'ABA') == [' /A\\', '(( b', ' \\A/'], \
2389  to_labelstates(PRIMEKNOTS[3][1], 'ABA')
2390  assert to_labelstates(PRIMEKNOTS[3][1], 'ABB') == [' /A\\', '(( b', ' \\B/'], \
2391  to_labelstates(PRIMEKNOTS[3][1], 'ABB')
2392  assert to_labelstates(PRIMEKNOTS[3][1], 'BAA') == [' /B\\', '(( a', ' \\A/'], \
2393  to_labelstates(PRIMEKNOTS[3][1], 'BAA')
2394  assert to_labelstates(PRIMEKNOTS[3][1], 'BAB') == [' /B\\', '(( a', ' \\B/'], \
2395  to_labelstates(PRIMEKNOTS[3][1], 'BAB')
2396  assert to_labelstates(PRIMEKNOTS[3][1], 'BBA') == [' /B\\', '(( b', ' \\A/'], \
2397  to_labelstates(PRIMEKNOTS[3][1], 'BBA')
2398  assert to_labelstates(PRIMEKNOTS[3][1], 'BBB') == [' /B\\', '(( b', ' \\B/'], \
2399  to_labelstates(PRIMEKNOTS[3][1], 'BBB')
2400  assert to_labelstates(PRIMEKNOTS[7][1]) \
2401  == [' _____', '/ \\', 'AAAAAAA', '\\ /', ' ^^^^^'], \
2402  to_labelstates(PRIMEKNOTS[7][1])
2403  for labels in labels_list(nb_cross(PRIMEKNOTS[7][1])):
2404  assert to_labelstates(PRIMEKNOTS[7][1], labels) \
2405  == [' _____', '/ \\', labels, '\\ /', ' ^^^^^'], \
2406  (labels, to_labelstates(PRIMEKNOTS[7][1]))
2407  for nb in PRIMEKNOTS:
2408  for i in PRIMEKNOTS[nb]:
2409  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2410  s = to_labelstates(PRIMEKNOTS[nb][i], labels)
2411  assert labelstates_is(s), (nb, i, labels, PRIMEKNOTS[nb][i], s)
2412  assert s == to_labelstates(s, labels), \
2413  (nb, i, labels, s, to_labelstates(s, labels))
2414  assert s == to_labelstates(to_labelstates(PRIMEKNOTS[nb][i]), labels), \
2415  (nb, i, labels, s,
2416  to_labelstates(to_labelstates(PRIMEKNOTS[nb][i]), labels))
2417  assert s == to_labelstates(to_labelstates(PRIMEKNOTS[nb][i], 'BA'*4), labels),\
2418  (nb, i, labels, s,
2419  to_labelstates(to_labelstates(PRIMEKNOTS[nb][i], 'BA'*4), labels))
2420  print('ok'); sys.stdout.flush()
2421 
2422 
2423  print('to_LaTeX()...', end=''); sys.stdout.flush()
2424  assert isinstance(to_LaTeX([]), str)
2425  assert isinstance(to_LaTeX(['']), str)
2426  assert isinstance(to_LaTeX([]), str)
2427  for nb in PRIMEKNOTS:
2428  for i in PRIMEKNOTS[nb]:
2429  assert isinstance(to_LaTeX(PRIMEKNOTS[nb][i]), str), (nb, i)
2430  assert isinstance(to_LaTeX(PRIMEKNOTS[nb][i],
2431  length=20, shape=5, grid=True), str), (nb, i)
2432  print('ok'); sys.stdout.flush()
2433 
2434 
2435  print('to_PS()...', end=''); sys.stdout.flush()
2436  assert isinstance(to_PS([]), str)
2437  assert isinstance(to_PS(['']), str)
2438  assert isinstance(to_PS([]), str)
2439  for nb in PRIMEKNOTS:
2440  for i in PRIMEKNOTS[nb]:
2441  assert isinstance(to_PS(PRIMEKNOTS[nb][i]), str), (nb, i)
2442  assert isinstance(to_PS(PRIMEKNOTS[nb][i],
2443  length=20, margin=0, shape=5, grid=True), str), (nb, i)
2444  print('ok'); sys.stdout.flush()
2445 
2446 
2447  print('to_states()...', end=''); sys.stdout.flush()
2448  assert to_states([]) == [], to_states([])
2449  assert to_states([CROSSUP, '']) == [VERT, ''], to_states([CROSSUP, ''])
2450  assert to_states([CROSSUP, ''], 'B') == [HORI, ''], \
2451  to_states([CROSSUP, ''], 'B')
2452  assert to_states([CROSSDOWN, '']) == [HORI, ''], to_states([CROSSDOWN, ''])
2453  assert to_states([CROSSDOWN, ''], 'B') == [VERT, ''], \
2454  to_states([CROSSDOWN, ''], 'B')
2455  assert to_states([CROSSUPA, '']) == [VERT, ''], to_states([CROSSUPA, ''])
2456  assert to_states([CROSSUPA, ''], 'B') == [HORI, ''], \
2457  to_states([CROSSUPA, ''], 'B')
2458  assert to_states([CROSSDOWNA, '']) == [HORI, ''], to_states([CROSSDOWNA, ''])
2459  assert to_states([CROSSDOWNA, ''], 'B') == [VERT, ''], \
2460  to_states([CROSSDOWNA, ''], 'B')
2461  for c in (KNOTCHARS + VERT + HORI) \
2462  .replace(CROSSUP, EMPTY).replace(CROSSDOWN, EMPTY):
2463  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2464  assert to_states([c, ''], labels) == [c, ''], \
2465  (c, labels, to_states([c, ''], labels))
2466  assert to_states(PRIMEKNOTS[0][1]) == PRIMEKNOTS[0][1], to_states(PRIMEKNOTS[0][1])
2467  assert to_states(PRIMEKNOTS[3][1]) == [' /"\\', '(( =', ' \\"/'], \
2468  to_states(PRIMEKNOTS[3][1])
2469  assert to_states(PRIMEKNOTS[3][1], 'B') == [' /=\\', '(( =', ' \\"/'], \
2470  to_states(PRIMEKNOTS[3][1], 'B')
2471  assert to_states(PRIMEKNOTS[3][1], 'AAA') == [' /"\\', '(( =', ' \\"/'], \
2472  to_states(PRIMEKNOTS[3][1], 'AAA')
2473  assert to_states(PRIMEKNOTS[3][1], 'AAB') == [' /"\\', '(( =', ' \\=/'], \
2474  to_states(PRIMEKNOTS[3][1], 'AAB')
2475  assert to_states(PRIMEKNOTS[3][1], 'ABA') == [' /"\\', '(( "', ' \\"/'], \
2476  to_states(PRIMEKNOTS[3][1], 'ABA')
2477  assert to_states(PRIMEKNOTS[3][1], 'ABB') == [' /"\\', '(( "', ' \\=/'], \
2478  to_states(PRIMEKNOTS[3][1], 'ABB')
2479  assert to_states(PRIMEKNOTS[3][1], 'BAA') == [' /=\\', '(( =', ' \\"/'], \
2480  to_states(PRIMEKNOTS[3][1], 'BAA')
2481  assert to_states(PRIMEKNOTS[3][1], 'BAB') == [' /=\\', '(( =', ' \\=/'], \
2482  to_states(PRIMEKNOTS[3][1], 'BAB')
2483  assert to_states(PRIMEKNOTS[3][1], 'BBA') == [' /=\\', '(( "', ' \\"/'], \
2484  to_states(PRIMEKNOTS[3][1], 'BBA')
2485  assert to_states(PRIMEKNOTS[3][1], 'BBB') == [' /=\\', '(( "', ' \\=/'], \
2486  to_states(PRIMEKNOTS[3][1], 'BBB')
2487  assert to_states(PRIMEKNOTS[7][1]) \
2488  == [' _____', '/ \\', '"""""""', '\\ /', ' ^^^^^'], \
2489  to_states(PRIMEKNOTS[7][1])
2490  for labels in labels_list(nb_cross(PRIMEKNOTS[7][1])):
2491  assert to_states(PRIMEKNOTS[7][1], labels) \
2492  == [' _____', '/ \\', labels.replace('A', '"').replace('B', '='),
2493  '\\ /', ' ^^^^^'], (labels, to_states(PRIMEKNOTS[7][1]))
2494  for nb in PRIMEKNOTS:
2495  for i in PRIMEKNOTS[nb]:
2496  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2497  s = to_states(PRIMEKNOTS[nb][i], labels)
2498  assert states_is(s), (nb, i, labels, PRIMEKNOTS[nb][i], s)
2499  assert s == to_states(s, labels), (nb, i, labels, s, to_states(s, labels))
2500  assert s == to_states(s), (nb, i, labels, s, to_states(s))
2501  assert s == to_states(s, 'BA'*4), (nb, i, labels, s, to_states(s, 'BA'*4))
2502  assert s == to_states(to_labelstates(PRIMEKNOTS[nb][i], labels), labels), \
2503  (nb, i, labels, s,
2504  to_states(to_labelstates(PRIMEKNOTS[nb][i], labels), labels))
2505  assert s == to_states(to_labelstates(PRIMEKNOTS[nb][i]), labels), \
2506  (nb, i, labels, s,
2507  to_states(to_labelstates(PRIMEKNOTS[nb][i]), labels))
2508  assert s == to_states(to_labelstates(PRIMEKNOTS[nb][i], 'BA'*4), labels), \
2509  (nb, i, labels, s,
2510  to_states(to_labelstates(PRIMEKNOTS[nb][i], 'BA'*4), labels))
2511  print('ok'); sys.stdout.flush()
2512 
2513 
2514  print('to_str()...', end=''); sys.stdout.flush()
2515  assert to_str([]) == '', to_str([])
2516  for c in SPACECHARS:
2517  assert to_str([c, '']) == c + '\n', (c, to_str([c, '']))
2518  assert to_str([c, c]) == c + '\n' + c, (c, to_str([c, c]))
2519  assert to_str(PRIMEKNOTS[0][1]) == '()', to_str(PRIMEKNOTS[0][1])
2520  assert to_str(PRIMEKNOTS[7][1]) == ' _____\n/ \\\n%%%%%%%\n\\ /\n ^^^^^', \
2521  to_str(PRIMEKNOTS[7][1])
2522  for labels in labels_list(nb_cross(PRIMEKNOTS[7][1])):
2523  assert to_str(to_labelstates(PRIMEKNOTS[7][1], labels)) \
2524  == ' _____\n/ \\\n{0}\n\\ /\n ^^^^^'.format(labels), \
2525  to_str(to_labelstates(PRIMEKNOTS[7][1], labels))
2526  assert to_str(to_states(PRIMEKNOTS[7][1], labels)) \
2527  == ' _____\n/ \\\n{0}\n\\ /\n ^^^^^' \
2528  .format(labels.replace('A', '"').replace('B', '=')), \
2529  to_str(to_states(PRIMEKNOTS[7][1], labels))
2530  assert to_str(to_universes(PRIMEKNOTS[7][1])) \
2531  == ' _____\n/ \\\nxxxxxxx\n\\ /\n ^^^^^', \
2532  to_str(to_universes(PRIMEKNOTS[7][1]))
2533  print('ok'); sys.stdout.flush()
2534 
2535 
2536  print('to_universes()...', end=''); sys.stdout.flush()
2537  assert to_universes([]) == [], to_universes([])
2538  for c in KNOTCHARS.replace(CROSSUP, EMPTY).replace(CROSSDOWN, EMPTY):
2539  assert to_universes([c, '']) == [c, ''], (c, to_universes([c, '']))
2540  assert to_universes([CROSSUP, '']) == [CROSS, ''], to_universes([CROSSUP, ''])
2541  assert to_universes([CROSSDOWN, '']) == [CROSS, ''], to_universes([CROSSDOWN, ''])
2542  assert to_universes([CROSSUPA, '']) == [CROSS, ''], to_universes([CROSSUPA, ''])
2543  assert to_universes([CROSSDOWNA, '']) == [CROSS, ''], to_universes([CROSSDOWNA, ''])
2544  assert to_universes([CROSSUPB, '']) == [CROSS, ''], to_universes([CROSSUPB, ''])
2545  assert to_universes([CROSSDOWNB, '']) == [CROSS, ''], to_universes([CROSSDOWNB, ''])
2546  assert to_universes(PRIMEKNOTS[0][1]) == PRIMEKNOTS[0][1], to_universes(PRIMEKNOTS[0][1])
2547  assert to_universes(PRIMEKNOTS[3][1]) == [' /x\\', '(( x', ' \\x/'], \
2548  to_universes(PRIMEKNOTS[3][1])
2549  assert to_universes(PRIMEKNOTS[7][1]) \
2550  == [' _____', '/ \\', 'xxxxxxx', '\\ /', ' ^^^^^'], \
2551  to_universes(PRIMEKNOTS[7][1])
2552  for nb in PRIMEKNOTS:
2553  for i in PRIMEKNOTS[nb]:
2554  u = to_universes(PRIMEKNOTS[nb][i])
2555  assert universes_is(u), (nb, i, PRIMEKNOTS[nb][i], u)
2556  assert u == to_universes(u), (nb, i, labels, u, to_universes(u))
2557  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2558  if nb == 0:
2559  assert u == to_universes(to_states(PRIMEKNOTS[nb][i], labels)), \
2560  (nb, i, labels, u,
2561  to_universes(to_states(PRIMEKNOTS[nb][i], labels)))
2562  else:
2563  assert u != to_universes(to_states(PRIMEKNOTS[nb][i], labels)), \
2564  (nb, i, labels, u,
2565  to_universes(to_states(PRIMEKNOTS[nb][i], labels)))
2566  assert u == to_universes(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
2567  (nb, i, labels, u,
2568  to_universes(to_labelstates(PRIMEKNOTS[nb][i], labels)))
2569  print('ok'); sys.stdout.flush()
2570 
2571 
2572  print('universes_is()...', end=''); sys.stdout.flush()
2573  assert universes_is([])
2574  assert universes_is([EMPTY, ''])
2575  for c in UNIVERSECHARS:
2576  assert universes_is([c])
2577  for i in range(256):
2578  c = chr(i)
2579  if c in UNIVERSECHARS:
2580  assert universes_is([c])
2581  for nb in PRIMEKNOTS:
2582  for i in PRIMEKNOTS[nb]:
2583  assert universes_is(to_universes(PRIMEKNOTS[nb][i])), \
2584  (nb, i, PRIMEKNOTS[nb][i], to_universes(PRIMEKNOTS[nb][i]))
2585  if nb > 0:
2586  for i in PRIMEKNOTS[nb]:
2587  for labels in labels_list(nb_cross(PRIMEKNOTS[nb][i])):
2588  assert not universes_is(to_labelstates(PRIMEKNOTS[nb][i], labels)), \
2589  (nb, i, PRIMEKNOTS[nb][i], labels,
2590  to_labelstates(PRIMEKNOTS[nb][i], labels))
2591  assert universes_is(to_states(PRIMEKNOTS[nb][i], labels)), \
2592  (nb, i, PRIMEKNOTS[nb][i], labels,
2593  to_states(PRIMEKNOTS[nb][i], labels))
2594  assert not universes_is(PRIMEKNOTS[nb][i]), (nb, i, PRIMEKNOTS[nb][i])
2595  print('ok'); sys.stdout.flush()
2596 
2597 
2598  print('write()...', end=''); sys.stdout.flush()
2599  s = []
2600  f = tempfile.NamedTemporaryFile(mode='w', suffix='.txt',
2601  prefix='DSPython_test_write_knots_{0}_{1}__'.format(nb, i),
2602  delete=False)
2603  for nb in PRIMEKNOTS:
2604  for i in PRIMEKNOTS[nb]:
2605  write(f, PRIMEKNOTS[nb][i])
2606  s += PRIMEKNOTS[nb][i]
2607  f.close()
2608 
2609  f = open(f.name)
2610  r = read(f)
2611  f.close()
2612  os.remove(f.name)
2613  assert r == s, (r, s)
2614  print('ok'); sys.stdout.flush()
2615 
2616 
2617  print('writhe()...', end=''); sys.stdout.flush()
2618  assert writhe([]) == 0, writhe([])
2619  assert writhe([EMPTY]) == 0, writhe([EMPTY])
2620  assert writhe(['(%)']) == 1, writhe(['(%)'])
2621  assert writhe(['(&)']) == -1, writhe(['(&)'])
2622  for nb in PRIMEKNOTS:
2623  for i in PRIMEKNOTS[nb]:
2624  assert -nb <= writhe(PRIMEKNOTS[nb][i]) <= nb, (nb, i, writhe(PRIMEKNOTS[nb][i]))
2625  assert writhe(PRIMEKNOTS[nb][i]) == -writhe(mirror(PRIMEKNOTS[nb][i])), \
2626  (nb, i, writhe(PRIMEKNOTS[nb][i]), writhe(mirror(PRIMEKNOTS[nb][i])))
2627  print('???', end='')
2628  print('ok'); sys.stdout.flush()
2629 
2630 
2631  print('writhe_cross()...', end=''); sys.stdout.flush()
2632  assert writhe_cross(['(%)'], 1, 0) == 1, writhe_cross(['(%)'], 1, 0)
2633  assert writhe_cross(['(&)'], 1, 0) == -1, writhe_cross(['(&)'], 1, 0)
2634  s = ['_',
2635  '%',
2636  '^']
2637  assert writhe_cross(s, 0, 1) == -1, writhe_cross(s, 0, 1)
2638  s = ['_',
2639  '&',
2640  '^']
2641  assert writhe_cross(s, 0, 1) == 1, writhe_cross(s, 0, 1)
2642  s = ['/%\\',
2643  '%//',
2644  '\\/']
2645  assert writhe_cross(s, 1, 0) == 0, a.writhe_cross(s, 1, 0)
2646  assert writhe_cross(s, 0, 1) == 0, a.writhe_cross(s, 0, 1)
2647  s = ['/%\\',
2648  '&//',
2649  '\\/']
2650  assert writhe_cross(s, 1, 0) == 0, a.writhe_cross(s, 1, 0)
2651  assert writhe_cross(s, 0, 1) == 0, a.writhe_cross(s, 0, 1)
2652  s = ['/%\\',
2653  '\\^/',
2654  ' ^']
2655  assert writhe_cross(s, 1, 0) == -1, writhe_cross(s, 1, 0)
2656  s = ['/&\\',
2657  '\\^/',
2658  ' ^']
2659  assert writhe_cross(s, 1, 0) == 1, writhe_cross(s, 1, 0)
2660  print('ok'); sys.stdout.flush()
2661 
2662 
2663  print()
2664  print('Corners()...', end=''); sys.stdout.flush()
2665  print('???', end='')
2666  print('ok'); sys.stdout.flush()
2667 
2668 
2669  print('Corners.extremity()...', end=''); sys.stdout.flush()
2670  print('???', end='')
2671  print('ok'); sys.stdout.flush()
2672 
2673 
2674  print('Corners.nb_cycle()...', end=''); sys.stdout.flush()
2675  assert Corners([]).nb_cycle() == (0, 0), Corners([]).nb_cycle()
2676  assert Corners([EMPTY, '']).nb_cycle() == (0, 0), Corners([EMPTY, '']).nb_cycle()
2677  assert Corners(['()']).nb_cycle() == (1, 0), Corners(['()']).nb_cycle()
2678  assert Corners([')(']).nb_cycle() == (0, 2), Corners([')(']).nb_cycle()
2679  assert Corners([' _',
2680  '( )',
2681  ' ^']).nb_cycle() == (1, 0), Corners([' _', '( )', ' ^']).nb_cycle()
2682  assert Corners(['_',
2683  ' )',
2684  '_']).nb_cycle() == (0, 2), Corners(['_', ' )', '_']).nb_cycle()
2685  assert Corners([' _',
2686  '( )',
2687  ' _']).nb_cycle() == (0, 2), Corners([' _', '( )', ' _']).nb_cycle()
2688  assert Corners([' _ _',
2689  '( )( )',
2690  ' _ ^']).nb_cycle() == (1, 2), \
2691  Corners([' _ _', '( )( )', ' _ ^']).nb_cycle()
2692  assert Corners([' _ _ _',
2693  ' ( )( )%',
2694  '() _ ^ ^']).nb_cycle() == (3, 2), \
2695  Corners([' _ _ _', ' ( )( )%', '() _ ^ ^']).nb_cycle()
2696  for c in KNOTCHARS.replace(VERT, EMPTY).replace(HORI, EMPTY) \
2697  .replace(CROSSUP, EMPTY).replace(CROSSDOWN, EMPTY):
2698  if c == EMPTY:
2699  assert Corners([c]).nb_cycle() == (0, 0), (c, Corners([c]).nb_cycle())
2700  else:
2701  assert Corners([c]).nb_cycle() == (0, 1), (c, Corners([c]).nb_cycle())
2702  for c in VERT + HORI + CROSSUP + CROSSDOWN + CROSSUPA + CROSSUPB + CROSSDOWNA + CROSSDOWNB:
2703  assert Corners([c]).nb_cycle() == (0, 2), (c, Corners([c]).nb_cycle())
2704  for nb in PRIMEKNOTS:
2705  for i in PRIMEKNOTS[nb]:
2706  assert Corners(PRIMEKNOTS[nb][i]).nb_cycle() == (1, 0), \
2707  (nb, i, Corners(PRIMEKNOTS[nb][i]).nb_cycle(), PRIMEKNOTS[nb][i])
2708  assert Corners(to_universes(PRIMEKNOTS[nb][i])).nb_cycle() == (1, 0), \
2709  (nb, i, Corners(to_universes(PRIMEKNOTS[nb][i])).nb_cycle(),
2710  to_universes(PRIMEKNOTS[nb][i]))
2711  print('ok'); sys.stdout.flush()
2712 
2713 
2714  print('Corners.writhe_cross()...', end=''); sys.stdout.flush()
2715  a = Corners(['(%)'])
2716  assert a.writhe_cross(1, 0, True) == 1, a.writhe_cross(1, 0, True)
2717  assert a.writhe_cross(1, 0, False) == -1, a.writhe_cross(1, 0, False)
2718  a = Corners(['_',
2719  '%',
2720  '^'])
2721  assert a.writhe_cross(0, 1, True) == -1, a.writhe_cross(0, 1, True)
2722  assert a.writhe_cross(0, 1, False) == 1, a.writhe_cross(0, 1, False)
2723  a = Corners(['/%\\',
2724  '%//',
2725  '\\/'])
2726  assert a.writhe_cross(1, 0, True) == 0, a.writhe_cross(1, 0, True)
2727  assert a.writhe_cross(1, 0, False) == 0, a.writhe_cross(1, 0, False)
2728  assert a.writhe_cross(0, 1, True) == 0, a.writhe_cross(0, 1, True)
2729  assert a.writhe_cross(0, 1, False) == 0, a.writhe_cross(0, 1, False)
2730  a = Corners(['/%\\',
2731  '&//',
2732  '\\/'])
2733  assert a.writhe_cross(1, 0, True) == 0, a.writhe_cross(1, 0, True)
2734  assert a.writhe_cross(1, 0, False) == 0, a.writhe_cross(1, 0, False)
2735  assert a.writhe_cross(0, 1, True) == 0, a.writhe_cross(0, 1, True)
2736  assert a.writhe_cross(0, 1, False) == 0, a.writhe_cross(0, 1, False)
2737  a = Corners(['/%\\',
2738  '\\^/',
2739  ' ^'])
2740  assert a.writhe_cross(1, 0, True) == -1, a.writhe_cross(1, 0, True)
2741  assert a.writhe_cross(1, 0, False) == 1, a.writhe_cross(1, 0, False)
2742  print('ok'); sys.stdout.flush()
2743  debug.test_end()
2744 
2745  main_test()
2746 ##\endcond MAINTEST