Lean  $LEAN_TAG$
BinaryComparison.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Linq.Expressions;
19 
20 namespace QuantConnect
21 {
22  /// <summary>
23  /// Enumeration class defining binary comparisons and providing access to expressions and functions
24  /// capable of evaluating a particular comparison for any type. If a particular type does not implement
25  /// a binary comparison than an exception will be thrown.
26  /// </summary>
27  public class BinaryComparison
28  {
29  /// <summary>
30  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.Equal"/>
31  /// </summary>
32  public static readonly BinaryComparison Equal = new BinaryComparison(ExpressionType.Equal);
33 
34  /// <summary>
35  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.NotEqual"/>
36  /// </summary>
37  public static readonly BinaryComparison NotEqual = new BinaryComparison(ExpressionType.NotEqual);
38 
39  /// <summary>
40  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.LessThan"/>
41  /// </summary>
42  public static readonly BinaryComparison LessThan = new BinaryComparison(ExpressionType.LessThan);
43 
44  /// <summary>
45  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.GreaterThan"/>
46  /// </summary>
47  public static readonly BinaryComparison GreaterThan = new BinaryComparison(ExpressionType.GreaterThan);
48 
49  /// <summary>
50  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.LessThanOrEqual"/>
51  /// </summary>
52  public static readonly BinaryComparison LessThanOrEqual = new BinaryComparison(ExpressionType.LessThanOrEqual);
53 
54  /// <summary>
55  /// Gets the <see cref="BinaryComparison"/> equivalent of <see cref="ExpressionType.GreaterThanOrEqual"/>
56  /// </summary>
57  public static readonly BinaryComparison GreaterThanOrEqual = new BinaryComparison(ExpressionType.GreaterThanOrEqual);
58 
59  /// <summary>
60  /// Gets the <see cref="BinaryComparison"/> matching the provided <paramref name="type"/>
61  /// </summary>
62  public static BinaryComparison FromExpressionType(ExpressionType type)
63  {
64  switch (type)
65  {
66  case ExpressionType.Equal: return Equal;
67  case ExpressionType.NotEqual: return NotEqual;
68  case ExpressionType.LessThan: return LessThan;
69  case ExpressionType.LessThanOrEqual: return LessThanOrEqual;
70  case ExpressionType.GreaterThan: return GreaterThan;
71  case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqual;
72  default:
73  throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
74  }
75  }
76 
77  /// <summary>
78  /// Gets the expression type defining the binary comparison.
79  /// </summary>
80  public ExpressionType Type { get; }
81 
82  private BinaryComparison(ExpressionType type)
83  {
84  Type = type;
85  }
86 
87  /// <summary>
88  /// Evaluates the specified <paramref name="left"/> and <paramref name="right"/> according to this <see cref="BinaryComparison"/>
89  /// </summary>
90  public bool Evaluate<T>(T left, T right)
91  => OfType<T>.GetFunc(Type)(left, right);
92 
93  /// <summary>
94  /// Gets a function capable of performing this <see cref="BinaryComparison"/>
95  /// </summary>
96  public Func<T, T, bool> GetEvaluator<T>()
97  => OfType<T>.GetFunc(Type);
98 
99  /// <summary>
100  /// Gets an expression representing this <see cref="BinaryComparison"/>
101  /// </summary>
102  public Expression<Func<T, T, bool>> GetExpression<T>()
103  => OfType<T>.GetExpression(Type);
104 
105  /// <summary>
106  /// Flips the logic ordering of the comparison's operands. For example, <see cref="LessThan"/>
107  /// is converted into <see cref="GreaterThan"/>
108  /// </summary>
110  {
111  switch (Type)
112  {
113  case ExpressionType.Equal: return this;
114  case ExpressionType.NotEqual: return this;
115  case ExpressionType.LessThan: return GreaterThan;
116  case ExpressionType.LessThanOrEqual: return GreaterThanOrEqual;
117  case ExpressionType.GreaterThan: return LessThan;
118  case ExpressionType.GreaterThanOrEqual: return LessThanOrEqual;
119  default:
120  throw new Exception(
121  "The skies are falling and the oceans are rising! " +
122  "If you've made it here then this exception is the least of your worries! " +
123  $"ExpressionType: {Type}"
124  );
125  }
126  }
127 
128  /// <summary>Returns a string that represents the current object.</summary>
129  /// <returns>A string that represents the current object.</returns>
130  public override string ToString()
131  {
132  return Type.ToString();
133  }
134 
135  /// <summary>
136  /// Provides thread-safe lookups of expressions and functions for binary comparisons by type.
137  /// MUCH faster than using a concurrency dictionary, for example, as it's expanded at runtime
138  /// and hard-linked, no look-up is actually performed!
139  /// </summary>
140  private static class OfType<T>
141  {
142  private static readonly Expression<Func<T, T, bool>> EqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.Equal);
143  private static readonly Expression<Func<T, T, bool>> NotEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.NotEqual);
144  private static readonly Expression<Func<T, T, bool>> LessThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThan);
145  private static readonly Expression<Func<T, T, bool>> LessThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThanOrEqual);
146  private static readonly Expression<Func<T, T, bool>> GreaterThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThan);
147  private static readonly Expression<Func<T, T, bool>> GreaterThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThanOrEqual);
148 
149  public static Expression<Func<T, T, bool>> GetExpression(ExpressionType type)
150  {
151  switch (type)
152  {
153  case ExpressionType.Equal: return EqualExpr;
154  case ExpressionType.NotEqual: return NotEqualExpr;
155  case ExpressionType.LessThan: return LessThanExpr;
156  case ExpressionType.LessThanOrEqual: return LessThanOrEqualExpr;
157  case ExpressionType.GreaterThan: return GreaterThanExpr;
158  case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualExpr;
159  default:
160  throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
161  }
162  }
163 
164  private static readonly Func<T, T, bool> EqualFunc = EqualExpr?.Compile();
165  private static readonly Func<T, T, bool> NotEqualFunc = NotEqualExpr?.Compile();
166  private static readonly Func<T, T, bool> LessThanFunc = LessThanExpr?.Compile();
167  private static readonly Func<T, T, bool> LessThanOrEqualFunc = LessThanOrEqualExpr?.Compile();
168  private static readonly Func<T, T, bool> GreaterThanFunc = GreaterThanExpr?.Compile();
169  private static readonly Func<T, T, bool> GreaterThanOrEqualFunc = GreaterThanOrEqualExpr?.Compile();
170 
171  public static Func<T, T, bool> GetFunc(ExpressionType type)
172  {
173  switch (type)
174  {
175  case ExpressionType.Equal: return EqualFunc;
176  case ExpressionType.NotEqual: return NotEqualFunc;
177  case ExpressionType.LessThan: return LessThanFunc;
178  case ExpressionType.LessThanOrEqual: return LessThanOrEqualFunc;
179  case ExpressionType.GreaterThan: return GreaterThanFunc;
180  case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualFunc;
181  default:
182  throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison.");
183  }
184  }
185 
186  private static Expression<Func<T, T, bool>> MakeBinaryComparisonLambdaOrNull(ExpressionType type)
187  {
188  try
189  {
190  return MakeBinaryComparisonLambda<T>(type);
191  }
192  catch
193  {
194  return null;
195  }
196  }
197  }
198  }
199 }