~ubuntu-branches/ubuntu/oneiric/bioperl/oneiric

« back to all changes in this revision

Viewing changes to Bio/Restriction/Analysis.pm

  • Committer: Bazaar Package Importer
  • Author(s): Matt Hope
  • Date: 2004-04-18 14:24:11 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040418142411-gr92uexquw4w8liq
Tags: 1.4-1
* New upstream release
* Examples and working code are installed by default to usr/bin,
  this has been moved to usr/share/doc/bioperl/bin

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#------------------------------------------------------------------
 
2
#
 
3
# BioPerl module Bio::Restriction::Analysis
 
4
#
 
5
# Cared for by Rob Edwards <redwards@utmem.edu>
 
6
#
 
7
# You may distribute this module under the same terms as perl itself
 
8
#------------------------------------------------------------------
 
9
 
 
10
## POD Documentation:
 
11
 
 
12
=head1 NAME
 
13
 
 
14
Bio::Restriction::Analysis - cutting sequences with restriction
 
15
enzymes
 
16
 
 
17
=head1 SYNOPSIS
 
18
 
 
19
  # analyze a DNA sequence for restriction enzymes
 
20
  use Bio::Restriction::Analysis;
 
21
  use Bio::PrimarySeq;
 
22
  use Data::Dumper;
 
23
 
 
24
  # get a DNA sequence from somewhere
 
25
  my $seq=new Bio::PrimarySeq
 
26
      (-seq =>'AGCTTAATTCATTAGCTCTGACTGCAACGGGCAATATGTCTC'.
 
27
       'TGTGTGGATTAAAAAAAGAGTGAGCTTCTGATAGCAGC',
 
28
       -primary_id => 'synopsis',
 
29
       -molecule => 'dna');
 
30
 
 
31
  # now start an analysis.
 
32
  # this is using the default set of enzymes
 
33
  my $ra=Bio::Restriction::Analysis->new(-seq=>$seq);
 
34
 
 
35
  # find unique cutters. This returns a
 
36
  # Bio::Restriction::EnzymeCollection object
 
37
  my $enzymes=$ra->unique_cutters;
 
38
  print "Unique cutters: ", join (', ', 
 
39
      map {$_->name} $enzymes->unique_cutters), "\n";
 
40
 
 
41
  # AluI is one them. Where does it cut?
 
42
  # This is will return an array of the sequence strings
 
43
 
 
44
  my $enz = 'AluI';
 
45
  my @frags=$ra->fragments($enz);
 
46
  # how big are the fragments?
 
47
  print "AluI fragment lengths: ", join(' & ', map {length $_} @frags), "\n";
 
48
 
 
49
  # You can also bypass fragments and call sizes directly:
 
50
  # to see all the fragment sizes
 
51
  print "All sizes: ", join " ", $ra->sizes($enz), "\n";
 
52
  # to see all the fragment sizes sorted by size like on a gel
 
53
  print "All sizes, sorted ", join (" ", $ra->sizes($enz, 0, 1)), "\n";
 
54
 
 
55
  # how many times does each enzyme cut
 
56
  my $cuts=$ra->cuts_by_enzyme('BamHI');
 
57
  print "BamHI cuts $cuts times\n";
 
58
 
 
59
  # How many enzymes do not cut at all?
 
60
  print "There are ", scalar $ra->zero_cutters->each_enzyme,
 
61
        " enzymes that do not cut\n";
 
62
 
 
63
  # what about enzymes that cut twice?
 
64
  my $two_cutters=$ra->cutters(2);
 
65
  print join (" ", map {$_->name} $two_cutters->each_enzyme),
 
66
      " cut the sequence twice\n";
 
67
 
 
68
  # what are all the enzymes that cut, and how often do they cut
 
69
  printf "\n%-10s%s\n", 'Enzyme', 'Number of Cuts';
 
70
  my $all_cutters=$ra->cutters;
 
71
  map {
 
72
      printf "%-10s%s\n", $_->name, $ra->cuts_by_enzyme($_->name)
 
73
  } $all_cutters->each_enzyme;
 
74
 
 
75
  # Finally, we can interact the restriction enzyme object by
 
76
  # retrieving it from the collection object see the docs for
 
77
  # Bio::Restriction::Enzyme.pm
 
78
  my $enzobj=$enzymes->get_enzyme($enz);
 
79
 
 
80
 
 
81
=head1 DESCRIPTION
 
82
 
 
83
Bio::Restriction::Analysis describes the results of cutting a DNA
 
84
sequence with restriction enzymes.
 
85
 
 
86
To use this module you can pass a sequence object and optionally a
 
87
Bio::Restriction::EnzymeCollection that contains the enzyme(s) to cut the
 
88
sequences with. There is a default set of enzymes that will be loaded
 
89
if you do not pass in a Bio::Restriction::EnzymeCollection.
 
90
 
 
91
To cut a sequence, set up a Restriction::Analysis object with a sequence
 
92
like this:
 
93
 
 
94
  use Bio::Restriction::Analysis;
 
95
  my $ra=Bio::Restriction::Analysis->new(-seq=>$seqobj);
 
96
 
 
97
or
 
98
 
 
99
  my $ra=Bio::Restriction::Analysis->new
 
100
      (-seq=>$seqobj, -enzymes=>$enzs);
 
101
 
 
102
Then, to get the fragments for a particular enzyme use this:
 
103
 
 
104
  @fragments=$ra->fragments('EcoRI');
 
105
 
 
106
Note that the naming of restriction enzymes is that the last numbers
 
107
are usually Roman numbers (I, II, III, etc). You may want to use
 
108
something like this:
 
109
 
 
110
  # get a reference to an array of unique (single) cutters
 
111
  $singles = $re->unique_cutters;
 
112
  foreach my $enz ($singles->each_enzyme) {
 
113
      @fragments=$re->fragments($enz);
 
114
      ... do something here ...
 
115
  }
 
116
 
 
117
Note that if your sequence is circular, the first and last fragment
 
118
will be joined so that they are the appropriate length and sequence
 
119
for further analysis. This fragment will also be checked for cuts
 
120
by the enzyme(s).  However, this will change the start of the
 
121
sequence!
 
122
 
 
123
There are two separate algorithms used depending on whether your
 
124
enzyme has ambiguity. The non-ambiguous algoritm is a lot faster,
 
125
and if you are using very large sequences you should try and use
 
126
this algorithm. If you have a large sequence (e.g. genome) and 
 
127
want to use ambgiuous enzymes you may want to make seperate
 
128
Bio::Restriction::Enzyme objects for each of the possible
 
129
alternatives and make sure that you don't set is_ambiguous!
 
130
 
 
131
This version should correctly deal with overlapping cut sites
 
