| 232 | } |
| 233 | |
| 234 | static Path *_AllPathsCtx_NextPath(AllPathsCtx *ctx) { |
| 235 | // As long as path is not empty OR there are neighbors to traverse. |
| 236 | while(Path_NodeCount(ctx->path) || _AllPathsCtx_LevelNotEmpty(ctx, 0)) { |
| 237 | uint32_t depth = Path_NodeCount(ctx->path); |
| 238 | |
| 239 | // Can we advance? |
| 240 | if(_AllPathsCtx_LevelNotEmpty(ctx, depth)) { |
| 241 | // Get a new frontier. |
| 242 | LevelConnection frontierConnection = array_pop(ctx->levels[depth]); |
| 243 | Node frontierNode = frontierConnection.node; |
| 244 | |
| 245 | /* See if frontier is already on path, |
| 246 | * it is OK for a path to contain an entity twice, |
| 247 | * such as in the case of a cycle, but in such case we |
| 248 | * won't expand frontier. |
| 249 | * i.e. closing a cycle and continuing traversal. */ |
| 250 | bool frontierAlreadyOnPath = Path_ContainsNode(ctx->path, &frontierNode); |
| 251 | |
| 252 | // Add frontier to path. |
| 253 | Path_AppendNode(ctx->path, frontierNode); |
| 254 | |
| 255 | /* If depth is 0 this is the source node, there is no leading edge to it. |
| 256 | * For depth > 0 for each frontier node, there is a leading edge. */ |
| 257 | if(depth > 0) Path_AppendEdge(ctx->path, frontierConnection.edge); |
| 258 | |
| 259 | // Update path depth. |
| 260 | depth++; |
| 261 | |
| 262 | /* Introduce neighbors only if path depth < maximum path length. |
| 263 | * and frontier wasn't already expanded. */ |
| 264 | if(depth < ctx->maxLen && !frontierAlreadyOnPath) { |
| 265 | addNeighbors(ctx, &frontierConnection, depth, ctx->dir); |
| 266 | } |
| 267 | |
| 268 | // See if we can return path. |
| 269 | /* TODO Note that further calls to this function will continue to operate on |
| 270 | * this path, so it is essential that the caller does not modify it (or creates |
| 271 | * a copy beforehand). If future features like an algorithm API use this routine, |
| 272 | * they should either be responsible for memory safety or a memory-safe boolean/routine |
| 273 | * should be offered. */ |
| 274 | if(depth >= ctx->minLen && depth <= ctx->maxLen) { |
| 275 | if(ctx->dst != NULL) { |
| 276 | Node dst = Path_Head(ctx->path); |
| 277 | if(ENTITY_GET_ID(ctx->dst) != ENTITY_GET_ID(&dst)) continue; |
| 278 | } |
| 279 | return ctx->path; |
| 280 | } |
| 281 | } else { |
| 282 | // No way to advance, backtrack. |
| 283 | Path_PopNode(ctx->path); |
| 284 | if(Path_EdgeCount(ctx->path)) Path_PopEdge(ctx->path); |
| 285 | } |
| 286 | } |
| 287 | // Couldn't find a path. |
| 288 | return NULL; |
| 289 | } |
| 290 | |
| 291 | Path *AllPathsCtx_NextPath(AllPathsCtx *ctx) { |
no test coverage detected