public static Tuple <int, List <string>, string> FeedbackForWhileToTM <S>(WExpr correctProgram, TMCB <int, S> attemptTM, int maxGrade) { //returns grade, feedback strings, and a sample input for the tape simulator TMCB <int, int> correctTM = correctProgram.ToTMCB(-1); int numTapes = correctProgram.GetNumVariables(); const int numInputs = 100; const int maxWrongOutpus = 5; //max number of wrong outputs the user is shown. //<correctTM> and <attemptTM> are compared by running both on a set of test inputs and comparing the outputs. int inputsPerTape = (int)Math.Pow(numInputs, (double)1 / numTapes); List <string> wrongOutputList = new List <string>(); string sampleInput = ""; int countCorrectOutputs = 0; int countWrongOutpus = 0; bool dummy; foreach (int[][] input in WhileUtilities.NonNegIntTestInputs(numTapes, inputsPerTape, correctProgram.GetUselessVariables().ToArray())) { int[][] correctOutput = correctTM.Run(input, out dummy); //<correctTM> uses LSBF encoding, <attemptTM> uses MSBF encoding. Reverse <input> for <attemptTM>. for (int i = 0; i < input.Length; ++i) { input[i] = input[i].Reverse().ToArray(); } int[][] attemptOutput = attemptTM.Run(input, out dummy); //for comparing the outputs, reverse <correctOutput> for (int i = 0; i < correctOutput.Length; ++i) { correctOutput[i] = correctOutput[i].Reverse().ToArray(); } //get string representations of the outputs string[] correctStrings = Array.ConvertAll(correctOutput, tape => WhileUtilities.TapeToString(tape, correctTM.blank, new[] { 0 }, "_")); string[] attemptStrings = Array.ConvertAll(attemptOutput, tape => WhileUtilities.TapeToString(tape, attemptTM.blank, new[] { 0 }, "_")); if (WhileUtilities.TapesEqual(correctStrings, attemptStrings)) { ++countCorrectOutputs; } else { if (countWrongOutpus == 0) { //first wrong output: corresponding input is stored in <sampleInput> string[] tapes = Array.ConvertAll(input, tape => WhileUtilities.TapeToString(tape, correctTM.blank, new[] { 0 }, "_")); sampleInput = string.Join(",", tapes); } ++countWrongOutpus; if (countWrongOutpus <= maxWrongOutpus) { wrongOutputList.Add(string.Empty); wrongOutputList.Add($"Input #{countWrongOutpus}:"); for (int i = 0; i < input.Length; ++i) { wrongOutputList.Add($"tape {i}: {WhileUtilities.TapeToString(input[i], correctTM.blank, null)}"); } wrongOutputList.Add("Your output:"); for (int i = 0; i < attemptStrings.Length; ++i) { wrongOutputList.Add($"tape {i}: {attemptStrings[i]}"); } wrongOutputList.Add("Correct output:"); for (int i = 0; i < correctStrings.Length; ++i) { wrongOutputList.Add($"tape {i}: {correctStrings[i]}"); } } } } double actualInputs = Math.Pow(inputsPerTape, numTapes); int grade = (int)((countCorrectOutputs / actualInputs) * maxGrade); List <string> feedbackList = new List <string>(); if (countWrongOutpus == 0) { feedbackList.Add("Correct!"); } else { if (countWrongOutpus <= maxWrongOutpus) { feedbackList.Add($"{countWrongOutpus} of {actualInputs} outpus were wrong:"); } else { feedbackList.Add($"{countWrongOutpus} of {actualInputs} outpus were wrong. Here are {maxWrongOutpus} of them:"); } foreach (string line in wrongOutputList) { feedbackList.Add(line); } } return(new Tuple <int, List <string>, string>(grade, feedbackList, sampleInput)); }
//public static TMCB<char, string> ParseTMFromXML(XElement automaton_wrapped) //{ // var Q = new HashSet<State<string>>(); // var F = new HashSet<State<string>>(); // State<string> q0 = null; // var Sigma = new HashSet<char>(); // var Gamma = new HashSet<char>(); // char blank = '?'; // //var Delta = new Dictionary<TwoTuple<State<string>, string>, ThreeTuple<State<string>, string, string>>(); // var IdStateDict = new Dictionary<int, State<string>>(); // XElement automaton = XElement.Parse(RemoveAllNamespaces(automaton_wrapped.ToString())); // //Parse Q: // XElement stateSet = automaton.Element("stateSet"); // foreach (XElement child in stateSet.Elements("state")) // { // try // { // int id = Int32.Parse(child.Element("id").Value); // string label = child.Element("label").Value; // var q = new State<string>(id, label); // Q.Add(q); // IdStateDict.Add(id, q); // } // catch (NullReferenceException e) // { // int x = 0; // } // } // //Parse F: // XElement acceptingSet = automaton.Element("acceptingSet"); // foreach (XElement child in acceptingSet.Elements("state")) // { // State<string> q; // int id = (int)child.Attribute("sid"); // if (!IdStateDict.TryGetValue(id, out q)) // { // //TODO: Throw even more exceptions // } // F.Add(q); // } // //Parse Sigma and Gamma: // XElement alphabet = automaton.Element("alphabet"); // foreach (XElement child in alphabet.Elements("symbol")) // { // char symbol = Convert.ToChar(child.Value); // Gamma.Add(symbol); // if (symbol != blank) // Sigma.Add(Convert.ToChar(child.Value)); // } // TMCB<char, string> rv = new TMCB<char, string>(Q, F, Sigma, Gamma, blank); // //Parse q0: // //TODO: exception for multiple initial states // XElement initState = automaton.Element("initState"); // foreach (XElement child in initState.Elements("state")) // { // int id = (int)child.Attribute("sid"); // if (!IdStateDict.TryGetValue(id, out q0)) // { // //TODO: throw exception // } // else // { // rv.q0 = q0; // } // } // //Parse Delta: // XElement transitionSet = automaton.Element("transitionSet"); // foreach (XElement child in transitionSet.Elements("transition")) // { // int from_id = Int32.Parse(child.Element("from").Value); // int to_id = Int32.Parse(child.Element("to").Value); // string read = Convert.ToString(child.Element("read").Value); // string write = Convert.ToString(child.Element("write").Value); // string head = Convert.ToString(child.Element("head").Value); // State<string> q1, q2; // if (!IdStateDict.TryGetValue(from_id, out q1) | !IdStateDict.TryGetValue(to_id, out q2)) // { // //TODO: throw exception // } // else // { // //only works if read, write and head have the same lengths // Transition<char, string> t = new Transition<char, string>(); // for (int i = 0; i < read.Length; ++i) // { // char readSymbol = read[i]; // char writeSymbol = write[i]; // char headSymbol = head[i]; // t.AddReadCondition(i, read[i]); // //only add write action if the symbol actually gets changed // //only add move action if the head actually moves // if (readSymbol != writeSymbol) // { // t.AddWriteAction(i, write[i]); // } // if (headSymbol == 'R') // { // t.AddMoveAction(i, Directions.Right); // } // if (headSymbol == 'L') // { // t.AddMoveAction(i, Directions.Left); // } // } // //only set target state if it is different from the source state // if (q1 != q2) // { // t.TargetState = q2; // } // rv.AddTransition(q1, t); // } // } // return rv; //} #endregion //not yet tested at all #region <int, string> public static TMCB <int, string> ParseTMFromXML(XElement automaton_wrapped) { var Q = new HashSet <State <string> >(); var F = new HashSet <State <string> >(); State <string> q0 = null; var Sigma = new HashSet <int>(); var Gamma = new HashSet <int>(); int blank = -1; //var Delta = new Dictionary<TwoTuple<State<string>, string>, ThreeTuple<State<string>, string, string>>(); var IdStateDict = new Dictionary <int, State <string> >(); XElement automaton = XElement.Parse(RemoveAllNamespaces(automaton_wrapped.ToString())); //Parse Q: XElement stateSet = automaton.Element("stateSet"); foreach (XElement child in stateSet.Elements("state")) { try { int id = Int32.Parse(child.Element("id").Value); string label = child.Element("label").Value; var q = new State <string>(id, label); Q.Add(q); IdStateDict.Add(id, q); } catch (NullReferenceException e) { int x = 0; } } //Parse F: XElement acceptingSet = automaton.Element("acceptingSet"); foreach (XElement child in acceptingSet.Elements("state")) { State <string> q; int id = (int)child.Attribute("sid"); if (!IdStateDict.TryGetValue(id, out q)) { //TODO: Throw even more exceptions } F.Add(q); } //Parse Sigma and Gamma: XElement alphabet = automaton.Element("alphabet"); foreach (XElement child in alphabet.Elements("symbol")) { char symbol = Convert.ToChar(child.Value); int intSymbol; if (!charToInt(symbol, blank, out intSymbol)) { //TODO: exception } Gamma.Add(intSymbol); if (intSymbol != blank) { Sigma.Add(intSymbol); } } TMCB <int, string> rv = new TMCB <int, string>(Q, F, Sigma, Gamma, blank); //Parse q0: //TODO: exception for multiple initial states XElement initState = automaton.Element("initState"); foreach (XElement child in initState.Elements("state")) { int id = (int)child.Attribute("sid"); if (!IdStateDict.TryGetValue(id, out q0)) { //TODO: throw exception } else { rv.q0 = q0; } } //Parse Delta: XElement transitionSet = automaton.Element("transitionSet"); foreach (XElement child in transitionSet.Elements("transition")) { int from_id = Int32.Parse(child.Element("from").Value); int to_id = Int32.Parse(child.Element("to").Value); string read = Convert.ToString(child.Element("read").Value); string write = Convert.ToString(child.Element("write").Value); string head = Convert.ToString(child.Element("head").Value); State <string> q1, q2; if (!IdStateDict.TryGetValue(from_id, out q1) | !IdStateDict.TryGetValue(to_id, out q2)) { //TODO: throw exception } else { //only works if read, write and head have the same lengths Transition <int, string> t = new Transition <int, string>(); for (int i = 0; i < read.Length; ++i) { int readSymbol; if (!charToInt(read[i], blank, out readSymbol)) { //TODO: exception } int writeSymbol; if (!charToInt(write[i], blank, out writeSymbol)) { //TODO: exception } char headSymbol = head[i]; t.AddReadCondition(i, readSymbol); //only add write action if the symbol actually gets changed //only add move action if the head actually moves if (readSymbol != writeSymbol) { t.AddWriteAction(i, writeSymbol); } if (headSymbol == 'R') { t.AddMoveAction(i, Directions.Right); } if (headSymbol == 'L') { t.AddMoveAction(i, Directions.Left); } } //only set target state if it is different from the source state if (q1 != q2) { t.TargetState = q2; } rv.AddTransition(q1, t); } } return(rv); }