132
in both ambiguous and non-ambiguous enzymes.
 
133
 
 
134
I have tried to write this module with speed and memory in mind
 
135
so that it can be effectively used for large (e.g. genome sized)
 
136
sequence. This module only stores the cut positions internally,
 
137
and calculates everything else on an as-needed basis. Therefore
 
138
when you call fragment_maps (for example), there may be another
 
139
delay while these are generated.
 
140
 
 
141
=head1 FEEDBACK
 
142
 
 
143
=head2 Mailing Lists 
 
144
 
 
145
User feedback is an integral part of the evolution of this and other
 
146
Bioperl modules. Send your comments and suggestions preferably to one
 
147
of the Bioperl mailing lists. Your participation is much appreciated.
 
148
 
 
149
    bioperl-l@bioperl.org             - General discussion
 
150
    http://bioperl.org/MailList.shtml - About the mailing lists
 
151
 
 
152
=head2 Reporting Bugs
 
153
 
 
154
Report bugs to the Bioperl bug tracking system to help us keep track
 
155
the bugs and their resolution. Bug reports can be submitted via email
 
156
or the web:
 
157
 
 
158
     bioperl-bugs@bio.perl.org
 
159
     http://bugzilla.bioperl.org/
 
160
 
 
161
=head1 AUTHOR
 
162
 
 
163
Rob Edwards, redwards@utmem.edu, 
 
164
Steve Chervitz, sac@bioperl.org
 
165
 
 
166
=head1 CONTRIBUTORS
 
167
 
 
168
Heikki Lehvaslaiho, heikki@ebi.ac.uk
 
169
 
 
170
=head1 COPYRIGHT
 
171
 
 
172
Copyright (c) 2003 Rob Edwards.  Some of this work is Copyright (c)
 
173
1997-2002 Steve A. Chervitz. All Rights Reserved.
 
174
 
 
175
This module is free software; you can redistribute it and/or modify it
 
176
under the same terms as Perl itself.
 
177
 
 
178
=head1 SEE ALSO
 
179
 
 
180
L<Bio::Restriction::Enzyme>, 
 
181
L<Bio::Restriction::EnzymeCollection>
 
182
 
 
183
=head1 APPENDIX
 
184
 
 
185
Methods beginning with a leading underscore are considered private and
 
186
are intended for internal use by this module. They are not considered
 
187
part of the public interface and are described here for documentation
 
188
purposes only.
 
189
 
 
190
=cut
 
191
 
 
192
package Bio::Restriction::Analysis;
 
193
use Bio::Restriction::EnzymeCollection;
 
194
use Bio::Root::Root;
 
195
use strict;
 
196
use Data::Dumper;
 
197
 
 
198
use vars qw (@ISA);
 
199
@ISA = qw(Bio::Root::Root);
 
200
 
 
201
=head1 new
 
202
 
 
203
 Title     : new
 
204
 Function  : Initializes the restriction enzyme object
 
205
 Returns   : The Restriction::Analysis object 
 
206
 Arguments : 
 
207
 
 
208
             $re_anal->new(-seq=$seqobj, 
 
209
                 -enzymes=>Restriction::EnzymeCollection object)
 
210
             -seq requires a Bio::PrimarySeq object
 
211
             -enzymes is optional.
 
212
              If ommitted it will use the default set of enzymes
 
213
 
 
214
This is the place to start. Pass in a sequence, and you will be able
 
215
to get the fragments back out.  Several other things are available
 
216
like the number of zero cutters or single cutters.
 
217
 
 
218
=cut
 
219
 
 
220
sub new {
 
221
    my($class, @args) = @_;
 
222
    my $self = $class->SUPER::new(@args);
 
223
    my ($seq,$enzymes) =
 
224
        $self->_rearrange([qw(
 
225
                              SEQ
 
226
                              ENZYMES
 
227
                             )], @args);
 
228
 
 
229
    $seq && $self->seq($seq);
 
230
 
 
231
    $enzymes ?  $self->enzymes($enzymes)
 
232
        :  ($self->{'_enzymes'} = Bio::Restriction::EnzymeCollection->new );
 
233
 
 
234
    # keep track of status
 
235
    $self->{'_cut'} = 0;
 
236
    
 
237
    # left these here because we want to reforce a _cut if someone
 
238
    # just calls new
 
239
    $self->{maximum_cuts} = 0;
 
240
 
 
241
    $self->{'_number_of_cuts_by_enzyme'} = {};
 
242
    $self->{'_number_of_cuts_by_cuts'} = {};
 
243
    $self->{'_fragments'} = {};
 
244
    $self->{'_cut_positions'} = {}; # cut position is the real position 
 
245
    $self->{'_frag_map_list'} = {};
 
246
 
 
247
    return $self;
 
248
 
 
249
}
 
250
 
 
251
=head1 Methods to set parameters
 
252
 
 
253
=cut
 
254
 
 
255
=head2 seq
 
256
 
 
257
 Title    : seq
 
258
 Usage    : $ranalysis->seq($newval);
 
259
 Function : get/set method for the  sequence to be cut
 
260
 Example  : $re->seq($seq);
 
261
 Returns  : value of seq
 
262
 Args     : A Bio::PrimarySeqI dna object (optional)
 
263
 
 
264
=cut
 
265
 
 
266
sub seq {
 
267
     my $self = shift;
 
268
     if (@_) {
 
269
         my $seq = shift;
 
270
         $self->throw('Need a sequence object ['. ref $seq.  ']')
 
271
             unless $seq->isa('Bio::PrimarySeqI');
 
272
         $self->throw('Need a DNA sequence object ['. $seq->alphabet.  ']')
 
273
             unless $seq->alphabet eq 'dna';
 
274
 
 
275
         $self->{'_seq'} = $seq;
 
276
         $self->{'_cut'} = 0;
 
277
     }
 
278
     return $self->{'_seq'};
 
279
}
 
280
 
 
281
=head2 enzymes
 
282
 
 
283
 Title    : enzymes
 
284
 Usage    : $re->enzymes($newval)
 
285
 Function : gets/Set the restriction enzyme enzymes
 
286
 Example  : $re->enzymes('EcoRI')
 
287
 Returns  : reference to the collection
 
288
 Args     : an array of Bio::Restriction::EnzymeCollection and/or
 
289
            Bio::Restriction::Enzyme objects
 
290
 
 
291
 
 
292
The default object for this method is
 
293
Bio::Restriction::EnzymeCollection.  However, you can also pass it a
 
294
list of Bio::Restriction::Enzyme objects - even mixed with Collection
 
295
objects.  They will all be stored into one collection.
 
296
 
 
297
=cut
 
