Repository 'motif_tools'
hg clone https://toolshed.g2.bx.psu.edu/repos/pjbriggs/motif_tools

Changeset 2:2f48cf393d25 (2018-04-09)
Previous changeset 1:764d562755bd (2018-03-21) Next changeset 3:856008c4a5f3 (2018-10-05)
Commit message:
Add Perl scripts missing from previous upload.
added:
CountUniqueIDs.pl
Scan_IUPAC_output_each_match.pl
Scan_IUPAC_output_matches_per_seq.pl
TFBScluster_candidates.pl
b
diff -r 764d562755bd -r 2f48cf393d25 CountUniqueIDs.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CountUniqueIDs.pl Mon Apr 09 04:56:28 2018 -0400
[
@@ -0,0 +1,42 @@
+#! /usr/bin/perl -w
+
+use strict;
+
+#### Read thru a GFF file of motifs return the number of unique ids
+#### Ian Donaldson Sept 2008
+
+#### Usage
+unless(@ARGV == 2) {
+   die("USAGE: $0 | GFF file | Output file\n\n");
+}
+
+#### Ready output file
+open(GFF, "<$ARGV[0]") or die("Could not open GFF file!!\n\n");
+open(OUTPUT, ">$ARGV[1]") or die("Could not open output file!!\n\n");
+
+#### Hash to hold ids
+my %id_hash = ();
+
+#### Work thru GFF file
+while(defined(my $gff_line = <GFF>)) {
+   if($gff_line =~ /(^#|^\s)/) { next }
+
+   my @gff_line_bits = split(/\t/, $gff_line);
+
+   my $id = $gff_line_bits[0];
+
+   $id_hash{$id}=1;
+}
+   
+my @all_keys = sort(keys(%id_hash));
+
+my $elements = scalar(@all_keys);
+
+#print OUTPUT "There are $elements unique sequences in the file\n";
+print OUTPUT "$elements non-redundant sequences\n";
+
+#### Close files
+close(GFF);
+close(OUTPUT);
+
+exit;
b
diff -r 764d562755bd -r 2f48cf393d25 Scan_IUPAC_output_each_match.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Scan_IUPAC_output_each_match.pl Mon Apr 09 04:56:28 2018 -0400
[
@@ -0,0 +1,210 @@
+#! /usr/bin/perl
+
+use strict;
+use FileHandle;
+use Bio::SeqIO;
+#use Statistics::Descriptive;
+
+#####
+# Program to count all occurences of a particular REGEX 
+# in a file containing mutiple FASTA sequences.
+# 11 September 2003. Ian Donaldson.
+# Revised to convert IUPAC to regex
+# Revised to read a multiple FASTA file
+# was CountRegexGFF_IUPAC_1input_nosummary.pl
+#####
+
+#### File handles
+my $input = new FileHandle;
+my $output = new FileHandle;
+
+#### Variables
+my $file_number = 0;
+my $count_fwd_regex = 0;
+my $count_rvs_regex = 0;
+my $count_all_regex = 0;
+my $seq_tally = 0;
+my @seq_totals = ();
+
+#### Command line usage
+if(@ARGV != 5) {
+   die ("USAGE: 
+   $0
+   IUPAC
+   Multiple FASTA input file
+   Output
+   Label
+   Skip palindromic (0=F+R-default|1=F only)\n\n");
+} 
+
+#### Search forward strand only?
+my $skip = $ARGV[4];
+unless($skip =~ /^[01]$/) {
+   die("Only accept 0 or 1 for Skip!\n");
+}
+
+#### Process IUPAC string
+my $iupac = $ARGV[0];
+chomp $iupac;
+$iupac = uc($iupac);
+
+if($iupac !~ /^[ACGTRYMKWSBDHVN]+$/) {
+   die("A non-IUPAC character was detected in your input string!\n");
+}
+
+#### Forward strand IUPAC
+my @fwd_iupac_letters = split(//, $iupac); 
+my @fwd_regex_list = ();
+
+foreach my $letter (@fwd_iupac_letters) {
+   my $converted_iupac = iupac2regex($letter);
+   push(@fwd_regex_list, $converted_iupac);
+}
+
+my $fwd_regex = join('', @fwd_regex_list);
+
+
+#### Reverse strand IUPAC
+my $revcomp_iupac = RevCompIUPAC($iupac);
+my @rev_iupac_letters = split(//, $revcomp_iupac); 
+my @rev_regex_list = ();
+
+foreach my $letter (@rev_iupac_letters) {
+   my $converted_iupac = iupac2regex($letter);
+   push(@rev_regex_list, $converted_iupac);
+}
+
+my $rvs_regex = join('', @rev_regex_list);
+
+#### Other variables
+my $label = $ARGV[3]; 
+
+if($label !~ /^[\w\d]+$/) {
+   die("A non-letter/number character was detected in your label string!\n");
+}
+
+my $length = length($iupac);
+
+#### Open output file
+$output->open(">$ARGV[2]") or die "Could not open output file $ARGV[2]!\n";
+#$output->print("##gff-version 2\n");
+
+#if($skip == 0) {
+#   $output->print("##Pattern search: $iupac and $revcomp_iupac\n");
+#}
+
+#else {
+#   $output->print("##Pattern search: $iupac\n");
+#}
+
+#### Work thru FASTA entries in the input file with SeqIO
+my $seqio = Bio::SeqIO->new(-file => "$ARGV[1]" , '-format' => 'Fasta');
+
+while( my $seqobj = $seqio->next_seq() ) {
+   $seq_tally++;
+   my $this_seq_tally = 0;
+   my $sequence = $seqobj->seq(); # actual sequence as a string
+   my $seq_id = $seqobj->id(); # header
+   #print(">$seq_id\n$seq\n\n");
+
+   #$output->print(">$seq_id\n");
+
+   #### Clean up $sequence to leave only nucleotides
+   $sequence =~ s/[\s\W\d]//g;
+
+   while ($sequence =~ /($fwd_regex)/ig) {
+      $this_seq_tally++;
+      $count_fwd_regex++; 
+      $count_all_regex++;
+      
+      my $end_position = pos($sequence);
+      my $start_position = $end_position - ($length - 1);
+      $output->print("$seq_id\tRegexSearch\tCNS\t$start_position\t$end_position\t.\t+\t.\t$label\n"); 
+   }
+
+   #### Count reverse REGEX
+   unless($skip == 1) {
+      while ($sequence =~ /($rvs_regex)/ig) {
+         $this_seq_tally++;
+         $count_rvs_regex++;
+         $count_all_regex++;
+
+         my $end_position = pos($sequence);
+         my $start_position = $end_position - ($length - 1);
+         $output->print("$seq_id\tRegexSearch\tCNS\t$start_position\t$end_position\t.\t-\t.\t$label\n"); 
+      }
+
+   push(@seq_totals, $this_seq_tally);
+   #$output->print("$this_seq_tally matches\n");
+   }
+}
+
+#### Mean motifs per seq
+#my $stat = Statistics::Descriptive::Full->new();
+#$stat->add_data(@seq_totals); 
+#my $mean = $stat->mean();
+
+
+#### Print a summary file
+if($skip == 0) {
+#   $output->print("##Forward: $fwd_regex. Reverse: $rvs_regex.\n",
+#                  "##$count_fwd_regex on the forward strand.\n",
+#                  "##$count_rvs_regex on the reverse strand.\n",
+#                  "##$count_all_regex in total.\n",
+#                  "##$seq_tally sequences.  Mean motifs per seq = $mean\n");
+#
+   print STDOUT "There were $count_all_regex instances of $fwd_regex and $rvs_regex.\n"; 
+}
+
+if($skip == 1) {
+#   $output->print("##Forward: $fwd_regex.\n",
+#                  "##$count_fwd_regex on the forward strand.\n",
+#                  "##$seq_tally sequences.  Mean motifs per seq = $mean\n");
+#
+   print STDOUT "There were $count_fwd_regex instances of $fwd_regex on the forward strand.\n"; 
+}
+
+$output->close;
+
+exit;
+
+sub iupac2regex {
+# Convert IUPAC codes to REGEX
+   my $iupac = shift;
+   
+   #### Series of regexes to convert 
+   if($iupac =~ /A/) { return 'A' }
+   if($iupac =~ /C/) { return 'C' }
+   if($iupac =~ /G/) { return 'G' }
+   if($iupac =~ /T/) { return 'T' }
+   if($iupac =~ /M/) { return '[AC]' }
+   if($iupac =~ /R/) { return '[AG]' }
+   if($iupac =~ /W/) { return '[AT]' }
+   if($iupac =~ /S/) { return '[CG]' }
+   if($iupac =~ /Y/) { return '[CT]' }
+   if($iupac =~ /K/) { return '[GT]' }
+   if($iupac =~ /V/) { return '[ACG]' }
+   if($iupac =~ /H/) { return '[ACT]' }
+   if($iupac =~ /D/) { return '[AGT]' }
+   if($iupac =~ /B/) { return '[CGT]' }
+   if($iupac =~ /N/) { return '[ACGT]' }
+
+   die("IUPAC not recognised by sub iupac2regex!\n");
+}
+
+sub RevCompIUPAC {
+   my $iupac_string = shift;
+   my @converted_list = ();
+
+   my @iupac_string_list = split(//, $iupac_string); 
+
+   @iupac_string_list = reverse(@iupac_string_list);
+
+   foreach my $letter (@iupac_string_list) {
+      $letter =~ tr/ACGTRYMKWSBDHVN/TGCAYRKMWSVHDBN/;
+      push(@converted_list, $letter);
+   }
+
+   my $joined_list = join('', @converted_list);
+   return $joined_list;
+}
b
diff -r 764d562755bd -r 2f48cf393d25 Scan_IUPAC_output_matches_per_seq.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Scan_IUPAC_output_matches_per_seq.pl Mon Apr 09 04:56:28 2018 -0400
[
@@ -0,0 +1,209 @@
+#! /usr/bin/perl
+
+use strict;
+use FileHandle;
+use Bio::SeqIO;
+#use Statistics::Descriptive;
+
+#####
+# Program to count all occurences of a particular REGEX 
+# in a file containing mutiple FASTA sequences.
+# 11 September 2003. Ian Donaldson.
+# Revised to convert IUPAC to regex
+# Revised to read a multiple FASTA file
+# was CountRegexGFF_IUPAC_1input_simple_output.pl
+#####
+
+#### File handles
+my $input = new FileHandle;
+my $output = new FileHandle;
+
+#### Variables
+my $file_number = 0;
+my $count_fwd_regex = 0;
+my $count_rvs_regex = 0;
+my $count_all_regex = 0;
+my $seq_tally = 0;
+my @seq_totals = ();
+
+#### Command line usage
+if(@ARGV != 4) {
+   die ("USAGE: 
+   $0
+   IUPAC
+   Multiple FASTA input file
+   Output
+   Skip palindromic (0=F+R-default|1=F only)\n\n");
+} 
+
+#### Search forward strand only?
+my $skip = $ARGV[3];
+unless($skip =~ /^[01]$/) {
+   die("Only accept 0 or 1 for Skip!\n");
+}
+
+#### Process IUPAC string
+my $iupac = $ARGV[0];
+chomp $iupac;
+$iupac = uc($iupac);
+
+if($iupac !~ /^[ACGTRYMKWSBDHVN]+$/) {
+   die("A non-IUPAC character was detected in your input string!\n");
+}
+
+#### Forward strand IUPAC
+my @fwd_iupac_letters = split(//, $iupac); 
+my @fwd_regex_list = ();
+
+foreach my $letter (@fwd_iupac_letters) {
+   my $converted_iupac = iupac2regex($letter);
+   push(@fwd_regex_list, $converted_iupac);
+}
+
+my $fwd_regex = join('', @fwd_regex_list);
+
+
+#### Reverse strand IUPAC
+my $revcomp_iupac = RevCompIUPAC($iupac);
+my @rev_iupac_letters = split(//, $revcomp_iupac); 
+my @rev_regex_list = ();
+
+foreach my $letter (@rev_iupac_letters) {
+   my $converted_iupac = iupac2regex($letter);
+   push(@rev_regex_list, $converted_iupac);
+}
+
+my $rvs_regex = join('', @rev_regex_list);
+
+#### Other variables
+#my $label = $ARGV[3]; 
+#
+#if($label !~ /^[\w\d]+$/) {
+#   die("A non-letter/number character was detected in your label string!\n");
+#}
+
+my $length = length($iupac);
+
+#### Open output file
+$output->open(">$ARGV[2]") or die "Could not open output file $ARGV[2]!\n";
+#$output->print("##gff-version 2\n");
+
+#if($skip == 0) {
+#   $output->print("##Pattern search: $iupac and $revcomp_iupac\n");
+#}
+
+#else {
+#   $output->print("##Pattern search: $iupac\n");
+#}
+
+#### Work thru FASTA entries in the input file with SeqIO
+my $seqio = Bio::SeqIO->new(-file => "$ARGV[1]" , '-format' => 'Fasta');
+
+while( my $seqobj = $seqio->next_seq() ) {
+   $seq_tally++;
+   my $this_seq_tally = 0;
+   my $sequence = $seqobj->seq(); # actual sequence as a string
+   my $seq_id = $seqobj->id(); # header
+   #print(">$seq_id\n$seq\n\n");
+
+   #$output->print(">$seq_id\n");
+
+   #### Clean up $sequence to leave only nucleotides
+   #$sequence =~ s/[\s\W\d]//g;
+
+   while ($sequence =~ /($fwd_regex)/ig) {
+      $this_seq_tally++;
+      $count_fwd_regex++; 
+      $count_all_regex++;
+      
+      #my $end_position = pos($sequence);
+      #my $start_position = $end_position - ($length - 1);
+      #$output->print("$seq_id\tRegexSearch\tCNS\t$start_position\t$end_position\t.\t+\t.\t$label\n"); 
+   }
+
+   #### Count reverse REGEX
+   unless($skip == 1) {
+      while ($sequence =~ /($rvs_regex)/ig) {
+         $this_seq_tally++;
+         $count_rvs_regex++;
+         $count_all_regex++;
+
+         #my $end_position = pos($sequence);
+         #my $start_position = $end_position - ($length - 1);
+         #$output->print("$seq_id\tRegexSearch\tCNS\t$start_position\t$end_position\t.\t-\t.\t$label\n"); 
+      }
+   }
+
+   push(@seq_totals, $this_seq_tally);
+   $output->print("$seq_id\t$this_seq_tally\n");
+}
+
+#### Mean motifs per seq
+#my $stat = Statistics::Descriptive::Full->new();
+#$stat->add_data(@seq_totals); 
+#my $mean = $stat->mean();
+
+
+#### Print a summary file
+#if($skip == 0) {
+#   $output->print("##Forward: $fwd_regex. Reverse: $rvs_regex.\n",
+#                  "##$count_fwd_regex on the forward strand.\n",
+#                  "##$count_rvs_regex on the reverse strand.\n",
+#                  "##$count_all_regex in total.\n",
+#                  "##$seq_tally sequences.  Mean motifs per seq = $mean\n");
+#
+#   print STDOUT "There were $count_all_regex instances of $fwd_regex and $rvs_regex.\n\n"; 
+#}
+
+#if($skip == 1) {
+#   $output->print("##Forward: $fwd_regex.\n",
+#                  "##$count_fwd_regex on the forward strand.\n",
+#                  "##$seq_tally sequences.  Mean motifs per seq = $mean\n");
+#
+#   print STDOUT "There were $count_fwd_regex instances of $fwd_regex on the forward strand.\n\n"; 
+#}
+
+$output->close;
+
+exit;
+
+sub iupac2regex {
+# Convert IUPAC codes to REGEX
+   my $iupac = shift;
+   
+   #### Series of regexes to convert 
+   if($iupac =~ /A/) { return 'A' }
+   if($iupac =~ /C/) { return 'C' }
+   if($iupac =~ /G/) { return 'G' }
+   if($iupac =~ /T/) { return 'T' }
+   if($iupac =~ /M/) { return '[AC]' }
+   if($iupac =~ /R/) { return '[AG]' }
+   if($iupac =~ /W/) { return '[AT]' }
+   if($iupac =~ /S/) { return '[CG]' }
+   if($iupac =~ /Y/) { return '[CT]' }
+   if($iupac =~ /K/) { return '[GT]' }
+   if($iupac =~ /V/) { return '[ACG]' }
+   if($iupac =~ /H/) { return '[ACT]' }
+   if($iupac =~ /D/) { return '[AGT]' }
+   if($iupac =~ /B/) { return '[CGT]' }
+   if($iupac =~ /N/) { return '[ACGT]' }
+
+   die("IUPAC not recognised by sub iupac2regex!\n");
+}
+
+sub RevCompIUPAC {
+   my $iupac_string = shift;
+   my @converted_list = ();
+
+   my @iupac_string_list = split(//, $iupac_string); 
+
+   @iupac_string_list = reverse(@iupac_string_list);
+
+   foreach my $letter (@iupac_string_list) {
+      $letter =~ tr/ACGTRYMKWSBDHVN/TGCAYRKMWSVHDBN/;
+      push(@converted_list, $letter);
+   }
+
+   my $joined_list = join('', @converted_list);
+   return $joined_list;
+}
b
diff -r 764d562755bd -r 2f48cf393d25 TFBScluster_candidates.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/TFBScluster_candidates.pl Mon Apr 09 04:56:28 2018 -0400
[
b'@@ -0,0 +1,964 @@\n+#!/usr/bin/perl\n+\n+# TFBScluster version 2.0 - cluster together TFBSs from distinct \n+#                           TFBSsearch generated libraries.\n+#\n+# (c) Ian Donaldson 2003 and Mike Chapman (TFBS overlap subs)\n+#\n+# This program is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU General Public License\n+# as published by the Free Software Foundation; either version 2\n+# of the License, or (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with this program; if not, write to the Free Software\n+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n+\n+use strict;\n+use FileHandle;\n+#use integer; # Force floating point into integers\n+\n+$|=1;\n+\n+###########\n+# Program to determine whether a TF site is in close proximity to\n+# one or more other TF sites.\n+# September 2003.  Ian Donaldson.\n+# Revised 8 Sept. 2003 to not include the query TF site in the threshold.\n+# This is to allow one to determine whether a TF is near to another of the \n+# same type.\n+# Revised 9 Sept to alter the threshold size to only include the core of a \n+# pattern i.e. gata of nngatann.\n+# Revised 19 Sept to replace query and subject libraries with the statement\n+# of all interested libraries.\n+# Revised 22 Sept to scan output for overlapping patterns.\n+# NOTE: Any overlap in comparative record start with start pattern\n+# Revised 30 Sept to ignore duplication of TFBS in a group record caused by \n+# palindrome.  By skipping if name and positions the same.\n+# Revised 6 Oct code for tree searching method to deal with overlapping TFBSs\n+###########\n+\n+#### Command line usage\n+if(@ARGV != 7)\n+{\n+   die ("USAGE: \n+   $0\n+   TF libraries \\(comma delimited NO SPACES\\)           \n+   Number of flanking \'N\'s for subject files \\(comma delimited NO SPACES\\)        \n+   Minimum number of occurences \\(comma delimited NO SPACES\\)\n+   TF IDs \\(comma delimited NO SPACES\\)\n+   Single range value in bp \\(+/-\\) query start and end values\n+   Include overlapping TFBSs \\(include/exclude\\)\n+   Output file\\n\\n");                                           \n+}\n+\n+#### File handles\n+my $subject = new FileHandle;\n+my $combined = new FileHandle;\n+my $sorted = new FileHandle;\n+my $groups = new FileHandle;\n+my $filtered_groups = new FileHandle;\n+my $output = new FileHandle;\n+my $output2 = new FileHandle;\n+\n+#### Variables\n+my @subject_files = (); # Array containing the names of selected subject files\n+my @flanking_n = (); # Array containing the number of flanking \'n\' for each pattern\n+my @min_occur = (); # Array containing the minimum occurences for the TF library\n+my @ids = (); # Array containing user defined IDs for the TF libraries\n+my @file_sizes = ();\n+my @sorted_file_sizes = ();\n+my $range = $ARGV[4];\n+my $allow = $ARGV[5];\n+my @regex_ids = ();\n+\n+#####################################################\n+#### Deal with user arguments processed into an array\n+#####################################################\n+\n+#### Convert TF file names string to an array\n+@subject_files = split(/,/, $ARGV[0]);\n+\n+#### Convert flanking \'N\' numbers string into an array\n+@flanking_n = split(/,/, $ARGV[1]);\n+\n+#### Convert minimum occurences string into an array\n+@min_occur = split(/,/, $ARGV[2]);\n+\n+#### Convert minimum occurences string into an array\n+@ids = split(/,/, $ARGV[3]);\n+\n+foreach my $id_string (@ids) {\n+  if($id_string !~ /^[\\w\\d_,]+$/) {\n+     die("A non-letter/number character was detected in your label string!\\n");\n+  }\n+}\n+\n+#### Record how large they are\n+for(my $i=0; $i < $#subject_files + 1; $i++) {\n+   my $size = (-s $subject_files[$i]); # -s performed on an unopened file!\n+\n+   push(@file_si'..b'ord = <$fh>;\n+\n+   $/ = $saveinputsep;\n+   return $record;\n+}\n+\n+sub tf_cluster_tree {\n+\n+my @path;\n+my $node_count;\n+my $node = 0;\n+my $success = 0;\n+my $choice = -1;\n+my @node_contains;\n+my @node_to_choice;\n+my @count;\n+my @branches;\n+$branches[0] = -1;\n+my $path_ref;\n+my $branch_ref;\n+my $node_contains;\n+my $node_to_choice;\n+my ($choice_ref,\n+\t$required_ref,\n+\t$tf_ref) = @_;\n+my @choices = @$choice_ref;\n+my @required = @$required_ref;\n+my @tfs = @$tf_ref;\n+($node,\n+ $choice,\n+ $node_count,\n+ $choice_ref,\n+ $path_ref,\n+ $branch_ref,\n+ $node_to_choice,\n+ $node_contains) = next_node ($node,\n+\t\t\t      $choice,\n+\t\t\t      $node_count,\n+\t\t\t      $choice_ref,\n+\t\t\t      \\@path,\n+\t\t\t      \\@branches,\n+\t\t\t      $tf_ref);\n+@choices = @$choice_ref;\n+@path = @$path_ref;\n+@branches = @$branch_ref;\n+$node_contains[$node] = $node_contains;\n+$node_to_choice[$node] = $node_to_choice;\n+\n+BLOCK_A:\n+\n+while (1) {\n+no strict "vars";\n+\n+\tif ( node_terminating($choice, $choice_ref) ) {\n+\t\tpush @path, $node;\n+\t\t@count = undef;\n+\t\tgrep { $count[ $node_contains[$_] ]++ } @path;\n+\t\t#print "Count is @count.\\n";\n+\t\tmy $score = grep { $count[$_] >= $required[$_] }\n+\t\t\t(0 .. $#count);\n+\t\t#print "Path is @path\\n";\n+\t\tif ($score == scalar @required) {\n+\t\t\t$success = 1;\n+\t\t\tlast BLOCK_A;\n+\t\t}\n+\t\tpop @path;\n+\t\t($node,\n+\t\t $choice,\n+\t \t $path_ref)\n+\t\t= last_unexplored_node(\\@path, $choice, $choice_ref,\n+\t\t\\@node_to_choice, \\@branches);\n+\t\tlast BLOCK_A if ($node == -1);\n+\t\t@path = @$path_ref;\n+\t}\n+\tif ( node_fully_explored($choice, $node, $choice_ref, \\@branches) ) {\n+\t\t($node,\n+\t\t $choice,\n+\t\t $path_ref)\n+\t\t= last_unexplored_node(\\@path, $choice, $choice_ref,\n+\t\t\\@node_to_choice, \\@branches);\n+\t\t@path = @$path_ref;\n+\t\tlast BLOCK_A if ($node == -1);\n+\t}\n+\t($node,\n+\t $choice,\n+\t $node_count,\n+\t $choice_ref,\n+\t $path_ref,\n+\t $branch_ref,\n+\t $node_to_choice,\n+\t $node_contains,) = next_node ($node,\n+\t \t\t\t       $choice,\n+\t\t\t\t       $node_count,\n+\t\t\t\t       $choice_ref,\n+\t\t\t\t       \\@path,\n+\t\t\t\t       \\@branches,\n+\t\t\t\t       $tf_ref);\n+\t@choices = @$choice_ref;\n+\t@path = @$path_ref;\n+\t@branches = @$branch_ref;\n+\t$node_contains[$node] = $node_contains;\n+\t$node_to_choice[$node] = $node_to_choice;\n+\t} \n+return $success;\n+}\n+\n+\n+sub next_node {\n+\n+\tmy ($node,\n+\tmy $choice,\n+\tmy $node_count,\n+\tmy $choice_ref,\n+\tmy $path_ref,\n+\tmy $branch_ref,\n+\tmy $tf_ref) = @_;\n+\tmy @choices = @$choice_ref;\n+\tmy @path = @$path_ref;\n+\tmy @branches = @$branch_ref;\n+\tmy @tfs = @$tf_ref;\n+\tmy $new_choice = $choices[$choice][0];\n+\tpush @{ $choices[$choice] }, shift @{ $choices[$choice] };\n+\t$choice = $new_choice;\n+\tmy $node_to_choice = $choice;\n+\tpush @path, $node if $node;\n+\t$branches[$node]++;\n+\t$node = $node_count++;\n+\tmy $node_contains = $tfs[$choice - 1] if $choice;\n+\treturn (\n+\t\t$node,\n+\t\t$choice,\n+\t\t$node_count,\n+\t\t\\@choices,\n+\t\t\\@path,\n+\t\t\\@branches,\n+\t\t$node_to_choice,\n+\t\t$node_contains);\n+}\n+\n+sub node_fully_explored {\n+no strict "vars";\n+\n+\tmy $choice = shift;\n+\tmy $node = shift;\n+\tmy $choices_ref = shift;\n+\tmy $branch_ref = shift;\n+\tmy @choices = @$choices_ref;\n+\tmy @branches = @$branch_ref;\n+\tif ($branches[$node] == (scalar @{ $choices[$choice] })) {\n+\t\treturn 1 }\n+\telse { return 0 }\n+}\n+\n+sub last_unexplored_node {\n+no strict "vars";\n+\n+\tmy $path_ref = shift;\n+\tmy $choice = shift;\n+\tmy $choice_ref = shift;\n+\tmy $node_to_choice_ref = shift;\n+\tmy $branch_ref = shift;\n+\tmy @path = @$path_ref;\n+\tmy @node_to_choice = @$node_to_choice_ref;\n+\tmy @branches = @$branch_ref;\n+\tmy $node;\n+\n+\tdo {\n+\t\t$node = pop @path;\n+\t\t$choice = $node_to_choice[$node];\n+\t\tif ( $node == 0 and node_fully_explored($choice, $node,\n+\t\t\t$choice_ref, \\@branches) ) {\n+\t\t\t$node = -1;\n+\t\t\tlast;\n+\t\t}\n+\t} while ( node_fully_explored($choice, $node, $choice_ref,\n+\t\t\\@branches) );\n+\treturn ($node, $choice, \\@path);\n+}\n+\n+sub node_terminating {\n+\n+\tmy $choice = shift;\n+\tmy $choices_ref = shift;\n+\tmy @choices = @$choices_ref;\n+\tif ($choices[$choice][0]) { return 0 }\n+\telse { return 1 }\n+\t\n+}\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n'