sync w/ main trunk
[bioperl-live.git] / Bio / LiveSeq / Chain.pm
blob49414f2c8abd6b67223e459daba7b9a5894b9677
1 #!/usr/bin/perl
2 # $Id$
4 # bioperl module for Bio::LiveSeq::Chain
6 # Please direct questions and support issues to <bioperl-l@bioperl.org>
8 # Cared for by Joseph Insana <insana@ebi.ac.uk> <jinsana@gmx.net>
10 # Copyright Joseph Insana
12 # You may distribute this module under the same terms as perl itself
14 # POD documentation - main docs before the code
17 =head1 NAME
19 Bio::LiveSeq::Chain - DoubleChain DataStructure for Perl
21 =head1 SYNOPSIS
23 #documentation needed
25 =head1 DESCRIPTION
27 This is a general purpose module (that's why it's not in object-oriented
28 form) that introduces a novel datastructure in PERL. It implements
29 the "double linked chain". The elements of the chain can contain basically
30 everything. From chars to strings, from object references to arrays or hashes.
31 It is used in the LiveSequence project to create a dynamical DNA sequence,
32 easier to manipulate and change. It's use is mainly for sequence variation
33 analysis but it could be used - for example - in e-cell projects.
34 The Chain module in itself doesn't have any biological bias, so can be
35 used for any programming purpose.
37 Each element of the chain (with the exclusion of the first and the last of the
38 chain) is connected to other two elements (the PREVious and the NEXT one).
39 There is no absolute position (like in an array), hence if positions are
40 important, they need to be computed (methods are provided).
41 Otherwise it's easy to keep track of the elements with their "LABELs".
42 There is one LABEL (think of it as a pointer) to each ELEMENT. The labels
43 won't change after insertions or deletions of the chain. So it's
44 always possible to retrieve an element even if the chain has been
45 modified by successive insertions or deletions.
46 From this the high potential profit for bioinformatics: dealing with
47 sequences in a way that doesn't have to rely on positions, without
48 the need of constantly updating them if the sequence changes, even
49 dramatically.
51 =head1 AUTHOR - Joseph A.L. Insana
53 Email: Insana@ebi.ac.uk, jinsana@gmx.net
55 =head1 APPENDIX
57 The rest of the documentation details each of the object
58 methods. Internal methods are usually preceded with a _
60 =cut
62 # Let the code begin...
64 # DoubleChain Data Structure for PERL
65 # by Joseph A.L. Insana - Deathson - Filius Mortis - Fal Mortais
66 # insana@ebi.ac.uk, jinsana@gmx.net
68 package Bio::LiveSeq::Chain;
69 # TODO_list:
70 # **** cleanup code
71 # **** performance concerns
72 # *??* create hash2dchain ???? (with hashkeys used for label)
73 # **????** how about using array of arrays instead than hash of arrays??
75 # further strict complaints:
76 # in verbose $string assignment around line 721 ???
78 # TERMINOLOGY update, naming convention:
79 # "chain" the datastructure
80 # "element" the individual units that compose a chain
81 # "label" the unique name of a single element
82 # "position" the position of an element into the chain according to a
83 # particular coordinate system (e.g. counting from the start)
84 # "value" what is stored in a single element
86 use Carp qw(croak cluck carp);
87 use Bio::Root::Version;
88 use strict;
89 use integer; # WARNING: this is to increase performance
90 # a little bit of attention has to be given if float need to
91 # be stored as elements of the array
92 # the use of this "integer" affects all operations but not
93 # assignments. So float CAN be assigned as elements of the chain
94 # BUT, if you assign $z=-1.8;, $z will be equal to -1 because
95 # "-" counts as a unary operation!
97 =head2 _updown_chain2string
99 Title : chain2string
100 Usage : $string = Bio::LiveSeq::Chain::chain2string("down",$chain,6,9)
101 Function: reads the contents of the chain, outputting a string
102 Returns : a string
103 Examples:
104 : down_chain2string($chain) -> all the chain from begin to end
105 : down_chain2string($chain,6) -> from 6 to the end
106 : down_chain2string($chain,6,4) -> from 6, going on 4 elements
107 : down_chain2string($chain,6,"",10) -> from 6 to 10
108 : up_chain2string($chain,10,"",6) -> from 10 to 6 upstream
109 Defaults: start=first element; if len undef, goes to last
110 if last undef, goes to end
111 if last defined, it overrides len (undefining it)
112 Error code: -1
113 Args : "up"||"down" as first argument to specify the reading direction
114 reference (to the chain)
115 [first] [len] [last] optional integer arguments to specify how
116 much and from (and to) where to read
118 =cut
120 # methods rewritten 2.61
121 sub up_chain2string {
122 _updown_chain2string("up",@_);
124 sub down_chain2string {
125 _updown_chain2string("down",@_);
128 sub _updown_chain2string {
129 my ($direction,$chain,$first,$len,$last)=@_;
130 unless($chain) { cluck "no chain input"; return (-1); }
131 my $begin=$chain->{'begin'}; # the label of the BEGIN element
132 my $end=$chain->{'end'}; # the label of the END element
133 my $flow;
135 if ($direction eq "up") {
136 $flow=2; # used to determine the direction of chain navigation
137 unless ($first) { $first=$end; } # if undef or 0, use $end
138 } else { # defaults to "down"
139 $flow=1; # used to determine the direction of chain navigation
140 unless ($first) { $first=$begin; } # if undef or 0, use $begin
143 unless($chain->{$first}) {
144 cluck "label for first not defined"; return (-1); }
145 if ($last) { # if last is defined, it gets priority and len is not used
146 unless($chain->{$last}) {
147 cluck "label for last not defined"; return (-1); }
148 if ($len) {
149 warn "Warning chain2string: argument LAST:$last overriding LEN:$len!";
150 undef $len;
152 } else {
153 if ($direction eq "up") {
154 $last=$begin; # if last not defined, go 'till begin (or upto len elements)
155 } else {
156 $last=$end; # if last not defined, go 'till end (or upto len elements)
160 my ($string,@array);
161 my $label=$first; my $i=1;
162 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
163 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
165 # proceed for len elements or until last, whichever comes first
166 # if $len undef goes till end
167 while (($label) && ($label != $afterlast) && ($i <= ($len || $i + 1))) {
168 @array=@{$chain->{$label}};
169 $string .= $array[0];
170 $label = $array[$flow];
171 $i++;
173 return ($string); # if chain is interrupted $string won't be complete
176 =head2 _updown_labels
178 Title : labels
179 Usage : @labels = Bio::LiveSeq::Chain::_updown_labels("down",$chain,4,16)
180 Function: returns all the labels in a chain or those between two
181 specified ones (termed "first" and "last")
182 Returns : a reference to an array containing the labels
183 Args : "up"||"down" as first argument to specify the reading direction
184 reference (to the chain)
185 [first] [last] (integer for the starting and eneding labels)
187 =cut
190 # arguments: CHAIN_REF [FIRSTLABEL] [LASTLABEL]
191 # returns: reference to array containing the labels
192 sub down_labels {
193 my ($chain,$first,$last)=@_;
194 _updown_labels("down",$chain,$first,$last);
196 sub up_labels {
197 my ($chain,$first,$last)=@_;
198 _updown_labels("up",$chain,$first,$last);
200 # arguments: "up"||"down" CHAIN_REF [FIRSTLABEL] [LASTLABEL]
201 # returns: reference to array containing the labels
202 sub _updown_labels {
203 my ($direction,$chain,$first,$last)=@_;
204 unless($chain) { cluck "no chain input"; return (0); }
205 my $begin=$chain->{'begin'}; # the label of the BEGIN element
206 my $end=$chain->{'end'}; # the label of the END element
207 my $flow;
208 if ($direction eq "up") { $flow=2;
209 unless ($first) { $first=$end; }
210 unless ($last) { $last=$begin; }
211 } else { $flow=1;
212 unless ($last) { $last=$end; }
213 unless ($first) { $first=$begin; }
215 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
216 unless($chain->{$last}) { warn "not existing label $last"; return (0); }
218 my $label=$first; my @labels;
219 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
220 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
222 while (($label)&&($label != $afterlast)) {
223 push(@labels,$label);
224 $label=$chain->{$label}[$flow];
226 return (\@labels); # if chain is interrupted @labels won't be complete
230 =head2 start
232 Title : start
233 Usage : $start = Bio::LiveSeq::Chain::start()
234 Returns : the label marking the start of the chain
235 Errorcode: -1
236 Args : none
238 =cut
240 sub start {
241 my $chain=$_[0];
242 unless($chain) { cluck "no chain input"; return (-1); }
243 return ($chain->{'begin'});
246 =head2 end
248 Title : end
249 Usage : $end = Bio::LiveSeq::Chain::end()
250 Returns : the label marking the end of the chain
251 Errorcode: -1
252 Args : none
254 =cut
256 sub end {
257 my $chain=$_[0];
258 unless($chain) { cluck "no chain input"; return (-1); }
259 return ($chain->{'end'});
262 =head2 label_exists
264 Title : label_exists
265 Usage : $check = Bio::LiveSeq::Chain::label_exists($chain,$label)
266 Function: It checks if a label is defined, i.e. if an element is there or
267 is not there anymore
268 Returns : 1 if the label exists, 0 if it is not there, -1 error
269 Errorcode: -1
270 Args : reference to the chain, integer
272 =cut
274 sub label_exists {
275 my ($chain,$label)=@_;
276 unless($chain) { cluck "no chain input"; return (-1); }
277 if ($label && $chain->{$label}) { return (1); } else { return (0) };
281 =head2 down_get_pos_of_label
283 Title : down_get_pos_of_label
284 Usage : $position = Bio::LiveSeq::Chain::down_get_pos_of_label($chain,$label,$first)
285 Function: returns the position of $label counting from $first, i.e. taking
286 $first as 1 of coordinate system. If $first is not specified it will
287 count from the start of the chain.
288 Returns :
289 Errorcode: 0
290 Args : reference to the chain, integer (the label of interest)
291 optional: integer (a different label that will be taken as the
292 first one, i.e. the one to count from)
293 Note: It counts "downstream". To proceed backward use up_get_pos_of_label
295 =cut
297 sub down_get_pos_of_label {
298 #down_chain2string($_[0],$_[2],undef,$_[1],"counting");
299 my ($chain,$label,$first)=@_;
300 _updown_count("down",$chain,$first,$label);
302 sub up_get_pos_of_label {
303 #up_chain2string($_[0],$_[2],undef,$_[1],"counting");
304 my ($chain,$label,$first)=@_;
305 _updown_count("up",$chain,$first,$label);
308 =head2 down_subchain_length
310 Title : down_subchain_length
311 Usage : $length = Bio::LiveSeq::Chain::down_subchain_length($chain,$first,$last)
312 Function: returns the length of the chain between the labels "first" and "last", included
313 Returns : integer
314 Errorcode: 0
315 Args : reference to the chain, integer, integer
316 Note: It counts "downstream". To proceed backward use up_subchain_length
318 =cut
320 # arguments: chain_ref [first] [last]
321 # returns the length of the chain between first and last (included)
322 sub down_subchain_length {
323 #down_chain2string($_[0],$_[1],undef,$_[2],"counting");
324 my ($chain,$first,$last)=@_;
325 _updown_count("down",$chain,$first,$last);
327 sub up_subchain_length {
328 #up_chain2string($_[0],$_[1],undef,$_[2],"counting");
329 my ($chain,$first,$last)=@_;
330 _updown_count("up",$chain,$first,$last);
333 # arguments: DIRECTION CHAIN_REF FIRSTLABEL LASTLABEL
334 # errorcode 0
335 sub _updown_count {
336 my ($direction,$chain,$first,$last)=@_;
337 unless($chain) { cluck "no chain input"; return (0); }
338 my $begin=$chain->{'begin'}; # the label of the BEGIN element
339 my $end=$chain->{'end'}; # the label of the END element
340 my $flow;
341 if ($direction eq "up") { $flow=2;
342 unless ($first) { $first=$end; }
343 unless ($last) { $last=$begin; }
344 } else { $flow=1;
345 unless ($last) { $last=$end; }
346 unless ($first) { $first=$begin; }
348 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
349 unless($chain->{$last}) { warn "not existing label $last"; return (0); }
351 my $label=$first; my $count;
352 my $afterlast=$chain->{$last}[$flow]; # if last=end, afterlast is undef
353 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
355 while (($label)&&($label != $afterlast)) {
356 $count++;
357 $label=$chain->{$label}[$flow];
359 return ($count); # if chain is interrupted, $i will be up to the breaking point
362 =head2 invert_chain
364 Title : invert_chain
365 Usage : $errorcode=Bio::LiveSeq::Chain::invert_chain($chain)
366 Function: completely inverts the order of the chain elements; begin is swapped with end and all links updated (PREV&NEXT fields swapped)
367 Returns : 1 if all OK, 0 if errors
368 Errorcode: 0
369 Args : reference to the chain
371 =cut
373 sub invert_chain {
374 my $chain=$_[0];
375 unless($chain) { cluck "no chain input"; return (0); }
376 my $begin=$chain->{'begin'}; # the name of the first element
377 my $end=$chain->{'end'}; # the name of the last element
378 my ($label,@array);
379 $label=$begin; # starts from the beginning
380 while ($label) { # proceed with linked elements, swapping PREV and NEXT
381 @array=@{$chain->{$label}};
382 ($chain->{$label}[1],$chain->{$label}[2])=($array[2],$array[1]); # swap
383 $label = $array[1]; # go to the next one
385 # now swap begin and end fields
386 ($chain->{'begin'},$chain->{'end'})=($end,$begin);
387 return (1); # that's it
390 # warning that method has changed name
391 #sub mutate_element {
392 #croak "Warning: old method name. Please update code to 'set_value_at_label'\n";
393 # &set_value_at_label;
396 =head2 down_get_value_at_pos
398 Title : down_get_value_at_pos
399 Usage : $value = Bio::LiveSeq::Chain::down_get_value_at_pos($chain,$position,$first)
400 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
401 Returns : whatever is stored in the element of the chain
402 Errorcode: 0
403 Args : reference to the chain, integer, [integer]
404 Note: It works "downstream". To proceed backward use up_get_value_at_pos
406 =cut
408 #sub get_value_at_pos {
409 #croak "Please use instead: down_get_value_at_pos";
410 ##&down_get_value_at_pos;
412 sub down_get_value_at_pos {
413 my ($chain,$position,$first)=@_;
414 my $label=down_get_label_at_pos($chain,$position,$first);
415 # check place of change
416 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
417 warn "not existing element $label"; return (0); }
418 return _get_value($chain,$label);
420 sub up_get_value_at_pos {
421 my ($chain,$position,$first)=@_;
422 my $label=up_get_label_at_pos($chain,$position,$first);
423 # check place of change
424 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
425 warn "not existing element $label"; return (0); }
426 return _get_value($chain,$label);
429 =head2 down_set_value_at_pos
431 Title : down_set_value_at_pos
432 Usage : $errorcode = Bio::LiveSeq::Chain::down_set_value_at_pos($chain,$newvalue,$position,$first)
433 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
434 Returns : 1
435 Errorcode: 0
436 Args : reference to the chain, newvalue, integer, [integer]
437 (newvalue can be: integer, string, object reference, hash ref)
438 Note: It works "downstream". To proceed backward use up_set_value_at_pos
439 Note2: If the $newvalue is undef, it will delete the contents of the
440 element but it won't remove the element from the chain.
442 =cut
444 #sub set_value_at_pos {
445 #croak "Please use instead: down_set_value_at_pos";
446 ##&down_set_value_at_pos;
448 sub down_set_value_at_pos {
449 my ($chain,$value,$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 _set_value($chain,$label,$value);
455 return (1);
457 sub up_set_value_at_pos {
458 my ($chain,$value,$position,$first)=@_;
459 my $label=up_get_label_at_pos($chain,$position,$first);
460 # check place of change
461 if (($label eq -1)||($label eq 0)) { # complain if label doesn't exist
462 warn "not existing element $label"; return (0); }
463 _set_value($chain,$label,$value);
464 return (1);
468 =head2 down_set_value_at_label
470 Title : down_set_value_at_label
471 Usage : $errorcode = Bio::LiveSeq::Chain::down_set_value_at_label($chain,$newvalue,$label)
472 Function: used to store a new value inside an element of the chain defined by its label.
473 Returns : 1
474 Errorcode: 0
475 Args : reference to the chain, newvalue, integer
476 (newvalue can be: integer, string, object reference, hash ref)
477 Note: It works "downstream". To proceed backward use up_set_value_at_label
478 Note2: If the $newvalue is undef, it will delete the contents of the
479 element but it won't remove the element from the chain.
481 =cut
483 sub set_value_at_label {
484 my ($chain,$value,$label)=@_;
485 unless($chain) { cluck "no chain input"; return (0); }
487 # check place of change
488 unless($chain->{$label}) { # complain if label doesn't exist
489 warn "not existing element $label"; return (0); }
490 _set_value($chain,$label,$value);
491 return (1);
494 =head2 down_get_value_at_label
496 Title : down_get_value_at_label
497 Usage : $value = Bio::LiveSeq::Chain::down_get_value_at_label($chain,$label)
498 Function: used to access the value of the chain from one element defined by its label.
499 Returns : whatever is stored in the element of the chain
500 Errorcode: 0
501 Args : reference to the chain, integer
502 Note: It works "downstream". To proceed backward use up_get_value_at_label
504 =cut
506 sub get_value_at_label {
507 my $chain=$_[0];
508 unless($chain) { cluck "no chain input"; return (0); }
509 my $label = $_[1]; # the name of the element
511 # check place of change
512 unless($chain->{$label}) { # complain if label doesn't exist
513 warn "not existing label $label"; return (0); }
514 return _get_value($chain,$label);
517 # arguments: CHAIN_REF LABEL VALUE
518 sub _set_value {
519 my ($chain,$label,$value)=@_;
520 $chain->{$label}[0]=$value;
522 # arguments: CHAIN_REF LABEL
523 sub _get_value {
524 my ($chain,$label)=@_;
525 return $chain->{$label}[0];
528 =head2 down_get_label_at_pos
530 Title : down_get_label_at_pos
531 Usage : $label = Bio::LiveSeq::Chain::down_get_label_at_pos($chain,$position,$first)
532 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
533 Returns : integer
534 Errorcode: 0
535 Args : reference to the chain, integer, [integer]
536 Note: It works "downstream". To proceed backward use up_get_label_at_pos
538 =cut
540 # arguments: CHAIN_REF POSITION [FIRST]
541 # returns: LABEL of element found counting from FIRST
542 sub down_get_label_at_pos {
543 _updown_get_label_at_pos("down",@_);
545 sub up_get_label_at_pos {
546 _updown_get_label_at_pos("up",@_);
549 # arguments: [DIRECTION] CHAIN_REF POSITION [FIRST]
550 # Default DIRECTION="down"
551 # if FIRST is undefined, FIRST=START (if DIRECTION=down) or FIRST=END (up)
553 sub _updown_get_label_at_pos {
554 my ($direction,$chain,$position,$first)=@_;
555 unless($chain) { cluck "no chain input"; return (0); }
556 my $begin=$chain->{'begin'}; # the label of the BEGIN element
557 my $end=$chain->{'end'}; # the label of the END element
558 my $flow;
559 if ($direction eq "up") { $flow=2; unless ($first) { $first=$end; }
560 } else { $flow=1; unless ($first) { $first=$begin; } }
561 unless($chain->{$first}) { warn "not existing label $first"; return (0); }
563 my $label=$first;
564 my $i=1;
565 while ($i < $position) {
566 $label=$chain->{$label}[$flow];
567 $i++;
568 unless ($label) { return (0); } # chain ended before position reached
570 return ($label);
573 # for english_concerned, latin_unconcerned people
574 sub preinsert_string { &praeinsert_string }
575 sub preinsert_array { &praeinsert_array }
577 # praeinsert_string CHAIN_REF STRING [POSITION]
578 # the chars of STRING are passed to praeinsert_array
579 # the chars are inserted in CHAIN, before POSITION
580 # if POSITION is undef, default is to prepend the string to the beginning
581 # i.e. POSITION is START of CHAIN
582 sub praeinsert_string {
583 my @string=split(//,$_[1]);
584 praeinsert_array($_[0],\@string,$_[2]);
587 # postinsert_string CHAIN_REF STRING [POSITION]
588 # the chars of STRING are passed to postinsert_array
589 # the chars are inserted in CHAIN, after POSITION
590 # if POSITION is undef, default is to append the string to the end
591 # i.e. POSITION is END of CHAIN
592 sub postinsert_string {
593 my @string=split(//,$_[1]);
594 postinsert_array($_[0],\@string,$_[2]);
597 # praeinsert_array CHAIN_REF ARRAY_REF [POSITION]
598 # the elements of ARRAY are inserted in CHAIN, before POSITION
599 # if POSITION is undef, default is to prepend the elements to the beginning
600 # i.e. POSITION is START of CHAIN
601 sub praeinsert_array {
602 _praepostinsert_array($_[0],"prae",$_[1],$_[2]);
605 # postinsert_array CHAIN_REF ARRAY_REF [POSITION]
606 # the elements of ARRAY are inserted in CHAIN, after POSITION
607 # if POSITION is undef, default is to append the elements to the end
608 # i.e. POSITION is END of CHAIN
609 sub postinsert_array {
610 _praepostinsert_array($_[0],"post",$_[1],$_[2]);
614 =head2 _praepostinsert_array
616 Title : _praepostinsert_array
617 Usage : ($insbegin,$insend) = Bio::LiveSeq::Chain::_praepostinsert_array($chainref,"post",$arrayref,$position)
618 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.
619 Returns : two labels: the first and the last of the inserted subchain
620 Defaults: if no position is specified, the new chain will be inserted after
621 (post) the first element of the chain
622 Errorcode: 0
623 Args : chainref, "prae"||"post", arrayref, integer (position)
625 =cut
627 # returns: 0 if errors, otherwise returns references of begin and end of
628 # the insertion
629 sub _praepostinsert_array {
630 my $chain=$_[0];
631 unless($chain) { cluck "no chain input"; return (0); }
632 my $praepost=$_[1] || "post"; # defaults to post
633 my ($prae,$post);
634 my $position=$_[3];
635 my $begin=$chain->{'begin'}; # the name of the first element of the chain
636 my $end=$chain->{'end'}; # the name of the the last element of the chain
637 # check if prae or post insertion and prepare accordingly
638 if ($praepost eq "prae") {
639 $prae=1;
640 unless (($position eq 0)||($position)) { $position=$begin; } # if undef, use $begin
641 } else {
642 $post=1;
643 unless (($position eq 0)||($position)) { $position=$end; } # if undef, use $end
645 # check place of insertion
646 unless($chain->{$position}) { # complain if position doesn't exist
647 warn ("Warning _praepostinsert_array: not existing element $position");
648 return (0);
651 # check if there are elements to insert
652 my $elements=$_[2]; # reference to the array containing the new elements
653 my $elements_count=scalar(@{$elements});
654 unless ($elements_count) {
655 warn ("Warning _praepostinsert_array: no elements input"); return (0); }
657 # create new chainelements with offset=firstfree(chain)
658 my ($insertbegin,$insertend)=_create_chain_elements($chain,$elements);
660 # DEBUGGING
661 #print "Executing ${praepost}insertion of $elements_count elements ('@{$elements}') at position: $position\n";
663 # attach the new chain to the old chain
664 # 4 cases: prae@begin, prae@middle, post@middle, post@end
665 # NOTE: in case of double joinings always join wisely so not to
666 # delete the PREV/NEXT attribute before it is needed
667 my $noerror=1;
668 if ($prae) {
669 if ($position==$begin) { # 1st case: prae@begin
670 $noerror=_join_chain_elements($chain,$insertend,$begin);
671 $chain->{'begin'}=$insertbegin;
672 } else { # 2nd case: prae@middle
673 $noerror=_join_chain_elements($chain,up_element($chain,$position),$insertbegin);
674 $noerror=_join_chain_elements($chain,$insertend,$position);
676 } elsif ($post) {
677 if ($position==$end) { # 4th case: post@end
678 $noerror=_join_chain_elements($chain,$end,$insertbegin);
679 $chain->{'end'}=$insertend;
680 } else { # 3rd case: post@middle # note the order of joins (important)
681 $noerror=_join_chain_elements($chain,$insertend,down_element($chain,$position));
682 $noerror=_join_chain_elements($chain,$position,$insertbegin);
684 } else { # this should never happen
685 die "_praepostinsert_array: Something went very wrong";
688 # check for errors and return begin,end of insertion
689 if ($noerror) {
690 return ($insertbegin,$insertend);
691 } else { # something went wrong with the joinings
692 warn "Warning _praepostinsert_array: Joining of insertion failed";
693 return (0);
697 # create new chain elements with offset=firstfree
698 # arguments: CHAIN_REF ARRAY_REF
699 # returns: pointers to BEGIN and END of new chained elements created
700 # returns 0 if error(s) encountered
701 sub _create_chain_elements {
702 my $chain=$_[0];
703 unless($chain) {
704 warn ("Warning _create_chain_elements: no chain input"); return (0); }
705 my $arrayref=$_[1];
706 my $array_count=scalar(@{$arrayref});
707 unless ($array_count) {
708 warn ("Warning _create_chain_elements: no elements input"); return (0); }
709 my $begin=$chain->{'firstfree'};
710 my $i=$begin-1;
711 my $element;
712 foreach $element (@{$arrayref}) {
713 $i++;
714 $chain->{$i}=[$element,$i+1,$i-1];
716 my $end=$i;
717 $chain->{'firstfree'}=$i+1; # what a new added element should be called
718 $chain->{'size'} += $end-$begin+1; # increase size of chain
719 # leave sticky edges (to be joined by whoever called this subroutine)
720 $chain->{$begin}[2]=undef;
721 $chain->{$end}[1]=undef;
722 return ($begin,$end); # return pointers to first and last of the newelements
725 # argument: CHAIN_REF ELEMENT
726 # returns: name of DOWN/NEXT element (the downstream one)
727 # returns -1 if error encountered (e.g. chain or elements undefined)
728 # returns 0 if there's no DOWN element
729 sub down_element {
730 _updown_element("down",@_);
732 # argument: CHAIN_REF ELEMENT
733 # returns: name of UP/PREV element (the upstream one)
734 # returns -1 if error encountered (e.g. chain or elements undefined)
735 # returns 0 if there's no UP element
736 sub up_element {
737 _updown_element("up",@_);
740 # used by both is_up_element and down_element
741 sub _updown_element {
742 my $direction=$_[0] || "down"; # defaults to downstream
743 my $flow;
744 if ($direction eq "up") {
745 $flow=2; # used to determine the direction of chain navigation
746 } else {
747 $flow=1; # used to determine the direction of chain navigation
749 my $chain=$_[1];
750 unless($chain) {
751 warn ("Warning ${direction}_element: no chain input"); return (-1); }
752 my $me = $_[2]; # the name of the element
753 my $it = $chain->{$me}[$flow]; # the prev||next one, upstream||downstream
754 if ($it) {
755 return ($it); # return the name of prev||next element
756 } else {
757 return (0); # there is no prev||next element ($it is undef)
761 # used by both is_downstream and is_upstream
762 sub _is_updownstream {
763 my $direction=$_[0] || "down"; # defaults to downstream
764 my $flow;
765 if ($direction eq "up") {
766 $flow=2; # used to determine the direction of chain navigation
767 } else {
768 $flow=1; # used to determine the direction of chain navigation
770 my $chain=$_[1];
771 unless($chain) {
772 warn ("Warning is_${direction}stream: no chain input"); return (-1); }
773 my $first=$_[2]; # the name of the first element
774 my $second=$_[3]; # the name of the first element
775 if ($first==$second) {
776 warn ("Warning is_${direction}stream: first==second!!"); return (0); }
777 unless($chain->{$first}) {
778 warn ("Warning is_${direction}stream: first element not defined"); return (-1); }
779 unless($chain->{$second}) {
780 warn ("Warning is_${direction}stream: second element not defined"); return (-1); }
781 my ($label,@array);
782 $label=$first;
783 my $found=0;
784 while (($label)&&(!($found))) { # searches till the end or till found
785 if ($label==$second) {
786 $found=1;
788 @array=@{$chain->{$label}};
789 $label = $array[$flow]; # go to the prev||next one, upstream||downstream
791 return $found;
794 =head2 is_downstream
796 Title : is_downstream
797 Usage : Bio::LiveSeq::Chain::is_downstream($chainref,$firstlabel,$secondlabel)
798 Function: checks if SECONDlabel follows FIRSTlabel
799 It runs downstream the elements of the chain from FIRST searching
800 for SECOND.
801 Returns : 1 if SECOND is found /after/ FIRST; 0 otherwise (i.e. if it
802 reaches the end of the chain without having found it)
803 Errorcode -1
804 Args : two labels (integer)
806 =cut
808 sub is_downstream {
809 _is_updownstream("down",@_);
812 =head2 is_upstream
814 Title : is_upstream
815 Usage : Bio::LiveSeq::Chain::is_upstream($chainref,$firstlabel,$secondlabel)
816 Function: checks if SECONDlabel follows FIRSTlabel
817 It runs upstream the elements of the chain from FIRST searching
818 for SECOND.
819 Returns : 1 if SECOND is found /after/ FIRST; 0 otherwise (i.e. if it
820 reaches the end of the chain without having found it)
821 Errorcode -1
822 Args : two labels (integer)
824 =cut
826 sub is_upstream {
827 _is_updownstream("up",@_);
830 =head2 check_chain
832 Title : check_chain
833 Usage : @errorcodes = Bio::LiveSeq::Chain::check_chain()
834 Function: a wraparound to a series of check for consistency of the chain
835 It will check for boundaries, size, backlinking and forwardlinking
836 Returns : array of 4 warn codes, each can be 1 (all ok) or 0 (something wrong)
837 Errorcode: 0
838 Args : none
839 Note : this is slow and through. It is not really needed. It is mostly
840 a code-developer tool.
842 =cut
844 sub check_chain {
845 my $chain=$_[0];
846 unless($chain) {
847 warn ("Warning check_chain: no chain input"); return (-1); }
848 my ($warnbound,$warnsize,$warnbacklink,$warnforlink);
849 $warnbound=&_boundcheck; # passes on the arguments of the subroutine
850 $warnsize=&_sizecheck;
851 $warnbacklink=&_downlinkcheck;
852 $warnforlink=&_uplinkcheck;
853 return ($warnbound,$warnsize,$warnbacklink,$warnforlink);
856 # consistency check for forwardlinks walking upstream
857 # argument: a chain reference
858 # returns: 1 all OK 0 problems
859 sub _uplinkcheck {
860 _updownlinkcheck("up",@_);
863 # consistency check for backlinks walking downstream
864 # argument: a chain reference
865 # returns: 1 all OK 0 problems
866 sub _downlinkcheck {
867 _updownlinkcheck("down",@_);
870 # consistency check for links, common to _uplinkcheck and _downlinkcheck
871 # argument: "up"||"down", check_ref
872 # returns: 1 all OK 0 problems
873 sub _updownlinkcheck {
874 my $direction=$_[0] || "down"; # defaults to downstream
875 my ($flow,$wolf);
876 my $chain=$_[1];
877 unless($chain) {
878 warn ("Warning _${direction}linkcheck: no chain input"); return (0); }
879 my $begin=$chain->{'begin'}; # the name of the first element
880 my $end=$chain->{'end'}; # the name of the last element
881 my ($label,@array,$me,$it,$itpoints);
882 if ($direction eq "up") {
883 $flow=2; # used to determine the direction of chain navigation
884 $wolf=1;
885 $label=$end; # start from end
886 } else {
887 $flow=1; # used to determine the direction of chain navigation
888 $wolf=2;
889 $label=$begin; # start from beginning
891 my $warncode=1;
893 while ($label) { # proceed with linked elements, checking neighbours
894 $me=$label;
895 @array=@{$chain->{$label}};
896 $label = $array[$flow]; # go to the next one
897 $it=$label;
898 if ($it) { # no sense in checking if next one not defined (END element)
899 @array=@{$chain->{$label}};
900 $itpoints=$array[$wolf];
901 unless ($me==$itpoints) {
902 warn "Warning: ${direction}LinkCheck: LINK wrong in $it, that doesn't point back to me ($me). It points to $itpoints\n";
903 $warncode=0;
907 return $warncode;
910 # consistency check for size of chain
911 # argument: a chain reference
912 # returns: 1 all OK 0 wrong size
913 sub _sizecheck {
914 my $chain=$_[0];
915 unless($chain) {
916 warn ("Warning _sizecheck: no chain input"); return (0); }
917 my $begin=$chain->{'begin'}; # the name of the first element
918 my $warncode=1;
919 my ($label,@array);
920 my $size=$chain->{'size'};
921 my $count=0;
922 $label=$begin;
923 while ($label) { # proceed with linked elements, counting
924 @array=@{$chain->{$label}};
925 $label = $array[1]; # go to the next one
926 $count++;
928 if ($size != $count) {
929 warn "Size check reports error: assumed size: $size, real size: $count ";
930 $warncode=0;
932 return $warncode;
936 # consistency check for begin and end (boundaries)
937 # argument: a chain reference
938 # returns: 1 all OK 0 problems
939 sub _boundcheck {
940 my $chain=$_[0];
941 unless($chain) {
942 warn ("Warning _boundcheck: no chain input"); return (0); }
943 my $begin=$chain->{'begin'}; # the name of the first element
944 my $end=$chain->{'end'}; # the name of the (supposedly) last element
945 my $warncode=1;
947 # check SYNC of beginning
948 if (($begin)&&($chain->{$begin})) { # if the BEGIN points to existing element
949 if ($chain->{$begin}[2]) { # if BEGIN element has PREV not undef
950 warn "Warning: BEGIN element has PREV field defined \n";
951 warn "\tWDEBUG begin: $begin\t";
952 warn "\tWDEBUG begin's PREV: $chain->{$begin}[2] \n";
953 $warncode=0;
955 } else {
956 warn "Warning: BEGIN key of chain does not point to existing element!\n";
957 warn "\tWDEBUG begin: $begin\n";
958 $warncode=0;
960 # check SYNC of end
961 if (($end)&&($chain->{$end})) { # if the END points to an existing element
962 if ($chain->{$end}[1]) { # if END element has NEXT not undef
963 warn "Warning: END element has NEXT field defined \n";
964 warn "\tWDEBUG end: $end\t";
965 warn "\tWDEBUG end's NEXT: $chain->{$end}[1] \n";
966 $warncode=0;
968 } else {
969 warn "Warning: END key of chain does not point to existing element!\n";
970 warn "\tWDEBUG end: $end\n";
971 $warncode=0;
973 return $warncode;
976 # arguments: chain_ref
977 # returns: the size of the chain (the number of elements)
978 # return code -1: unexistant chain, errors...
979 sub chain_length {
980 my $chain=$_[0];
981 unless($chain) {
982 warn ("Warning chain_length: no chain input"); return (-1); }
983 my $size=$chain->{'size'};
984 if ($size) {
985 return ($size);
986 } else {
987 return (-1);
991 # arguments: chain ref, first element name, second element name
992 # returns: 1 or 0 (1 ok, 0 errors)
993 sub _join_chain_elements {
994 my $chain=$_[0];
995 unless($chain) {
996 warn ("Warning _join_chain_elements: no chain input"); return (0); }
997 my $leftelem=$_[1];
998 my $rightelem=$_[2];
999 unless(($leftelem)&&($rightelem)) {
1000 warn ("Warning _join_chain_elements: element arguments??"); return (0); }
1001 if (($chain->{$leftelem})&&($chain->{$rightelem})) { # if the elements exist
1002 $chain->{$leftelem}[1]=$rightelem;
1003 $chain->{$rightelem}[2]=$leftelem;
1004 return 1;
1005 } else {
1006 warn ("Warning _join_chain_elements: elements not defined");
1007 return 0;
1011 =head2 splice_chain
1013 Title : splice_chain
1014 Usage : @errorcodes = Bio::LiveSeq::Chain::splice_chain($chainref,$first,$length,$last)
1015 Function: removes the elements designated by FIRST and LENGTH from a chain.
1016 The chain shrinks accordingly. If LENGTH is omitted, removes
1017 everything from FIRST onward.
1018 If END is specified, LENGTH is ignored and instead the removal
1019 occurs from FIRST to LAST.
1020 Returns : the elements removed as a string
1021 Errorcode: -1
1022 Args : chainref, integer, integer, integer
1024 =cut
1026 sub splice_chain {
1027 my $chain=$_[0];
1028 unless($chain) {
1029 warn ("Warning splice_chain: no chain input"); return (-1); }
1030 my $begin=$chain->{'begin'}; # the name of the first element
1031 my $end=$chain->{'end'}; # the name of the (supposedly) last element
1032 my $first=$_[1];
1033 unless (($first eq 0)||($first)) { $first=$begin; } # if undef, use $begin
1034 my $len=$_[2];
1035 my $last=$_[3];
1036 my (@array, $string);
1037 my ($beforecut,$aftercut);
1039 unless($chain->{$first}) {
1040 warn ("Warning splice_chain: first element not defined"); return (-1); }
1041 if ($last) { # if last is defined, it gets priority and len is not used
1042 unless($chain->{$last}) {
1043 warn ("Warning splice_chain: last element not defined"); return (-1); }
1044 if ($len) {
1045 warn ("Warning splice_chain: argument LAST:$last overriding LEN:$len!");
1046 undef $len;
1048 } else {
1049 $last=$end; # if last not defined, go 'till end (or to len, whichever 1st)
1052 $beforecut=$chain->{$first}[2]; # what's the element before 1st deleted?
1053 # if it is undef then it means we are splicing since the beginning
1055 my $i=1;
1056 my $label=$first;
1057 my $afterlast=$chain->{$last}[1]; # if $last=$end $afterlast should be undef
1058 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
1060 # proceed for len elements or until the end, whichever comes first
1061 # if len undef goes till last
1062 while (($label)&&($label != $afterlast) && ($i <= ($len || $i + 1))) {
1063 @array=@{$chain->{$label}};
1064 $string .= $array[0];
1065 $aftercut = $array[1]; # what's the element next last deleted?
1066 # also used as savevar to change label posdeletion
1067 delete $chain->{$label}; # this can be deleted now
1068 $label=$aftercut; # label is updated using the savevar
1069 $i++;
1072 # Now fix the chain (sticky edges, fields)
1073 # 4 cases: cut in the middle, cut from beginning, cut till end, cut all
1074 #print "\n\tstickyDEBUG beforecut: $beforecut "; # DEBUG
1075 #print "\taftercut: $aftercut \n"; # DEBUG
1076 if ($beforecut) {
1077 if ($aftercut) { # 1st case, middle cut
1078 _join_chain_elements($chain,$beforecut,$aftercut);
1079 } else { # 3rd case, end cut
1080 $chain->{'end'}=$beforecut; # update the END field
1081 $chain->{$beforecut}[1]=undef; # since we cut till the end
1083 } else {
1084 if ($aftercut) { # 2nd case, begin cut
1085 $chain->{'begin'}=$aftercut; # update the BEGIN field
1086 $chain->{$aftercut}[2]=undef; # since we cut from beginning
1087 } else { # 4th case, all has been cut
1088 $chain->{'begin'}=undef;
1089 $chain->{'end'}=undef;
1092 $chain->{'size'}=($chain->{'size'}) - $i + 1; # update the SIZE field
1094 return $string;
1098 # arguments: CHAIN_REF POSITION [FIRST]
1099 # returns: element counting POSITION from FIRST or from START if FIRST undef
1100 # i.e. returns the element at POSITION counting from FIRST
1101 #sub element_at_pos {
1102 #croak "Warning: old method name. Please update code to 'down_get_label_at_position'\n";
1103 ##&down_element_at_pos;
1105 #sub up_element_at_pos {
1106 ## old wraparound
1107 ##my @array=up_chain2string($_[0],$_[2],$_[1],undef,"elements");
1108 ##return $array[-1];
1109 #croak "old method name. Update code to: up_get_label_at_position";
1110 ##&up_get_label_at_pos;
1112 #sub down_element_at_pos {
1113 ## old wraparound
1114 ##my @array=down_chain2string($_[0],$_[2],$_[1],undef,"elements");
1115 ##return $array[-1];
1116 #croak "old method name. Update code to: down_get_label_at_position";
1117 ##&down_get_label_at_pos;
1120 # arguments: CHAIN_REF ELEMENT [FIRST]
1121 # returns: the position of ELEMENT counting from FIRST or from START
1122 #i if FIRST is undef
1123 # i.e. returns the Number of elements between FIRST and ELEMENT
1124 # i.e. returns the position of element taking FIRST as 1 of coordinate system
1125 #sub pos_of_element {
1126 #croak ("Warning: old and ambiguous method name. Please update code to 'down_get_pos_of_label'\n");
1127 ##&down_pos_of_element;
1129 #sub up_pos_of_element {
1130 #croak ("Warning: old method name. Please update code to 'up_get_pos_of_label'\n");
1131 ##up_chain2string($_[0],$_[2],undef,$_[1],"counting");
1133 #sub down_pos_of_element {
1134 #croak ("Warning: old method name. Please update code to 'down_get_pos_of_label'\n");
1135 ##down_chain2string($_[0],$_[2],undef,$_[1],"counting");
1138 # wraparounds to calculate length of subchain from first to last
1139 # arguments: chain_ref [first] [last]
1140 #sub subchain_length {
1141 #croak "Warning: old method name. Please update code to 'down_subchain_length'\n";
1142 ##&down_subchain_length;
1145 # wraparounds to have elements output
1146 # same arguments as chain2string
1147 # returns label|name of every element
1148 #sub elements {
1149 #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");
1150 ##&down_elements;
1152 #sub up_elements {
1153 #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");
1154 ##up_chain2string($_[0],$_[1],$_[2],$_[3],"elements");
1156 #sub down_elements {
1157 #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");
1158 ##down_chain2string($_[0],$_[1],$_[2],$_[3],"elements");
1161 # wraparounds to have verbose output
1162 # same arguments as chain2string
1163 # returns the chain in a very verbose way
1164 sub chain2string_verbose {
1165 carp "Warning: method no more supported.\n";
1166 &old_down_chain2string_verbose;
1168 sub up_chain2string_verbose {
1169 carp "Warning: method no more supported.\n";
1170 old_up_chain2string($_[0],$_[1],$_[2],$_[3],"verbose");
1172 sub down_chain2string_verbose {
1173 carp "Warning: method no more supported.\n";
1174 old_down_chain2string($_[0],$_[1],$_[2],$_[3],"verbose");
1177 #sub chain2string {
1178 #croak ("Warning: old method name. Please update code to 'down_chain2string'\n");
1179 ##&down_chain2string;
1181 sub old_up_chain2string {
1182 old_updown_chain2string("up",@_);
1184 sub old_down_chain2string {
1185 old_updown_chain2string("down",@_);
1188 # common to up_chain2string and down_chain2string
1189 # arguments: "up"||"down" chain_ref [first] [len] [last] [option]
1190 # [option] can be any of "verbose", "counting", "elements"
1191 # error: return -1
1192 # defaults: start = first element; if len undef, goes to last
1193 # if last undef, goes to end
1194 # if last def it overrides len (that gets undef)
1195 # returns: a string
1196 # example usage: down_chain2string($chain) -> all the chain from begin to end
1197 # example usage: down_chain2string($chain,6) -> from 6 to the end
1198 # example usage: down_chain2string($chain,6,4) -> from 6, going on 4 elements
1199 # example usage: down_chain2string($chain,6,"",10) -> from 6 to 10
1200 # example usage: up_chain2string($chain,10,"",6) -> from 10 to 6 upstream
1201 sub old_updown_chain2string {
1202 my ($direction,$chain,$first,$len,$last,$option)=@_;
1203 unless($chain) {
1204 warn ("Warning chain2string: no chain input"); return (-1); }
1205 my $begin=$chain->{'begin'}; # the name of the BEGIN element
1206 my $end=$chain->{'end'}; # the name of the END element
1207 my $flow;
1208 if ($direction eq "up") {
1209 $flow=2; # used to determine the direction of chain navigation
1210 unless ($first) { $first=$end; } # if undef or 0, use $end
1211 } else { # defaults to "down"
1212 $flow=1; # used to determine the direction of chain navigation
1213 unless ($first) { $first=$begin; } # if undef or 0, use $begin
1216 unless($chain->{$first}) {
1217 warn ("Warning chain2string: first element not defined"); return (-1); }
1218 if ($last) { # if last is defined, it gets priority and len is not used
1219 unless($chain->{$last}) {
1220 warn ("Warning chain2string: last element not defined"); return (-1); }
1221 if ($len) {
1222 warn ("Warning chain2string: argument LAST:$last overriding LEN:$len!");
1223 undef $len;
1225 } else {
1226 if ($direction eq "up") {
1227 $last=$begin; # if last not defined, go 'till begin (or upto len elements)
1228 } else {
1229 $last=$end; # if last not defined, go 'till end (or upto len elements)
1232 my (@array, $string, $count);
1233 # call for verbosity (by way of chain2string_verbose);
1234 my $verbose=0; my $elements=0; my @elements; my $counting=0;
1235 if ($option) { # keep strict happy
1236 if ($option eq "verbose") { $verbose=1; }
1237 if ($option eq "elements") { $elements=1; }
1238 if ($option eq "counting") { $counting=1; }
1241 if ($verbose) {
1242 print "BEGIN=$begin"; print " END=$end"; print " SIZE=$chain->{'size'}";
1243 print " FIRSTFREE=$chain->{'firstfree'} \n";
1246 my $i=1;
1247 my $label=$first;
1248 my $afterlast=$chain->{$last}[$flow]; # if $last=$end $afterlast should be undef
1249 unless (defined $afterlast) { $afterlast=0; } # keep strict happy
1251 # proceed for len elements or until last, whichever comes first
1252 # if $len undef goes till end
1253 while (($label)&&($label != $afterlast) && ($i <= ($len || $i + 1))) {
1254 @array=@{$chain->{$label}};
1255 if ($verbose) {
1256 $string .= "$array[2]_${label}_$array[1]=$array[0] ";
1257 $count++;
1258 } elsif ($elements) {
1259 push (@elements,$label); # returning element names/references/identifiers
1260 } elsif ($counting) {
1261 $count++;
1262 } else {
1263 $string .= $array[0]; # returning element content
1265 $label = $array[$flow]; # go to next||prev i.e. downstream||upstream
1266 $i++;
1268 #DEBUG#print "len: $len, first: $first, last: $last, afterlast=$afterlast \n";
1269 if ($verbose) { print "TOTALprinted: $count\n"; }
1270 if ($counting) {
1271 return $count;
1272 } elsif ($elements) {
1273 return @elements;
1274 } else {
1275 return $string;
1279 # sub string2schain
1280 # --------> deleted, no more supported <--------
1281 # creation of a single linked list/chain from a string
1282 # basically could be recreated by taking the *2chain methods and
1283 # omitting to set the 3rd field (label 2) containing the back links
1286 # creation of a double linked list/chain from a string
1287 # returns reference to a hash containing the chain
1288 # arguments: STRING [OFFSET]
1289 # defaults: OFFSET defaults to 1 if undef
1290 # the chain will contain as elements the single characters in the string
1291 sub string2chain {
1292 my @string=split(//,$_[0]);
1293 array2chain(\@string,$_[1]);
1296 =head2 array2chain
1298 Title : array2chain
1299 Usage : $chainref = Bio::LiveSeq::Chain::array2chain($arrayref,$offset)
1300 Function: creation of a double linked chain from an array
1301 Returns : reference to a hash containing the chain
1302 Defaults: OFFSET defaults to 1 if undef
1303 Error code: 0
1304 Args : a reference to an array containing the elements to be chainlinked
1305 an optional integer > 0 (this will be the starting count for
1306 the chain labels instead than having them begin from "1")
1308 =cut
1310 sub array2chain {
1311 my $arrayref=$_[0];
1312 my $array_count=scalar(@{$arrayref});
1313 unless ($array_count) {
1314 warn ("Warning array2chain: no elements input"); return (0); }
1315 my $begin=$_[1];
1316 if (defined $begin) {
1317 if ($begin < 1) {
1318 warn "Warning array2chain: Zero or Negative offsets not allowed"; return (0); }
1319 } else {
1320 $begin=1;
1322 my ($element,%hash);
1323 $hash{'begin'}=$begin;
1324 my $i=$begin-1;
1325 foreach $element (@{$arrayref}) {
1326 $i++;
1327 # hash with keys begin..end pointing to the arrays
1328 $hash{$i}=[$element,$i+1,$i-1];
1330 my $end=$i;
1331 $hash{'end'}=$end;
1332 $hash{firstfree}=$i+1; # what a new added element should be called
1333 $hash{size}=$end-$begin+1; # how many elements in the chain
1335 # eliminate pointers to unexisting elements
1336 $hash{$begin}[2]=undef;
1337 $hash{$end}[1]=undef;
1339 return (\%hash);
1342 1; # returns 1