File Coverage

blib/lib/RandomJungle/File/DB.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package RandomJungle::File::DB;
2              
3             =head1 NAME
4              
5             RandomJungle::File::DB - Low level access to the data in the RandomJungle DB file
6              
7             =cut
8              
9 5     5   1199 use strict;
  5         10  
  5         187  
10 5     5   26 use warnings;
  5         10  
  5         138  
11              
12 5     5   29 use Carp;
  5         10  
  5         332  
13 5     5   30 use Data::Dumper;
  5         10  
  5         336  
14 5     5   14779 use DBM::Deep;
  5         68108  
  5         136  
15 5     5   2064 use Devel::StackTrace;
  5         3925  
  5         138  
16              
17 5     5   3477 use RandomJungle::File::XML;
  0            
  0            
18             use RandomJungle::File::OOB;
19             use RandomJungle::File::RAW;
20              
21             =head1 VERSION
22              
23             Version 0.05
24              
25             =cut
26              
27             our $VERSION = 0.06;
28             our $ERROR; # used if new() fails
29              
30             =head1 SYNOPSIS
31              
32             RandomJungle::File::DB provides access to the data contained within the RandomJungle database that
33             is created using this module.
34             See RandomJungle::Jungle and RandomJungle::Tree for higher-level methods.
35              
36             use RandomJungle::File::DB;
37              
38             my $rjdb = RandomJungle::File::DB->new( db_file => $filename ) || die $RandomJungle::File::DB::ERROR;
39              
40             # Load data files into the db (all params are optional)
41             $rjdb->store_data( xml_file => $file1, oob_file => $file2, raw_file => $file3 ) || warn $rjdb->err_str;
42              
43             # Get the filenames for the data that was loaded
44             my $file = $rjdb->get_db_filename;
45             my $file = $rjdb->get_xml_filename;
46             my $file = $rjdb->get_oob_filename;
47             my $file = $rjdb->get_raw_filename;
48              
49             my $href = $rjdb->get_rj_params; # input params that were used when RJ was run
50             my $aref = $rjdb->get_header_labels; # (expected: FID IID PAT MAT)
51             my $aref = $rjdb->get_variable_labels; # (expected: SEX PHENOTYPE var1 ...)
52             my $aref = $rjdb->get_sample_labels; # from the IID column of the RAW file
53             my $aref = $rjdb->get_tree_ids; # sorted numerically
54              
55             # Returns the line (unsplit, unspliced) from the OOB file for a given sample (one param is required)
56             my $line = $rjdb->get_oob_by_sample( label => $sample_label, index => $sample_index )
57             or warn $rjdb->err_str;
58              
59             # Returns data for the sample specified by label => $label, where label is the IID from the RAW file
60             my $href = $rjdb->get_sample_data( label => $label ) || warn $rjdb->err_str;
61              
62             # Returns a href (not RJ::Tree objects) for each tree ID specified as an input param
63             my $href = $rjdb->get_tree_data( @tree_ids ); # may be big - use with caution
64              
65             =cut
66              
67             #*********************************************************************
68             # Public Methods
69             #*********************************************************************
70              
71             =head1 METHODS
72              
73             =head2 new()
74              
75             Creates and returns a new RandomJungle::File::DB object:
76              
77             my $rjdb = RandomJungle::File::DB->new( db_file => $filename );
78              
79             The 'db_file' parameter is required. Sets $ERROR and returns undef on failure.
80              
81             =cut
82              
83             sub new
84             {
85             # Returns RJ::File::DB object on success
86             # Sets $ERROR and returns undef on failure (e.g., 'db_file' param not set)
87             my ( $class, %args ) = @_;
88              
89             my $obj = {};
90             bless $obj, $class;
91             $obj->_init( %args ) || return; # $ERROR set by _init()
92              
93             return $obj;
94             }
95              
96             =head2 store_data()
97              
98             This method loads data into the RJ::File::DB database. All parameters are optional, so files can be
99             loaded in a single call or in multiple calls. Each type of file can only be loaded once; subsequent
100             calls to this method for a given file type will overwrite the previously-loaded data.
101              
102             $rjdb->store_data( xml_file => $file1, oob_file => $file2, raw_file => $file3 ) || die $rjdb->err_str;
103              
104             Returns true on success. Sets err_str and returns false if an error occurred.
105              
106             =cut
107              
108             sub store_data
109             {
110             # Returns true on success, false on failure
111             # The _load*file methods set err_str on failure (file does not exist or ::[XML|OOB|RAW]->new fails)
112             my ( $self, %args ) = @_;
113              
114             my $ok = 1;
115             my $errstr = '';
116              
117             if( defined $args{xml_file} )
118             {
119             $self->{params}{xml_file} = $args{xml_file};
120             $self->_load_xml_file( $args{xml_file} ) ||
121             do
122             {
123             $ok = 0;
124             $errstr .= $self->err_str;
125             };
126             }
127              
128             if( defined $args{oob_file} )
129             {
130             $self->{params}{oob_file} = $args{oob_file};
131             $self->_load_oob_file( $args{oob_file} ) ||
132             do
133             {
134             $ok = 0;
135             $errstr .= $self->err_str;
136             };
137             }
138              
139             if( defined $args{raw_file} )
140             {
141             $self->{params}{raw_file} = $args{raw_file};
142             $self->_load_raw_file( $args{raw_file} ) ||
143             do
144             {
145             $ok = 0;
146             $errstr .= $self->err_str;
147             };
148             }
149              
150             if( ! $ok )
151             {
152             $self->set_err( $errstr );
153             }
154              
155             return $ok;
156             }
157              
158             =head2 get_db_filename()
159              
160             Returns the name of the DB file specified in store_data():
161              
162             my $file = $rjdb->get_db_filename;
163              
164             =cut
165              
166             sub get_db_filename
167             {
168             # Returns the db filename
169             my ( $self ) = @_;
170             return $self->{params}{db_file};
171             }
172              
173             =head2 get_xml_filename()
174              
175             Returns the name of the XML file specified in store_data():
176              
177             my $file = $rjdb->get_xml_filename;
178              
179             =cut
180              
181             sub get_xml_filename
182             {
183             # Returns the XML filename
184             my ( $self ) = @_;
185             my $db = $self->{db};
186             my $data = $db->{XML}{filename}; # copy so can't modify db
187             return $data;
188             }
189              
190             =head2 get_rj_params()
191              
192             Returns a href of the input parameters used when Random Jungle was run:
193              
194             my $href = $rjdb->get_rj_params; # $href->{$param_name} = $param_value;
195              
196             =cut
197              
198             sub get_rj_params
199             {
200             # Returns a href of the input params that were used for RJ
201             my ( $self ) = @_;
202             my $db = $self->{db};
203             my %data = %{ $db->{XML}{options} }; # copy so can't modify db
204             return \%data;
205             }
206              
207             =head2 get_tree_ids()
208              
209             Returns an array ref of tree IDs (sorted numerically):
210              
211             my $aref = $rjdb->get_tree_ids;
212              
213             =cut
214              
215             sub get_tree_ids
216             {
217             # Returns an aref of tree IDs
218             my ( $self ) = @_;
219             my $db = $self->{db};
220             #my @ids = sort { $a <=> $b } keys %{ $db->{XML}{tree_data} };
221             #my @ids = @{ $db->{XML}{tree_ids} };
222             my @ids = split( "\t", $db->{XML}{tree_ids_str} );
223             return \@ids;
224             }
225              
226             =head2 get_tree_data()
227              
228             Returns a href containing a data record for each tree ID specified as an input param. The record
229             for each tree is a data structure from the XML file, not a RandomJungle::Tree object. Invalid tree
230             IDs are skipped. An empty href is returned if no valid IDs are provided.
231              
232             my $href = $rjdb->get_tree_data( @tree_ids ); # may be big - use with caution
233              
234             Note: This method is not intended to be called directly. See RandomJungle::Jungle::get_tree_by_id().
235              
236             =cut
237              
238             sub get_tree_data
239             {
240             # Returns a href containing a record for each tree ID specified as an input param
241             # Note the return struct contains data from the XML file (not RJ::Tree objects)
242             # Invalid tree IDs are skipped
243             # Returns an empty href if no valid IDs are provided
244             my ( $self, @tree_ids ) = @_;
245              
246             my $db = $self->{db};
247             my %data;
248              
249             foreach my $id ( @tree_ids )
250             {
251             # Devel::Cover has a bug that doesn't detect coverage on this statement with a DBM::Deep
252             # hash. See https://rt.cpan.org/Ticket/Display.html?id=72027 for the bug report.
253             next if( ! exists $db->{XML}{tree_data}{$id} );
254              
255             $data{$id} = {};
256             %{ $data{$id} } = %{ $db->{XML}{tree_data}{$id} };
257             }
258              
259             return \%data;
260             }
261              
262             =head2 get_oob_filename()
263              
264             Returns the name of the OOB file specified in store_data():
265              
266             my $file = $rjdb->get_oob_filename;
267              
268             =cut
269              
270             sub get_oob_filename
271             {
272             # Returns the OOB filename
273             my ( $self ) = @_;
274             my $db = $self->{db};
275             my $data = $db->{OOB}{filename}; # copy so can't modify db
276             return $data;
277             }
278              
279             =head2 get_oob_by_sample()
280              
281             Returns the line (unsplit, unspliced) from the OOB file for a given sample. The sample is specified
282             by either label => $label or index => $index (one is required), where label is the sample label
283             (IID) from the RAW file and index is the row number of the sample in the RAW file.
284             Sets err_str and returns undef if neither required parameter is specified or if the specified sample
285             cannot be found.
286              
287             my $line = $rjdb->get_oob_by_sample( label => $sample_label, index => $sample_index )
288             or warn $rjdb->err_str;
289              
290             =cut
291              
292             sub get_oob_by_sample
293             {
294             # Returns the line (unsplit, unspliced) from the OOB file for a given sample
295             # Sample is specified by either label => $label or index => $index (one is required),
296             # where label is the sample label (IID) from the RAW file
297             # and index is the row num of the sample in the RAW file (not the sample label, IID)
298             # Carps and returns undef if neither required param is specified or if the
299             # specified sample id cannot be found
300             my ( $self, %args ) = @_;
301              
302             # id supported for legacy code but is deprecated in favor of label (consistent with ::RAW)
303             if( ! defined $args{label} && defined $args{id} )
304             {
305             $args{label} = $args{id};
306             }
307              
308             if( ! defined $args{label} && ! defined $args{index} )
309             {
310             $self->set_err( 'Getting OOB by sample requires either sample label or index as input' );
311             return;
312             }
313              
314             my $db = $self->{db};
315             my $sample_i = exists $args{index} ? $args{index} : $self->_sample_label_to_index( $args{label} );
316              
317             if( ! defined $sample_i )
318             {
319             $self->set_err( "Cannot find sample index for sample label ($args{label})" );
320             return;
321             }
322              
323             my $line = $db->{OOB}{matrix}[$sample_i]; # will auto-vivify if OOB file is truncated
324              
325             if( ! defined $line )
326             {
327             $self->set_err( "Cannot find data for sample $sample_i (out of range?)" );
328             return;
329             }
330              
331             return $line;
332             }
333              
334             =head2 get_raw_filename()
335              
336             Returns the name of the RAW file specified in store_data():
337              
338             my $file = $rjdb->get_raw_filename;
339              
340             =cut
341              
342             sub get_raw_filename
343             {
344             # Returns the RAW filename
345             my ( $self ) = @_;
346             my $db = $self->{db};
347             my $data = $db->{RAW}{filename}; # copy so can't modify db
348             return $data;
349             }
350              
351             =head2 get_header_labels()
352              
353             Returns a reference to an array that contains the header labels from the RAW file:
354              
355             my $aref = $rjdb->get_header_labels; # (expected: FID IID PAT MAT)
356              
357             =cut
358              
359             sub get_header_labels
360             {
361             # Returns an aref of the header labels from the RAW file (expected: FID IID PAT MAT)
362             my ( $self ) = @_;
363             my $db = $self->{db};
364             my @data = @{ $db->{RAW}{header_labels} };
365             return \@data;
366             }
367              
368             =head2 get_variable_labels()
369              
370             Returns a reference to an array that contains the variable labels from the RAW file:
371              
372             my $aref = $rjdb->get_variable_labels; # (expected: SEX PHENOTYPE var1 ...)
373              
374             =cut
375              
376             sub get_variable_labels
377             {
378             # Returns an aref of the variable labels from the RAW file (expected: SEX PHENOTYPE var1 ...)
379             my ( $self ) = @_;
380             my $db = $self->{db};
381             #my @data = @{ $db->{RAW}{variable_labels} };
382             my @data = split( "\t", $db->{RAW}{variable_labels_str} );
383             return \@data;
384             }
385              
386             =head2 get_sample_labels()
387              
388             Returns a reference to an array that contains the sample labels from the IID column of the RAW file:
389              
390             my $aref = $rjdb->get_sample_labels;
391              
392             =cut
393              
394             sub get_sample_labels
395             {
396             # Returns an aref of sample labels from the IID column of the RAW file
397             my ( $self ) = @_;
398             my $db = $self->{db};
399             #my @data = @{ $db->{RAW}{sample_labels} };
400             my @data = split( "\t", $db->{RAW}{sample_labels_str} );
401             return \@data;
402             }
403              
404             =head2 get_sample_data()
405              
406             Returns a hash ref containing data for the sample specified by label => $label, where label is
407             the IID from the RAW file. Sets err_str and returns undef if label is not specified or is invalid.
408              
409             my $href = $rjdb->get_sample_data( label => $label ) || warn $rjdb->err_str;
410              
411             $href has the following structure:
412             SEX => $val,
413             PHENOTYPE => $val,
414             orig_data => $line, (unsplit, unspliced)
415             index => $i, (index in aref from get_sample_labels(), can be used to index into OOB matrix)
416             classification_data => $aref, (can be passed to RandomJungle::Tree->classify_data)
417              
418             =cut
419              
420             sub get_sample_data
421             {
422             # Returns sample data specified by label => $label, where label is the IID from the RAW file
423             # Sets err_str and returns undef if label is not specified or is invalid
424             my ( $self, %args ) = @_;
425              
426             if( ! defined $args{label} )
427             {
428             $self->set_err( 'Cannot retrieve sample data without sample label' );
429             return;
430             }
431              
432             my $db = $self->{db};
433             my $label = $args{label};
434              
435             # Devel::Cover has a bug that doesn't detect coverage on this statement with a DBM::Deep
436             # hash. See https://rt.cpan.org/Ticket/Display.html?id=72027 for the bug report.
437             if( ! exists $db->{RAW}{raw_data}{$label} )
438             {
439             $self->set_err( "Error retrieving sample data - invalid label ($label)" );
440             return;
441             }
442              
443             my %data = %{ $db->{RAW}{raw_data}{$label} };
444             $data{label} = $label;
445              
446             # Prepare an array of data values that is suitable for passing to ::Tree->classify_data
447             # Requires splitting and splicing off the non-variable elements
448             my @data = split( / /, $data{orig_data} ); # FID IID PAT MAT SEX PHENOTYPE ...
449             my ( $fid, $iid, $pat, $mat ) = splice( @data, 0, 4 );
450             $data{classification_data} = \@data;
451              
452             return \%data;
453             }
454              
455             =head2 set_err()
456              
457             Sets the error message (provided as a parameter) and creates a stack trace:
458              
459             $rjdb->set_err( 'Something went boom' );
460              
461             =cut
462              
463             sub set_err
464             {
465             my ( $self, $errstr ) = @_;
466              
467             $self->{err_str} = $errstr || '';
468             $self->{err_trace} = Devel::StackTrace->new;
469             }
470              
471             =head2 err_str()
472              
473             Returns the last error message that was set:
474              
475             my $msg = $rjdb->err_str;
476              
477             =cut
478              
479             sub err_str
480             {
481             my ( $self ) = @_;
482              
483             return $self->{err_str};
484             }
485              
486             =head2 err_trace()
487              
488             Returns a backtrace for the last error that was encountered:
489              
490             my $trace = $rjdb->err_trace;
491              
492             =cut
493              
494             sub err_trace
495             {
496             my ( $self ) = @_;
497              
498             return $self->{err_trace}->as_string;
499             }
500              
501             #*********************************************************************
502             # Private Methods and Routines
503             #*********************************************************************
504              
505             sub _init
506             {
507             # sets $ERROR and returns undef if $args{db_file} is not defined
508             my ( $self, %args ) = @_;
509              
510             if( ! defined $args{db_file} )
511             {
512             $ERROR = "Cannot create new object - db_file is a required parameter";
513             return;
514             }
515              
516             $self->_db_connect( $args{db_file} );
517             }
518              
519             sub _db_connect
520             {
521             my ( $self, $file ) = @_;
522              
523             # removed check so does not prevent creation of new dbm file
524             #if( ! -e $file )
525             #{
526             # carp "Error - cannot connect to db: $file does not exist";
527             # return;
528             #}
529              
530             $self->{params}{db_file} = $file;
531              
532             my $db = DBM::Deep->new( $file ); # apparently no risk of failure (see docs)
533              
534             $self->{db} = $db;
535             return 1;
536             }
537              
538             sub _load_xml_file
539             {
540             # returns true on success, sets err_str and returns undef on failure
541             # overwrites any existing XML entries in the db
542             my ( $self, $file ) = @_;
543              
544             my $rj_xml = RandomJungle::File::XML->new( filename => $file ) ||
545             do
546             {
547             # need to preserve the error from the original class
548             my $err = $RandomJungle::File::XML::ERROR;
549             $self->set_err( $err );
550             return;
551             };
552              
553             $rj_xml->parse ||
554             do
555             {
556             # need to preserve the error from the original class
557             my $err = join( "\n", $rj_xml->err_str, $rj_xml->err_trace );
558             $self->set_err( $err );
559             return;
560             };
561              
562             my $db = $self->{db};
563             $db = $db->{XML} = {}; # will overwrite existing
564              
565             $db->{filename} = $rj_xml->get_filename;
566             $db->{options} = $rj_xml->get_RJ_input_params;
567             $db->{tree_data} = $rj_xml->get_tree_data;
568             #$db->{tree_ids} = $rj_xml->get_tree_ids;
569             $db->{tree_ids_str} = join( "\t", @{ $rj_xml->get_tree_ids } );
570              
571             return 1;
572             }
573              
574             sub _load_oob_file
575             {
576             # returns true on success, sets err_str and returns undef on failure
577             # overwrites any existing OOB entries in the db
578             my ( $self, $file ) = @_;
579              
580             my $rj_oob = RandomJungle::File::OOB->new( filename => $file ) ||
581             do
582             {
583             # need to preserve the error from the original class
584             my $err = $RandomJungle::File::OOB::ERROR;
585             $self->set_err( $err );
586             return;
587             };
588              
589             $rj_oob->parse ||
590             do
591             {
592             # need to preserve the error from the original class
593             my $err = join( "\n", $rj_oob->err_str, $rj_oob->err_trace );
594             $self->set_err( $err );
595             return;
596             };
597              
598             my $db = $self->{db};
599             $db = $db->{OOB} = {}; # will overwrite existing
600              
601             $db->{filename} = $rj_oob->get_filename;
602             $db->{matrix} = $rj_oob->get_matrix;
603              
604             return 1;
605             }
606              
607             sub _load_raw_file
608             {
609             # returns true on success, sets err_str and returns undef on failure
610             # overwrites any existing RAW entries in the db
611             my ( $self, $file ) = @_;
612              
613             my $rj_raw = RandomJungle::File::RAW->new( filename => $file ) ||
614             do
615             {
616             # need to preserve the error from the original class
617             my $err = $RandomJungle::File::RAW::ERROR;
618             $self->set_err( $err );
619             return;
620             };
621              
622             $rj_raw->parse ||
623             do
624             {
625             # need to preserve the error from the original class
626             my $err = join( "\n", $rj_raw->err_str, $rj_raw->err_trace );
627             $self->set_err( $err );
628             return;
629             };
630              
631             my $db = $self->{db};
632             $db = $db->{RAW} = {}; # will overwrite existing
633              
634             $db->{filename} = $rj_raw->get_filename;
635             $db->{header_labels} = $rj_raw->get_header_labels;
636             $db->{raw_data} = $rj_raw->get_sample_data;
637              
638             #$db->{variable_labels} = $rj_raw->get_variable_labels;
639             $db->{variable_labels_str} = join( "\t", @{ $rj_raw->get_variable_labels } );
640              
641             #$db->{sample_labels} = $rj_raw->get_sample_labels;
642             my $sample_lbls = $rj_raw->get_sample_labels;
643             $db->{sample_labels_str} = join( "\t", @$sample_lbls );
644              
645             # add index to sample hashes so can easily index into OOB matrix
646             #my $num_samples = scalar @{ $db->{sample_labels} };
647             my $num_samples = scalar @$sample_lbls;
648              
649             foreach my $i ( 0 .. $num_samples-1 )
650             {
651             #my $sample_iid = $db->{sample_labels}[$i];
652             my $sample_iid = $sample_lbls->[$i];
653             $db->{raw_data}{$sample_iid}{index} = $i;
654             }
655              
656             return 1;
657             }
658              
659             =head1 INTERNAL METHODS
660              
661             =head2 _sample_label_to_index()
662              
663             Returns the sample index (row in the RAW file, used to index into the OOB file) for a given
664             sample label (from the IID column in the RAW file). Returns undef if the parameter is undef
665             or if the label is invalid.
666              
667             my $sample_index = $rjdb->_sample_label_to_index( $sample_label ) || warn "Invalid label";
668              
669             =cut
670              
671             sub _sample_label_to_index
672             {
673             # Returns the sample index (row in the RAW file, used to index into the OOB file)
674             # for a given sample label (from the IID column in the RAW file).
675             # Returns undef if the param is undef or an invalid label
676             my ( $self, $label ) = @_;
677              
678             return if( ! defined $label );
679              
680             my $db = $self->{db};
681              
682             # Devel::Cover has a bug that doesn't detect coverage on this statement with a DBM::Deep
683             # hash. See https://rt.cpan.org/Ticket/Display.html?id=72027 for the bug report.
684             return if( ! exists $db->{RAW}{raw_data}{$label} );
685              
686             return $db->{RAW}{raw_data}{$label}{index};
687             }
688              
689             =head1 SEE ALSO
690              
691             RandomJungle::Jungle, RandomJungle::Tree, RandomJungle::Tree::Node,
692             RandomJungle::XML, RandomJungle::OOB, RandomJungle::RAW,
693             RandomJungle::DB, RandomJungle::Classification_DB
694              
695             =head1 AUTHOR
696              
697             Robert R. Freimuth
698              
699             =head1 COPYRIGHT
700              
701             Copyright (c) 2011 Mayo Foundation for Medical Education and Research. All rights reserved.
702              
703             This program is free software; you can redistribute it and/or modify
704             it under the same terms as Perl itself.
705              
706             The full text of the license can be found in the
707             LICENSE file included with this module.
708              
709             =cut
710              
711             #*********************************************************************
712             # Guts
713             #*********************************************************************
714              
715             =begin guts
716              
717             $self
718             params
719             db_file => $filename
720             xml_file => $filename
721             oob_file => $filename
722             raw_file => $filename
723             $args => $val (passed as params to new)
724             db => $dbm_deep_object
725             err_str => $errstr
726             err_trace => Devel::StackTrace object
727              
728             $dbm_deep_object
729             XML
730             filename => $filename
731             options
732             $name => $value (input parameters for RJ)
733             tree_data
734             $tree_id
735             id => $tree_id, (also used to index into OOB matrix (column within $line)
736             var_id_str => varID string from XML, e.g., '((490,967,1102,...))'
737             values_str => values string from XML, e.g., '(((0)),((0)),((1)),...)'
738             branches_str => branches string from XML, e.g., '((1,370),(2,209),(3,160),...)'
739             #tree_ids => [ $tree_id, ... ] # no longer used, stored as string for efficient retrieval
740             tree_ids_str => join( "\t", $tree_id, ... )
741             OOB
742             filename => $filename
743             matrix => [ $line, ... ]
744            
745             RAW
746             filename => $filename
747             header_labels => [ FID, IID, PAT, MAT ] (expected)
748             #variable_labels => [ SEX, PHENOTYPE, rs... ] # no longer used, stored as string for efficient retrieval
749             variable_labels_str => join( "\t", qw( SEX PHENOTYPE rs... ) )
750             #sample_labels => [ $iid, ... ] # no longer used, stored as string for efficient retrieval
751             sample_labels_str => join( "\t", $iid, ... )
752             raw_data
753             $iid
754             SEX => $val
755             PHENOTYPE => $val
756             orig_data => $line (unsplit, unspliced)
757             index => $i (order in sample array, used to index into OOB matrix)
758              
759              
760             =cut
761              
762             1;
763