Bon en fait comme je n'aurais probablement pas le temps de finir avant de partir en vacances, je poste un code non terminé.
Il reste à affiner la partie Fréquence d'Apparition, permettant de déterminer quel code est meilleur que l'autre. Il faudrait également ajouter une petite GUI, histoire que ça fasse propre.
J'ai fait plusieurs tests, plus ou moins concluant... Il reste un gros travail d'affinage à faire, histoire d'arriver au meilleur résultat possible. Voilou, je posterai peut être un code plus complet plus tard, ou pas !
Code : Tout sélectionner
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Auteur: Cls
; Version : Juillet 2010
;
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Permet de déterminer la clef et le texte clair en fonction d'un texte chiffré selon l'aglorithme de Vigenere
; Constantes
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#TABLE_SIZE = 10000 ; Longueur maximale du message crypté
#REPEATED_SEQUENCE_MAX_SIZE = 11 ; Taille maximale des séquences identiques ; baisser pour accélérer le traitement
#REPEATED_SEQUENCE_MIN_SIZE = 3 ; Taille minimale des séquences identiques
#MIN_KEY_ASCII = 97 ; Premier caractère testé pour la clef (ici 'a')
#MAX_KEY_ASCII = 122 ; Dernier caractère testé pour la clef (ici 'z')
; Langue
#FR = 0
#EN = 1
Enumeration
#l_A
#l_B
#l_C
#l_D
#l_E
#L_F
#l_G
#l_H
#l_I
#l_J
#l_K
#l_L
#l_M
#l_N
#l_O
#l_P
#l_Q
#l_R
#l_S
#l_T
#l_U
#l_V
#l_W
#l_X
#l_Y
#l_Z
#l_ESPACE
EndEnumeration
; Total des fréquences (97 pour FR)
#SUM_FREQUENCE_APPARITION = 100
; Tableau stockant les fréquences d'apparition des lettres en fonction de la langue
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Voir ce lien pour + d'info : http://fr.wikipedia.org/wiki/Analyse_fr%C3%A9quentielle
Global Dim Frequence_Apparition(1, 26) ; Langue lettre
Frequence_Apparition(#FR, #l_A) = 9.42
Frequence_Apparition(#FR, #l_B) = 1.02
Frequence_Apparition(#FR, #l_C) = 2.64
Frequence_Apparition(#FR, #l_D) = 3.39
Frequence_Apparition(#FR, #l_E) = 15.87
Frequence_Apparition(#FR, #L_F) = 0.95
Frequence_Apparition(#FR, #l_G) = 1.04
Frequence_Apparition(#FR, #l_H) = 0.77
Frequence_Apparition(#FR, #l_I) = 8.41
Frequence_Apparition(#FR, #l_J) = 0.89
Frequence_Apparition(#FR, #l_K) = 0.00
Frequence_Apparition(#FR, #l_L) = 5.34
Frequence_Apparition(#FR, #l_M) = 3.24
Frequence_Apparition(#FR, #l_N) = 7.15
Frequence_Apparition(#FR, #l_O) = 5.14
Frequence_Apparition(#FR, #l_P) = 2.86
Frequence_Apparition(#FR, #l_Q) = 1.06
Frequence_Apparition(#FR, #l_R) = 6.46
Frequence_Apparition(#FR, #l_S) = 7.90
Frequence_Apparition(#FR, #l_T) = 7.26
Frequence_Apparition(#FR, #l_U) = 6.24
Frequence_Apparition(#FR, #l_V) = 2.15
Frequence_Apparition(#FR, #l_W) = 0.00
Frequence_Apparition(#FR, #l_X) = 0.30
Frequence_Apparition(#FR, #l_Y) = 0.24
Frequence_Apparition(#FR, #l_Z) = 0.32
Frequence_Apparition(#FR, #l_ESPACE) = 8
Frequence_Apparition(#EN, #l_A) = 8.08
Frequence_Apparition(#EN, #l_B) = 1.67
Frequence_Apparition(#EN, #l_C) = 3.18
Frequence_Apparition(#EN, #l_D) = 3.99
Frequence_Apparition(#EN, #l_E) = 12.56
Frequence_Apparition(#EN, #L_F) = 2.17
Frequence_Apparition(#EN, #l_G) = 1.8
Frequence_Apparition(#EN, #l_H) = 5.27
Frequence_Apparition(#EN, #l_I) = 7.24
Frequence_Apparition(#EN, #l_J) = 0.14
Frequence_Apparition(#EN, #l_K) = 0.63
Frequence_Apparition(#EN, #l_L) = 4.04
Frequence_Apparition(#EN, #l_M) = 2.60
Frequence_Apparition(#EN, #l_N) = 7.38
Frequence_Apparition(#EN, #l_O) = 7.47
Frequence_Apparition(#EN, #l_P) = 1.91
Frequence_Apparition(#EN, #l_Q) = 0.09
Frequence_Apparition(#EN, #l_R) = 6.42
Frequence_Apparition(#EN, #l_S) = 6.59
Frequence_Apparition(#EN, #l_T) = 9.15
Frequence_Apparition(#EN, #l_U) = 2.79
Frequence_Apparition(#EN, #l_V) = 1
Frequence_Apparition(#EN, #l_W) = 1.89
Frequence_Apparition(#EN, #l_X) = 0.21
Frequence_Apparition(#EN, #l_Y) = 1.65
Frequence_Apparition(#EN, #l_Z) = 0.07
Frequence_Apparition(#EN, #l_ESPACE) = 8
; Structures, tables & listes chainées
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Structure REPEATED_SEQUENCE
sequence.s
espace.i
EndStructure
;
Global Dim Crypt.b(#TABLE_SIZE)
Global NewList Repeated.REPEATED_SEQUENCE()
Global NewList Premier.i() : Global NewList Premier2.i()
Global NewList PossibleKeysSize.i()
; Procédures et fonctions
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Met les données en tableau et renvoi la taille des données
Procedure GetDatas()
Restore donnees
idx = 0
Repeat
Read.s tmp.s
;Debug tmp
If tmp = "" : Break : EndIf
For x = 1 To CountString(tmp, ",") + 1
Crypt(idx) = Val(StringField(tmp, x, ","))
idx + 1
Next
Until tmp = ""
ProcedureReturn idx
EndProcedure
; Pris sur le forum FR
; Auteur : Dr. Dri
Procedure GCD(a.l, b.l)
!MOV eax, dword [p.v_a]
!MOV ebx, dword [p.v_b]
gcd_loop:
;tant que b <> 0
!CMP ebx, 0
!JE l_gcd_end
;calcule le PGCD
!CDQ
!IDIV ebx
!MOV eax, ebx
!MOV ebx, edx
!JMP l_gcd_loop
gcd_end:
!RET 8
EndProcedure
Procedure Decompose(nombre.i)
i.i = 2
While nombre > 1
While nombre % i = 0
AddElement(Premier()) : Premier() = i
AddElement(Premier2()) : Premier2() = i
nombre = Round(nombre / i, #PB_Round_Down)
Wend
i + 1
Wend
EndProcedure
Procedure GeneratePossibleKeysSize(nombre.i)
AddElement(PossibleKeysSize()) : PossibleKeysSize() = nombre
ForEach Premier()
; Cherche si déjà en table
found.b = #False
ForEach PossibleKeysSize()
If PossibleKeysSize() = Premier(): found = #True : Break : EndIf
Next
If found = #False
AddElement(PossibleKeysSize()) : PossibleKeysSize() = Premier()
EndIf
ForEach Premier2()
nbGenerate.i = Premier() * Premier2()
If nbGenerate > nombre : Continue : EndIf
; Cherche si déjà en table
found.b = #False
ForEach PossibleKeysSize()
If PossibleKeysSize() = nbGenerate: found = #True : Break : EndIf
Next
If found : Continue : EndIf
AddElement(PossibleKeysSize()) : PossibleKeysSize() = nbGenerate
Next
Next
EndProcedure
Procedure.i FindMaxKeySize(size)
; Taille de la chaine à recherche
For x = #REPEATED_SEQUENCE_MAX_SIZE To #REPEATED_SEQUENCE_MIN_SIZE Step -1
;Debug "============ " + Str(x) + " ============"
; Recherche les séquences répétées dans le texte chiffré
; Concrètement on recherche les séquences de X à Y caractères qui se répètent
For i = 0 To size - x
; Génère la chaine à trouver
sequenceToFind.s = ""
For y = 0 To x - 1
sequenceToFind + Chr(Crypt(i + y))
Next
;Debug sequenceToFind
; Cherche si cette chaine existe dans le reste du texte
For j = i + 1 To size - x
; Génère une chaine à analyser
sequenceToSearch.s = ""
For z = 0 To x - 1
sequenceToSearch + Chr(Crypt(j + z))
Next
;Debug sequenceToSearch
; Test si les 2 concordent
If sequenceToFind = sequenceToSearch
AddElement(Repeated())
Repeated()\sequence = sequenceToFind
Repeated()\espace = Abs(i - j)
EndIf
Next
Next
Next
; Debug
;ForEach Repeated()
; Debug Repeated()\sequence + " > " + Str(Repeated()\espace)
;Next
; Exclu les valeurs trop basses
ForEach Repeated()
If Repeated()\espace < 2 : DeleteElement(Repeated()) : EndIf
Next
; Déboublonne
NewList RepeatedEspaceUnique.l()
NewList RepeatedEspaceUnique2.l()
ForEach Repeated()
; Cherche si déjà en table
found.b = #False
ForEach RepeatedEspaceUnique()
If RepeatedEspaceUnique() = Repeated()\espace : found = #True : Break : EndIf
Next
If found : Continue : EndIf
AddElement(RepeatedEspaceUnique()) : RepeatedEspaceUnique() = Repeated()\espace
AddElement(RepeatedEspaceUnique2()) : RepeatedEspaceUnique2() = Repeated()\espace
Next
; Puis détermine le diviseur commum (PGCD) qui correspond à toutes les valeurs
pgcd.l = 9999
ForEach RepeatedEspaceUnique()
ForEach RepeatedEspaceUnique2()
If RepeatedEspaceUnique() <> RepeatedEspaceUnique2()
tPGCD = GCD(RepeatedEspaceUnique(), RepeatedEspaceUnique2())
If tPGCD < pgcd : pgcd = tPGCD : EndIf
EndIf
Next
Next
ProcedureReturn pgcd
EndProcedure
Procedure.s HumanizeString(chaine.s)
; Remplace le "\" qui nous ferait planter la reg exp
ReplaceString(chaine, "\", " ", #PB_String_InPlace)
; On met en majuscule pour faire l'analyse
chaine = UCase(chaine)
founded.s = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ"
replace.s = "AAAAAAACEEEEIIIINOOOOOUUUUY"
For i = 0 To Len(founded) - 1
ReplaceString(chaine, Mid(founded, i, 1), Mid(replace, i, 1), #PB_String_InPlace)
Next
; Ensuite on supprime tous les caractères bizarres (non A-Z) via une RegExp
regexp = CreateRegularExpression(#PB_Any, "[^A-Z]+")
If regexp
chaine = ReplaceRegularExpression(regexp, chaine, "")
EndIf
ProcedureReturn chaine
EndProcedure
; Renvoi un float réprésentant la probabilité que ce texte soit Anglais ou Francais (de manière plus générale : LATIN)
Procedure.f AnalyseFrequenceApparition(chaine.s)
; On conserve la taille totale de la chaine pour pouvoir générer une moyenne sensée
lenBeforeHumanize = Len(chaine)
chaineBeforeHumanize.s = chaine
; Nettoie la chaine
chaine = HumanizeString(chaine)
; Pas de caractère "humain" => probabilite 0
If Len(chaine) = 0 : ProcedureReturn 0 : EndIf
; La chaine est maintenant nettoyee (lettre seulement) et en majuscule
; On lance notre algo de frequence de'appartion
; Pour chaque lettre on additionne les frequences associées
sumFrequence.f = #SUM_FREQUENCE_APPARITION
For i = 0 To Len(chaine) - 1
char.s = Mid(chaine, i, 1)
ascChar.b = Asc(char)
; Ce sont des majuscules, on retire l'offset des MAJUSCULES en table ASCII (donc 65)
sumFrequence + Frequence_Apparition(#FR, ascChar - Asc("A"))
Next
; On divise la sum des frequences par le nombre de lettre totale (avant humanize)
freqMoyenne.f = sumFrequence / lenBeforeHumanize
; On retire une frequence de penalite si il y a trop de caractère bizarre (permet d'affiner les resultats)
moyenneCaractereFaux.f = (lenBeforeHumanize - Len(chaine)) - (lenBeforeHumanize / Frequence_Apparition(#FR, #l_ESPACE))
penalite.f = (moyenneCaractereFaux / Len(chaine)) * Frequence_Apparition(#FR, #l_E)
; Bonus : on ajoute une frequence de bonus si on trouve des espaces dans la ligne
If FindString(chaineBeforeHumanize, " ", 1) > 0
limitation.i = CountString(chaineBeforeHumanize, " ") % Frequence_Apparition(#FR, #l_ESPACE) / 2
bonus.f = lenBeforeHumanize / Frequence_Apparition(#FR, #l_ESPACE) * (limitation)
freqMoyenne + bonus
EndIf
;Debug "Penalite : " + StrF(penalite, 3) + " - Bonus : " + StrF(bonus, 3)
retour.f = freqMoyenne - penalite
;If retour < 0 : retour = 0 : EndIf
ProcedureReturn retour
EndProcedure
; Initialise les données cryptées
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
size = GetDatas()
; 1ere étape : détermine les tailles de clef possible
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
lenKeyMax = FindMaxKeySize(size)
Debug "La clef fait : " + Str(lenKeyMax) + " caractères MAXIMUM"
Decompose(lenKeyMax)
GeneratePossibleKeysSize(lenKeyMax)
Debug "Il y a " + Str(ListSize(PossibleKeysSize())) + " longueurs de clefs possibles."
; On tri par longueur de clef DESC
SortList(PossibleKeysSize(), #PB_Sort_Descending)
; 2e étape : Génère les résultats possible en fonction des clefs
; puis compare le résultat avec les frequences usuels.
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Ce tableau stocke les meilleurs résultats par longueur de clef
Dim Results.f(ListSize(PossibleKeysSize()), lenKeyMax, 1)
Dim ResultsPassword.s(ListSize(PossibleKeysSize()))
idxBestResult.i = 0
totalFrequenceBestResult.f = -#SUM_FREQUENCE_APPARITION
; Teste toutes les longueurs possibles
ForEach PossibleKeysSize()
index.i = ListIndex(PossibleKeysSize())
; Longueur de clef
lenKey = PossibleKeysSize()
totalFrequence.f = 0.0
rotation.i = 0
; Tableau temporaire permettant d'exploiter le message crypté
Global Dim Cypher.b(lenKey, #TABLE_SIZE)
; On rempli un tableau à 2 dimensions avec :
; En X le rang dans la clef (donc de 0 a Len(KEY) - 1)
; En Y le rang dans le message total (donc entre 0 et Len(Message) / Len(KEY))
For i = 0 To size - 1
key.i = i % lenKey
line.i = Round(i / lenKey, #PB_Round_Down)
Cypher(key, line) = Crypt(i)
Next
; Nous avons maintenant un tableau permettant d'exploiter tous les caractères cryptés avec la même partie de la clef
; Pour chaque clef, on associe la frequence la plus forte
For idxKey = 0 To lenKey - 1
; Ici on test uniquement les minuscules
For i = #MIN_KEY_ASCII To #MAX_KEY_ASCII
lin.s = ""
For j = 0 To Round(size / lenKey, #PB_Round_Down) - 1
lin + Chr(Cypher(idxKey, j) + i)
Next
; Compte le nombre de rotation pour extraire la moyenne
rotation + 1
; Calcule la
frequence.f = AnalyseFrequenceApparition(lin)
totalFrequence.f + frequence
;Debug "Line : " + Str(idxKey) + " - Key : " + Chr(i) + " => " + lin + " - PROBA : " + StrF(frequence, 4)
;Debug "Line : " + Str(idxKey) + " - Key : " + Chr(i) + " - Proba : " + StrF(frequence, 4)
If Results(index, idxKey, 1) < frequence
Results(index, idxKey, 0) = i ; On stocke la clef sous forme ASCII
Results(index, idxKey, 1) = frequence ; On stocke la probablité qu'elle a reçu
EndIf
Next
Next
; Moyenne des frequences
totalFrequence / rotation
totalFrequence = Abs(totalFrequence)
; Ici on a trouvé la clef la plus probable pour cette longueur
password.s = ""
For i = 0 To lenKey - 1
password + Chr(Results(index, i, 0))
Next
Debug password + " - Freq : " + StrF(totalFrequence)
ResultsPassword(index) = password
; On teste si c'est le meilleur résultat de toutes les longueurs de clef
If totalFrequence > totalFrequenceBestResult
totalFrequenceBestResult = totalFrequence
idxBestResult = index
EndIf
Next
; On a trouvé la meilleure clef en fonction de frequence d'apparition cumulé
;
Debug ""
Debug "La meilleure clef trouvée est : " + ResultsPassword(idxBestResult)
Debug ""
Debug ""
Debug "Le texte en clair est probablement : "
Debug ""
SelectElement(PossibleKeysSize(), idxBestResult)
ligne.s = ""
For i = 0 To size - 1
ligne.s + Chr(Crypt(i) + Results(idxBestResult, i % PossibleKeysSize(), 0))
Next
Debug ligne
DataSection
donnees:
Data.s "-34,11,-1,116,-3,-82,0,13,2,4,4,-84,8,-24,3,14,119,-83,3,-8,-80,-9,-4,13,-69,0,5,-9,-16,14,3,-4,12,7,-15,-72,-21,5,-69,-5,-10,3,-1,-7,-11,13,1,-69"
Data.s "-15,-15,-86,2,-120,3,124,-2,-6,-19,10,-18,-11,8,-13,-5,11,6,-80,7,16,15,-69,0,-19,-86,-11,0,3,-8,13,-5,-15,-72,-21,-73,0,6,1,-4,1,3,-3,10,11,14,-83"
Data.s "-15,-8,-82,13,6,9,0,-1,-18,10,-20,-80,-51,-66,-67,-49,-65,-80,0,-62,-122,12,2,-11,-6,-13,-65,-5,-8,-69,-33,-5,18,-16,-4,7,-17,-83,-8,1,-2,3,9,0,-124"
Data.s "-83,-19,0,-3,8,9,-77,-1,1,1,-6,-13,121,-69,-15,-14,-73,-10,-8,-3,1,3,13,-14,-70,-86,-38,4,-73,3,13,-9,-7,1,-20,2,-69,-5,-10,3,-1,-7,-11,13,1,-69"
Data.s "-18,-84,115,2,-120,-73,-12,15,6,-15,1,-11,4,-69,-13,-5,-73,7,2,3,4,16,-69,-18,-6,-3,-82,2,6,1,15,4,-15,-72,103,-80,11,-13,-10,5,-8,-80,-8,0,18,19"
Data.s "-83,-4,-7,3,17,-73,-1,0,-78,-1,-3,-22,-1,9,-14,-69,-73,-42,-8,-11,12,18,0,-83,-10,-7,3,17,-73,-60,-48,-62,-84,5,-16,-4,7,-9,-4,5,6,-80,-8,-62,2,19"
Data.s "1,-15,-8,1,8,6,1,14,-78,-1,-3,-7,-15,4,-13,-5,11,-77,5,8,4,9,4,0,117,-17,1,-51"
Data.s ""
;
; Data.s "-37,15,15,127,7,-69,-5,21,12,0,6,-69,10,0,16,10,125,-69,6,4,-67,-6,-11,11,-70,18,22,4,-10,10,6,8,14,12,-7,-69,-2,20,-67,4,-3,7,6,8,-2,9,-8,-69,-2"
; Data.s "4,-67,11,125,7,-125,2,5,-8,6,2,-1,12,2,5,8,14,-70,18,18,9,-76,14,-5,-65,4,-8,0,0,12,8,2,-73,-8,-62,-1,23,17,-4,2,14,3,14,11,10,-76,0,8,-65,11,6,10"
; Data.s "0,7,1,15,-4,-76,-51,-54,-49,-43,-61,-76,7,-63,-120,14,12,-3,11,-1,-65,1,-4,-76,-24,9,25,6,3,0,-4,-70,0,11,5,3,9,-3,-120,-67,-8,10,10,3,17,-67,-5"
; Data.s "3,16,-4,11,-122,-73,-9,0,-70,2,5,0,-6,1,12,4,-53,-73,-32,0,-70,15,15,-4,1,4,-1,17,-67,4,-3,7,6,8,-2,9,-8,-69,-5,-65,-122,11,125,-69,-5,19,17,-4,-3"
; Data.s "9,14,-65,2,5,-76,15,12,14,6,10,-76,-4,8,18,-67,-6,3,9,14,17,2,-73,116,-69,10,4,6,5,-7,-69,-2,4,18,15,-76,11,9,20,15,-73,0,0,-70,18,2,-6,3,9,-2,-51"
; Data.s "-67,-38,-4,-4,11,20,2,-73,-2,10,15,17,-67,-56,-55,-53,-70,12,6,3,0,4,9,13,16,-73,-8,-62,-1,23,17,-4,2,14,3,14,11,10,-76,14,-1,17,-2,0,-7,9,14,-65"
; Data.s "18,11,-3,7,3,18,-122,-4,7,-55"
; Data.s ""
; Data.s "-37,15,15,127,7,-69,-12,17,12,3,17,-67,7,-11,14,6,-124,-67,6,4,-67,-6,-11,11,-77,14,22,7,1,12,3,-3,12,8,0,-67,-2,20,-67,4,-3,7,-1,4,-2,12,3,-67,-5"
; Data.s "-7,-69,7,-124,9,-125,2,5,-8,6,2,-8,8,2,8,19,16,-73,7,16,5,-69,16,-5,-65,4,-8,0,0,5,4,2,-70,3,-60,-4,12,15,-8,9,16,3,14,11,10,-76,0,1,-69,11,9,21"
; Data.s "2,4,-10,13,-8,-69,-49,-54,-49,-43,-61,-76,7,-70,-124,14,15,8,13,-4,-76,-1,-8,-69,-22,9,25,6,3,0,-4,-77,-4,11,8,14,11,-6,125,-69,-12,17,12,3,17,-67"
; Data.s "-5,3,16,-11,7,-122,-70,2,2,-73,-9,3,-4,1,3,12,4,-53,-73,-32,0,-77,11,15,-1,12,6,-4,6,-69,0,4,9,6,8,-2,9,-8,-69,-12,-69,-122,14,-120,-67,-8,8,15,-8"
; Data.s "4,11,14,-65,2,5,-76,15,5,10,6,13,-65,-2,5,7,-69,-10,10,11,14,17,2,-73,116,-69,3,0,6,8,4,-67,-5,-7,16,11,-69,13,9,20,15,-73,0,0,-77,14,2,-3,14,11"
; Data.s "-5,-62,-69,-42,3,-2,11,20,2,-73,-2,10,8,13,-67,-53,-44,-51,-73,1,4,-1,7,6,9,13,16,-73,-8,-62,-8,19,17,-1,13,16,0,3,9,6,-69,16,-1,17,-2,0,-7,9,7,-69"
; Data.s "18,14,8,9,0,7,-124,-8,14,-53"
; Data.s ""
EndDataSection