public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { if (resultOperator == null) { throw new ArgumentNullException("resultOperator"); } if (cc == null) { throw new ArgumentNullException("CodeContext can't be null"); } // Determine the type of the result operator we are processing and // anything we need to know about it. Type sumType; sumType = cc.LoopVariable.Type; bool doAverage = false; if (resultOperator is SumResultOperator) { doAverage = false; } else { doAverage = true; } // We only know how to sum basic types if (!sumType.IsNumberType()) { throw new InvalidOperationException(string.Format("Do not know how to generate C++ to sum type {0}.", sumType.Name)); } var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(sumType); accumulator.SetInitialValue("0"); // Sum and average are a alike in that we are going to add everything we see up. var add = Expression.Add(accumulator, cc.LoopVariable); var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container); gc.Add(new StatementAggregate(accumulator, addResolved)); // If this is a sum no further work needs to happen. if (!doAverage) { return(accumulator); } // If this is a average then we need to add a simple count. var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); counter.SetInitialValue("0"); var incbyone = Expression.Add(counter, Expression.Constant(1)); gc.Add(new StatementAggregate(counter, ExpressionToCPP.GetExpression(incbyone, gc, cc, container))); // Next, we have to delcare the counter and the accumulator. These are now both temprorary variables. if (cc.LoopIndexVariable == null) { throw new AverageNotAllowedAtTopLevelException("Attempt to use Average at top level, accross events. Not currently implemented."); } gc.AddOutsideLoop(counter); gc.AddOutsideLoop(accumulator); // It is an error to average a sequence with no elements. So we need to throw a C++ exception. We need to pop up out of the loop in order // to do this. // http://msdn.microsoft.com/en-us/library/bb354760.aspx (for specs on Average on this). var testForSomething = Expression.Equal(counter, Expression.Constant(0)); gc.AddAtResultScope(new StatementThrowIfTrue(ExpressionToCPP.GetExpression(testForSomething, gc, cc, container), "Can't take an average of a null sequence")); var returnType = DetermineAverageReturnType(sumType); var faccumulator = Expression.Convert(accumulator, returnType); var fcount = Expression.Convert(counter, returnType); var divide = Expression.Divide(faccumulator, fcount); // We are done with this calculation, so pop up and out. gc.Pop(); return(divide); }
/// <summary> /// Process the First/last. This means adding a pointer (or not if we are looking at a plane type) and /// then filling it till it is full or filling it till the loop is done. Bomb out if we are asked to at the end!! /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { /// /// First, do data normalization /// var asFirst = resultOperator as FirstResultOperator; var asLast = resultOperator as LastResultOperator; if (asFirst == null && asLast == null) { throw new ArgumentNullException("First/Last operator must be either first or last, and not null!"); } bool isFirst = asFirst != null; bool bombIfNothing = true; if (isFirst) { bombIfNothing = !asFirst.ReturnDefaultWhenEmpty; } else { bombIfNothing = !asLast.ReturnDefaultWhenEmpty; } // // Figure out if we need to cache the result: // - simple variable which has a default value which can be used later on. // like a double, etc. // - We actually allow for a default variable. // bool cacheResult = cc.LoopVariable.Type.IsNumberType(); cacheResult = cacheResult && !bombIfNothing; // // Next, make sure we are looping over something. This had better be an array we are looking at! // if (cc.LoopIndexVariable == null) { throw new InvalidOperationException(string.Format("Can't apply First operator when we aren't looping over some well formed array '{0}'", cc.LoopVariable.ToString())); } var indexExpr = cc.LoopIndexVariable; // // We need to hold onto either the first or the last item here, so we create a statement that holds nnto the // first or the last time. It also has to mark the thing as valid! It will break when it is done. // While the bool can be used later on to get at the exception we might be throwing, the actual // result may be used much further on down. To protect against that, we set the array index to be -1, // and then hope there is a crash later on! :-) // // It is possible that this is part of a dual selection. For example, if you are interested in the jet that has the closest track, and the // loop is constructed over the jets first, and then the tracks. This First will likely be for a track index, but we will be looking up the // track later. So we need to record both the jet and track index. To get the other indicies, we just look for all loop variables between here and // the result scope. // var valueWasSeen = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); var indexSeen = DeclarableParameter.CreateDeclarableParameterExpression(indexExpr.Type); if (indexSeen.Type.IsNumberType()) indexSeen.SetInitialValue("-1"); gc.AddAtResultScope(valueWasSeen); gc.AddAtResultScope(indexSeen); var rv = new Statements.StatementRecordValue(indexSeen, indexExpr, valueWasSeen, isFirst); gc.Add(rv); foreach (var v in gc.GetUsedQuerySourceVariables(rv, indexExpr)) { var saver = DeclarableParameter.CreateDeclarableParameterExpression(v.Type); gc.AddAtResultScope(saver); rv.AddNewSaver(saver, v); cc.Add(v.RawValue, saver); } gc.Pop(true); if (bombIfNothing) { var test = ExpressionToCPP.GetExpression(Expression.Not(valueWasSeen), gc, cc, container); gc.Add(new Statements.StatementThrowIfTrue(test, string.Format("First/Last predicate executed on a null sequence: {0}", queryModel.ToString()))); } // // Finally, we need the new expression. For this we basically just ask for the translated expression. We // also add a substitution for later on for more complex expressions. // var firstlastValue = cc.LoopVariable; cc.Add(indexExpr.RawValue, indexSeen); Debug.WriteLine("First/Last: {0} for QM {1}", indexSeen.ToString(), queryModel.ToString()); // Reset the expression we are looking at in the loop. var newIndexExpr = firstlastValue.ReplaceSubExpression(indexExpr.AsExpression(), indexSeen); cc.SetLoopVariable(newIndexExpr, indexSeen); if (cacheResult) { // // Set the default value // var actualValue = DeclarableParameter.CreateDeclarableParameterExpression(cc.LoopVariable.Type); actualValue.SetInitialValue("0"); // // If everything went well, then we can do the assignment. Otherwise, we leave // it as above (having the default value). // gc.Add(new Statements.StatementFilter(valueWasSeen)); gc.Add(new Statements.StatementAssign(actualValue, ExpressionToCPP.GetExpression(firstlastValue, gc, cc, container))); gc.Pop(); return actualValue; } else { // No need to cache the result - so no need to add extra code. return newIndexExpr; } }
/// <summary> /// Process the First/last. This means adding a pointer (or not if we are looking at a plane type) and /// then filling it till it is full or filling it till the loop is done. Bomb out if we are asked to at the end!! /// </summary> /// <param name="resultOperator"></param> /// <param name="queryModel"></param> /// <param name="_codeEnv"></param> /// <returns></returns> public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { /// /// First, do data normalization /// var asFirst = resultOperator as FirstResultOperator; var asLast = resultOperator as LastResultOperator; if (asFirst == null && asLast == null) { throw new ArgumentNullException("First/Last operator must be either first or last, and not null!"); } bool isFirst = asFirst != null; bool bombIfNothing = true; if (isFirst) { bombIfNothing = !asFirst.ReturnDefaultWhenEmpty; } else { bombIfNothing = !asLast.ReturnDefaultWhenEmpty; } // // Figure out if we need to cache the result: // - simple variable which has a default value which can be used later on. // like a double, etc. // - We actually allow for a default variable. // bool cacheResult = cc.LoopVariable.Type.IsNumberType(); cacheResult = cacheResult && !bombIfNothing; // // Next, make sure we are looping over something. This had better be an array we are looking at! // if (cc.LoopIndexVariable == null) { throw new InvalidOperationException(string.Format("Can't apply First operator when we aren't looping over some well formed array '{0}'", cc.LoopVariable.ToString())); } var indexExpr = cc.LoopIndexVariable; // // We need to hold onto either the first or the last item here, so we create a statement that holds nnto the // first or the last time. It also has to mark the thing as valid! It will break when it is done. // While the bool can be used later on to get at the exception we might be throwing, the actual // result may be used much further on down. To protect against that, we set the array index to be -1, // and then hope there is a crash later on! :-) // // It is possible that this is part of a dual selection. For example, if you are interested in the jet that has the closest track, and the // loop is constructed over the jets first, and then the tracks. This First will likely be for a track index, but we will be looking up the // track later. So we need to record both the jet and track index. To get the other indicies, we just look for all loop variables between here and // the result scope. // var valueWasSeen = DeclarableParameter.CreateDeclarableParameterExpression(typeof(bool)); var indexSeen = DeclarableParameter.CreateDeclarableParameterExpression(indexExpr.Type); if (indexSeen.Type.IsNumberType()) { indexSeen.SetInitialValue("-1"); } gc.AddAtResultScope(valueWasSeen); gc.AddAtResultScope(indexSeen); var rv = new Statements.StatementRecordValue(indexSeen, indexExpr, valueWasSeen, isFirst); gc.Add(rv); foreach (var v in gc.GetUsedQuerySourceVariables(rv, indexExpr)) { var saver = DeclarableParameter.CreateDeclarableParameterExpression(v.Type); gc.AddAtResultScope(saver); rv.AddNewSaver(saver, v); cc.Add(v.RawValue, saver); } gc.Pop(true); if (bombIfNothing) { var test = ExpressionToCPP.GetExpression(Expression.Not(valueWasSeen), gc, cc, container); gc.Add(new Statements.StatementThrowIfTrue(test, string.Format("First/Last predicate executed on a null sequence: {0}", queryModel.ToString()))); } // // Finally, we need the new expression. For this we basically just ask for the translated expression. We // also add a substitution for later on for more complex expressions. // var firstlastValue = cc.LoopVariable; cc.Add(indexExpr.RawValue, indexSeen); Debug.WriteLine("First/Last: {0} for QM {1}", indexSeen.ToString(), queryModel.ToString()); // Reset the expression we are looking at in the loop. var newIndexExpr = firstlastValue.ReplaceSubExpression(indexExpr.AsExpression(), indexSeen); cc.SetLoopVariable(newIndexExpr, indexSeen); if (cacheResult) { // // Set the default value // var actualValue = DeclarableParameter.CreateDeclarableParameterExpression(cc.LoopVariable.Type); actualValue.SetInitialValue("0"); // // If everything went well, then we can do the assignment. Otherwise, we leave // it as above (having the default value). // gc.Add(new Statements.StatementFilter(valueWasSeen)); gc.Add(new Statements.StatementAssign(actualValue, ExpressionToCPP.GetExpression(firstlastValue, gc, cc, container))); gc.Pop(); return(actualValue); } else { // No need to cache the result - so no need to add extra code. return(newIndexExpr); } }
public Expression ProcessResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, IGeneratedQueryCode gc, ICodeContext cc, CompositionContainer container) { if (resultOperator == null) throw new ArgumentNullException("resultOperator"); if (cc == null) throw new ArgumentNullException("CodeContext can't be null"); // // Determine the type of the result operator we are processing and // anything we need to know about it. // Type sumType; sumType = cc.LoopVariable.Type; bool doAverage = false; if (resultOperator is SumResultOperator) { doAverage = false; } else { doAverage = true; } // // We only know how to sum basic types // if (!sumType.IsNumberType()) { throw new InvalidOperationException(string.Format("Do not know how to generate C++ to sum type {0}.", sumType.Name)); } var accumulator = DeclarableParameter.CreateDeclarableParameterExpression(sumType); accumulator.SetInitialValue("0"); // // Now, in the loop we are currently in, we do the "add". // var add = Expression.Add(accumulator, cc.LoopVariable); var addResolved = ExpressionToCPP.GetExpression(add, gc, cc, container); gc.Add(new StatementAggregate(accumulator, addResolved)); // // The sum will just be this accumulator - so return it. // if (!doAverage) return accumulator; // // If this is a average then we need to add a simple count on. Further, we need to declare // everything we are going to need for later. // var counter = DeclarableParameter.CreateDeclarableParameterExpression(typeof(int)); counter.SetInitialValue("0"); gc.AddOutsideLoop(counter); gc.AddOutsideLoop(accumulator); var incbyone = Expression.Add(counter, Expression.Constant(1)); gc.Add(new StatementAggregate(counter, ExpressionToCPP.GetExpression(incbyone, gc, cc, container))); // It is an error to average a sequence with no elements. So we need to throw a C++ exception. We need to pop up out of the loop in order // to do this. // http://msdn.microsoft.com/en-us/library/bb354760.aspx (for specs on Average on this). var testForSomething = Expression.Equal(counter, Expression.Constant(0)); gc.AddAtResultScope(new StatementThrowIfTrue(ExpressionToCPP.GetExpression(testForSomething, gc, cc, container), "Can't take an average of a null sequence")); var returnType = DetermineAverageReturnType(sumType); var faccumulator = Expression.Convert(accumulator, returnType); var fcount = Expression.Convert(counter, returnType); var divide = Expression.Divide(faccumulator, fcount); // We are done with this calculation, so pop up and out. gc.Pop(); return divide; }