Changeset 423


Ignore:
Timestamp:
Aug 9, 2009, 10:38:31 PM (12 years ago)
Author:
Matthäus Wander
Message:
  • changed behaviour of analyzer: does not search for plugs after clicking stop
  • added additional scoring methods for analyzer
  • added n-gram statistics as txt (loaded automatically if scoring method needs it)
  • greatly improved performance: + uses helper method instead of string.IndexOf + removes unnecessary characters once and does not loop them through encrypt core + uses char[] array instead of StringBuilder at encrypt core
Location:
trunk/CrypPlugins/Enigma
Files:
3 added
2 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/CrypPlugins/Enigma/Enigma.cs

    r419 r423  
    234234    public class Enigma: IEncryption
    235235    {
    236         #region Private variables and constants
     236        #region Constants
    237237
    238238        internal const int ABSOLUTE = 0;
    239239        internal const int PERCENTAGED = 1;
    240240        internal const int LOG2 = 2;
     241        internal const int SINKOV = 3;
     242
     243        #endregion
     244
     245        #region Private variables
    241246
    242247        private EnigmaSettings settings;
     
    244249        private EnigmaAnalyzer analyzer;
    245250        private string inputString;
    246         private IDictionary<string, double[]> inputGrams;
     251        private IDictionary<int, IDictionary<string, double[]>> statistics;
     252        // FIXME: enable optional statistics input
     253        //private IDictionary<string, double[]> inputTriGrams;
    247254        private string outputString;
    248255        private string savedKey;
     
    270277        }
    271278
     279        internal class UnknownToken
     280        {
     281            internal string text;
     282            internal int position;
     283
     284            internal UnknownToken(char c, int position)
     285            {
     286                this.text = char.ToString(c);
     287                this.position = position;
     288            }
     289
     290            public override string ToString()
     291            {
     292                return "[" + text + "," + position + "]";
     293            }
     294        }
     295
     296        IList<UnknownToken> unknownList = new List<UnknownToken>();
     297
    272298        /// <summary>
    273299        /// Format the string to contain only alphabet characters in upper case
     
    278304        {
    279305            StringBuilder result = new StringBuilder();
    280 
    281             foreach (char c in text)
    282             {
    283                 if (!settings.Alphabet.Contains(char.ToUpper(c)))
     306            bool newToken = true;
     307            unknownList.Clear();
     308
     309            for (int i = 0; i < text.Length; i++)
     310            {
     311                if (settings.Alphabet.Contains(char.ToUpper(text[i])))
    284312                {
    285                     switch (settings.UnknownSymbolHandling)
     313                    newToken = true;
     314                    result.Append(char.ToUpper(text[i])); // FIXME: shall save positions of lowercase letters
     315                }
     316                else if (settings.UnknownSymbolHandling != 1) // 1 := remove
     317                {
     318                    // 0 := preserve, 2 := replace by X
     319                    char symbol = settings.UnknownSymbolHandling == 0 ? text[i] : 'X';
     320
     321                    if (newToken)
    286322                    {
    287                         case 0: // ignore
    288                             result.Append(c);
    289                             break;
    290                         case 1: // remove
    291                             continue;
    292                         case 2: // replace by X
    293                             result.Append('X');
    294                             break;
     323                        unknownList.Add(new UnknownToken(symbol, i));
     324                        newToken = false;
     325                    }
     326                    else
     327                    {
     328                        unknownList.Last().text += symbol;
    295329                    }
    296330                }
    297                 else
    298                     result.Append(c);
    299             }
    300 
    301             return result.ToString();
    302 
    303         }
     331            }
     332
     333            return result.ToString().ToUpper();
     334
     335        }
     336
     337        //// legacy code
     338        //switch (settings.UnknownSymbolHandling)
     339        //{
     340        //    case 0: // ignore
     341        //        result.Append(c);
     342        //        break;
     343        //    case 1: // remove
     344        //        continue;
     345        //    case 2: // replace by X
     346        //        result.Append('X');
     347        //        break;
     348        //}
    304349
    305350        /// <summary>
     
    311356        private string postFormatOutput(string text)
    312357        {
     358            StringBuilder workstring = new StringBuilder(text);
     359            foreach (UnknownToken token in unknownList)
     360            {
     361                workstring.Insert(token.position, token.text);
     362            }
     363
    313364            switch (settings.CaseHandling)
    314365            {
     366                default:
    315367                case 0: // preserve
    316                     break;
     368                    // FIXME: shall restore lowercase letters
     369                    return workstring.ToString();
    317370                case 1: // upper
    318                     return text.ToUpper();
     371                    return workstring.ToString().ToUpper();
    319372                case 2: // lower
    320                     return text.ToLower();
    321             }
    322             return text;
     373                    return workstring.ToString().ToLower();
     374            }
    323375        }
    324376
     
    328380
    329381        /// <summary>
    330         /// This venthandler is called, when the analyzer has an intermediate result
     382        /// This eventhandler is called, when the analyzer has an intermediate result
    331383        /// </summary>
    332384        /// <param name="sender"></param>
     
    335387        {
    336388            // Got an intermidate results from the analyzer, hence display it
    337             outputString = e.Result;
     389            outputString = postFormatOutput(e.Result);
    338390            OnPropertyChanged("OutputString");
    339391        }
     
    345397        private IDictionary<string, double[]> LoadDefaultStatistics(int length)
    346398        {
    347             SortedDictionary<string, double[]> grams = new SortedDictionary<string, double[]>();
     399            Dictionary<string, double[]> grams = new Dictionary<string, double[]>();
    348400
    349401            StreamReader reader = new StreamReader(Path.Combine(PluginResource.directoryPath, GetStatisticsFilename(length)));
    350402
    351403            string line;
    352 
    353404            while ((line = reader.ReadLine()) != null)
    354405            {
     
    357408
    358409                string[] tokens = new WordTokenizer(line).ToArray();
     410                if (tokens.Length == 0)
     411                    continue;
    359412                Debug.Assert(tokens.Length == 2, "Expected 2 tokens, found " + tokens.Length + " on one line");
    360413
    361                 grams.Add(tokens[0], new double[] { Double.Parse(tokens[1]), 0, 0 });
     414                grams.Add(tokens[0], new double[] { Double.Parse(tokens[1]), 0, 0, 0 });
    362415            }
    363416
     
    370423                g[PERCENTAGED] = g[ABSOLUTE] / sum;
    371424                g[LOG2] = Math.Log(g[ABSOLUTE], 2);
     425                g[SINKOV] = Math.Log(g[PERCENTAGED], Math.E);
    372426            }
    373427
     
    383437        private string GetStatisticsFilename(int length)
    384438        {
    385             switch(length)
    386             {
    387                 case 2:
    388                     return "Enigma_BigramFrequency1941.txt";
    389                 case 3:
    390                     return "Enigma_TrigramFrequency1941.txt";
    391                 default:
    392                     throw new NotSupportedException("There is no known default statistic for an n-gram length of " + length);
    393             }
     439            if (length < 1)
     440            {
     441                throw new ArgumentOutOfRangeException("There is no known default statistic for an n-gram length of " + length);
     442            }
     443
     444            return "Enigma_" + length + "gram_Frequency.txt";
    394445        }
    395446
     
    406457            this.analyzer = new EnigmaAnalyzer(this);
    407458            this.analyzer.OnIntermediateResult += new EventHandler<IntermediateResultEventArgs>(analyzer_OnIntermediateResult);
     459            this.statistics = new Dictionary<int, IDictionary<string, double[]>>();
    408460        }
    409461
     
    455507        }
    456508
    457         [PropertyInfo(Direction.InputData, "n-gram dictionary", "Dictionary with gram counts (string -> [absolute, percentaged, log2])", "", false, false, DisplayLevel.Experienced, QuickWatchFormat.Text, "FrequencyTest.QuickWatchDictionary")]
    458         public IDictionary<string, double[]> InputGrams
    459         {
    460             get { return this.inputGrams; }
    461             set
    462             {
    463                 if (value != inputGrams)
    464                 {
    465                     this.inputGrams = value;
    466                     OnPropertyChanged("InputGrams");
    467                 }
    468             }
    469         }
     509        //[PropertyInfo(Direction.InputData, "n-gram dictionary", "Dictionary with gram counts (string -> [absolute, percentaged, log2])", "", false, false, DisplayLevel.Experienced, QuickWatchFormat.Text, "FrequencyTest.QuickWatchDictionary")]
     510        //public IDictionary<string, double[]> InputGrams
     511        //{
     512        //    get { return this.inputTriGrams; }
     513        //    set
     514        //    {
     515        //        if (value != inputTriGrams)
     516        //        {
     517        //            this.inputTriGrams = value;
     518        //            OnPropertyChanged("InputTriGrams");
     519        //        }
     520        //    }
     521        //}
    470522
    471523        [PropertyInfo(Direction.OutputData, "Text output", "The string after processing with the Enigma machine", "", false, false, DisplayLevel.Beginner, QuickWatchFormat.Text, null)]
     
    532584
    533585
    534                     LogMessage("Enigma encryption done. The resulting index of coincidences is " + analyzer.IndexOfCoincidences(outputString), NotificationLevel.Info);
     586                    // FIXME: output all scorings
     587                    LogMessage("Enigma encryption done. The resulting index of coincidences is " + analyzer.calculateScore(outputString, 1), NotificationLevel.Info);
    535588
    536589                    // "fire" the output
     
    540593                    LogMessage("Enigma analysis starting ...", NotificationLevel.Info);
    541594
    542                     if (inputGrams == null && settings.PlugSearchMethod >= 1)
    543                     {
    544                         LogMessage("No n-gram statistics given, trying to load defaults", NotificationLevel.Info);
    545                         try
    546                         {
    547                             inputGrams = LoadDefaultStatistics(settings.GetGramLength());
    548                         }
    549                         catch (NotSupportedException e)
    550                         {
    551                             LogMessage(e.Message, NotificationLevel.Error);
    552                             return;
    553                         }
    554                     }
    555 
    556                     if (inputGrams != null && settings.PlugSearchMethod == 0)
    557                     {
    558                         LogMessage("The connected n-gram dictionary won't be used by selected plug search method (IC)", NotificationLevel.Warning);
    559                     }
    560                     if (inputGrams != null && inputGrams.Count > 0)
    561                     {
    562                         if (inputGrams.First().Key.Length != settings.GetGramLength())
    563                         {
    564                             LogMessage("The length of the used n-gram statistics does not match the selected plug search method", NotificationLevel.Warning);
    565                         }
    566                     }
    567 
    568595                    //prepare for analysis
    569596                    LogMessage("ANALYSIS: Preformatting text...", NotificationLevel.Debug);
     
    571598
    572599                    // perform the analysis
    573                     outputString = postFormatOutput(analyzer.Analyze(preformatedText, inputGrams));
     600                    outputString = postFormatOutput(analyzer.Analyze(preformatedText));
    574601                    OnPropertyChanged("OutputString");
    575602
     
    664691        }
    665692
     693        public IDictionary<string, double[]> GetStatistics(int gramLength)
     694        {
     695            // FIXME: inputTriGrams is not being used!
     696
     697            // FIXME: implement exception handling
     698
     699            if (!statistics.ContainsKey(gramLength))
     700            {
     701                LogMessage("Trying to load default statistics for " + gramLength + "-grams", NotificationLevel.Info);
     702                statistics[gramLength] = LoadDefaultStatistics(gramLength);
     703            }
     704
     705            return statistics[gramLength];
     706        }
     707
    666708        #endregion
    667709
  • trunk/CrypPlugins/Enigma/Enigma.csproj

    r408 r423  
    8080  </ItemGroup>
    8181  <ItemGroup>
    82     <None Include="Enigma_BigramFrequency1941.txt">
     82    <None Include="Enigma_2gram_Frequency.txt">
    8383      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    8484    </None>
    85     <None Include="Enigma_TrigramFrequency1941.txt">
     85    <None Include="Enigma_3gram_Frequency.txt">
    8686      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    8787    </None>
     88  </ItemGroup>
     89  <ItemGroup>
     90    <Content Include="Enigma_1gram_Frequency.txt">
     91      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     92    </Content>
    8893  </ItemGroup>
    8994  <ItemGroup>
  • trunk/CrypPlugins/Enigma/EnigmaAnalyzer.cs

    r408 r423  
    5959        private const int maxAnalysisEntries = 10; // should go in settings under "Analysis Options"
    6060
    61         private IDictionary<string, double[]> grams;
    62 
    6361        #endregion
    6462
     
    171169        {
    172170            string result = core.Encrypt(rotor1Pos, rotor2Pos, rotor3Pos, 0, text);
    173             double newIC = calculateIC(result);
     171            double newScore = calculateScore(result, settings.KeySearchMethod);
    174172
    175173            if (analysisCandidates.Count >= maxAnalysisEntries)
    176174            {
    177175                // List is full, check if we need to remove one
    178                 if (newIC > analysisCandidates[0].Score)
     176                if (newScore > analysisCandidates[0].Score)
    179177                {
    180178                    double currentMax = analysisCandidates[analysisCandidates.Count - 1].Score;
    181179
    182180                    analysisConfigSettings csetting = new analysisConfigSettings();
    183                     csetting.Score = newIC;
     181                    csetting.Score = newScore;
    184182                    csetting.Rotor1 = core.Rotor1;
    185183                    csetting.Rotor2 = core.Rotor2;
     
    198196
    199197
    200                     if (newIC > currentMax)
     198                    if (newScore > currentMax)
    201199                    {
    202200                        // new best option
     
    204202                        (rotorEnum)csetting.Rotor3, (rotorEnum)csetting.Rotor2, (rotorEnum)csetting.Rotor1,
    205203                        csetting.Ring3.ToString("00"), csetting.Ring2.ToString("00"), csetting.Ring1.ToString("00"),
    206                         csetting.Key, newIC.ToString());
     204                        csetting.Key, newScore.ToString());
    207205                        pluginFacade.LogMessage(status, NotificationLevel.Info);
    208206
     
    224222
    225223                analysisConfigSettings csetting = new analysisConfigSettings();
    226                 csetting.Score = newIC;
     224                csetting.Score = newScore;
    227225                csetting.Rotor1 = core.Rotor1;
    228226                csetting.Rotor2 = core.Rotor2;
     
    291289                    string result = core.Encrypt(rotatedR1Pos, r2pos, r3pos, 0, text);
    292290
    293                     double newIC = calculateIC(result);
    294 
    295                     if (newIC > enigmaConfig.Score)
     291                    double newScore = calculateScore(result, settings.KeySearchMethod);
     292
     293                    if (newScore > enigmaConfig.Score)
    296294                    {
    297295                        //better value, hence update the data
    298                         enigmaConfig.Score = newIC;
     296                        enigmaConfig.Score = newScore;
    299297                        enigmaConfig.Ring1 = i;
    300298                        enigmaConfig.Key = settings.Alphabet[r3pos].ToString() + settings.Alphabet[r2pos].ToString() + settings.Alphabet[rotatedR1Pos].ToString();
     
    320318                    string result = core.Encrypt(r1pos, rotatedR2Pos, r3pos, 0, text);
    321319
    322                     double newIC = calculateIC(result);
    323 
    324                     if (newIC > enigmaConfig.Score)
     320                    double newScore = calculateScore(result, settings.KeySearchMethod);
     321
     322                    if (newScore > enigmaConfig.Score)
    325323                    {
    326324                        //better value, hence update the data
    327                         enigmaConfig.Score = newIC;
     325                        enigmaConfig.Score = newScore;
    328326                        enigmaConfig.Ring2 = i;
    329327                        enigmaConfig.Key = settings.Alphabet[r3pos].ToString() + settings.Alphabet[rotatedR2Pos].ToString() + settings.Alphabet[r1pos].ToString();
     
    342340                            core.setInternalConfig(enigmaConfig.Rotor1, enigmaConfig.Rotor2, enigmaConfig.Rotor3, 0, settings.Reflector, k, j, i, 1, enigmaConfig.PlugBoard);
    343341                            string result = core.Encrypt(r1pos, r2pos, r3pos, 0, text);
    344                             double newIC = calculateIC(result);
    345 
    346                             if (newIC > enigmaConfig.Score)
     342                            double newScore = calculateScore(result, settings.KeySearchMethod);
     343
     344                            if (newScore > enigmaConfig.Score)
    347345                            {
    348346                                //better value, hence update the data
    349                                 enigmaConfig.Score = newIC;
     347                                enigmaConfig.Score = newScore;
    350348                                enigmaConfig.Ring1 = k;
    351349                                enigmaConfig.Ring2 = j;
     
    381379            int trials = 0;
    382380            string bestResult = encrypt(enigmaConfig, text, enigmaConfig.PlugBoard);
    383             enigmaConfig.Score = calculatePlugScore(bestResult);
    384 
    385             if (grams == null)
    386                 pluginFacade.LogMessage("Using legacy IC method to analyze plugs", NotificationLevel.Debug);
    387             else
    388                 pluginFacade.LogMessage("Using new trigram method to analyze plugs", NotificationLevel.Debug);
     381            enigmaConfig.Score = calculateScore(bestResult, settings.PlugSearchMethod);
    389382
    390383            for (int n = 0; n < maxPlugs; n++)
     
    427420
    428421                        string result = encrypt(enigmaConfig, text, plugboard.ToString());
    429                         double newScore = calculatePlugScore(result);
     422                        double newScore = calculateScore(result, settings.PlugSearchMethod);
    430423                        trials++;
    431424
     
    437430                            plugFound = true;
    438431                        }
     432
     433                        if (stop)
     434                            break;
    439435                    }
     436                    if (stop)
     437                        break;
    440438                }
    441439
     
    449447
    450448                // no plug could lead to a better result, hence abort plug search.
    451                 if (!plugFound)
     449                if (!plugFound || stop)
    452450                    break;
    453451            }
     
    508506            // start with the unmodified
    509507            core.setInternalConfig(enigmaConfig.Rotor1, enigmaConfig.Rotor2, enigmaConfig.Rotor3, 0, settings.Reflector, enigmaConfig.Ring1, enigmaConfig.Ring2, enigmaConfig.Ring3, 1, unmodifiedPlugboard);
    510             double unmodifiedScore = calculatePlugScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text));
     508            double unmodifiedScore = calculateScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text), settings.PlugSearchMethod);
    511509
    512510            // now o2p
    513511            core.setInternalConfig(enigmaConfig.Rotor1, enigmaConfig.Rotor2, enigmaConfig.Rotor3, 0, settings.Reflector, enigmaConfig.Ring1, enigmaConfig.Ring2, enigmaConfig.Ring3, 1, o2pPlugPlugboard.ToString());
    514             double o2pScore = calculatePlugScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text));
     512            double o2pScore = calculateScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text), settings.PlugSearchMethod);
    515513
    516514            // now c2o
    517515            core.setInternalConfig(enigmaConfig.Rotor1, enigmaConfig.Rotor2, enigmaConfig.Rotor3, 0, settings.Reflector, enigmaConfig.Ring1, enigmaConfig.Ring2, enigmaConfig.Ring3, 1, c2oPlugboard.ToString());
    518             double c2oScore = calculatePlugScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text));
     516            double c2oScore = calculateScore(core.Encrypt(r1pos, r2pos, r3pos, 0, text), settings.PlugSearchMethod);
    519517
    520518            string bestPlugBoard = enigmaConfig.PlugBoard;
     
    569567        #region Helper methods
    570568
    571         private double calculatePlugScore(string input)
    572         {
    573             return grams == null ? calculateIC(input) : calculateTrigrams(input);
    574         }
    575 
    576         /// <summary>
    577         /// The method calculates and returns the index of coincidences of a given text
    578         /// </summary>
    579         /// <param name="input">The index if coincidences of this text will be calculated</param>
    580         /// <returns>The index of coincidences</returns>
    581         private double calculateIC(string input)
    582         {
    583             int[] statistics = new int[settings.Alphabet.Length];
    584             long cipherTextLength = 0; //input.Length; //n
    585             long countDoubleCharacters = 0;
    586 
    587             // first count the frequency of (single) letters
    588             foreach (char c in input)
    589             {
    590                 int i = settings.Alphabet.IndexOf(char.ToUpper(c));
    591                 if (i >= 0) statistics[i]++;
    592             }
    593 
    594 
    595             // now calculate the index of coincidences
    596             for (int i = 0; i < statistics.Length; i++)
    597             {
    598                 cipherTextLength += statistics[i];
    599                 countDoubleCharacters += (statistics[i] * (statistics[i] - 1));
    600             }
    601 
    602             return ((double)countDoubleCharacters / (double)(cipherTextLength * (cipherTextLength - 1)));
    603         }
    604 
    605569        /// <summary>
    606570        /// This method calculates a trigram log2 score of a given text on the basis of a given grams dictionary.
     
    608572        /// </summary>
    609573        /// <param name="input">The text to be scored</param>
    610         /// <param name="grams">The scoring values to be used</param>
     574        /// <param name="length">n-gram length</param>
    611575        /// <returns>The trigram score result</returns>
    612         private double calculateTrigrams(string input)
     576        private double calculateNGrams(string input, int length, int valueSelection)
    613577        {
    614578            double score = 0;
    615             string workstring = StringUtil.StripUnknownSymbols(settings.Alphabet, input);
    616 
    617             // FIXME: this causes an exception when analysis is started before pending plugin search has finished
    618             foreach (KeyValuePair<string, double[]> item in grams)
    619             {
    620                 if (workstring.Contains(settings.CaseHandling == 0 ? item.Key : item.Key.ToUpper()))
    621                 {
    622                     score += item.Value[Enigma.LOG2];
    623                 }
     579            IDictionary<string, double[]> corpusGrams = pluginFacade.GetStatistics(length);
     580
     581            // FIXME: case handling?
     582
     583            HashSet<string> inputGrams = new HashSet<string>();
     584
     585            foreach (string g in new GramTokenizer(input, length, false))
     586            {
     587                // ensure each n-gram is counted only once
     588                if (inputGrams.Add(g))
     589                {
     590                    if (corpusGrams.ContainsKey(g))
     591                    {
     592                        score += corpusGrams[g][valueSelection];
     593                    }
     594                }
     595            }
     596
     597            return score;
     598        }
     599
     600        private IDictionary<string, double[]> calculateConditional(string input, int length)
     601        {
     602            IDictionary<string, double[]> inputGrams = new Dictionary<string, double[]>();
     603
     604            foreach (string g in new GramTokenizer(input, length, false))
     605            {
     606                if (inputGrams.ContainsKey(g))
     607                {
     608                    inputGrams[g][Enigma.ABSOLUTE]++;
     609                }
     610                else
     611                {
     612                    inputGrams[g] = new double[] { 1, 0 };
     613                }
     614            }
     615
     616            double sum = inputGrams.Values.Sum(item => item[Enigma.ABSOLUTE]);
     617            foreach (double[] values in inputGrams.Values)
     618            {
     619                values[Enigma.PERCENTAGED] = values[Enigma.ABSOLUTE] / sum;
     620            }
     621
     622            return inputGrams;
     623        }
     624
     625        private double calculateSinkov(string input, int length)
     626        {
     627            double score = 0;
     628            IDictionary<string, double[]> corpusGrams = pluginFacade.GetStatistics(length);
     629            IDictionary<string, double[]> inputGrams = calculateConditional(input, length);
     630
     631            foreach (KeyValuePair<string, double[]> item in inputGrams)
     632            {
     633                if (corpusGrams.ContainsKey(item.Key))
     634                {
     635                    score += item.Value[Enigma.PERCENTAGED] * corpusGrams[item.Key][Enigma.SINKOV];
     636                }
     637            }
     638
     639            return score;
     640        }
     641
     642        private double calculateEntropy(string input, int length)
     643        {
     644            double score = 0;
     645            IDictionary<string, double[]> inputGrams = calculateConditional(input, length);
     646
     647            foreach (double[] values in inputGrams.Values)
     648            {
     649                score += values[Enigma.PERCENTAGED] * Math.Log(values[Enigma.PERCENTAGED], 2);
    624650            }
    625651
     
    707733        /// <param name="preformatedText">The preformated text for analysis</param>
    708734        /// <returns>The cleartext as decoded with the analyzed key</returns>
    709         public string Analyze(string preformatedText, IDictionary<string, double[]> grams)
     735        public string Analyze(string preformatedText)
    710736        {
    711737            pluginFacade.LogMessage("=========> ANALYSIS OF ENIGMA MESSAGE STARTED <=========", NotificationLevel.Info);
    712738
    713739            // some initialisation
    714             this.grams = grams;
    715740            analysisCandidates.Clear();
    716741            stop = false;
    717742
     743            AnalyzeRun(preformatedText);
     744
     745            pluginFacade.LogMessage("=========> ANALYSIS OF ENIGMA MESSAGE DONE <=========", NotificationLevel.Info);
     746
     747            // switch back to verbose core
     748            core.VerboseLevel = VerboseLevels.VeryVerbose;
     749
     750            // decrypt with best option
     751            analysisConfigSettings bestConfig = analysisCandidates[analysisCandidates.Count - 1];
     752
     753            foreach (analysisConfigSettings config in analysisCandidates)
     754            {
     755                pluginFacade.LogMessage(encrypt(config, preformatedText, config.PlugBoard), NotificationLevel.Debug);
     756            }
     757
     758            return encrypt(bestConfig, preformatedText, bestConfig.PlugBoard);
     759        }
     760
     761        private void AnalyzeRun(string preformatedText)
     762        {
    718763            if (settings.AnalyzeRotors)
    719764            {
     
    744789            printBestCandidates();
    745790
     791            if (stop)
     792                return;
     793
    746794            // put the core in quiet mode, since now many internal changes occur
    747795            core.VerboseLevel = VerboseLevels.Quiet;
     
    751799                pluginFacade.LogMessage("ANALYSIS: ====> Stage 2 - Searching ring positions <====", NotificationLevel.Info);
    752800
    753                 for (int j = analysisCandidates.Count - 1; j >= 0; j--)
    754                 {
    755                     analysisCandidates[j].PlugBoard = settings.Alphabet; // empty plugs
    756                     analyzeRings(analysisCandidates[j], preformatedText);
     801                if (!stop)
     802                {
     803                    for (int j = analysisCandidates.Count - 1; j >= 0; j--)
     804                    {
     805                        analysisCandidates[j].PlugBoard = settings.Alphabet; // empty plugs
     806                        analyzeRings(analysisCandidates[j], preformatedText);
     807                    }
    757808                }
    758809
     
    774825            printBestCandidates();
    775826
     827            if (stop)
     828                return;
    776829
    777830            if (settings.AnalyzePlugs)
     
    803856
    804857            printBestCandidates();
    805 
    806 
    807             pluginFacade.LogMessage("=========> ANALYSIS OF ENIGMA MESSAGE DONE <=========", NotificationLevel.Info);
    808 
    809             // switch back to verbose core
    810             core.VerboseLevel = VerboseLevels.VeryVerbose;
    811 
    812             // decrypt with best option
    813             analysisConfigSettings bestConfig = analysisCandidates[analysisCandidates.Count - 1];
    814 
    815             foreach (analysisConfigSettings config in analysisCandidates)
    816             {
    817                 pluginFacade.LogMessage(encrypt(config, preformatedText, config.PlugBoard), NotificationLevel.Debug);
    818             }
    819 
    820             return encrypt(bestConfig, preformatedText, bestConfig.PlugBoard);
    821858        }
    822859
     
    830867
    831868        /// <summary>
    832         /// Returns the Index of coincidences of a given text
    833         /// </summary>
    834         /// <param name="text">The text</param>
     869        /// The method calculates and returns the index of coincidences of a given text
     870        /// </summary>
     871        /// <param name="input">The index if coincidences of this text will be calculated</param>
    835872        /// <returns>The index of coincidences</returns>
    836         public double IndexOfCoincidences(string text)
    837         {
    838             return calculateIC(text);
     873        public double calculateIC(string input)
     874        {
     875            int[] statistics = new int[settings.Alphabet.Length];
     876            long cipherTextLength = 0; //input.Length; //n
     877            long countDoubleCharacters = 0;
     878
     879            // first count the frequency of (single) letters
     880            foreach (char c in input)
     881            {
     882                int i = settings.Alphabet.IndexOf(char.ToUpper(c));
     883                if (i >= 0) statistics[i]++;
     884            }
     885
     886
     887            // now calculate the index of coincidences
     888            for (int i = 0; i < statistics.Length; i++)
     889            {
     890                cipherTextLength += statistics[i];
     891                countDoubleCharacters += (statistics[i] * (statistics[i] - 1));
     892            }
     893
     894            return ((double)countDoubleCharacters / (double)(cipherTextLength * (cipherTextLength - 1)));
     895        }
     896
     897        public double calculateScore(string input, int searchMethod)
     898        {
     899            switch (searchMethod)
     900            {
     901                case 0:
     902                    return calculateIC(input);
     903                case 1:
     904                case 2:
     905                    return calculateNGrams(input, settings.GetGramLength(searchMethod), Enigma.LOG2);
     906                case 3:
     907                case 4:
     908                    return calculateSinkov(input, settings.GetGramLength(searchMethod));
     909                case 5:
     910                    return calculateEntropy(input, settings.GetGramLength(searchMethod));
     911                default:
     912                    throw new NotSupportedException("Search method not supported");
     913            }
    839914        }
    840915
  • trunk/CrypPlugins/Enigma/EnigmaCore.cs

    r353 r423  
    298298        /// <param name="rotor3Pos">Position of rotor 3 (slowest)</param>
    299299        /// <param name="rotor4Pos">Position of rotor 4 (extra rotor for M4)</param>
    300         /// <param name="text">The text for en/decryption. All letters which are not in the alphabet are returned unprocessed.</param>
     300        /// <param name="input">The text for en/decryption. All letters which are not in the alphabet are returned unprocessed.</param>
    301301        /// <returns>The encrypted/decrypted string</returns>
    302         public string Encrypt(int rotor1Pos, int rotor2Pos, int rotor3Pos, int rotor4Pos, string text)
     302        public string Encrypt(int rotor1Pos, int rotor2Pos, int rotor3Pos, int rotor4Pos, string input)
    303303        {
    304304
    305305            if (!Stopwatch.IsHighResolution)
    306                 logMessage("No high resolution timer available. Time measurments will be inaccurate!", NotificationLevel.Warning);
     306                logMessage("No high resolution timer available. Time measurements will be inaccurate!", NotificationLevel.Warning);
    307307
    308308
     
    317317
    318318            // now perform the enigma operation for each character
    319             StringBuilder result = new StringBuilder();
    320             int i = 0;
    321 
    322             foreach (char c in text)
    323             {
    324                 if (settings.Alphabet.Contains(char.ToUpper(c)))
    325                 {
    326                     if (char.IsLower(c))
    327                         result.Append(char.ToLower(enigmacrypt(char.ToUpper(c))));
    328                     else
    329                         result.Append(enigmacrypt(c));
    330                 }
    331                 else
    332                     result.Append(c);
     319            char[] result = new char[input.Length];
     320
     321            for (int i = 0; i < input.Length; i++)
     322            {
     323                result[i] = enigmacrypt(input[i]);
    333324
    334325                //update the status, if we are not in anylzing mode
    335326                // this must be deactivated during analysis, since it takes a lot of time
    336327                if (settings.Action == 0)
    337                     pluginFacade.ShowProgress(++i, text.Length);
     328                    pluginFacade.ShowProgress(i, input.Length);
    338329            }
    339330
     
    343334            // Print some info on the console, if not in analyzing mode.
    344335            if (settings.Action == 0)
    345                 logMessage(String.Format("Enigma processing done! Processed {0} characters in {1} ms!", text.Length, sw.ElapsedMilliseconds), NotificationLevel.Info);
    346 
    347             return result.ToString();
     336                logMessage(String.Format("Enigma processing done! Processed {0} characters in {1} ms!", input.Length, sw.ElapsedMilliseconds), NotificationLevel.Info);
     337
     338            return new string(result);
    348339        }
    349340
     
    476467        private char enigmaPlugBoard(char c)
    477468        {
    478             return iCfg.PlugBoard[settings.Alphabet.IndexOf(c)];
     469            return iCfg.PlugBoard[settings.AlphabetIndexOf(c)];
    479470        }
    480471
     
    493484            foreach (char n in rotor1notches)
    494485            {
    495                 if (settings.Alphabet.IndexOf(n) == iCfg.Rotor1pos) iCfg.Rotor2pos = (iCfg.Rotor2pos + 1) % alen;
    496             }           
     486                if (settings.AlphabetIndexOf(n) == iCfg.Rotor1pos) iCfg.Rotor2pos = (iCfg.Rotor2pos + 1) % alen;
     487            }
    497488
    498489            foreach (char n in rotor2notches)
     
    500491                int currentRotor2pos = iCfg.Rotor2pos;
    501492
    502                 if (settings.Alphabet.IndexOf(n) == currentRotor2pos)
     493                if (settings.AlphabetIndexOf(n) == currentRotor2pos)
    503494                {
    504495                    iCfg.Rotor3pos = (iCfg.Rotor3pos + 1) % alen;
     
    509500            foreach (char n in rotor3notches)
    510501            {
    511                 if (settings.Alphabet.IndexOf(n) == iCfg.Rotor3pos) iCfg.Rotor4pos = (iCfg.Rotor4pos + 1) % alen;
     502                if (settings.AlphabetIndexOf(n) == iCfg.Rotor3pos) iCfg.Rotor4pos = (iCfg.Rotor4pos + 1) % alen;
    512503            }
    513504
     
    527518
    528519            // now do the substitution
    529             ch = A3[alen + A3.IndexOf(rotor1For[alen + A3.IndexOf(ch) + rotor1Pos]) - rotor1Pos];
    530             ch = A3[alen + A3.IndexOf(rotor2For[alen + A3.IndexOf(ch) + rotor2Pos]) - rotor2Pos];
    531             ch = A3[alen + A3.IndexOf(rotor3For[alen + A3.IndexOf(ch) + rotor3Pos]) - rotor3Pos];
    532             ch = reflector[alen + A3.IndexOf(ch)];
    533             ch = A3[alen + A3.IndexOf(rotor3Rev[alen + A3.IndexOf(ch) + rotor3Pos]) - rotor3Pos];
    534             ch = A3[alen + A3.IndexOf(rotor2Rev[alen + A3.IndexOf(ch) + rotor2Pos]) - rotor2Pos];
    535             ch = A3[alen + A3.IndexOf(rotor1Rev[alen + A3.IndexOf(ch) + rotor1Pos]) - rotor1Pos];
     520            ch = A3[alen + settings.AlphabetIndexOf(rotor1For[alen + settings.AlphabetIndexOf(ch) + rotor1Pos]) - rotor1Pos];
     521            ch = A3[alen + settings.AlphabetIndexOf(rotor2For[alen + settings.AlphabetIndexOf(ch) + rotor2Pos]) - rotor2Pos];
     522            ch = A3[alen + settings.AlphabetIndexOf(rotor3For[alen + settings.AlphabetIndexOf(ch) + rotor3Pos]) - rotor3Pos];
     523            ch = reflector[alen + settings.AlphabetIndexOf(ch)];
     524            ch = A3[alen + settings.AlphabetIndexOf(rotor3Rev[alen + settings.AlphabetIndexOf(ch) + rotor3Pos]) - rotor3Pos];
     525            ch = A3[alen + settings.AlphabetIndexOf(rotor2Rev[alen + settings.AlphabetIndexOf(ch) + rotor2Pos]) - rotor2Pos];
     526            ch = A3[alen + settings.AlphabetIndexOf(rotor1Rev[alen + settings.AlphabetIndexOf(ch) + rotor1Pos]) - rotor1Pos];
    536527
    537528            return ch;
  • trunk/CrypPlugins/Enigma/EnigmaSettings.cs

    r408 r423  
    251251
    252252        private int maxSearchedPlugs = 10;
     253        private int keySearchMethod = 0;
    253254        private int plugSearchMethod = 2;
    254255
     
    326327
    327328        /// <summary>
    328         /// Return the expected length of n-grams statistics for the selected PlugSearchMethod.
     329        /// Return the expected length of n-grams statistics for the given search method.
    329330        /// </summary>
    330331        /// <returns></returns>
    331         internal int GetGramLength()
    332         {
    333             switch (plugSearchMethod)
     332        public int GetGramLength(int searchMethod)
     333        {
     334            switch (searchMethod)
    334335            {
    335336                case 0:
     
    339340                case 2:
    340341                    return 3;
     342                case 3:
     343                    return 1;
     344                case 4:
     345                    return 2;
     346                case 5:
     347                    return 1;
    341348                default:
    342                     return -1;
     349                    throw new NotSupportedException("Search method with index " + searchMethod + " is unknown");
    343350            }
    344351        }
     
    348355            plugBoard = new StringBuilder("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    349356            OnPropertyChanged("PlugBoardDisplay");
     357        }
     358
     359        public int AlphabetIndexOf(char c)
     360        {
     361            return c - 'A';
    350362        }
    351363
     
    384396            null, 0, false, DisplayLevel.Beginner, ControlType.ComboBox,
    385397            new string[] { "Enigma A/B - since 1924", "Enigma D", "Reichsbahn (Rocket) - since 1941", "Enigma I / M3", "M4 (Shark)", "K-Model", "G (Defense model)" })]
     398        [PropertySaveOrder(1)]
    386399        public int Model
    387400        {
     
    475488
    476489
    477         [TaskPane("Operation mode", "Select the mode of operation for this Enigma simulator. Nate that all Enigmas since Enigma D are working with a reflector and therefore there is not difference between encrypting an decrypting.",
     490        [TaskPane("Operation mode", "Select the mode of operation for this Enigma simulator. Note that all Enigmas since Enigma D are working with a reflector and therefore there is not difference between encrypting an decrypting.",
    478491            null, 2, false, DisplayLevel.Beginner, ControlType.DynamicComboBox, new string[] { "ActionStrings" })]
     492        [PropertySaveOrder(9)]
    479493        public int Action
    480494        {
     
    491505        /// This collection contains the values for the Action combobox.
    492506        /// </summary>
     507        [PropertySaveOrder(9)]
    493508        public ObservableCollection<string> ActionStrings
    494509        {
     
    764779        }
    765780
    766         [TaskPane("Plug search method", "Which method should be used to assess the best plugboard configuration?", "Analysis options", 9, false, DisplayLevel.Experienced, ControlType.ComboBox, new string[] { "Index of coincidence", "log2-bigram", "log2-trigram" })]
     781        public static string[] GetConstArray()
     782        {
     783            return new string[] { "foo", "bar" };
     784        }
     785
     786        [TaskPane("Rotor/Ring/Key search method", "Which method should be used to assess the best rotor configuration?", "Analysis options", 9, false, DisplayLevel.Experienced, ControlType.ComboBox, new string[] { "Index of coincidence", "log2-bigram", "log2-trigram", "Sinkov unigram", "Sinkov bigram", "Unigram entropy" })]
     787        public int KeySearchMethod
     788        {
     789            get { return this.keySearchMethod; }
     790            set
     791            {
     792                if (value != keySearchMethod)
     793                {
     794                    hasChanges = true;
     795                    keySearchMethod = value;
     796                    OnPropertyChanged("KeySearchMethod");
     797                }
     798            }
     799        }
     800
     801        [TaskPane("Plug search method", "Which method should be used to assess the best plugboard configuration?", "Analysis options", 9, false, DisplayLevel.Experienced, ControlType.ComboBox, new string[] { "Index of coincidence", "log2-bigram", "log2-trigram", "Sinkov unigram", "Sinkov bigram", "Unigram entropy" })]
    767802        public int PlugSearchMethod
    768803        {
Note: See TracChangeset for help on using the changeset viewer.