298
 
 
299
sub enzymes {
 
300
     my $self = shift;
 
301
     if (@_) {
 
302
         $self->{'_enzymes'} = Bio::Restriction::EnzymeCollection->new (-empty => 1)
 
303
             unless $self->{'_enzymes'};
 
304
         $self->{'_enzymes'}->enzymes(@_);
 
305
         $self->{'_cut'} = 0;
 
306
     }
 
307
     return $self->{'_enzymes'};
 
308
}
 
309
 
 
310
 
 
311
=head1 Perform the analysis
 
312
 
 
313
=cut
 
314
 
 
315
=head2 cut
 
316
 
 
317
 Title    : cut
 
318
 Usage    : $re->cut()
 
319
 Function : Cut the sequence with the enzymes
 
320
 Example  : $re->cut(); $re->cut('single'); or $re->cut('multiple', $enzymecollection);
 
321
 Returns  : $self
 
322
 Args     : 'single' (optional), 'multiple' with enzyme collection.
 
323
 
 
324
An explicit cut method is needed to pass arguments to it. 
 
325
 
 
326
There are two varieties of cut. Single is the default, and need
 
327
not be explicitly called. This cuts the sequence with each
 
328
enzyme separately.
 
329
 
 
330
Multiple cuts a sequence with more than one enzyme. You must pass
 
331
it a Bio::Restriction::EnzymeCollection object of the set
 
332
of enzymes that you want to use in the double digest. The results
 
333
will be stored as an enzyme named "multiple_digest", so you can
 
334
use all the retrieval methods to get the data.
 
335
 
 
336
If you want to use the default setting there is no need to call cut
 
337
directly. Every method in the class that needs output checks the
 
338
object's internal status and recalculates the cuts if needed.
 
339
 
 
340
Note: cut doesn't now re-initialize everything before figuring
 
341
out cuts. This is so that you can do multiple digests, or add more
 
342
data or whatever. You'll have to use new to reset everything.
 
343
 
 
344
See also the comments in above about ambiguous and non-ambiguous
 
345
sequences.
 
346
 
 
347
=cut
 
348
 
 
349
sub cut {
 
350
    my ($self, $opt, $ec) = @_;
 
351
 
 
352
    # for the moment I have left this as a separate routine so
 
353
    # the user calls cuts rather than _cuts. This also initializes
 
354
    # some stuff we need to use.
 
355
  
 
356
    $self->throw("A sequence must be supplied")
 
357
        unless $self->seq;
 
358
 
 
359
    if (uc($opt) eq "MULTIPLE") {
 
360
      $self->throw("You must supply a separate enzyme collection for multiple digests") unless $ec;
 
361
      $self->_multiple_cuts($ec); # multiple digests
 
362
    } else {
 
363
    # reset some of the things that we save
 
364
       $self->{maximum_cuts} = 0;
 
365
       $self->{'_number_of_cuts_by_enzyme'} = {};
 
366
       $self->{'_number_of_cuts_by_cuts'} = {};
 
367
       $self->{'_fragments'} = {};
 
368
       $self->{'_cut_positions'} = {}; # cut position is the real position 
 
369
       $self->{'_frag_map_list'} = {};
 
370
       $self->_cuts;
 
371
    } 
 
372
    
 
373
    $self->{'_cut'} = 1;
 
374
    return $self;
 
375
}
 
376
 
 
377
=head2 mulitple_digest
 
378
 
 
379
 Title     : multiple_digest
 
380
 Function  : perform a multiple digest on a sequence
 
381
 Returns   : $self so you can go and get any of the other methods
 
382
 Arguments : An enzyme collection
 
383
 
 
384
 Multiple digests can use 1 or more enzymes, and the data is stored
 
385
 in as if it were an enzyme called multiple_digest. You can then
 
386
 retrieve information about multiple digests from any of the other
 
387
 methods.
 
388
 
 
389
 You can use this method in place of $re->cut('multiple', $enz_coll);
 
390
 
 
391
=cut
 
392
 
 
393
sub multiple_digest {
 
394
 my ($self, $ec)=@_;
 
395
 return $self->cut('multiple', $ec);
 
396
}
 
397
 
 
398
=head1 Query the results of the analysis
 
399
 
 
400
=cut
 
401
 
 
402
=head2 positions
 
403
 
 
404
  Title    : positions
 
405
  Function : Retrieve the positions that an enzyme cuts at
 
406
  Returns  : An array of the positions that an enzyme cuts at
 
407
           : or an empty array if the enzyme doesn't cut
 
408
  Arguments: An enzyme name to retrieve the positions for
 
409
  Comments : The cut occurs after the base specified.
 
410
 
 
411
=cut
 
412
 
 
413
sub positions {
 
414
    my ($self, $enz) = @_;
 
415
    $self->cut unless $self->{'_cut'};
 
416
    $self->throw('no enzyme selected to get positions for')
 
417
        unless $enz;
 
418
 
 
419
    return defined $self->{'_cut_positions'}->{$enz} ?
 
420
        @{$self->{'_cut_positions'}->{$enz}} : 
 
421
        ();
 
422
}
 
423
 
 
424
=head2 fragments
 
425
 
 
426
  Title    : fragments
 
427
  Function : Retrieve the fragments that we cut
 
428
  Returns  : An array of the fragments retrieved. 
 
429
  Arguments: An enzyme name to retrieve the fragments for
 
430
 
 
431
For example this code will retrieve the fragments for all enzymes that
 
432
cut your sequence
 
433
 
 
434
  my $all_cutters = $analysis->cutters;
 
435
  foreach my $enz ($$all_cutters->each_enzyme}) {
 
436
      @fragments=$analysis->fragments($enz);
 
437
  }
 
438
 
 
439
=cut
 
440
 
 
441
sub fragments {
 
442
    my ($self, $enz) = @_;
 
443
    $self->cut unless $self->{'_cut'};
 
444
    $self->throw('no enzyme selected to get fragments for')
 
445
        unless $enz;
 
446
    my @fragments;
 
447
    for ($self->fragment_maps($enz)) {push @fragments, $_->{seq}}
 
448
    return @fragments;
 
449
}
 
450
 
 
451
=head2 fragment_maps
 
452
 
 
453
  Title     : fragment_maps
 
454
  Function  : Retrieves fragment sequences with start and end
 
455
              points. Useful for feature construction.
 
456
 
 
457
  Returns   : An array containing a hash reference for each fragment,
 
458
              containing the start point, end point and DNA
 
459
              sequence. The hash keys are 'start', 'end' and
 
460
              'seq'. Returns an empty array if not defined.
 
461
 
 
462
  Arguments : An enzyme name, enzyme object, 
 
463
              or enzyme collection to retrieve the fragments for.
 
464
 
 
465
If passes an enzyme collection it will return the result of a multiple
 
