I am trying to implement a simple generic method in c# that can convert from an NI ComplexSingle and NI ComplexDouble which are structs to a Complex as defined in System.Numerics, which is also a struct. Both
ComplexSingle and ComplexDouble have properties Real and Imaginary defined here:
public double Real
{
get
{
return _privateBackingFieldForProperty_Real;
}
set
{
double privateBackingFieldForProperty_Real = _privateBackingFieldForProperty_Real;
_privateBackingFieldForProperty_Real = value;
}
}
public double Imaginary
{
get
{
return _privateBackingFieldForProperty_Imaginary;
}
set
{
double privateBackingFieldForProperty_Imaginary = _privateBackingFieldForProperty_Imaginary;
_privateBackingFieldForProperty_Imaginary = value;
}
}
All I want to do is make a single function that can take in either a ComplexSingle array or ComplexDouble array and convert either one into a Complex array. I was hoping to do that by using a generic method as below. However, I soon realized that the Real and Imaginary properties could not be found because the generic passed in doesn't know about them. Then, I thought maybe I could use an interface, which I defined below that has the properties that are found in the actual struct. Now I am getting an error below at:
Complex[] complexes = ComplexNItoComplex<ComplexDouble>(cd);
that
Code is below:
using NationalInstruments;
using System;
using System.Numerics;
namespace ComplexStuff
{
class ComplexMod
{
static void Main(string[] args)
{
Complex c1 = new Complex(4, 3);
Complex c2 = new Complex(5, 4);
Complex c3 = new Complex(6, 5);
ComplexDouble cd1 = new ComplexDouble(4, 3);
ComplexDouble cd2 = new ComplexDouble(4, 3);
ComplexDouble cd3 = new ComplexDouble(4, 3);
ComplexSingle cs1 = new ComplexSingle(7, 2);
ComplexSingle cs2 = new ComplexSingle(7, 2);
ComplexSingle cs3 = new ComplexSingle(7, 2);
ComplexDouble[] cd = new ComplexDouble[]{
cd1, cd2, cd3
};
Complex[] complexes = ComplexNItoComplex<ComplexDouble>(cd);
}
public interface IComplexNI
{
public double Real { get; set; }
public double Imaginary { get; set; }
}
static Complex[] ComplexNItoComplex<T>(IList<T> NIComplex) where T : IComplexNI
{
Complex[] c = new Complex[NIComplex.Count];
for (int i = 0; i < NIComplex.Count; i++)
{
c[i] = new Complex(NIComplex[i].Real, NIComplex[i].Imaginary);
}
return c;
}
}
}
Complex Double:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security;
using NationalInstruments.Internal;
using NationalInstruments.Restricted;
namespace NationalInstruments
{
[Serializable]
[DebuggerDisplay("\\{{Real} + {Imaginary}i}")]
[TypeConverter(typeof(ComplexDoubleConverter))]
public struct ComplexDouble : IFormattable, ISerializable, IEquatable<ComplexDouble>
{
private const string RealKey = "Real";
private const string ImaginaryKey = "Imaginary";
private double _privateBackingFieldForProperty_Real;
private double _privateBackingFieldForProperty_Imaginary;
public double Magnitude => Math.Sqrt(Real * Real + Imaginary * Imaginary);
public double Phase => Math.Atan2(Imaginary, Real);
public static ComplexDouble Zero => new ComplexDouble(0.0, 0.0);
public ComplexDouble ComplexConjugate => new ComplexDouble(Real, 0.0 - Imaginary);
public double Real
{
get
{
return _privateBackingFieldForProperty_Real;
}
set
{
double privateBackingFieldForProperty_Real = _privateBackingFieldForProperty_Real;
_privateBackingFieldForProperty_Real = value;
}
}
public double Imaginary
{
get
{
return _privateBackingFieldForProperty_Imaginary;
}
set
{
double privateBackingFieldForProperty_Imaginary = _privateBackingFieldForProperty_Imaginary;
_privateBackingFieldForProperty_Imaginary = value;
}
}
public ComplexDouble(double real, double imaginary)
{
this = default(ComplexDouble);
Real = real;
Imaginary = imaginary;
}
public static ComplexDouble FromPolar(double magnitude, double phase)
{
return new ComplexDouble(magnitude * Math.Cos(phase), magnitude * Math.Sin(phase));
}
public static ComplexDouble FromDouble(double real)
{
return new ComplexDouble(real, 0.0);
}
public static explicit operator ComplexDouble(double real)
{
return FromDouble(real);
}
public override string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
public string ToString(IFormatProvider formatProvider)
{
return ToString(null, formatProvider);
}
public string ToString(string format, IFormatProvider formatProvider)
{
string text = Real.ToString(format, formatProvider);
string text2 = Math.Abs(Imaginary).ToString(format, formatProvider);
NumberFormatInfo numberFormat = ComplexParser.GetNumberFormat(formatProvider);
string text3 = (Real.IsNegativeZero() ? numberFormat.NegativeSign : null);
string text4 = ((Imaginary < 0.0 || Imaginary.IsNegativeZero()) ? numberFormat.NegativeSign : numberFormat.PositiveSign);
return string.Format(CultureInfo.InvariantCulture, "{0}{1} {2} {3}i", text3, text, text4, text2);
}
public static ComplexDouble Parse(string input)
{
return Parse(input, null);
}
public static ComplexDouble Parse(string input, IFormatProvider provider)
{
if (input == null)
{
throw ExceptionBuilderBase.ArgumentNull("input");
}
if (!ComplexParser.AttemptParse(input, NumberStyles.Float | NumberStyles.AllowThousands, provider, ComplexParser.TryParseDouble, out var real, out var imaginary))
{
throw ExceptionBuilder.InvalidComplexDoubleFormat(input);
}
return new ComplexDouble(real, imaginary);
}
public static bool TryParse(string input, out ComplexDouble result)
{
return TryParse(input, null, out result);
}
public static bool TryParse(string input, IFormatProvider provider, out ComplexDouble result)
{
double real;
double imaginary;
bool result2 = ComplexParser.AttemptParse(input, NumberStyles.Float | NumberStyles.AllowThousands, provider, ComplexParser.TryParseDouble, out real, out imaginary);
result = new ComplexDouble(real, imaginary);
return result2;
}
public static ComplexDouble[] ComposeArray(double[] realData, double[] imaginaryData)
{
if (realData == null)
{
throw ExceptionBuilderBase.ArgumentNull("realData");
}
if (imaginaryData == null)
{
throw ExceptionBuilderBase.ArgumentNull("imaginaryData");
}
int num = realData.Length;
if (num != imaginaryData.Length)
{
throw ExceptionBuilder.ArrayLengthsNotEqual("imaginaryData");
}
ComplexDouble[] array = new ComplexDouble[num];
for (int i = 0; i < num; i++)
{
array[i] = new ComplexDouble(realData[i], imaginaryData[i]);
}
return array;
}
public static ComplexDouble[] ComposeArray(double[] realData, double[] imaginaryData, int startIndex, int length)
{
if (realData == null)
{
throw ExceptionBuilderBase.ArgumentNull("realData");
}
if (imaginaryData == null)
{
throw ExceptionBuilderBase.ArgumentNull("imaginaryData");
}
int num = realData.Length;
if (num != imaginaryData.Length)
{
throw ExceptionBuilder.ArrayLengthsNotEqual("imaginaryData");
}
if (startIndex < realData.GetLowerBound(0) || startIndex >= num)
{
throw ExceptionBuilderBase.ArgumentOutOfRange("startIndex");
}
if (length < 0 || startIndex + length > num)
{
throw ExceptionBuilderBase.ArgumentOutOfRange("length");
}
ComplexDouble[] array = new ComplexDouble[length];
int num2 = startIndex;
int num3 = 0;
while (num2 < startIndex + length)
{
array[num3] = new ComplexDouble(realData[num2], imaginaryData[num2]);
num2++;
num3++;
}
return array;
}
public static ComplexDouble[] ComposeArrayPolar(double[] magnitudes, double[] phases)
{
if (magnitudes == null)
{
throw ExceptionBuilderBase.ArgumentNull("magnitudes");
}
if (phases == null)
{
throw ExceptionBuilderBase.ArgumentNull("phases");
}
int num = magnitudes.Length;
if (num != phases.Length)
{
throw ExceptionBuilder.ArrayLengthsNotEqual("phases");
}
ComplexDouble[] array = new ComplexDouble[num];
for (int i = 0; i < num; i++)
{
array[i] = FromPolar(magnitudes[i], phases[i]);
}
return array;
}
public static ComplexDouble[] ComposeArrayPolar(double[] magnitudes, double[] phases, int startIndex, int length)
{
if (magnitudes == null)
{
throw ExceptionBuilderBase.ArgumentNull("magnitudes");
}
if (phases == null)
{
throw ExceptionBuilderBase.ArgumentNull("phases");
}
int num = magnitudes.Length;
if (num != phases.Length)
{
throw ExceptionBuilder.ArrayLengthsNotEqual("phases");
}
if (startIndex < magnitudes.GetLowerBound(0) || startIndex >= num)
{
throw ExceptionBuilderBase.ArgumentOutOfRange("startIndex");
}
if (length < 0 || startIndex + length > num)
{
throw ExceptionBuilderBase.ArgumentOutOfRange("length");
}
}
}
I know that the error only occurs if I have the real and imaginary properties in IComplexNI. I'm thinking I don't quite understand interfaces fully, since I was thinking that since I have Real and Complex defined in the interface, then the code would know to look for them when looking in the generic type. I guess that's wrong, though. I was guessing that you could reference the properties of objects or structs somehow in generic methods, but it sounds more complicated than what I was hoping..?
You can't write a single method to convert both types. Both types have a Real and an Imaginary property each, but the types of these properties are different.
The ComplexDouble type has Real and Imaginary properties of type double.
The ComplexSingle type has Real and Imaginary properties of type float.
The easiest solution is to use method overloading: You can have two methods with the same name and different parameter types. The compiler will then choose the correct method automatically based on the type of argument you pass to them:
static Complex[] ComplexNItoComplex(IEnumerable<ComplexDouble> NIComplex)
{
return (from c in NIComplex
select new Complex(c.Real, c.Imaginary))
.ToArray();
}
static Complex[] ComplexNItoComplex(IEnumerable<ComplexSingle> NIComplex)
{
return (from c in NIComplex
select new Complex(c.Real, c.Imaginary))
.ToArray();
}
Sample usage:
ComplexDouble cd1 = new ComplexDouble(4, 3);
ComplexDouble cd2 = new ComplexDouble(4, 3);
ComplexDouble cd3 = new ComplexDouble(4, 3);
ComplexSingle cs1 = new ComplexSingle(7, 2);
ComplexSingle cs2 = new ComplexSingle(7, 2);
ComplexSingle cs3 = new ComplexSingle(7, 2);
ComplexDouble[] cd = new ComplexDouble[]{
cd1, cd2, cd3
};
ComplexSingle[] cs = new ComplexSingle[]{
cs1, cs2, cs3
};
Complex[] complexes1 = ComplexNItoComplex(cd);
Complex[] complexes2 = ComplexNItoComplex(cs);
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 implemented a equality comparer in below manner.
class BoxEqualityComparer : IEqualityComparer<Box>
{
public bool Equals(Box b1, Box b2)
{
if (b2 == null && b1 == null)
return true;
else if (b1 == null | b2 == null)
return false;
else if(b1.Height == b2.Height && b1.Length == b2.Length
&& b1.Width == b2.Width)
return true;
else
return false;
}
public int GetHashCode(Box bx)
{
int hCode = bx.Height ^ bx.Length ^ bx.Width;
return hCode.GetHashCode();
}
}
Then I have created a Dictionary, in that I will add some values. So here it will compare object based on it's properties (height, width, length). I am getting the expected output. But I am wondering about the execution of GetHashCode method. I put a breakpoint in there, but I am unable to debug it. My question is when does GeHashCode method will be executed and how many times?
class Example
{
static void Main()
{
BoxEqualityComparer boxEqC = new BoxEqualityComparer();
var boxes = new Dictionary<Box, string>(boxEqC);
var redBox = new Box(4, 3, 4);
AddBox(boxes, redBox, "red");
var blueBox = new Box(4, 3, 4);
AddBox(boxes, blueBox, "blue");
var greenBox = new Box(3, 4, 3);
AddBox(boxes, greenBox, "green");
Console.WriteLine();
Console.WriteLine("The dictionary contains {0} Box objects.",
boxes.Count);
}
private static void AddBox(Dictionary<Box, String> dict, Box box, String name)
{
try {
dict.Add(box, name);
}
catch (ArgumentException e) {
Console.WriteLine("Unable to add {0}: {1}", box, e.Message);
}
}
}
public class Box
{
public Box(int h, int l, int w)
{
this.Height = h;
this.Length = l;
this.Width = w;
}
public int Height { get; set; }
public int Length { get; set; }
public int Width { get; set; }
public override String ToString()
{
return String.Format("({0}, {1}, {2})", Height, Length, Width);
}
}
See https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,fd1acf96113fbda9.
Add(key, value) calls the insert method, which in turn will always calculate a hash code via
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
So in other words, each call to Dictionary.Add should always trigger a calculation of the key's hash via the IEqualityComparer you provided.
As for your example code, this works fine for me, VS 2015 does break at BoxEqualityComparer.GetHashCode() for me.
I would like to create my own DataType called positiveInteger.
I know what you are thinking?
You are thinking that I should use uint here.
But uint contains 0 and I want only positive numbers.
Now you may tell me that Create a class called positiveInteger.
Yes, I can create a Class called positiveIntegerbut I don't know how to implement that class such that this new DataType accepts only positive integer values?
If you want to be able to "accept" values, which are (mostly) compiled as constant int values, then you'll need to implement an implicit conversion to positiveInteger from int
public class positiveInteger
{
public static implicit operator positiveInteger(int source)
{
if(source <= 0) throw new ArgumentOutOfRangeException();
}
}
This will allow you to assign a positiveInteger like so
positiveInteger number = 5;
It will also, however, make it possible to assign an int value
int i = 5;
positiveInteger number = i; // This will throw an exception when i <= 0
An example imlementation could be:
public struct PositiveInteger : IEquatable<PositiveInteger>, IComparable<PositiveInteger>
{
public PositiveInteger(uint value)
{
if (value <= 0) throw new ArgumentOutOfRangeException();
_value = value;
}
public uint Value { get { return _value == 0 ? 1 : _value; } }
private readonly uint _value;
public static implicit operator PositiveInteger(uint value)
{
return new PositiveInteger(value);
}
public static implicit operator uint(PositiveInteger value)
{
return value.Value;
}
public static PositiveInteger operator +(PositiveInteger value1, PositiveInteger value2)
{
var result = value1.Value + value2.Value;
if (result < value1.Value || result < value2.Value)
{
throw new ArgumentOutOfRangeException(); //overflow
}
return result;
}
public static PositiveInteger operator -(PositiveInteger value1, PositiveInteger value2)
{
if (value1.Value < value2.Value) throw new ArgumentOutOfRangeException();
return value1.Value - value2.Value;
}
public override bool Equals(object obj)
{
if (obj is PositiveInteger == false) return false;
return Equals((PositiveInteger)obj);
}
public bool Equals(PositiveInteger other)
{
return Value == other.Value;
}
public override int GetHashCode()
{
return (int)Value;
}
public int CompareTo(PositiveInteger other)
{
if (Value == other.Value) return 0;
return Value < other.Value ? -1 : 1;
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
And a small test:
void Test()
{
var list = new List<PositiveInteger> {5, 1, 3};
list.Sort(); // 1,3,5
var a = new PositiveInteger(1);
var b = new PositiveInteger(2);
var c = a + b; // = 3
var d = c - b; // = 1
var e = d - a; // throws ArgumentOutOfRangeException
}
Try this for your constructor. But it is still not a good idea because you should still validate all data before you use it. Just like checking that a denominator is not zero before blindly dividing.
public class PositiveInteger {
private uint _value;
public PositiveInteger(int x) {
if (x < 1) {
throw new Exception("Invalid value. Value is not positive.");
}
_value = x;
}
}
I need an int array, from an int value.
The int value 123456 converts to int[] {1,2,3,4,5,6}.
Is there any better solution than this:
using System.Diagnostics;
namespace test
{
#if DEBUG
[DebuggerDisplay("{GetDebuggerDisplay()}")]
#endif
public class IntArray
{
#if DEBUG
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
#endif
private int _value;
#if DEBUG
[DebuggerBrowsableAttribute(DebuggerBrowsableState.Never)]
#endif
private int[] _valueArray;
public IntArray(int intValue)
{
Value = intValue;
}
public int Value
{
get { return _value; }
set
{
_value = value;
_valueArray = null;
_valueArray = CreateIntArray(value);
}
}
public int[] Array
{
get { return _valueArray; }
}
private string GetDebuggerDisplay()
{
return string.Format("Value = {0}", Value);
}
private static int[] CreateIntArray(int value)
{
string s = value.ToString();
var intArray = new int[s.Length];
for (int i = 0; i < s.Length; i++)
intArray[i] = int.Parse(s[i].ToString());
return intArray;
}
}
}
Any help and criticism would be appreciated.
You can do as following using Linq. This is only the making of the array from the int value.
var arrayOfInts = myint.ToString().Select(i => int.Parse(i.ToString())).ToArray();
EDIT :
This can also be made as a extension method on int if you want to use this often.
public static class IntExtensions
{
public static int[] ToArray(this int i)
{
return i.ToString().Select(c => int.Parse(c.ToString())).ToArray();
}
}
Then you can use this extension by doing this :
var myArray = 123456.ToArray();
You may convert to int to String, later you can use LINQ to Convert each character to integer and then return an array of integers using .ToArray()
int a = 123456;
string tempA = a.ToString();
int[] temp = tempA.Select(r => Convert.ToInt32(r.ToString())).ToArray();
EDIT:
As per Styxxy comment:
int a = 123456;
int[] array = new int[a.ToString().Length];
int i = array.Length - 1;
while (a > 0)
{
array[i--] = a % 10;
a = a / 10;
}
Another approach:
public static int[] GetInts(this int value)
{
if (value == 0)
return new int[] { 0 };
else
{
int val = value;
List<int> values = new List<int>();
while (Math.Abs(val) >= 1)
{
values.Add(Math.Abs(val % 10));
val = val / 10;
}
values.Reverse();
return values.ToArray();
}
}
and use it:
int value = 123456;
int[] values = value.GetInts();
Edit: improved to work with negative numbers and zero
var res = 123456.ToString().Select(c => Int32.Parse(c.ToString())).ToArray();
Another way using char.GetNumericValue:
int[] ints = 123456.ToString().Select(c => (int)char.GetNumericValue(c)).ToArray();
or without Linq:
var chars = 123456.ToString();
int[] ints = new int[chars.Length];
for (int i = 0; i < chars.Length; i++)
ints[i] = (int)char.GetNumericValue(chars[i]);
As said in the comments, it is better to use basic arithmetic operations, rather than converting to a string, looping through a string and parsing strings to integers.
Here is an example (I made an extension method for an integer):
static class IntegerExtensions
{
public static int[] ToCypherArray(this int value)
{
var cyphers = new List<int>();
do
{
cyphers.Add(value % 10);
value = value / 10;
} while (value != 0);
cyphers.Reverse();
return cyphers.ToArray();
}
}
class Program
{
static void Main(string[] args)
{
int myNumber = 123456789;
int[] cypherArray = myNumber.ToCypherArray();
Array.ForEach(cypherArray, (i) => Console.WriteLine(i));
Console.ReadLine();
}
}
I'd like to create my own class extending array of ints. Is that possible? What I need is array of ints that can be added by "+" operator to another array (each element added to each), and compared by "==", so it could (hopefully) be used as a key in dictionary.
The thing is I don't want to implement whole IList interface to my new class, but only add those two operators to existing array class.
I'm trying to do something like this:
class MyArray : Array<int>
But it's not working that way obviously ;).
Sorry if I'm unclear but I'm searching solution for hours now...
UPDATE:
I tried something like this:
class Zmienne : IEquatable<Zmienne>
{
public int[] x;
public Zmienne(int ilosc)
{
x = new int[ilosc];
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return base.Equals((Zmienne)obj);
}
public bool Equals(Zmienne drugie)
{
if (x.Length != drugie.x.Length)
return false;
else
{
for (int i = 0; i < x.Length; i++)
{
if (x[i] != drugie.x[i])
return false;
}
}
return true;
}
public override int GetHashCode()
{
int hash = x[0].GetHashCode();
for (int i = 1; i < x.Length; i++)
hash = hash ^ x[i].GetHashCode();
return hash;
}
}
Then use it like this:
Zmienne tab1 = new Zmienne(2);
Zmienne tab2 = new Zmienne(2);
tab1.x[0] = 1;
tab1.x[1] = 1;
tab2.x[0] = 1;
tab2.x[1] = 1;
if (tab1 == tab2)
Console.WriteLine("Works!");
And no effect. I'm not good with interfaces and overriding methods unfortunately :(. As for reason I'm trying to do it. I have some equations like:
x1 + x2 = 0.45
x1 + x4 = 0.2
x2 + x4 = 0.11
There are a lot more of them, and I need to for example add first equation to second and search all others to find out if there is any that matches the combination of x'es resulting in that adding.
Maybe I'm going in totally wrong direction?
For a single type, it is pretty easy to encapsulate, as below. Note that as a key you want to make it immutable too. If you want to use generics, it gets harder (ask for more info):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
static class Program {
static void Main() {
MyVector x = new MyVector(1, 2, 3), y = new MyVector(1, 2, 3),
z = new MyVector(4,5,6);
Console.WriteLine(x == y); // true
Console.WriteLine(x == z); // false
Console.WriteLine(object.Equals(x, y)); // true
Console.WriteLine(object.Equals(x, z)); // false
var comparer = EqualityComparer<MyVector>.Default;
Console.WriteLine(comparer.GetHashCode(x)); // should match y
Console.WriteLine(comparer.GetHashCode(y)); // should match x
Console.WriteLine(comparer.GetHashCode(z)); // *probably* different
Console.WriteLine(comparer.Equals(x,y)); // true
Console.WriteLine(comparer.Equals(x,z)); // false
MyVector sum = x + z;
Console.WriteLine(sum);
}
}
public sealed class MyVector : IEquatable<MyVector>, IEnumerable<int> {
private readonly int[] data;
public int this[int index] {
get { return data[index]; }
}
public MyVector(params int[] data) {
if (data == null) throw new ArgumentNullException("data");
this.data = (int[])data.Clone();
}
private int? hash;
public override int GetHashCode() {
if (hash == null) {
int result = 13;
for (int i = 0; i < data.Length; i++) {
result = (result * 7) + data[i];
}
hash = result;
}
return hash.GetValueOrDefault();
}
public int Length { get { return data.Length; } }
public IEnumerator<int> GetEnumerator() {
for (int i = 0; i < data.Length; i++) {
yield return data[i];
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public override bool Equals(object obj)
{
return this == (obj as MyVector);
}
public bool Equals(MyVector obj) {
return this == obj;
}
public override string ToString() {
StringBuilder sb = new StringBuilder("[");
if (data.Length > 0) sb.Append(data[0]);
for (int i = 1; i < data.Length; i++) {
sb.Append(',').Append(data[i]);
}
sb.Append(']');
return sb.ToString();
}
public static bool operator ==(MyVector x, MyVector y) {
if(ReferenceEquals(x,y)) return true;
if(ReferenceEquals(x,null) || ReferenceEquals(y,null)) return false;
if (x.hash.HasValue && y.hash.HasValue && // exploit known different hash
x.hash.GetValueOrDefault() != y.hash.GetValueOrDefault()) return false;
int[] xdata = x.data, ydata = y.data;
if(xdata.Length != ydata.Length) return false;
for(int i = 0 ; i < xdata.Length ; i++) {
if(xdata[i] != ydata[i]) return false;
}
return true;
}
public static bool operator != (MyVector x, MyVector y) {
return !(x==y);
}
public static MyVector operator +(MyVector x, MyVector y) {
if(x==null || y == null) throw new ArgumentNullException();
int[] xdata = x.data, ydata = y.data;
if(xdata.Length != ydata.Length) throw new InvalidOperationException("Length mismatch");
int[] result = new int[xdata.Length];
for(int i = 0 ; i < xdata.Length ; i++) {
result[i] = xdata[i] + ydata[i];
}
return new MyVector(result);
}
}
Its not permitted to extend the array class, see the reference: http://msdn.microsoft.com/en-us/library/system.array.aspx
You could either implement IList (which has the basic methods), or encapsulate an Array in your class and provide conversion operators.
Please let me know if you need more detail.
Can you not just use the List class? This already does what you want via the AddRange method.
implement the ienumerable interface