/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq.Expressions;
using static QuantConnect.Util.ExpressionBuilder;
namespace QuantConnect
{
///
/// Enumeration class defining binary comparisons and providing access to expressions and functions
/// capable of evaluating a particular comparison for any type. If a particular type does not implement
/// a binary comparison than an exception will be thrown.
///
public class BinaryComparison
{
///
/// Gets the equivalent of
///
public static readonly BinaryComparison Equal = new BinaryComparison(ExpressionType.Equal);
///
/// Gets the equivalent of
///
public static readonly BinaryComparison NotEqual = new BinaryComparison(ExpressionType.NotEqual);
///
/// Gets the equivalent of
///
public static readonly BinaryComparison LessThan = new BinaryComparison(ExpressionType.LessThan);
///
/// Gets the equivalent of
///
public static readonly BinaryComparison GreaterThan = new BinaryComparison(ExpressionType.GreaterThan);
///
/// Gets the equivalent of
///
public static readonly BinaryComparison LessThanOrEqual = new BinaryComparison(ExpressionType.LessThanOrEqual);
///
/// Gets the equivalent of
///
public static readonly BinaryComparison GreaterThanOrEqual = new BinaryComparison(ExpressionType.GreaterThanOrEqual);
///
/// Gets the matching the provided
///
public static BinaryComparison FromExpressionType(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal: return Equal;
case ExpressionType.NotEqual: return NotEqual;
case ExpressionType.LessThan: return LessThan;
case ExpressionType.LessThanOrEqual: return LessThanOrEqual;
case ExpressionType.GreaterThan: return GreaterThan;
case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqual;
default:
throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
}
}
///
/// Gets the expression type defining the binary comparison.
///
public ExpressionType Type { get; }
private BinaryComparison(ExpressionType type)
{
Type = type;
}
///
/// Evaluates the specified and according to this
///
public bool Evaluate(T left, T right)
=> OfType.GetFunc(Type)(left, right);
///
/// Gets a function capable of performing this
///
public Func GetEvaluator()
=> OfType.GetFunc(Type);
///
/// Gets an expression representing this
///
public Expression> GetExpression()
=> OfType.GetExpression(Type);
///
/// Flips the logic ordering of the comparison's operands. For example,
/// is converted into
///
public BinaryComparison FlipOperands()
{
switch (Type)
{
case ExpressionType.Equal: return this;
case ExpressionType.NotEqual: return this;
case ExpressionType.LessThan: return GreaterThan;
case ExpressionType.LessThanOrEqual: return GreaterThanOrEqual;
case ExpressionType.GreaterThan: return LessThan;
case ExpressionType.GreaterThanOrEqual: return LessThanOrEqual;
default:
throw new Exception(
"The skies are falling and the oceans are rising! " +
"If you've made it here then this exception is the least of your worries! " +
$"ExpressionType: {Type}"
);
}
}
/// Returns a string that represents the current object.
/// A string that represents the current object.
public override string ToString()
{
return Type.ToString();
}
///
/// Provides thread-safe lookups of expressions and functions for binary comparisons by type.
/// MUCH faster than using a concurrency dictionary, for example, as it's expanded at runtime
/// and hard-linked, no look-up is actually performed!
///
private static class OfType
{
private static readonly Expression> EqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.Equal);
private static readonly Expression> NotEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.NotEqual);
private static readonly Expression> LessThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThan);
private static readonly Expression> LessThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThanOrEqual);
private static readonly Expression> GreaterThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThan);
private static readonly Expression> GreaterThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThanOrEqual);
public static Expression> GetExpression(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal: return EqualExpr;
case ExpressionType.NotEqual: return NotEqualExpr;
case ExpressionType.LessThan: return LessThanExpr;
case ExpressionType.LessThanOrEqual: return LessThanOrEqualExpr;
case ExpressionType.GreaterThan: return GreaterThanExpr;
case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualExpr;
default:
throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
}
}
private static readonly Func EqualFunc = EqualExpr?.Compile();
private static readonly Func NotEqualFunc = NotEqualExpr?.Compile();
private static readonly Func LessThanFunc = LessThanExpr?.Compile();
private static readonly Func LessThanOrEqualFunc = LessThanOrEqualExpr?.Compile();
private static readonly Func GreaterThanFunc = GreaterThanExpr?.Compile();
private static readonly Func GreaterThanOrEqualFunc = GreaterThanOrEqualExpr?.Compile();
public static Func GetFunc(ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal: return EqualFunc;
case ExpressionType.NotEqual: return NotEqualFunc;
case ExpressionType.LessThan: return LessThanFunc;
case ExpressionType.LessThanOrEqual: return LessThanOrEqualFunc;
case ExpressionType.GreaterThan: return GreaterThanFunc;
case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualFunc;
default:
throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
}
}
private static Expression> MakeBinaryComparisonLambdaOrNull(ExpressionType type)
{
try
{
return MakeBinaryComparisonLambda(type);
}
catch
{
return null;
}
}
}
}
}