public static void TrianglesByDegree(this PINQCollection <Pair <int> > graph, double epsilon) { Console.WriteLine("in TrianglesByDegree..."); // form (b, db) pairs each with weight 1/2 var bDegs = graph.GroupBy(e => e.a, e => e.b, (k, i) => new VertexData(k, i)); // form length 2 paths (a,b,c) weight 1/2db. var path = graph.Join(graph, x => x.b, y => y.a, x => x.a, y => y.b, (key, x, y) => new Triple(x, key, y));//.Where(x => x.a != x.c); // form ((a,b,c), db) tuples, with weights 1/2db(1 + db). var abc = path.Join(bDegs, x => x.b, y => y.name, x => x, y => y.edges.Length, (key, x, y) => new Pair <Triple, int>(x, y)); // rotate to get ((c,a,b),db) or equivalently ((a,b,c),dc) var cab = abc.Select(x => new Pair <Triple, int>(new Triple(x.a.c, x.a.a, x.a.b), x.b)); // rotate to get ((b,c,a),db) or equivalently ((a,b,c),da) var bca = abc.Select(x => new Pair <Triple, int>(new Triple(x.a.b, x.a.c, x.a.a), x.b)); // form length ((a,b,c),da,db) tuples with weight 1/2(da(1+da) + db(1 +db)) var tuple = abc.Join(bca, x => x.a, y => y.a, x => x.b, y => y.b, (key, x, y) => new Pair <Triple, Pair>(key, new Pair(y, x))); // form length ((a,b,c),da,db,db) tuples with weight 1/2(da(1+da) + db(1+db) + dc(1+dc)) var tuple2 = tuple.Join(cab, x => x.a, y => y.a, x => x.b, y => y.b, (key, x, y) => new Pair <Triple, Triple>(key, new Triple(x.a, x.b, y))); // transform to (da,db,dc) tuples where da < db < dc var tris = tuple2.Select(x => x.b); tris = tris.Select(x => new Triple(Math.Min(Math.Min(x.a, x.b), x.c), median(x.a, x.b, x.c), Math.Max(x.a, Math.Max(x.b, x.c)))); // return the noisy histogram var result = tris.Count(x => x, epsilon); }