/// <summary>
 /// Deletes all empty transitions from a Petri Net and redirects the connections
 /// </summary>
 /// <param name="petriNet"></param>
 /// <author>Jannik Arndt</author>
 public static PetriNet CleanUpPetriNet(PetriNet petriNet)
 {
     List<Node> nodesToBeDeleted = new List<Node>();
     foreach (Place place in petriNet.Places.Reverse<Place>())
     {
         if (place.IncomingTransitions.Count == 1
             && place.IncomingTransitions[0].OutgoingPlaces.Count == 1
             && place.OutgoingTransitions.Count == 1
             && place.OutgoingTransitions[0].IncomingPlaces.Count == 1)
         {
             if (place.IncomingTransitions[0].Name == "")
             {
                 // Redirect connections around the empty transition
                 place.OutgoingTransitions[0].AddIncomingPlaces(place.IncomingTransitions[0].IncomingPlaces);
                 nodesToBeDeleted.Add(place);
                 nodesToBeDeleted.Add(place.IncomingTransitions[0]);
             }
             if (place.OutgoingTransitions[0].Name == "")
             {
                 // Redirect connections around the empty transition
                 place.IncomingTransitions[0].AddOutgoingPlaces(place.OutgoingTransitions[0].OutgoingPlaces);
                 nodesToBeDeleted.Add(place);
                 nodesToBeDeleted.Add(place.OutgoingTransitions[0]);
             }
         }
     }
     petriNet.RemoveNodes(nodesToBeDeleted);
     return petriNet;
 }
        /// <summary>
        /// Replays the content of the EventLog on the given PetriNet
        /// </summary>
        /// <param name="petriNet"></param>
        /// <param name="eventLog"></param>
        /// <returns></returns>
        /// <author>Jannik Arndt</author>
        public static TokenReplayResult Replay(PetriNet petriNet, EventLog eventLog)
        {
            var result = new TokenReplayResult { NumberOfCases = eventLog.Cases.Count };

            foreach (var Case in eventLog.Cases)
            {
                var exceptionThrown = false;
                petriNet.ResetTokens();
                foreach (var Event in Case.EventList)
                {
                    try
                    {
                        petriNet.FireTransition(Event.Name);
                    }
                    catch (NullReferenceException)
                    {
                        result.FailedCasesTransitionNotFound.Add(Case);
                        exceptionThrown = true;
                        break;
                    }
                    catch (TransitionNotEnabledException)
                    {
                        result.FailedCasesTransitionNotEnabled.Add(Case);
                        exceptionThrown = true;
                        break;
                    }
                }
                if (!exceptionThrown)
                    result.SuccessfulReplays++;
            }
            return result;
        }
        /// <summary>
        ///     Build a List of Columns from a PetriNet
        /// </summary>
        /// <param name="petriNet"></param>
        /// <returns></returns>
        /// <author>Jannik Arndt</author>
        public static List<Column> Build(PetriNet petriNet)
        {
            // Initialize
            GlobalColumnList.Clear();

            Column firstColumn = new Column(0);
            Place startPlace = (Place) petriNet.GetSources()[0];

            firstColumn.HashSetOfNodes.Add(startPlace);
            GlobalColumnList.Add(firstColumn);

            // Recursively add all columns
            AddPlace(startPlace, 1);

            // Correct the end-place
            Place endPlace = (Place) petriNet.GetSinks()[0];
            Column temp = FindNode(endPlace);
            if (temp != null && temp.ColumnNumber < GlobalColumnList.Count)
            {
                temp.HashSetOfNodes.Remove(endPlace);
                TrimGlobalColumnList();
                GetOrCreateColumn(GlobalColumnList.Count).HashSetOfNodes.Add(endPlace);
            }

            return GlobalColumnList;
        }
        /// <summary>
        /// Adds all added Transition to the NetWithDifference
        /// </summary>
        /// <author>Jannik Arndt,Thomas Meents</author>
        public void AddAddedTransitions(PetriNet netWithDifference)
        {
            // Find all Transitions that have the DiffStatus "added"
            List<Transition> addedTransitions = ListOfChoosenProcessModels[1].Transitions.Where(transition => transition.DiffStatus == DiffState.Added).ToList();

            foreach (Transition addedTransition in addedTransitions)
            {
                netWithDifference.Places.AddRange(addedTransition.OutgoingPlaces);
                addedTransition.DiffStatus = DiffState.Added;

                // For non-starting transitions
                if (addedTransition.IncomingPlaces[0].IncomingTransitions != null && addedTransition.IncomingPlaces[0].IncomingTransitions.Count > 0)
                {
                    List<Transition> listOfFollower = new List<Transition>();
                    if (addedTransition.OutgoingPlaces[0].OutgoingTransitions.Count > 0)
                    {
                        foreach (Place t in addedTransition.OutgoingPlaces)
                            foreach (Transition newfollower in t.OutgoingTransitions)
                                if (!listOfFollower.Contains(newfollower))
                                    listOfFollower.Add(newfollower);
                    }
                    else
                    {
                        Transition follower = new Transition();
                        listOfFollower.Add(follower);
                    }

                    if (listOfFollower.Count > 0)
                    {
                        listOfFollower[0].IncomingPlaces.Add(addedTransition.OutgoingPlaces[0]);
                    }
                    else
                    {
                        Place endPlace = new Place("End");
                        addedTransition.OutgoingPlaces.Add(endPlace);
                    }
                }

                // For starting Transitions
                else
                {
                    foreach (Place t in addedTransition.OutgoingPlaces)
                    {
                        if (t.OutgoingTransitions.Count <= 0) continue;
                        addedTransition.IncomingPlaces[0].Name = "Start";
                        netWithDifference.Places.Add(addedTransition.IncomingPlaces[0]);

                        if (addedTransition.IncomingPlaces[0].OutgoingTransitions.Count > 1)
                            addedTransition.IncomingPlaces[0].OutgoingTransitions.RemoveAt(1);

                        if (addedTransition.OutgoingPlaces[0].IncomingTransitions.Count > 1)
                            addedTransition.OutgoingPlaces[0].IncomingTransitions.RemoveAt(1);

                        netWithDifference.Places.RemoveAt(0);
                    }
                }
                netWithDifference.Transitions.Add(addedTransition);
            }
        }
 /// <summary>
 /// Constructor for the HeuristicMiner, initializes values and sets the "field" that will be mined.
 /// </summary>
 /// <param name="field">A field from the DataSelection</param>
 public HeuristicMiner(Field field)
 {
     _field = field;
     _finalPetriNet = new PetriNet(field.Infotext);
     _listOfSubPetriNets = new List<PetriNet>();
     _openParallelismCount = new Stack<Parallelism>();
     _listOfNodes = new HashSet<double>();
 }
 /// <summary>
 /// Constructor for a tree node
 /// </summary>
 /// <param name="inductiveMiner"></param>
 /// <param name="operation">node operation</param>
 /// <param name="associatedEvent"></param>
 /// <param name="right"></param>
 /// <param name="left"></param>
 /// <author>Thomas Meents, Bernd Nottbeck</author>
 public InductiveMinerTreeNode(InductiveMiner inductiveMiner, OperationsEnum operation, Event associatedEvent = null, InductiveMinerTreeNode right = null, InductiveMinerTreeNode left = null)
 {
     Operation = operation;
     LeftLeaf = left;
     RightLeaf = right;
     Event = associatedEvent;
     petriNet = inductiveMiner.IMPetriNet;
 }
 /// <summary>
 /// Initialize the InductiveMiner instance.
 /// </summary>
 /// <param name="field">Corresponding field</param>
 /// <author>Krystian Zielonka, Thomas Meents, Bernd Nottbeck</author>
 public InductiveMiner(Field field)
 {
     _field = field;
     DirectRowList = new List<InductiveMinerRow>();
     EventDictionary = new Dictionary<Event, InductiveMinerGraphNode>();
     IMPetriNet = new PetriNet(_field.Infotext);
     StartEvent = new Event("StartEvent");
     EndEvent = new Event("EndEvent");
     EventuallyRowList = new List<InductiveMinerRow>();
 }
 public AlphaMiner(Field field)
 {
     _field = field;
     _petriNet = new PetriNet(field.Infotext);
     _listOfActivities = new HashSet<String>();
     _listOfStartActivities = new HashSet<String>();
     _listOfEndActivities = new HashSet<String>();
     _listOfAlphaPlaces = new List<AlphaMinerPlaces>();
     _listOfAllPairs = new Dictionary<String, Tuple<HashSet<String>, HashSet<String>>>();
     _listOfLoopsWithTheLengthOfTwo = new Dictionary<String, HashSet<String>>();
     _listOfAllParallelPairs = new Dictionary<String, HashSet<String>>();
     _listOfAllDoubleActivities = new HashSet<String>();
     _listOfActivitiesInCase = new List<List<String>>();
 }
 /// <summary>
 /// Constructor for a tree node´used duringt the cutting process.
 /// </summary>
 /// <param name="petriNet"></param>
 /// <param name="graphNode"></param>
 /// <param name="startEvent"></param>
 public InductiveMinerTreeNode(PetriNet petriNet, InductiveMinerGraphNode graphNode, Event startEvent)
 {
     GraphNode = graphNode;
     Operation = GraphNode.FollowerList.Count == 0 ? OperationsEnum.isLeaf : OperationsEnum.isUnkown;
     Event = GraphNode.Name;
     LeftLeaf = null;
     RightLeaf = null;
     this.startEvent = startEvent;
     visitedNodes = new HashSet<InductiveMinerGraphNode>();
     visitedLoopCheckNodes = new HashSet<InductiveMinerGraphNode>();
     this.petriNet = petriNet;
     newStart = new InductiveMinerGraphNode(new Event("NewStart"));
     DivideAndConquer();
 }
        /// <summary>
        /// (1) -> [One] -> (3)-> [Three] -> (5)-> [Five] -> (6)
        /// Has a corresponding EventLog!
        /// </summary>
        /// <author>Thomas Meents</author>
        public static PetriNet OneThreeFive()
        {
            var petriNet = new PetriNet("OneThreeFive");

            var place1 = petriNet.AddPlace("1");
            var place3 = petriNet.AddPlace("3");
            var place5 = petriNet.AddPlace("5");
            var place6 = petriNet.AddPlace("6");

            petriNet.AddTransition("One", incomingPlace: place1, outgoingPlace: place3);
            petriNet.AddTransition("Three", incomingPlace: place3, outgoingPlace: place5);
            petriNet.AddTransition("Five", incomingPlace: place5, outgoingPlace: place6);

            return petriNet;
        }
        /// <summary>
        ///     Slices a PetriNet into columns and then draws it on the given canvas.
        /// </summary>
        /// <param name="petriNet">A petrinet.</param>
        /// <param name="canvas">The canvas the net is drawn upon.</param>
        /// <param name="isDragAndDropAllowed">Enables drag and drop for all nodes. Default: true</param>
        /// <param name="forceRedraw"></param>
        /// <returns>The given canvas but with a net on it.</returns>
        /// <author>Jannik Arndt (based on Roman Bauers implementation)</author>
        public Canvas DrawPetriNet(PetriNet petriNet, Canvas canvas, bool isDragAndDropAllowed = true,
            bool forceRedraw = false)
        {
            Dictionary<Node, Thumb> nodesDictionary = new Dictionary<Node, Thumb>();

            List<Column> listOfColumns = ColumnBuilder.Build(petriNet);

            if (Settings.Default.AutomaticallyAlignNodes)
                listOfColumns = RowBuilder.CalculateRows(listOfColumns);

            // 1. Draw Transitions and Places
            foreach (Column column in listOfColumns)
            {
                int positionX = (Settings.Default.ColumnWidth*column.ColumnNumber) + Settings.Default.PlaceRadius;
                int positionY;

                foreach (Node node in column.HashSetOfNodes)
                {
                    // If the node has been drawn before, use the saved values
                    if (node.IsDrawn && forceRedraw == false)
                    {
                        positionX = node.PositionX;
                    }
                    else
                    {
                        node.PositionX = positionX;
                        positionY = Convert.ToInt32(node.Row*Settings.Default.RowHeight);
                        node.PositionY = positionY;
                        node.IsDrawn = true;
                    }

                    Thumb nodeThumb = DrawNode(canvas, node, isDragAndDropAllowed);

                    //Draw the place and cache the drawing.
                    if (!nodesDictionary.ContainsKey(node))
                        nodesDictionary.Add(node, nodeThumb);
                }
            }

            // 2. Draw arrows between the nodes
            foreach (KeyValuePair<Node, Thumb> node in nodesDictionary)
                foreach (Node followingNode in node.Key.OutgoingNodes)
                    ConnectByArrow(canvas, node.Value, nodesDictionary[followingNode]);
            return canvas;
        }
        /// <summary>
        /// Adds as many places as following events that each lead to a transition.
        /// </summary>
        /// <param name="startingTransition">Contains the starting transition</param>
        /// <param name="followers">node.followers</param>
        /// <param name="petriNet">Contains the current petri net</param>
        /// <returns>The transitions that are in an AND-relation.</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartAND(Transition startingTransition, List<EventNode> followers, PetriNet petriNet)
        {
            for (var index = 0; index < followers.Count; index++)
                _openParallelismCount.Push(Parallelism.And);
            var listOfFollowingTransitions = new List<Transition>();

            if (startingTransition.Name == "")
                startingTransition.Name = "AND Split";
            startingTransition.IsANDSplit = true;

            foreach (var node in followers)
            {
                var newPlace = petriNet.AddPlace();

                listOfFollowingTransitions.Add(petriNet.AddTransition(node.InnerEvent.Name, incomingPlace: newPlace));
                startingTransition.AddOutgoingPlace(newPlace);
            }
            return listOfFollowingTransitions;
        }
        /// <summary>
        /// Resolves a construct of three parallel events: a AND ( b XOR c )        
        /// </summary>
        /// <param name="event1">One event</param>
        /// <param name="a">An event node</param>
        /// <param name="b">An event node</param>
        /// <param name="c">An event node</param>
        /// <param name="petriNet">Current petri net</param>
        /// <returns>A list with final transitions</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartAand_BxorC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition("AND Join");
            finalTransition.IsANDJoin = true;

            var andTransition = StartAND(event1, new List<EventNode> { a }, petriNet);
            EndAND(andTransition, petriNet, finalTransition);

            var xorTransition = StartXOR(event1, new List<EventNode> { b, c }, petriNet);
            EndXOR(xorTransition, petriNet, finalTransition);

            return new List<Transition> { finalTransition };
        }
        /// <summary>
        /// Goes through the steps of the (improved) HeuristicMiner Algorithm:
        /// Find starting-events => build adjacency-matrix => convert into petrinet
        /// </summary>
        /// <returns>A PetriNet as a ProcessModel</returns>
        /// <exception cref="ArgumentNullException">If the field-parameter is null.</exception>
        /// <author>Jannik Arndt</author>
        public ProcessModel Mine()
        {
            if (_field == null)
                throw new Exception("The field parameter was null");

            // Get parameters from MinerSettings
            var threshold = MinerSettings.GetAsDouble("AdjacencyThresholdSlider") / 100;
            var maxDepth = MinerSettings.GetAsInt("MaximumRecursionDepthSlider");

            if (threshold < 0 || threshold > 1)
                throw new Exception("Threshold must be between 0 and 1.");
            if (maxDepth < 1)
                throw new Exception("Maximum recursion depth must be more greater than 1.");

            // Statistics
            var processingTimeStart = Process.GetCurrentProcess().TotalProcessorTime;

            // 1. Create Adjacency Matrix
            var adjacencyMatrix = CreateAdjacencyMatrix(_field);

            // 2. Calculate the indexes of the first events
            var startindexes = GetColumnsWithOnlyNegativeEntries(adjacencyMatrix);

            // for every sub-processmodel
            foreach (var startindex in startindexes)
            {
                // 3. Create the dependency-Graph
                var rootNode = CreateDependencyGraph(_field.EventLog.ListOfUniqueEvents, adjacencyMatrix, threshold, 0.0, startindex, maxDepth);

                // 4. Turn the dependency-graph into a petri-net
                _listOfSubPetriNets.Add(CreatePetriNetFromDependencyGraph(rootNode, _field.EventLog, _field.Infotext));
            }

            // 5. Connect all Petrinets by one XOR-Split
            if (_listOfSubPetriNets.Count > 1)
            {
                // 5a. Connect all subnets to one start
                var startingPlace = _finalPetriNet.AddPlace("Start");
                foreach (var petriNet in _listOfSubPetriNets)
                {
                    var xorTransition = _finalPetriNet.AddTransition(incomingPlace: startingPlace);
                    _finalPetriNet.MergeWithPetriNet(petriNet, xorTransition);
                }
                // 5b. Add following transitions to all subnet-endings
                foreach (var openEndPlace in _finalPetriNet.GetSinks())
                    if (openEndPlace.GetType() == typeof(Place))
                        _finalPetriNet.AddTransition(incomingPlace: (Place)openEndPlace);
            }
            else
                _finalPetriNet = _listOfSubPetriNets[0];

            // 6. Connect all open endings
            _finalPetriNet = FixEnding(_finalPetriNet);

            // 7. Delete empty transitions
            _finalPetriNet = PetriNetOperation.CleanUpPetriNet(_finalPetriNet);

            // 8. Save information about the mining-process
            var processingTimeEnd = Process.GetCurrentProcess().TotalProcessorTime;
            var processingTime = Math.Abs((processingTimeEnd - processingTimeStart).TotalMilliseconds) < 1 ? "< 15 ms" : (processingTimeEnd - processingTimeStart).TotalMilliseconds + " ms";
            _field.Information.Add("Processing Time", processingTime);
            _field.Information.Add("Number of Events", _finalPetriNet.Transitions.Count(s => s.IsLoop == false).ToString(CultureInfo.InvariantCulture));
            _field.Information.Add("Number of Transitions", _finalPetriNet.Transitions.Count.ToString(CultureInfo.InvariantCulture));
            _field.Information.Add("Number of Places", _finalPetriNet.Places.Count.ToString(CultureInfo.InvariantCulture));
            _field.Information.Add("Loops in the net", _finalPetriNet.CountLoops().ToString(CultureInfo.InvariantCulture));
            _field.Information.Add("Events used", _finalPetriNet.CountTransitionsWithoutANDs() - _finalPetriNet.CountLoops() + " of " + _field.EventLog.ListOfUniqueEvents.Count);
            _field.Information.Add("Parallel Models", startindexes.Count.ToString(CultureInfo.InvariantCulture));
            if (_listOfNodes.Count > 0)
            {
                _field.Information.Add("Minimal Adjacency", _listOfNodes.Min().ToString(CultureInfo.InvariantCulture));
                _field.Information.Add("Average Adjacency", Math.Round(_listOfNodes.Average(), 3).ToString(CultureInfo.InvariantCulture));
                if (Math.Abs(_listOfNodes.Min() - _listOfNodes.Average()) > 0.0001)
                    _field.Information.Add("Standard Deviation", CalculateStandardDeviation(_listOfNodes).ToString(CultureInfo.InvariantCulture));
            }

            return _finalPetriNet;
        }
        /// <summary>
        /// A recursive function to call the next level of HandleNode. Also closes open XORs and ANDs right away.
        /// </summary>
        /// <param name="trans"></param>
        /// <param name="node"></param>
        /// <param name="log"></param>
        /// <param name="petriNet"></param>
        /// <author>Jannik Arndt</author>
        public void HandleNodesAndCloseParallelisms(Transition trans, EventNode node, EventLog log, PetriNet petriNet)
        {
            var transitions = HandleNode(trans, node, log, petriNet);
            if (transitions == null)
                return;

            foreach (var transition in transitions)
                foreach (var eventNode in node.ListOfFollowers)
                    if (transition.Name == eventNode.InnerEvent.Name || transition.IsANDJoin)
                        HandleNodesAndCloseParallelisms(transition, eventNode, log, petriNet);
        }
        /// <summary>
        /// Returns a list of places between two transitions in a petrinet
        /// </summary>
        /// <param name="t1">Transitionname</param>
        /// <param name="t2">Transitionname</param>
        /// <param name="petriNet">Petrinet</param>
        /// <returns>Returns a list of places</returns>
        /// <autor>Andrej Albrecht</autor>
        private List<Place> GetListOfPlacesBetweenTwoTransitionsFromPetriNet(string t1, string t2, PetriNet petriNet)
        {
            Transition transition1 = petriNet.FindTransition(t1);
            Transition transition2 = petriNet.FindTransition(t2);
            HashSet<Place> placesInBetween = new HashSet<Place>();

            foreach (Place outgoingPlace in transition1.OutgoingPlaces)
                if (transition2.IncomingPlaces.Contains(outgoingPlace))
                    placesInBetween.Add(outgoingPlace);

            return placesInBetween.ToList();
        }
        /// <summary>
        /// Adds a place and the transitions it leads to.
        /// </summary>
        /// <param name="startingTransition">>Contains the starting transition</param>
        /// <param name="followers">node.followers</param>
        /// <param name="petriNet">Contains the current petri net</param>
        /// <returns>The two transitions that are in an XOR-relation.</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartXOR(Transition startingTransition, List<EventNode> followers, PetriNet petriNet)
        {
            if (followers.Count > 1)
                for (var index = 0; index < followers.Count; index++)
                    _openParallelismCount.Push(Parallelism.Xor);

            var newPlace = petriNet.AddPlace();
            var listOfFollowingTransitions = new List<Transition>();
            foreach (var eventNode in followers)
            {
                if (eventNode == null)
                    break;
                /* _____________________          __________         ____________________________
                 * | startingTransition |  --->  ( NewPlace )  --->  |  NewFollowingTransition  |
                 * '''''''''''''''''''''          **********         ''''''''''''''''''''''''''''
                 */
                var newFollowingTransition = petriNet.FindTransition(eventNode.InnerEvent.Name);
                // The following transition already exists => close something
                if (newFollowingTransition != null)
                {
                    if (_openParallelismCount.Count > 0)
                        switch (_openParallelismCount.Peek())
                        {
                            // Close XOR
                            case Parallelism.Xor:
                                newFollowingTransition.IncomingPlaces.Remove(newPlace);
                                startingTransition.AddOutgoingPlace(newFollowingTransition.IncomingPlaces.First());
                                _openParallelismCount.Pop();
                                break;
                            // Close AND
                            case Parallelism.And:
                                newFollowingTransition.AddIncomingPlace(newPlace);
                                startingTransition.AddOutgoingPlace(newPlace);
                                _openParallelismCount.Pop();
                                break;
                        }
                }
                // Open XOR
                else
                {
                    newFollowingTransition = petriNet.AddTransition(eventNode.InnerEvent.Name, incomingPlace: newPlace);
                    if (newPlace.IncomingTransitions.Count == 0)
                        startingTransition.AddOutgoingPlace(newPlace);
                }

                listOfFollowingTransitions.Add(newFollowingTransition);

            }
            if (newPlace.IncomingTransitions.Count == 0 && newPlace.OutgoingTransitions.Count == 0)
                petriNet.Places.Remove(newPlace);
            return listOfFollowingTransitions;
        }
        /// <summary>
        /// Adds a place and connects the open transitions to it. Then puts the given transition behind that whole thing.
        /// </summary>
        /// <param name="startingTransition">The starting transition</param>
        /// <param name="petriNet">The current petri net</param>
        /// <param name="endingTransition">The ending transition, if it exists already. Otherwise a new one is created.</param>
        /// <returns>The empty transition after the place that combines to XOR.</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> EndXOR(List<Transition> startingTransition, PetriNet petriNet, Transition endingTransition = null)
        {
            if (endingTransition == null)
                endingTransition = petriNet.AddTransition();

            if (_openParallelismCount.Count > 0)
                _openParallelismCount.Pop();

            var newPlace = petriNet.AddPlace();
            foreach (var transition in startingTransition)
                transition.AddOutgoingPlace(newPlace);
            endingTransition.AddIncomingPlace(newPlace);
            return new List<Transition> { endingTransition };
        }
        /// <summary>
        /// Goes through the dependency-graph and creates a petrinet.
        /// </summary>
        /// <param name="dependencyGraph">An EventNode from CreateDependencyGraph()</param>
        /// <param name="eventLog">The EventLog to calculate AND and XOR-relations</param>
        /// <param name="name">The name of the new PetriNet</param>
        /// <returns>A complete petrinet</returns>
        /// <author>Jannik Arndt, Bernhard Bruns</author>
        public PetriNet CreatePetriNetFromDependencyGraph(EventNode dependencyGraph, EventLog eventLog, string name)
        {
            var petriNet = new PetriNet(name);

            // 1. first event
            var startingPlace = petriNet.AddPlace("", 1);
            var firstEvent = petriNet.AddTransition(dependencyGraph.InnerEvent.Name, incomingPlace: startingPlace);

            // 2. Go through the net and turn Nodes into Places and Transitions, with parallel structures
            HandleNodesAndCloseParallelisms(firstEvent, dependencyGraph, eventLog, petriNet);

            // 3. handle loops
            petriNet = HandleLoops(eventLog, petriNet);

            return petriNet;
        }
        public void CompareEventLogAndPetriNetTest1()
        {
            //
            //EventLog
            //
            //create Case1
            Case ca1 = new Case();
            ca1.CreateEvent("A");
            ca1.CreateEvent("C");
            ca1.CreateEvent("D");
            ca1.CreateEvent("E");
            ca1.CreateEvent("H");

            //create Case2
            Case ca2 = new Case();
            ca2.CreateEvent("A");
            ca2.CreateEvent("B");
            ca2.CreateEvent("D");
            ca2.CreateEvent("E");
            ca2.CreateEvent("G");

            //create Case3
            Case ca3 = new Case();
            ca3.CreateEvent("A");
            ca3.CreateEvent("D");
            ca3.CreateEvent("C");
            ca3.CreateEvent("E");
            ca3.CreateEvent("H");

            //create Case4
            Case ca4 = new Case();
            ca4.CreateEvent("A");
            ca4.CreateEvent("B");
            ca4.CreateEvent("D");
            ca4.CreateEvent("E");
            ca4.CreateEvent("H");

            //create Case5
            Case ca5 = new Case();
            ca5.CreateEvent("A");
            ca5.CreateEvent("C");
            ca5.CreateEvent("D");
            ca5.CreateEvent("E");
            ca5.CreateEvent("G");

            //create Case6
            Case ca6 = new Case();
            ca6.CreateEvent("A");
            ca6.CreateEvent("D");
            ca6.CreateEvent("C");
            ca6.CreateEvent("E");
            ca6.CreateEvent("G");

            //create Case7
            Case ca7 = new Case();
            ca7.CreateEvent("A");
            ca7.CreateEvent("D");
            ca7.CreateEvent("B");
            ca7.CreateEvent("E");
            ca7.CreateEvent("H");

            //create Case8
            Case ca8 = new Case();
            ca8.CreateEvent("A");
            ca8.CreateEvent("C");
            ca8.CreateEvent("D");
            ca8.CreateEvent("E");
            ca8.CreateEvent("F");
            ca8.CreateEvent("D");
            ca8.CreateEvent("B");
            ca8.CreateEvent("E");
            ca8.CreateEvent("H");

            //create Case9
            Case ca9 = new Case();
            ca9.CreateEvent("A");
            ca9.CreateEvent("D");
            ca9.CreateEvent("B");
            ca9.CreateEvent("E");
            ca9.CreateEvent("G");

            //create Case10
            Case ca10 = new Case();
            ca10.CreateEvent("A");
            ca10.CreateEvent("C");
            ca10.CreateEvent("D");
            ca10.CreateEvent("E");
            ca10.CreateEvent("F");
            ca10.CreateEvent("B");
            ca10.CreateEvent("D");
            ca10.CreateEvent("E");
            ca10.CreateEvent("H");

            //create Case11
            Case ca11 = new Case();
            ca11.CreateEvent("A");
            ca11.CreateEvent("C");
            ca11.CreateEvent("D");
            ca11.CreateEvent("E");
            ca11.CreateEvent("F");
            ca11.CreateEvent("B");
            ca11.CreateEvent("D");
            ca11.CreateEvent("E");
            ca11.CreateEvent("G");

            //create Case12
            Case ca12 = new Case();
            ca12.CreateEvent("A");
            ca12.CreateEvent("C");
            ca12.CreateEvent("D");
            ca12.CreateEvent("E");
            ca12.CreateEvent("F");
            ca12.CreateEvent("D");
            ca12.CreateEvent("B");
            ca12.CreateEvent("E");
            ca12.CreateEvent("G");

            //create Case13
            Case ca13 = new Case();
            ca13.CreateEvent("A");
            ca13.CreateEvent("D");
            ca13.CreateEvent("C");
            ca13.CreateEvent("E");
            ca13.CreateEvent("F");
            ca13.CreateEvent("C");
            ca13.CreateEvent("D");
            ca13.CreateEvent("E");
            ca13.CreateEvent("H");

            //create Case14
            Case ca14 = new Case();
            ca14.CreateEvent("A");
            ca14.CreateEvent("D");
            ca14.CreateEvent("C");
            ca14.CreateEvent("E");
            ca14.CreateEvent("F");
            ca14.CreateEvent("D");
            ca14.CreateEvent("B");
            ca14.CreateEvent("E");
            ca14.CreateEvent("H");

            //create Case15
            Case ca15 = new Case();
            ca15.CreateEvent("A");
            ca15.CreateEvent("D");
            ca15.CreateEvent("C");
            ca15.CreateEvent("E");
            ca15.CreateEvent("F");
            ca15.CreateEvent("B");
            ca15.CreateEvent("D");
            ca15.CreateEvent("E");
            ca15.CreateEvent("G");

            //create Case16
            Case ca16 = new Case();
            ca16.CreateEvent("A");
            ca16.CreateEvent("C");
            ca16.CreateEvent("D");
            ca16.CreateEvent("E");
            ca16.CreateEvent("F");
            ca16.CreateEvent("B");
            ca16.CreateEvent("D");
            ca16.CreateEvent("E");
            ca16.CreateEvent("F");
            ca16.CreateEvent("D");
            ca16.CreateEvent("B");
            ca16.CreateEvent("E");
            ca16.CreateEvent("G");

            //create Case17
            Case ca17 = new Case();
            ca17.CreateEvent("A");
            ca17.CreateEvent("D");
            ca17.CreateEvent("C");
            ca17.CreateEvent("E");
            ca17.CreateEvent("F");
            ca17.CreateEvent("D");
            ca17.CreateEvent("B");
            ca17.CreateEvent("E");
            ca17.CreateEvent("G");

            //create Case18
            Case ca18 = new Case();
            ca18.CreateEvent("A");
            ca18.CreateEvent("D");
            ca18.CreateEvent("C");
            ca18.CreateEvent("E");
            ca18.CreateEvent("F");
            ca18.CreateEvent("B");
            ca18.CreateEvent("D");
            ca18.CreateEvent("E");
            ca18.CreateEvent("F");
            ca18.CreateEvent("B");
            ca18.CreateEvent("D");
            ca18.CreateEvent("E");
            ca18.CreateEvent("G");

            //create Case19
            Case ca19 = new Case();
            ca19.CreateEvent("A");
            ca19.CreateEvent("D");
            ca19.CreateEvent("C");
            ca19.CreateEvent("E");
            ca19.CreateEvent("F");
            ca19.CreateEvent("D");
            ca19.CreateEvent("B");
            ca19.CreateEvent("E");
            ca19.CreateEvent("F");
            ca19.CreateEvent("B");
            ca19.CreateEvent("D");
            ca19.CreateEvent("E");
            ca19.CreateEvent("H");

            //create Case20
            Case ca20 = new Case();
            ca20.CreateEvent("A");
            ca20.CreateEvent("D");
            ca20.CreateEvent("B");
            ca20.CreateEvent("E");
            ca20.CreateEvent("F");
            ca20.CreateEvent("B");
            ca20.CreateEvent("D");
            ca20.CreateEvent("E");
            ca20.CreateEvent("F");
            ca20.CreateEvent("D");
            ca20.CreateEvent("B");
            ca20.CreateEvent("E");
            ca20.CreateEvent("G");

            //create Case21
            Case ca21 = new Case();
            ca21.CreateEvent("A");
            ca21.CreateEvent("D");
            ca21.CreateEvent("C");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("D");
            ca21.CreateEvent("B");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("C");
            ca21.CreateEvent("D");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("D");
            ca21.CreateEvent("B");
            ca21.CreateEvent("E");
            ca21.CreateEvent("G");

            //create Event Log
            EventLog eventLog = new EventLog();
            eventLog.Cases.Add(ca1);
            eventLog.Cases.Add(ca2);
            eventLog.Cases.Add(ca3);
            eventLog.Cases.Add(ca4);
            eventLog.Cases.Add(ca5);
            eventLog.Cases.Add(ca6);
            eventLog.Cases.Add(ca7);
            eventLog.Cases.Add(ca8);
            eventLog.Cases.Add(ca9);
            eventLog.Cases.Add(ca10);
            eventLog.Cases.Add(ca11);
            eventLog.Cases.Add(ca12);
            eventLog.Cases.Add(ca13);
            eventLog.Cases.Add(ca14);
            eventLog.Cases.Add(ca15);
            eventLog.Cases.Add(ca16);
            eventLog.Cases.Add(ca17);
            eventLog.Cases.Add(ca18);
            eventLog.Cases.Add(ca19);
            eventLog.Cases.Add(ca20);
            eventLog.Cases.Add(ca21);

            //
            //PetriNet
            //
            PetriNet petriNet = new PetriNet("Petri-Net Name");

            Place pStart = new Place("Place Start", 0);
            Place pC1 = new Place("c1", 0);
            Place pC2 = new Place("c2", 0);
            Place pC3 = new Place("c3", 0);
            Place pC4 = new Place("c4", 0);
            Place pC5 = new Place("c5", 0);
            Place pEnd = new Place("Place End", 0);

            Transition tA = new Transition("A");
            Transition tB = new Transition("B");
            Transition tC = new Transition("C");
            Transition tD = new Transition("D");
            Transition tE = new Transition("E");
            Transition tF = new Transition("F");
            Transition tG = new Transition("G");
            Transition tH = new Transition("H");

            tG.AddOutgoingPlace(pEnd);
            tG.AddIncomingPlace(pC5);

            tH.AddOutgoingPlace(pEnd);
            tH.AddIncomingPlace(pC5);

            tE.AddIncomingPlace(pC3);
            tE.AddIncomingPlace(pC4);
            tE.AddOutgoingPlace(pC5);

            pC3.AppendIncomingTransition(tB);
            pC3.AppendIncomingTransition(tC);
            pC3.AppendOutgoingTransition(tE);

            pC4.AppendIncomingTransition(tD);
            pC4.AppendOutgoingTransition(tE);

            tB.AddIncomingPlace(pC1);
            tB.AddOutgoingPlace(pC3);

            tC.AddIncomingPlace(pC1);
            tC.AddOutgoingPlace(pC3);

            tD.AddIncomingPlace(pC2);
            tD.AddOutgoingPlace(pC4);

            pC1.AppendIncomingTransition(tA);
            pC1.AppendOutgoingTransition(tB);
            pC1.AppendOutgoingTransition(tC);

            pC2.AppendIncomingTransition(tA);
            pC2.AppendOutgoingTransition(tD);

            tF.AddIncomingPlace(pC5);
            tF.AddOutgoingPlace(pC1);
            tF.AddOutgoingPlace(pC2);

            //
            tA.AddIncomingPlace(pStart);
            tA.AddOutgoingPlace(pC1);
            tA.AddOutgoingPlace(pC2);

            pStart.AppendOutgoingTransition(tA);

            pEnd.AppendIncomingTransition(tG);
            pEnd.AppendIncomingTransition(tH);

            ////
            petriNet.Transitions.Add(tA);
            petriNet.Transitions.Add(tB);
            petriNet.Transitions.Add(tC);
            petriNet.Transitions.Add(tD);
            petriNet.Transitions.Add(tE);
            petriNet.Transitions.Add(tF);
            petriNet.Transitions.Add(tG);
            petriNet.Transitions.Add(tH);

            ////
            petriNet.Places.Add(pStart);
            petriNet.Places.Add(pC1);
            petriNet.Places.Add(pC2);
            petriNet.Places.Add(pC3);
            petriNet.Places.Add(pC4);
            petriNet.Places.Add(pC5);
            petriNet.Places.Add(pEnd);

            ComparingFootprint footprintEventLog = ComparingFootprintAlgorithm.CreateFootprint(eventLog);
            ComparingFootprint footprintPetriNet = ComparingFootprintAlgorithm.CreateFootprint(petriNet);

            ComparingFootprintResultMatrix footprintResultMatrix = new ComparingFootprintResultMatrix(footprintEventLog, footprintPetriNet);

            if (footprintResultMatrix.HeaderWithEventNames.Count != 8)
            {
                Assert.Fail("");
            }

            int y = 0;
            foreach (String headerNameY in footprintResultMatrix.HeaderWithEventNames)
            {
                int x = 0;
                foreach (String headerNameX in footprintResultMatrix.HeaderWithEventNames)
                {
                    ResultCellType resultCellType = footprintResultMatrix.ResultMatrix[y, x];
                    Console.WriteLine("Test headerNameY: " + headerNameY + ", headerNameX: " + headerNameX + " [" + y + "," + x + "].CellType:" + resultCellType);

                    if (!resultCellType.Equals(ResultCellType.NoDifferences))
                    {
                        Assert.Fail("Test headerNameY: " + headerNameY + ", headerNameX: " + headerNameX + " [" + y + "," + x + "].CellType:" + resultCellType);
                    }

                    x++;
                }
                y++;
            }

            //Arrange (Create Footprints)
            ComparingFootprint footPrintEventlog = ComparingFootprintAlgorithm.CreateFootprint(eventLog);
            ComparingFootprint footPrintPetrinet = ComparingFootprintAlgorithm.CreateFootprint(petriNet);
            ComparingFootprintResultMatrix resultFootprint = new ComparingFootprintResultMatrix(footPrintEventlog, footPrintPetrinet);

            // Act (Calculate Fitness)
            double fitnessComparingFootprint = ComparingFootprintAlgorithm.CalculateFitness(resultFootprint.GetNumberOfDifferences(), resultFootprint.GetNumberOfOpportunities());

            if (fitnessComparingFootprint != 1.0)
            {
                // Assert
                Assert.Fail("Fitness not correct! (" + fitnessComparingFootprint + ")");
            }
        }
        /// <summary>
        /// Remove old "End"-Places, combine sinks and add new "End"-Place
        /// </summary>
        /// <param name="petriNet">The petrinet to work on</param>
        /// <author>Jannik Arndt</author>
        public void FixEnding(PetriNet petriNet)
        {
            foreach (Place endPlace in petriNet.Places.Where(place => place.Name.Equals("End")))
                endPlace.Name = "";

            List<Place> endPlaces = (from sink in petriNet.GetSinks() where sink.GetType() == typeof(Place) select sink as Place).ToList();

            if (endPlaces.Count > 1)
                foreach (Place endPlace in endPlaces.Skip(1))
                {
                    foreach (Transition incomingTransition in endPlace.IncomingTransitions)
                    {
                        endPlaces[0].IncomingTransitions.Add(incomingTransition);
                        incomingTransition.OutgoingPlaces.Clear();
                        incomingTransition.OutgoingPlaces.Add(endPlaces[0]);
                    }
                    petriNet.Places.Remove(endPlace);
                }
            endPlaces[0].Name = "End";
        }
        /// <summary>
        /// Method to change the color of the given list of lines in a canvas
        /// </summary>
        /// <param name="listOfLinesToBeRemoved">List of lines that must be removed</param>
        /// <param name="conformanceCheckingCanvas"></param>
        /// <param name="petriNet">Petrinet</param>
        /// <autor>Andrej Albrecht</autor>
        private void SetColorOnLinesToBeRemoved(IEnumerable<List<string>> listOfLinesToBeRemoved, Canvas conformanceCheckingCanvas, PetriNet petriNet)
        {
            foreach (List<String> stringCombination in listOfLinesToBeRemoved)
            {
                if (stringCombination.Count == 2)
                {
                    String transition1 = stringCombination.ElementAt(0);
                    String transition2 = stringCombination.ElementAt(1);

                    IEnumerable<Shape> listWithArrowsToBeMarked = GetArrowsBetweenTransitions(transition1, transition2, conformanceCheckingCanvas, petriNet);

                    foreach (Shape shape in listWithArrowsToBeMarked)
                    {
                        shape.Stroke = Brushes.Red;
                        shape.Fill = Brushes.Red;
                    }
                }
            }
        }
        /// <summary>
        /// Resolves a construct of three parallel events: a XOR ( b AND c )
        /// </summary>
        /// <param name="event1">Event number one</param>
        /// <param name="a">An Event node</param>
        /// <param name="b">An Event node</param>
        /// <param name="c">An Event node</param>
        /// <param name="petriNet">The current petri net</param>
        /// <returns>A list with final transition</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> StartAxor_BandC(Transition event1, EventNode a, EventNode b, EventNode c, PetriNet petriNet)
        {
            var finalTransition = petriNet.AddTransition(); // empty transition, will possibly be cleaned up
            finalTransition.Name = FindFirstCommonSuccessor(new List<EventNode> { a, b, c });

            var xorTransition = StartXOR(event1, new List<EventNode> { a }, petriNet);
            EndXOR(xorTransition, petriNet, finalTransition);

            var andSplit = petriNet.AddTransition(); // no idea where this disappears...
            andSplit.AddIncomingPlace(xorTransition[0].IncomingPlaces[0]); // XOR-Split-Place

            var andTransition = StartAND(andSplit, new List<EventNode> { b, c }, petriNet);
            var andJoin = petriNet.AddTransition("AND Join");
            andJoin.IsANDJoin = true;
            var andTransition2 = EndAND(andTransition, petriNet, andJoin);

            andTransition2[0].AddOutgoingPlace(finalTransition.IncomingPlaces.FirstOrDefault());

            return new List<Transition> { finalTransition };
        }
 /// <summary>
 /// Connects all open endings to one end-place. This is useful for multiple processmodels in one net or for loops at the end of the net.
 /// </summary>
 /// <param name="petriNet">A petrinet with possible open endings</param>
 /// <returns>The given PetriNet with a fixed ending</returns>
 /// <author>Jannik Arndt</author>
 public PetriNet FixEnding(PetriNet petriNet)
 {
     if (petriNet.GetTransitionsWithoutFollowersIgnoreLoops().Count > 0)
     {
         var endingPlace = petriNet.AddPlace("End");
         foreach (var transition in petriNet.GetTransitionsWithoutFollowersIgnoreLoops())
         {
             if (transition.OutgoingPlaces.Count > 0)
             {
                 var temporaryTransition = petriNet.AddTransition();
                 transition.OutgoingPlaces[0].AppendOutgoingTransition(temporaryTransition);
                 temporaryTransition.AddIncomingPlace(transition.OutgoingPlaces[0]);
                 temporaryTransition.AddOutgoingPlace(endingPlace);
             }
             else
                 transition.AddOutgoingPlace(endingPlace);
         }
     }
     return petriNet;
 }
 /// <summary>
 /// Adds an optional Transition to the net, as well as the next mandatory transition.
 /// </summary>
 /// <param name="startingTransition">The existing transition</param>
 /// <param name="optionalAction">The optional transition</param>
 /// <param name="commonAction">The mandatory transition</param>
 /// <param name="petriNet"></param>
 /// <returns></returns>
 /// <author>Jannik Arndt</author>
 public List<Transition> StartOptionalTransition(Transition startingTransition, String optionalAction, String commonAction, PetriNet petriNet)
 {
     var place1 = petriNet.AddPlace();
     var place2 = petriNet.AddPlace();
     startingTransition.AddOutgoingPlace(place1);
     petriNet.AddTransition(optionalAction, incomingPlace: place1, outgoingPlace: place2);
     var commonTransition = petriNet.AddTransition(commonAction, new List<Place> { place1, place2 });
     return new List<Transition> { commonTransition };
 }
        /// <summary>
        /// Find loops in the eventlog and add them to the petrinet
        /// </summary>
        /// <param name="eventLog">The eventlog that is searched for loops</param>
        /// <param name="petriNet">The net the loops are added to</param>
        /// <returns>A petrinet with added loops</returns>
        /// <author>Jannik Arndt</author>
        public PetriNet HandleLoops(EventLog eventLog, PetriNet petriNet)
        {
            var loopedActivities = FindLoopingActivities(eventLog);

            foreach (var activity in loopedActivities)
                petriNet.AddLoop(activity);

            return petriNet;
        }
        public void CheckCorrectFitnessBetweenEventLogAndPetrinetTest1()
        {
            //
            //EventLog
            //
            //create Case1
            Case ca1 = new Case();
            ca1.CreateEvent("A");
            ca1.CreateEvent("C");
            ca1.CreateEvent("D");
            ca1.CreateEvent("E");
            ca1.CreateEvent("H");

            //create Case2
            Case ca2 = new Case();
            ca2.CreateEvent("A");
            ca2.CreateEvent("B");
            ca2.CreateEvent("D");
            ca2.CreateEvent("E");
            ca2.CreateEvent("G");

            //create Case3
            Case ca3 = new Case();
            ca3.CreateEvent("A");
            ca3.CreateEvent("D");
            ca3.CreateEvent("C");
            ca3.CreateEvent("E");
            ca3.CreateEvent("H");

            //create Case4
            Case ca4 = new Case();
            ca4.CreateEvent("A");
            ca4.CreateEvent("B");
            ca4.CreateEvent("D");
            ca4.CreateEvent("E");
            ca4.CreateEvent("H");

            //create Case5
            Case ca5 = new Case();
            ca5.CreateEvent("A");
            ca5.CreateEvent("C");
            ca5.CreateEvent("D");
            ca5.CreateEvent("E");
            ca5.CreateEvent("G");

            //create Case6
            Case ca6 = new Case();
            ca6.CreateEvent("A");
            ca6.CreateEvent("D");
            ca6.CreateEvent("C");
            ca6.CreateEvent("E");
            ca6.CreateEvent("G");

            //create Case7
            Case ca7 = new Case();
            ca7.CreateEvent("A");
            ca7.CreateEvent("D");
            ca7.CreateEvent("B");
            ca7.CreateEvent("E");
            ca7.CreateEvent("H");

            //create Case8
            Case ca8 = new Case();
            ca8.CreateEvent("A");
            ca8.CreateEvent("C");
            ca8.CreateEvent("D");
            ca8.CreateEvent("E");
            ca8.CreateEvent("F");
            ca8.CreateEvent("D");
            ca8.CreateEvent("B");
            ca8.CreateEvent("E");
            ca8.CreateEvent("H");

            //create Case9
            Case ca9 = new Case();
            ca9.CreateEvent("A");
            ca9.CreateEvent("D");
            ca9.CreateEvent("B");
            ca9.CreateEvent("E");
            ca9.CreateEvent("G");

            //create Case10
            Case ca10 = new Case();
            ca10.CreateEvent("A");
            ca10.CreateEvent("C");
            ca10.CreateEvent("D");
            ca10.CreateEvent("E");
            ca10.CreateEvent("F");
            ca10.CreateEvent("B");
            ca10.CreateEvent("D");
            ca10.CreateEvent("E");
            ca10.CreateEvent("H");

            //create Case11
            Case ca11 = new Case();
            ca11.CreateEvent("A");
            ca11.CreateEvent("C");
            ca11.CreateEvent("D");
            ca11.CreateEvent("E");
            ca11.CreateEvent("F");
            ca11.CreateEvent("B");
            ca11.CreateEvent("D");
            ca11.CreateEvent("E");
            ca11.CreateEvent("G");

            //create Case12
            Case ca12 = new Case();
            ca12.CreateEvent("A");
            ca12.CreateEvent("C");
            ca12.CreateEvent("D");
            ca12.CreateEvent("E");
            ca12.CreateEvent("F");
            ca12.CreateEvent("D");
            ca12.CreateEvent("B");
            ca12.CreateEvent("E");
            ca12.CreateEvent("G");

            //create Case13
            Case ca13 = new Case();
            ca13.CreateEvent("A");
            ca13.CreateEvent("D");
            ca13.CreateEvent("C");
            ca13.CreateEvent("E");
            ca13.CreateEvent("F");
            ca13.CreateEvent("C");
            ca13.CreateEvent("D");
            ca13.CreateEvent("E");
            ca13.CreateEvent("H");

            //create Case14
            Case ca14 = new Case();
            ca14.CreateEvent("A");
            ca14.CreateEvent("D");
            ca14.CreateEvent("C");
            ca14.CreateEvent("E");
            ca14.CreateEvent("F");
            ca14.CreateEvent("D");
            ca14.CreateEvent("B");
            ca14.CreateEvent("E");
            ca14.CreateEvent("H");

            //create Case15
            Case ca15 = new Case();
            ca15.CreateEvent("A");
            ca15.CreateEvent("D");
            ca15.CreateEvent("C");
            ca15.CreateEvent("E");
            ca15.CreateEvent("F");
            ca15.CreateEvent("B");
            ca15.CreateEvent("D");
            ca15.CreateEvent("E");
            ca15.CreateEvent("G");

            //create Case16
            Case ca16 = new Case();
            ca16.CreateEvent("A");
            ca16.CreateEvent("C");
            ca16.CreateEvent("D");
            ca16.CreateEvent("E");
            ca16.CreateEvent("F");
            ca16.CreateEvent("B");
            ca16.CreateEvent("D");
            ca16.CreateEvent("E");
            ca16.CreateEvent("F");
            ca16.CreateEvent("D");
            ca16.CreateEvent("B");
            ca16.CreateEvent("E");
            ca16.CreateEvent("G");

            //create Case17
            Case ca17 = new Case();
            ca17.CreateEvent("A");
            ca17.CreateEvent("D");
            ca17.CreateEvent("C");
            ca17.CreateEvent("E");
            ca17.CreateEvent("F");
            ca17.CreateEvent("D");
            ca17.CreateEvent("B");
            ca17.CreateEvent("E");
            ca17.CreateEvent("G");

            //create Case18
            Case ca18 = new Case();
            ca18.CreateEvent("A");
            ca18.CreateEvent("D");
            ca18.CreateEvent("C");
            ca18.CreateEvent("E");
            ca18.CreateEvent("F");
            ca18.CreateEvent("B");
            ca18.CreateEvent("D");
            ca18.CreateEvent("E");
            ca18.CreateEvent("F");
            ca18.CreateEvent("B");
            ca18.CreateEvent("D");
            ca18.CreateEvent("E");
            ca18.CreateEvent("G");

            //create Case19
            Case ca19 = new Case();
            ca19.CreateEvent("A");
            ca19.CreateEvent("D");
            ca19.CreateEvent("C");
            ca19.CreateEvent("E");
            ca19.CreateEvent("F");
            ca19.CreateEvent("D");
            ca19.CreateEvent("B");
            ca19.CreateEvent("E");
            ca19.CreateEvent("F");
            ca19.CreateEvent("B");
            ca19.CreateEvent("D");
            ca19.CreateEvent("E");
            ca19.CreateEvent("H");

            //create Case20
            Case ca20 = new Case();
            ca20.CreateEvent("A");
            ca20.CreateEvent("D");
            ca20.CreateEvent("B");
            ca20.CreateEvent("E");
            ca20.CreateEvent("F");
            ca20.CreateEvent("B");
            ca20.CreateEvent("D");
            ca20.CreateEvent("E");
            ca20.CreateEvent("F");
            ca20.CreateEvent("D");
            ca20.CreateEvent("B");
            ca20.CreateEvent("E");
            ca20.CreateEvent("G");

            //create Case21
            Case ca21 = new Case();
            ca21.CreateEvent("A");
            ca21.CreateEvent("D");
            ca21.CreateEvent("C");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("D");
            ca21.CreateEvent("B");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("C");
            ca21.CreateEvent("D");
            ca21.CreateEvent("E");
            ca21.CreateEvent("F");
            ca21.CreateEvent("D");
            ca21.CreateEvent("B");
            ca21.CreateEvent("E");
            ca21.CreateEvent("G");

            //create Event Log
            EventLog eventLog = new EventLog();
            eventLog.Cases.Add(ca1);
            eventLog.Cases.Add(ca2);
            eventLog.Cases.Add(ca3);
            eventLog.Cases.Add(ca4);
            eventLog.Cases.Add(ca5);
            eventLog.Cases.Add(ca6);
            eventLog.Cases.Add(ca7);
            eventLog.Cases.Add(ca8);
            eventLog.Cases.Add(ca9);
            eventLog.Cases.Add(ca10);
            eventLog.Cases.Add(ca11);
            eventLog.Cases.Add(ca12);
            eventLog.Cases.Add(ca13);
            eventLog.Cases.Add(ca14);
            eventLog.Cases.Add(ca15);
            eventLog.Cases.Add(ca16);
            eventLog.Cases.Add(ca17);
            eventLog.Cases.Add(ca18);
            eventLog.Cases.Add(ca19);
            eventLog.Cases.Add(ca20);
            eventLog.Cases.Add(ca21);

            //
            //PetriNet
            //
            PetriNet petriNet = new PetriNet("Petri-Net Name");

            Place pStart = new Place("Place Start", 0);
            Place pC1 = new Place("c1", 0);
            Place pC2 = new Place("c2", 0);
            Place pC3 = new Place("c3", 0);
            Place pC4 = new Place("c4", 0);
            Place pC5 = new Place("c5", 0);
            Place pEnd = new Place("Place End", 0);

            Transition tA = new Transition("A");
            Transition tB = new Transition("B");
            Transition tC = new Transition("C");
            Transition tD = new Transition("D");
            Transition tE = new Transition("E");
            Transition tF = new Transition("F");
            Transition tG = new Transition("G");
            Transition tH = new Transition("H");

            tG.AddOutgoingPlace(pEnd);
            tG.AddIncomingPlace(pC5);

            tH.AddOutgoingPlace(pEnd);
            tH.AddIncomingPlace(pC5);

            tE.AddIncomingPlace(pC3);
            tE.AddIncomingPlace(pC4);
            tE.AddOutgoingPlace(pC5);

            pC3.AppendIncomingTransition(tB);
            pC3.AppendIncomingTransition(tC);
            pC3.AppendOutgoingTransition(tE);

            pC4.AppendOutgoingTransition(tE);

            tB.AddIncomingPlace(pC1);
            tB.AddOutgoingPlace(pC3);

            tC.AddIncomingPlace(pC1);
            tC.AddOutgoingPlace(pC3);

            tD.AddIncomingPlace(pC2);
            tD.AddOutgoingPlace(pC4);

            pC1.AppendIncomingTransition(tA);
            pC1.AppendOutgoingTransition(tB);
            pC1.AppendOutgoingTransition(tC);

            pC2.AppendIncomingTransition(tA);
            pC2.AppendOutgoingTransition(tD);

            tF.AddIncomingPlace(pC5);
            tF.AddOutgoingPlace(pC1);
            tF.AddOutgoingPlace(pC2);

            //
            tA.AddIncomingPlace(pStart);
            tA.AddOutgoingPlace(pC1);
            tA.AddOutgoingPlace(pC2);

            pStart.AppendOutgoingTransition(tA);

            pEnd.AppendIncomingTransition(tG);
            pEnd.AppendIncomingTransition(tH);

            ////
            petriNet.Transitions.Add(tA);
            petriNet.Transitions.Add(tB);
            petriNet.Transitions.Add(tC);
            petriNet.Transitions.Add(tD);
            petriNet.Transitions.Add(tE);
            petriNet.Transitions.Add(tF);
            petriNet.Transitions.Add(tG);
            petriNet.Transitions.Add(tH);

            ////
            petriNet.Places.Add(pStart);
            petriNet.Places.Add(pC1);
            petriNet.Places.Add(pC2);
            petriNet.Places.Add(pC3);
            petriNet.Places.Add(pC4);
            petriNet.Places.Add(pC5);
            petriNet.Places.Add(pEnd);

            ComparingFootprintResultMatrix matrix = new ComparingFootprintResultMatrix(ComparingFootprintAlgorithm.CreateFootprint(eventLog), ComparingFootprintAlgorithm.CreateFootprint(petriNet));
            double fitness = ComparingFootprintAlgorithm.CalculateFitness(matrix.GetNumberOfDifferences(), matrix.GetNumberOfOpportunities());

            if (Math.Abs(fitness - 1.0) > 0.0001)
            {
                Assert.Fail("Fitness not correct! (" + fitness + ")");
            }
        }
        /// <summary>
        /// Depending on the count of followers this adds one (or more) places and their following transitions.
        /// </summary>
        /// <param name="event1"></param>
        /// <param name="node">An event node</param>
        /// <param name="log">The current event log</param>
        /// <param name="petriNet"></param>
        /// <returns>The newly added transitions. This is where you need to continue working.</returns>
        /// <exception cref="NotImplementedException">If there are more than 3 followers in a non-trivial relation.</exception>
        /// <author>Jannik Arndt</author>
        public List<Transition> HandleNode(Transition event1, EventNode node, EventLog log, PetriNet petriNet)
        {
            // Case: No followers
            if (node.ListOfFollowers.Count == 0)
                return new List<Transition>();

            // one or more more followers => count the AND-relations
            var andRelations = CountANDRelations(node, log);

            // Case: All nodes are AND-related
            if (andRelations == node.ListOfFollowers.Count)
                return StartAND(event1, node.ListOfFollowers, petriNet);

            // Case: All nodes are XOR-related
            if (andRelations == 0)
                return StartXOR(event1, node.ListOfFollowers, petriNet);

            // Case: 3 Followers
            if (node.ListOfFollowers.Count == 3)
            {
                var x = node;
                var a = node.ListOfFollowers[0];
                var b = node.ListOfFollowers[1];
                var c = node.ListOfFollowers[2];

                if (andRelations == 2) // XOR-Relations == 1
                {
                    // There are two and-relations and one xor-relation. Find the xor and order the parameters accordingly
                    if (IsXorRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                        return StartAand_BxorC(event1, a, b, c, petriNet);
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                        return StartAand_BxorC(event1, b, a, c, petriNet);
                    if (IsXorRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                        return StartAand_BxorC(event1, c, a, b, petriNet);
                }
                else // XOR-Relations == 2 && AND-Relations == 1
                {
                    // There are two xor-relations and one and-relation. Find the and and order the parameters accordingly
                    if (IsAndRelation(x.InnerEvent, b.InnerEvent, c.InnerEvent, log))
                        return StartAxor_BandC(event1, a, b, c, petriNet);
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, c.InnerEvent, log))
                        return StartAxor_BandC(event1, b, a, c, petriNet);
                    if (IsAndRelation(x.InnerEvent, a.InnerEvent, b.InnerEvent, log))
                        return StartAxor_BandC(event1, c, a, b, petriNet);
                }
            }
            if (node.ListOfFollowers.Count > 3)
                return StartXOR(event1, node.ListOfFollowers, petriNet);

            // optional transition
            if (node.ListOfFollowers.Count == 2)
                if (log.EventFollowsEvent(node.ListOfFollowers[0].InnerEvent, node.ListOfFollowers[1].InnerEvent) > 0)
                    return StartOptionalTransition(event1, node.ListOfFollowers[0].InnerEvent.Name, node.ListOfFollowers[1].InnerEvent.Name, petriNet);
                else if (log.EventFollowsEvent(node.ListOfFollowers[1].InnerEvent, node.ListOfFollowers[0].InnerEvent) > 0)
                    return StartOptionalTransition(event1, node.ListOfFollowers[1].InnerEvent.Name, node.ListOfFollowers[0].InnerEvent.Name, petriNet);

            return null;
        }
        /// <summary>
        /// Adds a place for each transition, then combines these places in the given transition.
        /// </summary>
        /// <param name="startingTransition">The starting transition</param>
        /// <param name="petriNet">The current petri net</param>
        /// <param name="endingTransition">The ending transition, if it exists already. Otherwise a new one is created></param>
        /// <returns>The transition where the places after the AND-related-events are connected.</returns>
        /// <author>Jannik Arndt</author>
        public List<Transition> EndAND(List<Transition> startingTransition, PetriNet petriNet, Transition endingTransition = null)
        {
            if (endingTransition == null)
                endingTransition = petriNet.AddTransition();

            if (_openParallelismCount.Count > 0)
                _openParallelismCount.Pop();

            var listOfClosingPlaces = new List<Place>();
            foreach (var transition in startingTransition)
            {
                var newPlace = petriNet.AddPlace();
                transition.AddOutgoingPlace(newPlace);
                listOfClosingPlaces.Add(newPlace);
            }
            endingTransition.AddIncomingPlaces(listOfClosingPlaces);
            return new List<Transition> { endingTransition };
        }
        /// <summary>
        /// This method is not finished yet
        /// 
        /// The method returns a list of shapes that are between two transitions
        /// </summary>
        /// <param name="t1">Transitionname</param>
        /// <param name="t2">Transitionname</param>
        /// <param name="conformanceCheckingCanvas"></param>
        /// <param name="petriNet">Petrinet</param>
        /// <autor>Andrej Albrecht</autor>
        private IEnumerable<Shape> GetArrowsBetweenTransitions(String t1, String t2, Canvas conformanceCheckingCanvas, PetriNet petriNet)
        {
            List<Place> listOfPlacesWithBadLines = GetListOfPlacesBetweenTwoTransitionsFromPetriNet(t1, t2, petriNet);
            List<Point2D> listWithPointsTo = GetListWithPointsToFromCanvas(t1, t2, conformanceCheckingCanvas);
            List<Point2D> listWithPointsFrom = GetListWithPointsFromCanvas(listOfPlacesWithBadLines, conformanceCheckingCanvas);
            List<Shape> listOfArrowsToBeMarked = GetListOfArrowsToBeMarked(conformanceCheckingCanvas, listWithPointsFrom, listWithPointsTo);

            List<Shape> listOfLinesToBeMarked = new List<Shape>();
            foreach (Object kind in conformanceCheckingCanvas.Children)
            {
                if (kind is Line)
                {
                    // Line KindP = (Line)Kind;
                    foreach (Shape shapeArrow in listOfArrowsToBeMarked)
                    {
                        if (shapeArrow is Arrow)
                        {
                            // Arrow Arrow = (Arrow)ShapeArrow;

                            //Place for the method who gets the lines and not only the arrows of the canvas:

                            //Started with the following algorithm that not worked now.
                            /*
                            //if (pointFrom.Equals(new Point2D(arrow.X1, arrow.Y1)) && pointTo.Equals(new Point2D(arrow.X2, arrow.Y2))
                            //    && KindP.X1==arrow.ArrowX1 && KindP.X2==arrow.ArrowX2 && KindP.Y1==arrow.ArrowY1 && KindP.Y2==arrow.ArrowY2)
                            if (arrow.ArrowX1 == KindP.X1 && arrow.ArrowY1 == KindP.Y1 && arrow.ArrowX2 == KindP.X1 && arrow.ArrowY2 == KindP.Y2)
                            {
                                System.Console.WriteLine("BadLine xFrom:" + arrow.ArrowX1 + " yFrom:" + arrow.ArrowY1 + " xTo:" + arrow.ArrowX2 + " yTo:" + arrow.ArrowY2 + " Name:" + arrow.Name);

                                listOfLinesToBeMarked.Add(KindP);
                            }
                            */
                        }
                    }
                }
            }
            return listOfArrowsToBeMarked.Union(listOfLinesToBeMarked).ToList();
        }