constructors
*/
- public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list ,boolean verbose){
+ public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,LabelList root_node_list ,int max_depth ,boolean verbose){
super( node_map ,recognizer_f_list );
-
- // Step 2: Run the cycle detection logic and mark up the graph.
- TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,verbose );
-
- // You can further process cycle detection results here if needed
-
+ TokenSet cycle_detection_result = graph_mark_cycles(root_node_list ,max_depth ,verbose);
}
-
- public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list){
+ public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,LabelList root_node_list){
super( node_map ,recognizer_f_list );
-
- // Step 2: Run the cycle detection logic and mark up the graph.
- TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,this.debug );
-
- // You can further process cycle detection results here if needed
+ TokenSet cycle_detection_result = graph_mark_cycles(root_node_list);
}
item on the cycle stack would be used as root nodes for a new cycle
search. Note the leftmost cycle on each recursive search on the leftmost
node, will be the original cycle.
-
-
*/
/*
- Cycle detection is based on label compares. Lookup is done to mark the node.
-
- 1. Given a path.
+ Given a path to a node in the graph, `left_path`.
- 2. Checks if the rightmost item, k_n, in the path occurs earlier in the path, say at k_i.
- If so, marks the nodes involved in the cycle.
-
- 3. if there is no cycle, returns null, otherwise returns the Interval [i ,n]
+ Checks if the rightmost node (referenced by label) recurs earlier in the path.
+ Presumably the rightmost node has been recently appended to the path.
+ If there is no cycle, returns null, otherwise returns the interval [i ,n],
+ of indexes into the path where the cycle is found. `n` will always be
+ the index of the rightmost node in the path list.
*/
- private List<Integer> path_find_cycle(List<LabelList> path_stack ,LabelList path ){
+ private List<Integer> path_find_cycle(LabelList path ){
if( path.size() <= 1 ) return null;
int rightmost_index = path.size() - 1;
}
/*
- When descending down the tree, if we find a cycle, we reset the iterator
- to the top of the cycle. The caller will then return to its caller as though
- having found a leaf node.
+ Given a path_stack, which is our graph iterator. Also given `left_path`
+ which is derived from path_stack, and represents the left most neighbor
+ of each of the neighbor lists in the path_stack.
+
+ Calls `path_find_cycle` to see if the most recently added to the path node,
+ i.e. the rightmost node, forms a cycle with other node(s) in the path. Upon
+ finding a cycle, unwinds the path_stack (decrements the iterator), to the top
+ of the cycle.
+
+ If a cycle is found, returns false, otherwise returns true.
*/
- private boolean graph_descend_cycle_case( List<Label> left_path ,List<LabelList> path_stack ,boolean verbose ){
+ private boolean graph_descend_cycle_case( LabelList left_path ,List<LabelList> path_stack ,boolean verbose ){
- List<Integer> cycle_index_interval = path_find_cycle( left_path ,verbose );
+ List<Integer> cycle_index_interval = path_find_cycle(left_path);
if( cycle_index_interval == null ){
return false; // No cycle found
}
if(verbose) Util.print_list(
"Found cycle:"
- ,left_path.subList( cycle_i0 ,n +1)
+ ,left_path.subList( cycle_i0 ,cycle_n + 1)
);
// Mark cycle members
for( int i = cycle_i0; i <= cycle_n; i++ ){
Label node_label = left_path.get(i);
Node node = super.lookup( node_label );
- if( node != null ){
- if( node.get("mark") == null ){
- node.put( "mark" ,new HashTokenSet() );
- }
- ( (TokenSet)node.get("mark") ).add( "cycle_member" );
+ if (node != null){
+ node.mark(new Token("cycle_member"));
}else{
undefined_node_list.add( node_label );
}
/*
Given a path_stack initialized to the root nodes for the graph traversal.
- Perform depth first descent from each node in succession. Algorithm
- works from left to right in the child lists.
-
- Searches for cycles in the descent paths.
+ Perform depth first descent from each node in succession while searching for
+ cycles by calling graph_descent_cycle_case. Algorithm follows the leftmost
+ nodes in the path_stack list of child lists.
+ Returns the descent termination condition.
*/
- private static TokenSet graph_descend_set = new TokenSet();
- static {
- graph_descend_set.add( new Token("empty_path_stack") );
- graph_descend_set.add( new Token("cycle_found") );
- graph_descend_set.add( new Token("undefined_node") );
- graph_descend_set.add( new Token("leaf") );
- graph_descend_set.add( new Token("max_depth_exceeded") );
- }
+ private static TokenSet graph_descend_set = new TokenSet() {{
+ add(new Token("empty_path_stack"));
+ add(new Token("cycle_found"));
+ add(new Token("undefined_node"));
+ add(new Token("leaf"));
+ add(new Token("max_depth_reached"));
+ }};
private TokenSet graph_descend( List<LabelList> path_stack ,int max_depth ,boolean verbose ){
- TokenSet ret_value = new HashTokenSet();
+ TokenSet ret_value = new TokenSet();
if( path_stack.isEmpty() ){
- ret_value.add( "empty_path_stack" );
+ ret_value.add( new Token("empty_path_stack") );
return ret_value;
}
// a leftmost path through the neighbor lists of the leftmost nodes
- LabelList left_path = new ArrayList<>();
+ LabelList left_path = new LabelList();
for( LabelList neighbor_list : path_stack ){
left_path.add( neighbor_list.get(0) );
}
// cycle case
if( graph_descend_cycle_case( left_path ,path_stack ,verbose ) ){
- ret_value.add( "cycle_found" );
+ ret_value.add( new Token("cycle_found") );
return ret_value;
}
// Non-cycle case, descend further into the tree.
// Increment the graph iterator (path_stack) to go down a level.
- Label it_pt_label = path_stack.get( path_stack.size() - 1 ).get(0);
- Node it_pt_node = super.lookup( it_pt_label );
- if( it_pt_node == null ){
- ret_value.add( "undefined_node" );
+ Label it_node_label = path_stack.get( path_stack.size() - 1 ).get(0);
+ Node it_node = super.lookup( it_node_label );
+ if( it_node == null ){
+ ret_value.add( new Token("undefined_node") );
return ret_value;
}
- NodeList neighbor_list = it_pt_node.get("neighbor");
- if( neighbor_list == null || neighbor_list.isEmpty() ){
- ret_value.add( "leaf" );
+ LabelList neighbor_list = it_node.neighbor_LabelList();
+ if( neighbor_list.isEmpty() ){
+ ret_value.add( new Token("leaf") );
return ret_value;
}
- path_stack.add( new ArrayList<>((LabelList) neighbor_list) );
+ // The iterator will destroy the neighbor_list as we traverse the graph,
+ // so we give it a copy.
+ path_stack.add( new LabelList(neighbor_list) );
Label it_next_label = neighbor_list.get(0);
- left_path.add( it_next_label ); // Properly add the label to the left_path
+ left_path.add( it_next_label ); // also extend the left_path
// bound the size of problem we are willing to work on
// set max_depth <= 0 to have this test ignored
if( max_depth > 0 ){
max_depth--; // Typo fixed
if( max_depth == 0 ){
- ret_value.add( "max_depth_exceeded" );
+ if(verbose){
+ Util.print_list("GraphDirectedAcyclic.GraphDescend:: max_depth reached, preternaturally ending the descent:" ,path_stack);
+ }
+ ret_value.add( new Token("max_depth_reached") );
return ret_value;
}
}
- }while( true ); // while descend
+ }while(true); // while descend
}
/*
- Given root_node_label_list, marks up the graph and returns a set possibly
- containing 'all_wellformed' and 'cycles_exist'.
+ Given root_node_label_list and a maximum depth for traversal.
- Marks potentially added to each node include 'cycle_member' and 'wellformed'.
- Note that these marks are independent.
- */
- // Saigens updates:
- /*
- Given root_node_label_list, marks up the graph and returns a set possibly
- containing 'all_wellformed' and 'cycles_exist'.
+ Cycles are handled gracefully, rather the constraint `max_depth` is present
+ because the user is allowed to provide production *functions* for generating
+ nodes. Who knows what crazy graphs a user could come up with, see the
+ document on the algorithm for more info.
+
+ Does a left first depth first traversal of the graph while marking
+ cycles. This routine pushes the graph traversal iterator to the right one
+ node after a left descent traversal, then it calls `graph_descend` descend
+ from said node.
- Marks potentially added to each node include 'cycle_member' and 'wellformed'.
- Note that these marks are independent.
-*/
- /*
+ Returns one or more symbols that characterize the termination condition.
+ */
-public TokenSet mark_cycle_graph( LabelList root_node_label_list ,boolean verbose ){
- TokenSet ret_value = new HashTokenSet();
+ public static TokenSet graph_mark_cycles_set = new TokenSet() {{
+ add(new Token("empty_root_label_list"));
+ add(new Token("cycle_exists"));
+ add(new Token("undefined_node_exists"));
+ add(new Token("bad_descent_termination"));
+ add(new Token("max_depth_reached"));
+ }};
+ public TokenSet graph_mark_cycles( LabelList root_node_LabelList ,int max_depth ,boolean verbose ){
+ TokenSet ret_value = new TokenSet();
boolean exists_malformed = false;
TokenSet result; // used variously
- if( root_node_label_list.isEmpty() ) return ret_value;
-
- // Initialize the DFS tree iterator.
- List<LabelList> path_stack = new ArrayList<>();
- path_stack.add( new ArrayList<>(root_node_label_list) );
-
- // Iterate over left side tree descent, avoids complexity in cycle detection logic.
- do{
- result = graph_descend( path_stack ,verbose );
- if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
- if( result.contains("undefined_node") ) exists_malformed = true; // Corrected flag
-
- // Increment the iterator to the next leftmost path
- LabelList top_list = path_stack.get( path_stack.size() - 1 );
- top_list.remove(0);
- if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
-
- }while( !path_stack.isEmpty() );
-
- if( !exists_malformed ) ret_value.add("all_wellformed");
-
- if( verbose ){
- if( exists_malformed ) System.out.println("One or more malformed nodes were found.");
- boolean exists_cycle = ret_value.contains("cycle_exists");
- if( exists_cycle ) System.out.println("One or more cyclic dependency loops found.");
- if( exists_malformed || exists_cycle ) System.out.println("Will attempt to build unaffected nodes.");
+ if( root_node_LabelList.isEmpty() ){
+ ret_value.add(new Token("empty_root_label_list"));
+ return ret_value;
}
- return ret_value;
-}
-
-public TokenSet mark_cycle_graph( LabelList root_node_label_list ){
- return mark_cycle_graph( root_node_label_list ,true );
-}
-//------------------
-
- public TokenSet mark_cycle_graph(LabelList root_node_label_list ,boolean verbose){
- TokenSet ret_value = new HashSet<>();
- boolean exists_malformed = false;
- TokenSet result; // used variously
-
- if( root_node_label_list.isEmpty() ) return ret_value;
-
- // Initialize the DFS tree iterator.
+ // `path_stack` is our graph iterator. It is initialized with the
+ // `root_node_LabelList`.
List<LabelList> path_stack = new ArrayList<>();
- path_stack.add( new ArrayList<>(root_node_label_list) );
+ path_stack.add( new LabelList(root_node_LabelList) );
- // iterate over left side tree descent, not ideal as it starts at the
- // root each time, but avoids complexity in the cycle detection logic.
+ // each call to graph_descend does a leftmost descent to a leaf
do{
- result = graph_descend(path_stack ,verbose);
- if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
- if( result.contains("undefined_node") ) exists_undefined = true;
+ result = graph_descend( path_stack ,max_depth ,verbose );
+ if( result.contains(new Token("cycle_found")) ) ret_value.add(new Token("cycle_exists"));
+ if( result.contains(new Token("undefined_node")) ) ret_value.add(new Token("undefined_node_exists"));
+ if( result.contains(new Token("max_depth_reached")) ) ret_value.add(new Token("max_depth_reached"));
+ if( !result.contains(new Token("leaf")) && !result.contains(new Token("cycle_found")) ) ret_value.add(new Token("bad_descent_termination"));
- // increment the iterator to the next leftmost path
+ // Push the iterator to the right by one node
LabelList top_list = path_stack.get( path_stack.size() - 1 );
top_list.remove(0);
if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
}while( !path_stack.isEmpty() );
- if( !exists_malformed ) ret_value.add("all_wellformed");
-
- if( verbose ){
- if( exists_malformed ) System.out.println("one or more malformed nodes were found");
- boolean exists_cycle = ret_value.contains("cycle_exists");
- if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
- if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
+ if(verbose){
+ if( ret_value.contains("bad_descent_termination") ){
+ System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: graph_descend terminated with other than leaf or cycle found condition.");
+ }
+ if( ret_value.contains("cycle_exists") ){
+ System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: There are one or more cycles in the graph.");
+ }
+ if( ret_value.contains("undefined_node_exists") ){
+ System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: There are one or more node label references that do not correspond to a defined node in this graph.");
+ }
}
return ret_value;
}
- public TokenSet mark_cycle_graph(LabelList root_node_label_list){
- return mark_cycle_graph(root_node_label_list ,true);
+ public TokenSet graph_mark_cycles(LabelList root_node_LabelList){
+ return graph_mark_cycles(root_node_LabelList ,this.debug?40:-1 ,this.debug);
}
- */
+
+
/*--------------------------------------------------------------------------------
Graph traversal
*/
@Override
- public Node lookup(Label node_label, boolean verbose) {
- Node node = super.lookup( node_label ,verbose );
-
- // Ensure the node exists and the mark property exists
- if( node != null ){
- TokenSet mark = (TokenSet)node.get("mark");
-
- // Check if the mark property exists and contains "cycle_member"
- if( mark != null && mark.contains("cycle_member") ){
- if( verbose ){
- System.out.println("Node is part of a cycle and will not be returned: " + node_label);
- }
- return null; // Exclude nodes in cycles
+ public Node lookup(Label node_label, boolean verbose){
+ Node node = super.lookup(node_label, verbose);
+ if(node != null && node.has_mark(new Token("cycle_member"))){
+ if(verbose){
+ System.out.println("GraphDirectedAcyclic.lookup:: Node is part of a cycle so it will not be returned: " + node_label);
}
+ return null; // Exclude nodes in cycles
}
-
return node;
}
- // Overloaded lookup method with default verbosity (true)
public Node lookup(Label node_label){
return lookup(node_label ,this.debug);
}
-
-
}