/// <summary> /// Sets the cell contents with a Formula. Throws CircularException if a circular dependency is established. /// </summary> /// <param name="name">Name of the cell to set contents inside of</param> /// <param name="formula">Formula to place in cell</param> /// <returns>ISet<string> of all the cells that need to be updated</string></returns> protected override ISet <string> SetCellContents(string name, SpreadsheetUtilities.Formula formula) { IEnumerable <string> variables = formula.GetVariables(); foreach (String variable in variables) { if (!IsValid(variable)) { throw new FormulaFormatException("Invalid cell name."); } cellDependency.AddDependency(Normalize(variable), name); } try { IEnumerable <string> cellsToRecalculate = GetCellsToRecalculate(name); } //Oops there's been a circular exception, restore the dependencies catch (CircularException) { foreach (String variable in variables) { cellDependency.RemoveDependency(Normalize(variable), name); } //Now that the dependencies are restored, we can throw the exception out throw new CircularException(); } //No exception, change the cell to the formula and evaluate the formula also //cells[name] = new Cell(formula, formula.Evaluate(s => (double)cells[s].value)); cells[name] = new Cell(formula, formula.Evaluate(VariableLookup)); return(new HashSet <string>(GetCellsToRecalculate(name))); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> /// <param name="name">cellname</param> /// <param name="formula">content is formula</param> /// <returns></returns> protected override ISet <string> SetCellContents(string name, SpreadsheetUtilities.Formula formula) { // check check check //if (ReferenceEquals(formula, null)) { throw new ArgumentNullException(); } //if (name == null) { throw new InvalidNameException(); } //if (!IsValid(name)) { throw new InvalidNameException(); } // initialized result and tempDependent HashSet <string> result = new HashSet <string>(); // store the name's Dependents in temporary hashset to prepare the circular dependency case HashSet <string> tempDependents = new HashSet <string>(DG.GetDependents(name)); // this try-catch statement will check circular dependency try { // input the empty hashset for dependent. if (sheet.ContainsKey(name)) { DG.ReplaceDependents(name, new HashSet <string>()); } // we have to return all veriables like set {A1, B1, C1} and store into DG foreach (String s in formula.GetVariables()) { DG.AddDependency(name, s); } //recalculate the cellname's contents result = new HashSet <string>(GetCellsToRecalculate(name)); } catch (CircularException e) { //just keep tracking previous dependents.. DG.ReplaceDependents(name, tempDependents); throw e; } // Now, set the sheet! if (sheet.ContainsKey(name)) { sheet.Remove(name); //make sure sheet[name] = new Cell(name, formula, lookup); } else { sheet.Add(name, new Cell(name, formula, lookup)); } //update if the cell has a formula! foreach (String s in result) { if (sheet.ContainsKey(s) && sheet[s].getContent() is Formula) { //For re-Evaluate Cell c = new Cell(s, sheet[s].getContent(), lookup); sheet.Remove(s); sheet.Add(s, c); }// exception handle?? } return(result); }
static void Main(string[] args) { String lpPattern = @"\("; String rpPattern = @"\)"; String opPattern = @"[\+\-*/]"; String varPattern = @"[a-zA-Z_](?: [a-zA-Z_]|\d)*"; //String doublePattern = @"(?: \d+\.\d* | \d*\.\d+ | \d+ ) (?: [eE][\+-]?\d+)?"; String doublePattern = @"^[0-9\.]*$"; String spacePattern = @"\s+"; // Overall pattern String pattern = String.Format("({0}) | ({1}) | ({2}) | ({3}) | ({4}) | ({5})", lpPattern, rpPattern, opPattern, varPattern, doublePattern, spacePattern); Formula f = new Formula("2.0 + x7 + b6+9- c"); String s = f.GetVariables().ToList().ToString(); Debug.WriteLine(s); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// contents of the named cell become a formula. /// /// The method returns a set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, Formula formula) { LinkedList<string> cellsToRecalc; cellsToRecalc = (LinkedList<string>)GetCellsToRecalculate(name); //Testing for CircularExceptions, does not affect cells so no need to backup old cell. foreach (string el in cellsToRecalc) { if (formula.GetVariables().Contains(el)) throw new CircularException(); } //Replacing cell name with new content and value. try { cells[name] = new Cell(name, formula, formula.Evaluate(lookup)); } catch (System.ArgumentException e) { cells[name] = new Cell(name, formula, new FormulaError()); } //spreadsheet has changed Changed = true; //recalculates the dependees of name. recalculateDependees(name, cellsToRecalc); //replace all dependents of name with new formulas variables. dependencies.ReplaceDependents(name, formula.GetVariables()); //grabs name and dependees(direct and indirect) of name and puts them into a HashSet and returns them. return new HashSet<string>(cellsToRecalc); }
/// <summary> /// Returns the formula with all the variables replaced with their normalized counterpart. /// </summary> /// <param name="f"></param> /// <returns></returns> protected Formula NormalizeFormula(Formula f) { IEnumerable<string> e = f.GetVariables(); foreach (string s in e) { f = new Formula(Regex.Replace(f.ToString(), s, Normalize(s))); } return f; }
public void Test53() { Formula f = new Formula("2+X1", s => "" + s + "_", s => true); HashSet<string> variables = new HashSet<string> { "X1_" }; Assert.IsTrue(variables.SetEquals(f.GetVariables())); }
public void Test49() { Formula f = new Formula("2*X2+X2"); List<string> actual = new List<string>(f.GetVariables()); HashSet<string> expected = new HashSet<string>() { "X2" }; Assert.AreEqual(actual.Count, 1); Assert.IsTrue(expected.SetEquals(actual)); }
/// <summary> /// Takes in a cell name and a formula and returns the dependents /// Calculates the value of the formula then recalculates the value of any cell that depends directly or inderectly on it. /// </summary> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, Formula formula) { name = Normalize(name); HashSet<string> mySet = new HashSet<string>(); HashSet<string> replaceSet = new HashSet<string>(); if (Object.ReferenceEquals(formula, null)) { throw new ArgumentNullException(); } if (Object.ReferenceEquals(name, null)) { throw new InvalidNameException(); } if (!Regex.IsMatch(name, "^([a-z]|[A-Z])+\\d+$") | !IsValid(name)) { throw new InvalidNameException(); } try { foreach (string s in formula.GetVariables()) { replaceSet.Add(s); } // dependentCells.ReplaceDependees(name, mySet); dependentCells.ReplaceDependees(name, replaceSet); GetCellsToRecalculate(name); } catch (CircularException) { throw new CircularException(); } Formula myEvl = new Formula(formula.ToString(), IsValid, Normalize); myCell = new Cell(formula, myEvl.Evaluate(myLookup)); if (mySpreadsheet.ContainsKey(name)) { mySpreadsheet[name] = myCell; } else { mySpreadsheet.Add(name, myCell); } mySet.Add(name); foreach (string s in GetCellsToRecalculate(name)) { mySet.Add(s); } foreach (string s in mySet) { myCell = new Cell(GetCellContents(s), myEvaluate(s)); mySpreadsheet[s] = myCell; } Changed = true; return mySet; }
public void GetVariables3() { Formula testFormula = new Formula("(3+x)*y", s => s.ToUpper(), s => (Regex.IsMatch(s, @"[A-Z]"))); foreach (string s in testFormula.GetVariables()) Assert.IsTrue(Regex.IsMatch(s, @"[XY]")); }
public void PublicTestGetVariables2() { List<string> expected = new List<string>(); expected.Add("X"); expected.Add("Y"); Formula f1 = new Formula("X + x + Y", s => s.ToUpper(), s => true); List<string> actual = new List<string>(f1.GetVariables()); CollectionAssert.AreEqual(expected, actual); }
// MODIFIED PROTECTION FOR PS5 /// <summary> /// If formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { if (formula == null) throw new ArgumentException(); CheckValidCellName(name); bool originalChangedState = Changed; List<string> origDeps = dependencies.GetDependees(name).ToList(); Dictionary<string, Cell> origCells = new Dictionary<string, Cell>(); for (int i = 0; i < cells.Count; i++) { origCells.Add(cells.ElementAt(i).Key, cells.ElementAt(i).Value.Clone()); } Cell getCell = new Cell(); if (cells.TryGetValue(name, out getCell)) { cells[name].Content = formula; cells[name].Value = null; } else { cells.Add(name, new Cell(name, formula, null)); } dependencies.ReplaceDependees(name, formula.GetVariables()); try { Changed = true; return new HashSet<string>(GetCellsToRecalculate(name)); } catch (CircularException) { Changed = originalChangedState; dependencies.ReplaceDependees(name, origDeps); cells = origCells; throw new CircularException(); } }
/// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes number. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> public override ISet<string> SetCellContents(string name, double number) { if (!Cell.validName(name)) { throw new InvalidNameException(); } HashSet<string> dependents = new HashSet<string>(); List<string> dependentCells = GetCellsToRecalculate(name).ToList(); // If cell doesn't exist, add it if (!allCells.ContainsKey(name)) { allCells.Add(name, new Cell(name, number)); foreach (string s in dependentCells) { dependents.Add(s); } dependents.Add(name); return dependents; } // If cell exists, overwrite it Cell temp; if (allCells.TryGetValue(name, out temp)) { string content = temp.getContents().ToString(); temp.setContents(number); // remove dependencies, if they exist Formula f = new Formula(content); foreach (string s in f.GetVariables()) { if (s.Equals(name)) { graph.RemoveDependency(name, s); } } } // Get all dependents, indirect and direct, and then add them to a List which is then // added to the returned HashSet dependents.Add(name); foreach (string s in dependentCells) { dependents.Add(s); } return dependents; }
/// <summary> /// sets the cell, name, to contain a specified formula /// </summary> /// <remarks> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// circ dependency example: /// when setting b1 to a1 /// a1 = b1 DG has pair (b1,a1) /// b1 = a1 DG can't be allowed to add(a1,b1) /// </remarks> /// <exception cref="ArgumentNullException"> If the formula parameter is null, throws an ArgumentNullException.</exception> /// <exception cref="InvalidNameException"> Otherwise, if name is null or invalid, throws an InvalidNameException.</exception> /// <exception cref="CircularException"> /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// </exception> /// <returns> /// The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// </returns> protected override ISet<string> SetCellContents(string name, Formula formula) { //If the formula parameter is null, throws an ArgumentNullExceptio if (formula == null) { throw new ArgumentNullException(); } //If name is null or invalid, throws an InvalidNameException if (name == null || !Regex.IsMatch(name, @"[a-zA-Z]+\d+")) { throw new InvalidNameException(); } IEnumerable<string> storedDents = DG.GetDependents(name); DG.ReplaceDependents(name, new HashSet<string>()); foreach (string var in formula.GetVariables()) { try { DG.AddDependency(name, var); } catch (InvalidOperationException) { DG.ReplaceDependents(name, storedDents); throw new CircularException(); } } if (Sheet.ContainsKey(name)) Sheet[name].Contents = formula; else Sheet.Add(name, new Cell(name, formula,lookerupper)); foreach (string nombre in GetCellsToRecalculate(name)) { //contents setter recalculates value. Sheet[nombre].Contents = Sheet[nombre].Contents; } Changed = true; HashSet<string> toreturn = new HashSet<string>(GetCellsToRecalculate(name)); toreturn.Add(name); return toreturn; }
/// <summary> /// Sets the contents of the cell. Determines whether the /// contents are a string, double, or formula and sets each /// appropriately /// </summary> /// <param name="name">Name of cell whose contents are to be set</param> /// <param name="content"></param> /// <returns></returns> public override ISet<string> SetContentsOfCell(string name, string content) { if (content == null) throw new ArgumentNullException(); if (name == null || !IsValid(name)) throw new InvalidNameException(); name = removeNewLines(name); content = removeNewLines(content); name = Normalize(name); if(IsValid(name)) { double doubleValue = 0.0; bool isDouble = double.TryParse(content, out doubleValue); HashSet<string> cellsToRecalc; if (isDouble) cellsToRecalc = (HashSet<string>)SetCellContents(name, doubleValue); else if (Regex.IsMatch(content, @"[\+\-\*\/=]")) { char[] splitFormula = content.ToCharArray(); string newFormula = ""; for (int i = 0; i < splitFormula.Length; i++) { if (splitFormula[i] != '=') newFormula += splitFormula[i]; } newFormula = Normalize(newFormula); Formula f = new Formula(newFormula); foreach (string s in f.GetVariables()) { if (!IsValid(s)) throw new FormulaFormatException("One or more variables is not valid"); } cellsToRecalc = (HashSet<string>)SetCellContents(name, f); } else cellsToRecalc = (HashSet<string>)SetCellContents(name, content); return cellsToRecalc; } else throw new InvalidNameException(); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { //check if name is valid IsValidInput(name, formula); //Save state of old dependency graph SSCell oldCell = null; IEnumerable<string> oldDependees = new HashSet<string>(); if (nameContentDict.TryGetValue(name, out oldCell)) { oldDependees = depenGraph.GetDependees(oldCell.Name); } depenGraph.ReplaceDependees(name, formula.GetVariables()); //check for circular dependiencies try { //check for circular dependeinces ISet<string> returnSet = GetAllDepdendents(name); // if we get here no circular dependency was found AddToGraphAndDict(name, formula); //reevaluate all dependents foreach (string cellName in returnSet) { nameContentDict[cellName].ReEvaluate(Lookup); } return returnSet; } catch (CircularException) { //restore dependecy graph depenGraph.ReplaceDependees(name, oldDependees); throw new CircularException(); } }
// ADDED FOR PS5 /// <summary> /// If content is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if content parses as a double, the contents of the named /// cell becomes that double. /// /// Otherwise, if content begins with the character '=', an attempt is made /// to parse the remainder of content into a Formula f using the Formula /// constructor. There are then three possibilities: /// /// (1) If the remainder of content cannot be parsed into a Formula, a /// SpreadsheetUtilities.FormulaFormatException is thrown. /// /// (2) Otherwise, if changing the contents of the named cell to be f /// would cause a circular dependency, a CircularException is thrown. /// /// (3) Otherwise, the contents of the named cell becomes f. /// /// Otherwise, the contents of the named cell becomes content. /// /// If an exception is not thrown, the method returns a set consisting of /// name plus the names of all other cells whose value depends, directly /// or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> public override ISet<String> SetContentsOfCell(String name, String content) { //spreadsheet has been changed Changed = true; //check input and Normalize name string normalizedName = IsValidInput(name, content); if (content.Equals("")) { //if an empty string is recieved the graph must be updated if (nameContentDict.ContainsKey(name)) { //any cell that dependends on this is know a formula error foreach(string cell in GetAllDepdendents(name)) { nameContentDict[cell].Value = new FormulaError(); } depenGraph.RemoveDependency(name, content); nameContentDict.Remove(name); } return new HashSet<string>(); } //see if content is a double, if so get value return double d; if (double.TryParse(content, out d)) { //add to dependen graph and name, cell to the Dcitonary, return dependents return SetCellContents(normalizedName, d); } //if is it a formula else if (content[0] == '=') { //get the rest of the string after the "="//double check off by one error here string formulaString = content.Substring(1, content.Length - 1); Formula formula; try { formula = new Formula(formulaString, Normalize, IsValid); //PS3 has different requirments for variables so check new standards here foreach( string str in formula.GetVariables()) IsValidName(str); //add to dependen graph and name, cell to the Dcitonary, return dependents return SetCellContents(normalizedName, formula); } //catch the invalid name exception catch (InvalidNameException) { throw new FormulaFormatException("bad formula"); } } else //it is a string, add to dependen graph and name, cell to the Dcitonary, return dependents return SetCellContents(normalizedName, content); }
/// <summary> /// If name is null, throws an ArgumentNullException. /// /// Otherwise, if name isn't a valid cell name, throws an InvalidNameException. /// /// Otherwise, returns an enumeration, without duplicates, of the names of all cells whose /// values depend directly on the value of the named cell. In other words, returns /// an enumeration, without duplicates, of the names of all cells that contain /// formulas containing name. /// /// For example, suppose that /// A1 contains 3 /// B1 contains the formula A1 * A1 /// C1 contains the formula B1 + A1 /// D1 contains the formula B1 - C1 /// The direct dependents of A1 are B1 and C1 /// </summary> protected override IEnumerable<string> GetDirectDependents(string name) { if (name == null) { throw new ArgumentNullException(); } if (!Cell.validName(name)) { throw new InvalidNameException(); } // Loop over every cell in spreadsheet, checking for dependents of name by looking for // cells that contain name HashSet<string> dependents = new HashSet<string>(); foreach (KeyValuePair<string, Cell> kvp in allCells) { // Turn name into a string, convert to a formula and call get variables on the formula // if there are variables in the formula (the contents of the cell) and one of those // variables is name, add the name of the cell that contains name to the dependents list if (kvp.Value.getContents() is Formula) { string content = kvp.Value.getContents().ToString(); Formula f = new Formula(content, Normalize, IsValid); foreach (string str in f.GetVariables()) { if (str.Equals(name)) { dependents.Add(kvp.Key); } } } } return dependents; }
/// <summary> /// Verifies that no circular exception will be created and then sends CHANGE request to server. /// </summary> /// <param name="name">The cell to be changed</param> /// <param name="content">The candidate contents of the cell</param> public void Change(string name, string content) { // If we're waiting for a change to be confirmed by the server, don't take a new change. if (_currentChangeCell != null) { return; } var normalizedName = Normalize(name); // Check if content is null. if (content == null) { throw new ArgumentNullException(); } // Check if name is null or invalid. if (normalizedName == null || !_validCellNameRegex.IsMatch(normalizedName) || !IsValid(normalizedName)) { throw new InvalidNameException(); } if (content.StartsWith("=")) { Formula formula = new Formula(content.Substring(1), IsValid, Normalize); // The HashSet dependents contains name and all of name's direct and indirect dependents. var dependents = new HashSet<string>(GetCellsToRecalculate(normalizedName)); // Variables contains name's new dependees. var variables = formula.GetVariables(); // Checking if any of name's new dependees are already its dependents // or if name is its own new dependee. if (dependents.Overlaps(variables)) { throw new CircularException(); } } _currentChangeCell = normalizedName; _currentChangeContent = content; _socket.BeginSend("CHANGE\n" + _name + "\n" + _version + "\nCell:" + normalizedName + "\nLength:" + content.Length.ToString() + "\n" + content + "\n", (e, o) => { }, null); }
public void GetVariables1() { Formula testFormula = new Formula("(3+x)*y"); foreach (string s in testFormula.GetVariables()) Assert.IsTrue(Regex.IsMatch(s, @"[xy]")); }
/// <summary> /// If formula parameter is null, throws an ArgumentNullException. /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { // Normaling name. var normalizedName = Normalize(name); if (formula == null) { throw new ArgumentNullException(); } if (normalizedName == null || !_validCellNameRegex.IsMatch(normalizedName) || !IsValid(normalizedName)) { throw new InvalidNameException(); } var cellsToRecalculate = GetCellsToRecalculate(normalizedName); var localCellsToRecalculate = cellsToRecalculate as string[] ?? cellsToRecalculate.ToArray(); _cells[normalizedName] = new Cell(formula); // Replacing name's dependees. _dependencies.ReplaceDependees(normalizedName, formula.GetVariables()); // Updating values (in order) of each affected cell. RecalculateCellValues(localCellsToRecalculate); Changed = true; return new HashSet<string>(localCellsToRecalculate); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { IEnumerable<String> dependee_list = dependency_graph.GetDependees(name); dependency_graph.ReplaceDependees(name, formula.GetVariables()); try { HashSet<String> all_dependees = new HashSet<String>(GetCellsToRecalculate(name)); if (cell.ContainsKey(name)) { cell[name] = new Cell(formula, LookupValue); } else { cell.Add(name, new Cell(formula, LookupValue)); } return all_dependees; } catch (CircularException) { dependency_graph.ReplaceDependees(name, dependee_list); throw new CircularException(); } }
public void GetVariables2() { Formula testFormula = new Formula("(3+x)*y+x"); int i = 0; foreach (string s in testFormula.GetVariables()){ i++; Assert.IsTrue(Regex.IsMatch(s, @"[xy]")); } Assert.AreEqual(2, i); }
public void GetVariablesTest2() { Formula f1 = new Formula("X+y+z+2"); foreach (string var in f1.GetVariables()) { Assert.IsTrue(Regex.IsMatch(var, @"[Xyz]")); } }
public void GetVariables4() { Formula testFormula = new Formula("a1+b2*c3+d4/A2-Z7+a1/y8"); List<string> variables = new List<string>(testFormula.GetVariables()); HashSet<string> expected = new HashSet<string>() { "a1", "b2", "c3", "d4", "A2", "Z7", "y8" }; Assert.AreEqual(variables.Count, 7); Assert.IsTrue(expected.SetEquals(variables)); }
public void GetVariablesTest3() { Formula f1 = new Formula("X+y+z+2",s=>s.ToUpper(),s=>(Regex.IsMatch(s,@"[A-Z]"))); foreach (string var in f1.GetVariables()) { Assert.IsTrue(Regex.IsMatch(var, @"[XYZ]")); } }
public void Test46() { Formula f = new Formula("2*5"); Assert.IsFalse(f.GetVariables().GetEnumerator().MoveNext()); }
public void GetVariablesTest4() { Formula f1 = new Formula("X+y+z+X+X"); int i=0; foreach (string var in f1.GetVariables()) { i++; Assert.IsTrue(Regex.IsMatch(var, @"[Xyz]")); } Assert.AreEqual(3, i); }
public void Test50() { Formula f = new Formula("X1+Y2*X3*Y2+Z7+X1/Z8"); List<string> actual = new List<string>(f.GetVariables()); HashSet<string> expected = new HashSet<string>() { "X1", "Y2", "X3", "Z7", "Z8" }; Assert.AreEqual(actual.Count, 5); Assert.IsTrue(expected.SetEquals(actual)); }
/// <summary> /// If text is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes text. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, string text) { if (text == null) { throw new ArgumentNullException(); } if (!Cell.validName(name) || !IsValid(name)) { throw new InvalidNameException(); } // If cell doesn't exist, add it if (!allCells.ContainsKey(name)) { allCells.Add(name, new Cell(name, text)); isChanged = true; // Get all dependents, indirect and direct, and then add them to a List which is then // added to the returned HashSet HashSet<string> dependents = new HashSet<string>(); List<string> dependentCells = GetCellsToRecalculate(name).ToList(); dependents.Add(name); foreach (string str in dependentCells) { allCells[str].eval(Lookup); dependents.Add(str); } return dependents; } // If cell exists, overwrite it's content Cell temp; if (allCells.TryGetValue(name, out temp)) { string content = temp.getContents().ToString(); temp.setContents(text); Formula f = new Formula(content); foreach (string str in f.GetVariables()) { if (str.Equals(name)) { graph.RemoveDependency(name, str); } } } // Get all dependents, indirect and direct, and then add them to a List which is then // added to the returned HashSet HashSet<string> dependent = new HashSet<string>(); List<string> dependentCell = GetCellsToRecalculate(name).ToList(); dependent.Add(name); foreach (string str in dependentCell) { allCells[str].eval(Lookup); dependent.Add(str); } return dependent; }
/// <summary> /// An implementation of the abstract method in AbstractSpreadsheet. /// <seealso cref="AbstractSpreadsheet.SetContentsOfCell"/> /// </summary> /// <param name="name"></param> /// <param name="content"></param> /// <returns></returns> public override ISet<string> SetContentsOfCell(string name, string content) { // Private Variable ISet<string> set; // If content is null, throws an ArgumentNullException. // Otherwise, if name is null or invalid, throws an InvalidNameException. name = CorrectInput(name, content); // Otherwise, if content parses as a double, the contents of the named // cell becomes that double. double d; if (Double.TryParse(content, out d)) set = SetCellContents(name, d); // Otherwise, if content begins with the character '=', an attempt is made // to parse the remainder of content into a Formula f using the Formula // constructor. else if (content.Length > 0 && content[0] == '=') { // There are then three possibilities: // (1) If the remainder of content cannot be parsed into a Formula, a // SpreadsheetUtilities.FormulaFormatException is thrown. // Be sure to check the validity of and normalize any variables. // (2) Otherwise, if changing the contents of the named cell to be f // would cause a circular dependency, a CircularException is thrown. Formula f = new Formula(content.Substring(1)); f = NormalizeFormula(f); try { foreach (string v in f.GetVariables()) CorrectInput(v); } catch (InvalidNameException) { throw new FormulaFormatException( String.Format("One or more variables in the formula '{0}' contained in cell {1} aren't valid.", f.ToString(), name)); } // (3) Otherwise, the contents of the named cell becomes f. set = SetCellContents(name, f); } // Otherwise, the contents of the named cell becomes content. else set = SetCellContents(name, content); // Recalculate the values of any cell dependent on the named cell, including the named cell itself. CalculateCellValues(name); //Remove any name associations to cell, reset to how it was before the cell was added. if (content == "") { cells.Remove(name); } // If an exception is not thrown, the method returns a set consisting of // name plus the names of all other cells whose value depends, directly // or indirectly, on the named cell. // For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the // set {A1, B1, C1} is returned. return set; }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { // If the formula parameter is null, throws an ArgumentNullException. if (formula.Equals(null)) { throw new ArgumentNullException(); } // Otherwise, if name is null or invalid, throws an InvalidNameException. if (!Cell.validName(name) || !IsValid(name)) { throw new InvalidNameException(); } // If cell doesn't exist, add it if (!allCells.ContainsKey(name)) { // add new cell allCells.Add(name, new Cell(name, formula, Lookup)); // if formula contained variables, setup new dependencies if (formula.GetVariables().Count() > 0) { foreach (string str in formula.GetVariables()) { graph.AddDependency(name, str); } } HashSet<string> dependents = new HashSet<string>(); List<string> dependentCells = GetCellsToRecalculate(name).ToList(); dependents.Add(name); foreach (string str in dependentCells) { allCells[str].eval(Lookup); dependents.Add(str); } return dependents; } // Otherwise, if changing the contents of the named cell to be the formula would cause a // circular dependency, throws a CircularException. (No change is made to the spreadsheet.) GetCellsToRecalculate(name); // Otherwise, the contents of the named cell becomes formula. // If cell exists, overwrite it's content Cell temp; if (allCells.TryGetValue(name, out temp)) { temp.setContents(formula); } // If the replacement formula has variables, replace the dependency of the old cell with // new ones from the new formula. if (formula.GetVariables().Count() > 0) { List<string> variables = new List<string>(); foreach (string str in formula.GetVariables()) { variables.Add(str); } graph.ReplaceDependents(name, variables); } // Get all dependents, indirect and direct, and then add them to a List which is then // added to the returned HashSet HashSet<string> dependent = new HashSet<string>(); List<string> dependentCell = GetCellsToRecalculate(name).ToList(); dependent.Add(name); foreach (string str in dependentCell) { allCells[str].eval(Lookup); dependent.Add(str); } return dependent; }
/// <summary> /// An implementation of the abstract method in AbstractSpreadsheet. /// <seealso cref="AbstractSpreadsheet.SetCellContents(String, Formula)"/> /// </summary> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet<String> SetCellContents(String name, Formula formula) { // If formula parameter is null, throws an ArgumentNullException. // Otherwise, if name is null or invalid, throws an InvalidNameException. name = CorrectInput(name, formula); formula = NormalizeFormula(formula); // Otherwise, if changing the contents of the named cell to be the formula would cause a // circular dependency, throws a CircularException. IEnumerable<string> previous = dependencies.GetDependees(name); try { dependencies.ReplaceDependees(name, formula.GetVariables()); GetCellsToRecalculate(name); } catch (CircularException) { dependencies.ReplaceDependees(name, previous); throw new CircularException(); }; //foreach (string v in formula.GetVariables()) // foreach (string d in GetCellsToRecalculate(v)) // if (name.Equals(d)) // { // dependencies.ReplaceDependees(name, previous); // throw new CircularException(); // } // Otherwise, the contents of the named cell becomes formula. Changed = true; GetCell(name).Contents = formula; // The method returns a Set consisting of name plus the names of all other // cells whose value depends, directly or indirectly, on the named cell. return new HashSet<string>(GetCellsToRecalculate(name)); }
/// <summary> /// If formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet <string> SetCellContents(string cell_name, SpreadsheetUtilities.Formula formula) { IEnumerable <string> backup_variables = null; object backup_contents = null; bool had_name = false; string name = Normalize(cell_name); //if the formula passed in is null this throws an ArgumentNullException(); if (formula == null) { throw new ArgumentNullException("Formula cannot be null"); } //checks if the name is null or invalid if so throws a new InvalidNameException(); if (name == null || IsValid(name) == false) { throw new InvalidNameException(); } //checks if the cell with the name has been set up, if so this sets the contents of the cell to the formula passed in. if (spreadsheet.ContainsKey(name)) { backup_variables = dependency_graph.GetDependees(name); backup_contents = spreadsheet[name].get_contents(); spreadsheet[name].set_contents(formula); had_name = true; } //if the cell hasn't been set up this creates a new cell and sets its contents to the text passed in. else { backup_contents = " "; backup_variables = new HashSet <string> { }; spreadsheet.Add(name, new Cell(name, formula)); } IEnumerable <string> variables = formula.GetVariables(); //Gets all the variables dependency_graph.ReplaceDependees(name, variables); //Replace the dependees of the name with all the variables in the formula passed in. //creates a hash set for the return ISet. ISet <string> depends = new HashSet <string> { }; //looks through each item in the dependency graph and add them to the hash set to return. try { IEnumerable <string> recalculate_cells = GetCellsToRecalculate(name); foreach (string item in recalculate_cells) { depends.Add(item); } } catch (CircularException) { spreadsheet[name].set_contents(backup_contents); dependency_graph.ReplaceDependees(name, backup_variables); throw new CircularException(); } //sets the value of the cell IEnumerable <string> evaluate_cells = GetCellsToRecalculate(name); foreach (string cell in evaluate_cells) { Formula value_of_formula = (Formula)GetCellContents(cell); try { spreadsheet[cell].set_value(value_of_formula.Evaluate(Lookup)); } catch (ArgumentException) { } } //returns the hash set with all the dependents of name. return(depends); }