public IEnumerable <PossibleAssembly> ExtendFromStartNode(DeBruijnNode start) { //TODO: I believe this handles figure 8s and palindromes just fine, should verify though. //First go Right var rightNeighbors = start.GetRightExtensionNodesWithOrientationMarkingEdgeAsVisited(false); List <PossibleAssembly> rights = new List <PossibleAssembly>(); foreach (var direction in rightNeighbors) { PossibleAssembly pa = new PossibleAssembly(start, true); rights.AddRange(ExtendChain(pa, direction.Key, true, direction.Value)); } List <PossibleAssembly> lefts = new List <PossibleAssembly>(); var leftNeighbors = start.GetLeftExtensionNodesWithOrientationMarkingEdgeAsVisited(false); foreach (var direction in leftNeighbors) { PossibleAssembly pa = new PossibleAssembly(start, false); lefts.AddRange(ExtendChain(pa, direction.Key, false, direction.Value)); } //Now to combine a left and right chain if (lefts.Count > 0 && rights.Count > 0) { foreach (var right in rights) { foreach (var left in lefts) { yield return(new PossibleAssembly(left, right)); } } } else if (lefts.Count > 0) { foreach (var left in lefts) { yield return(left); } } else if (rights.Count > 0) { foreach (var right in rights) { yield return(right); } } }
private IEnumerable <PossibleAssembly> ExtendChain(PossibleAssembly currentPath, DeBruijnNode nextNeighbor, bool goingRight, bool sameOrientation) { byte nextSymbol = MetaNode.GetNextSymbol(nextNeighbor, KmerLength, !goingRight); currentPath.Add(nextNeighbor, nextSymbol); nextNeighbor.IsVisited = true; bool nextRight = !goingRight ^ sameOrientation; List <KeyValuePair <DeBruijnNode, bool> > nextNodes = nextRight ? nextNeighbor.GetRightExtensionNodesWithOrientationMarkingEdgeAsVisited() : nextNeighbor.GetLeftExtensionNodesWithOrientationMarkingEdgeAsVisited(); DeBruijnNode next; //DeBruijnNode last = currentPath.constituentNodes[currentPath.constituentNodes.Count-1]; //DeBruijnNode first=currentPath.constituentNodes[0]; while (nextNodes.Count == 1) { var nextSet = nextNodes.First(); next = nextSet.Key; sameOrientation = nextSet.Value; nextRight = (!nextRight) ^ sameOrientation; nextSymbol = MetaNode.GetNextSymbol(next, KmerLength, !nextRight); //now check if we are in a circle or a loop at the end, these are very annoying situtations, basic criteria, can't leave //the same node the same way twice if (next.IsVisited && currentPath.constituentNodes.Contains(next)) { //okay, if we are equal to the first node or the last node, we can't leave or return the same way we came, otherwise we are done. var excludedNextNodes = currentPath.GetPreviousWaysNodeWasLeft(next); //how many neighbors dow we have in this group? var temp = nextRight ? next.GetRightExtensionNodesWithOrientationMarkingEdgeAsVisited() : next.GetLeftExtensionNodesWithOrientationMarkingEdgeAsVisited(); temp = temp.Where(x => !excludedNextNodes.Contains(x.Key)).ToList(); //only one way to go if (temp.Count == 1) { nextNodes = temp; //currentPath.contigSequence.Add(nextSymbol); currentPath.Add(next, nextSymbol); next.IsVisited = true; //flag not actually used though } else if (temp.Count == 0) //done { if (currentPath.constituentNodes[0] == next) { currentPath.CircularLoop = true; } yield return(currentPath); //nextNodes.Clear();//we are done yield break; } else //Extend path using all feasible options, then continue. { foreach (var neighbor in temp) { foreach (var v in ExtendChain(currentPath.Clone(), neighbor.Key, nextRight, neighbor.Value)) { yield return(v); } } //nextNodes.Clear();//done yield break; } } else { //currentPath.contigSequence.Add(nextSymbol); currentPath.Add(next, nextSymbol); next.IsVisited = true;//flag not actually used though nextNodes = nextRight ? next.GetRightExtensionNodesWithOrientationMarkingEdgeAsVisited() : next.GetLeftExtensionNodesWithOrientationMarkingEdgeAsVisited(); } } //If we have more than one node remaining, have to kick it off. if (nextNodes.Count > 1) { foreach (var neighbor in nextNodes) { foreach (var v in ExtendChain(currentPath.Clone(), neighbor.Key, nextRight, neighbor.Value)) { yield return(v); } } } if (nextNodes.Count == 0) { yield return(currentPath); } }