$" = ''; $\ = "\n"; chomp( my @edges = map /--/ ? join( '', sort /\w/g ) : (), ); my @nodes = sort { $a cmp $b } uniq( map /\w/g, @edges ); for $node ( @nodes ) { my $edge_rx = qr/$node/; my @other_nodes = map /$edge_rx/ ? grep( $_ ne $node, # ne vs gt split( //, $_ ) ) : (), @edges; use re 'eval'; my $third_edge = qr/([@other_nodes]) .* ([@other_nodes]) (??{push @TRI, join(" - ",sort($node,$1,$2))})/x; # removed the gt. Now nodes are unordered in the triangle. All triangles are visited three times, not just once. grep ! /$node/ && /$third_edge/, @edges; } print for uniq( sort @TRI ); sub uniq { my %seen; grep !$seen{$_}++, @_ } __DATA__ graph triangles { A -- B A -- C -- F -- I A -- D -- G -- J A -- E B -- C -- D -- E B -- F -- H -- J B -- I E -- G -- H -- I E -- J I -- J }