Commits

Shlomi Fish committed 8d81824

Got t/ascii.t to pass on perl-5.18.x.

This involved regularising the order of the hash traversals, so it will pass
all around.

  • Participants
  • Parent commits 9b367d3

Comments (0)

Files changed (92)

Graph-Easy/lib/Graph/Easy.pm

 use strict;
 my $att_aliases;
 
+use Graph::Easy::Util qw(ord_values);
+
 BEGIN 
   {
   # a few aliases for backwards compatibility
 
   delete $self->{chains};
   # clean out pointers in child-objects so that they can safely be reused
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     if (ref($n))
       {
       delete $n->{group};
       }
     }
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     if (ref($e))
       {
       delete $e->{from};
       }
     }
-  for my $g (values %{$self->{groups}})
+  for my $g (ord_values ( $self->{groups} ))
     {
     if (ref($g))
       {
   my $self = shift;
 
   my %count;
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     my $id = "$e->{to}->{id},$e->{from}->{id}";
     return 0 if exists $count{$id};
   my $self = shift;
 
   my @roots;
-  for my $node (values %{$self->{nodes}})
+  for my $node (ord_values ( $self->{nodes} ))
     {
     push @roots, $node 
       if (keys %{$node->{edges}} != 0) && !$node->has_predecessors();
   my $self = shift;
 
   my @roots;
-  for my $node (values %{$self->{nodes}})
+  for my $node (ord_values ( $self->{nodes} ))
     {
     push @roots, $node 
       if (keys %{$node->{edges}} == 0) || !$node->has_predecessors();
 
   return scalar keys %$n unless wantarray;	# shortcut
 
-  values %$n;
+  return ord_values ( $n );
   }
 
 sub anon_nodes
   if (!wantarray)
     {
     my $count = 0;
-    for my $node (values %$n)
+    for my $node (ord_values ($n))
       {
       $count++ if $node->is_anon();
       }
     }
 
   my @anon = ();
-  for my $node (values %$n)
+  for my $node (ord_values ( $n))
     {
     push @anon, $node if $node->is_anon();
     }
 
   return scalar keys %$e unless wantarray;	# shortcut
 
-  values %$e;
+  ord_values ($e);
   }
 
 sub edges_within
 
   return scalar keys %$e unless wantarray;	# shortcut
 
-  values %$e;
+  ord_values ($e);
   }
 
 sub sorted_nodes
 
   return $self unless ref($x) && ref($y) && ($x != $y);
 
-  for my $e (values %{$x->{edges}})
+  for my $e (ord_values ( $x->{edges} ))
     {
     $e->flip() if $e->{from} == $x && $e->{to} == $y;
     }
 
   # if we have nodes with rounded shapes:
   my $rounded = 0;
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     $rounded ++ and last if $n->shape() =~ /circle|ellipse|rounded/;
     }
     }
 
   # draw all cells into framebuffer
-  foreach my $v (values %$cells)
+  foreach my $v (ord_values ($cells))
     {
     next if $v->isa('Graph::Easy::Node::Cell');		# skip empty cells
 
   $out =~ s/\n+\z/\n/;		# remove trailing empty lines
 
   # restore height/width of cells from minw/minh
-  foreach my $v (values %$cells)
+  foreach my $v (ord_values $cells)
     {
     $v->{h} = $v->{minh};
     $v->{w} = $v->{minw};
     # clone the attributes
     $ng->{att} = $self->_clone( $self->{groups}->{$g}->{att} );
     }
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     my $nn = $new->add_node($n->{name});
     # clone the attributes
     # restore group membership for the node
     $nn->add_to_group( $n->{group}->{name} ) if $n->{group};
     }
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     my $ne = $new->add_edge($e->{from}->{name}, $e->{to}->{name} );
     # clone the attributes
   # if the node is part of a group, deregister it first from there
   $B->{group}->del_node($B) if ref($B->{group});
 
-  my @edges = values %{$A->{edges}};
+  my @edges = ord_values ( $A->{edges} );
 
   # drop all connections from A --> B
   for my $edge (@edges)
     }
 
   # Move all edges from/to B over to A, but drop "B --> B" and "B --> A".