466
digest. This : will also cause the special enzyme 'multiple_digest' to
 
467
be created so you can get : other information about this multiple
 
468
digest. (TMTOWTDI).
 
469
 
 
470
There is a minor problem with this and $self-E<gt>fragments that I
 
471
haven't got a good answer for (at the moment). If the sequence is not
 
472
cut, do we return undef, or the whole sequence?
 
473
 
 
474
For linear fragments it would be good to return the whole
 
475
sequence. For circular fragments I am not sure.
 
476
 
 
477
At the moment it returns the whole sequence with start of 1 and end of
 
478
length of the sequence.  For example:
 
479
 
 
480
  use Bio::Restriction::Analysis;
 
481
  use Bio::Restriction::EnzymeCollection;
 
482
  use Bio::PrimarySeq;
 
483
 
 
484
  my $seq=new Bio::PrimarySeq
 
485
      (-seq =>'AGCTTAATTCATTAGCTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATCCAAAAAAGAGTGAGCTTCTGAT',
 
486
       -primary_id => 'synopsis',
 
487
       -molecule => 'dna');
 
488
 
 
489
  my $ra=Bio::Restriction::Analysis->new(-seq=>$seq);
 
490
 
 
491
  my @gel;
 
492
  my @bam_maps = $ra->fragment_maps('BamHI');
 
493
  foreach my $i (@bam_maps) {
 
494
     my $start = $i->{start};
 
495
     my $end = $i->{end};
 
496
     my $sequence = $i->{seq};
 
497
     push @gel, "$start--$sequence--$end";
 
498
     @gel = sort {length $b <=> length $a} @gel;
 
499
  }
 
500
  print join("\n", @gel) . "\n";
 
501
 
 
502
=cut
 
