public void Test3() { var cfg = GenerateCFG( @"{ a = 5; 1: b = c; 2: goto 2; goto 1; d = a; goto 1; }"); /* * 5 блоков * 0 {a = 5} * 1 {1: b = c} * 2 {2: goto 2} * 3 {goto 1} * 4 {d = a; goto 1} */ var dict = cfg.classifyEdges(); Assert.AreEqual(dict[IndexEdge.Create(2, 2)], EdgeType.Retreating); Assert.AreEqual(dict[IndexEdge.Create(4, 6)], EdgeType.Cross); Assert.AreEqual(dict[IndexEdge.Create(3, 1)], EdgeType.Cross); dict.Remove(IndexEdge.Create(2, 2)); dict.Remove(IndexEdge.Create(4, 6)); dict.Remove(IndexEdge.Create(3, 1)); Assert.False(dict.Values.Any(type => type != EdgeType.Advancing)); BasicBlock.clearIndexCounter(); }
public IEnumerable <IndexEdge <TEntity> > ToEdgeResults() { //The Linq Selection provides IEnumerable for us... //Note: that's why we do NOT call ToList() here so that consuming classes may provide additional filtering... var results = this.CursorPage?.CursorResults .Where(cr => cr != null) .Select(cr => IndexEdge <TEntity> .Create(cr.Entity, cr.CursorIndex)); return(results); }
private static CursorPagingRange SliceRange( CursorPagingArguments arguments, int maxElementCount) { // [SPEC] if after is set then remove all elements of edges before and including // afterEdge. // // The cursor is increased by one so that the index points to the element after var startIndex = arguments.After is { } a ? IndexEdge <TEntity> .DeserializeCursor(a) + 1 : 0; // [SPEC] if before is set then remove all elements of edges before and including // beforeEdge. var before = arguments.Before is { } b ? IndexEdge <TEntity> .DeserializeCursor(b) : maxElementCount; // if after is negative we have know how much of the offset was in the negative range. // The amount of positions that are in the negative range, have to be subtracted from // the take or we will fetch too many items. var startOffsetCorrection = 0; if (startIndex < 0) { startOffsetCorrection = Math.Abs(startIndex); startIndex = 0; } CursorPagingRange range = new(startIndex, before); //[SPEC] If first is less than 0 throw an error ValidateFirst(arguments, out var first); if (first is not null) { first -= startOffsetCorrection; if (first < 0) { first = 0; } } //[SPEC] Slice edges to be of length first by removing edges from the end of edges. range.Take(first); //[SPEC] if last is less than 0 throw an error ValidateLast(arguments, out var last); //[SPEC] Slice edges to be of length last by removing edges from the start of edges. range.TakeLast(last); return(range); }
public void Test2() { var cfg = GenerateCFG( @"{ goto 1; a = 5; 1: while (a == 5) { x = 7; } if (z > 99) { f = 5; } else { b = 1; } a = 5 == 2; }"); /* * 10 блоков * 0 {goto 1} * 1 {a = 5} * 2 {1} * 3 {#L0 #t0 = a == 5; if #t0 goto #L1} * 4 {goto #L2} * 5 {#L1 x = 7; goto #L0} * 6 {#L2 #t1 = z > 99; if #t1 goto #L3} * 7 {b = 1; goto #L4} * 8 {#L3 f = 5} * 9 {#L4 #t2 = 5 == 2; a = #t2} */ var dict = cfg.classifyEdges(); Assert.AreEqual(dict[IndexEdge.Create(1, 2)], EdgeType.Cross); Assert.AreEqual(dict[IndexEdge.Create(5, 3)], EdgeType.Retreating); dict.Remove(IndexEdge.Create(1, 2)); dict.Remove(IndexEdge.Create(5, 3)); Assert.False(dict.Values.Any(type => type != EdgeType.Advancing)); BasicBlock.clearIndexCounter(); }
protected override ValueTask <Connection> SliceAsync(IResolverContext context, object source, CursorPagingArguments arguments) { //If Appropriate we handle the values here to ensure that no post-processing is done other than // correctly mapping the results into a GraphQL Connection as Edges with Cursors... if (source is IPreProcessedCursorSlice <TEntity> pagedResults) { bool includeTotalCountEnabled = this.PagingOptions.IncludeTotalCount ?? PagingDefaults.IncludeTotalCount; var graphQLParamsContext = new GraphQLParamsContext(context); //Optimized to only require TotalCount value if the query actually requested it! if (includeTotalCountEnabled && graphQLParamsContext.IsTotalCountRequested && pagedResults.TotalCount == null) { throw new InvalidOperationException($"Total Count is requested in the query, but was not provided with the results [{this.GetType().GetTypeName()}] from the resolvers pre-processing logic; TotalCount is null."); } int?totalCount = pagedResults.TotalCount; //Ensure we are null safe and return a valid empty list by default. IReadOnlyList <IndexEdge <TEntity> > selectedEdges = pagedResults?.ToEdgeResults().ToList() ?? new List <IndexEdge <TEntity> >();; IndexEdge <TEntity>?firstEdge = selectedEdges.FirstOrDefault(); IndexEdge <TEntity>?lastEdge = selectedEdges.LastOrDefault(); var connectionPageInfo = new ConnectionPageInfo( hasNextPage: pagedResults?.HasNextPage ?? false, hasPreviousPage: pagedResults?.HasPreviousPage ?? false, startCursor: firstEdge?.Cursor, endCursor: lastEdge?.Cursor, totalCount: totalCount ?? 0 ); var graphQLConnection = new Connection <TEntity>( selectedEdges, connectionPageInfo, ct => new ValueTask <int>(connectionPageInfo.TotalCount ?? 0) ); return(new ValueTask <Connection>(graphQLConnection)); } throw new GraphQLException($"[{nameof(PreProcessedCursorPagingHandler<TEntity>)}] cannot handle the specified data source of type [{source.GetType().Name}]."); }
public async ValueTask <IReadOnlyList <Edge <TEntity> > > ExecuteQueryAsync( int offset, CancellationToken cancellationToken) { var list = new List <IndexEdge <TEntity> >(); using IAsyncCursor <TEntity> cursor = await _source .ToCursorAsync(cancellationToken) .ConfigureAwait(false); var index = offset; while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false)) { foreach (TEntity item in cursor.Current) { list.Add(IndexEdge <TEntity> .Create(item, index++)); } } return(list); }
/// <summary> /// Generates the delaunay triangulation for a list of points. /// Requires the points to be sorted on the X-axis coordinate! /// Every the integers in the returned array are the indicies in the passes array of triangles. /// Implemented based upon: http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/ /// "Triangulate: Efficient Triangulation Algorithm Suitable for Terrain Modelling" /// by Paul Bourke /// </summary> public static int[] Triangulate(GridVector2[] points, GridVector2[] BoundingPoints) { if (BoundingPoints == null) { throw new ArgumentNullException("BoundingPoints"); } if (points == null) { throw new ArgumentNullException("points"); } if (points.Length < 3) return new int[0]; #if DEBUG //Check to ensure the input is really sorted on the X-Axis for (int iDebug = 1; iDebug < points.Length; iDebug++) { Debug.Assert(points[iDebug - 1].X <= points[iDebug].X); Debug.Assert(GridVector2.Distance(points[iDebug - 1], points[iDebug]) >= Global.Epsilon); } #endif List<GridIndexTriangle> triangles = new List<GridIndexTriangle>(points.Length); //Safe triangles have a circle with a center.X+radius which is less than the current point. //This means they can never intersect with a new point and we never need to test them again. List<GridIndexTriangle> safeTriangles = new List<GridIndexTriangle>(); int iNumPoints = points.Length; GridVector2[] allpoints = new GridVector2[iNumPoints + 4]; points.CopyTo(allpoints, 0); BoundingPoints.CopyTo(allpoints, iNumPoints); //Initialize bounding triangles triangles.AddRange(new GridIndexTriangle[] { new GridIndexTriangle(iNumPoints, iNumPoints + 1, iNumPoints + 2, ref allpoints), new GridIndexTriangle(iNumPoints + 1, iNumPoints + 2, iNumPoints + 3, ref allpoints) }); IndexEdge[] Edges = new IndexEdge[(triangles.Count * 3) * 2]; for(int iPoint = 0; iPoint < points.Length; iPoint++) { GridVector2 P = points[iPoint]; //Use preallocated buffer if we can, otherwise expand it int maxEdges = triangles.Count * 3; if (Edges.Length < maxEdges) Edges = new IndexEdge[maxEdges * 2]; int iTri = 0; int iEdge = 0; while(iTri < triangles.Count) { GridIndexTriangle tri = triangles[iTri]; GridCircle circle = tri.Circle; if (circle.Contains(P)) { Edges[iEdge++] = new IndexEdge(tri.i1, tri.i2); Edges[iEdge++] = new IndexEdge(tri.i2, tri.i3); Edges[iEdge++] = new IndexEdge(tri.i3, tri.i1); /* Edges.AddRange(new IndexEdge[] {new IndexEdge(tri.i1, tri.i2), new IndexEdge(tri.i2, tri.i3), new IndexEdge(tri.i3, tri.i1)}); */ triangles.RemoveAt(iTri); } //Check if the triangle is safe from ever intersecting with a new point again else if (circle.Center.X + circle.Radius + Global.Epsilon < P.X) { safeTriangles.Add(tri); triangles.RemoveAt(iTri); } else { iTri++; } } //Record how many edges there are int numEdges = iEdge; //Remove duplicates from edge buffer //This is easier with a list, but arrays were faster for (int iA = 0; iA < numEdges; iA++) { if (Edges[iA].IsValid == false) continue; for (int iB = iA + 1; iB < numEdges; iB++) { if (Edges[iB].IsValid == false) continue; if (Edges[iA] == Edges[iB]) { Edges[iB].IsValid = false; Edges[iA].IsValid = false; break; } } } //Add triangles with the remaining edges for(iEdge = 0; iEdge < numEdges; iEdge++) { IndexEdge E = Edges[iEdge]; if (!E.IsValid) continue; GridIndexTriangle newTri = new GridIndexTriangle(E.iA, E.iB, iPoint, ref allpoints); triangles.Add(newTri); #if DEBUG //Check to make sure the new triangle intersects the point. This is a slow test. Debug.Assert(((GridTriangle)newTri).Intersects(P)); #endif } } //Return all the safe triangles to the triangles list triangles.AddRange(safeTriangles); //Yank all the triangles that are part of the bounding triangles for (int iTri = 0; iTri < triangles.Count; iTri++) { GridIndexTriangle tri = triangles[iTri]; if (tri.i1 >= iNumPoints || tri.i2 >= iNumPoints || tri.i3 >= iNumPoints) { triangles.RemoveAt(iTri); iTri--; } } //Build a list of triangle indicies to return int[] TriangleIndicies = new int[triangles.Count * 3]; for (int iTri = 0; iTri < triangles.Count; iTri++) { GridIndexTriangle tri = triangles[iTri]; int iPoint = iTri * 3; TriangleIndicies[iPoint] = tri.i1; TriangleIndicies[iPoint + 1] = tri.i2; TriangleIndicies[iPoint + 2] = tri.i3; } return TriangleIndicies; }
public static ICursorPageSlice <T> SliceAsCursorPage <T>(this IEnumerable <T> items, string?after, int?first, string?before, int?last) where T : class { //Do nothing if there are no results... if (!items.Any()) { return(new CursorPageSlice <T>(Enumerable.Empty <ICursorResult <T> >(), 0, false, false)); } var afterIndex = after != null ? IndexEdge <string> .DeserializeCursor(after) : 0; var beforeIndex = before != null ? IndexEdge <string> .DeserializeCursor(before) : 0; //FIRST log the index of all items in the list BEFORE slicing, as these indexes are // the Cursor Indexes for paging up/down the entire list, & ICursorResult is the Decorator // around the Entity Models. //NOTE: We MUST materialize this after applying index values to prevent ongoing increments... int index = 0; IEnumerable <ICursorResult <T> > slice = items .Select(c => new CursorResult <T>(c, ++index)) .ToList(); int totalCount = slice.Count(); //If After specified, remove all before After (or skip past After) if (afterIndex > 0 && slice.Last().CursorIndex > afterIndex) { slice = slice.Skip(afterIndex); } //If Before is specified, remove all after Before (Skip Until Before is reached) if (beforeIndex > 0 && slice.Last().CursorIndex > beforeIndex) { slice = slice.SkipWhile(c => c.CursorIndex < beforeIndex); } //If First is specified, then take the first/top rows from the current Slice! if (first.HasValue && first > 0 && slice.Count() > first) { slice = slice.Take(first.Value); } //If First is specified, then take the first/top rows from the current Slice! if (last.HasValue && last > 0 && slice.Count() > last) { slice = slice.TakeLast(last.Value); } //Wrap all results into a PagedCursor Slice result wit Total Count... //NOTE: to ensure our pagination is complete, we materialize the Results! var results = slice.ToList(); var firstCursor = results.FirstOrDefault(); var lastCursor = results.LastOrDefault(); var cursorPageSlice = new CursorPageSlice <T>( results, totalCount, hasPreviousPage: firstCursor?.CursorIndex > 1, hasNextPage: lastCursor?.CursorIndex < totalCount ); return(cursorPageSlice); }
public static string?SerializeCursor(int?index) { return(index != null ? IndexEdge <string> .Create(String.Empty, (int)index)?.Cursor : default);