915 lines
43 KiB
C#
915 lines
43 KiB
C#
/*
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
All rights reserved.
|
|
|
|
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
|
|
|
|
|
|
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
|
|
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
|
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
|
|
|
|
See the Apache Version 2.0 License for specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Reflection;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Diagnostics;
|
|
using Microsoft.Research.DryadLinq.Internal;
|
|
|
|
namespace Microsoft.Research.DryadLinq
|
|
{
|
|
internal class DecompositionInfo
|
|
{
|
|
private Expression m_func; // The original function call
|
|
private LambdaExpression m_seed; // (TSource) => TAccumulate
|
|
private LambdaExpression m_accumulator; // (TAccumulate, TSource) => TAccumulate
|
|
private LambdaExpression m_recursiveAccumulator; // (TAccumulate, TAccumulate) => TAccumulate
|
|
private LambdaExpression m_finalReducer; // (TAccumulate) => TResult
|
|
|
|
public DecompositionInfo(Expression func,
|
|
LambdaExpression seed,
|
|
LambdaExpression accumulator,
|
|
LambdaExpression recursiveAccumulator,
|
|
LambdaExpression finalReducer)
|
|
{
|
|
this.m_func = func;
|
|
this.m_seed = seed;
|
|
this.m_accumulator = accumulator;
|
|
this.m_recursiveAccumulator = recursiveAccumulator;
|
|
this.m_finalReducer = finalReducer;
|
|
}
|
|
|
|
public Expression Func
|
|
{
|
|
get { return this.m_func; }
|
|
}
|
|
|
|
public LambdaExpression Seed
|
|
{
|
|
get { return this.m_seed; }
|
|
}
|
|
|
|
public LambdaExpression Accumulator
|
|
{
|
|
get { return this.m_accumulator; }
|
|
}
|
|
|
|
public LambdaExpression RecursiveAccumulator
|
|
{
|
|
get { return this.m_recursiveAccumulator; }
|
|
}
|
|
|
|
public LambdaExpression FinalReducer
|
|
{
|
|
get { return this.m_finalReducer; }
|
|
}
|
|
}
|
|
|
|
internal class Decomposition
|
|
{
|
|
internal static List<DecompositionInfo>
|
|
GetDecompositionInfoList(LambdaExpression resultSelectExpr, DryadLinqCodeGen codeGen)
|
|
{
|
|
ParameterExpression keyParam;
|
|
ParameterExpression groupParam;
|
|
if (resultSelectExpr.Parameters.Count == 1)
|
|
{
|
|
keyParam = null;
|
|
groupParam = resultSelectExpr.Parameters[0];
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(resultSelectExpr.Parameters.Count == 2);
|
|
keyParam = resultSelectExpr.Parameters[0];
|
|
groupParam = resultSelectExpr.Parameters[1];
|
|
}
|
|
|
|
List<DecompositionInfo> infoList = new List<DecompositionInfo>(1);
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam,
|
|
resultSelectExpr.Body,
|
|
infoList, codeGen);
|
|
if (isDecomposed)
|
|
{
|
|
return infoList;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static bool GetDecompositionInfoList(ParameterExpression keyParam,
|
|
ParameterExpression groupParam,
|
|
MemberBinding mbinding,
|
|
List<DecompositionInfo> infoList,
|
|
DryadLinqCodeGen codeGen)
|
|
{
|
|
if (mbinding is MemberAssignment)
|
|
{
|
|
Expression expr = ((MemberAssignment)mbinding).Expression;
|
|
return GetDecompositionInfoList(keyParam, groupParam, expr, infoList, codeGen);
|
|
}
|
|
else if (mbinding is MemberMemberBinding)
|
|
{
|
|
foreach (MemberBinding mb in ((MemberMemberBinding)mbinding).Bindings)
|
|
{
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam, mb, infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
}
|
|
else if (mbinding is MemberListBinding)
|
|
{
|
|
foreach (ElementInit ei in ((MemberListBinding)mbinding).Initializers)
|
|
{
|
|
foreach (Expression arg in ei.Arguments)
|
|
{
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam, arg, infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool GetDecompositionInfoList(ParameterExpression keyParam,
|
|
ParameterExpression groupParam,
|
|
Expression expr,
|
|
List<DecompositionInfo> infoList,
|
|
DryadLinqCodeGen codeGen)
|
|
{
|
|
IEnumerable<Expression> argList = null;
|
|
if (DryadLinqExpression.IsConstant(expr))
|
|
{
|
|
return true;
|
|
}
|
|
else if (expr is BinaryExpression)
|
|
{
|
|
BinaryExpression be = (BinaryExpression)expr;
|
|
argList = new Expression[] { be.Left, be.Right };
|
|
}
|
|
else if (expr is UnaryExpression)
|
|
{
|
|
UnaryExpression ue = (UnaryExpression)expr;
|
|
return GetDecompositionInfoList(keyParam, groupParam, ue.Operand, infoList, codeGen);
|
|
}
|
|
else if (expr is ConditionalExpression)
|
|
{
|
|
ConditionalExpression ce = (ConditionalExpression)expr;
|
|
argList = new Expression[] { ce.Test, ce.IfTrue, ce.IfFalse };
|
|
}
|
|
else if (expr is MethodCallExpression)
|
|
{
|
|
MethodCallExpression mcExpr = (MethodCallExpression)expr;
|
|
DecompositionInfo dinfo = GetDecompositionInfo(groupParam, mcExpr, codeGen);
|
|
if (dinfo != null)
|
|
{
|
|
infoList.Add(dinfo);
|
|
return true;
|
|
}
|
|
if (mcExpr.Object != null)
|
|
{
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam,
|
|
mcExpr.Object,
|
|
infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
argList = mcExpr.Arguments;
|
|
}
|
|
else if (expr is NewExpression)
|
|
{
|
|
argList = ((NewExpression)expr).Arguments;
|
|
}
|
|
else if (expr is NewArrayExpression)
|
|
{
|
|
argList = ((NewArrayExpression)expr).Expressions;
|
|
}
|
|
else if (expr is ListInitExpression)
|
|
{
|
|
ListInitExpression li = (ListInitExpression)expr;
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam,
|
|
li.NewExpression,
|
|
infoList, codeGen);
|
|
for (int i = 0, n = li.Initializers.Count; i < n; i++)
|
|
{
|
|
ElementInit ei = li.Initializers[i];
|
|
foreach (Expression arg in ei.Arguments)
|
|
{
|
|
isDecomposed = GetDecompositionInfoList(keyParam, groupParam, arg, infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else if (expr is MemberInitExpression)
|
|
{
|
|
MemberInitExpression mi = (MemberInitExpression)expr;
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam,
|
|
mi.NewExpression,
|
|
infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
foreach (MemberBinding mb in mi.Bindings)
|
|
{
|
|
isDecomposed = GetDecompositionInfoList(keyParam, groupParam, mb, infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
return true;
|
|
}
|
|
else if (keyParam == null)
|
|
{
|
|
while (expr is MemberExpression)
|
|
{
|
|
MemberExpression me = (MemberExpression)expr;
|
|
if (me.Expression == groupParam &&
|
|
me.Member.Name == "Key")
|
|
{
|
|
return true;
|
|
}
|
|
expr = me.Expression;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
while (expr is MemberExpression)
|
|
{
|
|
expr = ((MemberExpression)expr).Expression;
|
|
}
|
|
return (expr == keyParam);
|
|
}
|
|
|
|
foreach (var argExpr in argList)
|
|
{
|
|
bool isDecomposed = GetDecompositionInfoList(keyParam, groupParam, argExpr, infoList, codeGen);
|
|
if (!isDecomposed) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static DecompositionInfo GetDecompositionInfo(ParameterExpression groupParam,
|
|
MethodCallExpression mcExpr,
|
|
DryadLinqCodeGen codeGen)
|
|
{
|
|
if (mcExpr.Arguments.Count == 0 || mcExpr.Arguments[0] != groupParam)
|
|
{
|
|
return null;
|
|
}
|
|
for (int i = 1; i < mcExpr.Arguments.Count; i++)
|
|
{
|
|
if (DryadLinqExpression.Contains(groupParam, mcExpr.Arguments[i]))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
ExpressionSimplifier<object> evaluator = new ExpressionSimplifier<object>();
|
|
Type[] paramTypeArgs = groupParam.Type.GetGenericArguments();
|
|
Type sourceElemType = paramTypeArgs[paramTypeArgs.Length - 1];
|
|
Type resultType = mcExpr.Type;
|
|
Type decomposerType = null;
|
|
|
|
DecomposableAttribute decomposableAttrib = AttributeSystem.GetDecomposableAttrib(mcExpr);
|
|
if (decomposableAttrib != null)
|
|
{
|
|
decomposerType = decomposableAttrib.DecompositionType;
|
|
}
|
|
else
|
|
{
|
|
MethodInfo mInfo = mcExpr.Method;
|
|
if (mInfo.DeclaringType == typeof(System.Linq.Enumerable) ||
|
|
mInfo.DeclaringType == typeof(System.Linq.Queryable))
|
|
{
|
|
// For built-in decomposable operators.
|
|
switch (mInfo.Name)
|
|
{
|
|
case "Count":
|
|
case "LongCount":
|
|
{
|
|
Type outputType;
|
|
Expression body;
|
|
if (mInfo.Name == "Count")
|
|
{
|
|
outputType = typeof(Int32);
|
|
body = Expression.Constant(1, outputType);
|
|
}
|
|
else
|
|
{
|
|
outputType = typeof(Int64);
|
|
body = Expression.Constant((long)1, outputType);
|
|
}
|
|
ParameterExpression param1 = Expression.Parameter(outputType, "a");
|
|
ParameterExpression param2 = Expression.Parameter(sourceElemType, "e");
|
|
LambdaExpression seedExpr = Expression.Lambda(body, param2);
|
|
body = Expression.AddChecked(param1, body);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(body, param1, param2);
|
|
param2 = Expression.Parameter(outputType, "b");
|
|
body = Expression.AddChecked(param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(body, param1, param2);
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "Any":
|
|
{
|
|
ParameterExpression param1 = Expression.Parameter(typeof(bool), "a");
|
|
ParameterExpression param2;
|
|
Expression body;
|
|
if (mcExpr.Arguments.Count == 1)
|
|
{
|
|
param2 = Expression.Parameter(sourceElemType, "e");
|
|
body = Expression.Constant(true, typeof(bool));
|
|
}
|
|
else
|
|
{
|
|
LambdaExpression predExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
param2 = predExpr.Parameters[0];
|
|
body = predExpr.Body;
|
|
}
|
|
|
|
LambdaExpression seedExpr = Expression.Lambda(body, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(Expression.Or(param1, body), param1, param2);
|
|
param2 = Expression.Parameter(typeof(bool), "b");
|
|
body = Expression.Or(param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(body, param1, param2);
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "All":
|
|
{
|
|
ParameterExpression param1 = Expression.Parameter(typeof(bool), "a");
|
|
LambdaExpression predExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
ParameterExpression param2 = predExpr.Parameters[0];
|
|
|
|
Expression body = predExpr.Body;
|
|
LambdaExpression seedExpr = Expression.Lambda(body, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(Expression.And(param1, body), param1, param2);
|
|
param2 = Expression.Parameter(typeof(bool), "b");
|
|
body = Expression.And(param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(body, param1, param2);
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "First":
|
|
{
|
|
ParameterExpression param1 = Expression.Parameter(sourceElemType, "a");
|
|
ParameterExpression param2 = Expression.Parameter(sourceElemType, "e");
|
|
|
|
LambdaExpression seedExpr = Expression.Lambda(param2, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(param1, param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = accumulateExpr;
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "Last":
|
|
{
|
|
ParameterExpression param1 = Expression.Parameter(sourceElemType, "a");
|
|
ParameterExpression param2 = Expression.Parameter(sourceElemType, "e");
|
|
|
|
LambdaExpression seedExpr = Expression.Lambda(param2, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(param2, param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = accumulateExpr;
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "Sum":
|
|
{
|
|
ParameterExpression param1;
|
|
ParameterExpression param2;
|
|
Expression arg2;
|
|
if (mInfo.GetParameters().Length == 1)
|
|
{
|
|
param2 = Expression.Parameter(sourceElemType, "e");
|
|
arg2 = param2;
|
|
}
|
|
else
|
|
{
|
|
LambdaExpression selectExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
param2 = selectExpr.Parameters[0];
|
|
arg2 = selectExpr.Body;
|
|
}
|
|
|
|
Expression abody, sbody;
|
|
if (arg2.Type.IsGenericType)
|
|
{
|
|
param1 = Expression.Parameter(arg2.Type.GetGenericArguments()[0], "a");
|
|
MethodInfo accumulateInfo = typeof(DryadLinqVertex).GetMethod(
|
|
"SumAccumulate",
|
|
new Type[] { param1.Type, arg2.Type });
|
|
sbody = Expression.Constant(0, param1.Type);
|
|
sbody = Expression.Call(accumulateInfo, sbody, arg2);
|
|
abody = Expression.Call(accumulateInfo, param1, arg2);
|
|
}
|
|
else
|
|
{
|
|
param1 = Expression.Parameter(arg2.Type, "a");
|
|
sbody = arg2;
|
|
abody = Expression.AddChecked(param1, arg2);
|
|
}
|
|
|
|
LambdaExpression seedExpr = Expression.Lambda(sbody, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(abody, param1, param2);
|
|
param2 = Expression.Parameter(param1.Type, "b");
|
|
Expression rbody = Expression.AddChecked(param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(rbody, param1, param2);
|
|
Expression fbody = Expression.Convert(param1, arg2.Type);
|
|
LambdaExpression finalReduceExpr = Expression.Lambda(fbody, param1);
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr,
|
|
recursiveAccumulateExpr, finalReduceExpr);
|
|
}
|
|
case "Max":
|
|
case "Min":
|
|
{
|
|
ParameterExpression param2;
|
|
Expression abody;
|
|
if (mInfo.GetParameters().Length == 1)
|
|
{
|
|
param2 = Expression.Parameter(sourceElemType, "e");
|
|
abody = param2;
|
|
}
|
|
else
|
|
{
|
|
LambdaExpression selectExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
param2 = selectExpr.Parameters[0];
|
|
abody = selectExpr.Body;
|
|
}
|
|
|
|
ParameterExpression param1 = Expression.Parameter(abody.Type, "a");
|
|
Expression sbody = abody;
|
|
MethodInfo accumulateInfo;
|
|
string methodName = (mInfo.Name == "Max") ? "MaxAccumulate" : "MinAccumulate";
|
|
if (mInfo.IsGenericMethod && (mInfo.GetParameters().Length == 1))
|
|
{
|
|
accumulateInfo = typeof(DryadLinqVertex).GetMethod(methodName + "Generic");
|
|
accumulateInfo = accumulateInfo.MakeGenericMethod(sourceElemType);
|
|
}
|
|
else
|
|
{
|
|
accumulateInfo = typeof(DryadLinqVertex).GetMethod(
|
|
methodName,
|
|
new Type[] { param1.Type, abody.Type });
|
|
}
|
|
abody = Expression.Call(accumulateInfo, param1, abody);
|
|
|
|
LambdaExpression seedExpr = Expression.Lambda(sbody, param2);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(abody, param1, param2);
|
|
param2 = Expression.Parameter(param1.Type, "b");
|
|
Expression rbody = Expression.Call(accumulateInfo, param1, param2);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(rbody, param1, param2);
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "Aggregate":
|
|
{
|
|
ParameterExpression elemParam = Expression.Parameter(sourceElemType, "e");
|
|
LambdaExpression accumulateExpr;
|
|
LambdaExpression seedExpr;
|
|
if (mcExpr.Arguments.Count == 2)
|
|
{
|
|
accumulateExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
seedExpr = Expression.Lambda(elemParam, elemParam);
|
|
}
|
|
else
|
|
{
|
|
accumulateExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[2]);
|
|
object seedVal = evaluator.Eval(mcExpr.Arguments[1]);
|
|
Expression body = Expression.Constant(seedVal, seedVal.GetType());
|
|
ParameterSubst subst = new ParameterSubst(accumulateExpr.Parameters[0], body);
|
|
body = subst.Visit(accumulateExpr.Body);
|
|
seedExpr = Expression.Lambda(body, accumulateExpr.Parameters[1]);
|
|
}
|
|
if (!DryadLinqExpression.IsAssociative(accumulateExpr))
|
|
{
|
|
return null;
|
|
}
|
|
LambdaExpression recursiveAccumulateExpr = DryadLinqExpression.GetAssociativeCombiner(accumulateExpr);
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, null);
|
|
}
|
|
case "Average":
|
|
{
|
|
ParameterExpression param2;
|
|
Expression abody;
|
|
if (mInfo.GetParameters().Length == 1)
|
|
{
|
|
param2 = Expression.Parameter(sourceElemType, "e");
|
|
abody = param2;
|
|
}
|
|
else
|
|
{
|
|
LambdaExpression selectExpr = DryadLinqExpression.GetLambda(mcExpr.Arguments[1]);
|
|
param2 = selectExpr.Parameters[0];
|
|
abody = selectExpr.Body;
|
|
}
|
|
Type aggValueType = abody.Type;
|
|
if (aggValueType == typeof(int) ||
|
|
aggValueType == typeof(int?))
|
|
{
|
|
aggValueType = typeof(long);
|
|
}
|
|
else if (aggValueType == typeof(long?))
|
|
{
|
|
aggValueType = typeof(long);
|
|
}
|
|
else if (aggValueType == typeof(float) ||
|
|
aggValueType == typeof(float?))
|
|
{
|
|
aggValueType = typeof(double);
|
|
}
|
|
else if (aggValueType == typeof(double?))
|
|
{
|
|
aggValueType = typeof(double);
|
|
}
|
|
else if (aggValueType == typeof(decimal?))
|
|
{
|
|
aggValueType = typeof(decimal);
|
|
}
|
|
|
|
Type sumAndCountType = typeof(AggregateValue<>).MakeGenericType(aggValueType);
|
|
ParameterExpression param1 = Expression.Parameter(sumAndCountType, "a");
|
|
MethodInfo accumulateInfo = typeof(DryadLinqVertex).GetMethod(
|
|
"AverageAccumulate",
|
|
new Type[] { sumAndCountType, abody.Type });
|
|
|
|
// Seed:
|
|
Expression sbody = Expression.New(sumAndCountType);
|
|
sbody = Expression.Call(accumulateInfo, sbody, abody);
|
|
LambdaExpression seedExpr = Expression.Lambda(sbody, param2);
|
|
|
|
// Accumulate:
|
|
abody = Expression.Call(accumulateInfo, param1, abody);
|
|
LambdaExpression accumulateExpr = Expression.Lambda(abody, param1, param2);
|
|
|
|
// RecursiveAccumulate:
|
|
param2 = Expression.Parameter(param1.Type, "b");
|
|
PropertyInfo valueInfo = sumAndCountType.GetProperty("Value");
|
|
PropertyInfo countInfo = sumAndCountType.GetProperty("Count");
|
|
Expression sumExpr1 = Expression.Property(param1, valueInfo);
|
|
Expression countExpr1 = Expression.Property(param1, countInfo);
|
|
Expression sumExpr2 = Expression.Property(param2, valueInfo);
|
|
Expression countExpr2 = Expression.Property(param2, countInfo);
|
|
Expression sumExpr = Expression.AddChecked(sumExpr1, sumExpr2);
|
|
Expression countExpr = Expression.AddChecked(countExpr1, countExpr2);
|
|
ConstructorInfo cinfo = sumAndCountType.GetConstructor(new Type[] { sumExpr.Type, countExpr.Type });
|
|
Expression rbody = Expression.New(cinfo, sumExpr, countExpr);
|
|
LambdaExpression recursiveAccumulateExpr = Expression.Lambda(rbody, param1, param2);
|
|
|
|
// FinalReduce:
|
|
if (sumExpr1.Type == typeof(long))
|
|
{
|
|
sumExpr1 = Expression.Convert(sumExpr1, typeof(double));
|
|
}
|
|
Expression fbody = Expression.Divide(sumExpr1, countExpr1);
|
|
fbody = Expression.Convert(fbody, resultType);
|
|
if (resultType.IsGenericType)
|
|
{
|
|
Expression zeroExpr = Expression.Constant(0, typeof(long));
|
|
Expression condExpr = Expression.GreaterThan(countExpr1, zeroExpr);
|
|
Expression nullExpr = Expression.Constant(null, resultType);
|
|
fbody = Expression.Condition(condExpr, fbody, nullExpr);
|
|
}
|
|
LambdaExpression finalReduceExpr = Expression.Lambda(fbody, param1);
|
|
return new DecompositionInfo(mcExpr, seedExpr, accumulateExpr, recursiveAccumulateExpr, finalReduceExpr);
|
|
}
|
|
case "Contains":
|
|
{
|
|
decomposerType = typeof(ContainsDecomposition<>).MakeGenericType(sourceElemType);
|
|
break;
|
|
}
|
|
case "Distinct":
|
|
{
|
|
decomposerType = typeof(DistinctDecomposition<>).MakeGenericType(sourceElemType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (decomposerType == null) return null;
|
|
|
|
Type implementedInterface = null;
|
|
Type[] interfaces = decomposerType.GetInterfaces();
|
|
foreach (Type intf in interfaces)
|
|
{
|
|
if (intf.GetGenericTypeDefinition() == typeof(IDecomposable<,,>))
|
|
{
|
|
if (implementedInterface != null)
|
|
{
|
|
throw new DryadLinqException("Decomposition class can implement only one decomposable interface.");
|
|
}
|
|
implementedInterface = intf;
|
|
}
|
|
}
|
|
|
|
if (implementedInterface == null ||
|
|
implementedInterface.GetGenericArguments().Length != 3)
|
|
{
|
|
throw new DryadLinqException("Decomposition class " + decomposerType.FullName +
|
|
"must implement IDecomposable<,,>");
|
|
}
|
|
|
|
// The second type of the implemented interface definition is the accumulatorType.
|
|
Type accumulatorType = implementedInterface.GetGenericArguments()[1];
|
|
|
|
// Now check that all the types match up.
|
|
Type decomposerInterface = typeof(IDecomposable<,,>).MakeGenericType(
|
|
sourceElemType, accumulatorType, resultType);
|
|
if (!decomposerInterface.IsAssignableFrom(decomposerType))
|
|
{
|
|
throw new DryadLinqException("Decomposition class must match the function that it decorates.");
|
|
}
|
|
if (decomposerType.ContainsGenericParameters)
|
|
{
|
|
if (decomposerType.GetGenericArguments().Length != 1 ||
|
|
!decomposerType.GetGenericArguments()[0].IsGenericParameter)
|
|
{
|
|
throw new DryadLinqException(decomposerType.Name + " must match the function it annotates.");
|
|
}
|
|
decomposerType = decomposerType.MakeGenericType(sourceElemType);
|
|
}
|
|
if (decomposerType.GetConstructor(Type.EmptyTypes) == null)
|
|
{
|
|
throw new DryadLinqException("Decomposition class must have a default constructor.");
|
|
}
|
|
|
|
// Add to the codegen a call of the static Initializer of decomposerType
|
|
Expression[] args = new Expression[mcExpr.Arguments.Count-1];
|
|
for (int i = 0; i < args.Length; i++)
|
|
{
|
|
args[i] = Expression.Convert(mcExpr.Arguments[i+1], typeof(object));
|
|
}
|
|
Expression stateExpr = Expression.NewArrayInit(typeof(object), args);
|
|
string decomposerName = codeGen.AddDecompositionInitializer(decomposerType, stateExpr);
|
|
ParameterExpression decomposer = Expression.Parameter(decomposerType, decomposerName);
|
|
|
|
// Seed: TSource => TAccumulate
|
|
MethodInfo seedInfo1 = decomposerType.GetMethod("Seed");
|
|
ParameterExpression p2 = Expression.Parameter(sourceElemType, "e");
|
|
Expression sbody1 = Expression.Call(decomposer, seedInfo1, p2);
|
|
LambdaExpression seedExpr1 = Expression.Lambda(sbody1, p2);
|
|
|
|
// Accumulate: (TAccumulate, TSource) => TAccumulate
|
|
MethodInfo accumulateInfo1 = decomposerType.GetMethod("Accumulate");
|
|
ParameterExpression p1 = Expression.Parameter(accumulatorType, "a");
|
|
Expression abody1 = Expression.Call(decomposer, accumulateInfo1, p1, p2);
|
|
LambdaExpression accumulateExpr1 = Expression.Lambda(abody1, p1, p2);
|
|
|
|
// RecursiveAccumulate: (TAccumulate, TAccumulate) => TAccumulate
|
|
MethodInfo recursiveAccumulateInfo1 = decomposerType.GetMethod("RecursiveAccumulate");
|
|
p2 = Expression.Parameter(accumulatorType, "e");
|
|
Expression rbody1 = Expression.Call(decomposer, recursiveAccumulateInfo1, p1, p2);
|
|
LambdaExpression recursiveAccumulateExpr1 = Expression.Lambda(rbody1, p1, p2);
|
|
|
|
// FinalReduce: TAccumulate => TResult
|
|
MethodInfo finalReduceInfo1 = decomposerType.GetMethod("FinalReduce");
|
|
Expression fbody1 = Expression.Call(decomposer, finalReduceInfo1, p1);
|
|
LambdaExpression finalReduceExpr1 = Expression.Lambda(fbody1, p1);
|
|
|
|
return new DecompositionInfo(mcExpr, seedExpr1, accumulateExpr1, recursiveAccumulateExpr1, finalReduceExpr1);
|
|
}
|
|
|
|
// Precondition: idx < dInfoList.Count
|
|
internal static Expression AccumulateList(Expression valueExpr,
|
|
ParameterExpression elemParam,
|
|
List<DecompositionInfo> dInfoList,
|
|
int idx)
|
|
{
|
|
LambdaExpression accumulateExpr = dInfoList[idx].Accumulator;
|
|
if (dInfoList.Count == idx + 1)
|
|
{
|
|
ParameterSubst subst = new ParameterSubst(accumulateExpr.Parameters[0], valueExpr);
|
|
Expression resultExpr = subst.Visit(accumulateExpr.Body);
|
|
subst = new ParameterSubst(accumulateExpr.Parameters[1], elemParam);
|
|
return subst.Visit(resultExpr);
|
|
}
|
|
else
|
|
{
|
|
PropertyInfo keyPropInfo = valueExpr.Type.GetProperty("Key");
|
|
Expression keyValueExpr = Expression.Property(valueExpr, keyPropInfo);
|
|
ParameterSubst subst = new ParameterSubst(accumulateExpr.Parameters[0], keyValueExpr);
|
|
Expression expr1 = subst.Visit(accumulateExpr.Body);
|
|
subst = new ParameterSubst(accumulateExpr.Parameters[1], elemParam);
|
|
expr1 = subst.Visit(expr1);
|
|
|
|
PropertyInfo valuePropInfo = valueExpr.Type.GetProperty("Value");
|
|
Expression valueValueExpr = Expression.Property(valueExpr, valuePropInfo);
|
|
Expression expr2 = AccumulateList(valueValueExpr, elemParam, dInfoList, idx + 1);
|
|
|
|
Type pairType = typeof(Pair<,>).MakeGenericType(expr1.Type, expr2.Type);
|
|
return Expression.New(pairType.GetConstructors()[0], expr1, expr2);
|
|
}
|
|
}
|
|
|
|
// Precondition: idx < dInfoList.Count
|
|
internal static Expression RecursiveAccumulateList(Expression valueExpr1,
|
|
Expression valueExpr2,
|
|
List<DecompositionInfo> dInfoList,
|
|
int idx)
|
|
{
|
|
LambdaExpression recursiveAccumulateExpr = dInfoList[idx].RecursiveAccumulator;
|
|
if (dInfoList.Count == idx + 1)
|
|
{
|
|
ParameterSubst subst = new ParameterSubst(recursiveAccumulateExpr.Parameters[0], valueExpr1);
|
|
Expression resultExpr = subst.Visit(recursiveAccumulateExpr.Body);
|
|
subst = new ParameterSubst(recursiveAccumulateExpr.Parameters[1], valueExpr2);
|
|
return subst.Visit(resultExpr);
|
|
}
|
|
else
|
|
{
|
|
PropertyInfo keyPropInfo1 = valueExpr1.Type.GetProperty("Key");
|
|
Expression keyValueExpr1 = Expression.Property(valueExpr1, keyPropInfo1);
|
|
PropertyInfo keyPropInfo2 = valueExpr2.Type.GetProperty("Key");
|
|
Expression keyValueExpr2 = Expression.Property(valueExpr2, keyPropInfo2);
|
|
ParameterSubst subst = new ParameterSubst(recursiveAccumulateExpr.Parameters[0], keyValueExpr1);
|
|
Expression expr1 = subst.Visit(recursiveAccumulateExpr.Body);
|
|
subst = new ParameterSubst(recursiveAccumulateExpr.Parameters[1], keyValueExpr2);
|
|
expr1 = subst.Visit(expr1);
|
|
|
|
PropertyInfo valuePropInfo1 = valueExpr1.Type.GetProperty("Value");
|
|
Expression valueValueExpr1 = Expression.Property(valueExpr1, valuePropInfo1);
|
|
PropertyInfo valuePropInfo2 = valueExpr2.Type.GetProperty("Value");
|
|
Expression valueValueExpr2 = Expression.Property(valueExpr2, valuePropInfo2);
|
|
Expression expr2 = RecursiveAccumulateList(valueValueExpr1, valueValueExpr2, dInfoList, idx + 1);
|
|
|
|
Type pairType = typeof(Pair<,>).MakeGenericType(expr1.Type, expr2.Type);
|
|
return Expression.New(pairType.GetConstructors()[0], expr1, expr2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ContainsDecomposition<TSource> : IDecomposable<TSource, bool, bool>
|
|
{
|
|
private TSource m_value;
|
|
private IEqualityComparer<TSource> m_comparer;
|
|
|
|
public void Initialize(object state)
|
|
{
|
|
object[] args = state as object[];
|
|
this.m_value = (TSource)args[0];
|
|
if (args.Length > 1)
|
|
{
|
|
this.m_comparer = (IEqualityComparer<TSource>)args[1];
|
|
}
|
|
else
|
|
{
|
|
this.m_comparer = EqualityComparer<TSource>.Default;
|
|
}
|
|
}
|
|
|
|
public bool Seed(TSource val)
|
|
{
|
|
return this.m_comparer.Equals(this.m_value, val);
|
|
}
|
|
|
|
public bool Accumulate(bool acc, TSource val)
|
|
{
|
|
return acc || this.m_comparer.Equals(this.m_value, val);
|
|
}
|
|
|
|
public bool RecursiveAccumulate(bool acc, bool val)
|
|
{
|
|
return acc || val;
|
|
}
|
|
|
|
public bool FinalReduce(bool val)
|
|
{
|
|
return val;
|
|
}
|
|
}
|
|
|
|
public class DistinctDecomposition<TSource>
|
|
: IDecomposable<TSource, DistinctSet<TSource>, IEnumerable<TSource>>
|
|
{
|
|
private IEqualityComparer<TSource> m_comparer;
|
|
|
|
public void Initialize(object state)
|
|
{
|
|
object[] args = state as object[];
|
|
if (args.Length > 0)
|
|
{
|
|
this.m_comparer = (IEqualityComparer<TSource>)args[0];
|
|
}
|
|
else
|
|
{
|
|
this.m_comparer = EqualityComparer<TSource>.Default;
|
|
}
|
|
}
|
|
|
|
public DistinctSet<TSource> Seed(TSource val)
|
|
{
|
|
DistinctSet<TSource> set = new DistinctSet<TSource>();
|
|
set.Add(val, this.m_comparer);
|
|
return set;
|
|
}
|
|
|
|
public DistinctSet<TSource> Accumulate(DistinctSet<TSource> acc, TSource val)
|
|
{
|
|
acc.Add(val, this.m_comparer);
|
|
return acc;
|
|
}
|
|
|
|
public DistinctSet<TSource> RecursiveAccumulate(DistinctSet<TSource> acc,
|
|
DistinctSet<TSource> val)
|
|
{
|
|
foreach (TSource x in val.GetElems(this.m_comparer))
|
|
{
|
|
acc.Add(x, this.m_comparer);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
public IEnumerable<TSource> FinalReduce(DistinctSet<TSource> val)
|
|
{
|
|
return val.ToArray(this.m_comparer);
|
|
}
|
|
}
|
|
|
|
public class DistinctSet<TSource>
|
|
{
|
|
private const Int32 MaxCount = 32;
|
|
private static readonly TSource[] Empty = new TSource[0];
|
|
|
|
private TSource[] m_distinctElems;
|
|
private TSource[] m_elems;
|
|
private Int32 m_count;
|
|
|
|
public DistinctSet()
|
|
{
|
|
this.m_distinctElems = Empty;
|
|
this.m_elems = new TSource[1];
|
|
this.m_count = 0;
|
|
}
|
|
|
|
public void Add(TSource elem, IEqualityComparer<TSource> comparer)
|
|
{
|
|
if (this.m_count == this.m_elems.Length)
|
|
{
|
|
if (this.m_count < MaxCount)
|
|
{
|
|
TSource[] newElems = new TSource[this.m_count * 2];
|
|
Array.Copy(this.m_elems, 0, newElems, 0, this.m_count);
|
|
this.m_elems = newElems;
|
|
}
|
|
else
|
|
{
|
|
this.m_distinctElems = this.ToArray(comparer);
|
|
this.m_elems = new TSource[2];
|
|
this.m_count = 0;
|
|
}
|
|
}
|
|
this.m_elems[this.m_count++] = elem;
|
|
}
|
|
|
|
public IEnumerable<TSource> GetElems(IEqualityComparer<TSource> comparer)
|
|
{
|
|
HashSet<TSource> set = new HashSet<TSource>(comparer);
|
|
for (int i = 0; i < this.m_count; i++)
|
|
{
|
|
if (set.Add(this.m_elems[i]))
|
|
{
|
|
yield return this.m_elems[i];
|
|
}
|
|
}
|
|
foreach (var elem in this.m_distinctElems)
|
|
{
|
|
if (!set.Contains(elem))
|
|
{
|
|
yield return elem;
|
|
}
|
|
}
|
|
}
|
|
|
|
public TSource[] ToArray(IEqualityComparer<TSource> comparer)
|
|
{
|
|
HashSet<TSource> set = new HashSet<TSource>(comparer);
|
|
for (int i = 0; i < this.m_count; i++)
|
|
{
|
|
set.Add(this.m_elems[i]);
|
|
}
|
|
Int32 idx = 0;
|
|
for (int i = 0; i < this.m_distinctElems.Length; i++)
|
|
{
|
|
if (!set.Contains(this.m_distinctElems[i]))
|
|
{
|
|
this.m_distinctElems[idx++] = this.m_distinctElems[i];
|
|
}
|
|
}
|
|
TSource[] distinctElems = new TSource[idx + set.Count];
|
|
Array.Copy(this.m_distinctElems, 0, distinctElems, 0, idx);
|
|
foreach (var x in set)
|
|
{
|
|
distinctElems[idx++] = x;
|
|
}
|
|
return distinctElems;
|
|
}
|
|
}
|
|
}
|