-  for my $edge (values %{$B->{edges}})
+  for my $edge (ord_values ( $B->{edges} ))
     {
     # skip if going from B --> A or B --> B
     next if $edge->{to} == $A || ($edge->{to} == $B && $edge->{from} == $B);
   delete $self->{nodes}->{$node->{name}};
 
   # delete all edges from/to this node
-  for my $edge (values %{$node->{edges}})
+  for my $edge (ord_values ( $node->{edges} ))
     {
     # drop the edge from our global edge list
     delete $self->{edges}->{$edge->{id}};
   # get the groups at level 0
   my $current = 0;
   my @todo;
-  for my $g (values %{$self->{groups}})
+  for my $g (ord_values ( $self->{groups} ))
     {
     # no group set => belongs to graph, set to ourself => belongs to ourself
     push @todo, $g if ( ($are_graph && !defined $g->{group}) || $g->{group} == $self);
   if (!wantarray)
     {
     my $count = 0;
-    for my $group (values %$n)
+    for my $group (ord_values ($n))
       {
       $count++ if $group->is_anon();
       }
     }
 
   my @anon = ();
-  for my $group (values %$n)
+  for my $group (ord_values ($n))
     {
     push @anon, $group if $group->is_anon();
     }

Graph-Easy/lib/Graph/Easy/As_graphml.pm

 #   </y:ShapeNode>
 # </data>
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _as_graphml
   {
   my $self = shift;
 
   ###########################################################################
   # now the attributes on the objects:
-  for my $o (@nodes, values %{$self->{edges}})
+  for my $o (@nodes, ord_values ( $self->{edges} ))
     {
     $txt .=
 	$self->_graphml_attr_keys( $tpl, $tpl_no_default, $o->class(),
 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub as_graphml
   {
   my ($self, $indent, $ids) = @_;
 	$self->{graph}->type() . "\">\n";
   $txt .= $self->{graph}->_attributes_as_graphml($self, $indent, $ids->{graph});
 
-  foreach my $n (values %{$self->{nodes}})
+  foreach my $n (ord_values ( $self->{nodes} ))
     {
     my @out = $n->sorted_successors();
 

Graph-Easy/lib/Graph/Easy/As_graphviz.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 my $remap = {
   node => {
     'align' => undef,
   {
   my ($self,$group) = @_;
   $group->{_order}++;
-  for my $sg (values %{$group->{groups}})
+  for my $sg (ord_values( $group->{groups}))
 	{
 		$self->_order_group($sg);
 	}
     my $indent = '  ' x ($group->{_order});
     $txt .= $indent."subgraph \"cluster$group->{id}\" {\n${indent}label=\"$name\";\n";
 
-	for my $sg (values %{$group->{groups}})
+	for my $sg (ord_values ( $group->{groups} ))
 	{
 		#print '--'.$sg->{name}."\n";
 		$txt .= $self->_as_graphviz_group($sg,$indent);
       }
 
     # output node connections in this group
-    for my $e (values %{$group->{edges}})
+    for my $e (ord_values $group->{edges})
       {
       next if exists $e->{_p};
       $txt .= $self->_generate_edge($e, $indent);
   $self->_edges_into_groups() if $groups > 0;
 
   # output the groups (aka subclusters)
-  for my $group (values %{$self->{groups}})
+  for my $group (ord_values $self->{groups})
   {
    $self->_order_group($group);
   }
 
   # insert now edges between groups (clusters/subgraphs)
 
-  foreach my $e (values %{$self->{edges}})
+  foreach my $e (ord_values $self->{edges})
     {
     $txt .= $self->_generate_group_edge($e, '  ') 
      if $e->{from}->isa('Graph::Easy::Group') ||
     }
 
   # clean up
-  for my $n ( values %{$self->{nodes}}, values %{$self->{edges}})
+  for my $n ( ord_values( $self->{nodes}), ord_values( $self->{edges} ))
     {
     delete $n->{_p};
     }

Graph-Easy/lib/Graph/Easy/As_vcg.pm

   "  edge:$edge_att\n";				# return edge text
   }
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _as_vcg
   {
   my ($self) = @_;
 
   # gather all edge classes to build the classname attribute from them:
   $self->{_vcg_edge_classes} = {};
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     my $class = $e->sub_class();
     $self->{_vcg_edge_classes}->{$class} = undef if defined $class && $class ne '';
 #      }
 
 #    # output node connections in this group
-#    for my $e (values %{$group->{edges}})
+#    for my $e (ord_values ( $group->{edges} ))
 #      {
 #      next if exists $e->{_p};
 #      $txt .= $self->_generate_edge($e, $indent);
 
   # insert now edges between groups (clusters/subgraphs)
 
-#  foreach my $e (values %{$self->{edges}})
+#  foreach my $e (ord_values ( $self->{edges} ))
 #    {
 #    $txt .= $self->_generate_group_edge($e, '  ') 
 #     if $e->{from}->isa('Graph::Easy::Group') ||
 #    }
 
   # clean up
-  for my $n ( values %{$self->{nodes}}, values %{$self->{edges}})
+  for my $n ( ord_values ( $self->{nodes} ), ord_values ( $self->{edges} ))
     {
     delete $n->{_p};
     }

Graph-Easy/lib/Graph/Easy/Group.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 #############################################################################
 
 sub _init
   {
   my $self = shift;
 
-  wantarray ? ( values %{$self->{nodes}} ) : scalar keys %{$self->{nodes}};
+  wantarray ? ( ord_values ( $self->{nodes} ) ) : scalar keys %{$self->{nodes}};
   }
 
 sub edges
   # edges leading from/to this group
   my $self = shift;
 
-  wantarray ? ( values %{$self->{edges}} ) : scalar keys %{$self->{edges}};
+  wantarray ? ( ord_values ( $self->{edges} ) ) : scalar keys %{$self->{edges}};
   }
 
 sub edges_within
   # edges between nodes inside this group
   my $self = shift;
 
-  wantarray ? ( values %{$self->{edges_within}} ) : 
+  wantarray ? ( ord_values ( $self->{edges_within} ) ) : 
 		scalar keys %{$self->{edges_within}};
   }
 
 
   no warnings 'recursion';
 
-  push @$cur, values %{$self->{groups}};
+  push @$cur, ord_values ( $self->{groups} );
 
   return if $level >= $max_level;
 
-  for my $g (values %{$self->{groups}})
+  for my $g (ord_values ( $self->{groups} ))
     {
     $g->_groups_within($level+1,$max_level, $cur) if scalar keys %{$g->{groups}} > 0;
     }
   if ($name eq 'nodeclass')
     {
     my $class = $self->{att}->{nodeclass};
-    for my $node (values %{ $self->{nodes} } )
+    for my $node (ord_values ( $self->{nodes} ) )
       {
       $node->sub_class($class);
       }
     {
     # find all edges that mention this node and drop them from the group
     my $edges = $self->{edges_within};
-    for my $e (values %$edges)
+    for my $e (ord_values ( $edges))
       {
       delete $edges->{ $e->{id} } if $e->{from} == $n || $e->{to} == $n;
       }
 
   # find all edges that mention this node and drop them from the group
   my $edges = $self->{edges_within};
-  for my $e (values %$edges)
+  for my $e (ord_values ( $edges))
     {
     delete $edges->{ $e->{id} } if $e->{from} == $n || $e->{to} == $n;
     }
 
   my $lc;						# the label cell
 
-  for my $c (values %{$self->{_cells}})
+  for my $c (ord_values ( $self->{_cells} ))
     {
     # find a cell where to put the label
     next unless $c->{cell_class} =~ $match;
     {
     my ($left, $right);
     # find left/right most coordinates
-    for my $c (values %{$self->{_cells}})
+    for my $c (ord_values ( $self->{_cells} ))
       {
       next if $c->{y} != $lc->{y};
       $left = $c->{x} if !defined $left || $left > $c->{x};  
     my $center = int(($right - $left) / 2 + $left);
     my $min_dist;
     # find the cell mostly near the center in the found top-row
-    for my $c (values %{$self->{_cells}})
+    for my $c (ord_values ( $self->{_cells} ))
       {
       next if $c->{y} != $lc->{y};
       # squared to get rid of sign
   my ($self, $cells) = @_;
 
   # Set the right cell class for all of our cells:
-  for my $cell (values %{$self->{_cells}})
+  for my $cell (ord_values ( $self->{_cells} ))
     {
     $cell->_set_type($cells);
     }

Graph-Easy/lib/Graph/Easy/Layout.pm

 use Graph::Easy::Layout::Repair;		# group cells and splicing/repair
 use Graph::Easy::Layout::Path;			# path management
 
+use Graph::Easy::Util qw(ord_values);
+
 #############################################################################
 
 sub _assign_ranks
 
     my %suc;
 
-    for my $e (values %{$node->{edges}})
+    for my $e (ord_values ( $node->{edges} ))
       {
       my $to = $e->{to};
 
 
     # for all successors
     #for my $s (sort { $a->{name} cmp $b->{name} || $a->{id} <=> $b->{id} }  values %suc)
-    for my $s (values %suc)
+    for my $s (ord_values ( \%suc))
       {
       print STDERR "# suc $s->{name} chain ", $s->{_chain} || 'undef',"\n" if $self->{debug};
 
   # compute predecessors for all nodes: O(1)
   my $p;
   my $has_origin = 0;
-  foreach my $n (values %{$self->{nodes}}, values %{$self->{groups}})
-#  for my $n (values %{$self->{nodes}})
+  foreach my $n (ord_values ( $self->{nodes} ), ord_values ( $self->{groups} ))
+#  for my $n (ord_values ( $self->{nodes} ))
     {
     $n->{_chain} = undef;				# reset chain info
     $has_origin = 0;
 
   # cleanup
   $self->{chains} = undef;		# drop chain info
-  foreach my $n (values %{$self->{nodes}}, values %{$self->{groups}})
+  foreach my $n (ord_values ( $self->{nodes} ), ord_values ( $self->{groups} ))
     {
     # drop old chain info
     $n->{_next} = undef;
   # before the layout phase, we drop cached information from the last run
   my $self = shift;
 
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     # XXX after we laid out the individual groups:    
     # skip nodes that are not part of the current group
     $n->{w} = undef;			# force size recalculation
     $n->{_todo} = undef;		# mark as todo
     }
-  for my $g (values %{$self->{groups}})
+  for my $g (ord_values ( $self->{groups} ))
     {
     $g->{x} = undef; $g->{y} = undef;	# mark every group as not placed yet
     $g->{_todo} = undef;		# mark as todo
 
   $self->_drop_caches();
 
-  local $_; $_->_grow() for values %{$self->{nodes}};
+  local $_; $_->_grow() for ord_values ( $self->{nodes} );
 
   $self->_assign_ranks();
 
     }
 
   # mark all edges as unprocessed, so that we do not process them twice
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     { 
     $edge->_clear_cells();
     $edge->{_todo} = undef;		# mark as todo
   # take longest chain, resolve it and all "connected" chains, repeat until
   # heap is empty
 
-  for my $chain (sort { 
+  for my $chain (sort {
 
      # chain starting at root first
      (($b->{start} == $root) <=> ($a->{start} == $root)) ||
      (defined($a->{start}->{origin}) <=> defined ($b->{start}->{origin})) ||
 
      # last resort, sort on name of the first node in chain
-     ($a->{start}->{name} cmp $b->{start}->{name}) 
+     ($a->{start}->{name} cmp $b->{start}->{name})
 
      } values %{$self->{chains}})
     {
   # left-over edges and links. We do this for each node, and then for each of
   # its edges, but do the edges shortest-first.
  
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     push @todo, $self->_action( ACTION_NODE, $n, 0 ); # if exists $n->{_todo};
 
     # gather to-do edges
     my @edges = ();
     for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
-#    for my $e (values %{$n->{edges}})
+#    for my $e (ord_values ( $n->{edges} ))
       {
       # edge already done?
       next unless exists $e->{_todo};
     push @todo, [ ACTION_SPLICE ] if scalar $self->groups();
 
     # now do all group-to-group and node-to-group and group-to-node links:
-    for my $n (values %{$self->{groups}})
+    for my $n (ord_values ( $self->{groups} ))
       {
       }
     }
   # count placed nodes
   my $nodes = 0;
   my $i = 1;
-  for my $n (values %{$self->{nodes}})
+  for my $n (ord_values ( $self->{nodes} ))
     {
     $nodes++ if defined $n->{x};
     }
   my $edges = 0;
   $i = 1;
   # count fully routed edges
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     $edges++ if scalar @{$e->{cells}} > 0 && !exists $e->{_todo};
     }
 
   ###########################################################################
   # for each edge, compact HOR and VER stretches of cells
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     my $cells = $e->{cells};
 

Graph-Easy/lib/Graph/Easy/Layout/Chain.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 use constant {
   _ACTION_NODE  => 0, # place node somewhere
   _ACTION_TRACE => 1, # trace path from src to dest
     {
     # first do edges going from P to N
     #for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$pre->{edges}})
-    for my $e (values %{$pre->{edges}})
+    for my $e (ord_values ( $pre->{edges}))
       {
       # skip selfloops and backward links, these will be done later
       next if $e->{to} != $n;
 
     # gather all edges starting at $n, but do the ones with a flow first
 #    for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
-    for my $e (values %{$n->{edges}})
+    for my $e (ord_values ( $n->{edges})) 
       {
       # skip selfloops, these will be done later
       next if $e->{to} == $n;
   while (defined $n)
     {
 #    for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
-    for my $e (values %{$n->{edges}})
+    for my $e (ord_values $n->{edges})
       {
       next unless exists $e->{_todo};
 

Graph-Easy/lib/Graph/Easy/Layout/Force.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _layout_force
   {
   # Calculate for each node the force on it, then move them accordingly.
         }
 
       # for all edges connected at this node
-      for my $e (values %{$n->{edges}})
+      for my $e (ord_values ( $n->{edges} ))
 	{
 	# exclude self-loops
 	next if $e->{from} == $n && $e->{to} == $n;

Graph-Easy/lib/Graph/Easy/Layout/Grid.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _balance_sizes
   {
   # Given a list of column/row sizes and a minimum size that their sum must
   # first:
 
   # find all x and y occurances to sort them by row/columns
-  for my $cell (values %$cells)
+  for my $cell (ord_values $cells)
     {
     my ($x,$y) = ($cell->{x}, $cell->{y});
 
   $cols->{$mx+1} = 0;
 
   # do the last step again, but for multi-celled objects
-  for my $cell (values %$cells)
+  for my $cell (ord_values $cells)
     {
     my ($x,$y) = ($cell->{x}, $cell->{y});
 
   print STDERR "# Finding max. dimensions for framebuffer\n" if $self->{debug};
   my $max_y = 0; my $max_x = 0;
 
-  for my $v (values %$cells)
+  for my $v (ord_values $cells)
     {
     # Skip multi-celled nodes for later. 
     next if ($v->{cx}||1) + ($v->{cy}||1) != 2;
     }
 
   # repeat the previous step, now for multi-celled objects
-  foreach my $v (values %{$self->{cells}})
+  foreach my $v (ord_values ( $self->{cells} ))
     {
     next unless defined $v->{x} && (($v->{cx}||1) + ($v->{cy}||1) > 2);
 

Graph-Easy/lib/Graph/Easy/Layout/Repair.pm

 
 use strict;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _edges_into_groups
   {
   my $self = shift;
 
   # Put all edges between two nodes with the same group in the group as well
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values $self->{edges})
     {
     my $gf = $edge->{from}->group();
     my $gt = $edge->{to}->group();
   # cells as used (e.g. use only one global filler cell) since filler cells
   # aren't actually rendered, anyway.
 
-  for my $cell (values %$cells)
+  for my $cell (ord_values $cells)
     {
     next unless $cell->isa('Graph::Easy::Node::Cell');
 
   my ($self, $cell, $x, $y, $flag, $type, $match, $check, $where) = @_;
 
   my $edge = $cell->{edge};
-  if (grep { exists $_->{cell_class} && $_->{cell_class} =~ $match } values %$check)
+  if (grep { exists $_->{cell_class} && $_->{cell_class} =~ $match } ord_values ($check))
     {
     $cell->{type} &= ~ $flag;		# delete the flag
 
 
   if  (!ref($below) && (($cell->{type} & EDGE_END_MASK) == EDGE_END_S))
     {
-    if (grep { exists $_->{cell_class} && $_->{cell_class} =~ /g[tb]/ } values %{$rows->{$y}})
+    if (grep { exists $_->{cell_class} && $_->{cell_class} =~ /g[tb]/ } ord_values $rows->{$y})
       {
       # delete the start flag
       $cell->{type} &= ~ EDGE_END_S;
   $self->_repair_nodes();		# repair multi-celled nodes
 
   my $c = 'Graph::Easy::Group::Cell';
-  for my $cell (values %{$self->{cells}})
+  for my $cell (ord_values $self->{cells})
     {
     # DO NOT MODIFY $cell IN THE LOOP BODY!
 
   # three cells apart (y == 0 and y == 4) after the splicing, the step above
   # will not be able to close that hole - it will create fillers at y == 1 and
   # y == 3. So we close these holes now with an extra step.
-  for my $cell (values %{$self->{cells}})
+  for my $cell (ord_values ( $self->{cells} ))
     {
     # only for filler cells
     next unless $cell->isa('Graph::Easy::Group::Cell');
   # XXX TODO
   # we should "grow" the group area to close holes
 
-  for my $group (values %{$self->{groups}})
+  for my $group (ord_values ( $self->{groups} ))
     {
     $group->_set_cell_types($cells);
     }
   # create a mapping for each row/column so that we can repair edge starts/ends
   my $rows = {};
   my $cols = {};
-  for my $cell (values %$cells)
+  for my $cell (ord_values ($cells))
     {
     $rows->{$cell->{y}}->{$cell->{x}} = $cell;
     $cols->{$cell->{x}}->{$cell->{y}} = $cell;
 					# border rows/columns
 
   # for all groups, set the cell carrying the label (top-left-most cell)
-  for my $group (values %{$self->{groups}})
+  for my $group (ord_values ( $self->{groups} ))
     {
     $group->_find_label_cell();
     }
 
 # DEBUG:
-# for my $cell (values %$cells)
-#   { 
+# for my $cell (ord_values $cells)
+#   {
 #   $cell->_correct_size();
 #   }
 #

Graph-Easy/lib/Graph/Easy/Layout/Scout.pm

   EDGE_S_W() => [ 0,-1, EDGE_W_N_S, +1,0, EDGE_S_E_W ],
   };
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _get_joints
   { 
   # from a list of shared, already placed edges, get possible start/end fields
  
   my @R;
   # convert hash to array
-  for my $s (values %{$cells})
+  for my $s (ord_values ( $cells ))
     {
     push @R, @$s;
     }

Graph-Easy/lib/Graph/Easy/Node.pm

 use Graph::Easy::Attributes;
 @ISA = qw/Graph::Easy::Base/;
 
+use Graph::Easy::Util qw(ord_values);
+
 # to map "arrow-shape" to "arrowshape"
 my $att_aliases;
 
     } # end handling multi-celled node
 
   # unplace all edges leading to/from this node, too:
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     $e->_unplace($cells);
     }
 
   delete $self->{_todo};
 
-  for my $child (values %{$self->{children}})
+  for my $child (ord_values ( $self->{children} ))
     {
     $child->_mark_as_placed();
     }
 
   print STDERR "# placing children of $self->{name} based on $x,$y\n" if $self->{debug};
 
-  for my $child (values %{$self->{children}})
+  for my $child (ord_values ( $self->{children} ))
     {
     # compute place of children (depending on whether we are multicelled or not)
 
   # for relative flows, compute the incoming flow as base flow
 
   # check all edges
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     # only count incoming edges
     next unless $e->{from} != $self && $e->{to} == $self;
   if (!defined $in)
     {
     # check all predecessors
-    for my $e (values %{$self->{edges}})
+    for my $e (ord_values ( $self->{edges} ))
       {
       my $pre = $e->{from};
       $pre = $e->{to} if $e->{bidirectional};
   # count of outgoing edges
   my $outgoing = 0;
 
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     # count outgoing edges
     $outgoing++ if $e->{from} == $self;
       } # end for start/end port
     } # end for all edges
 
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     # the loop above will count all self-loops twice when they are
     # unrestricted. So subtract these again. Restricted self-loops
   return unless ref $self->{graph};
 
   my @edges;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     push @edges, $edge if $edge->{from} == $self && $edge->{to} == $other;
     }
   $self->_croak('port not defined') unless defined $port;
 
   my @edges;
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     # skip edges ending here if we look at start
     next if $e->{to} eq $self && $attr eq 'start';
   my ($self) = @_;
 
   my @edges;
-  for my $e (values %{$self->{edges}})
+  for my $e (ord_values ( $self->{edges} ))
     {
     my ($s_p,@ss_p) = $e->port('start');
     push @edges, $e if defined $s_p;
     $nodes->{ $to->{name} } = $to;
     }
 
-  (values %$nodes);
+  return (ord_values $nodes);
   }
 
 sub nodes_sharing_end
     $nodes->{ $from->{name} } = $from;
     }
 
-  (values %$nodes);
+  return (ord_values $nodes);
   }
 
 sub incoming
   if (!wantarray)
     {
     my $count = 0;
-    for my $edge (values %{$self->{edges}})
+    for my $edge (ord_values ( $self->{edges} ))
       {
       $count++ if $edge->{to} == $self;
       }
     }
 
   my @edges;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     push @edges, $edge if $edge->{to} == $self;
     }
   if (!wantarray)
     {
     my $count = 0;
-    for my $edge (values %{$self->{edges}})
+    for my $edge (ord_values ( $self->{edges} ))
       {
       $count++ if $edge->{from} == $self;
       }
     }
 
   my @edges;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     push @edges, $edge if $edge->{from} == $self;
     }
   # We need to count the connections, because "[A]->[A]" creates
   # two connections on "A", but only one edge! 
   my $con = 0;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     $con ++ if $edge->{to} == $self;
     $con ++ if $edge->{from} == $self;
   # no graph, no dice
   return unless ref $self->{graph};
 
-  wantarray ? values %{$self->{edges}} : scalar keys %{$self->{edges}};
+  return (wantarray
+      ? ord_values ( $self->{edges} )
+      : scalar keys %{$self->{edges}}
+  );
   }
 
 sub sorted_successors
   {
   # return successors of the node sorted by their chain value
-  # (e.g. successors with more successors first) 
+  # (e.g. successors with more successors first)
   my $self = shift;
 
   my @suc = sort {
   return () unless defined $self->{graph};
 
   my %suc;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     next unless $edge->{from} == $self;
     $suc{$edge->{to}->{id}} = $edge->{to};	# weed out doubles
     }
-  values %suc;
+    return ord_values( \%suc );
   }
 
 sub predecessors
   return () unless defined $self->{graph};
 
   my %pre;
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     next unless $edge->{to} == $self;
     $pre{$edge->{from}->{id}} = $edge->{from};	# weed out doubles
     }
-  values %pre;
+  return ord_values(\%pre);
   }
 
 sub has_predecessors
 
   return undef unless defined $self->{graph};
 
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     return 1 if $edge->{to} == $self;		# found one
     }
 
   return () unless defined $self->{graph};
 
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     return 1 if 
 	$edge->{to} == $self && $edge->{from} == $other;	# found one
 
   return () unless defined $self->{graph};
 
-  for my $edge (values %{$self->{edges}})
+  for my $edge (ord_values ( $self->{edges} ))
     {
     return 1 if
 	$edge->{from} == $self && $edge->{to} == $other;	# found one

Graph-Easy/lib/Graph/Easy/Parser.pm

 use strict;
 use constant NO_MULTIPLES => 1;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _init
   {
   my ($self,$args) = @_;
 
   my $g = $self->{_graph};
   
-  for my $n (values %{$g->{nodes}})
+  for my $n (ord_values ( $g->{nodes} ))
     {
     next if $n->{autosplit};
     $self->warn("Node '" . $self->_quote($n->{name}) . "' has an offset but no origin")

Graph-Easy/lib/Graph/Easy/Parser/Graphviz.pm

 use utf8;
 use constant NO_MULTIPLES => 1;
 
+use Graph::Easy::Util qw(ord_values);
+
 sub _init
   {
   my $self = shift;
       # part of the autosplit node (dot seems to render them arbitrarily
       # on the autosplit node):
 
-      for my $e (values %{$n->{edges}})
+      for my $e (ord_values( $n->{edges} ))
 	{
         $e->start_at($rc[0]) if $e->{from} == $n;
         $e->end_at($rc[0]) if $e->{to} == $n;

Graph-Easy/lib/Graph/Easy/Util.pm

 
 use base 'Exporter';
 
-our @EXPORT_OK = (qw(first_kv));
+our @EXPORT_OK = (qw(first_kv ord_values));
 
 use List::Util qw(minstr);
 
     return ($n, $v);
 }
 
+=head2 ord_values($hash_ref)
+
+The values of the hash ordered by a lexicographical keyname.
+
+=cut
+
+sub ord_values
+{
+    my $href = shift;
+
+    if ((!defined $href) || (! %$href))
+    {
+        return (wantarray ? () : 0);
+    }
+    else
+    {
+        return (wantarray ? @{$href}{sort keys( %$href )} : 0);
+    }
+}
+
 1;
 

Graph-Easy/t/ascii.t

 BEGIN
    {
    plan tests => 451;
-   chdir 't' if -d 't';
-   use lib '../lib';
+   # TEST
    use_ok ("Graph::Easy") or die($@);
+   # TEST
    use_ok ("Graph::Easy::Parser") or die($@);
    };
 
 is (ref($parser), 'Graph::Easy::Parser');
 is ($parser->error(), '', 'no error yet');
 
-opendir DIR, "in" or die ("Cannot read dir 'in': $!");
+opendir DIR, "t/in" or die ("Cannot read dir 'in': $!");
 my @files = readdir(DIR); closedir(DIR);
 
 my @failures;
 
 foreach my $f (sort @files)
   {
-  next unless -f "in/$f";			# only files
-  
+      my $path =  "t/in/$f";
+  next unless -f $path; 			# only files
+
   next unless $f =~ /\.txt/;			# ignore anything else
 
   print "# at $f\n";
-  my $txt = readfile("in/$f");
+  my $txt = readfile($path);
   my $graph = $parser->from_text($txt);		# reuse parser object
 
   $txt =~ s/\n\s+\z/\n/;			# remove trailing whitespace
   $txt =~ s/(^|\n)\s*#[^#]{2}.*\n//g;		# remove comments
- 
+
   $f =~ /^(\d+)/;
   my $nodes = $1;
 
   # for slow testing machines
   $graph->timeout(20);
   my $ascii = $graph->as_ascii();
-  my $out = readfile("out/$f");
+
+  my $out_path = "t/out/$f";
+  my $out = readfile($out_path);
   $out =~ s/(^|\n)\s*#[^#=]{2}.*\n//g;		# remove comments
   $out =~ s/\n\n\z/\n/mg;			# remove empty lines
 
         if ($ENV{__SHLOMIF__UPDATE_ME})
         {
             require IO::All;
-            IO::All->new->file("out/$f")->print($ascii);
+            IO::All->new->file($out_path)->utf8->print($ascii);
         }
     push @failures, $f;
     if (defined $Test::Differences::VERSION)
       }
     }
 
+  my $txt_path = "t/txt/$f";
   # if the txt output differes, read it in
-  if (-f "txt/$f")
+  if (-f $txt_path)
     {
-    $txt = readfile("txt/$f");
+    $txt = readfile($txt_path);
     }
 #  else
 #    {
         if ($ENV{__SHLOMIF__UPDATE_ME})
         {
             require IO::All;
-            IO::All->new->file("txt/$f")->print($graph->as_txt());
+            IO::All->new->file($txt_path)->utf8->print($graph->as_txt());
         }
     push @failures, $f;
     if (defined $Test::Differences::VERSION)
 
 sub readfile
   {
-  my ($file) = @_;
+  my ($filename) = @_;
 
-  open my $FILE, $file or die ("Cannot read file $file: $!");
-  binmode ($FILE, ':utf8') or die ("Cannot do binmode(':utf8') on $FILE: $!");
+  open my $fh, $filename or die ("Cannot read file ${filename}: $!");
+  binmode ($fh, ':utf8') or die ("Cannot do binmode(':utf8') on ${fh}: $!");
   local $/ = undef;				# slurp mode
-  my $doc = <$FILE>;
-  close $FILE;
+  my $doc = <$fh>;
+  close $fh;
 
   $doc;
   }

Graph-Easy/t/out/2_selfloop_flow_down.txt

-                     +-------+
-                     | Start |
-                     +-------+
-                       |
-                       |
-                       v
-    Until not done   +-------+   Until done
-  +----------------- |       | -------------+
-  |                  | Main  |              |
-  +----------------> |       | <------------+
-                     +-------+
+                 +-------+
+                 | Start |
+                 +-------+
+                   |
+                   |
+                   v
+    Until done   +-------+   Until not done
+  +------------- |       | -----------------+
+  |              | Main  |                  |
+  +------------> |       | <----------------+
+                 +-------+

Graph-Easy/t/out/3_edge_start.txt

 +---+     +---+
-|   | --> | C |
+|   | --> | B |
 | A |     +---+
 |   |     +---+
-|   | --> | B |
+|   | --> | C |
 +---+     +---+

Graph-Easy/t/out/3_nodes_5_edges.txt

        #= = = = = = = = = = = = = = = = = = #
        "                                    v
      +--------------+     +---------+     +-----------+
-  +- | Default Page | --> | Sign Up | --> |           |
-  !  +--------------+     +---------+     |           |
-  !    '                                  |           |
-  !    +- - - - - - - - - - - - - - - - > | Main Page |
-  !                                       |           |
-  !                                       |           |
-  +-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-> |           |
+  +  | Default Page | --> | Sign Up | --> |           |
+  '  +--------------+     +---------+     |           |
+  '    !                                  |           |
+  '    +.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-> | Main Page |
+  '                                       |           |
+  '                                       |           |
+  + - - - - - - - - - - - - - - - - - - > |           |
                                           +-----------+

Graph-Easy/t/out/4_bug_basename.txt

 
-       +---------+
-       |         v
-     +---+----+----+----+
-  +- | u |  a |  b |  c |
-  |  +---+----+----+----+
-  |    |    ^         ^
-  |    +----+         |
-  |                   |
-  |                   |
+  +--------------+
+  |              v
++---+     +---+----+----+
+| u | --> | a |  b |  c |
++---+     +---+----+----+
+  |                   ^
   +-------------------+

Graph-Easy/t/out/4_bug_joint_2.txt

        |   +---+
        +-- | C |
        |   +---+
+       |
+       |
+       |
        |   +---+
        +-- | D |
            +---+

Graph-Easy/t/out/4_edge_cross.txt

 
-  +---------------------+
-  |                     v
-+----+     +----+     +----+
-| E1 | --> | S1 | --> | S2 |
-+----+     +----+     +----+
-  |          ^          ^
-  |          |          |
-  v          |          |
-+----+       |          |
-| E2 | ------+----------+
-+----+       |
-  |          |
-  +----------+
+             +---------------------+
+             |                     |
+             |                     |
+  +----------+----------+          |
+  |          |          v          v
++----+     +----+     +----+     +----+
+| E1 | --> | E2 | --> | S1 | --> | S2 |
++----+     +----+     +----+     +----+
+  |                                ^
+  +--------------------------------+

Graph-Easy/t/out/4_minlen.txt

 +---------+          +---------+          +---------+
-| Potsdam | <------- | Berlin  | -------> | Leipzig |
+| Potsdam | <------- | Berlin  | -------> | Cottbus |
 +---------+          +---------+          +---------+
                        |
                        |
                        |
                        v
                      +---------+
-                     | Cottbus |
+                     | Leipzig |
                      +---------+

Graph-Easy/t/out/5_arrow_styles.txt

-+----------+      +--------------+
-| Uhlingen | ---- |    Hagnau    |
-+----------+      +--------------+
-  ^                 |
-  |                 |
-  |                 |                     +---------------------+
-  |                 |                     |                     |
-  |               +--------------+      .................     +--------+
-  +-------------> | Oberuhlingen | <--> : Unteruhlingen : --- | Mainau |
-                  +--------------+      :...............:     +--------+
+     +--------------+
+     |    Hagnau    | -------+
+     +--------------+        |
+       |                     |
+       |                     |
+       |                     |
+       |                     |
+     +--------------+      +---------------+
+  +> | Oberuhlingen | <--> |   Uhlingen    |
+  |  +--------------+      +---------------+
+  |
+  |    +---------------------+
+  |    |                     |
+  |  +--------------+      .................
+  |  |    Mainau    | ---- : Unteruhlingen :
+  |  +--------------+      :...............:
+  |                          ^
+  +--------------------------+

Graph-Easy/t/out/7_star.txt

                |
                |
 +------+     +-----------+     +----------+
-| Kiel | <-- |           | --> | Chemnitz |
+| Kiel | <-- |           | --> |  Berlin  |
 +------+     |  Dachau   |     +----------+
 +------+     |           |     +----------+
-| Ulm  | <-- |           | --> |  Berlin  |
+| Ulm  | <-- |           | --> | Chemnitz |
 +------+     +-----------+     +----------+
                |
                |

Graph-Easy/t/out/8_invisible.txt

 
-                            My sample graph
+                              My sample graph
 
-                    +-------+          +-------+  Test          +------+
-                    |  Two  |          |  One  |  label         | Four |
-                    |       |          |       | .............> |      |
-                    +-------+          +-------+                +------+
-                                         H
-                                         H
-                                         v
-                    +-------+          +-------+  Test label    +------+
-       +- - - - - - | Five  | <======> | Three | <............> | Six  |
-       '            +-------+          +-------+                +------+
-       '
-       ' Test label
-       '
-       '            +-------+          +-------+
-       +- - - - - > | Seven |          | Eight |
-                    +-------+          +-------+
+
+
+
+                 +--------+  Test label   +-------+                +-------+
+         #=====> |  Five  | - - - - - - > | Seven |                | Eight |
+         H       +--------+               +-------+                +-------+
+         H
+         H
+         H
+         H
+         #==================================#
+                                            v
++-----+          +--------+               +-------+  Test label    +-------+
+| Two |          |  One   | ============> | Three | <............> |  Six  |
++-----+          +--------+               +-------+                +-------+
+                   :
+                   : Test
+                   : label
+                   v
+                 +--------+
+                 |  Four  |
+                 +--------+

Graph-Easy/t/out/8_labels.txt

 
                               My sample graph
 
-                            Test label
-                   +- - - - - - - - - - - - +
-                   '                        v
-                 +--------+               +-------+                +-------+
-         #=====> |  Five  | ------------> | Seven | -------------- | Eight |
+
+                   +------------------------+
+                   |                        v
+                 +--------+  Test label   +-------+                +-------+
+         #=====> |  Five  | - - - - - - > | Seven | -------------- | Eight |
          H       +--------+               +-------+                +-------+
          H         |                                                 ^
          H         +-------------------------------------------------+

Graph-Easy/t/out/9_flow_south.txt

   |     |     |     |     |     |     |     |
   v     v     v     v     v     v     v     v
 +----++----++----++----++----++----++----++----+
-| A3 || A1 || A2 || A4 || A5 || A6 || A7 || A8 |
+| A1 || A2 || A3 || A4 || A5 || A6 || A7 || A8 |
 +----++----++----++----++----++----++----++----+

Graph-Easy/t/txt/10_repair.txt

   [ Server ]
 )
 
+[ 1Check ] --> [ 1Backend ]
+[ 1Check ] --> [ 1Backend ]
+[ 1Database ] --> [ 1Backend ]
+[ 1Database ] --> [ 1Backend ]
 [ 1Proxy ] --> [ 1Check ]
 [ 1Proxy ] --> [ 1Check ]
 [ 1Proxy ] --> { flow: south; } [ 1Database ]
 [ 1Proxy ] --> { flow: south; } [ 1Database ]
 [ 1Proxy ] --> [ 1Server ]
 [ 1Proxy ] --> [ 1Server ]
+[ Check ] --> [ Backend ]
+[ Database ] --> [ Backend ]
 [ Proxy ] --> [ Check ]
 [ Proxy ] --> { flow: south; } [ Database ]
 [ Proxy ] --> [ Server ]
-[ 1Check ] --> [ 1Backend ]
-[ 1Check ] --> [ 1Backend ]
-[ 1Database ] --> [ 1Backend ]
-[ 1Database ] --> [ 1Backend ]
-[ Check ] --> [ Backend ]
-[ Database ] --> [ Backend ]

Graph-Easy/t/txt/18_multiples.txt

 node { background: yellow; }
 
-[ 1Bonn ] --> [ 1Ulm ]
+[ 1Bautzen ] --> [ 1Berlin ]
+[ 1Berlin ] --> [ 1Kassel ]
 [ 1Bonn ] ..> [ 1Berlin ]
 [ 1Bonn ] .-> [ 1Koblenz ]
-[ 2Bonn ] --> [ 2Ulm ]
+[ 1Bonn ] --> [ 1Ulm ]
+[ 1Kassel ]
+[ 1Koblenz ]
+[ 1Ulm ] --> [ 1Bautzen ]
+[ 1Ulm ] --> [ 1Koblenz ]
+[ 2Bautzen ] --> [ 2Berlin ]
+[ 2Berlin ] --> [ 2Kassel ]
 [ 2Bonn ] ..> [ 2Berlin ]
 [ 2Bonn ] .-> [ 2Koblenz ]
-[ Bonn ] --> [ Ulm ]
+[ 2Bonn ] --> [ 2Ulm ]
+[ 2Kassel ]
+[ 2Koblenz ]
+[ 2Ulm ] --> [ 2Bautzen ]
+[ 2Ulm ] --> [ 2Koblenz ]
+[ Bautzen ] --> [ Berlin ]
+[ Berlin ] --> [ Kassel ]
 [ Bonn ] ..> [ Berlin ]
 [ Bonn ] .-> [ Koblenz ]
-[ 1Berlin ] --> [ 1Kassel ]
-[ 1Ulm ] --> [ 1Bautzen ]
-[ 1Ulm ] --> [ 1Koblenz ]
-[ 2Berlin ] --> [ 2Kassel ]
-[ 2Ulm ] --> [ 2Bautzen ]
-[ 2Ulm ] --> [ 2Koblenz ]
-[ Berlin ] --> [ Kassel ]
+[ Bonn ] --> [ Ulm ]
+[ Kassel ]
+[ Koblenz ]
 [ Ulm ] --> [ Bautzen ]
 [ Ulm ] --> [ Koblenz ]
-[ 1Bautzen ] --> [ 1Berlin ]
-[ 2Bautzen ] --> [ 2Berlin ]
-[ Bautzen ] --> [ Berlin ]

Graph-Easy/t/txt/25_autosplit_empty.txt

 [ | F | ] { class: empty; }
 [ |G| ] { class: empty; }
 
+[ A1 ]
+[ A2 ]
+[ A3 ]
+[ A4 ]
+[ A5 ]
+[ A6 ]
 [ | C | ]
 [ C.2 ] --> [ A1 ]
 [ D.2 ] --> [ A2 ]

Graph-Easy/t/txt/2_autolabel.txt

 [ Bonn ] { label: Bonn (ehemalige Bundeshauptstadt); }
 
 [ Bonn ] -- Acme Travels Incorporated --> [ Frankfurt (Main) / Flughafen ]
+[ Frankfurt (Main) / Flughafen ]

Graph-Easy/t/txt/2_dot.txt

 graph { label: // digraph G {; }
 
+[ Düsburg ]
 [ Kummersbach ] --> [ Düsburg ]

Graph-Easy/t/txt/2_graph_label.txt

   labelpos: top;
 }
 
+[ Passau ]
 [ Regensburg ] --> [ Passau ]

Graph-Easy/t/txt/2_invisible_left.txt

 [ ] --> [ Berlin ]
+[ Berlin ]

Graph-Easy/t/txt/2_invisible_right.txt

+[ ]
 [ Bonn ] --> [ ]

Graph-Easy/t/txt/3_cache_bug.txt

   [ A ]
 )
 
+[ ]
 [ A ] -- C --> { end: north; start: east; } [ B ]
 [ B ] --> [ ]

Graph-Easy/t/txt/3_edge_labels_from_class.txt

 
 [ A ] --> { class: yes; } [ B ]
 [ B ] --> [ C ]
+[ C ]

Graph-Easy/t/txt/3_edge_repair.txt

 )
 
 [ Input ] --> [ Output ]
+[ Network ]
 [ Output ] ==> { end: north; start: south; } [ Network ]

Graph-Easy/t/txt/3_edge_start.txt

 [ A ] --> { start: front; } [ B ]
 [ A ] --> { start: front; } [ C ]
+[ B ]
+[ C ]

Graph-Easy/t/txt/3_group_align_center.txt

   [ Right\nAligned ]
 ) { align: center; border: none; }
 
+[ Left\naligned ] --> [ Center\n aligned ]
 [ Right\nAligned ] -- label\n text --> { align: right; } [ Left\naligned ]
-[ Left\naligned ] --> [ Center\n aligned ]

Graph-Easy/t/txt/3_invisible_both.txt

 [ ] --> [ Bonn ]
+[ ]
 [ Bonn ] --> [ ]

Graph-Easy/t/txt/3_joint.txt

 [ Mannheim ] --> { end: back,0; } [ Weimar ]
 [ Potsdam ] --> { end: back,0; } [ Weimar ]
+[ Weimar ]

Graph-Easy/t/txt/3_lists.txt

+[ Berlin ]
 [ Bonn ] --> [ Berlin ]
 [ Ulm ] --> [ Berlin ]

Graph-Easy/t/txt/3_nodes_5_edges.txt

-[ Default Page ] --> [ Sign Up ]
 [ Default Page ] .-> [ Main Page ]
 [ Default Page ] = > [ Main Page ]
 [ Default Page ] - > [ Main Page ]
+[ Default Page ] --> [ Sign Up ]
+[ Main Page ]
 [ Sign Up ] --> [ Main Page ]

Graph-Easy/t/txt/3_selfloop.txt

+[ End ]
+[ Main ] --> [ End ]
+[ Main ] -- Until not done --> [ Main ]
 [ Start ] --> [ Main ]
-[ Main ] -- Until not done --> [ Main ]
-[ Main ] --> [ End ]

Graph-Easy/t/txt/3_selfloop_flip.txt

 [ Adenau ] --> [ Monschau ]
+[ Monschau ] --> [ Monschau ]
 [ Nideggen ] --> [ Monschau ]
-[ Monschau ] --> [ Monschau ]

Graph-Easy/t/txt/4_2x2nodes.txt

+[ Altona ]
 [ Frankfurt ] --> [ Hof ]
 [ Hamburg ] --> [ Altona ]
+[ Hof ]

Graph-Easy/t/txt/4_att.txt

 [ 3 ] { label: $a = %22%3b%25; }
 
 [ 1 ] --> [ 2 ]
+[ 2 ]
 [ 3 ] --> [ 4 ]
+[ 4 ]

Graph-Easy/t/txt/4_autosplit_offset.txt

 
 [ 1 ] --> [ 23.1 ]
 [ 23.1 ] --> [ 3 ]
+[ 3 ]

Graph-Easy/t/txt/4_collapse.txt

 
 [ A ] -- S P A C E --> [ B ]
 [ A B C ] -- A B C D --> [ X Z Y ]
+[ X Z Y ]

Graph-Easy/t/txt/4_comma.txt

 [ 1 ] --> [ 4 ]
 [ 2 ] --> [ 4 ]
 [ 3 ] --> [ 4 ]
+[ 4 ]

Graph-Easy/t/txt/4_edge_cross.txt

 [ E2 ] --> [ S1 ]
 [ E2 ] --> [ S2 ]
 [ S1 ] --> [ S2 ]
+[ S2 ]

Graph-Easy/t/txt/4_edge_labels.txt

+[ Berlin ] .. bus ..> [ Potsdam ]
+[ Berlin ] .- bike .-> [ Ulm ]
 [ Bonn ] == train ==> [ Berlin ]
 [ Bonn ] -- car --> [ Potsdam ]
-[ Berlin ] .. bus ..> [ Potsdam ]
-[ Berlin ] .- bike .-> [ Ulm ]
+[ Potsdam ]
+[ Ulm ]

Graph-Easy/t/txt/4_endless_loop_2.txt

 [ 4 ] { offset: 1,0; origin: 3; }
 
 [ 1 ] --> [ 2 ]
+[ 2 ]
 [ 3 ]

Graph-Easy/t/txt/4_flow.txt

+[ Aschaffenburg ]
 [ Passau ] --> { flow: down; } [ Siegen ]
+[ Regensburg ]
 [ Siegen ] --> { flow: left; } [ Aschaffenburg ]
 [ Siegen ] --> { flow: right; } [ Regensburg ]

Graph-Easy/t/txt/4_flow_chain.txt

+[ Berlin ]
+[ Bonn ] --> { flow: forward; } [ Berlin ]
 [ Bonn ] --> [ Cottbus ]
-[ Bonn ] --> { flow: forward; } [ Berlin ]
 [ Cottbus ] --> [ Ulm ]
+[ Ulm ]

Graph-Easy/t/txt/4_invisible.txt

 [ You don't see me! ] { shape: invisible; }
 
 [ Bischofswerda ] --> [ You don't see me! ]
+[ Borna ] --> [ Bremen ]
+[ Bremen ]
 [ You don't see me! ] --> [ Bischofswerda ]
 [ You don't see me! ] --> [ Borna ]
-[ Borna ] --> [ Bremen ]

Graph-Easy/t/txt/4_joint.txt

 [ A ] --> { end: south, 0; start: south; } [ C ]
 [ B ] --> { end: south, 0; start: south; } [ C ]
 [ B ] --> [ U ]
+[ U ]

Graph-Easy/t/txt/4_lists.txt

+[ Berlin ]
 [ Bonn ] --> [ Berlin ]
 [ Bonn ] --> [ Frankfurt ]
+[ Frankfurt ]
 [ Ulm ] --> [ Berlin ]
 [ Ulm ] --> [ Frankfurt ]

Graph-Easy/t/txt/4_minlen.txt

 [ Berlin ] --> { minlen: 4; } [ Cottbus ]
 [ Berlin ] --> { minlen: 4; } [ Leipzig ]
 [ Berlin ] --> { minlen: 4; } [ Potsdam ]
+[ Cottbus ]
+[ Leipzig ]
+[ Potsdam ]