Related
I have to find parser for solving equation with unknows. User enters two numbers and the parser parses equation to solve. For example:
Equation:
x^3 + y^2 + sin60°
User input:
x=4, y=2
It have to be done using AForge.NET framework. Console app/WinForms
I have been looking for it in AForge .NET documentation but can't find something matches my problem.
I don't believe there is an evaluator for regular mathematical expressions in AForge, but you could use the PolishExpression class to evaluate an expression written in reverse polish notation:
using AForge;
string expression = "$0 $0 $0 * * $1 $1 * + " + (Math.PI / 3) + " sin +";
// variables for the expression
double x = 4;
double y = 2;
double[] vars = new double[] { x, y };
// expression evaluation
double result = PolishExpression.Evaluate(expression, vars);
Console.WriteLine("Polish expression evaluation: " + result);
This yields the value 68.86602540378443. There's also no direct exponential operator, so I just multiplied x by itself three times. But you could use logaritms instead:
string expression = "$0 ln 3 * exp $1 ln 2 * exp + " + (Math.PI / 3) + " sin +";
You can also confirm that this is the same as you'd get in C# (note that you must convert 60 degrees to radians in order to use Math.sin):
double TestEvaluate(double x, double y)
{
// x^3 + y^2 + sin60°
// Note that Math.sin accepts radians, so we must convert 60°
return Math.Pow(x, 3) + Math.Pow(y, 2) + Math.Sin((Math.PI / 180) * 60);
}
Console.WriteLine("C# evalution: " + TestEvaluate(x, y));
But yeah, I'd recommend using another library if you don't want to use polish notation in your expressions.
I also tried the code example above with "AForge.Math.Expression", but I don't believe this class exists in AForge.Math 2.2.5.
Solution
It seems you need an expression parser. I decided to write one, that uses System.Linq.Expression for building expression trees. I started with the simple expression parser from https://github.com/toptensoftware/SimpleExpressionEngine and build from there.
Example Code
First the fun stuff. Look at this test code
public static void TestSOExpr()
{
string expression = "x^3 + y^2 + sin(60°)";
Parser parser = new Parser(expression);
Console.WriteLine("Expression: ");
Console.WriteLine(parser.Expression);
Console.WriteLine("Arguments: ");
foreach (var item in parser.Arguments)
{
Console.WriteLine(item);
}
Func<double,double,double> f = parser.CompileBinary();
double x = 4, y = 2;
Console.WriteLine($"f({x},{y}) = {f(x, y)}");
}
with output
Expression:
(((x ^ 3) + (y * y)) + sin((60 * 0.0174532925199433)))
Arguments:
x
y
f(4,2) = 68.8660254037844
The result is numerically correct. A lot of time was spent in trying to deal with ° as a constant value with implied multiplication. I am happy with the results.
Class Structure
To get there there are three classes needed
Token
This information class holds the data extracted from the string such as names, values, operators, functions and parenthesis.
public enum TokenType
{
EOF,
Operator,
StartBlock,
EndBlock,
Delimiter,
Identifier,
Number,
}
public readonly struct Token
{
public static readonly Token Empty = new Token(TokenType.EOF, string.Empty, 0, 0);
public Token(Token token, int level) : this()
{
this = token;
Level = level;
}
Token(TokenType type, string symbol, double number, int level) : this()
{
Type = type;
Symbol = symbol;
Number = number;
Level = level;
}
public static Token Operator(char symbol, int level = 0) => new Token(TokenType.Operator, symbol.ToString(), 0, level);
public static Token StartBlock(char symbol, int level = 0) => new Token(TokenType.StartBlock, symbol.ToString(), 0, level);
public static Token EndBlock(char symbol, int level = 0) => new Token(TokenType.EndBlock, symbol.ToString(), 0, level);
public static Token Delimiter(char symbol, int level = 0) => new Token(TokenType.Delimiter, symbol.ToString(), 0, level);
public static Token Identifier(string symbol, int level = 0) => new Token(TokenType.Identifier, symbol, 0, level);
public static Token Value(double number, int level = 0) => new Token(TokenType.Number, string.Empty, number, level);
public static Token operator +(Token token, int delta) => new Token(token, token.Level + delta);
public static Token operator -(Token token, int delta) => new Token(token, token.Level - delta);
public TokenType Type { get; }
public string Symbol { get; }
public double Number { get; }
public int Level { get; }
public bool IsEOF() => Type == TokenType.EOF;
public bool IsOperator(char #operator) => IsOperator(out char op) && op == #operator;
public bool IsOperator(out char #operator)
{
if (Type == TokenType.Operator)
{
#operator = Symbol[0];
return true;
}
#operator = '\0';
return false;
}
public bool IsIdentifier(string symbol) => IsIdentifier(out string x) && x.Equals(symbol);
public bool IsIdentifier(out string symbol)
{
if (Type == TokenType.Identifier)
{
symbol = Symbol;
return true;
}
symbol = string.Empty;
return false;
}
public bool IsNumber(double value) => IsNumber(out double x) && x.Equals(value);
public bool IsNumber(out double value)
{
if (Type == TokenType.Number)
{
value = Number;
return true;
}
value = 0;
return false;
}
public bool IsStartBlock(char delimiter) => IsStartBlock(out char sym) && sym == delimiter;
public bool IsStartBlock(out char delimiter)
{
if (Type == TokenType.StartBlock)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public bool IsEndBlock(char delimiter) => IsEndBlock(out char sym) && sym == delimiter;
public bool IsEndBlock(out char delimiter)
{
if (Type == TokenType.EndBlock)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public bool IsDelimiter(char delimiter) => IsDelimiter(out char sym) && sym == delimiter;
public bool IsDelimiter(out char delimiter)
{
if (Type == TokenType.Delimiter)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public override string ToString()
{
var tab = new string('\t', Level);
switch (Type)
{
case TokenType.EOF:
return $"{tab}{Type}";
case TokenType.Operator:
case TokenType.StartBlock:
case TokenType.EndBlock:
case TokenType.Delimiter:
case TokenType.Identifier:
return $"{tab}{Type}: {Symbol}";
case TokenType.Number:
return $"{tab}{Type}: {Number}";
default:
throw new NotSupportedException();
}
}
}
Tokenizer
The job of the tokenizer is to extract the Token from a string. This is done one step at a time with the .MoveNext() and .Reset() functions. At each step the .Current property holds the token information.
/// <summary>
/// Parses strings into Tokens for further processing.
/// </summary>
/// <remarks>Code taken from https://github.com/toptensoftware/SimpleExpressionEngine </remarks>
public class Tokenizer : IEnumerator<Token>, IEnumerable<Token>
{
readonly string _expression;
TextReader _reader;
char _currentChar;
public Tokenizer(string expression)
{
_expression = expression;
Reset();
}
public Token Current { get; private set; }
object System.Collections.IEnumerator.Current { get => Current; }
public IEnumerator<Token> GetEnumerator() => this;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
public void Reset()
{
_reader = new StringReader(_expression);
NextChar();
//MoveNext();
}
// Read the next character from the input strem
// and store it in _currentChar, or load '\0' if EOF
void NextChar()
{
int ch = _reader.Read();
_currentChar = ch < 0 ? '\0' : (char)ch;
}
public bool MoveNext()
{
// Skip whitespace
while (char.IsWhiteSpace(_currentChar))
{
NextChar();
}
switch (_currentChar)
{
case '\0':
{
if (Current.Level > 0)
{
throw new InvalidOperationException();
}
Current = Token.Empty;
return false;
}
case '+':
case '-':
case '*':
case '/':
case '^':
case '=':
{
Current = Token.Operator(_currentChar, Current.Level);
NextChar();
return true;
}
case '(':
{
Current = Token.StartBlock(_currentChar, Current.Level + 1);
NextChar();
return true;
}
case ')':
{
Current = Token.EndBlock(_currentChar, Current.Level - 1);
NextChar();
return true;
}
case ';':
case ',':
{
Current = Token.Delimiter(_currentChar, Current.Level);
NextChar();
return true;
}
}
if (char.IsDigit(_currentChar) || _currentChar == '.')
{
// Capture digits/decimal point
StringBuilder sb = new StringBuilder();
bool haveDecimalPoint = false;
while (char.IsDigit(_currentChar) || (!haveDecimalPoint && _currentChar == '.'))
{
sb.Append(_currentChar);
haveDecimalPoint = _currentChar == '.';
NextChar();
}
// Parse it
if (double.TryParse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out double x))
{
Current = Token.Value(x, Current.Level);
//Token = Token.Number;
return true;
}
}
// Identifier - starts with letter or underscore
if (char.IsLetter(_currentChar) || _currentChar == '_' || _currentChar == '°')
{
var sb = new StringBuilder();
// Accept letter, digit or underscore
while (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '°')
{
sb.Append(_currentChar);
NextChar();
}
// Setup token
Current = Token.Identifier(sb.ToString(), Current.Level);
return true;
}
Current = Token.Empty;
return false;
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposed)
{
if (disposed)
{
_reader.Dispose();
}
}
}
Parser
This is the main class that recursively builds the expression tree. When the constructor is called, the resulting expression tree is stored in the .Expression property, as well as any required arguments in .Arguments.
/// <summary>
/// Parses a string into an <see cref="Expression"/> tree.
/// </summary>
public class Parser
{
readonly Tokenizer _tokenizer;
readonly Dictionary<string, ParameterExpression> _arguments;
public Parser(string expression) : this(new Tokenizer(expression)) { }
public Parser(Tokenizer tokenizer)
{
this._tokenizer = tokenizer;
this._arguments = new Dictionary<string, ParameterExpression>();
Expression = ParseTokens();
}
public Expression Expression { get; }
public IReadOnlyList<ParameterExpression> Arguments => _arguments.Values.ToList();
public Func<double> CompileNonary()
{
if (Arguments.Count == 0)
{
return Compile<Func<double>>();
}
throw new InvalidOperationException("Expression has too many arguments.");
}
public Func<double, double> CompileUnary()
{
if (Arguments.Count == 1)
{
return Compile<Func<double, double>>();
}
if (Arguments.Count > 1)
{
throw new InvalidOperationException("Expression has too many arguments.");
}
else
{
throw new InvalidOperationException("Expression has too few arguments.");
}
}
public Func<double, double, double> CompileBinary()
{
if (Arguments.Count == 2)
{
return Compile<Func<double, double, double>>();
}
if (Arguments.Count > 2)
{
throw new InvalidOperationException("Expression has too many arguments.");
}
else
{
throw new InvalidOperationException("Expression has too few arguments.");
}
}
TFunc Compile<TFunc>() where TFunc : Delegate
{
ParameterExpression[] arguments = _arguments.Values.ToArray();
return Expression.Lambda<TFunc>(Expression, arguments).Compile() as TFunc;
}
Expression ParseTokens()
{
_tokenizer.Reset();
if (_tokenizer.MoveNext())
{
Expression expr = ParseEquals();
// Check everything was consumed
if (_tokenizer.Current.Type != TokenType.EOF)
{
throw new InvalidOperationException("Unexpected characters at end of expression");
}
return expr;
}
throw new InvalidOperationException("Invalid Expression");
}
Expression ParseEquals()
{
Expression lhs = ParseAddSubtract();
while (true)
{
if (_tokenizer.Current.IsOperator('='))
{
_tokenizer.MoveNext();
Expression rhs = ParseAddSubtract();
lhs = Expression.Equal(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseAddSubtract()
{
Expression lhs = ParseMulDivide();
while (true)
{
if (_tokenizer.Current.IsOperator('+'))
{
_tokenizer.MoveNext();
Expression rhs = ParseMulDivide();
lhs = Expression.Add(lhs, rhs);
}
else if (_tokenizer.Current.IsOperator('-'))
{
_tokenizer.MoveNext();
Expression rhs = ParseMulDivide();
lhs = Expression.Subtract(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseMulDivide()
{
Expression lhs = ParsePower();
bool negate = false;
if (lhs is ConstantExpression lex)
{
double x = Convert.ToDouble(lex.Value);
if (x == 0)
{
return Expression.Constant(0.0);
}
else if (x == -1)
{
negate = true;
}
if (_tokenizer.Current.IsIdentifier(out string name))
{
// 60π - or example
Expression rhs = ParseLeaf();
return Expression.Multiply(lhs, rhs);
}
}
while (true)
{
if (_tokenizer.Current.IsOperator('*'))
{
_tokenizer.MoveNext();
Expression rhs = ParsePower();
if (rhs is ConstantExpression rex)
{
double y = Convert.ToDouble(rex.Value);
if (y == 0)
{
lhs = Expression.Constant(0.0);
}
else if (y == 1)
{
// do nothing
}
else if (y == -1)
{
negate = !negate;
}
else
{
lhs = Expression.Multiply(lhs, rhs);
}
}
else
{
lhs = Expression.Multiply(lhs, rhs);
}
}
else if (_tokenizer.Current.IsOperator('/'))
{
_tokenizer.MoveNext();
Expression rhs = ParsePower();
if (rhs is ConstantExpression rex)
{
double y = Convert.ToDouble(rex.Value);
if (y == 0)
{
lhs = Expression.Constant(double.PositiveInfinity);
}
else if (y == 1)
{
// do nothing
}
else if (y == -1)
{
negate = !negate;
}
else
{
lhs = Expression.Divide(lhs, rhs);
}
}
else
{
lhs = Expression.Divide(lhs, rhs);
}
}
else
{
return negate ? Expression.Negate(lhs) : lhs;
}
}
}
Expression ParsePower()
{
Expression lhs = ParseUnary();
while (true)
{
if (_tokenizer.Current.IsOperator('^'))
{
_tokenizer.MoveNext();
Expression rhs = ParseUnary();
if (rhs is ConstantExpression cex)
{
double x = Convert.ToDouble(cex.Value);
if (x == 0)
{
return Expression.Constant(1.0);
}
if (x == 1)
{
return lhs;
}
if (x == -1)
{
return Expression.Divide(Expression.Constant(1.0), lhs);
}
if (x == 2)
{
return Expression.Multiply(lhs, lhs);
}
}
lhs = Expression.Power(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseUnary()
{
while (true)
{
if (_tokenizer.Current.IsOperator('+'))
{
// ignore unary +
_tokenizer.MoveNext();
continue;
}
if (_tokenizer.Current.IsOperator('-'))
{
_tokenizer.MoveNext();
Expression rhs = ParseUnary();
if (rhs is UnaryExpression uex)
{
if (uex.NodeType == ExpressionType.Negate)
{
return uex.Operand;
}
}
return Expression.Negate(rhs);
}
return ParseLeaf();
}
}
private Expression ParseLeaf()
{
if (_tokenizer.Current.IsNumber(out double x))
{
_tokenizer.MoveNext();
return Expression.Constant(x);
}
if (_tokenizer.Current.IsStartBlock('('))
{
_tokenizer.MoveNext();
Expression node = ParseAddSubtract();
if (!_tokenizer.Current.IsEndBlock(')'))
{
throw new InvalidOperationException("Mismatched Parenthesis.");
}
_tokenizer.MoveNext();
return node;
}
if (_tokenizer.Current.IsIdentifier(out string name))
{
_tokenizer.MoveNext();
if (_tokenizer.Current.IsStartBlock('('))
{
// function call
_tokenizer.MoveNext();
// Parse arguments
List<Expression> arguments = new List<Expression>();
while (true)
{
Expression node = ParseAddSubtract();
arguments.Add(node);
if (_tokenizer.Current.IsDelimiter(','))
{
_tokenizer.MoveNext();
continue;
}
// end of arguments
break;
}
if (!_tokenizer.Current.IsEndBlock(')'))
{
throw new InvalidOperationException("Mismatched Parenthesis.");
}
_tokenizer.MoveNext();
MethodInfo f = typeof(MathFunctions).GetMethod(name, BindingFlags.Static | BindingFlags.Public);
if (f != null)
{
switch (arguments.Count)
{
case 0:
return Expression.Call(f);
case 1:
return Expression.Call(f, arguments[0]);
case 2:
return Expression.Call(f, arguments[0], arguments[1]);
default:
throw new InvalidOperationException($"Too many arguments for function {name}.");
}
}
else
{
throw new InvalidOperationException($"Unknown function {name}.");
}
}
else
{
if (MathFunctions.knownConstants.ContainsKey(name))
{
// named constant
return Expression.Constant(MathFunctions.knownConstants[name]);
}
// variable
if (!_arguments.ContainsKey(name))
{
// add to list of arguments
_arguments.Add(name, Expression.Parameter(typeof(double), name));
}
return _arguments[name];
}
}
if (!_tokenizer.Current.IsEOF())
{
throw new InvalidOperationException($"Unexpected token {_tokenizer.Current}");
}
throw new NotImplementedException();
}
}
MathFunctions
The parser depends on a utility function to provide method bodies for math functions. Most are wrappers around the Math class, but some are new. The names are lowercase to match with any functions called in the parsed string.
public static class MathFunctions
{
internal static readonly Random rng = new Random();
internal static readonly Dictionary<string, double> knownConstants =
new Dictionary<string, double>()
{
["pi"] = Math.PI,
["π"] = Math.PI,
["e"] = Math.E,
["Φ"] = (1 + Math.Sqrt(5)) / 2,
["°"] = Math.PI / 180,
["deg"] = Math.PI / 180,
["rad"] = 180 / Math.PI,
["rpm"] = 2 * Math.PI / 60,
};
public static double rand() => rng.NextDouble();
public static double abs(double x) => Math.Abs(x);
public static double sqr(double x) => x * x;
public static double sqrt(double x) => Math.Sqrt(x);
public static double sign(double x) => (double)Math.Sign(x);
public static double floor(double x) => Math.Floor(x);
public static double round(double x) => Math.Round(x);
public static double exp(double x) => Math.Exp(x);
public static double log(double x) => Math.Log(x);
public static double sin(double x) => Math.Sin(x);
public static double cos(double x) => Math.Cos(x);
public static double tan(double x) => Math.Tan(x);
public static double asin(double x) => Math.Asin(x);
public static double acos(double x) => Math.Acos(x);
public static double atan(double x) => Math.Atan(x);
public static double sinh(double x) => Math.Sinh(x);
public static double cosh(double x) => Math.Cosh(x);
public static double tanh(double x) => Math.Tanh(x);
public static double asinh(double x) => Math.Log(Math.Sqrt(x * x + 1) + x);
public static double acosh(double x) => Math.Log(Math.Sqrt(x * x - 1) + x);
public static double atanh(double x) => -Math.Log((1 - x) / (1 + x)) / 2;
public static double pow(double x, double y) => Math.Pow(x, y);
public static double atan2(double dy, double dx) => Math.Atan2(dy, dx);
public static double min(double x, double y) => Math.Min(x, y);
public static double max(double x, double y) => Math.Max(x, y);
}
As a final note the parser also supports some named constants
"pi" = Math.PI,
"π" = Math.PI,
"e" = Math.E,
"Φ" = (1 + Math.Sqrt(5)) / 2,
"°" = Math.PI / 180,
"deg" = Math.PI / 180,
"rad" = 180 / Math.PI,
"rpm" = 2 * Math.PI / 60,
Such that inputs such as 6π and 60° and 1000rpm are supported.
Compiling
The .NET Expression class has a .Compile() method that can be used to turn the expression into a function. Wrappers around this method exists in the parser such that CompileUnary() and CompileBinary() produce functions of the form f(x) and f(x,y) respectively with arguments and return types that of double numbers.
From the example code above this is demonstrated with
var f = parser.CompileBinary();
double x = ...
double y = ...
double z = f(x, y);
I have created three classes LinkedList.cs Node.cs Program.cs and now I'm wondering how I use them to there full potentional. I want the user to be able to create a new dinosaur that is stored in the linked list but I'm not sure how to go about doing this, I know I need to use a linked list cause I need them to be able to add at any point and remove from any point of the list.
I'm sure that the LinkedList.cs has the functionality to do all of this but I'm not sure how I use it to do what I'm trying to do which is to allow the user to create a new dinosaur, add it to any position in the list and remove a dinosaur from any position in the list.
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Text;
namespace JurrasicFinal
{
public class LinkedList
{
private Node head;
private int count;
public LinkedList()
{
this.head = null;
this.count = 0;
}
public bool Empty
{
get { return this.count == 0; }
}
public int Count
{
get { return this.count; }
}
public object this[int index]
{
get { return this.Get(index); }
}
public object Add(int index, object o)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (index > count)
index = count;
Node current = this.head;
if (this.Empty || index == 0)
{
this.head = new Node(o, this.head);
}
else
{
for (int i = 0; i < index - 1; i++)
{
current = current.Next;
current.Next = new Node(o, current.Next);
}
}
count++;
return o;
}
public object Add(object o)
{
return this.Add(count, o);
}
public object Remove(int index)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (this.Empty)
return null;
if (index >= this.count)
index = count - 1;
Node current = this.head;
object result = null;
if (index == 0)
{
result = current.Data;
this.head = current.Next;
}
else
{
for (int i = 0; index < index - 1; i++) ;
current = current.Next;
result = current.Next.Data;
current.Next = current.Next.Next;
}
count--;
return result;
}
public void Clear()
{
this.head = null;
this.count = 0;
}
public int IndexOf(object o)
{
Node current = this.head;
for (int i = 0; i < this.count; i++)
{
if (current.Data.Equals(o))
return i;
current = current.Next;
}
return -1;
}
public bool Contains(object o)
{
return this.IndexOf(o) >= 0;
}
public object Get(int index)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (this.Empty)
return null;
if (index >= this.count)
index = this.count - 1;
Node current = this.head;
for (int i = 0; i < index; i++)
current = current.Next;
return current.Data;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace JurrasicFinal
{
public class Node
{
private object data;
private Node next;
public Node(object data, Node next)
{
this.data = data;
this.next = next;
}
public object Data
{
get { return this.data; }
set { this.data = value; }
}
public Node Next
{
get { return this.next; }
set { this.next = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
namespace JurrasicFinal
{
class Program
{
class Dinosaur
{
public string Name;
public string Classification;
public char Sex;
}
static void Main(string[] args)
{
LinkedList<Dinosaur> DinoList = new LinkedList<Dinosaur>();
Dinosaur Dino1 = new Dinosaur();
Dino1.Name = "Tyrannosaurus Rex";
Dino1.Classification = "Carnivorous";
Dino1.Sex = 'M';
Dinosaur Dino2 = new Dinosaur();
Dino2.Name = "Velociraptor";
Dino2.Classification = "Carnivorous";
Dino2.Sex = 'F';
Dinosaur Dino3 = new Dinosaur();
Dino3.Name = "Procompsognathus";
Dino3.Classification = "Carnivorous";
Dino3.Sex = 'M';
void printList()
{
Console.WriteLine("Current Queue: ");
Console.WriteLine("\n");
foreach (Dinosaur d in DinoList)
{
Console.WriteLine("Name: " + d.Name);
Console.WriteLine("Classification: " + d.Classification);
Console.WriteLine("Sex " + d.Sex);
Console.WriteLine("\n");
}
}
DinoList.AddLast(Dino1);
DinoList.AddLast(Dino2);
DinoList.AddLast(Dino3);
printList();
Console.WriteLine(DinoList.Count);
FileStream fileStream = File.OpenWrite("E:/University Work/Dinosaur.txt");
BinaryWriter writer = new BinaryWriter(fileStream);
foreach (Dinosaur d in DinoList)
{
writer.Write(d.Name);
writer.Write(d.Classification);
writer.Write(d.Sex);
}
writer.Close();
Console.WriteLine("Reading Back From File");
FileStream file = File.OpenRead("E:/University Work/Dinosaur.txt");
BinaryReader reader = new BinaryReader(file);
for (int i = 1; i < 3; i++)
{
Dinosaur d = new Dinosaur();
d.Name = reader.ReadString();
d.Classification = reader.ReadString();
d.Sex = reader.ReadChar();
DinoList.AddLast(d);
}
reader.Close();
Console.ReadKey();
}
}
}
Your LinkedList is not generic but you are using it generically in Program class. It is little confusing to me.
your LinkedList is not even list. Try implement at least IEnumerable or better IList interface to achieve more user friendly structure.
And think if you really need to use linked list. Course to add an item into list you need to copy O(n) pointers (when you iterating list) It is a lot similar to copying standard List when you are using its predefined functions.
These functions are implemented as unsafe, so it's much faster.
It looks like reinventing the wheel here to me.
To check the validity of a Binary Search Tree, I use a method. But the method always returns false, when tested against a valid Binary Search Tree.
Online Demo Here
The code
public class Program
{
public static void Main()
{
BinarySearchTree<int> tree = new BinarySearchTree<int>();
tree.Insert(2);
tree.Insert(1);
tree.Insert(3);
Console.WriteLine(tree.IsValidBinarySearchTreeRecursive(tree.root).ToString()); // this is supposed to return true when tested against the simple tree above
}
}
public class Node<T> where T : IComparable
{
public Node<T> left;
public Node<T> right;
public T data;
public Node(T data)
{
this.left = null;
this.right = null;
this.data = data;
}
}
public class BinarySearchTree<T> where T : IComparable
{
public Node<T> root;
public BinarySearchTree()
{
this.root = null;
}
public bool Insert(T data)
{
Node<T> before = null;
Node<T> after = this.root;
while(after != null)
{
before = after;
if(data.CompareTo(after.data) < 0)
after = after.left;
else if(data.CompareTo(after.data) > 0)
after = after.right;
else
return false;
}
Node<T> newNode = new Node<T>(data);
if (this.root == null)
{
this.root = newNode;
}
else
{
if(data.CompareTo(before.data) < 0)
before.left = newNode;
else
before.right = newNode;
}
return true;
}
private bool _HelperForIsValidBinarySearchTreeRecursive(Node<T> node, T lower, T upper)
{
if (node == null)
return true;
T val = node.data;
Type nodeType = typeof(T);
if(nodeType.IsNumeric())
{
if(val.CompareTo(lower) <= 0)
return false;
if(val.CompareTo(upper) >= 0)
return false;
}
else
{
if(lower != null && val.CompareTo(lower) <= 0)
return false;
if(upper != null && val.CompareTo(upper) >= 0)
return false;
}
if(!_HelperForIsValidBinarySearchTreeRecursive(node.right, val, upper))
return false;
if(!_HelperForIsValidBinarySearchTreeRecursive(node.left, lower, val))
return false;
return true;
}
public bool IsValidBinarySearchTreeRecursive(Node<T> root)
{
Type nodeType = typeof(T);
if(nodeType.IsNumeric())
return _HelperForIsValidBinarySearchTreeRecursive(root, root.data, root.data);
return _HelperForIsValidBinarySearchTreeRecursive(root, default(T), default(T));
}
}
public static class StaticUtilities
{
private static readonly HashSet<Type> NumericTypes = new HashSet<Type>
{
typeof(int), typeof(double), typeof(decimal),
typeof(long), typeof(short), typeof(sbyte),
typeof(byte), typeof(ulong), typeof(ushort),
typeof(uint), typeof(float)
};
public static bool IsNumeric(this Type myType)
{
return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType);
}
}
Your problem is here:
if(val.CompareTo(lower) <= 0)
return false; // you are always hitting this line
if(val.CompareTo(upper) >= 0)
return false;
Because your T val = node.data; and your lower and even your upper are node.data from your first call.
So most probably the line to fix is
return _HelperForIsValidBinarySearchTreeRecursive(root, root.data, root.data);
as I guess your original intention was to compare the left and right with the root?
Based on this: https://www.geeksforgeeks.org/a-program-to-check-if-a-binary-tree-is-bst-or-not/
for your numeric approach, your solution should be:
/* false if this node violates the min/max constraints */
if (node.data < min || node.data > max)
{
return false;
}
So your code becomes:
if(val.CompareTo(lower) < 0)
return false; // you are always hitting this line
if(val.CompareTo(upper) > 0)
return false;
Then you will hit the next obstacle down the line. My suggestion for a solution, would be to change this:
private bool _HelperForIsValidBinarySearchTreeRecursive(Node<T> node, T lower, T upper)
To
private bool _HelperForIsValidBinarySearchTreeRecursive(Node<T> node, Node<T> leftNode<T> right)
and call like this:
_HelperForIsValidBinarySearchTreeRecursive(root, root.Left, root.Right)
My C# Solution
public bool IsBST()
{
return CheckBST(Root, default(T), default(T));
}
private bool CheckBST(TreeNode<T> root, T Min, T Max)
{
if (root == null)
{
return true;
}
if (!(root.Value.CompareTo(Min) <= 0 || root.Value.CompareTo(Max) >= 1))
{
return false;
}
return CheckBST(root.Left, Min, root.Value) && CheckBST(root.Right, root.Value, Max);
}
public void checkInt(Scanner MemberNrSC)
{
MemberNrSC = new Scanner(txtMemberNr.getText());
while (!MemberNrSC.hasNextInt())
{
string correctedMemberNr = Interaction.InputBox(null,"Medlemsnummer skal være et nummer, indtast det rigtige");
if (correctedMemberNr == null)
{
MemberNrCancelled = true;
break;
}
txtMemberNr.setText(correctedMemberNr);
MemberNrSC = new Scanner(txtMemberNr.getText());
MemberNrCancelled = false;
}
}
This is my java checker, for if there is only numbers in the textbox..
But as there is no scanner in C# - how would i get this method converted correct?
I have my other methods needing a scanner class - but i was hoping i could have some help in here.
BTW: i am using a visual basic inputbox - as i do not know if there is a similar way in C#.
Thanks in advance ;-)
/Rasmus
Denmark
Try this maybe
class Scanner : System.IO.StringReader
{
string currentWord;
public Scanner(string source) : base(source)
{
readNextWord();
}
private void readNextWord()
{
System.Text.StringBuilder sb = new StringBuilder();
char nextChar;
int next;
do
{
next = this.Read();
if (next < 0)
break;
nextChar = (char)next;
if (char.IsWhiteSpace(nextChar))
break;
sb.Append(nextChar);
} while (true);
while((this.Peek() >= 0) && (char.IsWhiteSpace((char)this.Peek())))
this.Read();
if (sb.Length > 0)
currentWord = sb.ToString();
else
currentWord = null;
}
public bool hasNextInt()
{
if (currentWord == null)
return false;
int dummy;
return int.TryParse(currentWord, out dummy);
}
public int nextInt()
{
try
{
return int.Parse(currentWord);
}
finally
{
readNextWord();
}
}
public bool hasNextDouble()
{
if (currentWord == null)
return false;
double dummy;
return double.TryParse(currentWord, out dummy);
}
public double nextDouble()
{
try
{
return double.Parse(currentWord);
}
finally
{
readNextWord();
}
}
public bool hasNext()
{
return currentWord != null;
}
}
source: https://stackoverflow.com/a/722524/1714342
string Str = txtMemberNr.Text.Trim();
double Num;
bool isNum = double.TryParse(Str, out Num);
if (isNum)
{
// CODE IS HERE
}
else
{
MessageBox.Show("Brugernavn skal kun indeholde tal, prøv igen!", "advarsel", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
This was the most simple way - checking whether or not there is an int/string in the textbox field.
I was hoping that you/or someone who have option for making this solved.
Thank you for your help - but i could not make it work as wanted - even though there was good use of the scanner.. thank you.
How about
public static IEnumerable<string> Scanner(this string tgt, char delim)
{
var sb = new StringBuilder();
foreach (var c in tgt)
{
if (c == delim)
{
yield return sb.ToString();
sb.Clear();
}
else sb.Append(c);
}
}
I know that SortedDictionary is a binary search tree (and it can almost do what I need to do!) but I can't figure out how to do everything I need in the correct complexity.
So here are the constraints (and the data structure which I know has it)
Inserting and Deletion in O(log n) (SortedDictionary)
Search in O(log n) (SortedDictionary & SortedList)
Iteration from one searched element to another in O(log n) + O(m) (where m is the number of elements in between) (SortedList)
As you can see, I don't know how to get SortedDictionary to do number 3. Basically what I need to do is get all the elements with a range without iterating the set.
Please tell me if my question isn't clear.
This seems to describe a B+ tree perfectly: http://en.wikipedia.org/wiki/B%2B_tree :
Inserting a record requires O(log(n)) operations in the worst case
Finding a record requires O(log(n)) operations in the worst case
Performing a range query with k elements occurring within the range requires O(log(n) + k) operations in the worst case.
A C# implementation seems to exist here: http://bplusdotnet.sourceforge.net/
I don't think there is a collection in the framework that does what you describe, although I could be wrong.
What you are looking for is a linked list that is indexed with a binary tree. This would provide you O(log n) insertion, deletion and search using the binary tree, with O(m) traversal using the linked list.
You might want to have a look at the C5 Generic Collections Library. Although there doesn't appear to be a collection that fits your description there, you might be able to marry together their TreeSet<T> and LinkedList<T> objects, creating a new SortedLinkedList<T> object.
Some of the suggestions were pretty good, but I decided to implement the collection myself (it sounded fun). I started with the .NET implementation of SortedDictionary, and heavily modified it to do what I needed it to do
Just so other people can benefit from my work, here is the class:
internal delegate void TreeWalkAction<Key, Value>(BinaryTreeSearch<Key, Value>.Node node);
internal delegate bool TreeWalkTerminationPredicate<Key, Value>(BinaryTreeSearch<Key, Value>.Node node);
internal class BinaryTreeSearch<Key, Value>
{
// Fields
private IComparer<Key> comparer;
private int count;
private Node root;
private int version;
// Methods
public BinaryTreeSearch(IComparer<Key> comparer)
{
if (comparer == null)
{
this.comparer = Comparer<Key>.Default;
}
else
{
this.comparer = comparer;
}
}
private Node First
{
get
{
if (root == null) return null;
Node n = root;
while (n.Left != null)
{
n = n.Left;
}
return n;
}
}
public Key Min
{
get
{
Node first = First;
return first == null ? default(Key) : first.Key;
}
}
public Key Max
{
get
{
if (root == null) return default(Key);
Node n = root;
while (n.Right != null)
{
n = n.Right;
}
return n.Key;
}
}
public List<Value> this[Key key]
{
get
{
Node n = FindNode(key);
return n == null ? new List<Value>() : n.Values;
}
}
public List<Value> GetRange(Key start, Key end)
{
Node node = FindNextNode(start);
List<Value> ret = new List<Value>();
InOrderTreeWalk(node,
aNode => ret.AddRange(aNode.Values),
aNode => comparer.Compare(end, aNode.Key) < 0);
return ret;
}
public void Add(Key key, Value value)
{
if (this.root == null)
{
this.root = new Node(null, key, value, false);
this.count = 1;
}
else
{
Node root = this.root;
Node node = null;
Node grandParent = null;
Node greatGrandParent = null;
int num = 0;
while (root != null)
{
num = this.comparer.Compare(key, root.Key);
if (num == 0)
{
root.Values.Add(value);
count++;
return;
}
if (Is4Node(root))
{
Split4Node(root);
if (IsRed(node))
{
this.InsertionBalance(root, ref node, grandParent, greatGrandParent);
}
}
greatGrandParent = grandParent;
grandParent = node;
node = root;
root = (num < 0) ? root.Left : root.Right;
}
Node current = new Node(node, key, value);
if (num > 0)
{
node.Right = current;
}
else
{
node.Left = current;
}
if (node.IsRed)
{
this.InsertionBalance(current, ref node, grandParent, greatGrandParent);
}
this.root.IsRed = false;
this.count++;
this.version++;
}
}
public void Clear()
{
this.root = null;
this.count = 0;
this.version++;
}
public bool Contains(Key key)
{
return (this.FindNode(key) != null);
}
internal Node FindNode(Key item)
{
int num;
for (Node node = this.root; node != null; node = (num < 0) ? node.Left : node.Right)
{
num = this.comparer.Compare(item, node.Key);
if (num == 0)
{
return node;
}
}
return null;
}
internal Node FindNextNode(Key key)
{
int num;
Node node = root;
while (true)
{
num = comparer.Compare(key, node.Key);
if (num == 0)
{
return node;
}
else if (num < 0)
{
if (node.Left == null) return node;
node = node.Left;
}
else
{
node = node.Right;
}
}
}
private static Node GetSibling(Node node, Node parent)
{
if (parent.Left == node)
{
return parent.Right;
}
return parent.Left;
}
internal void InOrderTreeWalk(Node start, TreeWalkAction<Key, Value> action, TreeWalkTerminationPredicate<Key, Value> terminationPredicate)
{
Node node = start;
while (node != null && !terminationPredicate(node))
{
action(node);
node = node.Next;
}
}
private void InsertionBalance(Node current, ref Node parent, Node grandParent, Node greatGrandParent)
{
Node node;
bool flag = grandParent.Right == parent;
bool flag2 = parent.Right == current;
if (flag == flag2)
{
node = flag2 ? RotateLeft(grandParent) : RotateRight(grandParent);
}
else
{
node = flag2 ? RotateLeftRight(grandParent) : RotateRightLeft(grandParent);
parent = greatGrandParent;
}
grandParent.IsRed = true;
node.IsRed = false;
this.ReplaceChildOfNodeOrRoot(greatGrandParent, grandParent, node);
}
private static bool Is2Node(Node node)
{
return ((IsBlack(node) && IsNullOrBlack(node.Left)) && IsNullOrBlack(node.Right));
}
private static bool Is4Node(Node node)
{
return (IsRed(node.Left) && IsRed(node.Right));
}
private static bool IsBlack(Node node)
{
return ((node != null) && !node.IsRed);
}
private static bool IsNullOrBlack(Node node)
{
if (node != null)
{
return !node.IsRed;
}
return true;
}
private static bool IsRed(Node node)
{
return ((node != null) && node.IsRed);
}
private static void Merge2Nodes(Node parent, Node child1, Node child2)
{
parent.IsRed = false;
child1.IsRed = true;
child2.IsRed = true;
}
public bool Remove(Key key, Value value)
{
if (this.root == null)
{
return false;
}
Node root = this.root;
Node parent = null;
Node node3 = null;
Node match = null;
Node parentOfMatch = null;
bool flag = false;
while (root != null)
{
if (Is2Node(root))
{
if (parent == null)
{
root.IsRed = true;
}
else
{
Node sibling = GetSibling(root, parent);
if (sibling.IsRed)
{
if (parent.Right == sibling)
{
RotateLeft(parent);
}
else
{
RotateRight(parent);
}
parent.IsRed = true;
sibling.IsRed = false;
this.ReplaceChildOfNodeOrRoot(node3, parent, sibling);
node3 = sibling;
if (parent == match)
{
parentOfMatch = sibling;
}
sibling = (parent.Left == root) ? parent.Right : parent.Left;
}
if (Is2Node(sibling))
{
Merge2Nodes(parent, root, sibling);
}
else
{
TreeRotation rotation = RotationNeeded(parent, root, sibling);
Node newChild = null;
switch (rotation)
{
case TreeRotation.LeftRotation:
sibling.Right.IsRed = false;
newChild = RotateLeft(parent);
break;
case TreeRotation.RightRotation:
sibling.Left.IsRed = false;
newChild = RotateRight(parent);
break;
case TreeRotation.RightLeftRotation:
newChild = RotateRightLeft(parent);
break;
case TreeRotation.LeftRightRotation:
newChild = RotateLeftRight(parent);
break;
}
newChild.IsRed = parent.IsRed;
parent.IsRed = false;
root.IsRed = true;
this.ReplaceChildOfNodeOrRoot(node3, parent, newChild);
if (parent == match)
{
parentOfMatch = newChild;
}
node3 = newChild;
}
}
}
int num = flag ? -1 : this.comparer.Compare(key, root.Key);
if (num == 0)
{
flag = true;
match = root;
parentOfMatch = parent;
}
node3 = parent;
parent = root;
if (num < 0)
{
root = root.Left;
}
else
{
root = root.Right;
}
}
if (match != null)
{
if (match.Values.Remove(value))
{
this.count--;
}
if (match.Values.Count == 0)
{
this.ReplaceNode(match, parentOfMatch, parent, node3);
}
}
if (this.root != null)
{
this.root.IsRed = false;
}
this.version++;
return flag;
}
private void ReplaceChildOfNodeOrRoot(Node parent, Node child, Node newChild)
{
if (parent != null)
{
if (parent.Left == child)
{
parent.Left = newChild;
}
else
{
parent.Right = newChild;
}
if (newChild != null) newChild.Parent = parent;
}
else
{
this.root = newChild;
}
}
private void ReplaceNode(Node match, Node parentOfMatch, Node succesor, Node parentOfSuccesor)
{
if (succesor == match)
{
succesor = match.Left;
}
else
{
if (succesor.Right != null)
{
succesor.Right.IsRed = false;
}
if (parentOfSuccesor != match)
{
parentOfSuccesor.Left = succesor.Right; if (succesor.Right != null) succesor.Right.Parent = parentOfSuccesor;
succesor.Right = match.Right; if (match.Right != null) match.Right.Parent = succesor;
}
succesor.Left = match.Left; if (match.Left != null) match.Left.Parent = succesor;
}
if (succesor != null)
{
succesor.IsRed = match.IsRed;
}
this.ReplaceChildOfNodeOrRoot(parentOfMatch, match, succesor);
}
private static Node RotateLeft(Node node)
{
Node right = node.Right;
node.Right = right.Left; if (right.Left != null) right.Left.Parent = node;
right.Left = node; if (node != null) node.Parent = right;
return right;
}
private static Node RotateLeftRight(Node node)
{
Node left = node.Left;
Node right = left.Right;
node.Left = right.Right; if (right.Right != null) right.Right.Parent = node;
right.Right = node; if (node != null) node.Parent = right;
left.Right = right.Left; if (right.Left != null) right.Left.Parent = left;
right.Left = left; if (left != null) left.Parent = right;
return right;
}
private static Node RotateRight(Node node)
{
Node left = node.Left;
node.Left = left.Right; if (left.Right != null) left.Right.Parent = node;
left.Right = node; if (node != null) node.Parent = left;
return left;
}
private static Node RotateRightLeft(Node node)
{
Node right = node.Right;
Node left = right.Left;
node.Right = left.Left; if (left.Left != null) left.Left.Parent = node;
left.Left = node; if (node != null) node.Parent = left;
right.Left = left.Right; if (left.Right != null) left.Right.Parent = right;
left.Right = right; if (right != null) right.Parent = left;
return left;
}
private static TreeRotation RotationNeeded(Node parent, Node current, Node sibling)
{
if (IsRed(sibling.Left))
{
if (parent.Left == current)
{
return TreeRotation.RightLeftRotation;
}
return TreeRotation.RightRotation;
}
if (parent.Left == current)
{
return TreeRotation.LeftRotation;
}
return TreeRotation.LeftRightRotation;
}
private static void Split4Node(Node node)
{
node.IsRed = true;
node.Left.IsRed = false;
node.Right.IsRed = false;
}
// Properties
public IComparer<Key> Comparer
{
get
{
return this.comparer;
}
}
public int Count
{
get
{
return this.count;
}
}
internal class Node
{
// Fields
private bool isRed;
private Node left, right, parent;
private Key key;
private List<Value> values;
// Methods
public Node(Node parent, Key item, Value value) : this(parent, item, value, true)
{
}
public Node(Node parent, Key key, Value value, bool isRed)
{
this.key = key;
this.parent = parent;
this.values = new List<Value>(new Value[] { value });
this.isRed = isRed;
}
// Properties
public bool IsRed
{
get
{
return this.isRed;
}
set
{
this.isRed = value;
}
}
public Key Key
{
get
{
return this.key;
}
set
{
this.key = value;
}
}
public List<Value> Values { get { return values; } }
public Node Left
{
get
{
return this.left;
}
set
{
this.left = value;
}
}
public Node Right
{
get
{
return this.right;
}
set
{
this.right = value;
}
}
public Node Parent
{
get
{
return this.parent;
}
set
{
this.parent = value;
}
}
public Node Next
{
get
{
if (right == null)
{
if (parent == null)
{
return null; // this puppy must be lonely
}
else if (parent.Left == this) // this is a left child
{
return parent;
}
else
{
//this is a right child, we need to go up the tree
//until we find a left child. Then the parent will be the next
Node n = this;
do
{
n = n.parent;
if (n.parent == null)
{
return null; // this must have been a node along the right edge of the tree
}
} while (n.parent.right == n);
return n.parent;
}
}
else // there is a right child.
{
Node go = right;
while (go.left != null)
{
go = go.left;
}
return go;
}
}
}
public override string ToString()
{
return key.ToString() + " - [" + string.Join(", ", new List<string>(values.Select<Value, string>(o => o.ToString())).ToArray()) + "]";
}
}
internal enum TreeRotation
{
LeftRightRotation = 4,
LeftRotation = 1,
RightLeftRotation = 3,
RightRotation = 2
}
}
and a quick unit test (which doesn't actually cover all the code, so there might still be some bugs):
[TestFixture]
public class BTSTest
{
private class iC : IComparer<int>{public int Compare(int x, int y){return x.CompareTo(y);}}
[Test]
public void Test()
{
BinaryTreeSearch<int, int> bts = new BinaryTreeSearch<int, int>(new iC());
bts.Add(5, 1);
bts.Add(5, 2);
bts.Add(6, 2);
bts.Add(2, 3);
bts.Add(8, 2);
bts.Add(10, 11);
bts.Add(9, 4);
bts.Add(3, 32);
bts.Add(12, 32);
bts.Add(8, 32);
bts.Add(9, 32);
Assert.AreEqual(11, bts.Count);
Assert.AreEqual(2, bts.Min);
Assert.AreEqual(12, bts.Max);
List<int> val = bts[5];
Assert.AreEqual(2, val.Count);
Assert.IsTrue(val.Contains(1));
Assert.IsTrue(val.Contains(2));
val = bts[6];
Assert.AreEqual(1, val.Count);
Assert.IsTrue(val.Contains(2));
Assert.IsTrue(bts.Contains(5));
Assert.IsFalse(bts.Contains(-1));
val = bts.GetRange(5, 8);
Assert.AreEqual(5, val.Count);
Assert.IsTrue(val.Contains(1));
Assert.IsTrue(val.Contains(32));
Assert.AreEqual(3, val.Count<int>(num => num == 2));
bts.Remove(8, 32);
bts.Remove(5, 2);
Assert.AreEqual(9, bts.Count);
val = bts.GetRange(5, 8);
Assert.AreEqual(3, val.Count);
Assert.IsTrue(val.Contains(1));
Assert.AreEqual(2, val.Count<int>(num => num == 2));
bts.Remove(2, 3);
Assert.IsNull(bts.FindNode(2));
bts.Remove(12, 32);
Assert.IsNull(bts.FindNode(12));
Assert.AreEqual(3, bts.Min);
Assert.AreEqual(10, bts.Max);
bts.Remove(9, 4);
bts.Remove(5, 1);
bts.Remove(6, 2);
}
}
Check out System.Collections.ObjectModel.KeyedCollection<TKey, TItem> - it might not suit your requirements but it seems like a good fit, as it provides an internal lookup dictionary that enables O(1) retrieval of items by index and approaching O(1) by key.
The caveat is that it is intended to store objects where the key is defined as a property on the object, so unless you can mash your input data to fit, it won't be appropriate.
I would include some more information on what data you are intending to store and the volume, as this might help provide alternatives.