protected static PrimitiveExpression BinaryOp( int index, int length, PrimitiveExpression leftOperand, PrimitiveExpression rightOperand, Func <long, long, long> longOperation, Func <BigInteger, BigInteger, BigInteger> bigOperation, Func <decimal, decimal, decimal> decimalOperation ) { Debug.Assert(leftOperand.Type == rightOperand.Type); if (leftOperand.Type == PrimitiveType.IntegerLong) { // try natively long try { return(new PrimitiveExpression( index, length, longOperation(leftOperand.LongValue, rightOperand.LongValue) )); } catch (OverflowException) { } // try promoting to BigInteger try { return(new PrimitiveExpression( index, length, bigOperation(leftOperand.LongValue, rightOperand.LongValue) )); } catch (OverflowException) { } // last attempt: decimal return(new PrimitiveExpression( index, length, decimalOperation(leftOperand.LongValue, rightOperand.LongValue) )); } else if (leftOperand.Type == PrimitiveType.IntegerBig) { // try natively BigInteger try { return(new PrimitiveExpression( index, length, bigOperation(leftOperand.BigIntegerValue, rightOperand.BigIntegerValue) )); } catch (OverflowException) { } // last attempt: decimal return(new PrimitiveExpression( index, length, decimalOperation( checked ((decimal)leftOperand.BigIntegerValue), checked ((decimal)rightOperand.BigIntegerValue) ) )); } else if (leftOperand.Type == PrimitiveType.Decimal) { // just go decimal return(new PrimitiveExpression( index, length, decimalOperation(leftOperand.DecimalValue, rightOperand.DecimalValue) )); } else { Debug.Fail($"Unexpected primitive expression type '{leftOperand.Type}'."); return(new PrimitiveExpression(index, length, (long)0)); } }
public override PrimitiveExpression Simplified(Grimoire grimoire, CalcTimer timer) { timer.ThrowIfTimedOut(); PrimitiveExpression primLeft = LeftSide.Simplified(grimoire, timer); timer.ThrowIfTimedOut(); PrimitiveExpression primRight = RightSide.Simplified(grimoire, timer); timer.ThrowIfTimedOut(); // type check switch (Operation) { case Operation.BinaryAnd: case Operation.BinaryOr: case Operation.BinaryXor: if (primLeft.Type == PrimitiveType.Decimal || primRight.Type == PrimitiveType.Decimal) { throw new SimplificationException( "Cannot perform a bitwise operation on a floating-point number.", this ); } break; default: break; } try { // mixed types? coerce if (primLeft.Type == PrimitiveType.IntegerLong) { if (primRight.Type == PrimitiveType.IntegerBig) { // IntegerLong < IntegerBig primLeft = new PrimitiveExpression(primLeft.Index, primLeft.Length, new BigInteger(primLeft.LongValue)); } else if (primRight.Type == PrimitiveType.Decimal) { // IntegerLong < Decimal primLeft = new PrimitiveExpression(primLeft.Index, primLeft.Length, (decimal)primLeft.LongValue); } } else if (primLeft.Type == PrimitiveType.IntegerBig) { if (primRight.Type == PrimitiveType.IntegerLong) { // IntegerBig > IntegerLong primRight = new PrimitiveExpression(primRight.Index, primRight.Length, new BigInteger(primRight.LongValue)); } else if (primRight.Type == PrimitiveType.Decimal) { // IntegerBig < Decimal primLeft = new PrimitiveExpression(primLeft.Index, primLeft.Length, (decimal)primLeft.BigIntegerValue); } } else if (primLeft.Type == PrimitiveType.Decimal) { if (primRight.Type == PrimitiveType.IntegerLong) { // Decimal > IntegerLong primRight = new PrimitiveExpression(primRight.Index, primRight.Length, (decimal)primRight.LongValue); } else if (primRight.Type == PrimitiveType.IntegerBig) { // Decimal > IntegerBig primRight = new PrimitiveExpression(primRight.Index, primRight.Length, (decimal)primRight.BigIntegerValue); } } } catch (OverflowException ex) { throw new SimplificationException(this, ex); } catch (DivideByZeroException ex) { throw new SimplificationException(this, ex); } catch (FunctionDomainException ex) { throw new SimplificationException(this, ex); } catch (TimeoutException ex) { throw new SimplificationException(this, ex); } timer.ThrowIfTimedOut(); Debug.Assert(primLeft.Type == primRight.Type); int newIndex = primLeft.Index; int newLength = primRight.Index + primRight.Length - primLeft.Index; try { switch (Operation) { case Operation.Add: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a + b), (a, b) => checked (a + b), (a, b) => checked (a + b) )); case Operation.Divide: return(new PrimitiveExpression( newIndex, newLength, checked (primLeft.ToDecimal() / primRight.ToDecimal()) )); case Operation.IntegralDivide: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a / b), (a, b) => checked (a / b), (a, b) => Math.Truncate(checked (a / b)) )); case Operation.Multiply: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a * b), (a, b) => checked (a * b), (a, b) => checked (a * b) )); case Operation.Power: if (primLeft.Type == PrimitiveType.IntegerLong) { return(Pow(newIndex, newLength, primLeft.LongValue, primRight.LongValue, timer)); } else if (primLeft.Type == PrimitiveType.IntegerBig) { return(Pow(newIndex, newLength, primLeft.BigIntegerValue, primRight.BigIntegerValue, timer)); } else if (primLeft.Type == PrimitiveType.Decimal) { return(Pow(newIndex, newLength, primLeft.DecimalValue, primRight.DecimalValue, timer)); } break; case Operation.Remainder: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a % b), (a, b) => checked (a % b), (a, b) => checked (a % b) )); case Operation.Subtract: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a - b), (a, b) => checked (a - b), (a, b) => checked (a - b) )); case Operation.BinaryAnd: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a & b), (a, b) => checked (a & b), null )); case Operation.BinaryOr: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a | b), (a, b) => checked (a | b), null )); case Operation.BinaryXor: return(BinaryOp( newIndex, newLength, primLeft, primRight, (a, b) => checked (a ^ b), (a, b) => checked (a ^ b), null )); } } catch (OverflowException ex) { throw new SimplificationException(this, ex); } catch (DivideByZeroException ex) { throw new SimplificationException(this, ex); } catch (FunctionDomainException ex) { throw new SimplificationException(this, ex); } catch (TimeoutException ex) { throw new SimplificationException(this, ex); } throw new SimplificationException($"Cannot handle binary operator {Operation}.", this); }
public override PrimitiveExpression Simplified(Grimoire grimoire, CalcTimer timer) { timer.ThrowIfTimedOut(); PrimitiveExpression primOperand = Operand.Simplified(grimoire, timer); try { switch (Operation) { case Operation.Negate: if (primOperand.Type == PrimitiveType.IntegerLong) { return(new PrimitiveExpression(Index, Length, -primOperand.LongValue)); } else if (primOperand.Type == PrimitiveType.IntegerBig) { return(new PrimitiveExpression(Index, Length, -primOperand.BigIntegerValue)); } else if (primOperand.Type == PrimitiveType.Decimal) { return(new PrimitiveExpression(Index, Length, -primOperand.DecimalValue)); } break; case Operation.Factorial: if (primOperand.Type == PrimitiveType.IntegerLong) { return(new PrimitiveExpression(Index, Length, MathFuncs.Factorial(primOperand.LongValue, timer))); } else if (primOperand.Type == PrimitiveType.IntegerBig) { return(new PrimitiveExpression(Index, Length, MathFuncs.Factorial(primOperand.BigIntegerValue, timer))); } else if (primOperand.Type == PrimitiveType.Decimal) { throw new FunctionDomainException("Factorials are not defined on fractional numbers."); } break; default: break; } } catch (OverflowException ex) { throw new SimplificationException(this, ex); } catch (DivideByZeroException ex) { throw new SimplificationException(this, ex); } catch (FunctionDomainException ex) { throw new SimplificationException(this, ex); } catch (TimeoutException ex) { throw new SimplificationException(this, ex); } throw new SimplificationException($"Cannot handle unary operator {Operation}.", this); }