comparison variant_effect_predictor/Bio/LiveSeq/Chain.pm @ 0:1f6dce3d34e0

Uploaded
author mahtabm
date Thu, 11 Apr 2013 02:01:53 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1f6dce3d34e0
1 #!/usr/bin/perl
2 # $Id: Chain.pm,v 1.12 2001/06/18 08:27:53 heikki Exp $
3 #
4 # bioperl module for Bio::LiveSeq::Chain
5 #
6 # Cared for by Joseph Insana <insana@ebi.ac.uk> <jinsana@gmx.net>
7 #
8 # Copyright Joseph Insana
9 #
10 # You may distribute this module under the same terms as perl itself
11 #
12 # POD documentation - main docs before the code
13 #
14
15 =head1 NAME
16
17 Bio::LiveSeq::Chain - DoubleChain DataStructure for Perl
18
19 =head1 SYNOPSIS
20
21 #documentation needed
22
23 =head1 DESCRIPTION
24
25 This is a general purpose module (that's why it's not in object-oriented
26 form) that introduces a novel datastructure in PERL. It implements
27 the "double linked chain". The elements of the chain can contain basically
28 everything. From chars to strings, from object references to arrays or hashes.
29 It is used in the LiveSequence project to create a dynamical DNA sequence,
30 easier to manipulate and change. It's use is mainly for sequence variation
31 analysis but it could be used - for example - in e-cell projects.
32 The Chain module in itself doesn't have any biological bias, so can be
33 used for any programming purpose.
34
35 Each element of the chain (with the exclusion of the first and the last of the
36 chain) is connected to other two elements (the PREVious and the NEXT one).
37 There is no absolute position (like in an array), hence if positions are
38 important, they need to be computed (methods are provided).
39 Otherwise it's easy to keep track of the elements with their "LABELs".
40 There is one LABEL (think of it as a pointer) to each ELEMENT. The labels
41 won't change after insertions or deletions of the chain. So it's
42 always possible to retrieve an element even if the chain has been
43 modified by successive insertions or deletions.
44 From this the high potential profit for bioinformatics: dealing with
45 sequences in a way that doesn't have to rely on positions, without
46 the need of constantly updating them if the sequence changes, even
47 dramatically.
48
49 =head1 AUTHOR - Joseph A.L. Insana
50
51 Email: Insana@ebi.ac.uk, jinsana@gmx.net
52
53 Address:
54
55 EMBL Outstation, European Bioinformatics Institute
56 Wellcome Trust Genome Campus, Hinxton
57 Cambs. CB10 1SD, United Kingdom
58
59 =head1 APPENDIX
60
61 The rest of the documentation details each of the object
62 methods. Internal methods are usually preceded with a _
63
64 =cut
65
66 # Let the code begin...
67
68 # DoubleChain Data Structure for PERL
69 # by Joseph A.L. Insana - Deathson - Filius Mortis - Fal Mortais
70 # insana@ebi.ac.uk, jinsana@gmx.net
71
72 package Bio::LiveSeq::Chain;
73 # Version history:
74 # Fri Mar 10 16:46:51 GMT 2000 v1.0 begun working on chains in perl
75 # Sat Mar 11 05:47:21 GMT 2000 v.1.4 working on splice method
76 # Sun Mar 12 14:08:31 GMT 2000 v.1.5
77 # Sun Mar 12 17:21:51 GMT 2000 v.2.0 splice method working, is_updownstream made
78 # Sun Mar 12 18:11:22 GMT 2000 v.2.04 wrapped all in package Chain.pm
79 # Sun Mar 12 18:49:23 GMT 2000 v.2.08 added elements()
80 # Sun Mar 12 21:18:04 GMT 2000 v.2.1 done array2dchain, working on *insert*
81 # Sun Mar 12 23:04:40 GMT 2000 v.2.16 done *insert*, up_element, create_elems
82 # Sun Mar 12 23:45:32 GMT 2000 v.2.17 debugged and checked
83 # Mon Mar 13 00:44:51 GMT 2000 v.2.2 added mutate()
84 # Mon Mar 13 02:00:32 GMT 2000 v 2.21 added invert_dchain()
85 # Mon Mar 13 03:01:21 GMT 2000 v 2.22 created updown_chain2string
86 # Mon Mar 13 03:45:50 GMT 2000 v.2.24 added subchain_length()
87 # Mon Mar 13 17:25:04 GMT 2000 v.2.26 added element_at_pos and pos_of_element
88 # Wed Mar 15 23:05:06 GMT 2000 v.2.27 use strict enforced
89 # Thu Mar 16 19:05:34 GMT 2000 v.2.3 changed dchain->chain everywhere
90 # Fri Mar 17 01:48:36 GMT 2000 v.2.33 mutate_element renamed, created new
91 # methods: set_value, get_value...
92 # Fri Mar 17 05:03:15 GMT 2000 v.2.4 set_value_at_pos, get_value_at_pos
93 # get_label_at_pos...
94 # Fri Mar 17 15:51:07 GMT 2000 v.2.41 renamed pos_of_element -> get_pos_of_label
95 # Fri Mar 17 18:10:36 GMT 2000 v.2.44 recoded subchain_length and pos_of_label
96 # Fri Mar 17 20:12:27 GMT 2000 v.2.5 NAMING change: index->label everywhere
97 # Mon Mar 20 18:33:10 GMT 2000 v.2.52 label_exists(), start(), end()
98 # Mon Mar 20 23:10:28 GMT 2000 v.2.6 labels() created
99 # Wed Mar 22 18:35:17 GMT 2000 v.2.61 chain2string() rewritten
100 # Tue Dec 12 14:47:58 GMT 2000 v 2.66 optimized with /use integer/
101 # Tue Dec 12 16:28:45 GMT 2000 v 2.7 rewritten comments to methods in pod style
102
103 #
104 $VERSION=2.7;
105 #
106 # TODO_list:
107 # **** cleanup code
108 # **** performance concerns
109 # *??* create hash2dchain ???? (with hashkeys used for label)
110 # **????** how about using array of arrays instead than hash of arrays??
111 #
112 # further strict complaints:
113 # in verbose $string assignment around line 721 ???
114
115 # TERMINOLOGY update, naming convention:
116 # "chain" the datastructure
117 # "element" the individual units that compose a chain
118 # "label" the unique name of a single element
119 # "position" the position of an element into the chain according to a
120 # particular coordinate system (e.g. counting from the start)
121 # "value" what is stored in a single element
122
123 use Carp qw(croak cluck carp); # as of 2.3
124 use strict; # as of 2.27
125 use integer; # WARNING: this is to increase performance
126 # a little bit of attention has to be given if float need to
127 # be stored as elements of the array
128 # the use of this "integer" affects all operations but not
129 # assignments. So float CAN be assigned as elements of the chain
130 # BUT, if you assign $z=-1.8;, $z will be equal to -1 because
131 # "-" counts as a unary operation!
132
133 =head2 _updown_chain2string
134
135 Title : chain2string
136 Usage : $string = Bio::LiveSeq::Chain::chain2string("down",$chain,6,9)
137 Function: reads the contents of the chain, outputting a string
138 Returns : a string
139 Examples:
140 : down_chain2string($chain) -> all the chain from begin to end
141 : down_chain2string($chain,6) -> from 6 to the end
142 : down_chain2string($chain,6,4) -> from 6, going on 4 elements
143 : down_chain2string($chain,6,"",10) -> from 6 to 10
144 : up_chain2string($chain,10,"",6) -> from 10 to 6 upstream
145 Defaults: start=first element; if len undef, goes to last
146 if last undef, goes to end
147 if last defined, it overrides len (undefining it)
148 Error code: -1
149 Args : "up"||"down" as first argument to specify the reading direction
150 reference (to the chain)
151 [first] [len] [last] optional integer arguments to specify how
152 much and from (and to) where to read
153
154 =cut
155
156 # methods rewritten 2.61
157 sub up_chain2string {
158 _updown_chain2string("up",@_);
159 }
160 sub down_chain2string {
161 _updown_chain2string("down",@_);
162 }
163
164 sub _updown_chain2string {
165 my ($direction,$chain,$first,$len,$last)=@_;
166 unless($chain) { cluck "no chain input"; return (-1); }
167 my $begin=$chain->{'begin'}; # the label of the BEGIN element
168 my $end=$chain->{'end'}; # the label of the END element
169 my $flow;
170
171 if ($direction eq "up") {
172 $flow=2; # used to determine the direction of chain navigation
173 unless ($first) { $first=$end; } # if undef or 0, use $end
174 } else { # defaults to "down"
175 $flow=1; # used to determine the direction of chain navigation
176 unless ($first) { $first=$begin; } # if undef or 0, use $begin
177 }
178
179 unless($chain->{$first}) {
180 cluck "label for first not defined"; return (-1); }
181 if ($last) { # if last is defined, it gets priority and len is not used
182 unless($chain->{$last}) {
183 cluck "label for last not defined"; return (-1); }
184 if ($len) {
185 warn "Warning chain2string: argument LAST:$last overriding LEN:$len!";
186 undef $len;
187 }
188 } else {
189 if ($direction eq "up") {
190 $last=$begin; # if last not defined, go 'till begin (or upto len elements)
191 } else {
192 $last=$end; # if last not defined, go 'till end (or upto len elements)
193 }
194 }
195
196 my ($string,@array);
197 my $label=$first; my $i=1;
198 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
199 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
200
201 # proceed for len elements or until last, whichever comes first
202 # if $len undef goes till end
203 while (($label) && ($label != $afterlast) && ($i <= ($len || $i + 1))) {
204 @array=@{$chain->{$label}};
205 $string .= $array[0];
206 $label = $array[$flow];
207 $i++;
208 }
209 return ($string); # if chain is interrupted $string won't be complete
210 }
211
212 =head2 _updown_labels
213
214 Title : labels
215 Usage : @labels = Bio::LiveSeq::Chain::_updown_labels("down",$chain,4,16)
216 Function: returns all the labels in a chain or those between two
217 specified ones (termed "first" and "last")
218 Returns : a reference to an array containing the labels
219 Args : "up"||"down" as first argument to specify the reading direction
220 reference (to the chain)
221 [first] [last] (integer for the starting and eneding labels)
222
223 =cut
224
225
226 # arguments: CHAIN_REF [FIRSTLABEL] [LASTLABEL]
227 # returns: reference to array containing the labels
228 sub down_labels {
229 my ($chain,$first,$last)=@_;
230 _updown_labels("down",$chain,$first,$last);
231 }
232 sub up_labels {
233 my ($chain,$first,$last)=@_;
234 _updown_labels("up",$chain,$first,$last);
235 }
236 # arguments: "up"||"down" CHAIN_REF [FIRSTLABEL] [LASTLABEL]
237 # returns: reference to array containing the labels
238 sub _updown_labels {
239 my ($direction,$chain,$first,$last)=@_;
240 unless($chain) { cluck "no chain input"; return (0); }
241 my $begin=$chain->{'begin'}; # the label of the BEGIN element
242 my $end=$chain->{'end'}; # the label of the END element
243 my $flow;
244 if ($direction eq "up") { $flow=2;
245 unless ($first) { $first=$end; }
246 unless ($last) { $last=$begin; }
247 } else { $flow=1;
248 unless ($last) { $last=$end; }
249 unless ($first) { $first=$begin; }
250 }
251 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
252 unless($chain->{$last}) { warn "not existing label $last"; return (0); }
253
254 my $label=$first; my @labels;
255 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
256 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
257
258 while (($label)&&($label != $afterlast)) {
259 push(@labels,$label);
260 $label=$chain->{$label}[$flow];
261 }
262 return (\@labels); # if chain is interrupted @labels won't be complete
263 }
264
265
266 =head2 start
267
268 Title : start
269 Usage : $start = Bio::LiveSeq::Chain::start()
270 Returns : the label marking the start of the chain
271 Errorcode: -1
272 Args : none
273
274 =cut
275
276 sub start {
277 my $chain=$_[0];
278 unless($chain) { cluck "no chain input"; return (-1); }
279 return ($chain->{'begin'});
280 }
281
282 =head2 end
283
284 Title : end
285 Usage : $end = Bio::LiveSeq::Chain::end()
286 Returns : the label marking the end of the chain
287 Errorcode: -1
288 Args : none
289
290 =cut
291
292 sub end {
293 my $chain=$_[0];
294 unless($chain) { cluck "no chain input"; return (-1); }
295 return ($chain->{'end'});
296 }
297
298 =head2 label_exists
299
300 Title : label_exists
301 Usage : $check = Bio::LiveSeq::Chain::label_exists($chain,$label)
302 Function: It checks if a label is defined, i.e. if an element is there or
303 is not there anymore
304 Returns : 1 if the label exists, 0 if it is not there, -1 error
305 Errorcode: -1
306 Args : reference to the chain, integer
307
308 =cut
309
310 sub label_exists {
311 my ($chain,$label)=@_;
312 unless($chain) { cluck "no chain input"; return (-1); }
313 if ($label && $chain->{$label}) { return (1); } else { return (0) };
314 }
315
316
317 =head2 down_get_pos_of_label
318
319 Title : down_get_pos_of_label
320 Usage : $position = Bio::LiveSeq::Chain::down_get_pos_of_label($chain,$label,$first)
321 Function: returns the position of $label counting from $first, i.e. taking
322 $first as 1 of coordinate system. If $first is not specified it will
323 count from the start of the chain.
324 Returns :
325 Errorcode: 0
326 Args : reference to the chain, integer (the label of interest)
327 optional: integer (a different label that will be taken as the
328 first one, i.e. the one to count from)
329 Note: It counts "downstream". To proceed backward use up_get_pos_of_label
330
331 =cut
332
333 sub down_get_pos_of_label {
334 #down_chain2string($_[0],$_[2],undef,$_[1],"counting");
335 my ($chain,$label,$first)=@_;
336 _updown_count("down",$chain,$first,$label);
337 }
338 sub up_get_pos_of_label {
339 #up_chain2string($_[0],$_[2],undef,$_[1],"counting");
340 my ($chain,$label,$first)=@_;
341 _updown_count("up",$chain,$first,$label);
342 }
343
344 =head2 down_subchain_length
345
346 Title : down_subchain_length
347 Usage : $length = Bio::LiveSeq::Chain::down_subchain_length($chain,$first,$last)
348 Function: returns the length of the chain between the labels "first" and "last", included
349 Returns : integer
350 Errorcode: 0
351 Args : reference to the chain, integer, integer
352 Note: It counts "downstream". To proceed backward use up_subchain_length
353
354 =cut
355
356 # arguments: chain_ref [first] [last]
357 # returns the length of the chain between first and last (included)
358 sub down_subchain_length {
359 #down_chain2string($_[0],$_[1],undef,$_[2],"counting");
360 my ($chain,$first,$last)=@_;
361 _updown_count("down",$chain,$first,$last);
362 }
363 sub up_subchain_length {
364 #up_chain2string($_[0],$_[1],undef,$_[2],"counting");
365 my ($chain,$first,$last)=@_;
366 _updown_count("up",$chain,$first,$last);
367 }
368
369 # arguments: DIRECTION CHAIN_REF FIRSTLABEL LASTLABEL
370 # errorcode 0
371 sub _updown_count {
372 my ($direction,$chain,$first,$last)=@_;
373 unless($chain) { cluck "no chain input"; return (0); }
374 my $begin=$chain->{'begin'}; # the label of the BEGIN element
375 my $end=$chain->{'end'}; # the label of the END element
376 my $flow;
377 if ($direction eq "up") { $flow=2;
378 unless ($first) { $first=$end; }
379 unless ($last) { $last=$begin; }
380 } else { $flow=1;
381 unless ($last) { $last=$end; }
382 unless ($first) { $first=$begin; }
383 }
384 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
385 unless($chain->{$last}) { warn "not existing label $last"; return (0); }
386
387 my $label=$first; my $count;
388 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
389 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
390
391 while (($label)&&($label != $afterlast)) {
392 $count++;
393 $label=$chain->{$label}[$flow];
394 }
395 return ($count); # if chain is interrupted, $i will be up to the breaking point
396 }
397
398 =head2 invert_chain
399
400 Title : invert_chain
401 Usage : $errorcode=Bio::LiveSeq::Chain::invert_chain($chain)
402 Function: completely inverts the order of the chain elements; begin is swapped with end and all links updated (PREV&NEXT fields swapped)
403 Returns : 1 if all OK, 0 if errors
404 Errorcode: 0
405 Args : reference to the chain
406
407 =cut
408
409 sub invert_chain {
410 my $chain=$_[0];
411 unless($chain) { cluck "no chain input"; return (0); }
412 my $begin=$chain->{'begin'}; # the name of the first element
413 my $end=$chain->{'end'}; # the name of the last element
414 my ($label,@array);
415 $label=$begin; # starts from the beginning
416 while ($label) { # proceed with linked elements, swapping PREV and NEXT
417 @array=@{$chain->{$label}};
418 ($chain->{$label}[1],$chain->{$label}[2])=($array[2],$array[1]); # swap
419 $label = $array[1]; # go to the next one
420 }
421 # now swap begin and end fields
422 ($chain->{'begin'},$chain->{'end'})=($end,$begin);
423 return (1); # that's it
424 }
425
426 # warning that method has changed name
427 #sub mutate_element {
428 #croak "Warning: old method name. Please update code to 'set_value_at_label'\n";
429 # &set_value_at_label;
430 #}
431
432 =head2 down_get_value_at_pos
433
434 Title : down_get_value_at_pos
435 Usage : $value = Bio::LiveSeq::Chain::down_get_value_at_pos($chain,$position,$first)
436 Function: used to access the value of the chain at a particular position instead than directly with a label pointer. It will count the position from the start of the chain or from the label $first, if $first is specified
437 Returns : whatever is stored in the element of the chain
438 Errorcode: 0
439 Args : reference to the chain, integer, [integer]
440 Note: It works "downstream". To proceed backward use up_get_value_at_pos
441
442 =cut
443
444 #sub get_value_at_pos {
445 #croak "Please use instead: down_get_value_at_pos";
446 ##&down_get_value_at_pos;
447 #}
448 sub down_get_value_at_pos {
449 my ($chain,$position,$first)=@_;
450 my $label=down_get_label_at_pos($chain,$position,$first);
451 # check place of change
452 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
453 warn "not existing element $label"; return (0); }
454 return _get_value($chain,$label);
455 }
456 sub up_get_value_at_pos {
457 my ($chain,$position,$first)=@_;
458 my $label=up_get_label_at_pos($chain,$position,$first);
459 # check place of change
460 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
461 warn "not existing element $label"; return (0); }
462 return _get_value($chain,$label);
463 }
464
465 =head2 down_set_value_at_pos
466
467 Title : down_set_value_at_pos
468 Usage : $errorcode = Bio::LiveSeq::Chain::down_set_value_at_pos($chain,$newvalue,$position,$first)
469 Function: used to store a new value inside an element of the chain at a particular position instead than directly with a label pointer. It will count the position from the start of the chain or from the label $first, if $first is specified
470 Returns : 1
471 Errorcode: 0
472 Args : reference to the chain, newvalue, integer, [integer]
473 (newvalue can be: integer, string, object reference, hash ref)
474 Note: It works "downstream". To proceed backward use up_set_value_at_pos
475 Note2: If the $newvalue is undef, it will delete the contents of the
476 element but it won't remove the element from the chain.
477
478 =cut
479
480 #sub set_value_at_pos {
481 #croak "Please use instead: down_set_value_at_pos";
482 ##&down_set_value_at_pos;
483 #}
484 sub down_set_value_at_pos {
485 my ($chain,$value,$position,$first)=@_;
486 my $label=down_get_label_at_pos($chain,$position,$first);
487 # check place of change
488 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
489 warn "not existing element $label"; return (0); }
490 _set_value($chain,$label,$value);
491 return (1);
492 }
493 sub up_set_value_at_pos {
494 my ($chain,$value,$position,$first)=@_;
495 my $label=up_get_label_at_pos($chain,$position,$first);
496 # check place of change
497 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
498 warn "not existing element $label"; return (0); }
499 _set_value($chain,$label,$value);
500 return (1);
501 }
502
503
504 =head2 down_set_value_at_label
505
506 Title : down_set_value_at_label
507 Usage : $errorcode = Bio::LiveSeq::Chain::down_set_value_at_label($chain,$newvalue,$label)
508 Function: used to store a new value inside an element of the chain defined by its label.
509 Returns : 1
510 Errorcode: 0
511 Args : reference to the chain, newvalue, integer
512 (newvalue can be: integer, string, object reference, hash ref)
513 Note: It works "downstream". To proceed backward use up_set_value_at_label
514 Note2: If the $newvalue is undef, it will delete the contents of the
515 element but it won't remove the element from the chain.
516
517 =cut
518
519 sub set_value_at_label {
520 my ($chain,$value,$label)=@_;
521 unless($chain) { cluck "no chain input"; return (0); }
522
523 # check place of change
524 unless($chain->{$label}) { # complain if label doesn't exist
525 warn "not existing element $label"; return (0); }
526 _set_value($chain,$label,$value);
527 return (1);
528 }
529
530 =head2 down_get_value_at_label
531
532 Title : down_get_value_at_label
533 Usage : $value = Bio::LiveSeq::Chain::down_get_value_at_label($chain,$label)
534 Function: used to access the value of the chain from one element defined by its label.
535 Returns : whatever is stored in the element of the chain
536 Errorcode: 0
537 Args : reference to the chain, integer
538 Note: It works "downstream". To proceed backward use up_get_value_at_label
539
540 =cut
541
542 sub get_value_at_label {
543 my $chain=$_[0];
544 unless($chain) { cluck "no chain input"; return (0); }
545 my $label = $_[1]; # the name of the element
546
547 # check place of change
548 unless($chain->{$label}) { # complain if label doesn't exist
549 warn "not existing label $label"; return (0); }
550 return _get_value($chain,$label);
551 }
552
553 # arguments: CHAIN_REF LABEL VALUE
554 sub _set_value {
555 my ($chain,$label,$value)=@_;
556 $chain->{$label}[0]=$value;
557 }
558 # arguments: CHAIN_REF LABEL
559 sub _get_value {
560 my ($chain,$label)=@_;
561 return $chain->{$label}[0];
562 }
563
564 =head2 down_get_label_at_pos
565
566 Title : down_get_label_at_pos
567 Usage : $label = Bio::LiveSeq::Chain::down_get_label_at_pos($chain,$position,$first)
568 Function: used to retrieve the label of an an element of the chain at a particular position. It will count the position from the start of the chain or from the label $first, if $first is specified
569 Returns : integer
570 Errorcode: 0
571 Args : reference to the chain, integer, [integer]
572 Note: It works "downstream". To proceed backward use up_get_label_at_pos
573
574 =cut
575
576 # arguments: CHAIN_REF POSITION [FIRST]
577 # returns: LABEL of element found counting from FIRST
578 sub down_get_label_at_pos {
579 _updown_get_label_at_pos("down",@_);
580 }
581 sub up_get_label_at_pos {
582 _updown_get_label_at_pos("up",@_);
583 }
584
585 # arguments: [DIRECTION] CHAIN_REF POSITION [FIRST]
586 # Default DIRECTION="down"
587 # if FIRST is undefined, FIRST=START (if DIRECTION=down) or FIRST=END (up)
588
589 sub _updown_get_label_at_pos {
590 my ($direction,$chain,$position,$first)=@_;
591 unless($chain) { cluck "no chain input"; return (0); }
592 my $begin=$chain->{'begin'}; # the label of the BEGIN element
593 my $end=$chain->{'end'}; # the label of the END element
594 my $flow;
595 if ($direction eq "up") { $flow=2; unless ($first) { $first=$end; }
596 } else { $flow=1; unless ($first) { $first=$begin; } }
597 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
598
599 my $label=$first;
600 my $i=1;
601 while ($i < $position) {
602 $label=$chain->{$label}[$flow];
603 $i++;
604 unless ($label) { return (0); } # chain ended before position reached
605 }
606 return ($label);
607 }
608
609 # for english_concerned, latin_unconcerned people
610 sub preinsert_string { &praeinsert_string }
611 sub preinsert_array { &praeinsert_array }
612
613 # praeinsert_string CHAIN_REF STRING [POSITION]
614 # the chars of STRING are passed to praeinsert_array
615 # the chars are inserted in CHAIN, before POSITION
616 # if POSITION is undef, default is to prepend the string to the beginning
617 # i.e. POSITION is START of CHAIN
618 sub praeinsert_string {
619 my @string=split(//,$_[1]);
620 praeinsert_array($_[0],\@string,$_[2]);
621 }
622
623 # postinsert_string CHAIN_REF STRING [POSITION]
624 # the chars of STRING are passed to postinsert_array
625 # the chars are inserted in CHAIN, after POSITION
626 # if POSITION is undef, default is to append the string to the end
627 # i.e. POSITION is END of CHAIN
628 sub postinsert_string {
629 my @string=split(//,$_[1]);
630 postinsert_array($_[0],\@string,$_[2]);
631 }
632
633 # praeinsert_array CHAIN_REF ARRAY_REF [POSITION]
634 # the elements of ARRAY are inserted in CHAIN, before POSITION
635 # if POSITION is undef, default is to prepend the elements to the beginning
636 # i.e. POSITION is START of CHAIN
637 sub praeinsert_array {
638 _praepostinsert_array($_[0],"prae",$_[1],$_[2]);
639 }
640
641 # postinsert_array CHAIN_REF ARRAY_REF [POSITION]
642 # the elements of ARRAY are inserted in CHAIN, after POSITION
643 # if POSITION is undef, default is to append the elements to the end
644 # i.e. POSITION is END of CHAIN
645 sub postinsert_array {
646 _praepostinsert_array($_[0],"post",$_[1],$_[2]);
647 }
648
649
650 =head2 _praepostinsert_array
651
652 Title : _praepostinsert_array
653 Usage : ($insbegin,$insend) = Bio::LiveSeq::Chain::_praepostinsert_array($chainref,"post",$arrayref,$position)
654 Function: the elements of the array specified by $arrayref are inserted (creating a new subchain) in the chain specified by $chainref, before or after (depending on the "prae"||"post" keyword passed as second argument) the specified position.
655 Returns : two labels: the first and the last of the inserted subchain
656 Defaults: if no position is specified, the new chain will be inserted after
657 (post) the first element of the chain
658 Errorcode: 0
659 Args : chainref, "prae"||"post", arrayref, integer (position)
660
661 =cut
662
663 # returns: 0 if errors, otherwise returns references of begin and end of
664 # the insertion
665 sub _praepostinsert_array {
666 my $chain=$_[0];
667 unless($chain) { cluck "no chain input"; return (0); }
668 my $praepost=$_[1] || "post"; # defaults to post
669 my ($prae,$post);
670 my $position=$_[3];
671 my $begin=$chain->{'begin'}; # the name of the first element of the chain
672 my $end=$chain->{'end'}; # the name of the the last element of the chain
673 # check if prae or post insertion and prepare accordingly
674 if ($praepost eq "prae") {
675 $prae=1;
676 unless (($position eq 0)||($position)) { $position=$begin; } # if undef, use $begin
677 } else {
678 $post=1;
679 unless (($position eq 0)||($position)) { $position=$end; } # if undef, use $end
680 }
681 # check place of insertion
682 unless($chain->{$position}) { # complain if position doesn't exist
683 warn ("Warning _praepostinsert_array: not existing element $position");
684 return (0);
685 }
686
687 # check if there are elements to insert
688 my $elements=$_[2]; # reference to the array containing the new elements
689 my $elements_count=scalar(@{$elements});
690 unless ($elements_count) {
691 warn ("Warning _praepostinsert_array: no elements input"); return (0); }
692
693 # create new chainelements with offset=firstfree(chain)
694 my ($insertbegin,$insertend)=_create_chain_elements($chain,$elements);
695
696 # DEBUGGING
697 #print "Executing ${praepost}insertion of $elements_count elements ('@{$elements}') at position: $position\n";
698
699 # attach the new chain to the old chain
700 # 4 cases: prae@begin, prae@middle, post@middle, post@end
701 # NOTE: in case of double joinings always join wisely so not to
702 # delete the PREV/NEXT attribute before it is needed
703 my $noerror=1;
704 if ($prae) {
705 if ($position==$begin) { # 1st case: prae@begin
706 $noerror=_join_chain_elements($chain,$insertend,$begin);
707 $chain->{'begin'}=$insertbegin;
708 } else { # 2nd case: prae@middle
709 $noerror=_join_chain_elements($chain,up_element($chain,$position),$insertbegin);
710 $noerror=_join_chain_elements($chain,$insertend,$position);
711 }
712 } elsif ($post) {
713 if ($position==$end) { # 4th case: post@end
714 $noerror=_join_chain_elements($chain,$end,$insertbegin);
715 $chain->{'end'}=$insertend;
716 } else { # 3rd case: post@middle # note the order of joins (important)
717 $noerror=_join_chain_elements($chain,$insertend,down_element($chain,$position));
718 $noerror=_join_chain_elements($chain,$position,$insertbegin);
719 }
720 } else { # this should never happen
721 die "_praepostinsert_array: Something went very wrong";
722 }
723
724 # check for errors and return begin,end of insertion
725 if ($noerror) {
726 return ($insertbegin,$insertend);
727 } else { # something went wrong with the joinings
728 warn "Warning _praepostinsert_array: Joining of insertion failed";
729 return (0);
730 }
731 }
732
733 # create new chain elements with offset=firstfree
734 # arguments: CHAIN_REF ARRAY_REF
735 # returns: pointers to BEGIN and END of new chained elements created
736 # returns 0 if error(s) encountered
737 sub _create_chain_elements {
738 my $chain=$_[0];
739 unless($chain) {
740 warn ("Warning _create_chain_elements: no chain input"); return (0); }
741 my $arrayref=$_[1];
742 my $array_count=scalar(@{$arrayref});
743 unless ($array_count) {
744 warn ("Warning _create_chain_elements: no elements input"); return (0); }
745 my $begin=$chain->{'firstfree'};
746 my $i=$begin-1;
747 my $element;
748 foreach $element (@{$arrayref}) {
749 $i++;
750 $chain->{$i}=[$element,$i+1,$i-1];
751 }
752 my $end=$i;
753 $chain->{'firstfree'}=$i+1; # what a new added element should be called
754 $chain->{'size'} += $end-$begin+1; # increase size of chain
755 # leave sticky edges (to be joined by whoever called this subroutine)
756 $chain->{$begin}[2]=undef;
757 $chain->{$end}[1]=undef;
758 return ($begin,$end); # return pointers to first and last of the newelements
759 }
760
761 # argument: CHAIN_REF ELEMENT
762 # returns: name of DOWN/NEXT element (the downstream one)
763 # returns -1 if error encountered (e.g. chain or elements undefined)
764 # returns 0 if there's no DOWN element
765 sub down_element {
766 _updown_element("down",@_);
767 }
768 # argument: CHAIN_REF ELEMENT
769 # returns: name of UP/PREV element (the upstream one)
770 # returns -1 if error encountered (e.g. chain or elements undefined)
771 # returns 0 if there's no UP element
772 sub up_element {
773 _updown_element("up",@_);
774 }
775
776 # used by both is_up_element and down_element
777 sub _updown_element {
778 my $direction=$_[0] || "down"; # defaults to downstream
779 my $flow;
780 if ($direction eq "up") {
781 $flow=2; # used to determine the direction of chain navigation
782 } else {
783 $flow=1; # used to determine the direction of chain navigation
784 }
785 my $chain=$_[1];
786 unless($chain) {
787 warn ("Warning ${direction}_element: no chain input"); return (-1); }
788 my $me = $_[2]; # the name of the element
789 my $it = $chain->{$me}[$flow]; # the prev||next one, upstream||downstream
790 if ($it) {
791 return ($it); # return the name of prev||next element
792 } else {
793 return (0); # there is no prev||next element ($it is undef)
794 }
795 }
796
797 # used by both is_downstream and is_upstream
798 sub _is_updownstream {
799 my $direction=$_[0] || "down"; # defaults to downstream
800 my $flow;
801 if ($direction eq "up") {
802 $flow=2; # used to determine the direction of chain navigation
803 } else {
804 $flow=1; # used to determine the direction of chain navigation
805 }
806 my $chain=$_[1];
807 unless($chain) {
808 warn ("Warning is_${direction}stream: no chain input"); return (-1); }
809 my $first=$_[2]; # the name of the first element
810 my $second=$_[3]; # the name of the first element
811 if ($first==$second) {
812 warn ("Warning is_${direction}stream: first==second!!"); return (0); }
813 unless($chain->{$first}) {
814 warn ("Warning is_${direction}stream: first element not defined"); return (-1); }
815 unless($chain->{$second}) {
816 warn ("Warning is_${direction}stream: second element not defined"); return (-1); }
817 my ($label,@array);
818 $label=$first;
819 my $found=0;
820 while (($label)&&(!($found))) { # searches till the end or till found
821 if ($label==$second) {
822 $found=1;
823 }
824 @array=@{$chain->{$label}};
825 $label = $array[$flow]; # go to the prev||next one, upstream||downstream
826 }
827 return $found;
828 }
829
830 =head2 is_downstream
831
832 Title : is_downstream
833 Usage : Bio::LiveSeq::Chain::is_downstream($chainref,$firstlabel,$secondlabel)
834 Function: checks if SECONDlabel follows FIRSTlabel
835 It runs downstream the elements of the chain from FIRST searching
836 for SECOND.
837 Returns : 1 if SECOND is found /after/ FIRST; 0 otherwise (i.e. if it
838 reaches the end of the chain without having found it)
839 Errorcode -1
840 Args : two labels (integer)
841
842 =cut
843
844 sub is_downstream {
845 _is_updownstream("down",@_);
846 }
847
848 =head2 is_upstream
849
850 Title : is_upstream
851 Usage : Bio::LiveSeq::Chain::is_upstream($chainref,$firstlabel,$secondlabel)
852 Function: checks if SECONDlabel follows FIRSTlabel
853 It runs upstream the elements of the chain from FIRST searching
854 for SECOND.
855 Returns : 1 if SECOND is found /after/ FIRST; 0 otherwise (i.e. if it
856 reaches the end of the chain without having found it)
857 Errorcode -1
858 Args : two labels (integer)
859
860 =cut
861
862 sub is_upstream {
863 _is_updownstream("up",@_);
864 }
865
866 =head2 check_chain
867
868 Title : check_chain
869 Usage : @errorcodes = Bio::LiveSeq::Chain::check_chain()
870 Function: a wraparound to a series of check for consistency of the chain
871 It will check for boundaries, size, backlinking and forwardlinking
872 Returns : array of 4 warn codes, each can be 1 (all ok) or 0 (something wrong)
873 Errorcode: 0
874 Args : none
875 Note : this is slow and through. It is not really needed. It is mostly
876 a code-developer tool.
877
878 =cut
879
880 sub check_chain {
881 my $chain=$_[0];
882 unless($chain) {
883 warn ("Warning check_chain: no chain input"); return (-1); }
884 my ($warnbound,$warnsize,$warnbacklink,$warnforlink);
885 $warnbound=&_boundcheck; # passes on the arguments of the subroutine
886 $warnsize=&_sizecheck;
887 $warnbacklink=&_downlinkcheck;
888 $warnforlink=&_uplinkcheck;
889 return ($warnbound,$warnsize,$warnbacklink,$warnforlink);
890 }
891
892 # consistency check for forwardlinks walking upstream
893 # argument: a chain reference
894 # returns: 1 all OK 0 problems
895 sub _uplinkcheck {
896 _updownlinkcheck("up",@_);
897 }
898
899 # consistency check for backlinks walking downstream
900 # argument: a chain reference
901 # returns: 1 all OK 0 problems
902 sub _downlinkcheck {
903 _updownlinkcheck("down",@_);
904 }
905
906 # consistency check for links, common to _uplinkcheck and _downlinkcheck
907 # argument: "up"||"down", check_ref
908 # returns: 1 all OK 0 problems
909 sub _updownlinkcheck {
910 my $direction=$_[0] || "down"; # defaults to downstream
911 my ($flow,$wolf);
912 my $chain=$_[1];
913 unless($chain) {
914 warn ("Warning _${direction}linkcheck: no chain input"); return (0); }
915 my $begin=$chain->{'begin'}; # the name of the first element
916 my $end=$chain->{'end'}; # the name of the last element
917 my ($label,@array,$me,$it,$itpoints);
918 if ($direction eq "up") {
919 $flow=2; # used to determine the direction of chain navigation
920 $wolf=1;
921 $label=$end; # start from end
922 } else {
923 $flow=1; # used to determine the direction of chain navigation
924 $wolf=2;
925 $label=$begin; # start from beginning
926 }
927 my $warncode=1;
928
929 while ($label) { # proceed with linked elements, checking neighbours
930 $me=$label;
931 @array=@{$chain->{$label}};
932 $label = $array[$flow]; # go to the next one
933 $it=$label;
934 if ($it) { # no sense in checking if next one not defined (END element)
935 @array=@{$chain->{$label}};
936 $itpoints=$array[$wolf];
937 unless ($me==$itpoints) {
938 warn "Warning: ${direction}LinkCheck: LINK wrong in $it, that doesn't point back to me ($me). It points to $itpoints\n";
939 $warncode=0;
940 }
941 }
942 }
943 return $warncode;
944 }
945
946 # consistency check for size of chain
947 # argument: a chain reference
948 # returns: 1 all OK 0 wrong size
949 sub _sizecheck {
950 my $chain=$_[0];
951 unless($chain) {
952 warn ("Warning _sizecheck: no chain input"); return (0); }
953 my $begin=$chain->{'begin'}; # the name of the first element
954 my $warncode=1;
955 my ($label,@array);
956 my $size=$chain->{'size'};
957 my $count=0;
958 $label=$begin;
959 while ($label) { # proceed with linked elements, counting
960 @array=@{$chain->{$label}};
961 $label = $array[1]; # go to the next one
962 $count++;
963 }
964 if ($size != $count) {
965 warn "Size check reports error: assumed size: $size, real size: $count ";
966 $warncode=0;
967 }
968 return $warncode;
969 }
970
971
972 # consistency check for begin and end (boundaries)
973 # argument: a chain reference
974 # returns: 1 all OK 0 problems
975 sub _boundcheck {
976 my $chain=$_[0];
977 unless($chain) {
978 warn ("Warning _boundcheck: no chain input"); return (0); }
979 my $begin=$chain->{'begin'}; # the name of the first element
980 my $end=$chain->{'end'}; # the name of the (supposedly) last element
981 my $warncode=1;
982
983 # check SYNC of beginning
984 if (($begin)&&($chain->{$begin})) { # if the BEGIN points to existing element
985 if ($chain->{$begin}[2]) { # if BEGIN element has PREV not undef
986 warn "Warning: BEGIN element has PREV field defined \n";
987 warn "\tWDEBUG begin: $begin\t";
988 warn "\tWDEBUG begin's PREV: $chain->{$begin}[2] \n";
989 $warncode=0;
990 }
991 } else {
992 warn "Warning: BEGIN key of chain does not point to existing element!\n";
993 warn "\tWDEBUG begin: $begin\n";
994 $warncode=0;
995 }
996 # check SYNC of end
997 if (($end)&&($chain->{$end})) { # if the END points to an existing element
998 if ($chain->{$end}[1]) { # if END element has NEXT not undef
999 warn "Warning: END element has NEXT field defined \n";
1000 warn "\tWDEBUG end: $end\t";
1001 warn "\tWDEBUG end's NEXT: $chain->{$end}[1] \n";
1002 $warncode=0;
1003 }
1004 } else {
1005 warn "Warning: END key of chain does not point to existing element!\n";
1006 warn "\tWDEBUG end: $end\n";
1007 $warncode=0;
1008 }
1009 return $warncode;
1010 }
1011
1012 # arguments: chain_ref
1013 # returns: the size of the chain (the number of elements)
1014 # return code -1: unexistant chain, errors...
1015 sub chain_length {
1016 my $chain=$_[0];
1017 unless($chain) {
1018 warn ("Warning chain_length: no chain input"); return (-1); }
1019 my $size=$chain->{'size'};
1020 if ($size) {
1021 return ($size);
1022 } else {
1023 return (-1);
1024 }
1025 }
1026
1027 # arguments: chain ref, first element name, second element name
1028 # returns: 1 or 0 (1 ok, 0 errors)
1029 sub _join_chain_elements {
1030 my $chain=$_[0];
1031 unless($chain) {
1032 warn ("Warning _join_chain_elements: no chain input"); return (0); }
1033 my $leftelem=$_[1];
1034 my $rightelem=$_[2];
1035 unless(($leftelem)&&($rightelem)) {
1036 warn ("Warning _join_chain_elements: element arguments??"); return (0); }
1037 if (($chain->{$leftelem})&&($chain->{$rightelem})) { # if the elements exist
1038 $chain->{$leftelem}[1]=$rightelem;
1039 $chain->{$rightelem}[2]=$leftelem;
1040 return 1;
1041 } else {
1042 warn ("Warning _join_chain_elements: elements not defined");
1043 return 0;
1044 }
1045 }
1046
1047 =head2 splice_chain
1048
1049 Title : splice_chain
1050 Usage : @errorcodes = Bio::LiveSeq::Chain::splice_chain($chainref,$first,$length,$last)
1051 Function: removes the elements designated by FIRST and LENGTH from a chain.
1052 The chain shrinks accordingly. If LENGTH is omitted, removes
1053 everything from FIRST onward.
1054 If END is specified, LENGTH is ignored and instead the removal
1055 occurs from FIRST to LAST.
1056 Returns : the elements removed as a string
1057 Errorcode: -1
1058 Args : chainref, integer, integer, integer
1059
1060 =cut
1061
1062 sub splice_chain {
1063 my $chain=$_[0];
1064 unless($chain) {
1065 warn ("Warning splice_chain: no chain input"); return (-1); }
1066 my $begin=$chain->{'begin'}; # the name of the first element
1067 my $end=$chain->{'end'}; # the name of the (supposedly) last element
1068 my $first=$_[1];
1069 unless (($first eq 0)||($first)) { $first=$begin; } # if undef, use $begin
1070 my $len=$_[2];
1071 my $last=$_[3];
1072 my (@array, $string);
1073 my ($beforecut,$aftercut);
1074
1075 unless($chain->{$first}) {
1076 warn ("Warning splice_chain: first element not defined"); return (-1); }
1077 if ($last) { # if last is defined, it gets priority and len is not used
1078 unless($chain->{$last}) {
1079 warn ("Warning splice_chain: last element not defined"); return (-1); }
1080 if ($len) {
1081 warn ("Warning splice_chain: argument LAST:$last overriding LEN:$len!");
1082 undef $len;
1083 }
1084 } else {
1085 $last=$end; # if last not defined, go 'till end (or to len, whichever 1st)
1086 }
1087
1088 $beforecut=$chain->{$first}[2]; # what's the element before 1st deleted?
1089 # if it is undef then it means we are splicing since the beginning
1090
1091 my $i=1;
1092 my $label=$first;
1093 my $afterlast=$chain->{$last}[1]; # if $last=$end $afterlast should be undef
1094 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
1095
1096 # proceed for len elements or until the end, whichever comes first
1097 # if len undef goes till last
1098 while (($label)&&($label != $afterlast) && ($i <= ($len || $i + 1))) {
1099 @array=@{$chain->{$label}};
1100 $string .= $array[0];
1101 $aftercut = $array[1]; # what's the element next last deleted?
1102 # also used as savevar to change label posdeletion
1103 delete $chain->{$label}; # this can be deleted now
1104 $label=$aftercut; # label is updated using the savevar
1105 $i++;
1106 }
1107
1108 # Now fix the chain (sticky edges, fields)
1109 # 4 cases: cut in the middle, cut from beginning, cut till end, cut all
1110 #print "\n\tstickyDEBUG beforecut: $beforecut "; # DEBUG
1111 #print "\taftercut: $aftercut \n"; # DEBUG
1112 if ($beforecut) {
1113 if ($aftercut) { # 1st case, middle cut
1114 _join_chain_elements($chain,$beforecut,$aftercut);
1115 } else { # 3rd case, end cut
1116 $chain->{'end'}=$beforecut; # update the END field
1117 $chain->{$beforecut}[1]=undef; # since we cut till the end
1118 }
1119 } else {
1120 if ($aftercut) { # 2nd case, begin cut
1121 $chain->{'begin'}=$aftercut; # update the BEGIN field
1122 $chain->{$aftercut}[2]=undef; # since we cut from beginning
1123 } else { # 4th case, all has been cut
1124 $chain->{'begin'}=undef;
1125 $chain->{'end'}=undef;
1126 }
1127 }
1128 $chain->{'size'}=($chain->{'size'}) - $i + 1; # update the SIZE field
1129
1130 return $string;
1131 }
1132
1133
1134 # arguments: CHAIN_REF POSITION [FIRST]
1135 # returns: element counting POSITION from FIRST or from START if FIRST undef
1136 # i.e. returns the element at POSITION counting from FIRST
1137 #sub element_at_pos {
1138 #croak "Warning: old method name. Please update code to 'down_get_label_at_position'\n";
1139 ##&down_element_at_pos;
1140 #}
1141 #sub up_element_at_pos {
1142 ## old wraparound
1143 ##my @array=up_chain2string($_[0],$_[2],$_[1],undef,"elements");
1144 ##return $array[-1];
1145 #croak "old method name. Update code to: up_get_label_at_position";
1146 ##&up_get_label_at_pos;
1147 #}
1148 #sub down_element_at_pos {
1149 ## old wraparound
1150 ##my @array=down_chain2string($_[0],$_[2],$_[1],undef,"elements");
1151 ##return $array[-1];
1152 #croak "old method name. Update code to: down_get_label_at_position";
1153 ##&down_get_label_at_pos;
1154 #}
1155
1156 # arguments: CHAIN_REF ELEMENT [FIRST]
1157 # returns: the position of ELEMENT counting from FIRST or from START
1158 #i if FIRST is undef
1159 # i.e. returns the Number of elements between FIRST and ELEMENT
1160 # i.e. returns the position of element taking FIRST as 1 of coordinate system
1161 #sub pos_of_element {
1162 #croak ("Warning: old and ambiguous method name. Please update code to 'down_get_pos_of_label'\n");
1163 ##&down_pos_of_element;
1164 #}
1165 #sub up_pos_of_element {
1166 #croak ("Warning: old method name. Please update code to 'up_get_pos_of_label'\n");
1167 ##up_chain2string($_[0],$_[2],undef,$_[1],"counting");
1168 #}
1169 #sub down_pos_of_element {
1170 #croak ("Warning: old method name. Please update code to 'down_get_pos_of_label'\n");
1171 ##down_chain2string($_[0],$_[2],undef,$_[1],"counting");
1172 #}
1173
1174 # wraparounds to calculate length of subchain from first to last
1175 # arguments: chain_ref [first] [last]
1176 #sub subchain_length {
1177 #croak "Warning: old method name. Please update code to 'down_subchain_length'\n";
1178 ##&down_subchain_length;
1179 #}
1180
1181 # wraparounds to have elements output
1182 # same arguments as chain2string
1183 # returns label|name of every element
1184 #sub elements {
1185 #croak ("Warning: method no more supported. Please update code to 'down_labels' (NB: now it returns ref to array and doesn't allow length argument!)\n");
1186 ##&down_elements;
1187 #}
1188 #sub up_elements {
1189 #croak ("Warning: method no more supported. Please update code to 'up_labels' (NB: now it returns ref to array and doesn't allow length argument!)\n");
1190 ##up_chain2string($_[0],$_[1],$_[2],$_[3],"elements");
1191 #}
1192 #sub down_elements {
1193 #croak ("Warning: method no more supported. Please update code to 'down_labels' (NB: now it returns ref to array and doesn't allow length argument!)\n");
1194 ##down_chain2string($_[0],$_[1],$_[2],$_[3],"elements");
1195 #}
1196
1197 # wraparounds to have verbose output
1198 # same arguments as chain2string
1199 # returns the chain in a very verbose way
1200 sub chain2string_verbose {
1201 carp "Warning: method no more supported.\n";
1202 &old_down_chain2string_verbose;
1203 }
1204 sub up_chain2string_verbose {
1205 carp "Warning: method no more supported.\n";
1206 old_up_chain2string($_[0],$_[1],$_[2],$_[3],"verbose");
1207 }
1208 sub down_chain2string_verbose {
1209 carp "Warning: method no more supported.\n";
1210 old_down_chain2string($_[0],$_[1],$_[2],$_[3],"verbose");
1211 }
1212
1213 #sub chain2string {
1214 #croak ("Warning: old method name. Please update code to 'down_chain2string'\n");
1215 ##&down_chain2string;
1216 #}
1217 sub old_up_chain2string {
1218 old_updown_chain2string("up",@_);
1219 }
1220 sub old_down_chain2string {
1221 old_updown_chain2string("down",@_);
1222 }
1223
1224 # common to up_chain2string and down_chain2string
1225 # arguments: "up"||"down" chain_ref [first] [len] [last] [option]
1226 # [option] can be any of "verbose", "counting", "elements"
1227 # error: return -1
1228 # defaults: start = first element; if len undef, goes to last
1229 # if last undef, goes to end
1230 # if last def it overrides len (that gets undef)
1231 # returns: a string
1232 # example usage: down_chain2string($chain) -> all the chain from begin to end
1233 # example usage: down_chain2string($chain,6) -> from 6 to the end
1234 # example usage: down_chain2string($chain,6,4) -> from 6, going on 4 elements
1235 # example usage: down_chain2string($chain,6,"",10) -> from 6 to 10
1236 # example usage: up_chain2string($chain,10,"",6) -> from 10 to 6 upstream
1237 sub old_updown_chain2string {
1238 my ($direction,$chain,$first,$len,$last,$option)=@_;
1239 unless($chain) {
1240 warn ("Warning chain2string: no chain input"); return (-1); }
1241 my $begin=$chain->{'begin'}; # the name of the BEGIN element
1242 my $end=$chain->{'end'}; # the name of the END element
1243 my $flow;
1244 if ($direction eq "up") {
1245 $flow=2; # used to determine the direction of chain navigation
1246 unless ($first) { $first=$end; } # if undef or 0, use $end
1247 } else { # defaults to "down"
1248 $flow=1; # used to determine the direction of chain navigation
1249 unless ($first) { $first=$begin; } # if undef or 0, use $begin
1250 }
1251
1252 unless($chain->{$first}) {
1253 warn ("Warning chain2string: first element not defined"); return (-1); }
1254 if ($last) { # if last is defined, it gets priority and len is not used
1255 unless($chain->{$last}) {
1256 warn ("Warning chain2string: last element not defined"); return (-1); }
1257 if ($len) {
1258 warn ("Warning chain2string: argument LAST:$last overriding LEN:$len!");
1259 undef $len;
1260 }
1261 } else {
1262 if ($direction eq "up") {
1263 $last=$begin; # if last not defined, go 'till begin (or upto len elements)
1264 } else {
1265 $last=$end; # if last not defined, go 'till end (or upto len elements)
1266 }
1267 }
1268 my (@array, $string, $count);
1269 # call for verbosity (by way of chain2string_verbose);
1270 my $verbose=0; my $elements=0; my @elements; my $counting=0;
1271 if ($option) { # keep strict happy
1272 if ($option eq "verbose") { $verbose=1; }
1273 if ($option eq "elements") { $elements=1; }
1274 if ($option eq "counting") { $counting=1; }
1275 }
1276
1277 if ($verbose) {
1278 print "BEGIN=$begin"; print " END=$end"; print " SIZE=$chain->{'size'}";
1279 print " FIRSTFREE=$chain->{'firstfree'} \n";
1280 }
1281
1282 my $i=1;
1283 my $label=$first;
1284 my $afterlast=$chain->{$last}[$flow]; # if $last=$end $afterlast should be undef
1285 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
1286
1287 # proceed for len elements or until last, whichever comes first
1288 # if $len undef goes till end
1289 while (($label)&&($label != $afterlast) && ($i <= ($len || $i + 1))) {
1290 @array=@{$chain->{$label}};
1291 if ($verbose) {
1292 $string .= "$array[2]_${label}_$array[1]=$array[0] ";
1293 $count++;
1294 } elsif ($elements) {
1295 push (@elements,$label); # returning element names/references/identifiers
1296 } elsif ($counting) {
1297 $count++;
1298 } else {
1299 $string .= $array[0]; # returning element content
1300 }
1301 $label = $array[$flow]; # go to next||prev i.e. downstream||upstream
1302 $i++;
1303 }
1304 #DEBUG#print "len: $len, first: $first, last: $last, afterlast=$afterlast \n";
1305 if ($verbose) { print "TOTALprinted: $count\n"; }
1306 if ($counting) {
1307 return $count;
1308 } elsif ($elements) {
1309 return @elements;
1310 } else {
1311 return $string;
1312 }
1313 }
1314
1315 # sub string2schain
1316 # --------> deleted, no more supported <--------
1317 # creation of a single linked list/chain from a string
1318 # basically could be recreated by taking the *2chain methods and
1319 # omitting to set the 3rd field (label 2) containing the back links
1320
1321
1322 # creation of a double linked list/chain from a string
1323 # returns reference to a hash containing the chain
1324 # arguments: STRING [OFFSET]
1325 # defaults: OFFSET defaults to 1 if undef
1326 # the chain will contain as elements the single characters in the string
1327 sub string2chain {
1328 my @string=split(//,$_[0]);
1329 array2chain(\@string,$_[1]);
1330 }
1331
1332 =head2 array2chain
1333
1334 Title : array2chain
1335 Usage : $chainref = Bio::LiveSeq::Chain::array2chain($arrayref,$offset)
1336 Function: creation of a double linked chain from an array
1337 Returns : reference to a hash containing the chain
1338 Defaults: OFFSET defaults to 1 if undef
1339 Error code: 0
1340 Args : a reference to an array containing the elements to be chainlinked
1341 an optional integer > 0 (this will be the starting count for
1342 the chain labels instead than having them begin from "1")
1343
1344 =cut
1345
1346 sub array2chain {
1347 my $arrayref=$_[0];
1348 my $array_count=scalar(@{$arrayref});
1349 unless ($array_count) {
1350 warn ("Warning array2chain: no elements input"); return (0); }
1351 my $begin=$_[1];
1352 if (defined $begin) {
1353 if ($begin < 1) {
1354 warn "Warning array2chain: Zero or Negative offsets not allowed"; return (0); }
1355 } else {
1356 $begin=1;
1357 }
1358 my ($element,%hash);
1359 $hash{'begin'}=$begin;
1360 my $i=$begin-1;
1361 foreach $element (@{$arrayref}) {
1362 $i++;
1363 # hash with keys begin..end pointing to the arrays
1364 $hash{$i}=[$element,$i+1,$i-1];
1365 }
1366 my $end=$i;
1367 $hash{'end'}=$end;
1368 $hash{firstfree}=$i+1; # what a new added element should be called
1369 $hash{size}=$end-$begin+1; # how many elements in the chain
1370
1371 # eliminate pointers to unexisting elements
1372 $hash{$begin}[2]=undef;
1373 $hash{$end}[1]=undef;
1374
1375 return (\%hash);
1376 }
1377
1378 1; # returns 1