| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # | 
| 2 |  |  |  |  |  |  | # OO methods that calculates #B(n) on Thompson group F | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # Author: Roberto Alamos Moreno | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # Copyright (c) 2004 Roberto Alamos Moreno. All rights reserved. | 
| 7 |  |  |  |  |  |  | # This program is free software; you can redistribute it and/or | 
| 8 |  |  |  |  |  |  | # modify it under the same terms as Perl itself. | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  | # November 2004. Antofagasta, Chile. | 
| 11 |  |  |  |  |  |  | # | 
| 12 |  |  |  |  |  |  | package Math::Group::Thompson; | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | $VERSION = '0.96'; | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 1 |  |  | 1 |  | 6830 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 48 |  | 
| 17 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 37 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 1 |  |  | 1 |  | 975 | use FileHandle; | 
|  | 1 |  |  |  |  | 14801 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | =head1 NAME | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | Math::Group::Thompson - OO methods that calculates the cardinality | 
| 24 |  |  |  |  |  |  | of the ball of radius 'n' of Thompson group F | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | use Math::Group::Thompson; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | my $F = Math::Group::Thompson->new( VERBOSE => 0 ); | 
| 31 |  |  |  |  |  |  | my $card = $F->cardBn(3,''); | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | print "#B(3) = $card\n"; | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | The Math::Group::Thompson module provides objetct oriented methods | 
| 38 |  |  |  |  |  |  | that calculates the cardinality of the ball of radius 'n' | 
| 39 |  |  |  |  |  |  | of Thompson group F. | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | This module uses the presentation of F | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | F = < A,B | [AB^(-1),A^(-1)BA] = [AB^(-1),A^(-2)BA^2] = e > | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | where A,B are formal symbols, [x,y] is the usual commutator and e | 
| 46 |  |  |  |  |  |  | is the identity element of F. | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | [x,y] = xyx^(-1)y^(-1) | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | This means that for every g in F, g can be written as word | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | g = a_{1}a_{2} ... a_{n} | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | where all the a_{i} are A,B,A^(-1) or B^(-1) for all i <= n. | 
| 55 |  |  |  |  |  |  | Internally, Math::Group::Thompson representates A,B,A^(-1),B^(-1) as | 
| 56 |  |  |  |  |  |  | A,B,C,D respectively. | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | Considering the set S = { A,B,A^(-1),B^(-1) } as a generator set for F. | 
| 59 |  |  |  |  |  |  | One can define the length function L, as | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | L(g) = min{ n | g can be written as a word with n elements of S } | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | We have to define L(e) = 0 | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | With this definition, the ball of radius n of F, can be defined as | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | B(n) = { g in F | L(g) <= n } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | So, what this module do is to calculate #B(n) or #(gB(n) - B(n)), | 
| 70 |  |  |  |  |  |  | where g in F, depending on what you need. Note that by definition of S, | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | B(n+1) = (AB(n)-B(n))U(BB(n)-B(n))U(CB(n)-B(n))U(DB(n)-B(n)) U B(n) | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | so | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | #B(n+1) = #(AB(n)-B(n))+#(BB(n)-B(n))+#(CB(n)-B(n))+#(DB(n)-B(n))+#B(n) | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | Also, this module stores some special relations derived from | 
| 79 |  |  |  |  |  |  | [AB^(-1),A^(-1)BA] = [AB^(-1),A^(-2)BA^2] = e that must me avoided when | 
| 80 |  |  |  |  |  |  | counting the elements of B(n). For example, from [AB^(-1),A^(-1)BA] = e | 
| 81 |  |  |  |  |  |  | it can be derived the relations | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | A^(-1)BAA = AB^(-1)A^(-1)BAB | 
| 84 |  |  |  |  |  |  | A^(-1)BAAB^(-1) = AB^(-1)A^(-1)BA | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | among many other relations. The first relation show us that if | 
| 87 |  |  |  |  |  |  | we have a word g that contains AB^(-1)A^(-1)BAB it MUST NOT be counted | 
| 88 |  |  |  |  |  |  | as an element of B(n) for some n, because the word AB^(-1)A^(-1)BAB | 
| 89 |  |  |  |  |  |  | can be reduced to A^(-1)BAA and this implies that g was already counted | 
| 90 |  |  |  |  |  |  | as an element of B(n). Second relation tell us that if we have | 
| 91 |  |  |  |  |  |  | a word w that contains A^(-1)BAAB^(-1) it MUST NOT be counted as an | 
| 92 |  |  |  |  |  |  | element of B(n) because w was already counted (or will be counted) as | 
| 93 |  |  |  |  |  |  | and element of B(n). | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | Resuming, relation [AB^(-1),A^(-1)BA] = 1, allow us to derive relations | 
| 96 |  |  |  |  |  |  | between words with length 4 and length 6, and between words of length 5. | 
| 97 |  |  |  |  |  |  | And the second relation [AB^(-1),A^(-2)BA^2] = 1 can be used to derive | 
| 98 |  |  |  |  |  |  | relations between words with length 6 and words with length 8, and | 
| 99 |  |  |  |  |  |  | between words of length 7. | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | =head1 METHODS | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | =over 4 | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | =item new | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | Creates the Thompson object. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | Usage: my $F = new->Math::Group::Thompson( VERBOSE => $v ); | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | Verbose argument tells Math::Group::Thompson whether print every | 
| 112 |  |  |  |  |  |  | word generated ($v == 1) or not ($v == 0), or store them | 
| 113 |  |  |  |  |  |  | in a file, where $v is the name of the file (obviously different | 
| 114 |  |  |  |  |  |  | to 0 or 1). If the verbose file exists it is replaced, so you have to | 
| 115 |  |  |  |  |  |  | check for its integrity. | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | NOTE: | 
| 118 |  |  |  |  |  |  | It's not recommend to store the words on a file because for | 
| 119 |  |  |  |  |  |  | very small values of n, #B(n) or #gB(n)-B(n) are very very large. | 
| 120 |  |  |  |  |  |  | For example for n = 19, #B(n) ~ 3^n = 1162261467 ~ 1.1 Giga, but | 
| 121 |  |  |  |  |  |  | the space ocupped by the file will be (in bytes): | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | #B(1) + sum(i=2 to 19){i*(#B(i) - #B(i-1))} = | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =cut | 
| 126 |  |  |  |  |  |  | sub new { | 
| 127 | 0 |  | 0 | 0 | 1 |  | my $class = shift || undef; | 
| 128 | 0 | 0 |  |  |  |  | if(!defined $class) { | 
| 129 | 0 |  |  |  |  |  | return undef; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 |  |  |  |  |  | my %args = ( VERBOSE => 0, # By default don't print anything | 
| 133 |  |  |  |  |  |  | @_ ); | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | # Inverse elements | 
| 136 | 0 |  |  |  |  |  | my $inv = { B => 'D', # B is the inverse of B^(-1) | 
| 137 |  |  |  |  |  |  | A => 'C', # A is the inverse of A^(-1) | 
| 138 |  |  |  |  |  |  | D => 'B', # B^(-1) is the inverse of B | 
| 139 |  |  |  |  |  |  | C => 'A', # A^(-1) is the inverse of A | 
| 140 |  |  |  |  |  |  | }; | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | # Prohibited words | 
| 143 |  |  |  |  |  |  | # Words of length 5 | 
| 144 | 0 |  |  |  |  |  | my @rel5 = ( | 
| 145 |  |  |  |  |  |  | 'BAADC', | 
| 146 |  |  |  |  |  |  | 'ABCCD', | 
| 147 |  |  |  |  |  |  | 'AADCD', | 
| 148 |  |  |  |  |  |  | 'BABCC', | 
| 149 |  |  |  |  |  |  | 'ADCDA', | 
| 150 |  |  |  |  |  |  | 'CBABC', | 
| 151 |  |  |  |  |  |  | 'DCDAB', | 
| 152 |  |  |  |  |  |  | 'DCBAB', | 
| 153 |  |  |  |  |  |  | 'CDABC', | 
| 154 |  |  |  |  |  |  | 'ADCBA', | 
| 155 |  |  |  |  |  |  | ); | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | # Words of length 6 | 
| 158 | 0 |  |  |  |  |  | my @rel6 = ( | 
| 159 |  |  |  |  |  |  | 'AADCBA', | 
| 160 |  |  |  |  |  |  | 'DAADCB', | 
| 161 |  |  |  |  |  |  | 'CBAADC', | 
| 162 |  |  |  |  |  |  | 'BAADCD', | 
| 163 |  |  |  |  |  |  | 'DABCCB', | 
| 164 |  |  |  |  |  |  | 'CDABCC', | 
| 165 |  |  |  |  |  |  | 'BABCCD', | 
| 166 |  |  |  |  |  |  | 'ABCCDA', | 
| 167 |  |  |  |  |  |  | 'CDAADC', | 
| 168 |  |  |  |  |  |  | 'AADCDA', | 
| 169 |  |  |  |  |  |  | 'ABCCBA', | 
| 170 |  |  |  |  |  |  | 'CBABCC', | 
| 171 |  |  |  |  |  |  | 'CCDAAD', | 
| 172 |  |  |  |  |  |  | 'ADCDAB', | 
| 173 |  |  |  |  |  |  | 'BCCBAA', | 
| 174 |  |  |  |  |  |  | 'DCBABC', | 
| 175 |  |  |  |  |  |  | 'BCCDAA', | 
| 176 |  |  |  |  |  |  | 'DCDABC', | 
| 177 |  |  |  |  |  |  | 'CCBAAD', | 
| 178 |  |  |  |  |  |  | 'ADCBAB', | 
| 179 |  |  |  |  |  |  | ); | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # Words of length 7 | 
| 182 | 0 |  |  |  |  |  | my @rel7 = ( | 
| 183 |  |  |  |  |  |  | 'CBAAADC', | 
| 184 |  |  |  |  |  |  | 'ABCCCDA', | 
| 185 |  |  |  |  |  |  | 'BAAADCC', | 
| 186 |  |  |  |  |  |  | 'AABCCCD', | 
| 187 |  |  |  |  |  |  | 'AAADCCD', | 
| 188 |  |  |  |  |  |  | 'BAABCCC', | 
| 189 |  |  |  |  |  |  | 'AADCCDA', | 
| 190 |  |  |  |  |  |  | 'CBAABCC', | 
| 191 |  |  |  |  |  |  | 'ADCCDAA', | 
| 192 |  |  |  |  |  |  | 'CCBAABC', | 
| 193 |  |  |  |  |  |  | 'DCCDAAB', | 
| 194 |  |  |  |  |  |  | 'DCCBAAB', | 
| 195 |  |  |  |  |  |  | 'CCDAABC', | 
| 196 |  |  |  |  |  |  | 'ADCCBAA', | 
| 197 |  |  |  |  |  |  | ); | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | # Words of length 8 | 
| 200 | 0 |  |  |  |  |  | my @rel8 = ( | 
| 201 |  |  |  |  |  |  | 'AADCCBAA', | 
| 202 |  |  |  |  |  |  | 'AAADCCBA', | 
| 203 |  |  |  |  |  |  | 'CCBAAADC', | 
| 204 |  |  |  |  |  |  | 'CBAAADCC', | 
| 205 |  |  |  |  |  |  | 'CDAABCCC', | 
| 206 |  |  |  |  |  |  | 'CCDAABCC', | 
| 207 |  |  |  |  |  |  | 'AABCCCDA', | 
| 208 |  |  |  |  |  |  | 'ABCCCDAA', | 
| 209 |  |  |  |  |  |  | 'DAAADCCB', | 
| 210 |  |  |  |  |  |  | 'BAAADCCD', | 
| 211 |  |  |  |  |  |  | 'DAABCCCB', | 
| 212 |  |  |  |  |  |  | 'BAABCCCD', | 
| 213 |  |  |  |  |  |  | 'CDAAADCC', | 
| 214 |  |  |  |  |  |  | 'AAADCCDA', | 
| 215 |  |  |  |  |  |  | 'AABCCCBA', | 
| 216 |  |  |  |  |  |  | 'CBAABCCC', | 
| 217 |  |  |  |  |  |  | 'CCDAAADC', | 
| 218 |  |  |  |  |  |  | 'AADCCDAA', | 
| 219 |  |  |  |  |  |  | 'ABCCCBAA', | 
| 220 |  |  |  |  |  |  | 'CCBAABCC', | 
| 221 |  |  |  |  |  |  | 'CCCDAAAD', | 
| 222 |  |  |  |  |  |  | 'ADCCDAAB', | 
| 223 |  |  |  |  |  |  | 'BCCCBAAA', | 
| 224 |  |  |  |  |  |  | 'DCCBAABC', | 
| 225 |  |  |  |  |  |  | 'BCCCDAAA', | 
| 226 |  |  |  |  |  |  | 'DCCDAABC', | 
| 227 |  |  |  |  |  |  | 'CCCBAAAD', | 
| 228 |  |  |  |  |  |  | 'ADCCBAAB', | 
| 229 |  |  |  |  |  |  | ); | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # Define the generator set S = { A,B,A^(-1),B^(-1) } | 
| 233 | 0 |  |  |  |  |  | my @generators = ('B','A','D','C'); | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | # Open filehandle if we have to | 
| 236 | 0 |  |  |  |  |  | my $fh; | 
| 237 | 0 | 0 |  |  |  |  | if($args{VERBOSE}) { | 
| 238 | 0 | 0 |  |  |  |  | if($args{VERBOSE} ne '1') { | 
| 239 | 0 |  | 0 |  |  |  | $fh = new FileHandle ">".$args{VERBOSE} || undef; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 0 |  |  |  |  |  | return bless { INV => $inv,                          # Inverse relations | 
| 244 |  |  |  |  |  |  | REL => [\@rel5,\@rel6,\@rel7,\@rel8], # Prohibited words | 
| 245 |  |  |  |  |  |  | GEN => \@generators,                  # Generator set | 
| 246 |  |  |  |  |  |  | COUNTER => 0,                         # Element counter | 
| 247 |  |  |  |  |  |  | FIRST_ELEMENT => '',		       # F's element passed to the firs call of method cardBn | 
| 248 |  |  |  |  |  |  | FIRST_CALL => 1,                      # Flag of first call to method cardBn | 
| 249 |  |  |  |  |  |  | VERBOSE => $args{VERBOSE},            # Verbose mode | 
| 250 |  |  |  |  |  |  | FILEHANDLE => \$fh,                   # Filehandler | 
| 251 |  |  |  |  |  |  | }, $class; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =item cardBn | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | This method calculates #B(n) or #(gB(n) - B(n)) depending on if | 
| 257 |  |  |  |  |  |  | the argument passed to the first call of cardBn is '' or not. | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | Usage: my $c = $F->cardBn($radius,$g); | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | where | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | $radius is an integer number >= 0 and | 
| 264 |  |  |  |  |  |  | $g is an element of F (word written with A,B,C or D). | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | If the first time cardBn is called $g is not equal to '', then | 
| 267 |  |  |  |  |  |  | cardBn returns the cardinality of the set | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | gB(n) - B(n) = { w in F | w in gB(n) and w not in B(n) } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | If the firs time cardBn is callen $g is equal to '', then | 
| 272 |  |  |  |  |  |  | cardBn returns #B(n). | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | This algorithm runs on exponential time because | 
| 275 |  |  |  |  |  |  | F is of exponential growth (more "exactly", this algorithm is | 
| 276 |  |  |  |  |  |  | O(3^n) ). | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | =cut | 
| 279 |  |  |  |  |  |  | sub cardBn { | 
| 280 | 0 |  |  | 0 | 1 |  | my ($self,$n,$g) = @_; | 
| 281 | 0 | 0 |  |  |  |  | if(!defined $g) { $g = ""; } | 
|  | 0 |  |  |  |  |  |  | 
| 282 | 0 | 0 | 0 |  |  |  | if(!defined $self || !ref $self || !defined $n || $n < 0 || $n =~ /\D/ || $g =~ /[^ABCD]/) { | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 283 | 0 |  |  |  |  |  | return undef; | 
| 284 |  |  |  |  |  |  | } | 
| 285 | 0 | 0 |  |  |  |  | if($n == 0) { | 
| 286 |  |  |  |  |  |  | # We have to calculate #B(0) or #(gB(0) - B(0)). In any case is 1 | 
| 287 | 0 |  |  |  |  |  | return 1; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | # Check if we are in the first call of cardBn | 
| 291 | 0 | 0 |  |  |  |  | if($self->{FIRST_CALL}) { | 
| 292 |  |  |  |  |  |  | # The first element passed to cardBn is $g. Keep it | 
| 293 | 0 |  | 0 |  |  |  | $self->{FIRST_ELEMENT} = $g || ''; | 
| 294 | 0 |  |  |  |  |  | $self->{FIRST_CALL} = 0; | 
| 295 | 0 | 0 |  |  |  |  | if($self->{FIRST_ELEMENT} eq '') { | 
| 296 | 0 |  |  |  |  |  | $self->note('e'); | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | # For every element A,B,A^(-1) and B^(-1) | 
| 301 | 0 |  |  |  |  |  | for(0..3) { | 
| 302 | 0 |  | 0 |  |  |  | my $aux_g = $self->multiply($g,$self->{GEN}->[$_]) || ''; # Multiple $g by one of the generators | 
| 303 | 0 |  |  |  |  |  | my $i = 0; # Flag that say if the new word contains | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | # Check if the new word is one letter larger than the previous one | 
| 306 | 0 |  |  |  |  |  | my ($length_g,$length_auxg) = (0,0); | 
| 307 | 0 | 0 |  |  |  |  | if($g ne '') { | 
| 308 | 0 |  |  |  |  |  | $length_g += length($g); | 
| 309 |  |  |  |  |  |  | } | 
| 310 | 0 | 0 |  |  |  |  | if($aux_g ne '') { | 
| 311 | 0 |  |  |  |  |  | $length_auxg += length($aux_g); | 
| 312 |  |  |  |  |  |  | } | 
| 313 | 0 | 0 |  |  |  |  | if($length_auxg == ($length_g + 1)) { | 
| 314 |  |  |  |  |  |  | # Check if some prohibited word is in $aux_g | 
| 315 | 0 |  |  |  |  |  | LOOP: for(5..8) { | 
| 316 | 0 | 0 |  |  |  |  | if($length_auxg >= $_) { | 
| 317 |  |  |  |  |  |  | # Check if the word contains any prohibited relation | 
| 318 | 0 |  |  |  |  |  | foreach my $rel (@{$self->{REL}->[$_-5]}) { | 
|  | 0 |  |  |  |  |  |  | 
| 319 | 0 | 0 |  |  |  |  | if($aux_g =~ /$rel$/) { | 
| 320 |  |  |  |  |  |  | # Prohibited word found | 
| 321 | 0 |  |  |  |  |  | $i++; | 
| 322 | 0 |  |  |  |  |  | last LOOP; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | # Check if we foun any prohibited word | 
| 329 | 0 | 0 |  |  |  |  | if($i == 0) { | 
| 330 |  |  |  |  |  |  | # Determine if we are calculating #B(n) or #(gB(n) - B(n)) where g is the first argument received by cardBn | 
| 331 | 0 | 0 |  |  |  |  | if($self->{FIRST_ELEMENT} ne '') { | 
| 332 |  |  |  |  |  |  | # First element wasn't ''. We are calculating #(gB(n) - B(n)) | 
| 333 | 0 | 0 |  |  |  |  | if(length($aux_g) < ($n + length($self->{FIRST_ELEMENT}))) { | 
| 334 | 0 |  |  |  |  |  | $self->cardBn($n,$aux_g); | 
| 335 |  |  |  |  |  |  | } else { | 
| 336 |  |  |  |  |  |  | # Count this element | 
| 337 |  |  |  |  |  |  | # Print word if VERBOSE == 1 | 
| 338 | 0 |  |  |  |  |  | $self->note($aux_g); | 
| 339 | 0 |  |  |  |  |  | $self->{COUNTER}++; | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  | } else { | 
| 342 |  |  |  |  |  |  | # Count this element | 
| 343 |  |  |  |  |  |  | # Print word if VERBOSE == 1 | 
| 344 | 0 |  |  |  |  |  | $self->note($aux_g); | 
| 345 | 0 |  |  |  |  |  | $self->{COUNTER}++; | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | # First element was empty. We are calculating #B(n) | 
| 348 | 0 | 0 |  |  |  |  | if(length($aux_g) < $n) { | 
| 349 |  |  |  |  |  |  | # Word's length is < $n, continue | 
| 350 | 0 |  |  |  |  |  | $self->cardBn($n,$aux_g); | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  | } | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | # Return | 
| 358 | 0 | 0 |  |  |  |  | if($self->{FIRST_ELEMENT} eq '') { | 
| 359 | 0 |  |  |  |  |  | return $self->{COUNTER} + 1; # Returns #B(n). The 1 is for the identity element | 
| 360 |  |  |  |  |  |  | } else { | 
| 361 | 0 |  |  |  |  |  | return $self->{COUNTER};     # Returns #(gB(n)-B(n). | 
| 362 |  |  |  |  |  |  | } | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =item reset | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | Resets the counter used on cardBn method, set | 
| 368 |  |  |  |  |  |  | the FIRST_ELEMENT property at '', and the FIRST_CALL | 
| 369 |  |  |  |  |  |  | proporty to 1. | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | Usage: $F->reset; | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | =cut | 
| 374 |  |  |  |  |  |  | sub reset { | 
| 375 | 0 |  | 0 | 0 | 1 |  | my $self = shift || undef; | 
| 376 | 0 | 0 |  |  |  |  | if(!defined $self) { | 
| 377 | 0 |  |  |  |  |  | return; | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  |  | 
| 380 | 0 |  |  |  |  |  | $self->{COUNTER} = 0; | 
| 381 | 0 |  |  |  |  |  | $self->{FIRST_ELEMENT} = ''; | 
| 382 | 0 |  |  |  |  |  | $self->{FIRST_CALL} = 1; | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 0 |  |  |  |  |  | return 1; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | =item multiply | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | Multiplication between two words of F. This method | 
| 390 |  |  |  |  |  |  | considers the inverse relations stored in the attribute | 
| 391 |  |  |  |  |  |  | INV. | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | Usage: my $mul = $F->multiply($g,$w); | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | where $g and $w are elements of F, and $mul is the | 
| 396 |  |  |  |  |  |  | result of $g$w. | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =cut | 
| 399 |  |  |  |  |  |  | sub multiply { | 
| 400 | 0 |  |  | 0 | 1 |  | my ($self,$g,$h) = @_; | 
| 401 | 0 | 0 |  |  |  |  | if(!defined $self) { | 
| 402 | 0 |  |  |  |  |  | return; | 
| 403 |  |  |  |  |  |  | } | 
| 404 | 0 | 0 | 0 |  |  |  | if(!defined $g && !defined $h) { | 
|  |  | 0 | 0 |  |  |  |  | 
| 405 | 0 |  |  |  |  |  | return undef; | 
| 406 |  |  |  |  |  |  | } elsif($g eq '' && $h eq '') { | 
| 407 | 0 |  |  |  |  |  | return undef; | 
| 408 |  |  |  |  |  |  | } | 
| 409 | 0 | 0 |  |  |  |  | if(!defined $h) { | 
|  |  | 0 |  |  |  |  |  | 
| 410 | 0 |  |  |  |  |  | return $g; | 
| 411 |  |  |  |  |  |  | } elsif ($h eq '') { | 
| 412 | 0 |  |  |  |  |  | return $g; | 
| 413 |  |  |  |  |  |  | } | 
| 414 | 0 | 0 |  |  |  |  | if(!defined $g) { | 
|  |  | 0 |  |  |  |  |  | 
| 415 | 0 |  |  |  |  |  | return $h; | 
| 416 |  |  |  |  |  |  | } elsif($g eq '') { | 
| 417 | 0 |  |  |  |  |  | return $h; | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | # Get inverse relations | 
| 421 | 0 |  |  |  |  |  | my %inv = $self->get_inv; | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | # Multiply | 
| 424 | 0 |  |  |  |  |  | my @h = split(//,$h); | 
| 425 | 0 |  |  |  |  |  | foreach my $el (@h) { | 
| 426 | 0 |  |  |  |  |  | $g =~ /(.)$/; | 
| 427 | 0 | 0 |  |  |  |  | if($1 ne $inv{$el}) { | 
| 428 | 0 |  |  |  |  |  | return $g.$h; | 
| 429 |  |  |  |  |  |  | } else { | 
| 430 | 0 |  |  |  |  |  | $g =~ s/.$//; | 
| 431 | 0 |  |  |  |  |  | $h =~ s/^.//; | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  |  | 
| 434 | 0 | 0 | 0 |  |  |  | if($g eq '' && $h ne '') { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
| 435 | 0 |  |  |  |  |  | return $h | 
| 436 |  |  |  |  |  |  | } elsif($h eq '' && $g ne '') { | 
| 437 | 0 |  |  |  |  |  | return $g; | 
| 438 |  |  |  |  |  |  | } elsif($g eq '' && $h eq '') { | 
| 439 | 0 |  |  |  |  |  | return undef; | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | =item rotate | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | This module receives as argument a word in F and | 
| 447 |  |  |  |  |  |  | puts the last letter on word in its first place. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | Usage: $w = 'ABC'; | 
| 450 |  |  |  |  |  |  | $W = $self->rotate($w); # $W is now equal to 'CBA' | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =cut | 
| 453 |  |  |  |  |  |  | sub rotate { | 
| 454 | 0 |  |  | 0 | 1 |  | my ($self,$word) = @_; | 
| 455 | 0 | 0 | 0 |  |  |  | if(!defined $self || !defined $word) { | 
| 456 | 0 |  |  |  |  |  | return undef; | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  |  | 
| 459 | 0 |  |  |  |  |  | $word =~ s/(.)$//; | 
| 460 | 0 |  |  |  |  |  | return $1.$word; | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | =item inverse | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | This method receives a word in F and returns its inverse. | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | Usage: $w = 'ABC'; | 
| 468 |  |  |  |  |  |  | $W = $self->inverse($w); # $W == 'ADC' | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | =cut | 
| 471 |  |  |  |  |  |  | sub inverse { | 
| 472 | 0 |  |  | 0 | 1 |  | my ($self,$word) = @_; | 
| 473 | 0 | 0 | 0 |  |  |  | if(!defined $self || !defined $word) { | 
| 474 | 0 |  |  |  |  |  | return undef; | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  |  | 
| 477 | 0 |  |  |  |  |  | my %inv = $self->get_inv; | 
| 478 | 0 |  |  |  |  |  | my @word = split(//,$word); | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 0 |  |  |  |  |  | for(0..$#word) { | 
| 481 | 0 |  |  |  |  |  | $word[$_] = $inv{$word[$_]}; | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  |  | 
| 484 | 0 |  |  |  |  |  | $word = join('',@word); | 
| 485 | 0 |  |  |  |  |  | return reverse $word; | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | =item divide | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | This method receives a word in F and returns a 2-dimensional | 
| 491 |  |  |  |  |  |  | array where the first element is the first half | 
| 492 |  |  |  |  |  |  | of the word, and the second is the inverse of the | 
| 493 |  |  |  |  |  |  | second half of the word. | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | Usage: $w = 'AABC'; | 
| 496 |  |  |  |  |  |  | ($w1,$w2) = $self->divide($w); # Now $w1 == 'AA' and $w2 == 'AD' | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | =cut | 
| 499 |  |  |  |  |  |  | sub divide { | 
| 500 | 0 |  |  | 0 | 1 |  | my ($self,$word) = @_; | 
| 501 | 0 | 0 | 0 |  |  |  | if(!defined $self || !defined $word) { | 
| 502 | 0 |  |  |  |  |  | return undef; | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 | 0 |  |  |  |  |  | my $largo = length($word); | 
| 506 | 0 |  |  |  |  |  | my @word = split(//,$word); | 
| 507 | 0 |  |  |  |  |  | $word = join('',@word[0..($largo/2)-1]); | 
| 508 | 0 |  |  |  |  |  | my $word2 = join('',@word[($largo/2)..$#word]); | 
| 509 | 0 |  |  |  |  |  | $word2 = $self->inverse($word2); | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 0 |  |  |  |  |  | return ($word,$word2); | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | =item get_inv | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | This method return the hash of inverse relations | 
| 517 |  |  |  |  |  |  | between the generators elements of F. | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | =cut | 
| 520 |  |  |  |  |  |  | sub get_inv { | 
| 521 | 0 |  | 0 | 0 | 1 |  | my $self = shift || undef; | 
| 522 | 0 | 0 |  |  |  |  | if(!defined $self) { | 
| 523 | 0 |  |  |  |  |  | return undef; | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 0 |  |  |  |  |  | return %{$self->{INV}}; | 
|  | 0 |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | =item note | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | This method prints in STDERR the string received or | 
| 532 |  |  |  |  |  |  | puts it on the correspondent file. | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | Usage: $F->note('AA'); # Print AA."\n" or store it on a file. | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | =cut | 
| 537 |  |  |  |  |  |  | sub note { | 
| 538 | 0 |  | 0 | 0 | 1 |  | my $self = shift || undef; | 
| 539 | 0 | 0 |  |  |  |  | if(!defined $self) { | 
| 540 | 0 |  |  |  |  |  | return undef; | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 | 0 |  | 0 |  |  |  | my $g = shift || return undef; | 
| 544 |  |  |  |  |  |  |  | 
| 545 | 0 | 0 |  |  |  |  | if($self->{VERBOSE}) { | 
| 546 | 0 | 0 |  |  |  |  | if($self->{VERBOSE} eq '1') { | 
| 547 |  |  |  |  |  |  | # Print word to STDERR | 
| 548 | 0 |  |  |  |  |  | print STDERR $g,"\n"; | 
| 549 |  |  |  |  |  |  | } else { | 
| 550 |  |  |  |  |  |  | # Put word on the correspondent file | 
| 551 | 0 | 0 | 0 |  |  |  | if($self->{FILEHANDLE} && ref(${$self->{FILEHANDLE}}) eq 'FileHandle') { | 
|  | 0 |  |  |  |  |  |  | 
| 552 | 0 |  |  |  |  |  | my $fh = ${$self->{FILEHANDLE}}; | 
|  | 0 |  |  |  |  |  |  | 
| 553 | 0 |  |  |  |  |  | print $fh $g,"\n"; | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  | } | 
| 556 |  |  |  |  |  |  | } | 
| 557 |  |  |  |  |  |  | } | 
| 558 |  |  |  |  |  |  |  | 
| 559 |  |  |  |  |  |  | # Destroy function. Closes the filehandle opened in 'new' method (if it was opened). | 
| 560 |  |  |  |  |  |  | sub DESTROY { | 
| 561 | 0 |  | 0 | 0 |  |  | my $self = shift || undef; | 
| 562 | 0 | 0 |  |  |  |  | if(!defined $self) { | 
| 563 | 0 |  |  |  |  |  | return undef; | 
| 564 |  |  |  |  |  |  | } | 
| 565 |  |  |  |  |  |  |  | 
| 566 | 0 | 0 |  |  |  |  | if($self->{VERBOSE}) { | 
| 567 | 0 | 0 | 0 |  |  |  | if($self->{VERBOSE} ne '1' && ref(${$self->{FILEHANDLE}}) eq 'FileHandle') { | 
|  | 0 |  |  |  |  |  |  | 
| 568 | 0 | 0 |  |  |  |  | ${$self->{FILEHANDLE}}->close if $self->{FILEHANDLE}; | 
|  | 0 |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | =back 4 | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | =head1 BUGS | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | There isn't reported bugs yet, but that doesn't mean that there aren't ;) . | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | =head1 AUTHOR | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | Roberto Alamos Moreno | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | Thanks to professor Juan Rivera Letelier for his support to my thesis work, and help in the design | 
| 584 |  |  |  |  |  |  | of cardBn algorithm :) . | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | Copyright (c) 2004 Roberto Alamos Moreno. All rights reserved. | 
| 589 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | =cut | 
| 592 |  |  |  |  |  |  | 1; |