1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import os, sys, re, bisect
20
22
24 drapeau = 0
25 if os.name == 'mac':
26 dir_texte = 'textes:'
27 else: dir_texte = '/textes/'
28 while drapeau == 0 :
29 drapeau = 1
30 fic = raw_input(chaine)
31 nom = os.getcwd() + dir_texte + fic + '.txt'
32 if os.path.isfile(nom):
33 return fic
34 else:
35
36
37
38
39
40 drapeau = 0
41
43
44 if os.name == 'mac' :
45 if m == "r":
46 dir_texte = 'textes:'
47 else:
48 dir_texte = 'sortie:'
49 else:
50 if m == "r":
51 dir_texte = '/textes/'
52 else:
53 dir_texte = '/sortie/'
54 abs_fic = os.getcwd() + dir_texte + fic + '.txt'
55 return open(abs_fic, m)
56
58
59
60 fichier_entree = self.ouvrir(fic, "r")
61 return fichier_entree.read()
62
64 if not 'sortie' in os.listdir(os.getcwd()):
65 os.mkdir('sortie')
66 return self.ouvrir(fic, "w")
67
69 fichier_sortie = self.ouvrir(fic, "a")
70 fichier_sortie.write(T)
71
73 L = [ ]
74 for Clef in D.keys() :
75 if D[Clef] != { } and D[Clef] != [] :
76 L.append(Clef)
77
78
79 return L
80
82 L = [ ]
83 if D.values != {} :
84 for val in D.values() :
85 if val != { } or val != [] :
86 L.append(val)
87 return L
88
89
91
92 NL = [ ]
93 ncouple = []
94 nncouple = []
95 for couple in L :
96 if couple[0] < C[0] :
97 if couple[1] >= C[0] :
98 ncouple.append(couple[0])
99 ncouple.append(C[0])
100 if couple[1] > C[1] :
101 nncouple.append(C[1])
102 nncouple.append(couple[1])
103 elif couple[1] > C[1] and couple[0] <= C[1] :
104 ncouple.append(C[1])
105 ncouple.append(couple[1])
106 if ncouple != [] :
107 NL.append(ncouple)
108 ncouple = []
109 if nncouple != [] :
110 NL.append(nncouple)
111 nncouple = []
112 if couple[0] > C[1] or couple[1] < C[0] :
113 NL.append(couple)
114 return NL
115
117
118 L = L[:]
119 while len(L) > 0:
120 P = self.dif_intervalles(P, L.pop(0))
121
122 return P
123
124 - def miroir(self, locc, debut, fin):
125 """Prends une liste d'intervalles définis sur l'intervalle [debut,fin]
126 et retourne la différence entre [début,fin] et tous les elements de cette liste
127 en temps linéaire(locc)"""
128 LRes = []
129 pos = debut
130 for (d,f) in locc:
131 if pos < d :
132 LRes.append((pos,d))
133 pos = f
134 if pos < fin: LRes.append((pos,fin))
135
136
137
138
139
140
141
142 return LRes
143
145
146 Q = []
147 while L <> [] and L[-1][-1] >= C[0] :
148 if L[-1][0] > C[1]:
149 Q = [L[-1]] + Q
150 L.pop()
151 else:
152 C = [min(C[0], L[-1][0]), max(C[1], L[-1][-1])]
153 L.pop()
154 L.append(C)
155 return L+Q
156
158 for x in LC:
159
160 bisect.insort_right(L, x)
161 return L
162
164
165
166 bisect.insort_right(L, C)
167 return L
168
169
170
171
172
173
174
175
176
177
178
180 for elt in L1 :
181 if not elt in L2 :
182 return 0
183 return 1
184
186
187 L=L1[:]
188 for X in L2:
189 if X in L:
190 L.remove(X)
191 return L
192
194
195 L=[]
196 for x in L1:
197 if x not in L2: L.append(x)
198 for x in L2:
199 if x not in L1 and x not in L: L.append(x)
200 return L
201
203 for elt in L:
204 if (I[0]>= elt[0] and I[1] <= elt[1] ):
205 return 1
206 return 0
207
209 if (I1[0] >= I2[0] and I1[1] <= I2[1]):
210 return 1
211 return 0
212
214 LL = []
215 while L <> []:
216 if self.inclus_(L[-1], I):
217 LL.append(L[-1])
218
219 L.pop()
220 return LL
221
223 n = 0
224 while L <> []:
225 n = n + L[0][1] - L[0][0]
226
227 L.pop(0)
228 return n
229
230 - def union(self, L1, L2):
231 for I in L1:
232 L2 = self.ajout_intervalle(L2, I)
233 return L2
234
236 for I in L1:
237 L2 = self.addition_intervalle(L2, I)
238 return L2
239
241 ND = {}
242 for Clef,liste in D.iteritems():
243
244
245 if len(liste) > 0:
246 ND[Clef] = liste
247 return ND
248
249
250
251
252 - def rang_blocs(self, occs_blocs, occs_ts_blocs, occs_blocs_d):
253
254 L = self.rang_blocs_(occs_blocs, occs_ts_blocs, occs_blocs_d, 0, [], [0, 0])
255
256 return L
257
258 - def rang_blocs_(self, occs_blocs, occs_ts_blocs, occs_blocs_d, Rang, Acc, Der):
259
260 while occs_blocs <> []:
261 if occs_blocs[0] <= Der:
262 occs_blocs = occs_blocs[1:]
263 elif occs_ts_blocs == []:
264 Acc.append([occs_blocs[0], Rang])
265 occs_blocs = occs_blocs[1:]
266 elif ( occs_ts_blocs[0] in occs_blocs_d or
267 occs_ts_blocs[0] in occs_blocs ):
268 occs_ts_blocs = occs_ts_blocs[1:]
269 elif occs_blocs[0] < occs_ts_blocs[0]:
270 Acc.append([occs_blocs[0], Rang])
271 occs_blocs = occs_blocs[1:]
272 else:
273 Der = occs_ts_blocs[0]
274 occs_ts_blocs = occs_ts_blocs[1:]
275 Rang = Rang + 1
276
277
278 return Acc
279
280
282
283 if L == []:
284 return L
285 else:
286 return self.fusion__(L[-1], L[:-1])
287
289 aux = []
290 while L <> []:
291 if E[1] == L[-1][1]:
292 E = [[L[-1][0][0], E[0][1]], L[-1][1]]
293 L = L[:-1]
294 else:
295 aux = [E] + aux
296 E = L[-1]
297 L.pop()
298 aux = [E] + aux
299
300 return aux
301
302
304 aux0 = []
305 aux1 = []
306 while (L1 <> [] and L2 <> []):
307 if L1[-1][1] == L2[-1][1]:
308 if self.adequation_remplacement(L1[-1][0], L2[-1][0], T, ratio_min_remplacement):
309 aux0.append(L1[-1])
310 aux1.append(L2[-1])
311 L1.pop()
312 L2.pop()
313 elif L1[-1][1] > L2[-1][1]:
314 L1.pop()
315 else:
316 L2.pop()
317 aux0.reverse()
318 aux1.reverse()
319 return [aux0, aux1]
320
321
323 L = []
324 while (L1 <> [] and L2 <> []):
325 if L1[-1][1] == L2[-1][1]:
326 if self.adequation_remplacement(L1[-1][0], L2[-1][0], T, ratio_min_remplacement):
327 L = self.addition_intervalle(L, L1[-1][0])
328 L = self.addition_intervalle(L, L2[-1][0])
329 L1.pop()
330 L2.pop()
331 elif L1[-1][1] > L2[-1][1]:
332 L1.pop()
333 else:
334 L2.pop()
335 return L
336
338 if self.chaine_blanche(texte1) or self.chaine_blanche(texte2):
339 return 0
340 ratio = float(len(texte1))/float(len(texte2))
341
342 if ratio > ratio_min_remplacement or ratio < 1/ratio_min_remplacement:
343 return 0
344 return 1
345
347 for c in texte:
348 if c not in ' \n\t\r':
349 return 0
350 return 1
351
352
353
355 """Renvoie vrai si la chaine ne contient que des séparateurs """
356 while chaine <> '':
357 c = chaine[0]
358 if c <> ' ' and c <> '\n' and c <> '\t' and c <> '\r':
359 return 0
360 chaine = chaine[1:]
361 return 1
362
364
365
366
367 Q = []
368 while L <> [] and P <> []:
369
370
371 if L[0][1] < P[0][0]:
372 L.pop(0)
373
374 elif L[0][0] > P[0][1]:
375 Q = self.addition_intervalle(Q, P[0])
376 P.pop(0)
377
378 elif L[0][1] >= P[0][1]:
379 P.pop(0)
380
381 else:
382 L.pop(0)
383
384
385
386 while P <> []:
387 Q = self.addition_intervalle(Q, P[0])
388 P.pop(0)
389 return Q
390
392
393
394
395 Q = []
396 while L <> [] and P <> []:
397
398
399 if L[0][1] < P[0][0]:
400 L.pop(0)
401
402 elif L[0][0] > P[0][1]:
403 Q = self.addition_intervalle(Q, P[0])
404 P.pop(0)
405
406 elif L[0][0]<=P[0][0] and L[0][1] >= P[0][1]:
407 P.pop(0)
408
409 elif L[0][0]<=P[0][0] and L[0][1] <= P[0][1]:
410 NP = [L[0][1], P[0][1]]
411 L.pop(0)
412 P = [NP]+P[1:]
413 elif L[0][0]>P[0][0] and L[0][1] > P[0][1]:
414 Q = self.addition_intervalle(Q, [P[0][0],L[0][0]])
415 P.pop(0)
416
417
418
419
420 while P <> []:
421 Q = self.addition_intervalle(Q, P[0])
422 P.pop(0)
423 return Q
424
425 - def comp(self, x, y):
426 if x > y:
427 return -1
428 else: return +1
429
431 if len(x) > len(y):
432 return -1
433 else:
434 return +1
435
437 for chaines in liste:
438 if self.sous_motif(chaine, chaines):
439 return 1
440 return 0
441
443 try:
444 return re.search(motif, chaine)
445 except:
446 return 0
447
449
450 return (((I1[0] <= I2[0]) and (I1[1] >= I2[0])) or
451 ((I1[0] <= I2[1]) and (I1[1] >= I2[1])))
452
454
455 for II in L:
456 if self.adjacent(I, II):
457 return 1
458 elif II[0] > I[1]:
459 return 0
460 return 0
461
462
463
464
466
467 L_deb = L_occs_deb
468 L_suite = L_occs_suite
469 res = []
470 while L_deb and L_suite:
471 if L_deb[0]+dec < L_suite[0]:
472 L_deb.pop(0)
473 elif L_deb[0]+dec == L_suite[0]:
474 res.append(L_deb[0])
475 L_deb.pop(0)
476 L_suite.pop(0)
477 else:
478 L_suite.pop(0)
479 return res
480
481
483 res=[]
484 for X in L_occs_deb:
485 if X+dec in L_occs_suite:
486 res.append(X)
487 return res
488
490 res=[]
491 j=0
492 l=len(L_occs_suite)
493 for X in L_occs_deb:
494 while L_occs_suite[j] < X+dec:
495 if j < l-1:
496 j = j+1
497 else: return res
498 if L_occs_suite[j] == X+dec:
499 res.append(X)
500 return res
501
503 res=[]
504 for X in L_occs_deb:
505 if texteMot.posCarMot(X,dec) in L_occs_suite:
506 res.append(X)
507 return res
508
510 """ classe représentant une chaine de caractères à laquelle on peut accéder mot par mot
511 ou caractère par caractère
512 le choix se fait à la création avec le param carOuMot
513 si processEnd alors on calcule toute la table de correspondance mot_car
514 sinon on évite ce calcul et il sera fait à la demande avec testMotCar """
516 self.chaine = texte
517 self.carOuMot = carOuMot
518 self.lgCar = len(self.chaine)
519
520 if not self.carOuMot:
521 self.mot_car = {}
522 self.mot_car[0] = 0
523 self.car_mot = {}
524 self.car_mot[0] = 0
525
526 self.max_mot = 0
527 cur_mot = 0
528 cur_car = 1
529 if processEnd: end = self.lgCar
530 else: end = min(10,self.lgCar)
531 while cur_car < end:
532 if (self.chaine[cur_car-1]==' ' or self.chaine[cur_car-1]=='\r\n' or self.chaine[cur_car-1]=='\r' or self.chaine[cur_car-1]=='\n'):
533 cur_mot = cur_mot + 1
534 self.mot_car[cur_mot] = cur_car
535 self.max_mot = cur_mot
536 self.car_mot[cur_car] = cur_mot
537 cur_car = cur_car+1
538 self.lgMot = cur_mot+1
539
541
542 if pos not in self.mot_car:
543
544
545
546 cur_mot = self.max_mot
547 cur_car = self.mot_car[self.max_mot]+1
548
549 while cur_mot != pos:
550
551 if (self.chaine[cur_car-1]==' ' or self.chaine[cur_car-1]=='\r\n' or self.chaine[cur_car-1]=='\r' or self.chaine[cur_car-1]=='\n'):
552 cur_mot+=1
553 self.mot_car[cur_mot] = cur_car
554 self.max_mot = cur_mot
555 self.car_mot[cur_car] = cur_mot
556 cur_car = cur_car+1
557 self.lgMot = cur_mot+1
558
559
560
561
563 for i in self.mot_car:
564 sys.stderr.write(str(i)+":"+str(self.mot_car[i])+" / ")
565 sys.stderr.write(" "+self.toStr()+"\n")
566 sys.stderr.flush()
567
570
571 - def slice(self,deb,fin):
572
573 if (self.carOuMot):
574 return self.chaine[deb:fin]
575 else:
576
577
578 return self.chaine[self.mot_car[deb]:self.mot_car[fin]]
579
581 '''slice uniquement en caractères'''
582 return self.chaine[deb:fin]
583
584 - def sliceM(self, motDeb, carDeb, motFin, carFin):
585 """ si mode mot, slice en prenant le motDeb ième mot + carDeb caractères jusqu'au motFin ième mot + carFin caractères
586 sinon les 2 param mot sont considérés comme exprimés en caractères """
587 if (self.carOuMot):
588 return self.chaine[motDeb+carDeb:motFin+carFin]
589 else:
590
591
592
593
594
595
596 return self.chaine[self.mot_car[motDeb]+carDeb:self.mot_car[motFin]+carFin]
597
598 - def sliceC(self, carDeb, motDeb, carFin, motFin):
599 """ si mode mot, slice en prenant le mot qui correspond au carDeb ième caractère + motDeb mot
600 jusqu'au mot qui correspond au carFin ième caractère + motFin mot
601 sinon les 2 param mot sont considérés comme exprimés en caractères """
602 if (self.carOuMot):
603 return self.chaine[carDeb+motDeb:carFin+motFin]
604 else:
605
606
607
608
609
610
611
612 return self.chaine[self.mot_car[self.car_mot[carDeb]+motDeb]:self.mot_car[self.car_mot[carFin]+motFin]]
613
614
616 """ renvoie une position en nb de caractères en convertissant carDeb carac + motDeb mots """
617 if (self.carOuMot):
618
619 return carDeb+motDeb
620 else:
621
622 if ((carDeb in self.car_mot) and (self.car_mot[carDeb]+motDeb in self.mot_car)):
623 return self.mot_car[self.car_mot[carDeb]+motDeb]
624 else:
625 return -1
626
628 """ renvoie une position en nb de caractères en convertissant motDeb mots + carDeb carac """
629 if (self.carOuMot):
630
631 return motDeb+carDeb
632 else:
633
634 return self.mot_car[motDeb]+carDeb
635
637 if (self.carOuMot): return self.lgCar
638 else: return self.lgMot
639
641 """ retourne la longueur en caractères """
642 return self.lgCar
643
645 if (self.carOuMot): return self.lgCar
646 else: return self.lgMot
647
648
649
650
652 """ remplace le dictionnaire initial: hachage des chaines de facon à ne stocker que
653 les l_hach premiers elements
654 en mode mot, on indexe sur le nb de mots (et plus sur le nombre de car) et
655 sur la chaine de maximum l_hach premiers cararctères (comme en mode normal) et
656 on stocke comme valeurs les occurences en carctères (et pas en mots) """
657 - def __init__(self, l_hachage, texteMot, lg_texte1, carOuMot):
658 self.texteMot = texteMot
659 self.lg_texte1 = lg_texte1
660 self.l_hachage = l_hachage
661 self.carOuMot = carOuMot
662 self.E = {}
663 self.initialise()
664
665
674
676 """ ajoute une occurence i de ch au dictionnaire
677 ch doit être de type chaineMot """
678
679
680
681 ln = ch.length()
682
683 if ln not in self.E:
684 self.E[ln] = {}
685
686
687 if ch.sliceCar(0,self.l_hachage) not in self.E[ln]:
688
689
690 self.E[ln][ch.sliceCar(0,self.l_hachage)] = {}
691
692 occ = self.occ_chaine(ch)
693 if occ == []:
694
695 self.E[ln][ch.sliceCar(0,self.l_hachage)][i]=[i]
696
697 else:
698
699 self.E[ln][ch.sliceCar(0,self.l_hachage)][occ].append(i)
700
701
702
704 """ajoute une liste d'occurences à ch
705 ch doit être de type chaineMot """
706
707
708
709 ln = ch.length()
710 if ln not in self.E:
711 self.E[ln] = {}
712
713
714 if ch.sliceCar(0,self.l_hachage) not in self.E[ln]:
715 self.E[ln][ch.sliceCar(0,self.l_hachage)] = {}
716 occ = self.occ_chaine(ch)
717 if occ == []:
718 occ = L_occs[0]
719
720 self.E[ln][ch.sliceCar(0,self.l_hachage)][occ] = []
721 for i in L_occs:
722
723 self.E[ln][ch.sliceCar(0,self.l_hachage)][occ].append(i)
724
726 """ ch doit être de type chaineMot
727 si la chaine ch se trouve dans le dictionnaire, renvoie sa première
728 occurrence associee qui est la clé pour accéder à sa liste d'occurences
729 sinon, renvoie [] """
730
731 ln = ch.length()
732 G = self.E.get(ln)
733 if not G:
734 return []
735
736 H = G.get(ch.sliceCar(0,self.l_hachage))
737 if not H:
738 return []
739 if (self.carOuMot and ln < self.l_hachage):
740
741 return H.keys()[0]
742 else:
743
744 for occ in H:
745
746 if self.texteMot.sliceC(occ,0,occ,ln) == ch.toStr():
747 return occ
748
749
751 """ renvoie la liste des occurences de la chaine associée à la chaine ch """
752
753 k = ch.length()
754
755 H = self.E.get(k).get(ch.sliceCar(0,self.l_hachage))
756 if not H:
757 return []
758 if (self.carOuMot and k < self.l_hachage):
759 return H.values()[0]
760 else:
761
762 return self.E.get(k).get(ch.sliceCar(0,self.l_hachage)).get(self.occ_chaine(ch))
763 return []
764
766 """ Retourne la liste des occurences de la chaine de longueur k d'index radical
767 dans le texte. L'index a une taille inférieur à self.l_hachage qui peut etre inférieure à k """
768 G = self.E.get(k).get(radical)
769 if G:
770 return self.E.get(k).get(radical).iterkeys()
771 else: return []
772
774 """ Retourne la liste des index-chaines (ou radicaux) de longueur k """
775 G = self.E.get(k)
776 if G:
777 return self.E.get(k).iterkeys()
778 else: return []
779
781 """ Parcours les listes d'occurences associées à un radical
782 Si parmi ces listes, une est vide ou ne possède pas de répétitions
783 entre le texte1 et le texte2 alors supprimer cette liste d'occurences """
784 for occ in self.toutes_occ_chaines_it(k, radical):
785 if not self.repetition(self.E.get(k).get(radical)[occ]):
786 del self.E.get(k).get(radical)[occ]
787 if self.E.get(k)[radical] == { }:
788 del self.E.get(k)[radical]
789
791 """ Teste si la liste d'occurrences Locc mentionne des occurrences répétitives """
792 return (Locc <> [] and (Locc[0] < self.lg_texte1) and (Locc[-1] >= self.lg_texte1))
793
795 """ supprime la liste d'occurence d'une chaine ch
796 ch est de type chaineMot """
797
798 k = ch.length()
799
800 G = self.E.get(k).get(ch.sliceCar(0,self.l_hachage))
801 if G:
802
803 del self.E.get(k).get(ch.sliceCar(0,self.l_hachage))[self.occ_chaine(ch)]
804
805 self.nettoyer_radical(k, ch.sliceCar(0,self.l_hachage))
806
808 sys.stderr.write( "Impression etat dictionnaire\n")
809 for k in self.E:
810 nbre = len(self.E[k])
811 if nbre != -1:
812 sys.stderr.write("k = "+ str(k)+ " nombre chaines: "+ str(nbre) +"\n")
813
814
815
816 sys.stderr.flush()
817
818
820 """ C'est là une classe qui contient, dans un dictionnaire toutes les chaînes
821 optimales. Plus exactement, chaque chaine est repérée par son occurrence sur
822 le texte (l'occurrence de rang inférieur pour être plus précis), et par
823 l'occurrence de la fin de la chaîne. Cette classe est utilisée par la fonction
824 "blocs_maximaux" """
825
826 - def __init__(self, Dict, texte, lg_texte1, carOuMot, long_min_pivots):
827 """ L'initialisation se fait à l'aide du dictionnaire qui comprend l'ensemble des
828 fragments répétés et de la longueur du premier texte. """
829 self.tool = Utile()
830
831
832 self.chaines_optimales = { }
833
834
835 self.occs_optimales = { }
836 self.clefs = self.tool.non_nul_keys(Dict.E)
837 self.clefs.sort()
838 self.clefs.reverse()
839 self.lg_texte1 = lg_texte1
840 self.lg = len(texte)
841 self.texte = texte
842 self.carOuMot = carOuMot
843 self.long_min_pivots = long_min_pivots
844 for i in range(0, self.lg):
845 self.chaines_optimales[i] = i
846 self.occs_optimales[i] = []
847
848
849 self.construire_chaines_optimales(Dict)
850
851
852
853
854
855 self.allonger_chaines_i(0)
856
857
858 self.stockage_chaines(texte)
859
860
861
862
863
864
866 return (self.chaines_optimales[occ] >= occ + len(chaine))
867
868
869
871
872 return (Locc <> [] and (Locc[0] < self.lg_texte1) and (Locc[-1] >= self.lg_texte1))
873
875
876 N=0
877
878 LLocc = Locc[:]
879 while LLocc:
880 O = LLocc[0]
881 while O < LLocc[0]+ln and self.chaines_optimales[O] < LLocc[0]+ln:
882 N=1
883 self.chaines_optimales[O] = LLocc[0] + ln
884 self.occs_optimales[O] = Locc
885 O = O+1
886
887 LLocc = LLocc[1:]
888 return N
889
890
891
892
893
894
895
896
897
898
899
900
923
924
926
927 NOcc = self.chaines_optimales[Occ]
928 if NOcc == Occ:
929 NOcc = Occ + 1
930 i=0
931 while ((Occ < self.lg_texte1 and NOcc < self.lg_texte1)
932 or (Occ >= self.lg_texte1 and NOcc < self.lg)):
933
934 while self.allonger_chaines_(Occ):
935
936 pass
937 Occ += 1
938 NOcc = self.chaines_optimales[Occ]
939 if NOcc == Occ:
940 NOcc = Occ + 1
941 i+=1
942
943
945 Occ_fin = self.chaines_optimales[Occ]
946 NOcc = Occ
947 while (NOcc < self.lg and
948 self.repetition(self.occs_optimales[NOcc]) and
949 not self.chaines_optimales[NOcc] > Occ_fin):
950 NOcc = NOcc + 1
951
952 if NOcc >= self.lg:
953 NOcc = self.lg - 1
954 Occ_fin = self.lg
955 else: Occ_fin = self.chaines_optimales[NOcc]
956 LOcc = self.tool.composition_decalee_(self.occs_optimales[Occ], self.occs_optimales[NOcc], NOcc - Occ)
957
958
959
960
961 if self.repetition(LOcc):
962
963 return self.ajout_occs(LOcc, Occ_fin - Occ)
964 else:
965
966 return 0
967
968
969
970
972 d = 0
973 f = 0
974 self.blocs_texte = {}
975 while d < len(self.chaines_optimales):
976 if f <> self.chaines_optimales[d] :
977 f = self.chaines_optimales[d]
978 if (f-d >= self.long_min_pivots):
979
980 if self.blocs_texte.has_key(texte[d:f]):
981 self.blocs_texte[texte[d:f]].append(d)
982 else: self.blocs_texte[texte[d:f]] = [d]
983
984 d = d+1
985
986
988 tool = Utile()
989 locc = [(10,20),(40,45),(42,60)]
990 LRes = tool.miroir(locc,0,100)
991
992 if __name__ == '__main__':
993 test_miroir()
994