503
 
 
504
sub fragment_maps {
 
505
    my ($self, $enz) = @_;
 
506
    $self->cut unless $self->{'_cut'};
 
507
    $self->throw('no enzyme selected to get fragment maps for')
 
508
        unless $enz;
 
509
 
 
510
    # we are going to generate this on an as-needed basis rather than
 
511
    # for every enzyme this should cut down on the amount of
 
512
    # duplicated data we are trying to save in memory and make this
 
513
    # faster and easier for large sequences, e.g. genome analysis
 
514
 
 
515
    my @cut_positions;
 
516
    if (ref $enz eq '') {
 
517
        @cut_positions=@{$self->{'_cut_positions'}->{$enz}};
 
518
    } elsif ($enz->isa("Bio::Restriction::EnzymeI")) {
 
519
        @cut_positions=@{$self->{'_cut_positions'}->{$enz->name}};
 
520
    } elsif ($enz->isa("Bio::Restriction::EnzymeCollection")) {
 
521
        $self->cuts('multiple', $enz);
 
522
        @cut_positions=@{$self->{'_cut_positions'}->{'multiple_digest'}};
 
523
    }
 
524
 
 
525
    unless ($cut_positions[0]) {
 
526
        # it doesn't cut
 
527
        # return the whole sequence
 
528
        # this should probably have the is_circular command
 
529
        my %map=(
 
530
                 'start'  => 1,
 
531
                 'end'    => $self->{'_seq'}->length,
 
532
                 'seq'    => $self->{'_seq'}->seq
 
533
                );
 
534
        push (@{$self->{'_frag_map_list'}->{$enz}}, \%map);
 
535
        return defined $self->{'_frag_map_list'}->{$enz} ?
 
536
            @{$self->{'_frag_map_list'}->{$enz}} : ();
 
537
    }
 
538
 
 
539
    @cut_positions=sort {$a <=> $b} @cut_positions;
 
540
    push my @cuts, $cut_positions[0];
 
541
    foreach my $i (@cut_positions) {
 
542
        push @cuts, $i if $i != $cuts[$#cuts];
 
543
    }
 
544
 
 
545
    my $start=1; my $stop; my %seq; my %stop;
 
546
    foreach $stop (@cuts) {
 
547
        $seq{$start}=$self->{'_seq'}->subseq($start, $stop);
 
548
        $stop{$start}=$stop;
 
549
        $start=$stop+1;
 
550
    }
 
551
    $stop=$self->{'_seq'}->length;
 
552
    $seq{$start}=$self->{'_seq'}->subseq($start, $stop);
 
553
    $stop{$start}=$stop;
 
554
 
 
555
    if ($self->{'_seq'}->is_circular) {
 
556
        # join the first and last fragments
 
557
        $seq{$start}.=$seq{'1'};
 
558
        delete $seq{'1'};
 
559
        $stop{$start}=$stop{'1'};
 
560
        delete $stop{'1'};
 
561
    }
 
562
 
 
563
    foreach my $start (sort {$a <=> $b} keys %seq) {
 
564
        my %map=(
 
565
                 'start'  => $start,
 
566
                 'end'    => $stop{$start},
 
567
                 'seq'    => $seq{$start}
 
568
                );
 
569
        push (@{$self->{'_frag_map_list'}->{$enz}}, \%map);
 
570
    }
 
571
 
 
572
    return defined $self->{'_frag_map_list'}->{$enz} ?
 
573
        @{$self->{'_frag_map_list'}->{$enz}} : ();
 
574
}
 
575
 
 
576
 
 
577
=head2 sizes
 
578
 
 
579
  Title    : sizes
 
580
  Function : Retrieves an array with the sizes of the fragments
 
581
  Returns  : Array that has the sizes of the fragments ordered from 
 
582
             largest to smallest like they would appear in a gel.
 
583
  Arguments: An enzyme name to retrieve the sizes for is required and
 
584
             kilobases to the nearest 0.1 kb, else it will be in
 
585
             bp. If the optional third entry is set the results will
 
586
             be sorted.
 
587
 
 
588
This is designed to make it easy to see what fragments you should get
 
589
on a gel!
 
590
 
 
591
You should be able to do these:
 
592
 
 
593
  # to see all the fragment sizes,
 
594
  print join "\n", @{$re->sizes($enz)}, "\n";
 
595
  # to see all the fragment sizes sorted
 
596
  print join "\n", @{$re->sizes($enz, 0, 1)}, "\n";
 
597
  # to see all the fragment sizes in kb sorted
 
598
  print join "\n", @{$re->sizes($enz, 1, 1)}, "\n";
 
599
 
 
600
=cut
 
601
 
 
602
sub sizes {
 
603
    my ($self, $enz, $kb, $sort) = @_;
 
604
    $self->throw('no enzyme selected to get fragments for')
 
605
        unless $enz;
 
606
    $self->cut unless $self->{'_cut'};
 
607
    my @frag; my $lastsite=0;
 
608
    foreach my $site (@{$self->{'_cut_positions'}->{$enz}}) {
 
609
      $kb ? push (@frag, (int($site-($lastsite))/100)/10)
 
610
          : push (@frag, $site-($lastsite));
 
611
      $lastsite=$site;
 
612
    }
 
613
    $kb ? push (@frag, (int($self->{'_seq'}->length-($lastsite))/100)/10)
 
614
        : push (@frag, $self->{'_seq'}->length-($lastsite));
 
615
    if ($self->{'_seq'}->is_circular) {
 
616
       my $first=shift @frag;
 
617
       my $last=pop @frag;
 
618
       push @frag, ($first+$last);
 
619
    }
 
620
    $sort ? @frag = sort {$b <=> $a} @frag : 1;
 
621
 
 
622
    return @frag;
 
623
}
 
624
 
 
625
=head1 How many times does enzymes X cut?
 
626
 
 
627
=cut
 
628
 
 
629
=head2 cuts_by_enzyme
 
630
 
 
631
 Title     : cuts_by_enzyme
 
632
 Function  : Return the number of cuts for an enzyme
 
633
 Returns   : An integer with the number of times each enzyme cuts.
 
634
             Returns 0 if doesn't cut or undef if not defined
 
635
 Arguments : An enzyme name string
 
636
 
 
637
 
 
638
=cut
 
639
 
 
640
sub cuts_by_enzyme {
 
641
    my ($self, $enz)=@_;
 
642
 
 
643
    $self->throw("Need an enzyme name")
 
644
        unless defined $enz;
 
645
    $self->cut unless $self->{'_cut'};
 
646
    return $self->{'_number_of_cuts_by_enzyme'}->{$enz};
 
647
}
 
648
 
 
649
=head1 Which enzymes cut the sequence N times?
 
650
 
 
651
=cut
 
652
 
 
653
=head2 cutters
 
654
 
 
655
 Title     : cutters
 
656
 Function  : Find enzymes that cut a given number of times
 
657
 Returns   : a Bio::Restriction::EnzymeCollection
 
658
 Arguments : 1. exact time or lower limit,
 
659
                non-negative integer, optional
 
660
             2. upper limit, non-negative integer,
 
661
                larger or equalthan first, optional
 
662
 
 
663
 
 
664
If no argumets are given, the method returns all enzymes that do cut
 
665
the sequence. The argument zero, '0', is same as method
 
666
zero_cutters().  The argument one, '1', corresponds to unique_cutters.
 
667
If either of the limits is larger than number of cuts any enzyme cuts the
 
668
sequence, the that limit is automagically lowered. The method max_cuts()
 
669
gives the largest number of cuts.
 
670
 
 
671
See Also : L<unique_cutters|unique_cutters>,
 
672
L<zero_cutters|zero_cutters>, L<max_cuts|max_cuts>
 
673
 
 
674
=cut
 
675
 
 
676
sub cutters {
 
677
    my ($self, $a, $z) = @_;
 
678
 
 
679
    $self->cut unless $self->{'_cut'};
 
680
 
 
681
    my ($start, $end);
 
682
    if (defined $a) {
 
683
        $self->throw("Need a non-zero integer [$a]")
 
684
            unless $a =~ /^[+]?\d+$/;
 
685
        $start = $a;
 
686
    } else {
 
687
        $start = 1;
 
688
    }
 
689
    $start = $self->{'maximum_cuts'} if $start > $self->{'maximum_cuts'};
 
690
 
 
691
    if (defined $z) {
 
692
        $self->throw("Need a non-zero integer no smaller than start [0]")
 
693
            unless $z =~ /^[+]?\d+$/ and $z >= $a;
 
694
        $end = $z;
 
695
    }
 
696
    elsif (defined $a) {
 
697
        $end = $start;
 
698
    } else {
 
699
        $end = $self->{'maximum_cuts'};
 
700
    }
 
701
    $end = $self->{'maximum_cuts'} if $end > $self->{'maximum_cuts'};
 
702
    my $set = new Bio::Restriction::EnzymeCollection(-empty => 1);
 
703
 
 
704
    for (my $i=$start; $i<=$end; $i++) {
 
705
        $set->enzymes( @{$self->{_number_of_cuts_by_cuts}->{$i}} )
 
706
            if defined $self->{_number_of_cuts_by_cuts}->{$i};
 
707
    }
 
708
 
 
709
    return $set;
 
710
}
 
711
 
 
712
 
 
713
=head2 unique_cutters
 
714
 
 
715
 Title     : unique_cutters
 
716
 Function  : A special case if cutters() where enzymes only cut once
 
717
 Returns   : a Bio::Restriction::EnzymeCollection
 
718
 Arguments : -
 
719
 
 
720
 
 
721
See also:  L<cutters>, L<zero_cutters>
 
722
 
 
723
=cut
 
724
 
 
725
sub unique_cutters {
 
726
    shift->cutters(1);
 
727
}
 
728
 
 
729
=head2 zero_cutters
 
730
 
 
731
 Title     : zero_cutters
 
732
 Function  : A special case if cutters() where enzymes don't cut the sequence
 
733
 Returns   : a Bio::Restriction::EnzymeCollection
 
734
 Arguments : -
 
735
 
 
736
See also:  L<cutters>, L<unique_cutters>
 
737
 
 
738
=cut
 
739
 
 
740
sub zero_cutters {
 
741
    shift->cutters(0);
 
742
}
 
743
 
 
744
=head2 max_cuts
 
745
 
 
746
 Title     : max_cuts
 
747
 Function  : Find the most number of cuts
 
748
 Returns   : The number of times the enzyme that cuts most cuts.
 
749
 Arguments : None
 
750
 
 
751
This is not a very practical method, but if you are curious...
 
752
 
 
753
=cut
 
754
 
 
755
sub max_cuts { return shift->{maximum_cuts} }
 
756
 
 
757
=head1 Internal methods
 
758
 
 
759
=cut
 
760
 
 
761
=head2 _cuts
 
762
 
 
763
 Title     : _cuts
 
764
 Function  : Figures out which enzymes we know about and cuts the sequence.
 
765
 Returns   : Nothing.
 
766
 Arguments : None.
 
767
 Comments  : An internal method. This will figure out where the sequence 
 
768
             should be cut, and provide the appropriate results.
 
769
 
 
770
=cut
 
771
 
 
772
sub _cuts {
 
773
    my $self = shift;
 
774
 
 
775
    my $target_seq=uc $self->{'_seq'}->seq; # I have been burned on this before :)
 
776
 
 
777
 
 
778
    # first, find out all the enzymes that we have
 
779
    foreach my $enz ($self->{'_enzymes'}->each_enzyme) {
 
780
        my @all_cuts;
 
781
        my @others = $enz->others if $enz->can("others");
 
782
        foreach my $enzyme ($enz, @others) {
 
783
            my ($beforeseq, $afterseq)=$self->_enzyme_sites($enzyme);
 
784
            # cut the sequence
 
785
 
 
786
            # if the enzyme is ambiguous we need to use a regexp to find the cut site
 
787
            # otherwise we can use index (much faster)
 
788
 
 
789
            # All of these methods return references to arrays. 
 
790
            # All of the arrays are positions in the DNA where the sequence is cut
 
791
            # We will push everything into @all_cuts, and then deconvolute it
 
792
            # and figure everything else out from there.
 
793
        
 
794
            my $cut_positions;
 
795
            if ($enzyme->is_ambiguous) {
 
796
                $cut_positions= $self->_ambig_cuts($beforeseq, $afterseq, $target_seq, $enzyme);
 
797
            } else {
 
798
                $cut_positions= $self->_nonambig_cuts($beforeseq, $afterseq, $target_seq, $enzyme);
 
799
            }
 
800
 
 
801
            push @all_cuts, @$cut_positions;
 
802
 
 
803
            # deal with is_circular sequences
 
804
            if ($self->{'_seq'}->is_circular) {
 
805
                $cut_positions=$self->_circular($beforeseq, $afterseq, $enzyme);
 
806
                push @all_cuts, @$cut_positions;
 
807
            }
 
808
 
 
809
            # we need to deal with non-palindromic enzymes separately
 
810
            unless ($enzyme->is_palindromic) {
 
811
                $cut_positions=$self->_non_pal_enz($target_seq, $enzyme);
 
812
                push @all_cuts, @$cut_positions;
 
813
            }
 
814
        }
 
815
 
 
816
        if (defined $all_cuts[0]) {
 
817
            # now just remove any duplicate cut sites
 
818
            @all_cuts = sort {$a <=> $b} @all_cuts;
 
819
            push  @{$self->{'_cut_positions'}->{$enz->name}},  $all_cuts[0];
 
820
            foreach my $i (@all_cuts) {
 
821
                push @{$self->{'_cut_positions'}->{$enz->name}}, $i 
 
822
                    if $i != ${$self->{'_cut_positions'}->{$enz->name}}[$#{$self->{'_cut_positions'}->{$enz->name}}];
 
823
            }
 
824
        } else {
 
825
            # this just fixes an eror when @all_cuts is not defined!
 
826
            @{$self->{'_cut_positions'}->{$enz->name}}=();
 
827
        }
 
828
 
 
829
        # note I have removed saving any other information except the
 
830
        # cut_positions this should significantly decrease the amount
 
831
        # of memory that is required for large sequences. It should
 
832
        # also speed things up dramatically, because fragments and
 
833
        # fragment maps are only calculated for those enzymes they are
 
834
        # needed for.
 
835
        
 
836
        # finally, save minimal information about each enzyme
 
837
        my $number_of_cuts=scalar @{$self->{'_cut_positions'}->{$enz->name}};
 
838
        # now just store the number of cuts
 
839
        $self->{_number_of_cuts_by_enzyme}->{$enz->name}=$number_of_cuts;
 
840
        push (@{$self->{_number_of_cuts_by_cuts}->{$number_of_cuts}}, $enz);
 
841
        if ($number_of_cuts > $self->{maximum_cuts}) {
 
842
            $self->{maximum_cuts}=$number_of_cuts;
 
843
        }
 
844
 
 
845
    }
 
846
}
 
847
 
 
848
=head2 _enzyme_sites
 
849
 
 
850
 Title     : _enzyme_sites
 
851
 Function  : An internal method to figure out the two sides of an enzyme
 
852
 Returns   : The sequence before the cut and the sequence after the cut
 
853
 Arguments : A Bio::Restriction::Enzyme object
 
854
 
 
855
=cut
 
856
 
 
857
sub _enzyme_sites {
 
858
    my ($self, $enz)=@_;
 
859
    # get the cut site
 
860
    # I have reworked this so that it uses $enz->cut to get the site
 
861
 
 
862
    my $site=$enz->cut;
 
863
    # split it into the two fragments for the sequence before and after.
 
864
    $site=0 unless defined $site;
 
865
    # shouldn't happen, but what if the cut site is outside of the sequence
 
866
    if ($site < 0 || $site > length($enz->string)) {
 
867
       $self->throw("This is (probably) not your fault.\nGot a cut site of $site and a sequence of ".$enz->string);
 
868
    }
 
869
 
 
870
    # the default values just stop an error from an undefined
 
871
    # string. But they don't affect the split.
 
872
    my ($beforeseq, $afterseq)= ('.', '.');
 
873
 
 
874
    if ($site == 0) {
 
875
       $afterseq=$enz->string;
 
876
    }
 
877
    elsif ($site == $enz->seq->length) {
 
878
       $beforeseq=$enz->string;
 
879
    }
 
880
    else {
 
881
       $beforeseq=$enz->seq->subseq(1, $site);
 
882
       $afterseq=$enz->seq->subseq($site+1, $enz->seq->length);
 
883
    }
 
884
 
 
885
    # if the enzyme is ambiguous we need to convert this into a perl string
 
886
    if ($enz->is_ambiguous) {
 
887
       $beforeseq=$self->_expanded_string($beforeseq);
 
888
       $afterseq =$self->_expanded_string($afterseq);
 
889
    }
 
890
 
 
891
    return ($beforeseq, $afterseq);
 
892
}
 
893
 
 
894
 
 
895
=head2 _non_pal_enz
 
896
 
 
897
  Title    : _non_pal_enz
 
898
  Function : Analyses non_palindromic enzymes for cuts in both ways
 
899
  Returns  : A reference to an array of cut positions
 
900
  Arguments: The sequence to check and the enzyme object
 
901
 
 
902
=cut
 
903
 
 
904
sub _non_pal_enz {
 
905
    my ($self, $target_seq, $enz) =@_;
 
906
    # add support for non-palindromic sequences
 
907
    # the enzyme is not the same forwards and backwards
 
908
    my $site=$enz->complementary_cut;
 
909
    # we are going to rc the sequence, so complementary_cut becomes length-complementary_cut
 
910
 
 
911
    my ($beforeseq, $afterseq)=('.', '.');
 
912
 
 
913
    my $new_left_cut=$enz->seq->length-$site;
 
914
    # there is a problem when this is actually zero
 
915
    if ($new_left_cut == 0) {$afterseq=$enz->seq->revcom->seq}
 
916
    elsif ($new_left_cut == $enz->seq->length) {$beforeseq=$enz->seq->revcom->seq}
 
917
    else {
 
918
       $beforeseq=$enz->seq->revcom->subseq(1, ($enz->seq->length-$site));
 
919
       $afterseq=$enz->seq->revcom->subseq(($enz->seq->length-$site), $enz->seq->length);
 
920
     }
 
921
 
 
922
    # complementary cut is the position on the forward strand
 
923
    # correct for reverse strand - I think this is right
 
924
    my $results=[];
 
925
    if ($enz->is_ambiguous) {
 
926
          $results= $self->_ambig_cuts($beforeseq, $afterseq, $target_seq, $enz);
 
927
    } else {
 
928
          $results= $self->_nonambig_cuts($beforeseq, $afterseq, $target_seq, $enz);
 
929
    }
 
930
 
 
931
    # deal with is_circular
 
932
    my $more_results=[];
 
933
    $more_results=$self->_circular($beforeseq, $afterseq, $enz) 
 
934
        if ($self->{'_seq'}->is_circular);
 
935
    push my @all_cuts, (@$more_results, @$results);
 
936
    return \@all_cuts;
 
937
 
938
 
 
939
=head2 _ambig_cuts
 
940
 
 
941
 Title     : _ambig_cuts
 
942
 Function  : An internal method to localize the cuts in the sequence
 
943
 Returns   : A reference to an array of cut positions
 
944
 Arguments : The separated enzyme site, the target sequence, and the enzyme object
 
945
 Comments  : This is a slow implementation but works for ambiguous sequences.
 
946
             Whenever possible, _nonambig_cuts should be used as it is a lot faster.
 
947
 
 
948
=cut
 
949
 
 
950
sub _ambig_cuts {
 
951
    my ($self, $beforeseq, $afterseq, $target_seq, $enz) = @_;
 
952
    
 
953
    # cut the sequence. This is done with split so we can use
 
954
    # regexp. 
 
955
    
 
956
    my @cuts = split /($beforeseq)($afterseq)/i, $target_seq;
 
957
    # now the array has extra elements --- the before and after!
 
958
    # we have:
 
959
    # element 0 sequence
 
960
    # element 1 3' end
 
961
    # element 2 5' end of next sequence
 
962
    # element 3 sequence
 
963
    # ....
 
964
 
 
965
    # we need to loop through the array and add the ends to the
 
966
    # appropriate parts of the sequence
 
967
 
 
968
    my $i=0;
 
969
    my @re_frags;
 
970
    if ($#cuts) {           # there is >1 element
 
971
        while ($i<$#cuts) {
 
972
            my $joinedseq;
 
973
            # the first sequence is a special case
 
974
            if ($i == 0) {
 
975
                $joinedseq=$cuts[$i].$cuts[$i+1];
 
976
            } else {
 
977
                $joinedseq=$cuts[$i-1].$cuts[$i].$cuts[$i+1];
 
978
            }
 
979
            # now deal with overlapping sequences
 
980
            # we can do this through a regular regexp as we only
 
981
            # have a short fragment to look through
 
982
 
 
983
            while ($joinedseq =~ /$beforeseq$afterseq/) {
 
984
                $joinedseq =~ s/^(.*?$beforeseq)($afterseq)/$2/;
 
985
                push @re_frags, $1;
 
986
            }
 
987
            push @re_frags, $joinedseq;
 
988
            $i+=3;
 
989
        }
 
990
 
 
991
    # I don't think we want the last fragment in. It is messing up the _circular
 
992
    # part of things. So I deleted this part of the code :)
 
993
 
 
994
    } else {
 
995
            # if we don't cut, leave the array empty
 
996
            return [];
 
997
    } # the sequence was not cut.
 
998
 
 
999
    # now @re_frags has the fragments of all the sequences
 
1000
    # but some people want to have this return the lengths
 
1001
    # of the fragments.
 
1002
 
 
1003
    # in theory the actual cut sites should be the length
 
1004
    # of the fragments in @re_frags
 
1005
 
 
1006
    # note, that now this is the only data that we are saving. We
 
1007
    # will have to go back add regenerate re_frags. The reason is
 
1008
    # that we can use this in _circular easier
 
1009
 
 
1010
    my @cut_positions = map {length($_)} @re_frags;
 
1011
 
 
1012
    # the cut positions are right now the lengths of the sequence, but
 
1013
    # we need to add them all onto each other
 
1014
 
 
1015
    for (my $i=1; $i<=$#cut_positions; $i++) {
 
1016
     $cut_positions[$i]+=$cut_positions[$i-1];
 
1017
    }
 
1018
 
 
1019
    # in one of those oddities in life, 2 fragments mean an enzyme cut once
 
1020
    # so $#re_frags is the number of cuts
 
1021
    return \@cut_positions;
 
1022
}
 
1023
 
 
1024
 
 
1025
=head2 _nonambig_cuts
 
1026
 
 
1027
 Title     : _nonambig_cuts
 
1028
 Function  : Figures out which enzymes we know about and cuts the sequence.
 
1029
 Returns   : Nothing.
 
1030
 Arguments : The separated enzyme site, the target sequence, and the enzyme object
 
1031
 
 
1032
An internal method. This will figure out where the sequence should be
 
1033
cut, and provide the appropriate results.  This is a much faster
 
1034
implementation because it doesn't use a regexp, but it can not deal
 
1035
with ambiguous sequences
 
1036
 
 
1037
=cut
 
1038
 
 
1039
sub _nonambig_cuts {
 
1040
    my ($self, $beforeseq, $afterseq, $target_seq, $enz) = @_;
 
1041
 
 
1042
    if ($beforeseq eq ".") {$beforeseq = ''}
 
1043
    if ($afterseq  eq ".") {$afterseq  = ''}
 
1044
    my $index_posn=index($target_seq, $beforeseq.$afterseq);
 
1045
    return [] if ($index_posn == -1); # there is no match to the sequence
 
1046
 
 
1047
    # there is at least one cut site
 
1048
    my @cuts;
 
1049
    while ($index_posn > -1) {
 
1050
          push (@cuts, $index_posn+length($beforeseq));
 
1051
          $index_posn=index($target_seq, $beforeseq.$afterseq, $index_posn+1);
 
1052
    }
 
1053
 
 
1054
    return \@cuts;
 
1055
}
 
1056
 
 
1057
 
 
1058
=head2 _mulitple_cuts
 
1059
 
 
1060
 Title     : _multiple_cuts
 
1061
 Function  : Figures out multiple digests
 
1062
 Returns   : An array of the cut sites for multiply digested DNA
 
1063
 Arguments : A Bio::Restriction::EnzymeCollection object
 
1064
 Comments  : Double digests is one subset of this, but you can use
 
1065
             as many enzymes as you want.
 
1066
 
 
1067
=cut
 
1068
 
 
1069
sub _multiple_cuts {
 
1070
    my ($self, $ec)=@_;
 
1071
    $self->cut unless $self->{'_cut'};
 
1072
 
 
1073
    # now that we are using positions rather than fragments
 
1074
    # this is really easy
 
1075
    my @cuts;
 
1076
    foreach my $enz ($ec->each_enzyme) { 
 
1077
       push @cuts, @{$self->{'_cut_positions'}->{$enz->name}}
 
1078
           if defined $self->{'_cut_positions'}->{$enz->name};
 
1079
    }
 
1080
    @{$self->{'_cut_positions'}->{'multiple_digest'}}=sort {$a <=> $b} @cuts;
 
1081
 
 
1082
    my $number_of_cuts;
 
1083
 
 
1084
    $number_of_cuts=scalar @{$self->{'_cut_positions'}->{'multiple_digest'}};
 
1085
    $self->{_number_of_cuts_by_enzyme}->{'multiple_digest'}=$number_of_cuts;
 
1086
    push (@{$self->{_number_of_cuts_by_cuts}->{$number_of_cuts}}, 'multiple_digest');
 
1087
    if ($number_of_cuts > $self->{maximum_cuts}) {
 
1088
        $self->{maximum_cuts}=$number_of_cuts;
 
1089
    }
 
1090
}
 
1091
 
 
1092
 
 
1093
=head2 _circular
 
1094
 
 
1095
 Title     : _circular
 
1096
 Function  : Deals with circular sequences
 
1097
 Returns   : Nothing.
 
1098
 Arguments : None.
 
1099
 
 
1100
There are two problems with circular sequences.
 
1101
 
 
1102
  1. When you cut a sequence and rejoin fragments you could generate
 
1103
  new cut sites.
 
1104
 
 
1105
  2. There could be a cut site at the end of the sequence.
 
1106
 
 
1107
I think these may be the same problem, and so we're working on #2 first!
 
1108
 
 
1109
=cut
 
1110
 
 
1111
sub _circular {
 
1112
    my ($self, $beforeseq, $afterseq, $enz) = @_;
 
1113
    my $target_seq=uc $self->{'_seq'}->seq; # I have been burned on this before :)
 
1114
 
 
1115
    # the approach I am taking is to find out the longest enzyme in the collection
 
1116
    # (I'll have to add a new function in enzyme collection for this)
 
1117
    # and then add more than that sequence from the end of the sequence to the start
 
1118
    # of the sequence, and map the new cut sites for each of the enzymes.
 
1119
 
 
1120
    # The cut sites that we are interested in must be within the length of the 
 
1121
    # enzyme sequence from the start or the end.
 
1122
 
 
1123
    my $longest_enz=$self->{'_enzymes'}->longest_cutter;
 
1124
    my $longest_cut=$longest_enz->recognition_length;
 
1125
    # this is an error that I don't want to deal with at the moment
 
1126
    $self->throw("Crap. The longest recognition site ($longest_cut) is longer than the".
 
1127
      " length of the sequence") if ($longest_cut > $self->{'_seq'}->length);
 
1128
 
 
1129
   # newseq is just the last part of the sequence and the first part of the sequence
 
1130
   # we don't want to go through and check the whole sequence again
 
1131
 
 
1132
   my ($first, $last) =
 
1133
       (substr($target_seq, 0, $longest_cut),substr($target_seq, -$longest_cut));
 
1134
   my $newseq=$last.$first;
 
1135
 
 
1136
   # now find the cut sites
 
1137
   # if the enzyme is ambiguous we need to use a regexp to find the cut site
 
1138
   # otherwise we can use index (much faster)
 
1139
   my $cut_positions;
 
1140
   if ($enz->is_ambiguous) {
 
1141
      $cut_positions= $self->_ambig_cuts($beforeseq, $afterseq, $newseq, $enz);
 
1142
   } else {
 
1143
      $cut_positions=$self->_nonambig_cuts($beforeseq, $afterseq, $newseq, $enz);
 
1144
   }
 
1145
 
 
1146
   # the enzyme doesn't cut in the new fragment - likely to be default  
 
1147
   return [] if (!$cut_positions);
 
1148
 
 
1149
   # now we are going to add things to _cut_positions
 
1150
   # in this shema it doesn't matter if the site is there twice - 
 
1151
   # we will take care of that later. Because we are using position
 
1152
   # rather than frag or anything else, we can just
 
1153
   # remove duplicates.
 
1154
   my @circ_cuts;
 
1155
   foreach my $cut (@$cut_positions) {
 
1156
    if ($cut == length($last)) {
 
1157
     # the cut is actually at position 0, but we're going to call this the
 
1158
     # length of the sequence so we don't confuse no cuts with a 0 cut
 
1159
     push (@circ_cuts, $self->{'_seq'}->length);
 
1160
    }
 
1161
    elsif ($cut < length($last)) {
 
1162
     # the cut is before the end of the sequence
 
1163
     # there is VERY likely to be an off by one error here
 
1164
     push (@circ_cuts, $self->{'_seq'}->length - (length($last) - $cut));
 
1165
    }
 
1166
    else {
 
1167
     # the cut is at the start of the sequence (position >=1)
 
1168
     # there is VERY likely to be an off by one error here
 
1169
     # note, we put this at the beginning of the array rather than the end!
 
1170
     unshift (@circ_cuts, $cut-length($last));
 
1171
    }
 
1172
   }
 
1173
   return \@circ_cuts;
 
1174
}
 
1175
 
 
1176
 
 
1177
 
 
1178
 
 
1179
 
 
1180
=head2 _expanded_string
 
1181
 
 
1182
 Title     : _expanded_string
 
1183
 Function  : Expand nucleotide ambiguity codes to their representative letters
 
1184
 Returns   : The full length string
 
1185
 Arguments : The string to be expanded.
 
1186
 
 
1187
Stolen from the original RestrictionEnzyme.pm
 
1188
 
 
1189
=cut
 
1190
 
 
1191
 
 
1192
sub _expanded_string {
 
1193
    my ($self, $str) = @_;
 
1194
 
 
1195
    $str =~ s/N|X/\./g;
 
1196
    $str =~ s/R/\[AG\]/g;
 
1197
    $str =~ s/Y/\[CT\]/g;
 
1198
    $str =~ s/S/\[GC\]/g;
 
1199
    $str =~ s/W/\[AT\]/g;
 
1200
    $str =~ s/M/\[AC\]/g;
 
1201
    $str =~ s/K/\[TG\]/g;
 
1202
    $str =~ s/B/\[CGT\]/g;
 
1203
    $str =~ s/D/\[AGT\]/g;
 
1204
    $str =~ s/H/\[ACT\]/g;
 
1205
    $str =~ s/V/\[ACG\]/g;
 
1206
 
 
1207
    return $str;
 
1208
}
 
1209
 
 
1210
 
 
1211
1;