source: trunk/CrypPlugins/CostFunction/CostFunction.cs @ 991

Last change on this file since 991 was 991, checked in by kohnen, 12 years ago

added visibility for Regex-TextBox.
Removed 'cointains' option

File size: 20.4 KB
Line 
1/*                             
2   Copyright 2009 Team CrypTool (Sven Rech,Dennis Nolte,Raoul Falk,Nils Kopal), Uni Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21using Cryptool.PluginBase.Cryptography;
22using Cryptool.PluginBase;
23using Cryptool.PluginBase.Miscellaneous;
24using Cryptool.PluginBase.Analysis;
25using System.ComponentModel;
26using Cryptool.PluginBase.Control;
27using System.IO;
28using System.Text.RegularExpressions;
29namespace Cryptool.Plugins.CostFunction
30{
31    [Author("Nils Kopal", "Nils.Kopal@cryptool.org", "Uni Duisburg-Essen", "http://www.uni-due.de")]
32    [PluginInfo(false, "CostFunction", "CostFunction", null, "CostFunction/icon.png")]
33    public class CostFunction : IAnalysisMisc
34    {
35        #region private variables
36        private CostFunctionSettings settings = new CostFunctionSettings();
37        private byte[] inputText = null;
38        private byte[] outputText = null;
39        private double value = 0;
40        private Boolean stopped = true;
41        private IControlCost controlSlave;
42        private String bigramInput;
43        private double[,] bigramMatrix;
44        private IDictionary<string, double[]> corpusGrams;
45
46        private DataManager dataMgr = new DataManager();
47        private const string DATATYPE = "transposition";
48
49        private IDictionary<String, DataFileMetaInfo> txtList;
50        private IDictionary<int, IDictionary<string, double[]>> statistics;
51
52        #endregion
53        #region internal constants
54        internal const int ABSOLUTE = 0;
55        internal const int PERCENTAGED = 1;
56        internal const int LOG2 = 2;
57        internal const int SINKOV = 3;
58        #endregion
59        #region CostFunctionInOut
60
61        [PropertyInfo(Direction.InputData, "Text Input", "Input your Text here", "", DisplayLevel.Beginner)]
62        public byte[] InputText
63        {
64            get
65            {
66                return inputText;
67            }
68            set
69            {
70                this.inputText = value;
71                OnPropertyChanged("InputText");
72            }
73        }
74
75        [PropertyInfo(Direction.OutputData, "Text Output", "Your Text will be send here", "", DisplayLevel.Beginner)]
76        public byte[] OutputText
77        {
78            get
79            {
80                return outputText;
81            }
82            set
83            {
84                this.outputText = value;
85                OnPropertyChanged("OutputText");
86            }
87        }
88
89        [PropertyInfo(Direction.OutputData, "Value", "The value of the function will be send here", "", DisplayLevel.Beginner)]
90        public double Value
91        {
92            get
93            {
94                return value;
95            }
96            set
97            {
98                this.value = value;
99                OnPropertyChanged("Value");
100            }
101        }
102
103        [PropertyInfo(Direction.ControlSlave, "SDES Slave", "Direct access to SDES.", "", DisplayLevel.Beginner)]
104        public IControlCost ControlSlave
105        {
106            get
107            {
108                if (controlSlave == null)
109                    controlSlave = new CostFunctionControl(this);
110                return controlSlave;
111            }
112        }
113
114        #endregion
115
116        #region IPlugin Members
117
118        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
119        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
120
121
122        public ISettings Settings
123        {
124            get { return this.settings; }
125            set { this.settings = (CostFunctionSettings)value; }
126        }
127
128        public System.Windows.Controls.UserControl Presentation
129        {
130            get { return null; }
131        }
132
133        public System.Windows.Controls.UserControl QuickWatchPresentation
134        {
135            get { return null; }
136        }
137
138        public void PreExecution()
139        {
140            this.stopped = false;
141        }
142
143        public void Execute()
144        {
145            if (this.InputText is Object && this.stopped == false)
146            {
147                int bytesToUse = 0;
148                try
149                {
150                    bytesToUse = int.Parse(settings.BytesToUse);
151                }
152                catch (Exception ex)
153                {
154                    GuiLogMessage("Entered bytesToUse is not an integer: " + ex.Message, NotificationLevel.Error);
155                    return;
156                }
157
158                if (bytesToUse > this.InputText.Length)
159                {
160                    bytesToUse = 0;
161                }
162
163                byte[] array;
164
165                if (bytesToUse > 0)
166                {
167                    //Create a new Array of size of bytesToUse if needed
168                    array = new byte[bytesToUse];
169                    for (int i = 0; i < bytesToUse && i < this.InputText.Length; i++)
170                    {
171                        array[i] = InputText[i];
172                    }
173                }
174                else
175                {
176                    array = this.InputText;
177                }
178
179                ProgressChanged(0.5, 1);
180                bigramInput = ByteArrayToString(array);
181                switch (settings.FunctionType)
182                {
183
184                    case 0: // Index of Coincedence
185                        this.Value = calculateIndexOfCoincidence(array);
186                        break;
187
188                    case 1: // Entropy
189                        this.Value = calculateEntropy(array);
190                        break;
191
192                    case 2: // Log 2 Bigrams
193                        this.Value = calculateNGrams(bigramInput, 2, 2);
194                        break;
195
196                    case 3: // sinkov Bigrams
197                        this.Value = calculateNGrams(bigramInput, 2, 3);
198                        break;
199                    case 4: //percentaged Bigrams
200                        this.Value = calculateNGrams(bigramInput, 2, 1);
201                        break;
202                    case 5: //regular expressions
203                        this.Value = regex(bigramInput);
204                        break;
205                    default:
206                        this.Value = -1;
207                        break;
208                }//end switch               
209
210                this.OutputText = this.InputText;
211                ProgressChanged(1, 1);
212
213            }//end if
214
215        }//end Execute
216
217
218        public void PostExecution()
219        {
220            this.stopped = true;
221        }
222
223        public void Pause()
224        {
225
226        }
227
228        public void Stop()
229        {
230            this.stopped = false;
231        }
232
233        public void Initialize()
234        {
235
236        }
237
238        public void Dispose()
239        {
240
241        }
242
243        #endregion
244
245        #region INotifyPropertyChanged Members
246
247        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
248
249        public void OnPropertyChanged(string name)
250        {
251            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(name));
252        }
253
254        private void ProgressChanged(double value, double max)
255        {
256            EventsHelper.ProgressChanged(OnPluginProgressChanged, this, new PluginProgressEventArgs(value, max));
257        }
258
259        public void GuiLogMessage(string p, NotificationLevel notificationLevel)
260        {
261            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs(p, this, notificationLevel));
262        }
263
264        #endregion
265
266        #region IPlugin Members
267
268        public event StatusChangedEventHandler OnPluginStatusChanged;
269
270        #endregion
271
272        #region private methods
273
274
275        //public double contains(string input)
276        //{
277        //    if (settings.Contains == null)
278        //    {
279        //        GuiLogMessage("There is no text to be searched for. Please insert text in the 'Contains text / Regular Expression' - Textarea", NotificationLevel.Error);
280        //        return new Double();
281        //    }
282
283        //    if (input.Contains(settings.Contains))
284        //    {
285        //        return 1.0;
286        //    }
287        //    return -1.0;
288        //}
289
290        public double regex(string input)
291        {
292            if (settings.RegEx == null)
293            {
294                GuiLogMessage("There is no Regular Expression to be searched for. Please insert regex in the 'Regular Expression' - Textarea", NotificationLevel.Error);
295                return new Double();
296            }
297            try
298            {
299                Match match = Regex.Match(input, settings.RegEx);
300                if (match.Success)
301                {
302                    return 1.0;
303                }
304                else
305                {
306                    return -1.0;
307                }
308            }
309            catch (Exception e)
310            {
311                GuiLogMessage(e.Message, NotificationLevel.Error);
312                return -1.0;
313            }
314
315        }
316
317
318        /// <summary>
319        /// Calculates the Index of Coincidence multiplied with 100 of
320        /// a given byte array
321        ///
322        /// for example a German text has about 7.62
323        ///           an English text has about 6.61
324        /// </summary>
325        /// <param name="text">text to use</param>
326        /// <returns>Index of Coincidence</returns>
327        public double calculateIndexOfCoincidence(byte[] text)
328        {
329            return calculateIndexOfCoincidence(text, text.Length);
330        }
331
332        /// <summary>
333        /// Calculates the Index of Coincidence multiplied with 100 of
334        /// a given byte array
335        ///
336        /// for example a German text has about 7.62
337        ///           an English text has about 6.61
338        /// </summary>
339        /// <param name="text">text to use</param>
340        /// <param name="text">bytesToUse</param>
341        /// <returns>Index of Coincidence</returns>
342        public double calculateIndexOfCoincidence(byte[] text, int bytesToUse)
343        {
344            if (bytesToUse > text.Length)
345                bytesToUse = text.Length;
346
347            double[] n = new double[256];
348            //count all ASCII symbols
349            int counter = 0;
350            foreach (byte b in text)
351            {
352                n[b]++;
353                counter++;
354                if (counter == bytesToUse)
355                    break;
356            }
357
358            double coindex = 0;
359            //sum them
360            for (int i = 0; i < n.Length; i++)
361            {
362                coindex = coindex + n[i] * (n[i] - 1);
363            }
364
365            coindex = coindex / (bytesToUse);
366            coindex = coindex / (bytesToUse - 1);
367
368            return coindex * 100;
369
370        }//end calculateIndexOfCoincidence
371
372        /// <summary>
373        /// Calculates the Entropy of a given byte array
374        /// for example a German text has about 4.0629
375        /// </summary>
376        /// <param name="text">text to use</param>
377        /// <returns>Entropy</returns>
378        public double calculateEntropy(byte[] text)
379        {
380            return calculateEntropy(text, text.Length);
381        }
382
383        /// <summary>
384        /// Calculates the Entropy of a given byte array
385        /// for example a German text has about 4.0629
386        /// </summary>
387        /// <param name="text">text to use</param>
388        /// <returns>Entropy</returns>
389        public double calculateEntropy(byte[] text, int bytesToUse)
390        {
391            if (bytesToUse > text.Length)
392                bytesToUse = text.Length;
393
394            double[] n = new double[256];
395            //count all ASCII symbols
396            int counter = 0;
397            foreach (byte b in text)
398            {
399                n[b]++;
400                counter++;
401                if (counter == bytesToUse)
402                    break;
403            }
404
405            double entropy = 0;
406            //calculate probabilities and sum entropy
407            for (int i = 0; i < n.Length; i++)
408            {
409                double pz = n[i] / bytesToUse; //probability of character n[i]
410                if (pz > 0)
411                    entropy = entropy + pz * Math.Log(pz, 2);
412            }
413
414            return -1 * entropy; // because of log we have negative values, but we want positive
415
416        }//end calculateEntropy
417
418
419        /// <summary>
420        /// This method calculates a trigram log2 score of a given text on the basis of a given grams dictionary.
421        /// Case is insensitive.
422        /// </summary>
423        /// <param name="input">The text to be scored</param>
424        /// <param name="length">n-gram length</param>
425        /// <returns>The trigram score result</returns>
426        public double calculateNGrams(string input, int length, int valueSelection)
427        {
428            this.statistics = new Dictionary<int, IDictionary<string, double[]>>();
429            double score = 0;
430            if (corpusGrams == null)
431            { corpusGrams = GetStatistics(length); }
432            input = input.ToUpper();
433            // FIXME: case handling?
434
435            HashSet<string> inputGrams = new HashSet<string>();
436
437            foreach (string g in GramTokenizer.tokenize(input, length, false))
438            {
439                // ensure each n-gram is counted only once
440                if (inputGrams.Add(g))
441                {
442                    if (corpusGrams.ContainsKey(g))
443                    {
444                        score += corpusGrams[g][valueSelection];
445
446                    }
447                }
448            }
449
450            return score;
451        }
452        public IDictionary<string, double[]> GetStatistics(int gramLength)
453        {
454            // FIXME: inputTriGrams is not being used!
455
456            // FIXME: implement exception handling
457            if (!statistics.ContainsKey(gramLength))
458            {
459                //GuiLogMessage("Trying to load default statistics for " + gramLength + "-grams", NotificationLevel.Info);
460                statistics[gramLength] = LoadDefaultStatistics(gramLength);
461            }
462
463            return statistics[gramLength];
464        }
465
466        private IDictionary<string, double[]> LoadDefaultStatistics(int length)
467        {
468            txtList = dataMgr.LoadDirectory(DATATYPE);
469
470            return calculateAbsolutes(txtList["2gram.txt"].DataFile.FullName);
471        }
472
473        private IDictionary<string, double[]> calculateAbsolutes(String path)
474        {
475
476
477            Dictionary<string, double[]> grams = new Dictionary<string, double[]>();
478
479            StreamReader reader = new StreamReader(path);
480            String text = reader.ReadToEnd();
481
482            text.ToUpper();
483            text = Regex.Replace(text, "[^A-Z]*", "");
484
485
486            for (int i = 0; i < text.Length - 1; i++)
487            {
488                char a = text[i];
489                char b = text[i + 1];
490                String key = a.ToString();
491                key = key + b.ToString();
492
493                if (!grams.ContainsKey(key))
494                {
495                    grams.Add(key, new double[] { 1, 0, 0, 0 });
496                }
497                else
498                {
499                    grams[key][0] = grams[key][0] + 1.0;
500                }
501            }
502
503            double sum = grams.Values.Sum(item => item[ABSOLUTE]);
504            GuiLogMessage("Sum of all n-gram counts is: " + sum, NotificationLevel.Debug);
505
506            // calculate scaled values
507            foreach (double[] g in grams.Values)
508            {
509                g[PERCENTAGED] = g[ABSOLUTE] / sum;
510                g[LOG2] = Math.Log(g[ABSOLUTE], 2);
511                g[SINKOV] = Math.Log(g[PERCENTAGED], Math.E);
512            }
513
514
515
516            return grams;
517        }
518
519        public string ByteArrayToString(byte[] arr)
520        {
521            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
522            return enc.GetString(arr);
523        }
524
525
526        #endregion
527
528
529    }
530
531    #region slave
532
533    public class CostFunctionControl : IControlCost
534    {
535        public event IControlStatusChangedEventHandler OnStatusChanged;
536        #region IControlCost Members
537
538        private CostFunction plugin;
539
540        #endregion
541
542        /// <summary>
543        /// Constructor
544        /// </summary>
545        /// <param name="plugin"></param>
546        public CostFunctionControl(CostFunction plugin)
547        {
548            this.plugin = plugin;
549        }
550
551        public int getBytesToUse()
552        {
553            try
554            {
555                return int.Parse(((CostFunctionSettings)this.plugin.Settings).BytesToUse);
556            }
557            catch (Exception ex)
558            {
559                throw new Exception("Entered bytesToUse is not an integer: " + ex.Message);
560            }
561        }
562
563        /// <summary>
564        /// Returns the relation operator of the cost function which is set by by CostFunctionSettings
565        /// </summary>
566        /// <returns>RelationOperator</returns>
567        public RelationOperator getRelationOperator()
568        {
569            switch (((CostFunctionSettings)this.plugin.Settings).FunctionType)
570            {
571                case 0: //Index of coincidence
572                    return RelationOperator.LargerThen;
573                case 1: //Entropy
574                    return RelationOperator.LessThen;
575                case 2: // Bigrams: log 2
576                    return RelationOperator.LessThen;
577                case 3: // Sinkov
578                    return RelationOperator.LargerThen;
579                case 4: // percentage
580                    return RelationOperator.LargerThen;
581                case 5: // Regular Expression
582                    return RelationOperator.LargerThen;
583                default:
584                    throw new NotImplementedException("The value " + ((CostFunctionSettings)this.plugin.Settings).FunctionType + " is not implemented.");
585            }//end switch
586        }//end getRelationOperator
587
588        /// <summary>
589        /// Calculates the cost function of the given text
590        ///
591        /// Cost function can be set by CostFunctionSettings
592        /// This algorithm uses a bytesToUse which can be set by CostFunctionSettings
593        /// If bytesToUse is set to 0 it uses the whole text
594        ///
595        /// </summary>
596        /// <param name="text"></param>
597        /// <returns>cost</returns>
598        public double calculateCost(byte[] text)
599        {
600            int bytesToUse = 0;
601            try
602            {
603                bytesToUse = int.Parse(((CostFunctionSettings)this.plugin.Settings).BytesToUse);
604            }
605            catch (Exception ex)
606            {
607                throw new Exception("Entered bytesToUse is not an integer: " + ex.Message);
608            }
609
610            switch (((CostFunctionSettings)this.plugin.Settings).FunctionType)
611            {
612                case 0: //Index of coincidence
613                    return plugin.calculateIndexOfCoincidence(text, bytesToUse);
614                case 1: //Entropy
615                    return plugin.calculateEntropy(text, bytesToUse);
616                case 2: // Bigrams: log 2
617                    return plugin.calculateNGrams(plugin.ByteArrayToString(text), 2, 2);
618                case 3: // Bigrams: Sinkov
619                    return plugin.calculateNGrams(plugin.ByteArrayToString(text), 2, 3);
620                case 4: // Bigrams: Percentaged
621                    return plugin.calculateNGrams(plugin.ByteArrayToString(text), 2, 1);
622                case 5: // regular expression
623                    return plugin.regex(plugin.ByteArrayToString(text));
624                default:
625                    throw new NotImplementedException("The value " + ((CostFunctionSettings)this.plugin.Settings).FunctionType + " is not implemented.");
626            }//end switch
627        }
628
629
630    #endregion
631    }
632
633}
634
Note: See TracBrowser for help on using the repository browser.