pax_global_header00006660000000000000000000000064130410021330014475gustar00rootroot0000000000000052 comment=02c59f31dd3637fefef865edb6dff779d077dce7 perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/000077500000000000000000000000001304100213300212235ustar00rootroot00000000000000perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/Changelog000066400000000000000000000557621304100213300230540ustar00rootroot0000000000000022/1/17 (PerlPrimer-1.2.3) - Added the option of using Onodera and Melcher (2004) rules for the 3' GC clamp. This option is not currently enabled by default, but can be selected in the prefs "Exclusions" tab 21/1/17 (PerlPrimer-1.2.2) - Added a "-1" button to the range options: use "Set from ORF" and then "-1" to only find primers that cover the entire ORF. 20/1/17 (PerlPrimer-1.2.1) - Added a name column when copying primers (the name is built from the gene name, the coordinates of the primer and the direction). 16/1/17 (PerlPrimer-1.2) - Re-wrote Ensembl routines to use the Ensembl REST API (a much more elegant solution -- thanks to Ensembl for developing this). Ensembl compatibility should never break again. (Some code tidying still needs to be done to remove some redundancies, but the code is fully functional. Please note that Ensembl REST sadly provides no fuzzy searching for genes: the correct gene name or Ensembl ID must be used.) 17/2/11 (PerlPrimer-1.1.20) - Fixed Ensembl compatibility - Fixed small error in Tm calculation code (thanks to Henning Lenz for pointing this out) - Changed browser launching code in Windows (used for viewing alternative transcript data) It's not particularly nice (still blocks the GUI) but it does seem to work. 17/3/10 (PerlPrimer-1.1.19) - Fixed Ensembl compatibility again (Ensembl search now lists the genes first, without transcript info) 9/10/09 (PerlPrimer-1.1.18) - Fixed Ensembl compatibility - Added many more Ensembl genomes to the dropdown list 24/3/09 (PerlPrimer-1.1.17) - Fixed Ensembl retrieval of genomic sequences (thanks to Karl Kashofer for this fix!) 10/12/08 (PerlPrimer-1.1.16) - Fixed general compatibility with Ensembl database - New fix for Ensembl transcripts recognition -- should find transcripts from all Ensembl databases 31/08/07 (PerlPrimer-1.1.15) - Ensembl compatibility restored for Arabidopsis genes 18/10/06 (PerlPrimer-1.1.14) - Ensembl compatibility restored once again (slight change in the searchview output was causing problems) 22/6/06 (PerlPrimer-1.1.13) - Ensembl compatibility restored again (back to searchview) - Added code to check for the presence of Genomic and mRNA seqs before running spidey (error message that resulted otherwise was confusing to users) 5/6/06 (PerlPrimer-1.1.12) - Retrieval of un-annotated genes from Ensembl (using the Ensembl Gene ID) is now possible - BLAST search results compatibility restored - ORF calculation re-written to allow for transciption initiation from TTG, GTG and CTG codons, in the absence of an initiating ATG 31/5/06 (PerlPrimer-1.1.11) - Ensembl compatibility restored yet again (Ensembl now apparently back to using a modified text view rather than searchview) - Better error notification if the Ensembl format changes yet again - Fixed a small bug related to finding the GCG restriction enzyme file in the user's home directory 20/3/06 (PerlPrimer-1.1.10) - Ensembl compatibility yet again restored - Button in Ensembl transcripts dialogue to allow user to open a browser window to view the different transcripts (on the Ensembl gene web page) - Option in Preferences->Network to specify web browser to use (may not work under Win32 - seems to use default system browser regardless) - List of Ensembl genome databases updated - Translation from forward primer to ORF start is now shown in a lighter colour - Active buttons in dialogues are now invoked by pressing - Update to checkbutton referencing code to support later versions of Perl/Tk under *nix - Slight cosmetic changes to GUI colours 8/9/05 (PerlPrimer-1.1.9) - Ensembl compatibility restored once again - Slight change to Spidey error dialogue to reflect the possibility that the error might be Ensembl-related 9/8/05 (PerlPrimer-1.1.8) - Ensembl compatibility is restored, and now allows the user to select the desired transcripts from genes - View intron/exon structure now provides a indication of sequence length - Minor code tidying 30/05/05 (PerlPrimer-1.1.7) - Added "Span Intron/Exon Boundary" option for QPCR - this is selected by default and was the previous default behaviour; however, it is now possible to design primers for sequences without introns (e.g. 18S) - Added GC% information for Primer information tab, and adjusted the layout - Ensembl genome databases updated (added Canis_familiaris, Gallus_gallus, Pan_troglodytes, Saccharomyces_cerevisiae, Tetraodon_nigroviridis, Xenopus_tropicalis; removed Caenorhabditis_briggsae) - Retieve gene from Ensembl function now always prompts user to select from the list of matching gene names, even if only one name matches - this ensures that the correct gene has been identified (Ensembl occasionally has some unexpected single matches to queries) - Some Ensembl search matches were not detected correctly due to a slight change in the HTML output from Ensembl - this is now adjusted for - Added word-size option to blast searching - BLAST now uses the more correct word size of 7 bases by default - Blast search now uses real clock measurments to show elapsed time, rather than just calculating the number of times the routine had been called - Default interface font on Windows is now Arial instead of MS Sans Serif - using a TrueType font such as Arial allows nice sub-pixel rendering on LCD displays - Updated Rebase restriction enzyme file to gcg.505 - BUGFIX: Sequence right-click menu works again - BUGFIX: Save FASTA dialogue now works correctly - BUGFIX: DNA graphic and primers are now resized when window is resized 01/04/05 (PerlPrimer-1.1.6) - BUGFIX: Problems in Tm and dG calculation that prevented program startup when [dNTPs] > [Mg++] - quick fix provided to prevent this by keeping the value of sqrt([Mg++] - [dNTPs]) as 0. A warning prompt is displayed if this situation is set in the preferences - BUGFIX: Oligo concentration in the preferences was incorrectly described as being in mM when it is in fact calculated as nM - Acknowledgment of Rebase files provided in the acknowledgements dialogue - Tutorial html file from http://perlprimer.sf.net added to distribution package 21/12/04 (PerlPrimer-1.1.5) - Old preferences are automatically copied to the new home directory location when upgrading - Win32 executable built with ActivePerl 5.8.6 and PAR 0.86 20/12/04 - PerlPrimer now translates in frame from the forward primer if this starts upstream of the ORF (allows easy identification of stop codons when designing cloning primers) - Better handling of information from FASTA files - FASTA header now automatically becomes the file name - Several minor bug fixes and code-tidying 03/11/04 - Home and temporary directory fixes - directories are now set from Perl environmental variables if possible; tmp directory set to home if user does not have correct read/write permissions or if directory does not exist - Full primer-dimer values are calculated as well as extensible primer-dimers, and results are sorted based on both values 01/09/04 - BLAST search can now use a local blast database and server - "View intron/exon structure" button in QPCR tab graphically draws the exon/intron structure of the genomic DNA (thus showing the size of the introns) - New browse directory code (requires a recent version of Perl/Tk to run correctly) - BUGFIX: Saving .ppr files did not work correctly under some Perl distributions - Complete re-write of the internal GUI code (now made more extensible and more comprehensible) 13/07/04 (PerlPrimer-1.1.4a) - BUGFIX: "Open Sequence" buttons did not work correctly when opening sequences in FASTA format (or genomic DNA sequence for QPCR tab) - BUGFIX: Bisulphite PCR tab did not display primer information in the status bar when dimer dG == 0 - BUGFIX: Finding sequencing primers without a dG cutoff did not calculate dG for primers - Minor GUI changes for Win32 to work with ActivePerl v5.8.4 (some cosmetic Perl/Tk changes) 21/06/04 (PerlPrimer-1.1.4) - View spidey output command now runs spidey directly - Win32::FileOp code disabled - causes redraw problems with Perl/Tk - Minor code tidying 17/06/04 - BUGFIX: Fixed a bug added in the last version that did not reverse the 3' restriction enzyme site and anchor when adding cloning sequences 28/04/04 - Work begun on unequal loops primer-dimer routines (not fully implemented as yet) 22/04/04 - Code to support Win32::FileOp if it exists under windows for selecting directories, as DirTree seems flakey at best with some extremely eratic behaviour. This is not currently an ActivePerl module, so will only be relevant for the Windows installer version. 21/04/04 (PerlPrimer-1.1.3a) - Windows installer version only - PerlPrimer now searches for Spidey in the PerlPrimer directory on startup - Minor code tidying 19/04/04 (PerlPrimer-1.1.3) - Trial self-executable format for Windows (warning: >3 Mb download) created using PAR (http://par.perl.org) - Save DNA sequence dialogue now has options for both .fasta and .txt formats (default is .fasta) - Saving DNA sequence in FASTA format now prompts the user for a description line (default is the open file name, if any) - Better processing of FASTA files when opening (whitespace and some illegal characters are removed from the description line when taking the file name) 18/04/04 - Buttons to choose home, tmp, and spidey-location directories in the Preferences dialogue - Rearranged Preferences dialogue: Separate sections for directories and behaviour when opening new files in the "General" tab; defer-to-capitalised-regions option is now in the "Bioinformatics" tab 15/04/04 - Checkbox now present to control whether QPCR primers need to span an intron/exon boundary or not (old method from 02/04/04 still works as well) - When QPCR data are retrieved from Ensembl, Spidey is run automatically and intron/exon boundaries are shown on the DNA graphic - QPCR exon limits can now be selected with the mouse on the DNA graphic - use the left button to select the 5' range and the middle button (or Ctrl-Button 1) to select the 3' range (i.e. just like setting the range for other PCR methods) - Species name is now added to the default file name when a sequence is retrieved from Ensembl - Path to system temporary directory can be set in the Preferences - BUGFIX: Generate report did not save a report if the perlprimer file had not been saved - BUGFIX: PerlPrimer now handles degenerate sequences better (including gapped sequences) when finding ORFs - BUGFIX: If default values were 0 they were not restored to default when opening a new file - Code tidying and new balloon help messages 14/04/04 - QPCR can now select primers targeting a range of exons or a single exon (suggested by Alf Eaton) - BUGFIX: temporary dna and mrna files for Spidey are now saved in the system tmp directory, rather than the directory where Spidey is located (suggested by Alf Eaton) 02/04/04 - QPCR can now design primers that surround an intron/exon boundary but do not span the boundary (delete the value in the "Minimum Intron/Exon boundary" box for this to work (now deprecated - see above)) (suggested by John Luckey) 01/04/04 - Changes to dialogue box code - Minor code tidying 31/03/04 - Preferences option to automatically find primers when receiving data from external applications through the tcp socket - Opening a new file when previous data exists now prompts the user to save (may be overridden in the preferences) - Opening a file now explicitly clears all data before opening 30/03/04 - TCP socket reading added for interaction between PerlPrimer and Contig Viewer 29/03/04 (PerlPrimer-1.1.2) - BUGFIX: After finding sequencing primers, primer-dimer finding routine was not reset to only search for extensible dimers - Minor changes to DNA sequence opening routines to make use of the improved FASTA header functions 24/03/04 - BUGFIX: a simple bug in the CpG island detection algorithm prevented small islands from being reported - BUGFIX: PerlPrimer under MacOS X now correctly detects it is running under a unix environment - Better routines for finding the Spidey executable on all platforms (perlprimer now uses a *pidey.* glob and picks the largest file from all matches, in case the archive file is present in the same directory) - Perlprimer now screens each primer for degenerate/non-DNA symbols and will not calculate Tm if these are present 22/03/04 - All fonts (main gui font, primer list font and fixed font for sequences) can now be specified in the preferences (suggested by Alf Eaton) 16/03/04 (PerlPrimer-1.1.1) - Added the .fasta file type to the Open File dialogue box 15/03/04 - New feature: PerlPrimer can save current parameters as defaults for each PCR tab (Use the Tools menu option "Save default values for this page"). Values are saved to files in the user's home directory, ".perlprimer.[name_of_page]". (Use the Tools menu option "Restore in-built default values" to delete this file) 11/03/04 - New feature: PerlPrimer can now open FASTA files with range information included in the FASTA header - syntax for the first line is ">Name of DNA sequence 5prime_region[?-?] 3prime_region[?-?] page[?]" where the page argument is optional (defaults to the standard PCR page) and where page 1 = standard PCR, page 2 = bisulphite PCR, etc. Suggested by Alexander Kozik, for use by Contig Viewer (http://www.atgc.org/Py_ContigViewer/) 09/03/04 (PerlPrimer-1.1) - Minor code tidying 02/03/04 - New icons for opening and saving DNA sequences - BUGFIX: a workaround for a very weird Perl/Tk bug - if the Preferences dialogue was opened and closed twice, PerlPrimer stopped doing floating point arithmetic (?!). 27/02/04 - If multiple genes match the expression used to search the ensembl database, PerlPrimer now displays the complete list and allows the user to choose any of the descriptions (or to cancel and refine the search) - Primer-dimer routines revised again - previous method was not considering that initial/terminal mismatches will not contribute to dimer instability and thus should not be included when calculating dG values - New feature: Users can select the temperature at which to calculate primer-dimer dG in the preferences - Default MRU file list is now 8 files 23/02/04 - New feature: Users can disable repeat/run checking in the Preferences window - Completed abstraction of the add_cloning subroutine - BUGFIX: Preference writing routine now saves the MRU file list when closing preferences dialogue (previously the MRU file list was lost if the preferences were changed and then PerlPrimer was killed) - Several minor bug fixes - General code tidying 18/02/04 - New feature: "Find primers for cloning" menu option (in the tools menu) - only available in standard PCR mode, reduces the 5' outer range by 10 bp increments and the 3' inner range by 10 bp increments until primers are found - Renamed "Auto +", "Auto -" buttons to "Find outwards" and "Find inwards" 05/02/04 - New feature: "Add cloning sequences" dialogue. PerlPrimer now uses the GCG restriction enzyme database file from REBASE to list the non-cutting restriction enzymes within a sequence when in Standard PCR mode. To use, select "Add cloning sequences" from the Tools menu. (note: the file 'gcg.###' - included with the distribution - will need to be installed in either your home directory or in the same directory as PerlPrimer for this to work) - Minor bug fixes 02/02/04 (PerlPrimer-1.0.2) - Added a Recently Opened Files menu (number of files is customisable through the preferences) - Added bindings for left and right arrow keys when focussed in the primer lists - allows the user to quickly switch to the primer-dimer tab (with the right arrow key) and back again (with the left arrow key). Note that up and down arrow keys will still move through the primer list as before - PerlPrimer now deletes the dna and mrna temporary files required by Spidey after use - The %GC above and below which to exclude primers automatically can now be set in the preferences (before it was between 40% and 60%) - Several cosmetic changes: - look-and-feel of menubar and toolbar is now OS dependent - fonts are consistent across all widgets (exception under windows is the HList font - set to be verdana at one point less than the user assigned font size) - most fonts may be overridden by system defaults if desired - the notebook widget on Win32 now has shaded inactive tabs - minor interface tidying - If sequencing primers cannot be found within the specified read length between primers, a dialogue is now displayed and the run is aborted (previously, the program would find the next best primer, which could be at a distance much greater than that specified) - Slight modifications to the preference reading/writing routines to allow array data to be saved (needed for the recently used files list) - Open and Save file functions now list the percentage opened/saved on the status line - The dG hash for calculating primer-dimer stability is now calculated from the current salt conditions and the enthalpy and entropy hashes - meaning that primer-dimer calculations are much more accurate. (note that if changing salt concentrations in the preferences, OK needs to be pressed before the hash is recalculated) - Reworked menus in the new (and more correct) Perl/Tk format - Re-ordering of File menu items - "Sort primers" popup menu is now a sub-menu - Minor bugfixes and code tidying 23/1/04 (PerlPrimer-1.0.1) - PerlPrimer now uses eval routines to check for all perl modules, and either exits with a useful message or disables some functions if a module is not found. - Several minor bugs fixed/general code tidying. - NB: Previous public release (1.0) had an older copy of perlprimer (Beta 5) by mistake - please upgrade if using that release as several important bugs have been fixed. 18/12/03 (PerlPrimer-1.0) - BUGFIX: Generate Report (and copy printable) now includes the last line of sequence - Minor code tidying 16/12/03 (PerlPrimer-1.0_Beta_6) - Rehashed primer-dimer routine (again!) - routine now only searches for *extensible* 3' primer-dimers when searching for primer pairs, and pd_full now searches for all other primer-dimers (3' and 5'). This equates to approximately a 100% increase in speed when searching for primers, and provides more useful information to the end user. - BUGFIX: Generate Report now provides the correct information for amplicon size and primer positions. - Partially abstracted the added-cloning-sequence routines to a subroutine (only implemented for the dna_magnify routine as yet) 12/12/03 - BUGFIX: removed a few extraneous bitwise ands that were lying around (caused problems with some Perl distributions) - suggested by Chris Vega - BUGFIX: made all Tk calls use a dash for each option key (caused problems on some systems) - suggested by Chris Vega 11/12/03 (PerlPrimer-1.0_Beta_5) 10/12/03 - BLAST search now has a (semi-fake) elapsed time display while searching (counts 15 seconds per search interval, which should be approximately correct!) - BUGFIX: Many minor bugs squashed, including several problems that occured when range values were defined but 0 - Open sequence will accept and work with multi-lined entries (preventing slowdowns, although ORF finding will still convert to a single lined entry - this needs to be changed) - Many other minor changes/tidying - Balloon help texts updated for the new options (sequencing page, pref options, etc) 07/12/03 - New feature: Sequencing page (finds primers spaced at a user-defined interval along a sequence) - Gui packing system greatly reworked, now uses far less reliance on the grid geometry manager and less frames to achieve layout. A new subroutine, nr() (=newrow) is used to create new frame rows, and all widgets are packed into the last row created from the left - simpler and easier. Should also be slight faster when redrawing on old systems. - BUGFIX: QPCR tab was not assigned a "File not saved" description, thus not displaying the open file name correctly. - Reworked the primer windowing subroutines to now use a two dimensional array to store primer details (slightly faster and much neater) - New feature: PerlPrimer no longer defers to capitalised regions by default but uses the ORF/CpG finding algorithms instead (less confusing and more useful) Defer to capitalised regions is now a preferences option. - New feature: PerlPrimer no longer emulates cpgplot behaviour by default, but uses the "correct" algorithm instead. Emulate cpgplot behaviour is now a preferences option. - Dialogues are now unique (for non-static dialogues the old is destroyed before the new one is created); - Main window now has a minimum size (= the default packing size when the program is started) to prevent widgets disappearing when the window is resized too small. - General code tidying and far too many small changes to list independantly. 27/11/03 (perlprimer-1.0_Beta_4) - Redesigned Tm and Primer Size widgets, and amplicon size widgets in QPCR tab 26/11/03 - File saving reworked: PerlPrimer now has standard "Save"/"Save as" functionality and behaves as expected - Title of program now displays the name of the file being worked on, even if multiple files are open at the same time - Default extention of saved sequences and reports is now .txt 25/11/03 - New feature: proxy server support (enter in preferences) - will still use env_proxy if box unchecked - File open/save now allows multiple line DNA sequences - prevents slowdown for large genomic sequences used for QPCR - Default exclude_ie value is now 7 (was 5, which generates far too many primers for most genes and is not as specific) - BUGFIX: intron-exon boundary checking was not quite right with exclude_ie - this is now fixed. 24/11/03 (perlprimer-1.0_Beta_3) - New feature: retrieve genes from Ensembl - which also will automatically retrieve genomic and cdna sequence for QPCR - New QPCR option: minimum intron/exon boundary - sets minimum overlap of primers across intron/exon boundary - Minor code tidying/fixes 20/11/03 (perlprimer-1.0_Beta_2) - BUGFIX: fixed infinite loop that occurred when no primers were found using the "Auto +" button - pp_open_file routine now checks that a PerlPrimer file has in fact been selected and prompts if not - "Generate Report" now includes Amplicon size and primer position information - Minor code tidying 13/11/03 (perlprimer-1.0_Beta_1) - Preferences dialogue now in NoteBook format - Preference options for BLAST searching (and associated modification of BLAST subroutine) - "Generate Report" now includes modified primers, file name and date - pack_gui subroutine now supports BrowseEntry widgets (needed for BLAST preferences) - Minor code tidying 11/11/03 (perlprimer-0.99.95) - BUGFIX: primer Tm was not calculated for lower-case primers - get_tm subroutine is now specific for a single primer if only one is entered - Minor code changes/tidying 28/10/03 (perlprimer-0.99.9) - "New File" feature added (see new_file subroutine) - Changes to dialogue subroutine to allow different dialogue types - New button-packing code for toolbars - Code tidying (especially GUI packing routines)/bug fixes 26/10/03 - "Generate Report" feature added - Toolbar added - Magnify button created next to canvas display - "New File" feature added to menus, but not yet implemented - Several icons added - General code tidying - "Reset" buttons removed - functionality taken over by "Set from ..." buttons 24/10/03 - Initial public release (perlprimer-0.99.8) perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/LICENSE000066400000000000000000000431761304100213300222430ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/README.md000066400000000000000000000037011304100213300225030ustar00rootroot00000000000000# PerlPrimer PerlPrimer is a free, open-source GUI application written in Perl that designs primers for standard PCR, bisulphite PCR, real-time PCR (QPCR) and sequencing. It aims to automate and simplify the process of primer design. PerlPrimer's current features include the following: * Calculation of possible primer-dimers * Retrieval of genomic or cdna sequences from Ensembl (including both sequences automatically for QPCR) * Ability to BLAST search primers using the NCBI server or a local server * Results can be saved or optionally exported in a tab-delimited format that is compatible with most spreadsheet applications. * ORF and CpG island detection algorithms * Ability to add cloning sequences to primers, automatically adjusted to be in-frame * QPCR primer design without manual intron-exon boundary entry PerlPrimer calculates primer melting temperature using J. SantaLucia's extensive nearest-neighbour thermodynamic parameters. To adjust for the salt conditions of the PCR, PerlPrimer uses the empirical formula derived by von Ahsen, et al. (2001) and allows the user to specify the concentration of Mg2+, dNTPs and primers, or use standard PCR conditions. The result is a highly accurate prediction of primer melting temperature, giving rise to a maximum yield of product when amplified. PerlPrimer is written in Perl and Perl/Tk. In addition, for QPCR functionality PerlPrimer uses the open-source Spidey executable from NCBI, and restriction enzyme data from the REBASE project is used when adding cloning sites. The program is designed to be cross-platform compatible and has been developed and tested on both Microsoft Windows and GNU/Linux-based operating systems. Users have also reported success using the program under Mac OS X. Please cite the reference below if this program is useful. Marshall OJ. PerlPrimer: cross-platform, graphical primer design for standard, bisulphite and real-time PCR. Bioinformatics 2004 20(15):2471-2472 [Pubmed] perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/gcg.603000066400000000000000000001532471304100213300222310ustar00rootroot00000000000000 REBASE version 603 gcg.603 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= REBASE, The Restriction Enzyme Database http://rebase.neb.com Copyright (c) Dr. Richard J. Roberts, 2006. All rights reserved. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Rich Roberts Mar 01 2006 REBASE codes for commercial sources of enzymes A GE Healthcare (8/05) B Invitrogen Corporation (8/05) C Minotech Biotechnology (9/05) E Stratagene (9/05) F Fermentas International Inc. (2/06) G Qbiogene (9/05) H American Allied Biochemical, Inc. (9/05) I SibEnzyme Ltd. (2/06) J Nippon Gene Co., Ltd. (8/05) K Takara Bio Inc. (9/05) M Roche Applied Science (8/05) N New England Biolabs (2/06) O Toyobo Biochemicals (9/05) Q Molecular Biology Resources (8/05) R Promega Corporation (9/05) S Sigma Chemical Corporation (9/05) U Bangalore Genei (9/05) V Vivantis Technologies (1/06) X EURx Ltd. (9/05) Y CinnaGen Inc. (9/05) .. AarI 11 CACCTGCnnnn'nnnn_ 4 ! >F ;AasI 7 GACnn_nn'nnGTC -2 ! DrdI,DseDI >F ;AatI 3 AGG'CCT 0 ! StuI,Eco147I,PceI,SseBI >O AatII 5 G_ACGT'C -4 ! ZraI >AFGIKMNORV AccI 2 GT'mk_AC 2 ! FblI,XmiI >ABGJKMNORSU ;AccII 2 CG'CG 0 ! Bsh1236I,BstFNI,BstUI,MvnI >AJK ;AccIII 1 T'CCGG_A 4 ! Aor13HI,BlfI,BseAI,Bsp13I,BspEI,Kpn2I,MroI >GJKR ;Acc16I 3 TGC'GCA 0 ! AviII,FspI,NsbI >IV ;Acc36I 10 ACCTGCnnnn'nnnn_ 4 ! BspMI,BfuAI,BveI >I Acc65I 1 G'GTAC_C 4 ! KpnI,Asp718I >FGINRV ;AccB1I 1 G'GyrC_C 4 ! BanI,BshNI,BspT107I >IV ;AccB7I 7 CCAn_nnn'nTGG -3 ! PflMI,Van91I >IRV ;AccBSI 3 CCG'CTC 0 ! BsrBI,MbiI >IV ;AceIII 13 CAGCTCnnnnnnn'nnnn_ 4 ! > AciI 1 C'CG_C 2 ! BspACI,SsiI >N AclI 2 AA'CG_TT 2 ! Psp1406I >INV ;AclWI 9 GGATCnnnn'n_ 1 ! AlwI,BspPI >I AcoI 1 y'CCGG_r 4 ! >I ;AcsI 1 r'AATT_y 4 ! ApoI,XapI >IMV AcuI 22 CTGAAGnnnnnnnnnnnnnn_nn' -2 ! Eco57I >IN ;AcvI 3 CAC'GTG 0 ! PmaCI,BbrPI,Eco72I,PmlI,PspCI >QX ;AcyI 2 Gr'CG_yC 2 ! BsaHI,BssNI,BstACI,Hin1I,Hsp92I >JM ;AdeI 6 CAC_nnn'GTG -3 ! DraIII >F ;AfaI 2 GT'AC 0 ! RsaI,Csp6I >AK AfeI 3 AGC'GCT 0 ! Eco47III,Aor51HI >IN ;AfiI 7 CCnn_nnn'nnGG -3 ! BsiYI,Bsc4I,BseLI,BslI >V AflII 1 C'TTAA_G 4 ! BfrI,BspTI,Bst98I,MspCI,Vha464I >AJKNO AflIII 1 A'CryG_T 4 ! >GMNS AgeI 1 A'CCGG_T 4 ! AsiGI,BshTI,CspAI,PinAI >GJNR ;AhaIII 3 TTT'AAA 0 ! DraI > AhdI 6 GACnn_n'nnGTC -1 ! Eam1105I,AspEI,BmeRI,DriI,EclHKI >GN ;AhlI 1 A'CTAG_T 4 ! SpeI,BcuI >IV ;AjiI 3 CAC'GTC 0 ! BtrI,BmgBI >F ;AjnI 0 'CCwGG_ 5 ! EcoRII,BptI,BseBI,BstNI,BstOI,Bst2UI,MvaI,Psp6I,PspGI >I AjuI 25 GAAnnnnnnnTTGGnnnnnn_nnnnn' -5 ! >F AjuI 26 CCAAnnnnnnnTTCnnnnnnn_nnnnn' -5 ! >F AleI 5 CACnn'nnGTG 0 ! OliI >N AlfI 24 GCAnnnnnnTGCnnnnnnnnnn_nn' -2 ! >F AloI 25 GAACnnnnnnTCCnnnnnnn_nnnnn' -5 ! >F AloI 25 GGAnnnnnnGTTCnnnnnnn_nnnnn' -5 ! >F AluI 2 AG'CT 0 ! >ABCFGHIJKMNOQRSUVXY AlwI 9 GGATCnnnn'n_ 1 ! AclWI,BspPI >N ;Alw21I 5 G_wGCw'C -4 ! Bbv12I,BsiHKAI >F ;Alw26I 6 GTCTCn'nnnn_ 4 ! BsmAI,BstMAI >FR ;Alw44I 1 G'TGCA_C 4 ! ApaLI,VneI >FJMORS ;AlwFI 6 GAAAynnnnnrTG 0 ? ! > AlwNI 6 CAG_nnn'CTG -3 ! CaiI >N ;Ama87I 1 C'yCGr_G 4 ! AvaI,BmeT110I,BsiHKCI,BsoBI,Eco88I >IV ;Aor13HI 1 T'CCGG_A 4 ! AccIII,BlfI,BseAI,Bsp13I,BspEI,Kpn2I,MroI >K ;Aor51HI 3 AGC'GCT 0 ! Eco47III,AfeI >AK ApaI 5 G_GGCC'C -4 ! Bsp120I,PspOMI >ABFGIJKMNOQRSUVX ;ApaBI 8 GCA_nnnnn'TGC -5 ! BstAPI > ApaLI 1 G'TGCA_C 4 ! Alw44I,VneI >AKNU ;ApeKI 1 G'CwG_C 3 ! TseI >N ApoI 1 r'AATT_y 4 ! AcsI,XapI >N AscI 2 GG'CGCG_CC 4 ! PalAI,SgsI >GN AseI 2 AT'TA_AT 2 ! VspI,PshBI >JNO ;AsiGI 1 A'CCGG_T 4 ! AgeI,BshTI,CspAI,PinAI >IV AsiSI 5 GCG_AT'CGC -2 ! SgfI,RgaI >N ;AspI 4 GACn'n_nGTC 1 ! Tth111I,PflFI,PsyI >M ;Asp700I 5 GAAnn'nnTTC 0 ! XmnI,MroXI,PdmI >M ;Asp718I 1 G'GTAC_C 4 ! KpnI,Acc65I >M ;AspA2I 1 C'CTAG_G 4 ! AvrII,BlnI,XmaJI >IV ;AspCNI 2 GCCGC 0 ? ! > ;AspEI 6 GACnn_n'nnGTC -1 ! Eam1105I,AhdI,BmeRI,DriI,EclHKI >M ;AspLEI 3 G_CG'C -2 ! HhaI,BstHHI,CfoI,Hin6I,HinP1I,HspAI >IV ;AspS9I 1 G'GnC_C 3 ! BmgT120I,Cfr13I,Sau96I >IV ;AssI 3 AGT'ACT 0 ! ScaI,BmcAI,ZrmI >U ;AsuI 1 G'GnC_C 3 ! AspS9I,BmgT120I,Cfr13I,Sau96I > ;AsuII 2 TT'CG_AA 2 ! Bpu14I,Bsp119I,BspT104I,BstBI,Csp45I,NspV,SfuI >C ;AsuC2I 2 CC's_GG 1 ! BcnI,BpuMI,NciI >I ;AsuHPI 13 GGTGAnnnnnnn_n' -1 ! HphI >IV ;AsuNHI 1 G'CTAG_C 4 ! NheI,BmtI >IV AvaI 1 C'yCGr_G 4 ! Ama87I,BmeT110I,BsiHKCI,BsoBI,Eco88I >ABGJKMNORSUX AvaII 1 G'GwC_C 3 ! Bme18I,Eco47I,SinI,VpaK11BI >AGJKMNRSY ;AvaIII 3 ATGCAT 0 ? ! EcoT22I,Mph1103I,NsiI,Zsp2I > ;AviII 3 TGC'GCA 0 ! Acc16I,FspI,NsbI >M AvrII 1 C'CTAG_G 4 ! AspA2I,BlnI,XmaJI >N ;AxyI 2 CC'TnA_GG 3 ! Bse21I,Bsu36I,Eco81I >J BaeI 23 ACnnnnGTAyCnnnnnnn_nnnnn' -5 ! >N BaeI 26 GrTACnnnnGTnnnnnnnnnn_nnnnn' -5 ! >N ;BalI 3 TGG'CCA 0 ! MlsI,MluNI,MscI,Msp20I >AJKR BamHI 1 G'GATC_C 4 ! >ABCFGHIJKMNOQRSUVXY BanI 1 G'GyrC_C 4 ! AccB1I,BshNI,BspT107I >NORU BanII 5 G_rGCy'C -4 ! Eco24I,EcoT38I,FriOI >AGKMNOQRSX ;BanIII 2 AT'CG_AT 2 ! ClaI,Bsa29I,BseCI,BshVI,BspDI,BspXI,Bsu15I,BsuTUI >O ;BauI 1 C'ACGA_G 4 ! BssSI,Bst2BI >F BbeI 5 G_GCGC'C -4 ! NarI,DinI,EgeI,EheI,KasI,Mly113I,SfoI >AK ;Bbr7I 13 GAAGACnnnnnnn'nnnn_ 4 ! BbsI,BpiI,BpuAI,BstV2I > ;BbrPI 3 CAC'GTG 0 ! PmaCI,AcvI,Eco72I,PmlI,PspCI >MO BbsI 8 GAAGACnn'nnnn_ 4 ! BpiI,BpuAI,BstV2I >N ;BbuI 5 G_CATG'C -4 ! SphI,PaeI >R BbvI 13 GCAGCnnnnnnnn'nnnn_ 4 ! BseXI,BstV1I >N ;BbvII 8 GAAGACnn'nnnn_ 4 ! BbsI,BpiI,BpuAI,BstV2I > ;Bbv12I 5 G_wGCw'C -4 ! Alw21I,BsiHKAI >IV BbvCI 2 CC'TCA_GC 3 ! >N BccI 9 CCATCnnnn'n_ 1 ! >N ;Bce83I 22 CTTGAGnnnnnnnnnnnnnn_nn' -2 ! BpuEI > BceAI 17 ACGGCnnnnnnnnnnnn'nn_ 2 ! >N ;BcefI 17 ACGGCnnnnnnnnnnnn'n_ 1 ! BceAI > BcgI 24 CGAnnnnnnTGCnnnnnnnnnn_nn' -2 ! >N BcgI 24 GCAnnnnnnTCGnnnnnnnnnn_nn' -2 ! >N BciVI 12 GTATCCnnnnn_n' -1 ! BfuI >N BclI 1 T'GATC_A 4 ! FbaI,Ksp22I >CFGJMNORSUY ;BcnI 2 CC's_GG 1 ! AsuC2I,BpuMI,NciI >FK ;BcuI 1 A'CTAG_T 4 ! SpeI,AhlI >F BdaI 24 TGAnnnnnnTCAnnnnnnnnnn_nn' -2 ! >F ;BetI 1 w'CCGG_w 4 ! BsaWI > BfaI 1 C'TA_G 2 ! MaeI,FspBI,XspI >N ;BfiI 11 ACTGGGnnnn_n' -1 ! BmrI,BmuI >F ;BfmI 1 C'TryA_G 4 ! BpcI,BstSFI,SfcI >F ;BfrI 1 C'TTAA_G 4 ! AflII,BspTI,Bst98I,MspCI,Vha464I >MO ;BfuI 12 GTATCCnnnnn_n' -1 ! BciVI >F ;BfuAI 10 ACCTGCnnnn'nnnn_ 4 ! BspMI,Acc36I,BveI >N ;BfuCI 0 'GATC_ 4 ! MboI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI >N BglI 7 GCCn_nnn'nGGC -3 ! >ACFGHIJKMNOQRSUVXY BglII 1 A'GATC_T 4 ! >ABCFGHIJKMNOQRSUVXY ;BinI 9 GGATCnnnn'n_ 1 ! AclWI,AlwI,BspPI > BisI 2 GC'n_GC 1 ! >I ;BlfI 1 T'CCGG_A 4 ! AccIII,Aor13HI,BseAI,Bsp13I,BspEI,Kpn2I,MroI >U ;BlnI 1 C'CTAG_G 4 ! AvrII,AspA2I,XmaJI >AKMS BlpI 2 GC'TnA_GC 3 ! Bpu1102I,Bsp1720I,CelII >N ;BmcAI 3 AGT'ACT 0 ! ScaI,AssI,ZrmI >V ;Bme18I 1 G'GwC_C 3 ! AvaII,Eco47I,SinI,VpaK11BI >IV ;Bme1390I 2 CC'n_GG 1 ! ScrFI,BmrFI,BssKI,BstSCI,MspR9I,StyD4I >F Bme1580I 5 G_kGCm'C -4 ! BseSI >N ;BmeRI 6 GACnn_n'nnGTC -1 ! Eam1105I,AhdI,AspEI,DriI,EclHKI >V BmeT110I 2 Cy'CG_rG 2 ! AvaI,Ama87I,BsiHKCI,BsoBI,Eco88I >K ;BmgI 3 GkGCCC 0 ? ! > BmgBI 3 CAC'GTC 0 ! BtrI,AjiI >N BmgT120I 2 GG'n_CC 1 ! AspS9I,Cfr13I,Sau96I >K ;BmiI 3 GGn'nCC 0 ! NlaIV,BspLI,PspN4I >V BmrI 11 ACTGGGnnnn_n' -1 ! BfiI,BmuI >N ;BmrFI 2 CC'n_GG 1 ! ScrFI,Bme1390I,BssKI,BstSCI,MspR9I,StyD4I >V BmtI 5 G_CTAG'C -4 ! NheI,AsuNHI >INV ;BmuI 11 ACTGGGnnnn_n' -1 ! BfiI,BmrI >I ;BoxI 5 GACnn'nnGTC 0 ! PshAI,BstPAI >F ;BpcI 1 C'TryA_G 4 ! BfmI,BstSFI,SfcI >U ;BpiI 8 GAAGACnn'nnnn_ 4 ! BbsI,BpuAI,BstV2I >F BplI 24 GAGnnnnnCTCnnnnnnnn_nnnnn' -5 ! >F BpmI 22 CTGGAGnnnnnnnnnnnnnn_nn' -2 ! GsuI >IN ;BptI 2 CC'w_GG 1 ! EcoRII,AjnI,BseBI,BstNI,BstOI,Bst2UI,MvaI,Psp6I,PspGI >U Bpu10I 2 CC'TnA_GC 3 ! >FINV ;Bpu14I 2 TT'CG_AA 2 ! AsuII,Bsp119I,BspT104I,BstBI,Csp45I,NspV,SfuI >IV ;Bpu1102I 2 GC'TnA_GC 3 ! BlpI,Bsp1720I,CelII >AFK ;BpuAI 8 GAAGACnn'nnnn_ 4 ! BbsI,BpiI,BstV2I >M BpuEI 22 CTTGAGnnnnnnnnnnnnnn_nn' -2 ! >N ;BpuMI 2 CC's_GG 1 ! AsuC2I,BcnI,NciI >V ;BpvUI 4 CG_AT'CG -2 ! PvuI,MvrI,Ple19I >V BsaI 7 GGTCTCn'nnnn_ 4 ! Eco31I,Bso31I,BspTNI >N ;Bsa29I 2 AT'CG_AT 2 ! ClaI,BanIII,BseCI,BshVI,BspDI,BspXI,Bsu15I,BsuTUI >I BsaAI 3 yAC'GTr 0 ! BstBAI,Ppu21I >N BsaBI 5 GATnn'nnATC 0 ! Bse8I,BseJI,MamI >N BsaHI 2 Gr'CG_yC 2 ! AcyI,BssNI,BstACI,Hin1I,Hsp92I >N BsaJI 1 C'CnnG_G 4 ! BseDI,BssECI >N ;BsaMI 7 GAATG_Cn' -2 ! BsmI,Mva1269I,PctI >GR BsaWI 1 w'CCGG_w 4 ! >N BsaXI 21 ACnnnnnCTCCnnnnnnn_nnn' -3 ! >N BsaXI 23 GGAGnnnnnGTnnnnnnnnn_nnn' -3 ! >N ;BsbI 3 CAACAC 0 ? ! > ;Bsc4I 7 CCnn_nnn'nnGG -3 ! BsiYI,AfiI,BseLI,BslI >I ;BscAI 9 GCATCnnnn'nn_ 2 ! SfaNI,LweI > ;BscGI 2 CCCGT 0 ? ! > ;Bse1I 6 ACTG_Gn' -2 ! BsrI,BseNI,BsrSI >IV ;Bse8I 5 GATnn'nnATC 0 ! BsaBI,BseJI,MamI >IV ;Bse21I 2 CC'TnA_GG 3 ! AxyI,Bsu36I,Eco81I >IV ;Bse118I 1 r'CCGG_y 4 ! Cfr10I,BsrFI,BssAI >IV ;BseAI 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,Bsp13I,BspEI,Kpn2I,MroI >CM ;BseBI 2 CC'w_GG 1 ! EcoRII,AjnI,BptI,BstNI,BstOI,Bst2UI,MvaI,Psp6I,PspGI >C ;BseCI 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BshVI,BspDI,BspXI,Bsu15I,BsuTUI >C ;BseDI 1 C'CnnG_G 4 ! BsaJI,BssECI >F ;Bse3DI 8 GCAATG_nn' -2 ! BsrDI,BseMI >IV ;BseGI 7 GGATG_nn' -2 ! FokI,BstF5I,BtsCI >F ;BseJI 5 GATnn'nnATC 0 ! BsaBI,Bse8I,MamI >F ;BseLI 7 CCnn_nnn'nnGG -3 ! BsiYI,AfiI,Bsc4I,BslI >F ;BseMI 8 GCAATG_nn' -2 ! BsrDI,Bse3DI >F BseMII 15 CTCAGnnnnnnnn_nn' -2 ! BspCNI >F ;BseNI 6 ACTG_Gn' -2 ! BsrI,Bse1I,BsrSI >FG ;BsePI 1 G'CGCG_C 4 ! BssHII,PauI >IV BseRI 16 GAGGAGnnnnnnnn_nn' -2 ! >N ;BseSI 5 G_kGCm'C -4 ! Bme1580I >F ;BseXI 13 GCAGCnnnnnnnn'nnnn_ 4 ! BbvI,BstV1I >F ;BseX3I 1 C'GGCC_G 4 ! BstZI,EagI,EclXI,Eco52I >IV BseYI 1 C'CCAG_C 4 ! >N BsgI 22 GTGCAGnnnnnnnnnnnnnn_nn' -2 ! >N ;Bsh1236I 2 CG'CG 0 ! AccII,BstFNI,BstUI,MvnI >F ;Bsh1285I 4 CG_ry'CG -2 ! BsiEI,BstMCI >F ;BshFI 2 GG'CC 0 ! HaeIII,BsnI,BspANI,BsuRI,PhoI >C ;BshNI 1 G'GyrC_C 4 ! AccB1I,BanI,BspT107I >F ;BshTI 1 A'CCGG_T 4 ! AgeI,AsiGI,CspAI,PinAI >F ;BshVI 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BseCI,BspDI,BspXI,Bsu15I,BsuTUI >V ;BsiI 1 C'ACGA_G 4 ! BauI,BssSI,Bst2BI > BsiEI 4 CG_ry'CG -2 ! Bsh1285I,BstMCI >N BsiHKAI 5 G_wGCw'C -4 ! Alw21I,Bbv12I >N ;BsiHKCI 1 C'yCGr_G 4 ! AvaI,Ama87I,BmeT110I,BsoBI,Eco88I >QX ;BsiSI 1 C'CG_G 2 ! HpaII,HapII,MspI >C BsiWI 1 C'GTAC_G 4 ! Pfl23II,PspLI >MNO ;BsiYI 7 CCnn_nnn'nnGG -3 ! AfiI,Bsc4I,BseLI,BslI >M BslI 7 CCnn_nnn'nnGG -3 ! BsiYI,AfiI,Bsc4I,BseLI >GN ;BslFI 15 GGGACnnnnnnnnnn'nnnn_ 4 ! BsmFI,FaqI >I BsmI 7 GAATG_Cn' -2 ! BsaMI,Mva1269I,PctI >JMNOS BsmAI 6 GTCTCn'nnnn_ 4 ! Alw26I,BstMAI >N BsmBI 7 CGTCTCn'nnnn_ 4 ! Esp3I >N BsmFI 15 GGGACnnnnnnnnnn'nnnn_ 4 ! BslFI,FaqI >N ;BsnI 2 GG'CC 0 ! HaeIII,BshFI,BspANI,BsuRI,PhoI >V ;Bso31I 7 GGTCTCn'nnnn_ 4 ! Eco31I,BsaI,BspTNI >IV ;BsoBI 1 C'yCGr_G 4 ! AvaI,Ama87I,BmeT110I,BsiHKCI,Eco88I >N ;Bsp13I 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,BseAI,BspEI,Kpn2I,MroI >IV ;Bsp19I 1 C'CATG_G 4 ! NcoI >IV ;Bsp24I 24 GACnnnnnnTGGnnnnnnn_nnnnn' -5 ! > ;Bsp24I 25 CCAnnnnnnGTCnnnnnnnn_nnnnn' -5 ! > ;Bsp68I 3 TCG'CGA 0 ! NruI,BtuMI >F ;Bsp119I 2 TT'CG_AA 2 ! AsuII,Bpu14I,BspT104I,BstBI,Csp45I,NspV,SfuI >F ;Bsp120I 1 G'GGCC_C 4 ! ApaI,PspOMI >FG ;Bsp143I 0 'GATC_ 4 ! MboI,BfuCI,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI >F ;Bsp143II 5 r_GCGC'y -4 ! HaeII,BstH2I >F Bsp1286I 5 G_dGCh'C -4 ! SduI,MhlI >JKNR ;Bsp1407I 1 T'GTAC_A 4 ! BsrGI,BstAUI,SspBI >FK ;Bsp1720I 2 GC'TnA_GC 3 ! BlpI,Bpu1102I,CelII >IV ;BspACI 1 C'CG_C 2 ! AciI,SsiI >I ;BspANI 2 GG'CC 0 ! HaeIII,BshFI,BsnI,BsuRI,PhoI >X BspCNI 14 CTCAGnnnnnnn_nn' -2 ! BseMII >N ;BspDI 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BseCI,BshVI,BspXI,Bsu15I,BsuTUI >N BspEI 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,BseAI,Bsp13I,Kpn2I,MroI >N ;BspGI 3 CTGGAC 0 ? ! > BspHI 1 T'CATG_A 4 ! PagI,RcaI >N ;BspLI 3 GGn'nCC 0 ! NlaIV,BmiI,PspN4I >F ;BspLU11I 1 A'CATG_T 4 ! PciI,PscI >M BspMI 10 ACCTGCnnnn'nnnn_ 4 ! Acc36I,BfuAI,BveI >N ;BspMII 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,BseAI,Bsp13I,BspEI,Kpn2I,MroI > ;BspMAI 5 C_TGCA'G -4 ! PstI >X ;BspNCI 2 CCAGA 0 ? ! > ;BspPI 9 GGATCnnnn'n_ 1 ! AclWI,AlwI >F ;BspTI 1 C'TTAA_G 4 ! AflII,BfrI,Bst98I,MspCI,Vha464I >F ;BspT104I 2 TT'CG_AA 2 ! AsuII,Bpu14I,Bsp119I,BstBI,Csp45I,NspV,SfuI >K ;BspT107I 1 G'GyrC_C 4 ! AccB1I,BanI,BshNI >K ;BspTNI 7 GGTCTCn'nnnn_ 4 ! Eco31I,BsaI,Bso31I >X ;BspXI 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BseCI,BshVI,BspDI,Bsu15I,BsuTUI >G BsrI 6 ACTG_Gn' -2 ! Bse1I,BseNI,BsrSI >N BsrBI 3 CCG'CTC 0 ! AccBSI,MbiI >N BsrDI 8 GCAATG_nn' -2 ! Bse3DI,BseMI >N BsrFI 1 r'CCGG_y 4 ! Cfr10I,Bse118I,BssAI >N BsrGI 1 T'GTAC_A 4 ! Bsp1407I,BstAUI,SspBI >N ;BsrSI 6 ACTG_Gn' -2 ! BsrI,Bse1I,BseNI >R ;BssAI 1 r'CCGG_y 4 ! Cfr10I,Bse118I,BsrFI >C ;BssECI 1 C'CnnG_G 4 ! BsaJI,BseDI >I BssHII 1 G'CGCG_C 4 ! BsePI,PauI >AJKMNOQRSX ;BssKI 0 'CCnGG_ 5 ! ScrFI,Bme1390I,BmrFI,BstSCI,MspR9I,StyD4I >N ;BssMI 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BstKTI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI >V ;BssNI 2 Gr'CG_yC 2 ! AcyI,BsaHI,BstACI,Hin1I,Hsp92I >V ;BssNAI 3 GTA'TAC 0 ! Bst1107I,BstZ17I >IV BssSI 1 C'ACGA_G 4 ! BauI,Bst2BI >N ;BssT1I 1 C'CwwG_G 4 ! StyI,Eco130I,EcoT14I,ErhI >IV ;Bst6I 7 CTCTTCn'nnn_ 3 ! Ksp632I,Eam1104I,EarI >IV ;Bst98I 1 C'TTAA_G 4 ! AflII,BfrI,BspTI,MspCI,Vha464I >R ;Bst1107I 3 GTA'TAC 0 ! BssNAI,BstZ17I >FKM ;BstACI 2 Gr'CG_yC 2 ! AcyI,BsaHI,BssNI,Hin1I,Hsp92I >I BstAPI 7 GCAn_nnn'nTGC -3 ! >IN ;BstAUI 1 T'GTAC_A 4 ! Bsp1407I,BsrGI,SspBI >IV BstBI 2 TT'CG_AA 2 ! AsuII,Bpu14I,Bsp119I,BspT104I,Csp45I,NspV,SfuI >N ;Bst2BI 1 C'ACGA_G 4 ! BauI,BssSI >IV ;BstBAI 3 yAC'GTr 0 ! BsaAI,Ppu21I >IV ;Bst4CI 3 AC_n'GT -1 ! HpyCH4III,TaaI >IV ;BstC8I 3 GCn'nGC 0 ! Cac8I >I ;BstDEI 1 C'TnA_G 3 ! DdeI,HpyF3I >IV ;BstDSI 1 C'CryG_G 4 ! BtgI >IV BstEII 1 G'GTnAC_C 5 ! BstPI,Eco91I,EcoO65I,PspEI >GHJMNORSU ;BstENI 5 CCTnn'n_nnAGG 1 ! EcoNI,XagI >IV BstF5I 7 GGATG_nn' -2 ! FokI,BseGI,BtsCI >INV ;BstFNI 2 CG'CG 0 ! AccII,Bsh1236I,BstUI,MvnI >IV ;BstH2I 5 r_GCGC'y -4 ! HaeII,Bsp143II >IV ;BstHHI 3 G_CG'C -2 ! HhaI,AspLEI,CfoI,Hin6I,HinP1I,HspAI >IV BstKTI 3 G_AT'C -2 ! MboI,BfuCI,Bsp143I,BssMI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI >I ;BstMAI 6 GTCTCn'nnnn_ 4 ! BsmAI,Alw26I >IV ;BstMBI 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,DpnII,Kzo9I,NdeII,Sau3AI >IV ;BstMCI 4 CG_ry'CG -2 ! Bsh1285I,BsiEI >IV ;BstMWI 7 GCnn_nnn'nnGC -3 ! MwoI,HpyF10VI >I BstNI 2 CC'w_GG 1 ! EcoRII,AjnI,BptI,BseBI,BstOI,Bst2UI,MvaI,Psp6I,PspGI >N ;BstNSI 5 r_CATG'y -4 ! NspI,XceI >IV ;BstOI 2 CC'w_GG 1 ! EcoRII,AjnI,BptI,BseBI,BstNI,Bst2UI,MvaI,Psp6I,PspGI >R ;BstPI 1 G'GTnAC_C 5 ! BstEII,Eco91I,EcoO65I,PspEI >K ;BstPAI 5 GACnn'nnGTC 0 ! PshAI,BoxI >IV ;BstSCI 0 'CCnGG_ 5 ! ScrFI,Bme1390I,BmrFI,BssKI,MspR9I,StyD4I >I ;BstSFI 1 C'TryA_G 4 ! BfmI,BpcI,SfcI >I ;BstSNI 3 TAC'GTA 0 ! SnaBI,Eco105I >IV BstUI 2 CG'CG 0 ! AccII,Bsh1236I,BstFNI,MvnI >N ;Bst2UI 2 CC'w_GG 1 ! EcoRII,AjnI,BptI,BseBI,BstNI,BstOI,MvaI,Psp6I,PspGI >IV ;BstV1I 13 GCAGCnnnnnnnn'nnnn_ 4 ! BbvI,BseXI >I ;BstV2I 8 GAAGACnn'nnnn_ 4 ! BbsI,BpiI,BpuAI >IV BstXI 8 CCAn_nnnn'nTGG -4 ! >AFGHIJKMNOQRVX ;BstX2I 1 r'GATC_y 4 ! XhoII,BstYI,MflI,PsuI >IV BstYI 1 r'GATC_y 4 ! XhoII,BstX2I,MflI,PsuI >N ;BstZI 1 C'GGCC_G 4 ! BseX3I,EagI,EclXI,Eco52I >R BstZ17I 3 GTA'TAC 0 ! BssNAI,Bst1107I >N ;Bsu15I 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BseCI,BshVI,BspDI,BspXI,BsuTUI >F Bsu36I 2 CC'TnA_GG 3 ! AxyI,Bse21I,Eco81I >NR ;BsuRI 2 GG'CC 0 ! HaeIII,BshFI,BsnI,BspANI,PhoI >FI ;BsuTUI 2 AT'CG_AT 2 ! ClaI,BanIII,Bsa29I,BseCI,BshVI,BspDI,BspXI,Bsu15I >X BtgI 1 C'CryG_G 4 ! BstDSI >N BtgZI 16 GCGATGnnnnnnnnnn'nnnn_ 4 ! >N ;BthCI 4 G_CnG'C -3 ! Fnu4HI,Fsp4HI,ItaI,SatI > ;BtrI 3 CAC'GTC 0 ! AjiI,BmgBI >IV BtsI 8 GCAGTG_nn' -2 ! >N ;BtsCI 7 GGATG_nn' -2 ! FokI,BseGI,BstF5I >N ;BtuMI 3 TCG'CGA 0 ! NruI,Bsp68I >V ;BveI 10 ACCTGCnnnn'nnnn_ 4 ! BspMI,Acc36I,BfuAI >F Cac8I 3 GCn'nGC 0 ! BstC8I >N ;CaiI 6 CAG_nnn'CTG -3 ! AlwNI >F ;CauII 2 CC's_GG 1 ! AsuC2I,BcnI,BpuMI,NciI > ;CciNI 2 GC'GGCC_GC 4 ! NotI >IV ;CdiI 4 CATC'G 0 ! > ;CelII 2 GC'TnA_GC 3 ! BlpI,Bpu1102I,Bsp1720I >M ;CfoI 3 G_CG'C -2 ! HhaI,AspLEI,BstHHI,Hin6I,HinP1I,HspAI >GMRS ;CfrI 1 y'GGCC_r 4 ! EaeI >F ;Cfr9I 1 C'CCGG_G 4 ! SmaI,XmaI,XmaCI >FO ;Cfr10I 1 r'CCGG_y 4 ! Bse118I,BsrFI,BssAI >FGKO ;Cfr13I 1 G'GnC_C 3 ! AspS9I,BmgT120I,Sau96I >AFKO ;Cfr42I 4 CC_GC'GG -2 ! SacII,KspI,Sfr303I,SgrBI,SstII >F ;ChaI 4 _GATC' -4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI > ;CjeI 26 CCAnnnnnnGTnnnnnnnnn_nnnnnn' -6 ! > ;CjeI 25 ACnnnnnnTGGnnnnnnnn_nnnnnn' -6 ! > ;CjeNII 5 GAGnnnnnGT 0 ? ! > ;CjePI 26 CCAnnnnnnnTCnnnnnnnn_nnnnnn' -6 ! > ;CjePI 25 GAnnnnnnnTGGnnnnnnn_nnnnnn' -6 ! > ;CjuI 5 CAynnnnnrTG 0 ? ! > ;CjuII 5 CAynnnnnCTC 0 ? ! > ClaI 2 AT'CG_AT 2 ! BanIII,Bsa29I,BseCI,BshVI,BspDI,BspXI,Bsu15I,BsuTUI >ABHKMNRSU ;CpoI 2 CG'GwC_CG 3 ! RsrII,CspI,Rsr2I >AFK ;CseI 10 GACGCnnnnn'nnnnn_ 5 ! HgaI >F ;CspI 2 CG'GwC_CG 3 ! RsrII,CpoI,Rsr2I >OR Csp6I 1 G'TA_C 2 ! RsaI,AfaI >F ;Csp45I 2 TT'CG_AA 2 ! AsuII,Bpu14I,Bsp119I,BspT104I,BstBI,NspV,SfuI >OR ;CspAI 1 A'CCGG_T 4 ! AgeI,AsiGI,BshTI,PinAI >C CspCI 24 CAAnnnnnGTGGnnnnnnnnnn_nn' -2 ! >N CspCI 25 CCACnnnnnTTGnnnnnnnnnnn_nn' -2 ! >N ;CstMI 26 AAGGAGnnnnnnnnnnnnnnnnnn_nn' -2 ! > CviAII 1 C'AT_G 2 ! NlaIII,FaeI,FatI,Hin1II,Hsp92II >N CviJI 2 rG'Cy 0 ! >VX ;CviRI 2 TG'CA 0 ! HpyCH4V > DdeI 1 C'TnA_G 3 ! BstDEI,HpyF3I >BGMNORS ;DinI 3 GGC'GCC 0 ! NarI,BbeI,EgeI,EheI,KasI,Mly113I,SfoI >V DpnI 2 GA'TC 0 ! MalI >BEFGMNRS ;DpnII 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,Kzo9I,NdeII,Sau3AI >N DraI 3 TTT'AAA 0 ! >ABFGIJKMNOQRSUVXY ;DraII 2 rG'GnC_Cy 3 ! EcoO109I >GM DraIII 6 CAC_nnn'GTG -3 ! AdeI >GIMNV DrdI 7 GACnn_nn'nnGTC -2 ! AasI,DseDI >N ;DrdII 3 GAACCA 0 ? ! > ;DriI 6 GACnn_n'nnGTC -1 ! Eam1105I,AhdI,AspEI,BmeRI,EclHKI >I ;DsaI 1 C'CryG_G 4 ! BstDSI,BtgI > ;DseDI 7 GACnn_nn'nnGTC -2 ! DrdI,AasI >I EaeI 1 y'GGCC_r 4 ! CfrI >AKMN EagI 1 C'GGCC_G 4 ! BseX3I,BstZI,EclXI,Eco52I >GN ;Eam1104I 7 CTCTTCn'nnn_ 3 ! Ksp632I,Bst6I,EarI >F ;Eam1105I 6 GACnn_n'nnGTC -1 ! AhdI,AspEI,BmeRI,DriI,EclHKI >FK EarI 7 CTCTTCn'nnn_ 3 ! Ksp632I,Bst6I,Eam1104I >N EciI 17 GGCGGAnnnnnnnnn_nn' -2 ! >N ;Ecl136II 3 GAG'CTC 0 ! SacI,EcoICRI,Psp124BI,SstI >F ;EclHKI 6 GACnn_n'nnGTC -1 ! Eam1105I,AhdI,AspEI,BmeRI,DriI >R ;EclXI 1 C'GGCC_G 4 ! BseX3I,BstZI,EagI,Eco52I >MS ;Eco24I 5 G_rGCy'C -4 ! BanII,EcoT38I,FriOI >F ;Eco31I 7 GGTCTCn'nnnn_ 4 ! BsaI,Bso31I,BspTNI >F ;Eco32I 3 GAT'ATC 0 ! EcoRV >F ;Eco47I 1 G'GwC_C 3 ! AvaII,Bme18I,SinI,VpaK11BI >FO ;Eco47III 3 AGC'GCT 0 ! AfeI,Aor51HI >FGMOR ;Eco52I 1 C'GGCC_G 4 ! BseX3I,BstZI,EagI,EclXI >FKO ;Eco57I 22 CTGAAGnnnnnnnnnnnnnn_nn' -2 ! AcuI >F ;Eco72I 3 CAC'GTG 0 ! PmaCI,AcvI,BbrPI,PmlI,PspCI >F ;Eco81I 2 CC'TnA_GG 3 ! AxyI,Bse21I,Bsu36I >AFKO ;Eco88I 1 C'yCGr_G 4 ! AvaI,Ama87I,BmeT110I,BsiHKCI,BsoBI >F ;Eco91I 1 G'GTnAC_C 5 ! BstEII,BstPI,EcoO65I,PspEI >F ;Eco105I 3 TAC'GTA 0 ! SnaBI,BstSNI >FO ;Eco130I 1 C'CwwG_G 4 ! StyI,BssT1I,EcoT14I,ErhI >F ;Eco147I 3 AGG'CCT 0 ! StuI,AatI,PceI,SseBI >F ;EcoHI 0 'CCsGG_ 5 ! AsuC2I,BcnI,BpuMI,NciI > EcoICRI 3 GAG'CTC 0 ! SacI,Ecl136II,Psp124BI,SstI >IRV Eco57MI 22 CTGrAGnnnnnnnnnnnnnn_nn' -2 ! >F EcoNI 5 CCTnn'n_nnAGG 1 ! BstENI,XagI >N ;EcoO65I 1 G'GTnAC_C 5 ! BstEII,BstPI,Eco91I,PspEI >K EcoO109I 2 rG'GnC_Cy 3 ! DraII >AFJKN EcoRI 1 G'AATT_C 4 ! >ABCFGHIJKMNOQRSUVXY ;EcoRII 0 'CCwGG_ 5 ! AjnI,BptI,BseBI,BstNI,BstOI,Bst2UI,MvaI,Psp6I,PspGI >FJMOS EcoRV 3 GAT'ATC 0 ! Eco32I >ABCGHIJKMNOQRSUVXY ;EcoT14I 1 C'CwwG_G 4 ! StyI,BssT1I,Eco130I,ErhI >K ;EcoT22I 5 A_TGCA'T -4 ! Mph1103I,NsiI,Zsp2I >AKO ;EcoT38I 5 G_rGCy'C -4 ! BanII,Eco24I,FriOI >J ;EgeI 3 GGC'GCC 0 ! NarI,BbeI,DinI,EheI,KasI,Mly113I,SfoI >I ;EheI 3 GGC'GCC 0 ! NarI,BbeI,DinI,EgeI,KasI,Mly113I,SfoI >FO ;ErhI 1 C'CwwG_G 4 ! StyI,BssT1I,Eco130I,EcoT14I >IV ;EsaBC3I 2 TC'GA 0 ! TaqI > ;EspI 2 GC'TnA_GC 3 ! BlpI,Bpu1102I,Bsp1720I,CelII > ;Esp3I 7 CGTCTCn'nnnn_ 4 ! BsmBI >F ;FaeI 4 _CATG' -4 ! NlaIII,CviAII,FatI,Hin1II,Hsp92II >I FalI 24 AAGnnnnnCTTnnnnnnnn_nnnnn' -5 ! >I ;FaqI 15 GGGACnnnnnnnnnn'nnnn_ 4 ! BslFI,BsmFI >F FatI 0 'CATG_ 4 ! NlaIII,CviAII,FaeI,Hin1II,Hsp92II >IN FauI 9 CCCGCnnnn'nn_ 2 ! SmuI >IN ;FauNDI 2 CA'TA_TG 2 ! NdeI >IV ;FbaI 1 T'GATC_A 4 ! BclI,Ksp22I >AK ;FblI 2 GT'mk_AC 2 ! AccI,XmiI >IV ;FinI 2 GGGAC 0 ? ! BslFI,BsmFI,FaqI > ;FmuI 4 G_GnC'C -3 ! AspS9I,BmgT120I,Cfr13I,Sau96I > ;FnuDII 2 CG'CG 0 ! AccII,Bsh1236I,BstFNI,BstUI,MvnI > Fnu4HI 2 GC'n_GC 1 ! Fsp4HI,ItaI,SatI >N FokI 14 GGATGnnnnnnnnn'nnnn_ 4 ! BseGI,BstF5I,BtsCI >AGIJKMNRV ;FriOI 5 G_rGCy'C -4 ! BanII,Eco24I,EcoT38I >IV FseI 6 GG_CCGG'CC -4 ! >AKN FspI 3 TGC'GCA 0 ! Acc16I,AviII,NsbI >JNO FspAI 4 rTGC'GCAy 0 ! >F ;FspBI 1 C'TA_G 2 ! MaeI,BfaI,XspI >F ;Fsp4HI 2 GC'n_GC 1 ! Fnu4HI,ItaI,SatI >I ;GdiII 1 C'GGCC_r 4 ! > ;GsuI 22 CTGGAGnnnnnnnnnnnnnn_nn' -2 ! BpmI >F ;HaeI 3 wGG'CCw 0 ! > HaeII 5 r_GCGC'y -4 ! Bsp143II,BstH2I >GJKMNORS HaeIII 2 GG'CC 0 ! BshFI,BsnI,BspANI,BsuRI,PhoI >ABGHIJKMNOQRSUXY ;HaeIV 25 GAynnnnnrTCnnnnnnnnn_nnnnn' -5 ! > ;HaeIV 24 GAynnnnnrTCnnnnnnn_nnnnnn' -6 ! > ;HapII 1 C'CG_G 2 ! HpaII,BsiSI,MspI >AK HgaI 10 GACGCnnnnn'nnnnn_ 5 ! CseI >IN ;HgiAI 5 G_wGCw'C -4 ! Alw21I,Bbv12I,BsiHKAI > ;HgiCI 1 G'GyrC_C 4 ! AccB1I,BanI,BshNI,BspT107I > ;HgiEII 6 ACCnnnnnnGGT 0 ? ! > ;HgiJII 5 G_rGCy'C -4 ! BanII,Eco24I,EcoT38I,FriOI > HhaI 3 G_CG'C -2 ! AspLEI,BstHHI,CfoI,Hin6I,HinP1I,HspAI >ABFGJKNORUY ;Hin1I 2 Gr'CG_yC 2 ! AcyI,BsaHI,BssNI,BstACI,Hsp92I >FKO ;Hin1II 4 _CATG' -4 ! NlaIII,CviAII,FaeI,FatI,Hsp92II >F Hin4I 24 GAynnnnnvTCnnnnnnnn_nnnnn' -5 ! >F Hin4I 24 GAbnnnnnrTCnnnnnnnn_nnnnn' -5 ! >F ;Hin4II 2 CCTTC 0 ? ! > ;Hin6I 1 G'CG_C 2 ! HhaI,AspLEI,BstHHI,CfoI,HinP1I,HspAI >F HinP1I 1 G'CG_C 2 ! HhaI,AspLEI,BstHHI,CfoI,Hin6I,HspAI >N HincII 3 GTy'rAC 0 ! HindII >ABFGHJKNOQRUXY ;HindII 3 GTy'rAC 0 ! HincII >IMSV HindIII 1 A'AGCT_T 4 ! >ABCFGHIJKMNOQRSUVXY HinfI 1 G'AnT_C 3 ! >ABCFGHIJKMNOQRUVXY HpaI 3 GTT'AAC 0 ! KspAI >ABCGHIJKMNOQRSUVX HpaII 1 C'CG_G 2 ! BsiSI,HapII,MspI >BFGIMNOQRSUVX HphI 13 GGTGAnnnnnnn_n' -1 ! AsuHPI >FN Hpy8I 3 GTn'nAC 0 ! >F Hpy99I 5 _CGwCG' -5 ! >N ;Hpy178III 2 TC'nn_GA 2 ! Hpy188III > Hpy188I 3 TC_n'GA -1 ! >N Hpy188III 2 TC'nn_GA 2 ! >N ;HpyAV 11 CCTTCnnnnn_n' -1 ! > HpyCH4III 3 AC_n'GT -1 ! Bst4CI,TaaI >N HpyCH4IV 1 A'CG_T 2 ! MaeII,TaiI >N HpyCH4V 2 TG'CA 0 ! >N ;HpyF3I 1 C'TnA_G 3 ! DdeI,BstDEI >F ;HpyF10VI 7 GCnn_nnn'nnGC -3 ! MwoI,BstMWI >F ;Hsp92I 2 Gr'CG_yC 2 ! AcyI,BsaHI,BssNI,BstACI,Hin1I >R ;Hsp92II 4 _CATG' -4 ! NlaIII,CviAII,FaeI,FatI,Hin1II >R ;HspAI 1 G'CG_C 2 ! HhaI,AspLEI,BstHHI,CfoI,Hin6I,HinP1I >IV ;ItaI 2 GC'n_GC 1 ! Fnu4HI,Fsp4HI,SatI >M KasI 1 G'GCGC_C 4 ! NarI,BbeI,DinI,EgeI,EheI,Mly113I,SfoI >N KpnI 5 G_GTAC'C -4 ! Acc65I,Asp718I >ABCFGHIJKMNOQRSUVXY ;Kpn2I 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,BseAI,Bsp13I,BspEI,MroI >F ;KspI 4 CC_GC'GG -2 ! SacII,Cfr42I,Sfr303I,SgrBI,SstII >MS ;Ksp22I 1 T'GATC_A 4 ! BclI,FbaI >IV ;Ksp632I 7 CTCTTCn'nnn_ 3 ! Bst6I,Eam1104I,EarI >M ;KspAI 3 GTT'AAC 0 ! HpaI >F ;Kzo9I 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,NdeII,Sau3AI >I ;LguI 8 GCTCTTCn'nnn_ 3 ! SapI,PciSI >F ;LpnI 3 rGC'GCy 0 ! HaeII,Bsp143II,BstH2I > ;LweI 10 GCATCnnnnn'nnnn_ 4 ! SfaNI >F ;MabI 1 A'CCwGG_T 5 ! SexAI >I ;MaeI 1 C'TA_G 2 ! BfaI,FspBI,XspI >M ;MaeII 1 A'CG_T 2 ! HpyCH4IV,TaiI >M MaeIII 0 'GTnAC_ 5 ! >M ;MalI 2 GA'TC 0 ! DpnI >I ;MamI 5 GATnn'nnATC 0 ! BsaBI,Bse8I,BseJI >M ;MbiI 3 CCG'CTC 0 ! BsrBI,AccBSI >F MboI 0 'GATC_ 4 ! BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,NdeII,Sau3AI >ABCFGKNQRUXY MboII 13 GAAGAnnnnnnn_n' -1 ! >AFGIJKNOQRVX ;McrI 4 CG_ry'CG -2 ! Bsh1285I,BsiEI,BstMCI > MfeI 1 C'AATT_G 4 ! MunI >N ;MflI 1 r'GATC_y 4 ! XhoII,BstX2I,BstYI,PsuI >K ;MhlI 5 G_dGCh'C -4 ! SduI,Bsp1286I >IV ;MjaIV 3 GTnnAC 0 ? ! Hpy8I > ;MlsI 3 TGG'CCA 0 ! BalI,MluNI,MscI,Msp20I >F MluI 1 A'CGCG_T 4 ! >ABFGHIJKMNOQRSUVX ;MluNI 3 TGG'CCA 0 ! BalI,MlsI,MscI,Msp20I >MS MlyI 10 GAGTCnnnnn' 0 ! PleI,PpsI,SchI >N ;Mly113I 2 GG'CG_CC 2 ! NarI,BbeI,DinI,EgeI,EheI,KasI,SfoI >I MmeI 26 TCCrACnnnnnnnnnnnnnnnnnn_nn' -2 ! >NX MnlI 11 CCTCnnnnnn_n' -1 ! >FGINQVX ;Mph1103I 5 A_TGCA'T -4 ! EcoT22I,NsiI,Zsp2I >F ;MroI 1 T'CCGG_A 4 ! AccIII,Aor13HI,BlfI,BseAI,Bsp13I,BspEI,Kpn2I >MO ;MroNI 1 G'CCGG_C 4 ! NaeI,NgoMIV,PdiI >IV ;MroXI 5 GAAnn'nnTTC 0 ! XmnI,Asp700I,PdmI >IV MscI 3 TGG'CCA 0 ! BalI,MlsI,MluNI,Msp20I >BNO MseI 1 T'TA_A 2 ! Tru1I,Tru9I >BN MslI 5 CAynn'nnrTG 0 ! SmiMI >N ;MspI 1 C'CG_G 2 ! HpaII,BsiSI,HapII >AFGHIJKMNOQRSUVXY ;Msp20I 3 TGG'CCA 0 ! BalI,MlsI,MluNI,MscI >IV MspA1I 3 CmG'CkG 0 ! >INRV ;MspCI 1 C'TTAA_G 4 ! AflII,BfrI,BspTI,Bst98I,Vha464I >C ;MspR9I 2 CC'n_GG 1 ! ScrFI,Bme1390I,BmrFI,BssKI,BstSCI,StyD4I >I ;MssI 4 GTTT'AAAC 0 ! PmeI >F ;MstI 3 TGC'GCA 0 ! Acc16I,AviII,FspI,NsbI > ;MunI 1 C'AATT_G 4 ! MfeI >FKM ;MvaI 2 CC'w_GG 1 ! EcoRII,AjnI,BptI,BseBI,BstNI,BstOI,Bst2UI,Psp6I,PspGI >AFGKMOS ;Mva1269I 7 GAATG_Cn' -2 ! BsmI,BsaMI,PctI >F ;MvnI 2 CG'CG 0 ! AccII,Bsh1236I,BstFNI,BstUI >M ;MvrI 4 CG_AT'CG -2 ! PvuI,BpvUI,Ple19I >U MwoI 7 GCnn_nnn'nnGC -3 ! BstMWI,HpyF10VI >N NaeI 3 GCC'GGC 0 ! MroNI,NgoMIV,PdiI >ACKMNORU NarI 2 GG'CG_CC 2 ! BbeI,DinI,EgeI,EheI,KasI,Mly113I,SfoI >GJMNOQRUX NciI 2 CC's_GG 1 ! AsuC2I,BcnI,BpuMI >GJNORS NcoI 1 C'CATG_G 4 ! Bsp19I >ABCFGHJKMNOQRSUXY NdeI 2 CA'TA_TG 2 ! FauNDI >ABFGJKMNRSXY ;NdeII 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,Sau3AI >GJMRS NgoMIV 1 G'CCGG_C 4 ! NaeI,MroNI,PdiI >NR NheI 1 G'CTAG_C 4 ! AsuNHI,BmtI >ABFGJKMNORSU NlaIII 4 _CATG' -4 ! CviAII,FaeI,FatI,Hin1II,Hsp92II >GN NlaIV 3 GGn'nCC 0 ! BmiI,BspLI,PspN4I >GN ;Nli3877I 5 C_yCGr'G -4 ! AvaI,Ama87I,BmeT110I,BsiHKCI,BsoBI,Eco88I > ;NmuCI 0 'GTsAC_ 5 ! Tsp45I >F NotI 2 GC'GGCC_GC 4 ! CciNI >ABCFGHJKMNOQRSUXY NruI 3 TCG'CGA 0 ! Bsp68I,BtuMI >ABCGIJKMNOQRSUX ;NsbI 3 TGC'GCA 0 ! Acc16I,AviII,FspI >FK NsiI 5 A_TGCA'T -4 ! EcoT22I,Mph1103I,Zsp2I >BGHJMNRSU NspI 5 r_CATG'y -4 ! BstNSI,XceI >MN ;NspV 2 TT'CG_AA 2 ! AsuII,Bpu14I,Bsp119I,BspT104I,BstBI,Csp45I,SfuI >JO ;NspBII 3 CmG'CkG 0 ! MspA1I > ;OliI 5 CACnn'nnGTG 0 ! AleI >F ;PabI 3 G_TA'C -2 ! RsaI,AfaI,Csp6I > PacI 5 TTA_AT'TAA -2 ! >GNO ;PaeI 5 G_CATG'C -4 ! SphI,BbuI >F ;PaeR7I 1 C'TCGA_G 4 ! XhoI,Sfr274I,SlaI,StrI,TliI >N ;PagI 1 T'CATG_A 4 ! BspHI,RcaI >F ;PalAI 2 GG'CGCG_CC 4 ! AscI,SgsI >I PasI 2 CC'CwG_GG 3 ! >F ;PauI 1 G'CGCG_C 4 ! BsePI,BssHII >F ;PceI 3 AGG'CCT 0 ! StuI,AatI,Eco147I,SseBI >IV PciI 1 A'CATG_T 4 ! BspLU11I,PscI >IN ;PciSI 8 GCTCTTCn'nnn_ 3 ! SapI,LguI >I ;PctI 7 GAATG_Cn' -2 ! BsmI,BsaMI,Mva1269I >IV ;PdiI 3 GCC'GGC 0 ! NaeI,MroNI,NgoMIV >F ;PdmI 5 GAAnn'nnTTC 0 ! XmnI,Asp700I,MroXI >F ;PfeI 1 G'AwT_C 3 ! TfiI >F ;Pfl23II 1 C'GTAC_G 4 ! BsiWI,PspLI >F ;Pfl1108I 3 TCGTAG 0 ? ! > ;PflFI 4 GACn'n_nGTC 1 ! Tth111I,AspI,PsyI >N PflMI 7 CCAn_nnn'nTGG -3 ! AccB7I,Van91I >N PfoI 1 T'CCnGG_A 5 ! >F ;PhoI 2 GG'CC 0 ! HaeIII,BshFI,BsnI,BspANI,BsuRI >N ;PinAI 1 A'CCGG_T 4 ! AgeI,AsiGI,BshTI,CspAI >BM PleI 9 GAGTCnnnn'n_ 1 ! MlyI,PpsI,SchI >N ;Ple19I 4 CG_AT'CG -2 ! PvuI,BpvUI,MvrI >I ;PmaCI 3 CAC'GTG 0 ! AcvI,BbrPI,Eco72I,PmlI,PspCI >AK PmeI 4 GTTT'AAAC 0 ! MssI >GN PmlI 3 CAC'GTG 0 ! PmaCI,AcvI,BbrPI,Eco72I,PspCI >N PpiI 25 GAACnnnnnCTCnnnnnnnn_nnnnn' -5 ! >F PpiI 24 GAGnnnnnGTTCnnnnnnn_nnnnn' -5 ! >F ;PpsI 9 GAGTCnnnn'n_ 1 ! PleI,MlyI,SchI >I ;Ppu10I 1 A'TGCA_T 4 ! EcoT22I,Mph1103I,NsiI,Zsp2I > ;Ppu21I 3 yAC'GTr 0 ! BsaAI,BstBAI >F PpuMI 2 rG'GwC_Cy 3 ! Psp5II,PspPPI >NO ;PscI 1 A'CATG_T 4 ! BspLU11I,PciI >F PshAI 5 GACnn'nnGTC 0 ! BoxI,BstPAI >AKN ;PshBI 2 AT'TA_AT 2 ! VspI,AseI >K PsiI 3 TTA'TAA 0 ! >IN ;Psp03I 4 G_GwC'C -3 ! AvaII,Bme18I,Eco47I,SinI,VpaK11BI > ;Psp5II 2 rG'GwC_Cy 3 ! PpuMI,PspPPI >F ;Psp6I 0 'CCwGG_ 5 ! EcoRII,AjnI,BptI,BseBI,BstNI,BstOI,Bst2UI,MvaI,PspGI >I ;Psp1406I 2 AA'CG_TT 2 ! AclI >FKM ;Psp124BI 5 G_AGCT'C -4 ! SacI,Ecl136II,EcoICRI,SstI >IV ;PspCI 3 CAC'GTG 0 ! PmaCI,AcvI,BbrPI,Eco72I,PmlI >IV ;PspEI 1 G'GTnAC_C 5 ! BstEII,BstPI,Eco91I,EcoO65I >IV PspGI 0 'CCwGG_ 5 ! EcoRII,AjnI,BptI,BseBI,BstNI,BstOI,Bst2UI,MvaI,Psp6I >N ;PspLI 1 C'GTAC_G 4 ! BsiWI,Pfl23II >I ;PspN4I 3 GGn'nCC 0 ! NlaIV,BmiI,BspLI >I PspOMI 1 G'GGCC_C 4 ! ApaI,Bsp120I >INV ;PspPPI 2 rG'GwC_Cy 3 ! PpuMI,Psp5II >I PspXI 2 vC'TCGA_Gb 4 ! >IN PsrI 25 GAACnnnnnnTACnnnnnnn_nnnnn' -5 ! >I PsrI 25 GTAnnnnnnGTTCnnnnnnn_nnnnn' -5 ! >I ;PssI 5 rG_GnC'Cy -3 ! DraII,EcoO109I > PstI 5 C_TGCA'G -4 ! BspMAI >ABCFGHIJKMNOQRSUVXY ;PsuI 1 r'GATC_y 4 ! XhoII,BstX2I,BstYI,MflI >F ;PsyI 4 GACn'n_nGTC 1 ! Tth111I,AspI,PflFI >F PvuI 4 CG_AT'CG -2 ! BpvUI,MvrI,Ple19I >ABFGKMNOQRSUXY PvuII 3 CAG'CTG 0 ! >ABCFGHIJKMNOQRSUVXY ;RcaI 1 T'CATG_A 4 ! BspHI,PagI >M ;RgaI 5 GCG_AT'CGC -2 ! SgfI,AsiSI >I ;RleAI 18 CCCACAnnnnnnnnn_nnn' -3 ! > RsaI 2 GT'AC 0 ! AfaI,Csp6I >BCFGHIJMNOQRSVXY RsrII 2 CG'GwC_CG 3 ! CpoI,CspI,Rsr2I >MNQX ;Rsr2I 2 CG'GwC_CG 3 ! RsrII,CpoI,CspI >I SacI 5 G_AGCT'C -4 ! Ecl136II,EcoICRI,Psp124BI,SstI >AFGHJKMNOQRSUX SacII 4 CC_GC'GG -2 ! Cfr42I,KspI,Sfr303I,SgrBI,SstII >AGHJKNOQRX SalI 1 G'TCGA_C 4 ! >ABCFGHIJKMNOQRSUVXY SanDI 2 GG'GwC_CC 3 ! >E SapI 8 GCTCTTCn'nnn_ 3 ! LguI,PciSI >N ;SatI 2 GC'n_GC 1 ! Fnu4HI,Fsp4HI,ItaI >F ;SauI 2 CC'TnA_GG 3 ! AxyI,Bse21I,Bsu36I,Eco81I > Sau96I 1 G'GnC_C 3 ! AspS9I,BmgT120I,Cfr13I >GJMNOU ;Sau3AI 0 'GATC_ 4 ! MboI,BfuCI,Bsp143I,BssMI,BstKTI,BstMBI,DpnII,Kzo9I,NdeII >AGHJKMNOQRSUX SbfI 6 CC_TGCA'GG -4 ! Sse8387I,SdaI >INV ScaI 3 AGT'ACT 0 ! AssI,BmcAI,ZrmI >ABCFGJKMNOQRSX ;SchI 10 GAGTCnnnnn' 0 ! PleI,MlyI,PpsI >F ;SciI 3 CTC'GAG 0 ! XhoI,PaeR7I,Sfr274I,SlaI,StrI,TliI > ScrFI 2 CC'n_GG 1 ! Bme1390I,BmrFI,BssKI,BstSCI,MspR9I,StyD4I >JMNOS ;SdaI 6 CC_TGCA'GG -4 ! Sse8387I,SbfI >F ;SduI 5 G_dGCh'C -4 ! Bsp1286I,MhlI >F ;SecI 1 C'CnnG_G 4 ! BsaJI,BseDI,BssECI > ;SelI 0 'CGCG_ 4 ! AccII,Bsh1236I,BstFNI,BstUI,MvnI > SexAI 1 A'CCwGG_T 5 ! MabI >MN SfaNI 10 GCATCnnnnn'nnnn_ 4 ! LweI >IN SfcI 1 C'TryA_G 4 ! BfmI,BpcI,BstSFI >N ;SfeI 1 C'TryA_G 4 ! BfmI,BpcI,BstSFI,SfcI > SfiI 8 GGCCn_nnn'nGGCC -3 ! >ACFGIJKMNOQRSUVX SfoI 3 GGC'GCC 0 ! NarI,BbeI,DinI,EgeI,EheI,KasI,Mly113I >N ;Sfr274I 1 C'TCGA_G 4 ! XhoI,PaeR7I,SlaI,StrI,TliI >IV ;Sfr303I 4 CC_GC'GG -2 ! SacII,Cfr42I,KspI,SgrBI,SstII >IV ;SfuI 2 TT'CG_AA 2 ! AsuII,Bpu14I,Bsp119I,BspT104I,BstBI,Csp45I,NspV >M ;SgfI 5 GCG_AT'CGC -2 ! AsiSI,RgaI >R SgrAI 2 Cr'CCGG_yG 4 ! >MN ;SgrBI 4 CC_GC'GG -2 ! SacII,Cfr42I,KspI,Sfr303I,SstII >C ;SgrDI 4 CGTCGACG 0 ? ! > ;SgsI 2 GG'CGCG_CC 4 ! AscI,PalAI >F ;SimI 2 GG'GTC_ 3 ! > ;SinI 1 G'GwC_C 3 ! AvaII,Bme18I,Eco47I,VpaK11BI >GR ;SlaI 1 C'TCGA_G 4 ! XhoI,PaeR7I,Sfr274I,StrI,TliI >C SmaI 3 CCC'GGG 0 ! Cfr9I,XmaI,XmaCI >ABCFGHIJKMNOQRSUVXY ;SmiI 4 ATTT'AAAT 0 ! SwaI >FIV ;SmiMI 5 CAynn'nnrTG 0 ! MslI >I SmlI 1 C'TyrA_G 4 ! SmoI >N ;SmoI 1 C'TyrA_G 4 ! SmlI >F ;SmuI 9 CCCGCnnnn'nn_ 2 ! FauI >F ;SnaI 3 GTATAC 0 ? ! BssNAI,Bst1107I,BstZ17I > SnaBI 3 TAC'GTA 0 ! BstSNI,Eco105I >ACKMNR SpeI 1 A'CTAG_T 4 ! AhlI,BcuI >ABGHJKMNOQRSUX SphI 5 G_CATG'C -4 ! BbuI,PaeI >ABCGHIJKMNOQRSVX ;SplI 1 C'GTAC_G 4 ! BsiWI,Pfl23II,PspLI > SrfI 4 GCCC'GGGC 0 ! >EO ;Sse9I 0 'AATT_ 4 ! TspEI,TasI,Tsp509I >IV ;Sse232I 2 CG'CCGG_CG 4 ! > ;Sse8387I 6 CC_TGCA'GG -4 ! SbfI,SdaI >AK ;Sse8647I 2 AG'GwC_CT 3 ! > ;SseBI 3 AGG'CCT 0 ! StuI,AatI,Eco147I,PceI >C ;SsiI 1 C'CG_C 2 ! AciI,BspACI >F SspI 3 AAT'ATT 0 ! >ABCFGIJKMNOQRSUVX ;SspBI 1 T'GTAC_A 4 ! Bsp1407I,BsrGI,BstAUI >M ;SspD5I 13 GGTGAnnnnnnnn' 0 ! HphI,AsuHPI > ;SstI 5 G_AGCT'C -4 ! SacI,Ecl136II,EcoICRI,Psp124BI >BC ;SstII 4 CC_GC'GG -2 ! SacII,Cfr42I,KspI,Sfr303I,SgrBI >B ;Sth132I 8 CCCGnnnn'nnnn_ 4 ! > ;Sth302II 2 CC'GG 0 ! HpaII,BsiSI,HapII,MspI > ;StrI 1 C'TCGA_G 4 ! XhoI,PaeR7I,Sfr274I,SlaI,TliI >U ;StsI 15 GGATGnnnnnnnnnn'nnnn_ 4 ! FokI,BseGI,BstF5I,BtsCI > StuI 3 AGG'CCT 0 ! AatI,Eco147I,PceI,SseBI >ABJKMNQRSUX StyI 1 C'CwwG_G 4 ! BssT1I,Eco130I,EcoT14I,ErhI >CJMNRS StyD4I 0 'CCnGG_ 5 ! ScrFI,Bme1390I,BmrFI,BssKI,BstSCI,MspR9I >N SwaI 4 ATTT'AAAT 0 ! SmiI >GKMNS ;TaaI 3 AC_n'GT -1 ! Bst4CI,HpyCH4III >F TaiI 4 _ACGT' -4 ! MaeII,HpyCH4IV >F TaqI 1 T'CG_A 2 ! >ABCFGIJKMNOQRSUVXY TaqII 17 GACCGAnnnnnnnnn_nn' -2 ! >VX TaqII 17 CACCCAnnnnnnnnn_nn' -2 ! >VX ;TasI 0 'AATT_ 4 ! TspEI,Sse9I,Tsp509I >F TatI 1 w'GTAC_w 4 ! >F TauI 4 G_CsG'C -3 ! >F TfiI 1 G'AwT_C 3 ! PfeI >N ;TliI 1 C'TCGA_G 4 ! XhoI,PaeR7I,Sfr274I,SlaI,StrI >N ;Tru1I 1 T'TA_A 2 ! MseI,Tru9I >F ;Tru9I 1 T'TA_A 2 ! MseI,Tru1I >GIMRV TseI 1 G'CwG_C 3 ! ApeKI >N TsoI 17 TArCCAnnnnnnnnn_nn' -2 ! >F Tsp45I 0 'GTsAC_ 5 ! NmuCI >N Tsp509I 0 'AATT_ 4 ! TspEI,Sse9I,TasI >N ;Tsp4CI 3 AC_n'GT -1 ! Bst4CI,HpyCH4III,TaaI > TspDTI 16 ATGAAnnnnnnnnn_nn' -2 ! >VX ;TspEI 0 'AATT_ 4 ! Sse9I,TasI,Tsp509I >O TspGWI 16 ACGGAnnnnnnnnn_nn' -2 ! >VX TspRI 9 _nnCAsTGnn' -9 ! >GN ;TssI 4 GAGnnnCTC 0 ? ! > TstI 24 CACnnnnnnTCCnnnnnnn_nnnnn' -5 ! >F TstI 25 GGAnnnnnnGTGnnnnnnnn_nnnnn' -5 ! >F ;TsuI 2 GCGAC 0 ? ! > Tth111I 4 GACn'n_nGTC 1 ! AspI,PflFI,PsyI >GIKNQRVX ;Tth111II 17 CAArCAnnnnnnnnn_nn' -2 ! > ;UbaF9I 6 TACnnnnnrTGT 0 ? ! > ;UbaF11I 2 TCGTA 0 ? ! > ;UbaPI 3 CGAACG 0 ? ! > ;UnbI 0 'GGnCC_ 5 ! AspS9I,BmgT120I,Cfr13I,Sau96I > ;Van91I 7 CCAn_nnn'nTGG -3 ! PflMI,AccB7I >AFGKM ;Vha464I 1 C'TTAA_G 4 ! AflII,BfrI,BspTI,Bst98I,MspCI >IV ;VneI 1 G'TGCA_C 4 ! ApaLI,Alw44I >IV ;VpaK11AI 0 'GGwCC_ 5 ! AvaII,Bme18I,Eco47I,SinI,VpaK11BI > ;VpaK11BI 1 G'GwC_C 3 ! AvaII,Bme18I,Eco47I,SinI >K ;VspI 2 AT'TA_AT 2 ! AseI,PshBI >FIRV ;XagI 5 CCTnn'n_nnAGG 1 ! EcoNI,BstENI >F ;XapI 1 r'AATT_y 4 ! ApoI,AcsI >F XbaI 1 T'CTAG_A 4 ! >ABCFGHIJKMNOQRSUVXY ;XceI 5 r_CATG'y -4 ! NspI,BstNSI >F XcmI 8 CCAnnnn_n'nnnnTGG -1 ! >N XhoI 1 C'TCGA_G 4 ! PaeR7I,Sfr274I,SlaI,StrI,TliI >ABFGHJKMNOQRSUXY ;XhoII 1 r'GATC_y 4 ! BstX2I,BstYI,MflI,PsuI >GMR XmaI 1 C'CCGG_G 4 ! SmaI,Cfr9I,XmaCI >INRUV ;XmaIII 1 C'GGCC_G 4 ! BseX3I,BstZI,EagI,EclXI,Eco52I > ;XmaCI 1 C'CCGG_G 4 ! SmaI,Cfr9I,XmaI >M ;XmaJI 1 C'CTAG_G 4 ! AvrII,AspA2I,BlnI >F ;XmiI 2 GT'mk_AC 2 ! AccI,FblI >F XmnI 5 GAAnn'nnTTC 0 ! Asp700I,MroXI,PdmI >GNRU ;XspI 1 C'TA_G 2 ! MaeI,BfaI,FspBI >K ZraI 3 GAC'GTC 0 ! AatII >INV ;ZrmI 3 AGT'ACT 0 ! ScaI,AssI,BmcAI >I ;Zsp2I 5 A_TGCA'T -4 ! EcoT22I,Mph1103I,NsiI >IV perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/perlprimer000077500000000000000000012525311304100213300233430ustar00rootroot00000000000000#!/usr/bin/perl -w # PerlPrimer # Designs primers for PCR, Bisulphite PCR, QPCR (Realtime), and Sequencing # Copyright 2003-2017, Owen Marshall # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA use strict; #---------------# # Usage message # #---------------# my ($version, $commandline, $win_exe); BEGIN { $version = "1.2.3"; $win_exe = 0; ($commandline) = @ARGV; if ($commandline && $commandline =~ /^-[\w-]+/) { print < { \$min_tm_pr => $min_tm_pr, \$max_tm_pr => $max_tm_pr, \$max_diff_pr => $max_diff_pr, \$pri_win_min_pr => $pri_win_min_pr, \$pri_win_max_pr => $pri_win_max_pr, \$exclude_gc => $exclude_gc, \$exclude_clamp => $exclude_clamp, }, seq => { \$min_tm_seq => $min_tm_seq, \$max_tm_seq => $max_tm_seq, \$pri_win_min_seq => $pri_win_min_seq, \$pri_win_max_seq => $pri_win_max_seq, \$seq_spacing_min => $seq_spacing_min, \$seq_spacing_max => $seq_spacing_max, \$exclude_gc_seq => $exclude_gc_seq, \$exclude_clamp_seq => $exclude_clamp_seq, \$exclude_pd_seq => $exclude_pd_seq, \$seq_pd_min => $seq_pd_min, }, bis => { \$min_tm_bs => $min_tm_bs, \$max_tm_bs => $max_tm_bs, \$max_diff_bs => $max_diff_bs, \$pri_win_min_bs => $pri_win_min_bs, \$pri_win_max_bs => $pri_win_max_bs, \$exclude_cpg => $exclude_cpg, \$pre_bs => $pre_bs, \$exclude_3c => $exclude_3c, \$bisul_min_c => $bisul_min_c, }, qpcr => { \$min_tm_q => $min_tm_q, \$max_tm_q, => $max_tm_q, \$max_diff_q => $max_diff_q, \$pri_win_min_q => $pri_win_min_q, \$pri_win_max_q => $pri_win_max_q, \$min_ampsize_q => $min_ampsize_q, \$max_ampsize_q => $max_ampsize_q, \$exclude_gc => $exclude_gc, \$exclude_clamp => $exclude_clamp, \$ie_overlap => $ie_overlap, \$exclude_ie => $exclude_ie, \$ie_span => $ie_span, }, ); # Home environment variables and OS-specific tweaking: # These variables are the total changes required for nice cross-platform compatibility # (and most of these are cosmetic - strange that the Tk look&feel is not consistent with # things such as widget spacing, most notably the checkbuttons) my $HOME = $ENV{HOME} || $ENV{HOMEPATH}; # HOMEPATH may not work correctly on Win32 ... my $tmp = $ENV{TEMP} || $ENV{TMP}; my ($dir_sep, $os, $gui_font_face, $gui_font_size, $text_font_face, $text_font_size, $list_font_face, $list_font_size, $font_override, $menu_relief, $frame_pady, $check_pady, $button_pady, $button_pack_padx, $button_pack_pady, $browser); if ($^O =~ /mswin/i) { # MS Windows OS $os = 'win'; $browser = 'c:\Program Files\Internet Explorere\iexplore'; $HOME ||= 'c:\\'; # only if not set by $ENV above $tmp ||= 'c:\temp\\'; # only if not set by $ENV above $dir_sep = '\\'; $gui_font_face = "Arial"; $gui_font_size = 8; $list_font_face = "Verdana"; $list_font_size = 7; $text_font_face = "Courier"; $text_font_size = 8; $menu_relief = 'flat'; $button_pady = 1; $button_pack_padx = 2; $button_pack_pady = 2; $frame_pady = 2; $check_pady = 0; } else { # *nix OS $os = 'nix'; $browser = 'firefox'; $HOME ||= (getpwuid($<))[7].'/'; # only if not set above $tmp = '/tmp/'; # only if not set above $dir_sep = '/'; $gui_font_face = "Helvetica"; $gui_font_size = 10; $list_font_face = "Helvetica"; $list_font_size = 10; $text_font_face = "Courier"; $text_font_size = 10; $menu_relief = 'raised'; $button_pady = 2; $button_pack_padx = 0; $button_pack_pady = 0; $frame_pady = 1; $check_pady = 3; } # check tmp directory exists, is user readable and writable, # else use the home directory $tmp = $HOME unless (-e $tmp && -r $tmp && -w $tmp); # check directories have trailing slash ... $tmp = check_path($tmp); $HOME = check_path($HOME); # directory the program has been run in (for RE enzyme database) my $program_directory = $0; $program_directory =~ s/([^\\\/]*$)//; # flags my $bs=0; my $qpcr_flag=0; my $cancel=0; my $defer_to_caps=0; my $onodera_clamp=0; # benchmarking - for code optimisation my $benchmark=0; # DNA canvas sizing: my $dna_canvas_offset=15; my $dna_canvas_height=21; my $dna_canvas_middle=int($dna_canvas_height/2)+2; my $half_dna_size=3; my $dc_dna_y1 = $dna_canvas_middle - $half_dna_size; my $dc_dna_y2 = $dna_canvas_middle + $half_dna_size; my $dc_selection_offset = 3; my $dc_sel_y1 = $dc_dna_y1-$dc_selection_offset; my $dc_sel_y2 = $dc_dna_y2+$dc_selection_offset; my $dc_sel_offset2 = 2; # Repeats/runs my $exclude_rr = 1; my $repeat = 3; my $run = 4; my $exclude_rr_bs = 1; my $repeat_bs = 3; my $run_bs = 4; # %GC exclusions my $max_gc = 60; my $min_gc = 40; # CpG prediction variables my $cpg_window = 200; my $min_cpg_island = 200; my $cpg_gc = 50; my $cpg_oe = 0.6; my $cpgplot_method = 0; # Http proxy my $use_proxy = 0; my ($http_proxy, $http_port); # Ensembl data my $ensembl; # GUI page my $ensembl_gene; my $ensembl_organism = 'Homo_sapiens'; my $ensembl_type = 'cdna'; my @ensembl_species = split("\n", "Homo_sapiens Mus_musculus Danio_rerio Drosophila_melanogaster Caenorhabditis_elegans Saccharomyces_cerevisiae ---- Primates ---- Otolemur_garnettii Pan_troglodytes Gorilla_gorilla Homo_sapiens Macaca_mulatta Callithrix_jacchus Microcebus_murinus Pongo_pygmaeus Tarsius_syrichta ---- Rodents etc. ---- Cavia_porcellus Dipodomys_ordii Mus_musculus Ochotona_princeps Oryctolagus_cuniculus Rattus_norvegicus Spermophilus_tridecemlineatus Tupaia_belangeri ---- Laurasiatheria ---- Vicugna_pacos Felis_catus Bos_taurus Canis_familiaris Tursiops_truncatus Erinaceus_europaeus Equus_caballus Pteropus_vampyrus Myotis_lucifugus Sus_scrofa Sorex_araneus ---- Afrotheria ---- Loxodonta_africana Procavia_capensis Echinops_telfairi ---- Xenarthra ---- Dasypus_novemcinctus Choloepus_hoffmanni ---- Other mammals ---- Monodelphis_domestica Ornithorhynchus_anatinus Macropus_eugenii ---- Birds & Reptiles ---- Anolis_carolinensis Gallus_gallus Taeniopygia_guttata ---- Amphibians ---- Xenopus_tropicalis ---- Fish ---- Takifugu_rubripes Oryzias_latipes Gasterosteus_aculeatus Tetraodon_nigroviridis Danio_rerio ---- Other chordates ---- Ciona_intestinalis Ciona_savignyi ---- Other eukaryotes ---- Aedes_aegypti Anopheles_gambiae Apis_mellifera Caenorhabditis_elegans Drosophila_melanogaster Aspergillus_nidulans Saccharomyces_cerevisiae Schizosaccharomyces_pombe"); my @ensembl_types = split("\n", "genomic cdna cds utr5 utr3"); # BLAST search parameters my $blast_expect = 10; my $blast_word_size = 7; my $blast_database = 'nr'; my @blast_database_array = split("\n", "nr est est_human est_mouse est_others gss htgs pat yeast mito vector pdb month alu dbsts chromosome E. coli Drosophila genome"); my $blast_entrez_query = 'none'; my @blast_entrez_array = split("\n", "none ------------------- Viruses [ORGN] Archaea [ORGN] Bacteria [ORGN] Eukaryota [ORGN] Viridiplantae [ORGN] Fungi [ORGN] Metazoa [ORGN] Arthropoda [ORGN] Vertebrata [ORGN] Mammalia [ORGN] Rodentia [ORGN] Primates [ORGN] ------------------- Aeropyrum pernix [ORGN] Aquifex aeolicus [ORGN] Arabidopsis thaliana [ORGN] Bacillus subtilis [ORGN] Bos taurus [ORGN] Caenorhabditis elegans [ORGN] Danio rerio [ORGN] Dictyostelium discoideum [ORGN] Drosophila melanogaster [ORGN] Escherichia coli [ORGN] Gallus gallus [ORGN] Homo sapiens [ORGN] Human immunodeficiency virus type 1 [ORGN] Methanococcus jannaschii [ORGN] Mus musculus [ORGN] Oryctolagus cuniculus [ORGN] Oryza sativa [ORGN] Ovis aries [ORGN] Plasmodium falciparum [ORGN] Rattus norvegicus [ORGN] Saccharomyces cerevisiae [ORGN] Schizosaccharomyces pombe [ORGN] Simian immunodeficiency virus [ORGN] Synechocystis PCC6803 [ORGN] Takifugu rubripes [ORGN] Xenopus laevis [ORGN] Zea mays [ORGN]"); my $local_blast = 0; my $local_blast_directory = $HOME; my $local_blast_database; my $blast_count; my $http_abort; # Primer-dimer parameters # my $pd_full=0; my $pd_extensible=1; my $pd_temperature=37; # Restriction enzyme cloning sequence parameters my $cloning_anchor = "GCGCGC"; my $simple_sites = 1; my $exclude_found_sites = 1; # IPC using TCP sockets (with Contig Viewer) my $file_data_overwrite = 1; my $ipc_autofind = 0; my $tcp_port = 2500; my $socket_polling_interval = 1000; # msec # Header columns my @header_list_primers = split("\n", "Forward Primer Pos Len Tm Reverse Primer Pos Len Tm Amp Ext. dimer dG Full dimer dG"); # Mouse wheel my $scroll_factor = 2; # Win32 only - for button pre-lights my $activebackground_color="#ffffff"; # Spidey details my (%spidey_exec); my $spidey_path = "$HOME"; # search for spidey in program directory my @spidey_files = glob("$program_directory*pidey.*"); @spidey_files = glob("$program_directory*pidey*") unless @spidey_files; if (@spidey_files) { $spidey_path = "$program_directory"; } # my $spidey_out; # More global variables my ( $primer_f, $primer_r, $pos, $rprimer_r, $pd, @score_sort, $reverse, $pfkeys, $pkeys, @PF, @PR, %primer_hash, $gc_exclude, $gc_percent_ex, @primer_pairs, $prl, $pfl, $pl, @bind_string, %rating_hash, @score, %packed_widgets, $save_seq, $save_seq2, @primer_pairs_pr_s, @primer_pairs_seq_s, @primer_pairs_bs_s, @primer_pairs_q_s, @save_selection, @intron_exon_bounds, $ie_limit, $ie_limit_5p, $ie_limit_3p, $primer_seq_5f, $primer_seq_5r, $primer_seq_5f_frame, $primer_seq_5r_frame, $primer_seq_5f_atg, $primer_seq_5r_atg, ); my ( $min_ampsize_pr, $max_ampsize_pr, $max_range_5p_pr, $min_range_pr, $max_range_pr, $max_range_3p_pr, $min_range_seq, $max_range_seq, $min_ampsize_bs, $max_ampsize_bs, $max_range_5p_bs, $min_range_bs, $max_range_bs, $max_range_3p_bs, $max_range_5p_q, $min_range_q, $max_range_q, $max_range_3p_q, ); # GUI globals my ( %prr, %prc, %pre, %prb, %prl, %prf, $fprimer, $fprimer_tm, $fprimer_len, $fprimer_ds, $fprimer_dh, $fprimer_dg, $fprimer_gc, $rprimer, $rprimer_tm, $rprimer_len, $rprimer_ds, $rprimer_dh, $rprimer_dg, $rprimer_gc, $old_reference, $text_widget_ref, $style_primer, $style_tm, %min_amp_canvas, %max_amp_canvas, %min_range_canvas, %max_range_canvas, $flag, $rid_get, @blast_results_1, @blast_results_2, @blast_results_3, $rptid, $blast_status, $popup_sort, $popup_sort_seq, $stored_page, $forward_re_site, $reverse_re_site, ); # GUI dialogues my ( $blast_d, $view_base, $prefs, $ack_d, $canvas_info_d, $info_d, $cloning_d, $view_ie, ); my $balloon_help = 1; # Recently used files my @mru; my $mru_number = 8; # Define the perlprimer recognised filetypes my $file_types = [ ['PerlPrimer Files', '.ppr'], ['Fasta Files', '.fasta'], ['All Files', '*'] ]; my $file_types_dna = [ ['Fasta Files', '.fasta'], ['Text Files', '.txt'], ['All Files', '*'] ]; my $file_types_text = [ ['Text Files', '.txt'], ['All Files', '*'] ]; # Open file hash (for writing reports) my %open_file; for my $i (qw(pd seq bis qpcr)) { $open_file{$i} = "File not saved" } # Variable/array hashes for opening and saving my %variables = ( pd => { mintm => \$min_tm_pr, maxtm => \$max_tm_pr, maxdiff => \$max_diff_pr, minwin => \$pri_win_min_pr, maxwin => \$pri_win_max_pr, minamp => \$min_ampsize_pr, maxamp => \$max_ampsize_pr, maxrange5p => \$max_range_5p_pr, minrange => \$min_range_pr, maxrange => \$max_range_pr, maxrange3p => \$max_range_3p_pr, exclude_gc => \$exclude_gc, gc_clamp => \$exclude_clamp, seq => \$save_seq, primer_seq_5f => \$primer_seq_5f, primer_seq_5f_frame => \$primer_seq_5f_frame, primer_seq_5r => \$primer_seq_5r, primer_seq_5r_frame => \$primer_seq_5r_frame, }, seq => { mintm => \$min_tm_seq, maxtm => \$max_tm_seq, minwin => \$pri_win_min_seq, maxwin => \$pri_win_max_seq, minrange => \$min_range_seq, maxrange => \$max_range_seq, seq_spacing_min => \$seq_spacing_min, seq_spacing_max => \$seq_spacing_max, exclude_gc => \$exclude_gc_seq, gc_clamp => \$exclude_clamp_seq, exclude_pd => \$exclude_pd_seq, seq_pd_min => \$seq_pd_min, seq => \$save_seq, }, bis => { mintm => \$min_tm_bs, maxtm => \$max_tm_bs, maxdiff => \$max_diff_bs, minwin => \$pri_win_min_bs, maxwin => \$pri_win_max_bs, minamp => \$min_ampsize_bs, maxamp => \$max_ampsize_bs, maxrange5p => \$max_range_5p_bs, minrange => \$min_range_bs, maxrange => \$max_range_bs, maxrange3p => \$max_range_3p_bs, exclude_cpg => \$exclude_cpg, rrpostconv => \$pre_bs, exclude_cs => \$exclude_3c, min_c => \$bisul_min_c, seq => \$save_seq, }, qpcr => { mintm => \$min_tm_q, maxtm => \$max_tm_q, maxdiff => \$max_diff_q, minwin => \$pri_win_min_q, maxwin => \$pri_win_max_q, minamp => \$min_ampsize_q, maxamp => \$max_ampsize_q, exclude_gc => \$exclude_gc, gc_clamp => \$exclude_clamp, mrna_seq => \$save_seq, dna_seq => \$save_seq2, ie_overlap => \$ie_overlap, exclude_ie => \$exclude_ie, ie_limit => \$ie_limit, ie_limit_5p => \$ie_limit_5p, ie_limit_3p => \$ie_limit_3p, ie_span => \$ie_span, }, ); my %arrays = ( pd => { res => \@primer_pairs_pr_s, selection => \@save_selection, }, seq => { res => \@primer_pairs_seq_s, selection => \@save_selection, }, bis => { res => \@primer_pairs_bs_s, selection => \@save_selection, }, qpcr => { res => \@primer_pairs_q_s, selection => \@save_selection, intron_exon => \@intron_exon_bounds, }, ); # Hash for opening/saving prefs my %pref_variables = ( # home => \$HOME, browser => \$browser, tmp => \$tmp, spidey_path => \$spidey_path, repeats => \$repeat, runs => \$run, exclude_rr => \$exclude_rr, repeats_bs => \$repeat_bs, runs_bs => \$run_bs, exclude_rr_bs => \$exclude_rr_bs, onodera_clamp => \$onodera_clamp, max_gc => \$max_gc, min_gc => \$min_gc, mg_conc => \$mg_conc, monovalent_cation_conc => \$monovalent_cation_conc, dNTP_conc => \$dntp_conc, oligo_conc => \$oligo_conc, cpg_window => \$cpg_window, min_cpg_island => \$min_cpg_island, cpg_gc_percent => \$cpg_gc, cpg_observed_expected => \$cpg_oe, balloon_help => \$balloon_help, blast_database => \$blast_database, blast_entrez_query => \$blast_entrez_query, blast_expect => \$blast_expect, blast_word_size => \$blast_word_size, local_blast => \$local_blast, local_blast_directory => \$local_blast_directory, local_blast_database => \$local_blast_database, gui_font_face => \$gui_font_face, gui_font_size => \$gui_font_size, list_font_face => \$list_font_face, list_font_size => \$list_font_size, text_font_face => \$text_font_face, text_font_size => \$text_font_size, font_override => \$font_override, scroll_factor => \$scroll_factor, ensembl_organism => \$ensembl_organism, ensembl_type => \$ensembl_type, use_proxy => \$use_proxy, http_proxy => \$http_proxy, http_proxy_port => \$http_port, cpgplot_method => \$cpgplot_method, defer_to_caps => \$defer_to_caps, mru_number => \$mru_number, re_simple_sites => \$simple_sites, cloning_anchor_sequence => \$cloning_anchor, pd_temperature => \$pd_temperature, exclude_found_sites => \$exclude_found_sites, file_overwrite => \$file_data_overwrite, ipc_autofind => \$ipc_autofind, tcp_port => \$tcp_port, ); my %pref_arrays = ( mru => \@mru, ); # Hash for variable references my $null = undef; my %page_specific_vars = ( pd => { min_tm => \$min_tm_pr, max_tm => \$max_tm_pr, max_diff => \$max_diff_pr, pri_win_win => \$pri_win_min_pr, pri_win_max => \$pri_win_max_pr, min_ampsize => \$min_ampsize_pr, max_ampsize => \$max_ampsize_pr, max_range_5p => \$max_range_5p_pr, min_range => \$min_range_pr, max_range => \$max_range_pr, max_range_3p => \$max_range_3p_pr, seq => \$packed_widgets{seq}, primers => \@primer_pairs_pr_s, hlist => \$packed_widgets{res}, canvas => \$packed_widgets{primer_canvas}, subroutine => \&get_gene, find_sub => \&find_orf, primer_sub => \&get_primers, popup => \$popup_sort, }, seq => { min_tm => \$min_tm_seq, max_tm => \$max_tm_seq, pri_win_win => \$pri_win_min_seq, pri_win_max => \$pri_win_max_seq, min_ampsize => \$null, max_ampsize => \$null, max_range_5p => \$null, min_range => \$min_range_seq, max_range => \$max_range_seq, max_range_3p => \$null, seq => \$packed_widgets{seq_seq}, primers => \@primer_pairs_seq_s, hlist => \$packed_widgets{seq_res}, canvas => \$packed_widgets{seq_canvas}, subroutine => \&get_gene, find_sub => \&find_orf, primer_sub => \&get_seq_primers, popup => \$popup_sort_seq, }, bis => { min_tm => \$min_tm_bs, max_tm => \$max_tm_bs, max_diff => \$max_diff_bs, pri_win_win => \$pri_win_min_bs, pri_win_max => \$pri_win_max_bs, min_ampsize => \$min_ampsize_bs, max_ampsize => \$max_ampsize_bs, max_range_5p => \$max_range_5p_bs, min_range => \$min_range_bs, max_range => \$max_range_bs, max_range_3p => \$max_range_3p_bs, seq => \$packed_widgets{bisul_seq}, primers => \@primer_pairs_bs_s, hlist => \$packed_widgets{bisul_res}, canvas => \$packed_widgets{bisul_canvas}, subroutine => \&get_cpg, find_sub => \&find_cpg, primer_sub => \&get_bisulphite, popup => \$popup_sort, }, qpcr => { min_tm => \$min_tm_q, max_tm => \$max_tm_q, max_diff => \$max_diff_q, pri_win_win => \$pri_win_min_q, pri_win_max => \$pri_win_max_q, min_ampsize => \$min_ampsize_q, max_ampsize => \$max_ampsize_q, max_range_5p => \$null, min_range => \$null, max_range => \$null, max_range_3p => \$null, seq => \$packed_widgets{qmrna_seq}, primers => \@primer_pairs_q_s, hlist => \$packed_widgets{qpcr_res}, canvas => \$packed_widgets{qprimer_canvas}, find_sub => \&find_orf, popup => \$popup_sort, }, ); # prefs_file my $pref_file = $HOME.'.perlprimer'; # check for upgrading users under win32 who have a different home environment from c:\ if ($os eq 'win' && !check_packages('File::Copy') && -e "c:\.perlprimer" && !-e $pref_file) { print "\n\nPlease Note: as of PerlPrimer v1.1.5 the preferences file is now stored in the user's home directory\nCopying old PerlPrimer preferences to $pref_file ...\n"; copy("c:\.perlprimer",$pref_file); } read_prefs(); # Balloon help messages my %balloonmsg = ( 'primer_getgene', "Find the longest ORF within the sequence and set the selected range", 'primer_reset', "Reset the range to the ORF boundaries", 'primer_stepin_1', "Reduce the inner range by 1bp on each side", 'primer_stepin', "Reduce the inner range by 10bp on each side", 'primer_stepout', "Increase the outer range by 10bp on each side", 'exclude_gc', "Exclude primers with a GC content outside the range specified in the preferences\n(default is 40-60% GC)", 'gc_clamp', "Require primers to have a 3' GC clamp\n(two of the last three bases G or C)", 'primer_seq_5f', "Sequence to add to the 5' end of the forward primer\nPlace an underscore (_) before restriction enzyme site\n\nUse the 'Add cloning sequences' menu option to configure this automatically", 'primer_seq_5r', "Sequence to add to the 5' end of the reverse primer\nPlace an underscore (_) before restriction enzyme site\n\nUse the 'Add cloning sequences' menu option to configure this automatically", 'primer_seq_5f_frame', "Frame of the restriction enzyme site in the cloning plasmid\n0 = in frame\n1 = +1 base\n2 = +2 bases", 'primer_seq_5r_frame', "Frame of the restriction enzyme site in the cloning plasmid\n0 = in frame\n1 = +1 base\n2 = +2 bases", 'seq', "DNA sequence: right-click for menu with options\nincluding opening and saving files", 'res', "Matching primer pairs are displayed here:\nDouble click to see primer-dimers\nRight-click for option menu", 'primerbutton', "Find primer pairs", 'autobuttonin', "Successively reduce the inner range by 10bp increments\nuntil primer pairs are found", 'autobuttonout', "Successively increase the outer range by 10bp increments\nuntil primer pairs are found", 'primer_cancel', "Cancel the current task", 'primer_view', "Copy the selected primer pairs to the clipboard\n(format is tab-delimited text)", 'primer_canvas', "Primer canvas", 'bisul_getcpg', "Find CpG islands within the sequence and set the selected range", 'bisul_reset', "Reset the range to the ORF boundaries", 'bisul_stepin', "Reduce the inner range by 10bp on each side", 'bisul_stepout', "Increase the outer range by 10bp on each side", 'bisul_seq', "DNA sequence: right-click for menu with options\nincluding opening and saving files", 'bisul_res', "Matching primer pairs are displayed here:\nDouble click to see primer-dimers\nRight-click for option menu", 'bisul_exclude_cpg', "Exclude primers with CpG residues", 'bisul_pre_bs', "Exclude primers with repeats / runs after bisulphite conversion", 'bisul_exclude_cs', "Exclude primers without 3' C content\n(either last base as C, or two of last three bases C)", 'bisul_min_c', "Minimum \%C content for primers", 'bisul_button', "Find primer pairs", 'bisul_autobuttonin', "Successively reduce the inner range by 10bp increments\nuntil primer pairs are found", 'bisul_autobuttonout', "Successively increase the outer range by 10bp increments\nuntil primer pairs are found", 'bisul_cancel', "Cancel the current task", 'bisul_view', "Copy the selected primer pairs to the clipboard\n(format is tab-delimited text)", 'qexclude_gc', "Exclude primers with a GC content outside the range specified in the preferences\n(default is 40-60% GC)", 'qgc_clamp', "Require primers to have a 3' GC clamp\n(two of the last three bases G or C)", 'qdna_seq', "Genomic DNA sequence: right-click for menu with options\nincluding opening and saving files", 'qmrna_seq', "mRNA sequence: right-click for menu with options\nincluding opening and saving files", 'qpcr_res', "Matching primer pairs are displayed here:\nDouble click to see primer-dimers\nRight-click for option menu", 'qprimerbutton', "Find primer pairs\n(at least one primer must span an intron-exon boundary)", 'qprimer_cancel', "Cancel the current task", 'qprimer_view', "Copy the selected primer pairs to the clipboard\n(format is tab-delimited text)", 'qprimer_spidey', "Detailed intron/exon boundary information", 'qexclude_ie', "Minimum number of bases within a primer that should overlap an intron/exon boundary", 'qie_limit', "Limit primer search to this range of exons", 'qie_limit_5p', "Leave blank to represent the first exon\nThe exon can also be selected by left-clicking on the DNA graphic", 'qie_limit_3p', "Leave blank to represent the last exon\nThe exon can also be selected by middle-clicking on the DNA graphic", 'qie_overlap', "Require at least one primer to overlap an intron/exon boundary", 'qie_span', "Require primers to span an intron/exon boundary", 'sspacingmin', "Minimum distance between sequencing primers", 'sspacingmax', "Maximum distance between sequencing primers", 'seqminrange', "The region of the DNA to be sequenced", 'seqmaxrange', "The region of the DNA to be sequenced", 'seq_getgene', "Set the sequencing region to the boundaries of the largest ORF present", 'exclude_gc_seq', "Exclude primers with a GC content outside the range specified in the preferences\n(default is 40-60% GC)", 'gc_clamp_seq', "Require primers to have a 3' GC clamp\n(two of the last three bases G or C)", 'exclude_pd_seq', "Do not consider primers which can form dimers\nwith a stability greater than this value", 'spdmin', "Do not consider primers which can form dimers\nwith a stability greater than this value", 'seq_button', "Find primer pairs", 'seq_cancel', "Cancel the current task", 'seq_view', "Copy the selected primer pairs to the clipboard\n(format is tab-delimited text)", 'tmbutton', "Calculate Tm, thermodynamic properties and primer-dimers from the primer sequences", 'blastbutton', "Perform a BLAST search on the primers\n(requires an internet connection)", 'prefs_defer', "Do not attempt to find an ORF or CpG Islands if\na captalised region is already present in the\nDNA sequence - use that region to set the range instead", 'prefs_cpgplot_method', "Emulate the behaviour of the program cpgplot when\nfinding CpG islands (please note that this will cause the \%GC and O/E\nto be an overestimate - see the documentation for details)", 'prefs_gui_override', "Use system default fonts for most widgets\n(requires program restart)", 'prefs_gui_family', "Changes will not take effect until program restart", 'prefs_gui_list_family', "Changes will not take effect until program restart", 'prefs_gui_text_family', "Please use a fixed-width font\nChanges will not take effect until program restart", 'prefs_simple_sites', "Limit restriction enzyme database to 6-base cutting enzymes\n(Also excludes degenerate cutting enzymes)", 'prefs_exclude_found_sites', "When displaying the list of restriction enzymes,\nonly list those that will not cut the input sequence\n(Highly recommended!)", 'cloning_anchor', "Sequence to add 5' of the restiction enzyme sites\n(recommended for successful digestion after amplification)", 'blast_f', "Display results for the forward primer", 'blast_r', "Display results for the reverse primer", 'blast_search_string', "Enter a string to limit results", 'blast_search', "Limit results", 'view_base_copy', "Copies the layout to the clipboard\nas 80-column wrapped text", 'view_base_pf', "Jump to the forward primer", 'view_base_pr', "Jump to the reverse primer", ); #--------------------# # deltaG (kcal/mol) # #--------------------# # Recalculate oligo_dG (kcal/mol) for PCR salt conditions # (the initiation values are salt independent) my %oligo_dG=( qw(initC 0.98 initG 0.98 initA 1.03 initT 1.03), ); recalculate_dG(); # Load pixmap icon data my ( $perlprimer_icon, $icon_open, $icon_open_small, $icon_save, $icon_clear, $icon_info, $icon_magnify, $icon_new, $dna_canvas_pixmap, $icon_separator, $icon_prefs, $icon_report, $icon_save_as, $icon_ensembl, $icon_dna_open, $icon_dna_save, $info_pixmap, $error_pixmap, ); load_icon_data(); # Setup UserAgent my $ua = LWP::UserAgent->new(); # timeout - default value of 180 seconds is too long $ua->timeout(60); $ua->agent('User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'); #-----------------# # # # GUI Interface # # # #-----------------# #-------------# # Main Window # #-------------# my $top = MainWindow->new(-title=>"PerlPrimer v$version"); $top->withdraw(); # configure fonts my $gui_font = "{$gui_font_face} $gui_font_size"; my $font = $gui_font; my $gui_font_bold = $gui_font . ' bold'; my $text_font = "{$text_font_face} $text_font_size"; my $list_font = "{$list_font_face} $list_font_size"; unless ($font_override) { $top->optionAdd("*font", $gui_font); $top->optionAdd("*ROText.font", $text_font); $top->optionAdd("*Text.font", $text_font); $top->optionAdd("*HList.font", $list_font); } # general options $top->optionAdd("*Entry.relief", 'groove'); $top->optionAdd("*Entry.background", '#eeeeee'); $top->optionAdd("*Text.relief", 'groove'); $top->optionAdd("*Text.background", '#eeeeee'); $top->optionAdd("*ROText.relief", 'groove'); $top->optionAdd("*ROText.background", '#eeeeee'); $top->optionAdd("*HList.relief", 'groove'); $top->optionAdd("*HList.background", '#eeeeee'); $top->optionAdd("*HList.header", 1); $top->optionAdd("*HList.selectmode", 'extended'); $top->optionAdd("*Label.justify", 'left'); $top->optionAdd("*Button.padY", $button_pady); $top->optionAdd("*Button.padX", 5); $top->optionAdd("*Checkbutton.padY", $check_pady); $top->optionAdd("*LabFrame.LabelSide", 'acrosstop'); # $top->optionAdd("*LabFrame.background", '#eeeeee'); # $top->optionAdd("*BrowseEntry.background", '#eeeeee'); #--------------# # Balloon help # #--------------# # Set the balloon background colour (nobody actually *likes* the default sick yellow # colour, do they??) $top->optionAdd("*Balloon.Background", "#ececec"); my $Balloon = $top->Balloon(); $Balloon->configure(-state => 'none') if $balloon_help == 0; $Balloon->configure(-state => 'balloon') if $balloon_help == 1; # Notebook reference hash my %nb_page_ref = ( 1 => 'pd', 2 => 'bis', 3 => 'qpcr', 4 => 'seq', ); # Load user-defined default variables load_defaults(); #---------# # Menubar # #---------# # It seems impossible to get the same menu behaviour across platforms with the # same code! This is a compromise which seems to work on my systems. Hopefully # this will hold for other systems too ... my $menu = $top->Menu(-bd => 1, -relief => $menu_relief, -type=>'menubar'); # setting $menu as the -menu for $top causes geometry problems under *nix ... # packing the menubar as a widget causes alignment problems under Win32 ... # (sometimes I think it was easier with menubuttons ....) $menu->pack(-fill=>'x') if $os eq 'nix'; $top->configure(-menu => $menu) if $os eq 'win'; # recently used items my $menu_mru = $menu->Menu(-title => "Recently opened", -menuitems => [ [command => "- none available -", -state => "disabled" ] ]); recently_used_files(); my $menu_file = $menu->cascade(-label => "File", -menuitems => [ [command => "New file", -command =>\&new_file, -accelerator=>'Ctrl-N' ], [command => "Open ...", -command =>\&pp_file_open, -accelerator=>'Ctrl-O' ], [cascade => "Open Previous", -menu=>$menu_mru ], "-", [command => "Save ...", -command =>\&pp_file_save, -accelerator=>'Ctrl-S' ], [command => "Save as ...", -command =>[sub {pp_file_save(1)}], ], "-", [command => "Retrieve gene from Ensembl", -command =>\&get_ensembl, -accelerator=>'Ctrl-E' ], "-", [command => "Restart", -command => \&restart, ], [command => "Exit", -command => \&end_prog, -accelerator=>'Ctrl-Q' ] ]); my $menu_tools = $menu->cascade(-label => 'Tools', -menuitems => [ [command => "Find primers for cloning", -command => \&get_primers_cloning, ], [command => "Add cloning sequences", -command => \&find_re_sites, ], "-", [command => "Generate report", -command => \&generate_report, -accelerator=>'Ctrl-R' ], "-", [command => "Save default values for this page", -command => \&save_defaults, ], [command => "Restore in-built default values", -command => \&restore_defaults, ], "-", [command => "Preferences", -command => \&prefs, -accelerator=>'Ctrl-P' ], ]); my $menu_help = $menu->cascade(-label => "Help", -menuitems => [ ['Checkbutton' => "Balloon help", -variable => \$balloon_help, -command => \&balloon_toggle], "-", [command => "Acknowledgements", -command => \&acknowledgements ], [command => "About ...", -command => \&info, -accelerator=>'Ctrl-Shift-A' ], ]); #--------------# # Key bindings # #--------------# # If you're wondering about the anonymous sub for file opening - it's because # Perl/Tk sends the calling widget as the first argument ... $top->bind('' => \&new_file); $top->bind('' => [sub {pp_file_open()}]); $top->bind('' => [sub {pp_file_save()}]); $top->bind('' => \&end_prog); $top->bind('' => \&generate_report); $top->bind('' => \&prefs); $top->bind('' => \&info); $top->bind('' => \&get_ensembl); #---------# # Toolbar # #---------# my $separator_menu = $top->Frame(-relief => "groove", -height=>2, -bd => 2); $separator_menu->pack(-fill=> 'x') if $os eq 'win'; my $toolbar = $top->Frame(-relief => $menu_relief, -bd => 1) ->pack(-fill=> 'x'); my $tool_new = pack_button($toolbar, $top->Pixmap(-data => $icon_new), \&new_file)->pack(-side=>'left'); $Balloon->attach($tool_new, -balloonposition => 'mouse', -balloonmsg => "New file"); my $tool_open = pack_button($toolbar, $top->Pixmap(-data => $icon_open), \&pp_file_open)->pack(-side=>'left'); $Balloon->attach($tool_open, -balloonposition => 'mouse', -balloonmsg => "Open PerlPrimer file"); my $tool_save = pack_button($toolbar, $top->Pixmap(-data => $icon_save), \&pp_file_save)->pack(-side=>'left'); $Balloon->attach($tool_save, -balloonposition => 'mouse', -balloonmsg => "Save file"); my $tool_save_as = pack_button($toolbar, $top->Pixmap(-data => $icon_save_as), [sub {pp_file_save(1)}])->pack(-side=>'left'); $Balloon->attach($tool_save_as, -balloonposition => 'mouse', -balloonmsg => "Save file as ..."); my $tool_sep1 = $toolbar->Label(-image => $top->Pixmap(-data => $icon_separator))->pack(-side=>'left', -padx=>2); my $tool_ensembl = pack_button($toolbar, $top->Pixmap(-data => $icon_ensembl), \&get_ensembl)->pack(-side=>'left'); $Balloon->attach($tool_ensembl, -balloonposition => 'mouse', -balloonmsg => "Retrieve gene from Ensembl"); my $tool_sep2 = $toolbar->Label(-image => $top->Pixmap(-data => $icon_separator))->pack(-side=>'left', -padx=>2); my $tool_prefs = pack_button($toolbar, $top->Pixmap(-data => $icon_prefs), \&prefs)->pack(-side=>'left'); $Balloon->attach($tool_prefs, -balloonposition => 'mouse', -balloonmsg => "Preferences"); my $tool_report = pack_button($toolbar, $top->Pixmap(-data => $icon_report), \&generate_report)->pack(-side=>'left'); $Balloon->attach($tool_report, -balloonposition => 'mouse', -balloonmsg => "Generate report"); my $separator_tools = $top->Frame(-relief => "groove", -height=>2, -bd => 2); $separator_tools->pack(-fill=> 'x') if $os eq 'win'; #---------------# # Draw Notebook # #---------------# # notebook inactivebackground calculation # (uses a 90% shade of the default widget colour - as per Perl/Tk *nix defaults. # Win32 does not use this by default, but I feel it improves usability) my $nb_colour = $top->cget(-bg); my ($red, $green, $blue) = $top->rgb($nb_colour); $red *= 0.9; $green *= 0.9; $blue *= 0.9; $nb_colour = sprintf "%lx%lx%lx", $red, $green, $blue; my $nb = $top->NoteBook( -relief => 'raised', -inactivebackground => "#$nb_colour", -bd => 1, )->pack( -expand=>1, -padx=>2, -pady=>4, -fill=>'both', ); # Notebook pages my $page_primer_design = $nb->add("pd", -label=>"Standard PCR", -anchor=>"nw"); my $page_bisul_seq = $nb->add("bis", -label=>"Bisulphite PCR", -anchor=>"nw"); my $page_qpcr = $nb->add("qpcr", -label=>"Real-time PCR", -anchor=>'nw'); my $page_seq = $nb->add("seq", -label=>"Sequencing", -anchor=>"nw"); my $page_primer = $nb->add("primer", -label=>"Primers", -anchor=>"nw"); # Event binding to allow name display $page_primer_design->bind('' => \&update_title); $page_bisul_seq->bind('' => \&update_title); $page_qpcr->bind('' => \&update_title); $page_seq->bind('' => \&update_title); # Have to use frames to get the nw anchor - grid plonks everything in the centre ... my $page_primerf = $page_primer->Frame()->pack(-anchor=>'nw', -expand=>'1', -fill=>'both'); my $page_primerfb = $page_primer->Frame()->pack(-side=>'bottom', -anchor=>'sw'); my $page_primer_designf = $page_primer_design->Frame()->pack(-anchor=>'nw', -expand=>'1', -fill=>'both'); my $page_primer_designfb = $page_primer_design->Frame()->pack(-side=>'bottom', -anchor=>'sw'); my $page_sequencingf = $page_seq->Frame()->pack(-anchor=>'nw', -expand=>'1', -fill=>'both'); my $page_sequencingfb = $page_seq->Frame()->pack(-side=>'bottom', -anchor=>'sw'); my $page_bisul_seqf = $page_bisul_seq->Frame()->pack(-anchor=>'nw', -expand=>'1', -fill=>'both'); my $page_bisul_seqfb = $page_bisul_seq->Frame()->pack(-side=>'bottom', -anchor=>'sw'); my $page_qpcrf = $page_qpcr->Frame()->pack(-anchor=>'nw', -expand=>'1', -fill=>'both'); my $page_qpcrfb = $page_qpcr->Frame()->pack(-side=>'bottom', -anchor=>'sw'); #------------# # Status bar # #------------# my $status_bar_f = $top->Frame()->pack(-side=>'bottom', -fill=>'x', -padx=>0,); my $status_bar = $status_bar_f->ROText(-height=>1, -bg=>'#eeeeee', -relief=>'sunken', )->pack(-fill=>'x'); $status_bar->tagConfigure('red', -foreground => 'red'); $status_bar->tagConfigure('blue', -foreground => 'blue'); $status_bar->tagConfigure('grey', -foreground => 'grey20'); tie (*SBAR, 'Tk::Text', $status_bar); # Packing system # # IMPORTANT UPDATE: reliance on grid() has now been greatly reduced (only used for labframe # widgets) - each widget row is now separately defined by the nr() subroutine and widgets # are packed from the left until nr() is called again. Much simpler and easier! # (NB - GUI code still has lots of relic options in the pack_gui() calls - needs to be # tidied up a lot!) # # FURTHER UPDATE: as of versions > 1.1.4a, the system has been completely re-written, providing # a simpler and more straightforward system with the ability to include any number of custom arguments. # It should be slightly faster, too, at the expense of some code verbosity. # # New format is: # [more specific args] [generic Perl/Tk options] # # Widgets are packed from the left into horizontal frames defined by the nr() call my @row_counter; #-------------# # Primer page # #-------------# pack_gui('LabFrame', 'Forward primer', 'forward_l', \$page_primerf); nr(\$packed_widgets{forward_l}); pack_gui('Label', 'Sequence', 'fprimerseql'); pack_gui('Entry', \$fprimer, 'fprimerseq', 40); nr(); my $col_ref = $row_counter[-1]; nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'Tm: ', 'fprimertm', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_tm, 'fprimertm'); pack_gui('Label', 'C', 'fprimertm'); nr('',0); pack_gui('Label', 'dS: ', 'fprimerds', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_ds, 'fprimerds'); pack_gui('Label', 'eu', 'fprimerds'); # nr('',0); # pack_gui('Label', "dG$pd_temperature: ", 'fprimerds'); # pack_gui('Label', \$fprimer_dg, 'fprimerds'); # pack_gui('Label', 'kcal/mol', 'fprimerds'); nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'Length: ', 'fprimerlen', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_len, 'fprimerlen'); pack_gui('Label', 'bases', 'fprimerlen'); nr('',0); pack_gui('Label', 'dH: ', 'fprimerdh', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_dh, 'fprimerdh'); pack_gui('Label', 'kcal/mol', 'fprimerdh'); nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'GC: ', 'fprimergc', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_gc, 'fprimergc'); pack_gui('Label', '%', 'fprimergc'); nr('',0); pack_gui('Label', "dG$pd_temperature: ", 'fprimerds', -font=>$gui_font_bold); pack_gui('Label', \$fprimer_dg, 'fprimerds'); pack_gui('Label', 'kcal/mol', 'fprimerds'); pack_gui('LabFrame', 'Reverse primer', 'reverse_l', \$page_primerf); nr(\$packed_widgets{reverse_l}); pack_gui('Label', 'Sequence', 'rprimerseq'); pack_gui('Entry', \$rprimer, 'rprimerseq', 40); nr(); $col_ref = $row_counter[-1]; nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'Tm: ', 'rprimertm', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_tm, 'rprimertm'); pack_gui('Label', 'C', 'rprimertm'); nr('',0); pack_gui('Label', 'dS: ', 'rprimerds', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_ds, 'rprimerds'); pack_gui('Label', 'eu', 'rprimerds'); # nr('',0); # pack_gui('Label', "dG$pd_temperature: ", 'rprimerds'); # pack_gui('Label', \$rprimer_dg, 'rprimerds'); # pack_gui('Label', 'kcal/mol', 'rprimerds'); nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'Length: ', 'rprimerlen', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_len, 'rprimerlen'); pack_gui('Label', 'bases', 'rprimerlen'); nr('',0); pack_gui('Label', 'dH: ', 'rprimerdh', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_dh, 'rprimerdh'); pack_gui('Label', 'kcal/mol', 'rprimerdh'); nc(\$col_ref); nr(\$row_counter[-1],0); pack_gui('Label', 'GC: ', 'fprimergc', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_gc, 'fprimergc'); pack_gui('Label', '%', 'fprimergc'); nr('',0); pack_gui('Label', "dG$pd_temperature: ", 'fprimerds', -font=>$gui_font_bold); pack_gui('Label', \$rprimer_dg, 'fprimerds'); pack_gui('Label', 'kcal/mol', 'fprimerds'); pack_gui('LabFrame', 'Dimers', 'dim_l', \$page_primerf); nr(\$packed_widgets{dim_l}, $frame_pady, 1); pack_gui('ROText', 'Dimers', 'dim', 60, 15, -scrollbars=>'oe'); $packed_widgets{dim}->bind('' => \&jump_to_tm); $packed_widgets{dim}->bind('' => \&jump_back); $packed_widgets{dim}->bind('' => \&jump_back); nr(\$page_primerfb, 0); pack_gui('Button', 'Calculate Tm', 'tmbutton', \&get_tm, 'active'); pack_gui('Button', 'BLAST primers', 'blastbutton', \&blast_primers); # Grid layout pack_grid(qw(forward_l)); pack_grid(qw(reverse_l)); pack_grid(qw(dim_l)); $packed_widgets{dim}->configure(-fg=>'grey30'); $packed_widgets{dim}->tagConfigure('blue', -foreground => 'midnightblue'); $packed_widgets{dim}->tagConfigure('red', -foreground => 'red'); $packed_widgets{dim}->tagConfigure('black', -foreground => 'black'); # Column and row weights $page_primerf->gridColumnconfigure(0,-weight=>1); $page_primerf->gridRowconfigure(2,-weight=>1); tie (*DIMER, 'Tk::Text',$packed_widgets{'dim'}); #--------------------# # Primer Design page # #--------------------# pack_gui('LabFrame', 'Primer Tm', 'primer_tm_l', \$page_primer_designf); nr(\$packed_widgets{primer_tm_l}); pack_gui('Entry', \$min_tm_pr, 'mintm', 3); pack_gui('Label', '-'); pack_gui('Entry', \$max_tm_pr, 'maxtm', 3); pack_gui('Label', 'C Difference'); pack_gui('Entry', \$max_diff_pr, 'maxdiff', 3); pack_gui('Label', 'C'); pack_gui('LabFrame', 'Primer Length', 'primer_len_l', \$page_primer_designf); nr(\$packed_widgets{primer_len_l}); pack_gui('Entry', \$pri_win_min_pr, 'minwin', 3); pack_gui('Label', '-'); pack_gui('Entry', \$pri_win_max_pr, 'maxwin', 3); pack_gui('Label', 'bases'); pack_gui('LabFrame', 'Amplified range', 'primer_range_l', \$page_primer_designf); nr(\$packed_widgets{primer_range_l}, 2); pack_gui('Label', "5'"); pack_gui('Entry', \$max_range_5p_pr, 'maxrange5p', 5); pack_gui('Label', '-'); pack_gui('Entry', \$min_range_pr, 'minrange', 5); pack_gui('Label', " 3'"); pack_gui('Entry', \$max_range_pr, 'maxrange', 5); pack_gui('Label', '-'); pack_gui('Entry', \$max_range_3p_pr, 'maxrange3p', 5); nr(''); pack_gui('Label', 'Amplicon size: '); pack_gui('Label', \$min_ampsize_pr); pack_gui('Label', '-'); pack_gui('Label', \$max_ampsize_pr); pack_gui('Label', 'bases'); nr('', 0); pack_gui('Button', 'Set from ORF', 'primer_getgene', \&reset_bounds); pack_gui('Button', '-1', 'primer_stepin_1', sub{step_in(1)}); pack_gui('Button', '-10', 'primer_stepin', sub{step_in(10)}); pack_gui('Button', '+10', 'primer_stepout', \&step_out); pack_gui('LabFrame', 'Options', 'primer_options_l', \$page_primer_designf); nr(\$packed_widgets{primer_options_l}); pack_gui('Checkbutton', 'Exclude %GC', 'exclude_gc', \$exclude_gc); pack_gui('Checkbutton', 'GC clamp', 'gc_clamp', \$exclude_clamp); nr(); pack_gui('Label', "Add 5' F seq "); pack_gui('Entry', \$primer_seq_5f, 'primer_seq_5f', 12); pack_gui('Label', 'Frame'); pack_gui('Entry', \$primer_seq_5f_frame, 'primer_seq_5f_frame', 2); # not currently implemented and may never be ... # pack_gui('ATG', $primer_seq_5f_atg, 'primer_seq_5f_atg', '', 'c', 0, 1); nr(); pack_gui('Label', "Add 5' R seq "); pack_gui('Entry', \$primer_seq_5r, 'primer_seq_5r', 12); pack_gui('Label', 'Frame'); pack_gui('Entry', \$primer_seq_5r_frame, 'primer_seq_5r_frame', 2); # pack_gui('ATG', $primer_seq_5r_atg, 'primer_seq_5r_atg', '', 'c', 0, 1); pack_gui('LabFrame', 'Sequence', 'seq_l', \$page_primer_designf); nr(\$packed_widgets{seq_l}); pack_gui('Text', 'Sequence', 'seq', 60, 6, -scrollbars=>'oe'); pack_menu('seq'); pack_osc_buttons('seq'); pack_gui('LabFrame', 'Results', 'res_l', \$page_primer_designf); nr(\$packed_widgets{res_l}, $frame_pady, 1); pack_gui('HList', '', 'res', 60, 10, 11, \&browse_primer); header_create(\$packed_widgets{'res'}, @header_list_primers); nr(); pack_gui('Canvas', '', 'primer_canvas', 1, 1); nr(\$page_primer_designfb, 0); pack_gui('Button', 'Find primers', 'primerbutton', \&get_primers, 'active'); pack_gui('Button', 'Find inwards', 'autobuttonin', \&get_primers_auto_in); pack_gui('Button', 'Find outwards', 'autobuttonout', \&get_primers_auto_out); pack_gui('Button', 'Cancel', 'primer_cancel', \&cancel); pack_gui('Button', 'Copy selected', 'primer_view', \©_selected_primers); # Grid layout pack_grid(qw(primer_tm_l primer_len_l)); pack_grid(qw(primer_range_l primer_options_l)); pack_grid(qw(seq_l -)); pack_grid(qw(res_l -)); # Column and row weights $page_primer_designf->gridColumnconfigure(0,-weight=>1); $page_primer_designf->gridColumnconfigure(1,-weight=>1); $page_primer_designf->gridRowconfigure(3,-weight=>1); #-----------------# # Sequencing page # #-----------------# pack_gui('LabFrame', 'Primer Tm', 'seq_tm_l', \$page_sequencingf); nr(\$packed_widgets{seq_tm_l}); pack_gui('Entry', \$min_tm_seq, 'smintm', 3); pack_gui('Label', '-'); pack_gui('Entry', \$max_tm_seq, 'smaxtm', 3); pack_gui('Label', 'C'); pack_gui('LabFrame', 'Primer Length', 'seq_len_l', \$page_sequencingf); nr(\$packed_widgets{seq_len_l}); pack_gui('Entry', \$pri_win_min_seq, 'sminwin', 3); pack_gui('Label', '-'); pack_gui('Entry', \$pri_win_max_seq, 'smaxwin', 3); pack_gui('Label', 'bases'); pack_gui('LabFrame', 'Spacing / Coverage', 'seq_amp_l', \$page_sequencingf); nr(\$packed_widgets{seq_amp_l}, 2); pack_gui('Label', 'Primers every'); pack_gui('Entry', \$seq_spacing_min, 'sspacingmin', 4); pack_gui('Label', '-'); pack_gui('Entry', \$seq_spacing_max, 'sspacingmax', 4); pack_gui('Label', 'bases'); nr(''); pack_gui('Label', "Range: 5'"); pack_gui('Entry', \$min_range_seq, 'seqminrange', 5); pack_gui('Label', '-'); pack_gui('Entry', \$max_range_seq, 'seqmaxrange', 5); pack_gui('Label', "3'"); nr('', 0); pack_gui('Button', 'Set range from ORF', 'seq_getgene', \&reset_bounds); pack_gui('LabFrame', 'Options', 'seq_options_l', \$page_sequencingf); nr(\$packed_widgets{seq_options_l}, 0); pack_gui('Checkbutton', 'Exclude %GC', 'exclude_gc_seq', \$exclude_gc_seq); pack_gui('Checkbutton', 'GC clamp', 'gc_clamp_seq', \$exclude_clamp_seq); nr('', 0); pack_gui('Checkbutton', 'Exclude self-complimentarity > -', 'exclude_pd_seq', \$exclude_pd_seq); pack_gui('Entry', \$seq_pd_min, 'spdmin', 3); pack_gui('Label', 'dG37'); pack_gui('LabFrame', 'Sequence', 'seq_seq_l', \$page_sequencingf); nr(\$packed_widgets{seq_seq_l}); pack_gui('Text', 'Sequence', 'seq_seq', 60, 6, -scrollbars=>'oe'); pack_menu('seq_seq'); pack_osc_buttons('seq_seq'); pack_gui('LabFrame', 'Results', 'seq_res_l', \$page_sequencingf); nr(\$packed_widgets{seq_res_l}, $frame_pady, 1); pack_gui('HList', '', 'seq_res', 60, 10, 11, \&browse_primer); header_create(\$packed_widgets{'seq_res'}, @header_list_primers); nr(); pack_gui('Canvas', '', 'seq_canvas', 1, 1); nr(\$page_sequencingfb, 0); pack_gui('Button', 'Find primers', 'seq_button', \&get_seq_primers, 'active'); pack_gui('Button', 'Cancel', 'seq_cancel', \&cancel); pack_gui('Button', 'Copy selected', 'seq_view', \©_selected_primers); # Grid layout pack_grid(qw(seq_tm_l seq_len_l)); pack_grid(qw(seq_amp_l seq_options_l)); pack_grid(qw(seq_seq_l -)); pack_grid(qw(seq_res_l -)); # Column and row weights $page_sequencingf->gridColumnconfigure(0,-weight=>1); $page_sequencingf->gridColumnconfigure(1,-weight=>1); $page_sequencingf->gridRowconfigure(3,-weight=>1); #----------------------------# # Bisulphite sequencing page # #----------------------------# pack_gui('LabFrame', 'Primer Tm', 'bisul_tm_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_tm_l}); pack_gui('Entry', \$min_tm_bs, 'bisul_mintm', 3); pack_gui('Label', '-'); pack_gui('Entry', \$max_tm_bs, 'bisul_maxtm', 3); pack_gui('Label', 'C Difference'); pack_gui('Entry', \$max_diff_bs, 'bisul_maxdiff', 3); pack_gui('Label', 'C'); pack_gui('LabFrame', 'Primer Length', 'bisul_len_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_len_l}); pack_gui('Entry', \$pri_win_min_bs, 'minwin', 3); pack_gui('Label', '-'); pack_gui('Entry', \$pri_win_max_bs, 'maxwin', 3); pack_gui('Label', 'bases'); pack_gui('LabFrame', 'Amplified range', 'bisul_range_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_range_l}, 2); pack_gui('Label', "5'"); pack_gui('Entry', \$max_range_5p_bs, 'bisul_maxrange5p', 5); pack_gui('Label', '-'); pack_gui('Entry', \$min_range_bs, 'bisul_minrange', 5); pack_gui('Label', " 3'"); pack_gui('Entry', \$max_range_bs, 'bisul_maxrange', 5); pack_gui('Label', '-'); pack_gui('Entry', \$max_range_3p_bs, 'bisul_maxrange3p', 5); nr(''); pack_gui('Label', 'Amplicon size: '); pack_gui('Label', \$min_ampsize_bs); pack_gui('Label', '-'); pack_gui('Label', \$max_ampsize_bs); pack_gui('Label', 'bases'); nr('', 0); pack_gui('Button', 'Set from CpG island', 'bisul_getcpg', \&reset_bounds); pack_gui('Button', '-1', 'primer_stepin_1', sub{step_in(1)}); pack_gui('Button', '-10', 'primer_stepin', sub{step_in(10)}); pack_gui('Button', '+10', 'primer_stepout', \&step_out); pack_gui('LabFrame', 'Options', 'bisul_options_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_options_l}, 0); pack_gui('Checkbutton', 'Exclude CpG', 'bisul_exclude_cpg', \$exclude_cpg); pack_gui('Checkbutton', "Require 3' C", 'bisul_exclude_cs', \$exclude_3c); nr('', 0); pack_gui('Checkbutton', 'Repeats / runs post conversion', 'bisul_pre_bs', \$pre_bs); nr(); pack_gui('Label', 'Minimum primer C content '); pack_gui('Entry', \$bisul_min_c, 'bisul_min_c', 4); pack_gui('Label', '%'); pack_gui('LabFrame', 'Sequence', 'bisul_seq_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_seq_l}); pack_gui('Text', 'Sequence', 'bisul_seq', 60, 6, -scrollbars=>'oe'); pack_menu('bisul_seq'); pack_osc_buttons('bisul_seq'); pack_gui('LabFrame', 'Results', 'bisul_res_l', \$page_bisul_seqf); nr(\$packed_widgets{bisul_res_l}, $frame_pady, 1); pack_gui('HList', '', 'bisul_res', 60, 10, 11, \&browse_bisulphite); header_create(\$packed_widgets{'bisul_res'}, @header_list_primers); nr(); pack_gui('Canvas', '', 'bisul_canvas', 1, 1); nr(\$page_bisul_seqfb, 0); pack_gui('Button', 'Find primers', 'bisul_button', \&get_bisulphite, 'active'); pack_gui('Button', 'Find inwards', 'bisul_autobuttonin', \&get_primers_auto_in); pack_gui('Button', 'Find outwards', 'bisul_autobuttonout', \&get_primers_auto_out); pack_gui('Button', 'Cancel', 'bisul_cancel', \&cancel); pack_gui('Button', 'Copy selected', 'bisul_view', \©_selected_primers); # Grid layout pack_grid(qw(bisul_tm_l bisul_len_l)); pack_grid(qw(bisul_range_l bisul_options_l)); pack_grid(qw(bisul_seq_l -)); pack_grid(qw(bisul_res_l -)); # Column and row weights $page_bisul_seqf->gridColumnconfigure(0,-weight=>1); $page_bisul_seqf->gridColumnconfigure(1,-weight=>1); $page_bisul_seqf->gridRowconfigure(3,-weight=>1); #-----------# # QPCR page # #-----------# pack_gui('LabFrame', 'Primer Tm', 'qprimer_tm_l', \$page_qpcrf); nr(\$packed_widgets{qprimer_tm_l}); pack_gui('Entry', \$min_tm_q, 'qmintm', 3); pack_gui('Label', '-'); pack_gui('Entry', \$max_tm_q, 'qmaxtm', 3); pack_gui('Label', 'C Difference'); pack_gui('Entry', \$max_diff_q, 'qmaxdiff', 3); pack_gui('Label', 'C'); pack_gui('LabFrame', 'Primer Length', 'qprimer_len_l', \$page_qpcrf); nr(\$packed_widgets{qprimer_len_l}); pack_gui('Entry', \$pri_win_min_q, 'qminwin', 3); pack_gui('Label', '-'); pack_gui('Entry', \$pri_win_max_q, 'qmaxwin', 3); pack_gui('Label', 'bases'); pack_gui('LabFrame', 'Amplicon size', 'qprimer_amp_l', \$page_qpcrf); nr(\$packed_widgets{qprimer_amp_l}); pack_gui('Entry', \$min_ampsize_q, 'qminamp', 4); pack_gui('Label', '-'); pack_gui('Entry', \$max_ampsize_q, 'qmaxamp', 4); pack_gui('Label', 'bases'); nr(); pack_gui('Checkbutton', 'Limit primers to exon(s)', 'qie_limit', \$ie_limit); pack_gui('Entry', \$ie_limit_5p, 'qie_limit_5p', 3); pack_gui('Label', '-'); pack_gui('Entry', \$ie_limit_3p, 'qie_limit_3p', 3); pack_gui('LabFrame', 'Options', 'qprimer_options_l', \$page_qpcrf); nr(\$packed_widgets{qprimer_options_l}, 0); pack_gui('Checkbutton', 'Exclude %GC', 'qexclude_gc', \$exclude_gc); pack_gui('Checkbutton', 'GC clamp', 'qgc_clamp', \$exclude_clamp); nr(); pack_gui('Checkbutton', 'Span intron/exon boundary', 'qie_span', \$ie_span); nr(); pack_gui('Checkbutton', 'Overlap intron/exon boundary by', 'qie_overlap', \$ie_overlap); pack_gui('Entry', \$exclude_ie, 'qexclude_ie', 3); pack_gui('Label', 'bases'); pack_gui('LabFrame', 'Genomic sequence', 'qdna_seq_l', \$page_qpcrf); nr(\$packed_widgets{qdna_seq_l}); pack_gui('Text', '', 'qdna_seq', 25, 6, -scrollbars=>'oe'); pack_menu('qdna_seq'); pack_osc_buttons('qdna_seq'); pack_gui('LabFrame', 'mRNA sequence', 'qmrna_seq_l', \$page_qpcrf); nr(\$packed_widgets{qmrna_seq_l}); pack_gui('Text', '', 'qmrna_seq', 25, 6, -scrollbars=>'oe'); pack_menu('qmrna_seq'); pack_osc_buttons('qmrna_seq'); pack_gui('LabFrame', 'Results', 'qpcr_res_l', \$page_qpcrf); nr(\$packed_widgets{qpcr_res_l}, $frame_pady, 1); pack_gui('HList', '', 'qpcr_res', 60, 10, 11, \&browse_primer); header_create(\$packed_widgets{'qpcr_res'}, @header_list_primers); nr(); pack_gui('Canvas', '', 'qprimer_canvas', 1, 1); nr(\$page_qpcrfb, 0); pack_gui('Button', 'Find primers', 'qprimerbutton', \&get_qprimers, 'active'); pack_gui('Button', 'Cancel', 'qprimer_cancel', \&cancel); pack_gui('Button', 'Copy selected', 'qprimer_view', \©_selected_primers); pack_gui('Button', 'View intron/exon structure', 'qprimer_view_ie', \&view_intron_exon_structure); pack_gui('Button', 'Spidey output', 'qprimer_spidey', \&view_spidey_out); # Grid layout pack_grid(qw(qprimer_tm_l qprimer_len_l)); pack_grid(qw(qprimer_amp_l qprimer_options_l)); pack_grid(qw(qdna_seq_l qmrna_seq_l)); pack_grid(qw(qpcr_res_l -)); # Column and row weights $page_qpcrf->gridColumnconfigure(0,-weight=>1); $page_qpcrf->gridColumnconfigure(1,-weight=>1); $page_qpcrf->gridRowconfigure(3,-weight=>1); #-------------# # Window icon # #-------------# my $pixmap = $top->Pixmap(-data => $perlprimer_icon); # set icon for main window ... $top->Icon(-image => $pixmap); #-------------# # Popup Menus # #-------------# # A big problem with perl/Tk: # HList does not have any way to bind to column headers or even # to identify in which column a click was - this is the only way to # provide sorting ... It's very clumsy! # (update: after using this for a while, I actually think it works rather well # for this system - since not all the Hlist column headers are generally visible # at any one time, it's nice to have this system in place) my $sort_reverse=0; my $sort_prev = 10; $popup_sort = $top->Menu(-menuitems => [ [cascade => 'Sort primers by ...', -menuitems => [ [command => 'Forward position', -command => [\&sort_primers, 1]], [command => ' ... length', -command => [\&sort_primers, 2]], [command => ' ... Tm', -command => [\&sort_primers, 3]], '-', [command => 'Reverse position', -command => [\&sort_primers, 8]], [command => ' ... length', -command => [\&sort_primers, 6]], [command => ' ... Tm', -command => [\&sort_primers, 7]], '-', [command => 'Amplicon size', -command => [\&sort_primers, 9]], [command => 'Extensible Dimer dG', -command => [\&sort_primers, 10]], [command => 'Full Dimer dG', -command => [\&sort_primers, 13]], '-', [Checkbutton => 'Reversed', -variable => \$sort_reverse, -command =>[\&sort_primers, "r"]], ]], '-', [command => 'Select all', -command => [\&select_all_primers]], [command => 'Copy', -command => [\©_selected_primers]], '-', [command => 'Set range from selected', -command => [\&primer_take_range]], ]); $popup_sort_seq = $top->Menu(-menuitems => [ [Button => 'Select all', -command => [\&select_all_primers]], '-', [Button => 'Copy', -command => [\©_selected_primers]], ]); # Text menu my $popup_text = $top->Menu(-menuitems => [ [Button => 'Open sequence ...', -command => [\&open_seq, \$text_widget_ref]], [Button => 'Save sequence ...', -command => [\&save_seq, \$text_widget_ref]], '-', [Button => 'Select All', -command => [\&select_all_text, \$text_widget_ref]], [Button => 'Clear', -command => [\&clear_text, \$text_widget_ref]], [Button => 'Lower case', -command => [\&lc_text, \$text_widget_ref]], '-', [Button => 'Reverse complement', -command => [\&reverse_complement, \$text_widget_ref]], ]); # Minimize the dosbox if we're running as a standalone executable ... if ($win_exe && !check_packages("Win32::GUI")) { # Use GUI::Hide if you want to hide the box, but personally I'd rather leave # the console visible in case of debugging info. Win32::GUI::Minimize(scalar(Win32::GUI::GetPerlWindow())); } # Show the main window $top->deiconify(); # prevent the user from resizing the window any smaller (which makes things hidden/messy) my $min_width = $top->geometry; my ($min_x, $min_y) = ($min_width =~ /(\d.*)x(\d.*?)\+/); $top->minsize($min_x, $min_y); # Open file if specified on the command line if ($commandline) { $top->update; pp_file_open($commandline); } # Override HList bindings for left/right and other commands... my $class = "Tk::HList"; $top->bind($class,'', \&jump_to_tm); $top->bind($class,'', \&jump_back); $top->bind($class,'<2>', \©_selected_primers); $top->bind($class,'', \&select_all_primers); $top->bind($class,'', \©_selected_primers); $top->bind($class,'<3>', \&menu_popup); # Socket code for contig viewer my ($sock, $sel, $sock_repeat_id); setup_sock(); if ($win_exe) { print "\nPerlPrimer started successfully\n"; } MainLoop(); # Program end exit_program(); #------------------------------# # # # Subroutines start here ... # # # #------------------------------# #------------------# # Post-GUI routine # #------------------# sub exit_program { # Save updated prefs, (including Balloon help) my $file_data = ""; foreach my $i (keys %pref_variables) { my $pointer = $pref_variables{$i}; $file_data .= "$i = $$pointer\n"; } foreach my $i (keys %pref_arrays) { my $pointer = $pref_arrays{$i}; $file_data .= "$i = [".join(",",@$pointer)."]\n"; } open (PREFS, ">$pref_file") || die "Error: could not open $pref_file for writing: $!\n"; print PREFS $file_data; close (PREFS); exit 0; } #----------------# # Socket reading # #----------------# sub setup_sock { $sock = IO::Socket::INET->new( Listen => SOMAXCONN, Reuse => 1, LocalPort => $tcp_port, Proto => 'tcp', ); if (defined($sock)) { if ($os eq 'win') { # Fileevent with sockets does not work with Win32 - # here we check by manually polling the socket every second use IO::Select; $sel = IO::Select->new; $sel->add($sock); $sock_repeat_id = $top->repeat($socket_polling_interval => \&read_sock); } else { $top->fileevent($sock, 'readable' => \&read_sock); } } else { # no socket available print "Could not open socket at port $tcp_port\n"; } } sub read_sock { # Read the socket my $hand = $sock; if ($os eq 'win') { # direct polling ... my(@ready) = $sel->can_read(0); return if $#ready == -1; # Nothing to read ... move along ... $hand = $ready[0]; } my $new_sock = $hand->accept(); my $numbytes = 2048; my $data = ""; my $count; my $num = $numbytes; while ($num==$numbytes) { my $buf; $num = sysread $new_sock, $buf, $numbytes; unless (defined($num)) { last; } $data .= $buf; } my (@lines) = split("\n", $data); if ($#lines == -1) { # connection has been broken or something has gone wrong return; } else { # Open the socket data as a FASTA file pp_file_open("[data from external application]", '', @lines); my ($sub) = get_variables(qw(primer_sub)); &$sub() if $ipc_autofind; } } #-------------------------# # GUI building subroutine # #-------------------------# # The messy guts that allow all the other GUI parts of the program to be so neat # and tidy. It's nothing fancy - in fact, it's downright inelegant - but it gets # the job done, keeps things consistent, and writing new gui code (eg, the # preferences dialogue) is a matter of minutes to do ... sub nr { # New row signal my ($reference, $pady, $expand) = @_; $reference ||= $old_reference; $expand ||= 0; my $fill = $expand ? 'both' : 'x'; $pady = $frame_pady unless defined($pady); push @row_counter, $$reference->Frame( )->pack(-side=>'top', -anchor=>'nw', -expand=>$expand, -fill=>$fill, -pady=>$pady, -padx=>3); $old_reference = $reference; } sub nc { # New column signal my ($reference, $padx, $pady) = @_; $reference ||= $old_reference; $padx = 4 unless defined($padx); $pady = $frame_pady unless defined($pady); push @row_counter, $$reference->Frame( )->pack(-side=>'left', -anchor=>'nw', -pady=>$pady, -padx=>$padx); $old_reference = $reference; } sub pack_gui { my ($widget_type, $textvariable, $widget_name, @args) = @_; my $top_reference = $row_counter[-1]; $widget_name||='null'; # makes it easy for labels, etc, that don't need balloon messages my $widget_ref = \$packed_widgets{$widget_name}; # Switch on $widget_type ... for ($widget_type) { /^Entry/ && do { my ($width, @widget_args) = @args; $$widget_ref = $top_reference->$widget_type( -textvariable=>$textvariable, -width=>$width, @widget_args, )->pack(-side=>'left'); last; }; /^Label/ && do { my (@widget_args) = @args; my $text_option = ref($textvariable) ? "textvariable" : "text"; $$widget_ref = $top_reference->$widget_type( -$text_option=>$textvariable, @widget_args, )->pack(-side=>'left'); last; }; /^Checkbutton/ && do { my ($variable, @widget_args) = @args; $$widget_ref = $top_reference->$widget_type( -text=>$textvariable, -variable=>$variable, -onvalue=>1, -offvalue=>0, @widget_args, )->pack(-side=>'left', -anchor=>'w', -pady=>0, -ipady=>0); last; }; /^BrowseEntry/ && do { my ($array_ref, $listwidth, @widget_args) = @args; $listwidth||=20; $$widget_ref = $top_reference->$widget_type( -variable=>$textvariable, -choices=>[@$array_ref], -listwidth=>$listwidth*7, -width => $listwidth, @widget_args, )->pack(-side=>'left', -padx=>0, -ipadx=>0); last; }; /^Button/ && do { my ($command, $default, $state, @widget_args) = @args; $state ||= 'normal'; $default ||= 'normal'; $$widget_ref = $top_reference->$widget_type( -text=>$textvariable, -command=>\$command, -state=>$state, -default=>$default, @widget_args, )->pack(-side=>'left', -anchor=>'w', -pady=>$button_pack_pady, -padx=>$button_pack_padx); # bind return to default button if ($default eq 'active') { my $parent = $$old_reference->parent; $parent->bind('' => sub{$$widget_ref->invoke()}); } last; }; /^LabFrame/ && do { my ($frame_reference, @widget_args) = @args; $$widget_ref=$$frame_reference->$widget_type( # -background=>'grey50', -label=>$textvariable, -labelside=>'acrosstop', @widget_args, ); last; }; /Text/ && do { my ($width, $height, @widget_args) = @args; $$widget_ref=$top_reference->Scrolled($widget_type, -width=>$width, -height=>$height, @widget_args, )->pack(-side=>'left', -expand=>1, -fill=>'both'); bind_mousewheel($$widget_ref); last; }; /^HList/ && do { my ($width, $height, $columns, $browse_command, @widget_args) = @args; $$widget_ref=$top_reference->Scrolled($widget_type, -scrollbars=>'osoe', -width=>$width, -height=>$height, -columns=>$columns, -browsecmd=>\&$browse_command, -command=>\&hlist_command, @widget_args, )->pack(-side=>'left', -expand=>1, -fill=>'both'); bind_mousewheel($$widget_ref); last; }; /^Canvas/ && do { my ($buttons, $bindings, @widget_args) = @args; canvas_buttons($top_reference, $widget_name) if $buttons; $$widget_ref=$top_reference->$widget_type( -height=>$dna_canvas_height, @widget_args, )->pack(-side=>'left', -expand=>1, -fill=>'x', -padx=>6, -pady=>2); bind_canvas($widget_ref) if $bindings; last; }; /^Radio/ && do { my (@widget_args) = @args; $$widget_ref=$top_reference->$widget_type( -text=>$textvariable, @widget_args, )->pack(-side=>'left', -anchor=>'w'); last; }; # Warning - an undefined widget type ... print "Undefined widget: @_\n"; } #--------------# # Balloon help # #--------------# # This is a very easy way to attach balloons to widgets, while keeping all the help messages together # (in the %balloonmsg hash above) if ($balloonmsg{$widget_name}) { $Balloon->attach($$widget_ref, -balloonposition => 'mouse', -balloonmsg => $balloonmsg{$widget_name}); } } sub pack_grid { my @widgets = map {/[\-\+\^]/ ? $_ : $packed_widgets{$_}} @_; Tk::grid(@widgets, -sticky=>'nsew', -padx=>4); } sub pack_button { my ($widget, $image, $sub_ref) = @_; return $widget->Button( -relief => 'flat', -image => $image, -command=> $sub_ref, -activebackground=>$activebackground_color, ) if $os eq 'win'; return $widget->Button( -relief => 'flat', -image => $image, -command=> $sub_ref, )->pack(-side=>'left'); } sub pack_osc_buttons { #open/save/clear button stuff ... my ($widget_name) = @_; $packed_widgets{"$widget_name b"} = $row_counter[-1]->Frame()->pack(-side=>'left', -anchor=>'nw'); $packed_widgets{"$widget_name open"}=pack_button($packed_widgets{"$widget_name b"}, $top->Pixmap(-data => $icon_dna_open), [\&open_seq, \$packed_widgets{$widget_name}]) ->pack(-side=>'top', -anchor=>'w', -fill=>'x'); $Balloon->attach($packed_widgets{"$widget_name open"}, -balloonposition => 'mouse', -balloonmsg => "Open DNA sequence"); $packed_widgets{"$widget_name save"}=pack_button($packed_widgets{"$widget_name b"}, $top->Pixmap(-data => $icon_dna_save), [\&save_seq, \$packed_widgets{$widget_name}]) ->pack(-side=>'top', -anchor=>'w', -fill=>'x'); $Balloon->attach($packed_widgets{"$widget_name save"}, -balloonposition => 'mouse', -balloonmsg => "Save DNA sequence"); $packed_widgets{"$widget_name clear"}=pack_button($packed_widgets{"$widget_name b"}, $top->Pixmap(-data => $icon_clear), [\&clear_text, \$packed_widgets{$widget_name}]) ->pack(-side=>'top', -anchor=>'w', -fill=>'x'); $Balloon->attach($packed_widgets{"$widget_name clear"}, -balloonposition => 'mouse', -balloonmsg => "Clear sequence"); } sub pack_menu { my $name = shift; $packed_widgets{$name}->menu(undef); $packed_widgets{$name}->bind('<3>', \&menu_text); } sub canvas_buttons { my ($widget_ref, $widget_name) = @_; $packed_widgets{"$widget_name info"}=pack_button($widget_ref, $top->Pixmap(-data => $icon_info), \&canvas_info) ->pack(-side=>'right', -expand=>0, -anchor=>'e', -fill=>'none'); $Balloon->attach($packed_widgets{"$widget_name info"}, -balloonposition => 'mouse', -balloonmsg => "Graphical display help"); $packed_widgets{"$widget_name magnify"}=pack_button($widget_ref, $top->Pixmap(-data => $icon_magnify), [\&dna_magnify, 0]) ->pack(-side=>'right', -expand=>0, -anchor=>'e', -fill=>'none'); $Balloon->attach($packed_widgets{"$widget_name magnify"}, -balloonposition => 'mouse', -balloonmsg => "Magnified view"); } sub bind_canvas { # Bindings for DNA canvas my ($widget_ref) = @_; $$widget_ref->CanvasBind('' => sub {items_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('' => sub {amplicon_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('<1>' => sub {items_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('<2>' => sub {amplicon_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('' => sub {amplicon_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('' => sub {amplicon_drag($Tk::event->x, $widget_ref)}); $$widget_ref->CanvasBind('<3>' => sub {dna_magnify($Tk::event->x)}); $$widget_ref->CanvasBind('', \&draw_dna); $$widget_ref->CanvasBind('' => \&draw_dna); } #---------------------# # MouseWheel bindings # #---------------------# sub bind_mousewheel { # Thanks to Slaven Rezic, Steve Liddie and "Mastering Perl/Tk" for this routine (slightly modified) ... my ($w) = @_; if ($os eq 'win') { # Windows bindings # Apparently mousewheel support is now built in .... ?! # $w->bind('' => [ sub { # $_[0]->yview('scroll', -($_[1] / 120) * $scroll_factor, 'units') # }, Ev('D') ] # ); } else { # *nix bindings $w->bind('<4>' => sub { $_[0]->yview('scroll', -$scroll_factor, 'units'); }); $w->bind('<5>' => sub { $_[0]->yview('scroll', $scroll_factor, 'units'); }); } } sub update_title { my $nb_page = which_nb_page(); if ($open_file{$nb_page}) { if ($open_file{$nb_page} eq 'File not saved') { $top->configure(-title=>"PerlPrimer v$version"); return; } else { my $file = $open_file{$nb_page}; $top->configure(-title=>"PerlPrimer v$version - $file"); } } return; } #-------------------# # Read DNA sequence # #-------------------# sub read_windows { my ($sub_ref, $dnaseq_f, $dnaseq_f_len, $dnaseq_r) = @_; $reverse=0; sbarprint("\nWindowing forward seq ..."); @PF = &$sub_ref($dnaseq_f, $dnaseq_f_len); $pfkeys=@PF; if ($dnaseq_r) { $reverse=1; sbarprint("\nWindowing reverse seq ..."); @PR = &$sub_ref($dnaseq_r, $dnaseq_f_len); } } #-------------------------------------------------# # basic complementation and bisulphite conversion # #-------------------------------------------------# sub complement { $_ = shift; tr/AGCTagct/TCGAtcga/; return $_; } sub bisulphite { $_ = shift; tr/C/T/; return $_; } #----------------------------# # primer window calculations # #----------------------------# sub primer_window { my ($primer_seq, $dnaseq_len) = @_; my $key; my @primer_list=(); my $repeat_real = $repeat-1; my ($min_tm, $max_tm, $max_diff, $pri_win_min, $pri_win_max, $min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_tm max_tm max_diff pri_win_win pri_win_max min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); # old method - using amplicon range only ... my ($seqbound5,$seqbound3)=($$min_range,$$max_range); $seqbound5 ||= 0; $seqbound5 = $$max_range-$$max_ampsize if ($$max_range) && ($$max_ampsize) && ($reverse==0) && ($$max_range-$$max_ampsize>0); $seqbound5 = $dnaseq_len-$$min_range-$$max_ampsize if ($$min_range) && ($reverse==1) && ($dnaseq_len-$$min_range-$$max_ampsize > 0); if (($$max_ampsize) && ($$min_ampsize)) { if ($reverse==0) { unless ($$min_range) { $seqbound3 = $dnaseq_len-$$min_ampsize; } else { $seqbound3 = ($dnaseq_len-$$min_ampsize>$$min_range ? $$min_range : $dnaseq_len-$$min_ampsize); } } else { $seqbound3 = ($$max_range ? $dnaseq_len-$$max_range+$$pri_win_max : $dnaseq_len) } } # my $i; # foreach (@intron_exon_bounds) { # print $i++," $_\n"; # } # print "[$reverse] bounds: $seqbound5 - $seqbound3\n"; # QPCR specific (limiting range to specific exons) if ($ie_limit) { unless ($reverse) { if ($ie_limit_5p) { my $limit = $ie_limit_5p-2; if ($limit>=0) { $seqbound5 = $intron_exon_bounds[$limit]-$$pri_win_max; } } if ($ie_limit_3p) { my $limit = $ie_limit_3p-1; $limit = 0 if $limit < 0; if (defined($limit)) { unless ($limit > $#intron_exon_bounds) { $seqbound3 = $intron_exon_bounds[$limit]+$$pri_win_max; } } } } else { # reverse sequence if ($ie_limit_3p) { #### in progress my $limit = $ie_limit_3p-1; $limit = 0 if $limit < 0; if (defined($limit)) { unless ($limit > $#intron_exon_bounds) { $seqbound5 = $dnaseq_len - ($intron_exon_bounds[$limit]+$$pri_win_max); } } } if ($ie_limit_5p) { my $limit = $ie_limit_5p-2; if ($limit>=0) { $seqbound3 = $dnaseq_len - ($intron_exon_bounds[$limit]-$$pri_win_max); } } } } # print "[$reverse] bounds: $seqbound5 - $seqbound3\n"; # the above covers the unlikely possibility that $max_ampsize is set # and $max_range_5p and $max_range_3p are not. It might be desirable at some # point, and it was the original way of doing things. It doesn't hurt! # ... and is now used for qpcr! # To use without specific range boundaries but only with a minimum and # maximum amplicon distance, keep $max_range_5p and $min_range equal, # and likewise at the 3' end if (defined($$max_range_5p)&&defined($$max_range_3p)) { unless (($$max_range_5p==$$min_range)&&($$max_range_3p==$$max_range)) { if ($reverse==0) { $seqbound5 = $$max_range_5p; $seqbound3 = $$min_range + $$pri_win_max; } if ($reverse==1) { $seqbound5 = $dnaseq_len-$$max_range_3p-1; $seqbound3 = $dnaseq_len-$$max_range + $$pri_win_max; } } } return unless defined($seqbound5) && defined($seqbound3); for my $i ($seqbound5 .. $seqbound3) { for my $primer_win ($$pri_win_min .. $$pri_win_max) { next if $i+$primer_win>$dnaseq_len; my $currwindow=substr($primer_seq, $i, $primer_win); next if check_degenerate($currwindow); if ($exclude_gc) { # exclude based on %GC content my $gc=gc($currwindow); next if $gc < $min_gc or $gc > $max_gc; } if ($exclude_clamp) { if ($onodera_clamp) { # Onodera and Melcher exclusion next unless onodera(substr($currwindow, -3, 3)); } else { # calculate GC clamp at 5' end: two out of last three residues required... my $gc_clamp=gc(substr($currwindow, -3, 3)); next unless $gc_clamp > 50; } } if ($exclude_rr) { # simple run exclusion: # ($run is number of consecutive bases to exclude) next if ($currwindow =~ /(C{$run,}|A{$run,}|G{$run,}|T{$run,})/); # simple repeat exclusion: # ($repeat is number of consecutive repeats of two or more bases) next if ($currwindow =~ /(.{2,})\1{$repeat_real,}/); } # All possible primers have now been excluded # Get the tm and check this against the accepted range: my ($tm)=tm($currwindow); if (($$min_tm < $tm) && ($tm < $$max_tm)) { # Looks good, so let's add the primer to the array push @primer_list, [$i, $primer_win, $currwindow, $tm]; } } } return @primer_list; } sub check_degenerate { $_ = shift; if (/[^ATGC]/i) { dialogue("One of your primer sequences has a degenerate or non-DNA character. PerlPrimer cannot calculate the Tm of degenerate sequences") if shift; return 1; } } #---------------------------------# # bisulphite window calculations # #---------------------------------# sub bisul_window { # See the primer_window subroutine above for general comments my ($primer_seq, $dnaseq_len)=@_; my $key; my $repeat_bs_real = $repeat_bs-1; my ($seqbound5,$seqbound3) = 0; my @primer_list=(); my ($min_tm, $max_tm, $max_diff, $pri_win_min, $pri_win_max, $min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_tm max_tm max_diff pri_win_win pri_win_max min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); $seqbound5 = 0; $seqbound5 = $$max_ampsize-$$max_range if ($$max_range) && ($reverse==0) && ($$max_ampsize-$$max_range>0); $seqbound5 = $dnaseq_len-$$min_range-$$max_ampsize if ($$min_range) && ($reverse==1) && ($dnaseq_len-$$min_range-$$max_ampsize > 0); if (defined($$max_range_5p)&&defined($$max_range_3p)) { unless (($$max_range_5p==$$min_range)&&($$max_range_3p==$$max_range)) { if ($reverse==0) { $seqbound5 = $$max_range_5p; $seqbound3 = $$min_range + $$pri_win_max; } if ($reverse==1) { $seqbound5 = $dnaseq_len-$$max_range_3p-1; $seqbound3 = $dnaseq_len-$$max_range + $$pri_win_max; } } } else { if ($reverse==0) { unless ($$min_range) { $seqbound3 = $dnaseq_len-$$min_ampsize; } else { $seqbound3 = ($dnaseq_len-$$min_ampsize>$$min_range ? $$min_range : $dnaseq_len-$$min_ampsize); } } else { $seqbound3 = ($$max_range ? $dnaseq_len-$$max_range+$$pri_win_max : $dnaseq_len) } } return unless defined($seqbound5) && defined($seqbound3); my $count; for my $i ($seqbound5 .. $seqbound3) { for my $primer_win ($$pri_win_min .. $$pri_win_max) { $count++; next if $i+$primer_win>$dnaseq_len; my $currwindow=substr($primer_seq, $i, $primer_win); next if check_degenerate($currwindow); # Check for %C content next if cc($currwindow) < $bisul_min_c; #next if ga($currwindow) < 50; # Check for exclude_cpg # If exclude_cpg is set, no primers with CpG residues are include # Otherwise, primers are allowed to have up to a maximum of 2 CpG residues if ($exclude_cpg) { next if $currwindow =~ /CG/ } else { my $count_cpg = 0; while ($currwindow =~ /CG/g) { $count_cpg++ } next if $count_cpg >2; } # Check for 3' C content if ($exclude_3c) { # It is important that primer specificity to the converted template is given # by having C residues at the 3' end # The following gives the most flexibility: a primer is accepted if # it either ends in a C or has two of the last three bases as C's next unless ((substr($currwindow, -1, 1) eq "C") or (cc(substr($currwindow, -3, 3)) > 50)); } # now convert the primer ... my $currwindowbs = bisulphite($currwindow); # do we exclude repeats and runs before or after bisulphite conversion? my $checkref = ($pre_bs ? \$currwindowbs : \$currwindow); if ($exclude_rr_bs) { # Run exclusion: next if ($$checkref =~ /(C{$run_bs,}|A{$run_bs,}|G{$run_bs,}|T{$run_bs,})/); # Repeat exclusion: next if ($$checkref =~ /(.{2,})\1{$repeat_bs_real,}/); } # Need to complement if we're creating reverse primers, # as the two converted strands of DNA are non-complementary if ($reverse==1) { $currwindowbs=complement($currwindowbs); } my ($tm)=tm($currwindowbs); if (($$min_tm < $tm) && ($tm < $$max_tm)) { # Add the original, unconverted sequence to the array as well: # Note - this will not be complemented if reversed seq push @primer_list, [$i, $primer_win, $currwindowbs, $tm, $currwindow]; } } } return @primer_list; } #-----------------------------------------# # Calculation of amplicon lengths and # # compatible temperature matching primers # #-----------------------------------------# sub calc_amplicon { my $dnaseq_r_len = shift; my $count_amplicon=0; @primer_pairs=(); my $count=0; my ($min_tm, $max_tm, $max_diff, $pri_win_min, $pri_win_max, $min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_tm max_tm max_diff pri_win_win pri_win_max min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); # (the reverse sequence was reversed to make it 5'->3') # So I either have to recalculate the revese numbers or # not reverse the sequence and count backwards! # # Solution: # # realpos_start = total_length - calc_pos # and closest_real_base = realpos_start - window # thus = total_length - calc_pos - window foreach my $key_f (@PF) { return if ($cancel==1); my ($pos_f, $len_f, $seq_f, $tm_f, $unconverted_f ) = @$key_f; foreach my $key_r (@PR) { return if ($cancel==1); my ($pos_r, $len_r, $seq_r, $tm_r, $unconverted_r ) = @$key_r; my $realpos_r = $dnaseq_r_len - $pos_r - 1; my $amp_size=$realpos_r-$pos_f; # Skip all pairs where forward > reverse ... next unless ($realpos_r-$len_r>$pos_f+$len_f); # Range check (messy): # Now should be covered in windowing! but need for qpcr if (($$min_range) && ($$max_range)) { next unless ($pos_f < $$min_range && $$max_range < $realpos_r); } # Temperature check: ### could be more efficient next unless ($tm_r > ($tm_f-$$max_diff) && $tm_f > ($tm_r-$$max_diff)); # QPCR specific: if ($qpcr_flag == 1) { # Amplicon size check next if ($amp_size < $$min_ampsize || $amp_size > $$max_ampsize); #check amp range spans i/e boundary my $qpcr_check=0; if ($ie_span) { foreach my $i (@intron_exon_bounds) { $qpcr_check=1 if $pos_f<$i && $i<$realpos_r } next unless $qpcr_check==1; } #check at least one primer falls across i/e boundary $qpcr_check=0; if (($ie_overlap) && ($exclude_ie)) { foreach my $i (@intron_exon_bounds) { $qpcr_check=1 if $pos_f<($i-$exclude_ie) && ($i+$exclude_ie)<($pos_f+$len_f); $qpcr_check=1 if ($realpos_r-$len_r)<($i-$exclude_ie) && ($i+$exclude_ie)<$realpos_r; } next unless $qpcr_check==1; } } # all OK up to here, so let's check primer-dimers: # NB: this is a big speed hit ... # my (@pd_score, @pd_sorted)=(); # # push(@pd_score,primer_dimer($seq_f,$seq_f)); # push(@pd_score,primer_dimer($seq_f,$seq_r)); # push(@pd_score,primer_dimer($seq_r,$seq_r)); # @pd_sorted=sort{$a <=> $b} @pd_score; # extensible my $pd_score = primer_dimer($seq_f,$seq_f); my $new_score = primer_dimer($seq_f,$seq_r); $pd_score = $new_score if $new_score < $pd_score; $new_score = primer_dimer($seq_r,$seq_r); $pd_score = $new_score if $new_score < $pd_score; # non_extensible my $pd_score_full = primer_dimer($seq_f,$seq_f,1); $new_score = primer_dimer($seq_f,$seq_r,1); $pd_score_full = $new_score if $new_score < $pd_score_full; $new_score = primer_dimer($seq_r,$seq_r,1); $pd_score_full = $new_score if $new_score < $pd_score_full; $tm_f = sprintf("%.2f", $tm_f); $tm_r = sprintf("%.2f", $tm_r); push @primer_pairs, [ $seq_f, $pos_f, $len_f, $tm_f, $seq_r, $pos_r, $len_r, $tm_r, $realpos_r, $amp_size, $pd_score, ( $unconverted_f ? $unconverted_f : 0), ( $unconverted_r ? $unconverted_r : 0), $pd_score_full ]; } $count++; my $percent = sprintf("%.f", ($count/$pfkeys)*100); sbarprint("\nCalculating amplicons ... $percent% completed ..."); } sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); } #-----------------------------------------# # Calculation of amplicon lengths and # # compatible temperature matching primers # #-----------------------------------------# sub calc_seq_primers { # scaled down calc_amplicon routine my $count_amplicon=0; @primer_pairs=(); my $count=0; my $pdcheck=1; my ($min_range, $max_range) = get_variables(qw(min_range max_range)); my $last_seq; # $pd_full = 1; # $pd_extensible = 0; foreach (@PF) { return if ($cancel==1); $count++; my $percent = sprintf("%.f", ($count/$pfkeys)*100); sbarprint("\nFinding primers ... $percent% completed ..."); my ($pos, $len, $seq, $tm_f ) = @$_; # take the first one that fits if last_seq hasn't been set ... unless (defined($last_seq)) { next if $pos < $$min_range; if ($pos > $seq_spacing_max+$$min_range) { dialogue("Contiguous primers could not be found within the set parameters"); last; } } else { next if $pos > $$max_range; next if $pos < $last_seq + $seq_spacing_min; if ($pos > $last_seq + $seq_spacing_max) { dialogue("Contiguous primers could not be found within the set parameters"); last; } } # calculate primer-dimers my $pd = primer_dimer($seq,$seq,1); # exclude all primer-dimers if asked ... if ($exclude_pd_seq == 1) { next if $pd < -$seq_pd_min; } $tm_f = sprintf("%.2f", $tm_f); push @primer_pairs, [ $seq, $pos, $len, $tm_f, $pd ]; $last_seq = $pos; } # $pd_full = 0; # $pd_extensible = 1; sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); } # sub calc_seq_primers { # # scaled down calc_amplicon routine # my $count_amplicon=0; # @primer_pairs=(); # my $count=0; # # my $pdcheck=1; # # my ($min_range, $max_range) # = get_variables(qw(min_range max_range)); # # my $last_seq; # my $seq_spacing_mid = int(($seq_spacing_max+$seq_spacing_min)/2); # # $pd_full = 1; # # $pd_extensible = 0; # # # build hash table # my $i; # my $flag=0; # my $skip; # my $interval=1; # my %seq_primers; # foreach (@PF) { # $i++; # my ($pos) = @$_; # $seq_primers{$pos} = $i; # } # # my $j; # for ($j=$$min_range; $j < $$max_range;) { # my ($pos, $len, $seq, $tm_f); # return if ($cancel==1); # # if ($skip) { # $flag = 1-$flag; # if ($flag) { # $j+=$interval; # } else { # $j-=$interval; # $interval++; # } # $skip=0; # } # # $count++; # # my $percent = sprintf("%.f", ($count/$pfkeys)*100); # sbarprint("\nFinding primers ... $count primers checked ..."); # # # # take the first one that fits if last_seq hasn't been set ... # unless (defined($last_seq)) { # if ($seq_primers{$j}) { # ($pos, $len, $seq, $tm_f ) = @{ $PF[$seq_primers{$j}] }; # } else { # # we want the first primer to be as near to the start as possible # print "$j\n"; # $j++; # next; # } # print "$j: OK! pos was $pos\n"; # # next if $pos < $$min_range; # if ($pos > $seq_spacing_max+$$min_range) { # dialogue("Contiguous primers could not be found within the set parameters"); # last; # } # } else { # if ($seq_primers{$j}) { # ($pos, $len, $seq, $tm_f ) = @{ $PF[$seq_primers{$j}] }; # } else { # $skip=1; # next; # } # if ($pos > $$max_range) { # $skip=1; # next; # } # if ($pos < $last_seq + $seq_spacing_min) { # $skip=1; # next; # } # if ($pos > $last_seq + $seq_spacing_max) { # dialogue("Contiguous primers could not be found within the set parameters"); # last; # } # } # # # exclude all primer-dimers if asked ... # my $pd; # if ($exclude_pd_seq == 1) { # if ($pd = primer_dimer($seq,$seq) < -$seq_pd_min) { # $skip=1; # next; # } # } # # $tm_f = sprintf("%.2f", $tm_f); # # push @primer_pairs, [ $seq, $pos, $len, $tm_f, $pd ]; # $interval = 1; # $last_seq = $pos; # $j = $pos + $seq_spacing_mid; # } # # # $pd_extensible = 1; # sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); # } #-------------------------# # calculate %base content # #-------------------------# # %GC sub gc { $_ = $_[0]; my($gc,$countgc,$counttotal); $gc=0; $countgc=0; $countgc = tr/GCgc/GCgc/; $counttotal = length(); $gc = $countgc/$counttotal*100; return $gc; } # %C sub cc { $_ = $_[0]; my($cc,$countc,$counttotal); $cc=0; $countc=0; $countc = tr/C/C/; $counttotal = length(); $cc = $countc/$counttotal*100; return $cc; } # Onodera and Melcher exclusion sub onodera { $_ = shift; return 1 if (m/[G|C][A|T][G|C]/ || m/[A|T][G|C][G|C]/ && !(m/[A|T]CG/)); } #----------------# # Clean sequence # #----------------# sub clean_seq { $_ = shift; my $nb_page = which_nb_page(); my ($fasta_name) = /^\s*>(.*\n)/; s/^\s*>(.*\n)//g; #remove FASTA formatting if it exists # print "name = $fasta_name\n"; if ($fasta_name && $open_file{$nb_page} eq "File not saved") { # if FASTA details are present ... my $name = format_file_name($1); $open_file{$nb_page} = $name; $top->configure(-title=>"PerlPrimer v$version - $name"); } s/[\s\n\r]//g; #remove spaces/new lines s/-//g; #remove gaps in sequence s/\d//g; #remove digits return $_; } #-------------------------------# # forward and reverse sequences # #-------------------------------# sub getseq { my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); if (($$min_range) && ($$max_range)) { $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range - $$min_range if ($$max_range - $$min_range)>$$max_ampsize; } # forward $_ = uc(shift); $_ = clean_seq($_); my $dnaseq_f = $_; # reverse my $dnaseq_r_top = reverse($dnaseq_f); my $dnaseq_r = complement($dnaseq_r_top); my $dnaseq_f_len = length($dnaseq_f); my $dnaseq_r_len = length($dnaseq_r); return ($dnaseq_f, $dnaseq_f_len, $dnaseq_r_top, $dnaseq_r, $dnaseq_r_len); } #----- # subroutine for calculating Tm as per SantaLucia(1998), cited above, with Mg++ # (and K+, Tris++) concentration calculated via equations 7 and 8 from # von Ahsen, et al, 2001, Clinical Chemistry 47(11):1956-1961 #----- sub tm { my ($primer) = @_; $primer = uc($primer); # if user enters primer directly as lower-case my ($i, $nn, $initterm, $endterm); my $primer_len = length($primer); my ($deltaH, $deltaS); #-----------------------------# # calculate deltaH and deltaS # #-----------------------------# for ($i=0; $i<$primer_len-1; $i++) { $nn = substr($primer, $i, 2); $deltaH+= $oligo_dH{$nn}; $deltaS+= $oligo_dS{$nn}; } #-------------------------# # initial term correction # #-------------------------# $initterm="init" . substr($primer, 0, 1); $deltaH+= $oligo_dH{$initterm}; $deltaS+= $oligo_dS{$initterm}; $endterm="init" . substr($primer, -1, 1); $deltaH+= $oligo_dH{$endterm}; $deltaS+= $oligo_dS{$endterm}; # Tm at 1M NaCl # $tm= ($deltaH * 1000) / ($deltaS + (1.987 * log($oligo_conc / 4))) - 273.15; #------------------------------------------# # correct for salt concentration on deltaS # #------------------------------------------# # Big problems if [dNTPs] > [Mg++] !! This is a quick fix ... my $salt_correction; if ($mg_conc > $dntp_conc) { $salt_correction = sqrt($mg_conc - $dntp_conc); } else { $salt_correction = 0; } my $na_eq=($monovalent_cation_conc + 120 * $salt_correction)/1000; # deltaS correction: $deltaS += (0.368 * ($primer_len - 1) * log($na_eq)); my $oligo_conc_mols = $oligo_conc / 1000000000; # Salt corrected Tm # NB - for PCR I'm assuming for the moment that the [strand target] << [oligo] # and that therefore the C(t) correction term approx equals [oligo] my $corrected_tm=(($deltaH * 1000) / ($deltaS + (1.987 * log($oligo_conc_mols/4)))) - 273.15; return ($corrected_tm, $deltaH, $deltaS); } #----------------------------------------------------------# # Recalculate the oligo_dG hash on current salt conditions # #----------------------------------------------------------# sub recalculate_dG { # because dG = dH - TdS, and dS is dependent on the salt concentration ... my $temperature = shift || $pd_temperature; # Big problems if [dNTPs] > [Mg++] !! This is a quick fix ... my $salt_correction; if ($mg_conc > $dntp_conc) { $salt_correction = sqrt($mg_conc - $dntp_conc); } else { $salt_correction = 0; } my $na_eq=($monovalent_cation_conc + 120 * $salt_correction)/1000; # the length of each NN dimer is 2, therefore the modifier is 1 my $entropy_adjust = (0.368 * log($na_eq)); foreach my $key (keys(%oligo_dH_full)) { next if $key =~ /init/; # the length of each monomer is 1, thus the modifier of dS is 0 and the values are precalulated my $dS = $oligo_dS_full{$key} + $entropy_adjust; my $dG = $oligo_dH_full{$key}-((273.15+$temperature)*($dS/1000)); $oligo_dG{$key} = $dG; } } #--------------------------# # Primer-dimer calculation # #--------------------------# sub primer_dimer { # This is my second attempt at implementing a primer-dimer system: # The first attempt aligned each combination together explicitly; this attempt # creates a binding matrix and then reads each primer combination from the # matrix. It's not significantly faster, but it does have the advantage of # extending this algorithm to allow for unequal loops (although this has not # been implemented as yet) and providing the possiblity of reading all # primer-dimers (not just 3') by adjusting a single variable (which is used when # displaying primer information. my ($primer_f, $primer_r, $pd_full) = @_; return unless ($primer_f) && ($primer_r); my ($k, $l); @score=(); %primer_hash=(); @score_sort=(); @bind_string=(); %rating_hash=(); # $pl = greatest length $pfl=length($primer_f); $prl=length($primer_r); $pl = ($pfl>$prl ? $pfl : $prl); my $rcompr = reverse(complement($primer_r)); my $rcomprlc = lc($rcompr); my $fprimer_r=lc(reverse($primer_f)); $rprimer_r=reverse($primer_r); # Scan the primers against each other: # The default is to only consider 3' primer-dimers, for speed concerns (5' # pd's will reduce the primer population, but won't cause extendible dimers) - # however, setting $pd_full to 1 will calculate all primer-dimers. This is # currently used only when viewing individual primers, for both speed concerns # and because it's 3' primer-dimers that are the real problem in PCR. # create a binding array for each of the four bases foreach $l (0 .. $pfl-1) { my $mbase = substr($fprimer_r, $l, 1); $primer_hash{$mbase}[$l]=1; my @nucleotides = qw(a g c t); foreach $k (@nucleotides) { $primer_hash{$k}[$l] ||=0; } } # create the primer matrix my @primer_comp; foreach $k (0 .. $prl-1) { $primer_comp[$k]=$primer_hash{substr($rcomprlc, $k, 1)}; } # read each combination from the matrix, calculate dG for each dimer my $pd_len = ($pd_full ? $pfl+$prl-1 : $pl-2); foreach $k (0 .. $pd_len) { $score[$k]=0; my $bind; my $score_p=0; # extensible primer short-circuit - ignore all primers that will # not create extensible (i.e. amplifiable) dimers my $start = $k>$pfl-1 ? $pfl-1 : $k; my $end = $k>$prl-1 ? $prl-1 : $k; if ($pd_extensible && !$pd_full) { next unless $primer_comp[0][$start] == 1; next unless $primer_comp[$end][$start-$k] == 1; } # elsif ($pd_extensible) { # # no point reconsidering them! # next if $primer_comp[0][$start] == 1 && $primer_comp[$end][$start-$k] == 1; # } # read the binding data foreach $l (0 .. $prl-1) { if (($k-$l)<$pfl) { $bind .= $primer_comp[$l][$k-$l] if ($k-$l)>=0; } else { # spacer $bind .= "2"; } } # Single matched bases surrounded by mismatches are unstable, # so we remove them with the regexp (look ahead is needed otherwise # strings of consecutive match/mismatches are not caught) $bind =~ s/01(?=[^1])/00/gx; # Short circuit if there's nothing to bind next unless $bind =~ /[1]/; # Find start and end of similarity my ($pb_init,$pb_end); foreach $l (0 .. length($bind)-1) { # at first I tried finding the initiating terminal bases with # regexps, but that was much slower ... if (substr($bind, $l, 1) eq "1") { defined($pb_init) || ($pb_init = $l); $pb_end=$l; } } if (defined($pb_init)) { # deltaG calculation foreach $l ($pb_init .. $pb_end-1) { next if substr($bind, $l, 2) eq "00"; next if substr($bind, $l, 1) eq "2"; $score_p+=$oligo_dG{substr($primer_f, $pfl-$k+$l-1, 2).substr($rprimer_r, $l, 2)}; } # init term corrections my $initterm="init" . substr($rprimer_r, $pb_init, 1); $score_p+= $oligo_dG{$initterm}; my $endterm="init" . substr($rprimer_r, $pb_end, 1); $score_p+= $oligo_dG{$endterm}; # add to the hash ... $score[$k]=sprintf("%.2f",$score_p); $bind_string[$k]=$bind; $rating_hash{$score[$k]}=$k; } } # sort the dimers to give the most stable: @score_sort = sort { $a <=> $b } @score; # Returns the most stable dimer return $score_sort[0]; } sub primer_dimer_new { # This is my second attempt at implementing a primer-dimer system: # The first attempt aligned each combination together explicitly; this attempt # creates a binding matrix and then reads each primer combination from the # matrix. It's not significantly faster, but it does have the advantage of # extending this algorithm to allow for unequal loops (although this has not # been implemented as yet) and providing the possiblity of reading all # primer-dimers (not just 3') by adjusting a single variable (which is used when # displaying primer information. ### TODO: # It is easily possible to calculate *all* primers when searching for primer # pairs ... I really should add a preferences option to do this ... my ($primer_f, $primer_r, $pd_full) = @_; return unless ($primer_f) && ($primer_r); my ($k, $l); @score=(); %primer_hash=(); @score_sort=(); @bind_string=(); %rating_hash=(); # $pl = greatest length $pfl=length($primer_f); $prl=length($primer_r); $pl = ($pfl>$prl ? $pfl : $prl); my $rcompr = reverse(complement($primer_r)); my $rcomprlc = lc($rcompr); my $fprimer_r=lc(reverse($primer_f)); $rprimer_r=reverse($primer_r); # Scan the primers against each other: # The default is to only consider 3' primer-dimers, for speed concerns (5' # pd's will reduce the primer population, but won't cause extendible dimers) - # however, setting $pd_full to 1 will calculate all primer-dimers. This is # currently used only when viewing individual primers, for both speed concerns # and because it's 3' primer-dimers that are the real problem in PCR. # create a binding array for each of the four bases foreach $l (0 .. $pfl-1) { my $mbase = substr($fprimer_r, $l, 1); $primer_hash{$mbase}[$l]=1; foreach $k (qw(a g c t)) { $primer_hash{$k}[$l] ||=0; } } # create the primer matrix my @primer_comp; foreach my $kk (0 .. $prl-1) { $primer_comp[$kk]=$primer_hash{substr($rcomprlc, $kk, 1)}; } # print the matrix - for debugging print "$primer_f vs. $primer_r - full pd = $pd_full\n"; print " \t"; foreach $l (0 .. $pfl-1) { my $mbase = substr($fprimer_r, $l, 1); print "$mbase "; } print "\n"; foreach my $kk (0 .. $prl-1) { my $base = substr($rprimer_r, $kk, 1); print "$base:\t@{$primer_comp[$kk]}\n"; } my @binding_data; # read each combination from the matrix, calculate dG for each dimer my $pd_len = ($pd_full ? $pfl+$prl-1 : $pl-2); foreach my $kk (0 .. $pd_len) { $score[$kk]=0; my $bind; my $score_p=0; # starting coords my $pf_coord_start = ($kk >= $pfl ? $pfl-1 : $kk); my $pr_coord_start = ($kk - $pfl > 0 ? $kk - $pfl : 0); my ($pf_coord, $pr_coord, $first, $flag); # read through each combination finding multiple matches for ($pf_coord = $pf_coord_start, $pr_coord = $pr_coord_start; $pf_coord>=0 && $pr_coord<$prl; $pf_coord--,$pr_coord++) { # read base print "error: \[$pr_coord\] \[$pf_coord\]\n\n" unless defined($primer_comp[$pr_coord][$pf_coord]); if ($primer_comp[$pr_coord][$pf_coord]==1) { # binding bases $bind++; if ($flag) { next; } else { $first=$pf_coord; $flag=1; } } elsif ($flag) { # end of a binding stretch push @binding_data, [$kk, $first, $bind] if $bind > 1; $bind=0; $flag=0; } # set up for next loop # $pf_coord--; # $pr_coord++; # last if $pf_coord<0 || $pr_coord>$prl; # redo; } } # check for best binding for each possibility that yeilds two or more matched bases together my $last; my @unequal_loops; foreach my $i (@binding_data) { # skip multiple matches # next if @$i[0] == $last; # # $last = @$i[0]; my $pf_coord = @$i[1]; my $length = @$i[2]; print "@$i\n"; } # # extensible primer short-circuit - ignore all primers that will # # not create extensible (i.e. amplifiable) dimers # # my $start = $k>$pfl-1 ? $pfl-1 : $k; # # my $end = $k>$prl-1 ? $prl-1 : $k; # # if ($pd_extensible && !$pd_full) { # # next unless $primer_comp[0][$start] == 1; # # next unless $primer_comp[$end][$start-$k] == 1; # # } # # # read the binding data # foreach $l (0 .. $prl-1) { # if (($k-$l)<$pfl) { # $bind .= $primer_comp[$l][$k-$l] if ($k-$l)>=0; # } else { # # spacer # $bind .= "2"; # } # } # # # Single matched bases surrounded by mismatches are unstable, # # so we remove them with the regexp (look ahead is needed otherwise # # strings of consecutive match/mismatches are not caught) # $bind =~ s/01(?=[^1])/00/gx; # # # Short circuit if there's nothing to bind # next unless $bind =~ /[1]/; # # # Find start and end of similarity # my ($pb_init,$pb_end); # foreach $l (0 .. length($bind)-1) { # # at first I tried finding the initiating terminal bases with # # regexps, but that was much slower ... # if (substr($bind, $l, 1) eq "1") { # defined($pb_init) || ($pb_init = $l); # $pb_end=$l; # } # } # # if (defined($pb_init)) { # # deltaG calculation # foreach $l ($pb_init .. $pb_end-1) { # next if substr($bind, $l, 2) eq "00"; # next if substr($bind, $l, 1) eq "2"; # $score_p+=$oligo_dG{substr($primer_f, $pfl-$k+$l-1, 2).substr($rprimer_r, $l, 2)}; # } # # # init term corrections # my $initterm="init" . substr($rprimer_r, $pb_init, 1); # $score_p+= $oligo_dG{$initterm}; # # my $endterm="init" . substr($rprimer_r, $pb_end, 1); # $score_p+= $oligo_dG{$endterm}; # # # add to the hash ... # $score[$k]=sprintf("%.2f",$score_p); # $bind_string[$k]=$bind; # $rating_hash{$score[$k]}=$k; # } # } # # # sort the dimers to give the most stable: # @score_sort = sort { $a <=> $b } @score; # # # Returns the most stable dimer # return $score_sort[0]; } #--------------------------------# # Rountine to draw primer-dimers # #--------------------------------# sub draw_dimer { # This all seems a bit cumbersome!! my ($primer_f, $primer_r, $pos, $FH) = @_; my $rprimer_r=reverse($primer_r); my $dimer_binding=""; my $pr_space=""; my $fspace=""; my $rspace=""; my $fspace_def = $pl-$pfl>0 ? $pl-$pfl : 0; $fspace=" "x($fspace_def+($pos>$pl-1?$pos-$pl+1:0)); if ($pos+1>=$pfl) { $rspace=" "x($pl-$pos-1); } else { $rspace=$fspace; } $pr_space=" "x($pfl-$pos-1); for my $j (0 .. $pos) { next unless $j < $prl; if (substr($bind_string[$pos],$j,1)==1) { $dimer_binding=$dimer_binding."|" } elsif (substr($bind_string[$pos],$j,1)==0) { $dimer_binding=$dimer_binding."." } else { $dimer_binding=$dimer_binding." " } } print $FH "$fspace"."5' "."$primer_f"." 3'\n". "$rspace"." "."$pr_space"."$dimer_binding\n". "$rspace"."$pr_space"."3' "."$rprimer_r"." 5'\n\n"; } #----------------------# # Program exit/restart # #----------------------# sub end_prog { # This sends the program to the pref writing routine after Mainloop() ... $top->destroy; } sub restart { exec "$0"; } #---------------------# # GUI button routines # #---------------------# sub get_tm { my ($report) = @_; # Lower case bug fix: $fprimer = uc($fprimer); $rprimer = uc($rprimer); my ($deltaG, $deltaH, $deltaS); my $oligo_conc_mols = $oligo_conc / 1000000000; # my $check = check_degenerate($fprimer, 1); # print "fprimer was $fprimer; check was $check\n"; if ($fprimer && !check_degenerate($fprimer, 1)) { ($fprimer_tm, $deltaH, $deltaS) = tm($fprimer); $fprimer_gc = int(gc($fprimer)); $fprimer_tm = sprintf("%.2f", $fprimer_tm); # since dG = dH - TdS, we don't need to calculate based on NN's ... $deltaG = $deltaH-((273.15+$pd_temperature)*($deltaS/1000)); $fprimer_ds = sprintf("%.2f", $deltaS); $fprimer_dh = sprintf("%.2f", $deltaH); $fprimer_dg = sprintf("%.2f", $deltaG); $fprimer_len = length($fprimer); } if ($rprimer && !check_degenerate($rprimer, 1)) { ($rprimer_tm, $deltaH, $deltaS) = tm($rprimer); $rprimer_gc = int(gc($rprimer)); $rprimer_tm = sprintf("%.2f", $rprimer_tm); $deltaG = $deltaH-((273.15+$pd_temperature)*($deltaS/1000)); $rprimer_ds = sprintf("%.2f", $deltaS); $rprimer_dh = sprintf("%.2f", $deltaH); $rprimer_dg = sprintf("%.2f", $deltaG); $rprimer_len = length($rprimer); } my $repeat_real = $repeat-1; $packed_widgets{dim}->delete(0.1,"end"); $packed_widgets{dim}->insert('end', "Warning: forward primer run found\n", 'red') if ($fprimer =~ /(C{$run,}|A{$run,}|G{$run,}|T{$run,})/); $packed_widgets{dim}->insert('end', "Warning: forward primer repeat found : $1\n", 'red') if ($fprimer =~ /(.{2,})\1{$repeat_real,}/); $packed_widgets{dim}->insert('end', "Warning: reverse primer run found\n", 'red') if ($rprimer =~ /(C{$run,}|A{$run,}|G{$run,}|T{$run,})/); $packed_widgets{dim}->insert('end', "Warning: reverse primer repeat found\n", 'red') if ($rprimer =~ /(.{2,})\1{$repeat_real,}/); $packed_widgets{dim}->insert('end', "Most stable 3' extensible primer-dimers (at $pd_temperatureC), if any\n\n", 'blue'); my ($pd1, $pd2, $pd3); if ($fprimer && !check_degenerate($fprimer)) { $pd1 = primer_dimer($fprimer,$fprimer); $pos=$rating_hash{$score_sort[0]}; unless ($score_sort[0]==0) { $packed_widgets{dim}->insert('end', "Forward vs. Forward: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($fprimer, $fprimer, $pos, \ *DIMER); } if ($rprimer && !check_degenerate($rprimer)) { $pd2 = primer_dimer($fprimer,$rprimer); $pos=$rating_hash{$score_sort[0]}; unless ($score_sort[0]==0) { $packed_widgets{dim}->insert('end', "Forward vs. Reverse: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($fprimer, $rprimer, $pos, \ *DIMER); } } } if ($rprimer && !check_degenerate($rprimer)) { $pd3 = primer_dimer($rprimer,$rprimer); $pos=$rating_hash{$score_sort[0]}; unless ($score_sort[0]==0) { $packed_widgets{dim}->insert('end', "Reverse vs. Reverse: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($rprimer, $rprimer, $pos, \ *DIMER) unless ($score_sort[0]==0); } } # $pd_full = 1; $packed_widgets{dim}->insert('end', "\nMore stable non-extensible primer-dimers (at $pd_temperatureC), if any\n\n", 'blue'); if ($fprimer && !check_degenerate($fprimer)) { primer_dimer($fprimer,$fprimer,1); $pos=$rating_hash{$score_sort[0]}; if ($score_sort[0]<$pd1) { $packed_widgets{dim}->insert('end', "Forward vs. Forward: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($fprimer, $fprimer, $pos, \ *DIMER); } if ($rprimer && !check_degenerate($rprimer)) { primer_dimer($fprimer,$rprimer,1); $pos=$rating_hash{$score_sort[0]}; if ($score_sort[0]<$pd2) { $packed_widgets{dim}->insert('end', "Forward vs. Reverse: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($fprimer, $rprimer, $pos, \ *DIMER); } } } if ($rprimer && !check_degenerate($rprimer)) { primer_dimer($rprimer,$rprimer,1); $pos=$rating_hash{$score_sort[0]}; if ($score_sort[0]<$pd3) { $packed_widgets{dim}->insert('end', "Reverse vs. Reverse: $score_sort[0] kcal/mol\n\n", 'black'); draw_dimer($rprimer, $rprimer, $pos, \ *DIMER); } } # $pd_full = 0; # again ... messy tie in for report generating routine ... # Yes, I know this is a really, really, ugly way to do things!! if ($report) { my $dimer_text = $packed_widgets{dim}->get('0.1','end'); my $primer_text = <see(0.1); } sub blast_primers { return if check_packages("HTTP::Request", "LWP::UserAgent"); ### Todo: find matching sequence names from f and r primer blast results ### - easily check possible amp problems # I've tried to make this the way blast *should* be, with colour output # and the ability to limit the results ... if (($fprimer eq "") && ($rprimer eq "")) { dialogue("Please enter primers to BLAST"); return; } my ($blast_out_f,$blast_out_r)=("Waiting on Blast Server for forward primer results ...","Waiting on Blast Server for reverse primer results ..."); my ($blast_header_f, @blast_summary_f, @blast_results_f1, @blast_results_f2, @blast_results_f3); my ($blast_header_r, @blast_summary_r, @blast_results_r1, @blast_results_r2, @blast_results_r3); my $display='f'; my $blast_search_string; $blast_status = ""; my $blast_display = sub { # I don't know whether this is messy or beautiful ... # But I really love the fact that I can do it!! my ($blast_header_ref, $blast_summary_ref, $blast_results_1_ref, $blast_results_2_ref, $blast_results_3_ref, $search_string); if ($display eq 'f') { $blast_header_ref = \$blast_header_f; $blast_summary_ref = \@blast_summary_f; $blast_results_1_ref = \@blast_results_f1; $blast_results_2_ref = \@blast_results_f2; $blast_results_3_ref = \@blast_results_f3; } else { $blast_header_ref = \$blast_header_r; $blast_summary_ref = \@blast_summary_r; $blast_results_1_ref = \@blast_results_r1; $blast_results_2_ref = \@blast_results_r2; $blast_results_3_ref = \@blast_results_r3; } $packed_widgets{'blast_text'}->delete('0.1', 'end'); unless ($$blast_header_ref) { return } $packed_widgets{'blast_text'}->insert('0.1', $$blast_header_ref, 'black'); ### TODO: hyperlinks?? # Users might get confused about "."'s in the string box ... ($search_string = $blast_search_string) ||="."; for my $i (0 .. $#$blast_summary_ref) { $_ = $$blast_summary_ref[$i]; next unless /$search_string/; if (m/^(\w*\|[\w\|\.\d]+)(.*?)([\d\.]+\s+[\d\.\w\-]+\s*$)/) { $packed_widgets{'blast_text'}->insert('end', $1); $packed_widgets{'blast_text'}->insert('end', $2, 'blue'); $packed_widgets{'blast_text'}->insert('end', $3."\n"); } else { ## Debug only # print "WARNING: failed parsing $_ from BLAST results\n"; } } for my $i (0 .. $#$blast_results_1_ref) { next unless $$blast_results_1_ref[$i] =~ /$search_string/; $packed_widgets{'blast_text'}->insert('end', "\n\n".$$blast_results_1_ref[$i]."\n\n ", 'blue'); $packed_widgets{'blast_text'}->insert('end', $$blast_results_2_ref[$i]."\n\n", 'black'); $packed_widgets{'blast_text'}->insert('end', $$blast_results_3_ref[$i]."\n"); } }; # GUI code # destroy old window if one exists (and cancel last blast search ...) if (Exists($blast_d)) { # $top->afterCancel($rptid); $rptid->cancel; $blast_d->destroy; } $blast_d = $top->Toplevel(-title=>'BLAST Search'); $blast_d->withdraw(); my $blast_d_f = $blast_d->Frame()->pack(-expand=>1, -side=>'top', -fill=>'both'); my $blast_d_fb = $blast_d->Frame()->pack(-side=>'left', -anchor=>'sw'); my $blast_d_fs = $blast_d->Frame()->pack(-side=>'right', -anchor=>'se'); $blast_d->Icon(-image => $pixmap); nr(\$blast_d_f, $frame_pady, 1); pack_gui('ROText', '', 'blast_text', 95, 30, -scrollbars=>'osoe'); # buttons nr(\$blast_d_fb); pack_gui('Button', "OK", "blast_full", sub{ $rptid->cancel if $rptid; $blast_d->destroy; }, "active"); pack_gui('Button', "Forward", "blast_f", sub { $display='f'; &$blast_display; }, 'disabled'); pack_gui('Button', "Reverse", "blast_r", sub { $display='r'; &$blast_display; }, 'disabled'); # Status line pack_gui('Label', 'Status: ', 'blast_status_l'); pack_gui('Label', \$blast_status, 'blast_status'); # text search button nr(\$blast_d_fs); pack_gui('Label', 'String:'); pack_gui('Entry', \$blast_search_string, 'blast_search_string', 20); pack_gui('Button', 'Search', "blast_search", sub { &$blast_display; }, 'disabled'); $packed_widgets{blast_text}->configure(-fg=>'grey30'); $packed_widgets{blast_text}->tagConfigure('blue', -foreground => 'midnightblue'); $packed_widgets{blast_text}->tagConfigure('black', -foreground => 'black'); $blast_status = "Sending BLAST request ..."; $blast_d->update; $blast_d->deiconify(); $top->update; my $blast_summary; if ($fprimer) { # Blast forward primer $blast_status="Blasting forward primer ..."; ($blast_header_f, $blast_summary) = blast($fprimer); if ($blast_summary) { @blast_summary_f = split("\n",$blast_summary); @blast_results_f1 = @blast_results_1; @blast_results_f2 = @blast_results_2; @blast_results_f3 = @blast_results_3; $display='f'; &$blast_display; $packed_widgets{blast_f}->configure(-state=>'normal'); $packed_widgets{blast_search}->configure(-state=>'normal'); } else { $blast_status="Unable to connect to server"; } } if ($rprimer) { #Blast reverse primer $blast_status="Blasting reverse primer ..."; ($blast_header_r, $blast_summary) = blast($rprimer); if ($blast_summary) { @blast_summary_r = split("\n",$blast_summary); @blast_results_r1 = @blast_results_1; @blast_results_r2 = @blast_results_2; @blast_results_r3 = @blast_results_3; $packed_widgets{blast_r}->configure(-state=>'normal'); $packed_widgets{blast_search}->configure(-state=>'normal'); $blast_status="Blast search complete"; } else { $blast_status="Unable to connect to server"; } } } sub get_primers { my ($max_ampsize, $max_range_5p, $max_range_3p) = get_variables(qw(max_ampsize max_range_5p max_range_3p)); $cancel=0; $bs=0; my $seq = get_seq(); check_range(); # set the max_ampsize in case the user has modified # either max_range_5p or max_range_3p by hand ... if (($$max_range_5p)&&($$max_range_3p)) { $$max_ampsize = $$max_range_3p-$$max_range_5p } @PF=@PR=(); my ($gene_5p, $gene_3p)=find_gene($seq); sbarprint("\nMoving forward and reverse into arrays ..."); my ($dnaseq_f, $dnaseq_f_len, $dnaseq_r_top, $dnaseq_r, $dnaseq_r_len) = getseq($seq); return if ($cancel==1); # Check that there was actually a DNA sequence found! if (length($dnaseq_f)==0) { sbarprint("\nNo DNA sequence"); $cancel = 1; return; } read_windows(\&primer_window, $dnaseq_f, $dnaseq_f_len, $dnaseq_r); return if ($cancel==1); sbarprint("\nCalculating amplicons ..."); calc_amplicon($dnaseq_r_len); @primer_pairs_pr_s = @primer_pairs; sort_primers('13',1); sort_primers('10'); sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); # draw_dna(\$packed_widgets{primer_canvas},\$packed_widgets{seq}); draw_dna(); } sub get_seq_primers { my ($min_range, $max_range, $slist) = get_variables(qw(min_range max_range primers)); $cancel=0; $bs=0; my $seq = get_seq(); # check_range(); # unless boundaries are set, set them to sequence limits unless (($$max_range)&&($$max_range)) { $$min_range = 0; $$max_range = length($seq); } $$max_range = length($seq) if $$max_range > length($seq); @PF=@PR=(); my ($gene_5p, $gene_3p)=find_gene($seq); sbarprint("\nMoving forward and reverse into arrays ..."); my ($dnaseq_f, $dnaseq_f_len) = getseq($seq); return if ($cancel==1); # Check that there was actually a DNA sequence found! if (length($dnaseq_f)==0) { sbarprint("\nNo DNA sequence"); return; } read_windows(\&primer_window, $dnaseq_f, $dnaseq_f_len); return if ($cancel==1); sbarprint("\nFinding primers ..."); calc_seq_primers(); @$slist = @primer_pairs; sort_primers('1'); sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); # draw_dna(\$packed_widgets{primer_canvas},\$packed_widgets{seq}); draw_dna(); } sub get_qprimers { $cancel=0; $bs=0; my ($spidey_out, $mrna_seq) = run_spidey(1); return if $cancel == 1; @PF=@PR=(); my ($gene_5p, $gene_3p)=find_gene($mrna_seq); sbarprint("\nMoving forward and reverse into arrays ..."); my ($dnaseq_f, $dnaseq_f_len, $dnaseq_r_top, $dnaseq_r, $dnaseq_r_len) = getseq($mrna_seq); return if ($cancel==1); # Check that there was actually a DNA sequence found! if (length($dnaseq_f)==0) { sbarprint("\nNo DNA sequence"); return; } read_windows(\&primer_window, $dnaseq_f, $dnaseq_f_len, $dnaseq_r); return if ($cancel==1); sbarprint("\nCalculating amplicons ..."); # set qpcr flag for amplicon and draw_dna subroutines: $qpcr_flag=1; calc_amplicon($dnaseq_r_len); @primer_pairs_q_s = @primer_pairs; sort_primers('13',1); sort_primers('10'); sbarprint("\nFinished ... found ".($#primer_pairs+1)." primer pairs"); draw_dna(); # unset flag $qpcr_flag=0; } sub find_re_sites { my $page = which_nb_page(); unless ($page eq "pd") { dialogue("This feature is only relevant for the Standard PCR page"); return; } my $seq = get_seq(); my ($max_range_5p, $max_range_3p) = get_variables(qw(max_range_5p max_range_3p)); $seq = substr($seq, $$max_range_5p, $$max_range_3p-$$max_range_5p) if $seq; # find the rebase file (different versions are released constantly) my @gcg_paths = glob("$HOME"."gcg.*"); print @gcg_paths; unless (@gcg_paths) { # search for the file in the program directory @gcg_paths = glob("$program_directory"."gcg.*"); } unless (@gcg_paths) { # search for the file where it should be @gcg_paths = glob("/usr/share/perlprimer/"."gcg.*"); } unless (@gcg_paths) { # search for the file in the current directory @gcg_paths = glob("./"."gcg.*"); } my $gcg_path; foreach (@gcg_paths) { if (/.*gcg\.\d*$/) { $gcg_path = $_; } } unless ($gcg_path) { dialogue("Error: Cannot find the Restriction Enzyme database.\n\nYou need the GCG database from REBASE (http://rebase.neb.com/rebase/rebase.html) for this function to work. The file 'gcg.###' (where ### is the version) should be included in the PerlPrimer distribution - please place that file either in the directory where the program is located, or in the directory $HOME, and try again.\n\nAlternatively, you can download the latest version of the database from the REBASE ftp site."); return; } # open rebase unless (open (REBASE, "<$gcg_path")) { dialogue("Error: Cannot open the Restriction Enzyme database: $!"); return; } my @re_data = ; close REBASE; # parse data my $i; my %enzymes; my %enzyme_sites; my %enzymes_full; foreach (@re_data) { s/[\n\r]//g; my ($enzyme, $re_site) = /^\;*([A-Z|a-z|0-9|\;]+)\s+\d+\s+([A-z\'_]+)\s+/; next unless $enzyme && $re_site; my $original_site = $re_site; $re_site =~ s/\'//g; $re_site =~ s/_//g; if ($enzymes_full{$re_site}) { $enzymes_full{$re_site}.=" | $enzyme"; } else { $enzymes_full{$re_site}=$enzyme; } $enzyme_sites{$enzyme}=$original_site; if ($simple_sites) { next if $re_site =~ /[rymkswbdhvn]/i; next if length($re_site) > 6; next if length($re_site) < 6; } else { $re_site =~ s/r/[ga]/ig; $re_site =~ s/y/[ct]/ig; $re_site =~ s/m/[ac]/ig; $re_site =~ s/k/[gt]/ig; $re_site =~ s/s/[gc]/ig; $re_site =~ s/w/[at]/ig; $re_site =~ s/b/[cgt]/ig; $re_site =~ s/d/[agt]/ig; $re_site =~ s/h/[act]/ig; $re_site =~ s/v/[acg]/ig; $re_site =~ s/n/[acgt]/ig; } $enzymes{$enzyme}=$re_site; } my @no_matches; if ($exclude_found_sites) { foreach my $key (keys(%enzymes)) { if ($seq !~ /$enzymes{$key}/i) { push @no_matches, "$key - $enzyme_sites{$key}"; } } } else { foreach my $key (keys(%enzymes)) { push @no_matches, "$key - $enzyme_sites{$key}"; } } unless (Exists($cloning_d)) { $cloning_d = $top->Toplevel(-title=>'Cloning site configuration'); my $cloning_f = $cloning_d->Frame()->pack(-fill=>'both', -pady=>7); my $cloning_fb = $cloning_d->Frame()->pack(-side=>'bottom', -fill=>'none'); my $re_enzymes = [sort @no_matches]; # deconstruct current info if it exists my ($tmp_site, $enzyme, $key); if ($primer_seq_5f) { $_ = uc($primer_seq_5f); ($cloning_anchor, $tmp_site) = /([A-Z]*)_([A-Z]*)/; if ($enzymes_full{$tmp_site}) { $enzyme = $enzymes_full{$tmp_site}; ($key) = ($enzyme =~ /([A-z0-9]*)/); $forward_re_site = "$enzyme - $enzyme_sites{$key}"; } else { $forward_re_site = $tmp_site; } } if ($primer_seq_5r) { $_ = uc($primer_seq_5r); my $tmp_site; ($cloning_anchor, $tmp_site) = /([A-Z]*)_([A-Z]*)/; if ($enzymes_full{$tmp_site}) { $enzyme = $enzymes_full{$tmp_site}; ($key) = ($enzyme =~ /([A-z0-9]*)/); $reverse_re_site = "$enzyme - $enzyme_sites{$key}"; } else { $reverse_re_site = $tmp_site; } } nr(\$cloning_f); pack_gui('Label', 'Forward restriction enzyme site: ', 'cloning_re_f'); pack_gui('BrowseEntry', \$forward_re_site, 'cloning_re_f', $re_enzymes); nr(); pack_gui('Label', 'Reverse restriction enzyme site: ', 'cloning_re_r'); pack_gui('BrowseEntry', \$reverse_re_site, 'cloning_re_r', $re_enzymes); nr('', 5); pack_gui('Label', "5' anchor sequence", "cloning_anchor"); pack_gui('Entry', \$cloning_anchor, "cloning_anchor", 15); nr(\$cloning_fb); pack_gui('Button', 'OK', 'cloning_ok', sub { my ($enzyme) = ($forward_re_site =~ /([A-z0-9]*)/); $primer_seq_5f = lc("$cloning_anchor\_$enzymes{$enzyme}"); ($enzyme) = ($reverse_re_site =~ /([A-z0-9]*)/); $primer_seq_5r = lc("$cloning_anchor\_$enzymes{$enzyme}"); $cloning_d->destroy; return; }, "active"); pack_gui('Button', 'Cancel', 'cloning_cancel', sub {$cloning_d->destroy;}); $cloning_d->Icon(-image => $pixmap); } else { $cloning_d->deiconify; $cloning_d->raise; } } sub run_spidey { my ($print_alignment) = @_; $print_alignment ||= 0; # Find the spidey executable my @spidey_files = glob("$spidey_path*pidey.*"); @spidey_files = glob("$spidey_path*pidey*") unless @spidey_files; unless (@spidey_files) { dialogue("Error: cannot find the Spidey executable in $spidey_path\n\nIf this is not the directory where Spidey is located, please specify the correct location in the Preferences"); $cancel=1; return; } my %sizes_names; foreach (@spidey_files) { my $size = (stat($_))[7]; $sizes_names{$size}=$_; } my $largest = (sort {$b <=> $a} keys %sizes_names)[0]; my $spidey_exec = $sizes_names{$largest}; my $spidey_command = "\"$spidey_exec\" -i \"$tmp.dna_tmp\" -m \"$tmp.mrna_tmp\" -p $print_alignment"; my $mrna_seq = $packed_widgets{"qmrna_seq"}->get(0.1,"end"); unless (length(clean_seq($mrna_seq))) { dialogue("Error: Please enter an mRNA sequence\n"); $cancel=1; return; } my $dna_seq = $packed_widgets{"qdna_seq"}->get(0.1,"end"); unless (length(clean_seq($dna_seq))) { dialogue("Error: Please enter a DNA sequence\n"); $cancel=1; return; } # Spidey will only accept input as files, so we need to move the two # sequences to temporary files unless (open (MRNA, ">".$tmp.".mrna_tmp")) { dialogue("Error: could not write mRNA temp file: $!\n"); $cancel=1; return; } print MRNA $mrna_seq; close (MRNA); unless (open (DNA, ">".$tmp.".dna_tmp")) { dialogue("Error: could not write DNA temp file: $!\n"); $cancel=1; return; } print DNA $dna_seq; close (DNA); # run spidey ... sbarprint("\nRunning spidey ..."); $_ = `$spidey_command`; # open(SPIDEY, "$spidey_command |"); # $_ = join("",); # close SPIDEY; # abort and complain if we don't see the --SPIDEY signature unless (/--SPIDEY/) { print "$_\n"; if (m/no valid bioseqs(.*)/i) { dialogue("Error: No valid bioseqs$1\n\n(If all sequences are present, this may mean that the temp directory is incorrectly set in the preferences - please make sure that this directory is writable)"); sbarprint("\nCancelled - No valid bioseqs$1"); } else { dialogue("Error: Cannot run Spidey executable correctly\n(Please check the console for error messages)"); sbarprint("\nCancelled - Spidey executable not found"); } $cancel=1; return; } # Warn if problems occur ... print "\n\nWarning: incomplete sequence data or large intron present\n" unless /overall percent identity: 100\.0%/; print "\n\nWarning: mRNA coverage incomplete\n" unless /mRNA coverage: 100%/; # ... and try using the large intron option if they have unless (/overall percent identity: 100\.0%/ && /mRNA coverage: 100%/) { print "Trying spidey again using large intron option ...\n"; $_ = `$spidey_command -X`; # if things are still crap, display a dialogue with spidey's output, # so the user can work out what's gone wrong dialogue("Alignment error:\nSequence data is incomplete\n\n$_") unless /overall percent identity: 100\.0%/; dialogue("Alignment error:\nmRNA coverage is incomplete\n\n$_") unless /mRNA coverage: 100%/; } # delete those tmp files unlink("$tmp.mrna_tmp") || dialogue("Error: could not delete temporary file $spidey_path.mrna_tmp"); unlink("$tmp.dna_tmp") || dialogue("Error: could not delete temporary file $spidey_path.dna_tmp"); sbarprint("\n"); return ($_, $mrna_seq) unless $print_alignment; # isolate intron/exon boundaries @intron_exon_bounds = (); while (/(\d+)-(\d+) \(mRNA\)/g) { push (@intron_exon_bounds, $2); } # last boundary is end of mRNA sequence: remove pop @intron_exon_bounds; return ($_, $mrna_seq); } sub get_bisulphite { # Criteria: # Nested or heminested PCR # Primer amplicon not greater than 450bp # Selection for bisulphite-converted templates # Even distribution of bases prior to conversion # As high GA content as posible [currently not used] # Lack of CpG residues or degeneracy # Long (25-30mer) primer $cancel=0; # old benchmarking code - used for optimising routines my $t_old = new Benchmark if $benchmark; $bs = 1; check_range(); my ($max_ampsize, $max_range_5p, $max_range_3p) = get_variables(qw(max_ampsize max_range_5p max_range_3p)); # set the max_ampsize in case the user has modified # either max_range_5p or max_range_3p by hand ... if (($$max_range_5p)&&($$max_range_3p)) { $$max_ampsize = $$max_range_3p-$$max_range_5p } my $seq = $packed_widgets{"bisul_seq"}->get(0.1,"end"); my ($dnaseq_f, $dnaseq_f_len, $dnaseq_r_top, $dnaseq_r, $dnaseq_r_len) = getseq($seq); # Check that there was actually a DNA sequence found! if (length($dnaseq_f)==0) { sbarprint("\nNo DNA sequence"); return; } @PF=@PR=(); return if ($cancel==1); # Only use top strand for primer design (because conversion makes # the two templates non-complementary), so use $dnaseq_r_top read_windows(\&bisul_window, $dnaseq_f, $dnaseq_f_len, $dnaseq_r_top); return if ($cancel==1); sbarprint("\nCalculating amplicons ..."); calc_amplicon($dnaseq_r_len); @primer_pairs_bs_s = @primer_pairs; sort_primers('13',1); sort_primers('10'); $bs = 0; # benchmarking code if ($benchmark) { my $t_new = new Benchmark; my $diff = timediff ($t_new, $t_old); my $str = timestr ($diff, "all", "5.3f"); print "Time for bisulphite was $str\n\n"; } } sub check_range { my ($max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(max_ampsize max_range_5p min_range max_range max_range_3p)); $$max_range_5p = undef if defined($$max_range_5p) && $$max_range_5p eq ""; $$max_range_3p = undef if defined($$max_range_3p) && $$max_range_3p eq ""; unless (defined($$max_range_5p)&&defined($$max_range_3p)) { return unless defined($$min_range)&&defined($$max_range); $$max_range_5p = $$min_range unless $$max_range_5p; $$max_range_3p = $$max_range unless $$max_range_3p; $$max_ampsize = $$max_range_3p - $$max_range_5p; } } sub copy_selected_primers { # Get variable refs my ($ref, $slist) = get_variables(qw(hlist primers)); my $page = which_nb_page(); # get selected primers my @sel = $$ref->selectionGet; # Clipboard copying routine my $clip = ($page eq "seq") ? "Name\tForward Primer\tPos\tLen\tTm\n" : "Name\tForward Primer\tPos\tLen\tTm\tName\tReverse Primer\tPos\tLen\tTm\tAmp\tdG\n"; my (@gene_array, $gene_frame, $seq); if ($page eq "pd" && (($primer_seq_5f) || ($primer_seq_5r))) { $seq = get_seq(); @gene_array=find_gene($seq); $gene_frame=$gene_array[0][0]%3; } my ($file_name,$dir,$ext) = fileparse($open_file{$page}, qr/\.[^.]*/); $file_name =~ s/_.*//g; foreach my $sel (@sel) { my $nb_page = which_nb_page(); my @details; if ($page eq "seq") { @details = (0 .. 3); } else { @details = ( 0 .. 4, 8, 6, 7, 9, 10 ); } for my $j (@details) { # Names if ($j == 0) { $clip .= $file_name."_".$$slist[$sel][1]."F\t"; } elsif ($j == 4) { $clip .= $file_name."_".$$slist[$sel][8]."R\t"; } if ($page eq "bis") { # Primer redundancy for CpG residues: # Replaces T with Y (pyrimidine) for forward # Replaces A with R (purine) for reverse my $temp_seq; if ($j == 0) { $temp_seq = $$slist[$sel][0]; my $f_original = $$slist[$sel][11]; for my $i (0 .. length($f_original)) { if (substr($f_original, $i, 2) eq "CG") { substr($temp_seq, $i, 1) = 'Y'; } } $clip .= "$temp_seq\t"; } elsif ($j == 4) { $temp_seq = $$slist[$sel][4]; my $r_original = $$slist[$sel][12]; for my $i (0 .. length($r_original)) { if (substr($r_original, $i, 2) eq "CG") { substr($temp_seq, $i, 1) = 'R'; } } $clip .= "$temp_seq\t"; } else { $clip .= "$$slist[$sel][$j]\t"; } } elsif ($page eq "pd") { if ($j == 0 && $primer_seq_5f) { my $fprimer = $$slist[$sel][$j]; my ($primer_seq_5f_real, $insert_f) = add_cloning($seq, $fprimer, $$slist[$sel][1]); $clip .= uc("$primer_seq_5f_real$insert_f\_$fprimer\t"); } elsif ($j == 4 && $primer_seq_5r) { my $rprimer = $$slist[$sel][$j]; my ($primer_seq_5r_real, $insert_r) = (add_cloning($seq, '', '', '', $rprimer, $$slist[$sel][8]))[5,6]; # print $clip .= uc("$primer_seq_5r_real$insert_r\_$rprimer\t"); } else { $clip .= "$$slist[$sel][$j]\t"; } } else { $clip .= "$$slist[$sel][$j]\t"; } } $clip .= "\n"; } $top->clipboardClear; $top->clipboardAppend($clip); } sub select_all_primers { my ($ref, $slist) = get_variables(qw(hlist primers)); $$ref->selectionSet(0,$#$slist); } sub primer_take_range { # Get variable refs my ($max_range_5p, $min_range, $max_range, $max_range_3p, $hlist, $slist) = get_variables(qw(max_range_5p min_range max_range max_range_3p hlist primers)); # get selected primer my @sel = $$hlist->selectionGet; my $primer = shift @sel; $$max_range_5p = $$min_range = $$slist[$primer][1]+$$slist[$primer][2]; $$max_range_3p = $$max_range = $$slist[$primer][8]-$$slist[$primer][6]; draw_dna(); browse_primer($primer); } sub menu_popup { my ($popup_ref) = get_variables(qw(popup)); $$popup_ref->Popup( -popanchor => 'nw', -popover => 'cursor'); } sub menu_text { $text_widget_ref = $_[0]; $popup_text->Popup( -popanchor => 'nw', -popover => 'cursor'); } sub sort_primers { my ($sort_criteria, $quiet) = @_; my ($list, $slist) = get_variables(qw(hlist primers)); my @old_slist = @$slist; $$list->delete('all'); # Re-sort forwards or backwards # allow the ability to simply redisplay the list (when opening files) if ($sort_criteria) { $sort_criteria = $sort_prev if $sort_criteria eq "r"; # We want dG to be sorted from highest to lowest by default: # this is really inelegant! my $sort_order = $sort_reverse; $sort_order = 1-$sort_reverse if ($sort_criteria eq '10' || $sort_criteria eq '13'); @$slist = sort {@$a[$sort_criteria] <=> @$b[$sort_criteria]} @old_slist if $sort_order==0; @$slist = sort {@$b[$sort_criteria] <=> @$a[$sort_criteria]} @old_slist if $sort_order==1; } return if $quiet; my $num_elements = @{ @$slist[0] } if @$slist; $num_elements ||=0; # Re-draw if ($num_elements == 5) { # sequencing quick fix!! for my $i ( 0 .. $#$slist ) { $$list->add($i); $$list->itemCreate($i, 0, -text=>"$$slist[$i][0]", -style=> $style_primer); $$list->itemCreate($i, 1, -text=>"$$slist[$i][1]"); $$list->itemCreate($i, 2, -text=>"$$slist[$i][2]"); $$list->itemCreate($i, 3, -text=>"$$slist[$i][3]", -style=> $style_tm); $$list->itemCreate($i, 4, -text=>"$$slist[$i][4]"); } } else { for my $i ( 0 .. $#$slist ) { $$list->add($i); $$list->itemCreate($i, 0, -text=>"$$slist[$i][0]", -style=> $style_primer); $$list->itemCreate($i, 1, -text=>"$$slist[$i][1]"); $$list->itemCreate($i, 2, -text=>"$$slist[$i][2]"); $$list->itemCreate($i, 3, -text=>"$$slist[$i][3]", -style=> $style_tm); $$list->itemCreate($i, 4, -text=>"$$slist[$i][4]", -style=> $style_primer); $$list->itemCreate($i, 5, -text=>"$$slist[$i][8]"); $$list->itemCreate($i, 6, -text=>"$$slist[$i][6]"); $$list->itemCreate($i, 7, -text=>"$$slist[$i][7]", -style=> $style_tm); $$list->itemCreate($i, 8, -text=>"$$slist[$i][9]"); $$list->itemCreate($i, 9, -text=>"$$slist[$i][10]"); $$list->itemCreate($i, 10, -text=>"$$slist[$i][13]") if defined($$slist[$i][13]); } } # save last value so that "reverse" can be applied if neccessary $sort_prev = $sort_criteria; } sub cancel { # Set cancel flag # (this stops the primer-pairs calculation, # which can go on for some time ...) $cancel = 1; sbarprint("\nOperation cancelled"); } sub get_primers_auto_in { my ($sub_ref, $primers_ref) = get_variables(qw(primer_sub primers)); &$sub_ref(); while ($#$primers_ref < 0) { step_in(); return if $cancel==1; &$sub_ref(); } } sub get_primers_auto_out { my ($sub_ref, $primers_ref) = get_variables(qw(primer_sub primers)); &$sub_ref(); while ($#$primers_ref < 0) { step_out(); return if $cancel==1; &$sub_ref(); } } sub get_primers_cloning { my $page = which_nb_page(); unless ($page eq 'pd') { dialogue("This feature is only relevant for the Standard PCR page"); return; } my $seq = get_seq(); unless ($seq) { dialogue("No DNA sequence found!"); return; } my ($sub_ref, $primers_ref) = get_variables(qw(primer_sub primers)); &$sub_ref(); while ($#$primers_ref < 0) { step_cloning(); return if $cancel==1; &$sub_ref(); } } #----------# # New File # #----------# sub new_file { my ($widget_ref) = @_; my $page = which_nb_page(); if ($page eq "primer") { dialogue("You can't open a new file from the primer information page - please switch to the relevant project page first"); return; } my ($seq_ref, $hlist) = get_variables(qw(seq hlist)); $seq_ref = $widget_ref if ref($widget_ref); $_ = $$seq_ref->get(0.1,"end"); my ($seq_check) = /atcg/i; # print "seq was $_\n\n\$seq was $seq\n"; # my $seq = $$seq_ref->search(-regexp => "[a|g|t|c|A|T|G|C]", "0.1"); # If there's a sequence present, prompt to make sure the user wants to # lose all changes ... if (defined($seq_check) && $file_data_overwrite) { my $answer = dialogue("Save before closing?", 'Yes', 'No', 'Cancel'); if ($answer =~ /cancel/i) { $cancel = 1; return; } elsif ($answer =~ /yes/i) { pp_file_save(); } } if ($page eq "qpcr") { # only for qpcr - quick and dodgy fix $packed_widgets{qdna_seq}->delete(0.1,"end"); $packed_widgets{qmrna_seq}->delete(0.1,"end"); } # Clear variables foreach my $key (keys %{ $variables{$page}}) { my $pointer = $variables{$page}{$key}; if (eval {defined($$pointer)}) { undef($$pointer); if ($default_variables{$page}{$pointer}) { $$pointer = $default_variables{$page}{$pointer}; } } } # Clear arrays foreach my $key (keys %{ $arrays{$page}}) { my $pointer = $arrays{$page}{$key}; if (eval {@$pointer}) { undef(@$pointer); } } # Clear sequence and hlist displays $$seq_ref->delete(0.1,"end"); $$hlist->delete('all'); draw_dna(); # Clear title $top->configure(-title=>"PerlPrimer v$version"); $open_file{$page} = 'File not saved'; } #-----------------------------------------------# # Saving/loading/deleting user-defined defaults # #-----------------------------------------------# sub save_defaults { # Save user-defined default values my $page = which_nb_page(); my $file_defaults = "$HOME.perlprimer.$page"; my $defaults; # get list of variables and values foreach my $key (keys %{ $variables{$page}}) { my $pointer = $variables{$page}{$key}; if (eval {defined($$pointer)}) { if (defined($default_variables{$page}{$pointer})) { $default_variables{$page}{$pointer} = $$pointer; $defaults .= "$key = $$pointer\n"; } } } unless (open (DEFAULTS, ">$file_defaults")) { dialogue("Error: could not open $file_defaults for writing: $!"); return; } print DEFAULTS $defaults; close DEFAULTS; } sub restore_defaults { # Remove user-specified preferences file if exists my $page = which_nb_page(); my $file_defaults = "$HOME.perlprimer.$page"; if (-e $file_defaults) { my $answer = dialogue("Warning: This will permanently remove the user-defined default values for this page and restore the built-in values (requires program restart)",'OkCancel'); return if $answer =~ /cancel/i; unlink($file_defaults) || dialogue("Error: cannot delete file $file_defaults: $1"); dialogue("Built-in values restored. Please restart the program for changes to take effect"); } else { dialogue("Error - User-defined defaults have not been saved for this page.\n\nIf you are trying to reset the parameters for this tab and remove all sequence data, use the \"New File\" command from the File menu"); } } sub load_defaults { # load user-defined default values (same idea as save defaults above, but in reverse) foreach my $key (keys (%nb_page_ref)) { my $page = $nb_page_ref{$key}; my $file_defaults = "$HOME.perlprimer.$nb_page_ref{$key}"; if (-e $file_defaults) { print "Loading user-defined defaults file: $file_defaults\n"; unless (open (DEFAULTS, "<$file_defaults")) { dialogue("Error: could not open $file_defaults for reading: $!"); return; } while () { chomp; my ($variable, $value) = split / = /; my $pointer = $variables{$page}{$variable}; if (defined($default_variables{$page}{$pointer})) { $$pointer = $value; $default_variables{$page}{$pointer} = $value; } } close DEFAULTS; } } } #----------------# # Blast routines # #----------------# sub blast { # for more info on using BLAST via http requests, see: # http://www.ncbi.nlm.nih.gov/BLAST/Doc/ # which details the request syntax unless (Exists($blast_d)) { # this routine should not continue running if the user has destroyed # the blast search dialogue. Hopefully this will stop it! # $top->afterCancel($rptid); $rptid->cancel; return; } return unless (my $query=shift); unless ($local_blast) { # Standard NCBI server blast over http my $blast_put = "http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?QUERY=$query&DATABASE=$blast_database&ENTREZ_QUERY=($blast_entrez_query)&EXPECT=$blast_expect&WORD_SIZE=$blast_word_size&FORMAT_TYPE=Text&PROGRAM=blastn&SERVICE=plain&CMD=Put"; # send off the query $_ = http_get($blast_put); # abort if unsuccessfull return if $_ eq " "; # find the RID string /QBlastInfoBegin(.*)QBlastInfoEnd/sm; $_ = $1; /RID = ([\d\w\-.]*)/; $rid_get = "http://www.ncbi.nlm.nih.gov/blast/Blast.cgi?RID=$1&FORMAT_TYPE=Text&CMD=Get"; # Hah! How about this for a simple way to unblock your GUI ... the only way I # know of to use non-blocking, sleeping subroutines without forks - which we # can't use with ActivePerl for Win32 :( $flag=undef; $blast_count = time(); $rptid = $top->repeat(15000, \&blast_wait); $blast_d->waitVariable(\$flag); # $blast_d->afterCancel($rptid); $rptid->cancel; # Having asked for text output, blast still provides some HTML formatting! s/\//sg; s/\<[\w\\]*\>//g; s/  //g; } else { # local BLAST server # need to save the primer sequence to a temp file for input to local BLAST unless (open (PRIMER, ">$tmp.primer_tmp")) { dialogue("Error: could not write BLAST temp file: $!\n"); return; } print PRIMER $query; close (PRIMER); my $local_blast_command = $local_blast_directory."blastall -p blastn -d $local_blast_database -W $blast_word_size -i $tmp.primer_tmp"; $_ = `$local_blast_command`; # delete tmp file unlink "$tmp.primer_tmp"; } # OK, let's try something really nifty ... let's pull each entry out: my $blast_out = $_; undef(@blast_results_1); undef(@blast_results_2); undef(@blast_results_3); # s/\r//g; /(BLASTN.*?Value.*?)(\w+.*?)\>/s; my ($blast_header, $blast_summary) = ($1,$2); # search for each sequence identifier and find corresponding blast entry while (m/^(\w*\|[\w\|\.\d]+)/mg) { my $found_result = $1; if ($blast_out =~ /\n\>(\Q$found_result\E.*?Length\s*?=\s*?\d*)[\n\s]*(Score\s*?=.*?Strand\s*?=\s*?\w*\s*?\/\s*?\w*)[\n\s]*(Query.*?Sbjct.*?)\n/s) { push @blast_results_1, $1; push @blast_results_2, $2; push @blast_results_3, $3; } } return ($blast_header, $blast_summary); } sub blast_wait { #print "Waiting for server response ...\n"; my $time_now = time(); my $time = convert_time($time_now-$blast_count); $blast_status = "Waiting for server response ... $time elapsed"; $top->update; # print "status: $blast_status\n"; $_ = http_get($rid_get); unless (/Status=WAITING/) { $flag=0; } else { #### debugging ... really need to use a timeout with repeat above ... # print "output was $_\n"; } } sub convert_time { my ($time) = @_; my $seconds = $time % 60; my $minutes_total = ($time-$seconds)/60; # my $minutes = $minutes_total % 60; # my $hours = ($minutes_total-$minutes)/60; my $time_readable = sprintf("%02d:%02d", $minutes_total, $seconds); return $time_readable; } #------------------# # Ensembl routines # #------------------# sub get_ensembl { return if check_packages("HTTP::Request", "LWP::UserAgent"); # retrieve a gene sequence (or a gene and genomic sequence) from ensembl.org ... # Now uses Ensembl's very nice REST API to retrieve all details -- should never break again! # (a very quick kludge for now ... will tidy up code later ...) my $nb_page = which_nb_page(); if ($nb_page eq "primer") { dialogue("Please switch to the project page that you wish to enter the Ensembl data into"); return; } unless (Exists($ensembl)) { $ensembl = $top->Toplevel(-title=>'Retrieve gene from Ensembl'); my $ensembl_f = $ensembl->Frame()->pack(-fill=>'both', -pady=>7); my $ensembl_fb = $ensembl->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$ensembl_f); pack_gui('Label', 'Gene name ', "ensembl_gene"); pack_gui('Entry', \$ensembl_gene, "ensembl_gene", 15); nr(); pack_gui('Label', 'Organism', 'ensembl_organsim'); pack_gui('BrowseEntry', \$ensembl_organism, 'ensembl_organsim', \@ensembl_species, 20); nr(); pack_gui('Label', 'Retrieve', 'ensembl_type'); pack_gui('BrowseEntry', \$ensembl_type, 'ensembl_type', \@ensembl_types, 10); nr(\$ensembl_fb); pack_gui('Button', 'OK', 'ensembl_ok', \&fetch_ensembl, "active"); pack_gui('Button', 'Cancel', 'ensembl_cancel', sub {$ensembl->destroy;}); $ensembl->Icon(-image => $pixmap); } else { $ensembl->deiconify; $ensembl->raise; } } sub fetch_ensembl { my $page = which_nb_page(); unless (($ensembl_gene) && ($ensembl_organism) && ($ensembl_type)) { dialogue("Please make sure gene name, organism and retrieval type are filled in"); return; } print STDERR "Gene was $ensembl_gene ...\n"; my $http = HTTP::Tiny->new(); my $server = 'http://rest.ensembl.org'; my $ext = "/xrefs/symbol/$ensembl_organism/$ensembl_gene?content-type=application/json"; my $response = $http->get($server.$ext, { headers => { 'Content-type' => 'application/json' } }); die "Failed!\n" unless $response->{success}; my $gene_id; if(length $response->{content}) { my $gene = decode_json($response->{content}); unless (@{$gene}[0]) { # no matches dialogue("Sorry, a matching Ensembl ID for that gene name was not found. Gene names need to be typed exactly (both Ensembl names and Ensembl IDs can be used).\n\nPlease try again."); # print "output was\n$_\n"; return; } #print Dumper @{$gene}; $gene_id = @{$gene}[0]->{id}; printf("Gene: %s\n",$gene_id); } $ext = "/lookup/id/$gene_id?expand=1"; $response = $http->get($server.$ext, { headers => { 'Content-type' => 'application/json' } }); die "Failed!\n" unless $response->{success}; # find the Ensembl gene ID, and count the number - if there's more than one # we'll have to ask the user to be more specific my @gene_names; my $name; my %ids; my @enst; my %trans; my @enst_readable; if(length $response->{content}) { my $gene = decode_json($response->{content}); push @gene_names, $gene->{description}; } if (@gene_names) { # Ask user to confirm gene identity or pick the gene of interest if multiple matches my $ensembl_mm = $top->Toplevel(-title=>"Select gene of interest ..."); my $ensembl_mm_f = $ensembl_mm->Frame()->pack(-fill=>'both', -pady=>7); my $ensembl_mm_fb = $ensembl_mm->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$ensembl_mm_f); pack_gui('Label', "Found ".($#gene_names+1)." matching gene".($#gene_names > 0 ? 's' : '')." ...", "ensemble_mm_d_note"); nr(); $name = $gene_names[0]; pack_gui('BrowseEntry', \$name, 'ensembl_mm_d_genes', \@gene_names, 40); my $cancel=1; nr(\$ensembl_mm_fb); pack_gui('Button', 'OK', 'ensembl_ok', sub { $cancel=undef; $ensembl_mm->destroy; $gene_id = $ids{$name}[0]; }, "active"); pack_gui('Button', 'Cancel', 'ensembl_cancel', sub { $ensembl_mm->destroy; }); $ensembl_mm->Icon(-image => $pixmap); # (we need it to freeze execution at this point, since the user may # wish to cancel and refine their choice) $ensembl_mm->waitWindow; return if $cancel; } # Having selected the gene, we can now get the transcripts ... if(length $response->{content}) { my $gene = decode_json($response->{content}); $gene_id = $gene->{id}; foreach my $v (@{ $gene->{Transcript}}) { if ($v->{Translation}) { push @enst_readable, "$v->{id} $v->{display_name} (size: $v->{Translation}->{length} aa)"; } else { push @enst_readable, "$v->{id} $v->{display_name}" } push @enst, $v->{id}; } } my $transcript; if (@enst) { $transcript = $enst_readable[0]; if ($#enst > 0) { # multiple transcripts: ask user to select transcript ID my @transcripts = @enst; my $ensembl_mt = $top->Toplevel(-title=>"Please select transcipt ..."); my $ensembl_mt_f = $ensembl_mt->Frame()->pack(-fill=>'both', -pady=>7); my $ensembl_mt_fb = $ensembl_mt->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$ensembl_mt_f); pack_gui('Label', "Ensemble gene $name has ".($#transcripts+1)." transcript".($#transcripts > 0 ? 's' : '')." ...", "ensemble_mt_d_note"); nr(); pack_gui('BrowseEntry', \$transcript, 'ensembl_mt_d_genes', \@enst_readable, 50); my $cancel=1; nr(\$ensembl_mt_fb); pack_gui('Button', 'OK', 'ensembl_ok', sub { $cancel=undef; $ensembl_mt->destroy; }, "active"); pack_gui('Button', 'Cancel', 'ensembl_cancel', sub { $ensembl_mt->destroy; }); pack_gui('Button', 'View transcripts', 'ensembl_view_transcripts', sub { my $command = "\"$browser\" http://www.ensembl.org/$ensembl_organism/geneview?gene=$gene_id"; if ($os eq 'win') { system "$command &"; } else { system "$command &"; } }); $ensembl_mt->Icon(-image => $pixmap); # (we need it to freeze execution at this point, since the user may # wish to cancel and refine their choice) $ensembl_mt->waitWindow; return if $cancel; } } ($transcript) = ($transcript =~ m/([\w\d\.\-]+)/) if $transcript; unless ($gene_id) { # no matches if (/did not match any records in the database/si) { dialogue("Your query did not match any records in the database. Please make sure all terms are spelled correctly."); } elsif ($_ eq " ") { # returned if response->is_error below return; } else { dialogue("Error: Unable to find gene_id in response from server.\n\nIf this problem persists, it probably means that Ensembl has changed formats - please report this in an email to owenjm\@users.sf.net or submit a bug report at http://perlprimer.sf.net ...\n\nThanks!"); # print "output was\n$_\n"; } return; } new_file(); if ($page eq 'qpcr') { # retrieve both gene and transcript sequences - retrieval type is ignored $_ = convert_ensembl($ensembl_organism,$transcript,'genomic'); $packed_widgets{qdna_seq}->delete(0.1,"end"); $packed_widgets{qdna_seq}->insert(0.1,$_); $_ = convert_ensembl($ensembl_organism,$transcript,'cdna'); $packed_widgets{qmrna_seq}->delete(0.1,"end"); $packed_widgets{qmrna_seq}->insert(0.1,$_); # Run spidey automatically to show the user the intron/exon boundaries run_spidey(1); } else { # retrieve requested sequence my ($seq_ref) = get_variables('seq'); $_ = convert_ensembl($ensembl_organism,$transcript,$ensembl_type); $$seq_ref->delete(0.1,"end"); $$seq_ref->insert(0.1,$_); } # update title my $file_name = "$ensembl_gene"."_($ensembl_organism)"; $file_name = format_file_name($file_name); $top->configure(-title=>"PerlPrimer v$version - $file_name"); $open_file{$page} = $file_name; sbarprint("\n$ensembl_gene ($ensembl_organism) retrieved sucessfully"); # destroy dialogue, draw the sequence $ensembl->destroy; draw_dna(); reset_bounds(); return; } sub convert_ensembl { # argument to http address converter (addresses changed for 1.1.21) my ($ensembl_organism,$transcript,$ensembl_type,$export_type) = @_; my $http = HTTP::Tiny->new(); my $server = 'http://rest.ensembl.org'; my $ext = "/sequence/id/$transcript?content-type=text/plain;type=$ensembl_type"; my $response_cds = $http->get($server."/sequence/id/$transcript?content-type=text/plain;type=$ensembl_type"); #print $server."/sequence/id/$transcript?content-type=text/plain;type=$ensembl_type"."\n"; if(length $response_cds->{content}) { return $response_cds->{content}; } } #------------------------# # http retrieval routine # #------------------------# sub http_get { return if check_packages("HTTP::Request", "LWP::UserAgent"); # simple html retrieval ... used by BLAST and Ensembl routines # I'd love to fork this and stop the GUI blocking here - but I cannot find # an effective way to do this ... :( my ($address, $method) = @_; #print "address:$address\n"; # proxy server if ($use_proxy) { # allow for users using "http://" in the proxy address $http_proxy =~ /(http:\/\/)*(.*)/; my $proxy = $2; $ua->proxy('http', "http://$proxy:$http_port"); } else { # still allow env_proxy $ua->env_proxy(); } my $req = HTTP::Request->new(GET => "$address"); my $response = $ua->request($req); if ($response->is_error) { my $code = $response->code; my $message = $response->message; dialogue("Error: $code $message"); return " "; } $top->update; my $http = $response->content; return $http; } #---------------------# # Status bar updating # #---------------------# sub sbarprint { print SBAR $_[0]; $top->update; } #------------# # HList subs # #------------# sub header_create { my ($widget_r, @headers) = @_; my $i=0; foreach (@headers) { $$widget_r->header('create', $i, -text => $_); $i++; } $style_primer = $$widget_r->ItemStyle("text", -background => '#eeeeee', -foreground => 'red'); $style_tm = $$widget_r->ItemStyle("text", -background => '#eeeeee', -foreground => 'blue'); } sub browse_bisulphite { # my $seq = get_seq(); # Believe it or not, when a primer-pair is double-clicked to send the user # to the hlist-command sub, the browse-command sub is still executed! In # our case, that's pretty bad since we're now requesting a sequence on a # page that doesn't have one. So there's a failsafe here: # return if $seq eq "1"; my ($hlist_sel) = @_; # This routine colours original C residues red and original CpG C's blue # if ($primer_pairs_bs_s[$hlist_sel][10]) { # brute force!! my $f_original = $primer_pairs_bs_s[$hlist_sel][11]; my $f_converted = $primer_pairs_bs_s[$hlist_sel][0]; my $r_original = $primer_pairs_bs_s[$hlist_sel][12]; my $r_comp_original = complement($r_original); my $r_converted = $primer_pairs_bs_s[$hlist_sel][4]; $status_bar->insert('end',"\nF: 5' ", 'grey'); for my $i (0 .. length($f_original)) { if (substr($f_original, $i, 2) eq "CG") { $status_bar->insert('end',substr($f_converted, $i, 1), 'blue'); } elsif (compare_bs($f_original, $f_converted, $i)) { $status_bar->insert('end',substr($f_converted, $i, 1), 'red') } else { $status_bar->insert('end',substr($f_converted, $i, 1)) } } $status_bar->insert('end'," 3' R: 5' ", 'grey'); for my $i (0 .. length($r_comp_original)) { if (substr($r_comp_original, $i, 2) eq "GC") { $status_bar->insert('end',substr($r_converted, $i, 1), 'blue'); } elsif (compare_bs($r_comp_original, $r_converted, $i)) { $status_bar->insert('end',substr($r_converted, $i, 1), 'red') } else { $status_bar->insert('end',substr($r_converted, $i, 1)) } } $status_bar->insert('end'," 3'", 'grey'); $status_bar->see('end'); browse_primer($hlist_sel); # } } sub browse_primer { my ($hlist, $slist, $canv) = get_variables(qw(hlist primers canvas)); my $seq = get_seq(); return if $seq eq "1"; my $hlist_sel; if (defined($_[0])) { $hlist_sel = $_[0]; } else { my @sel = $$hlist->selectionGet; $hlist_sel = shift @sel; } my $width = $$canv->width; my $height= $$canv->height; my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); $$canv->delete('primerf','primerr'); my $fprimerpos = $$slist[$hlist_sel][1]; my $rprimerpos = $$slist[$hlist_sel][8]; my $fprimerposl = $$slist[$hlist_sel][2]; my $rprimerposl = $$slist[$hlist_sel][6]; my ($fprimer_x1,$fprimer_x2,$rprimer_x1,$rprimer_x2); if (defined($fprimerpos)) { $fprimer_x1 = $fprimerpos*$dna_canvas_size + $dna_canvas_offset; $fprimer_x2 = ($fprimerpos+$fprimerposl)*$dna_canvas_size + $dna_canvas_offset; } if (defined($rprimerpos)) { $rprimer_x1 = $rprimerpos*$dna_canvas_size + $dna_canvas_offset; $rprimer_x2 = ($rprimerpos-$rprimerposl)*$dna_canvas_size + $dna_canvas_offset; } my $line_arrow_h = 4; my $line_arrow_w = 4; $$canv->createLine($fprimer_x1,$dc_sel_y1,$fprimer_x2,$dc_sel_y1,$fprimer_x2-$line_arrow_w,$dc_sel_y1-$line_arrow_h, -fill=>'red', -width=>2, -tag=>'primerf') if $fprimerpos; $$canv->createLine($rprimer_x1,$dc_sel_y2,$rprimer_x2,$dc_sel_y2,$rprimer_x2+$line_arrow_w,$dc_sel_y2+$line_arrow_h, -fill=>'red', -width=>2, -tag=>'primerr') if $rprimerpos; } sub compare_bs { return 1 if substr($_[0], $_[2], 1) ne substr($_[1], $_[2], 1); } sub jump_to_tm { # an arrow key shortcut ... jumps from the primer listings to detailed info # including primer-dimers my $nb_page = which_nb_page(); return if $nb_page eq "primer"; $stored_page = $nb_page; my ($hlist, $slist) = get_variables(qw(hlist primers)); my @sel = $$hlist->selectionGet; my $hlist_sel = $sel[0]; return unless @$slist; $fprimer = $$slist[$hlist_sel][0]; $rprimer = $$slist[$hlist_sel][4] if $$slist[$hlist_sel][5]; get_tm(); $nb->raise('primer'); $packed_widgets{dim}->focus; } sub jump_back { # jumps back from the primer information page to the project page my $nb_page = which_nb_page(); return unless $nb_page eq "primer"; return unless $stored_page; $nb->raise($stored_page); my ($hlist) = get_variables(qw(hlist)); $$hlist->focus; } sub hlist_command { my $hlist_sel = $_[0]; my ($hlist_ref, $slist) = get_variables(qw(hlist primers)); my $nb_page = which_nb_page(); return unless ref($slist) eq 'ARRAY'; $stored_page = $nb_page; $fprimer = $$slist[$hlist_sel][0]; if ($$slist[$hlist_sel][5]) { $rprimer = $$slist[$hlist_sel][4]; } else { $rprimer = ""; } get_tm(); $nb->raise('primer'); } #----------------------------# # Generate Report subroutine # #----------------------------# sub generate_report { my ($hlist_ref, $slist) = get_variables(qw(hlist primers)); my $nb_page = which_nb_page(); if ($nb_page eq "primer") { dialogue("You can't generate a report from the primer information page - please switch to the project page first"); return; } my ($hlist_sel) = $$hlist_ref->selectionGet; unless (@$slist && defined($hlist_sel)) { dialogue("The Generate Report function saves the statistics and alignment of a particular primer pair - please select a primer pair first"); return; } my $fprimer_mod = $fprimer = $$slist[$hlist_sel][0]; my $rprimer_mod = $rprimer = $$slist[$hlist_sel][4] unless $nb_page eq 'seq'; my $fprimerpos = $$slist[$hlist_sel][1]; my $rprimerpos = $$slist[$hlist_sel][8] unless $nb_page eq 'seq'; my $amplicon_size = $$slist[$hlist_sel][9] unless $nb_page eq 'seq'; # Generate modified primers: my (@gene_array, $gene_frame, $seq); if ($nb_page eq "pd" && (($primer_seq_5f) || ($primer_seq_5r))) { $seq = get_seq(); @gene_array=find_gene($seq); $gene_frame=$gene_array[0][0]%3; } for my $primer ($fprimer, $rprimer) { if ($nb_page eq "bis") { # Primer redundancy for CpG residues: # Replaces T with Y (pyrimidine) for forward # Replaces A with R (purine) for reverse my $f_original = $$slist[$hlist_sel][11]; for my $i (0 .. length($f_original)) { if (substr($f_original, $i, 2) eq "CG") { substr($fprimer_mod, $i, 1) = 'Y'; } } my $r_original = $$slist[$hlist_sel][12]; for my $i (0 .. length($r_original)) { if (substr($r_original, $i, 2) eq "CG") { substr($rprimer_mod, $i, 1) = 'R'; } } } elsif ($nb_page eq "pd") { if ($primer_seq_5f) { my ($primer_seq_5f_real, $insert_f) = add_cloning($seq, $fprimer, $fprimerpos); $fprimer_mod = uc("$primer_seq_5f_real$insert_f\_$fprimer"); } if ($primer_seq_5r) { my ($primer_seq_5r_real, $insert_r) = (add_cloning($seq, '', '', '', $rprimer, $rprimerpos))[5,6]; $rprimer_mod = uc("$primer_seq_5r_real$insert_r\_$rprimer"); } } } my $tm_text = get_tm(1); my $text = dna_magnify('',1); my $time = localtime; my $amplicon = "$amplicon_size bases ($fprimerpos - $rprimerpos)" unless $nb_page eq 'seq'; $amplicon ||= ""; my $output = <getSaveFile(-defaultextension=>'.txt', -initialfile=>$filename, -filetypes=>$file_types_text); } else { $file = $top->getSaveFile(-defaultextension=>'.txt', -filetypes=>$file_types_text); } if (defined($file)) { open (REPORT, ">$file") || dialogue("Error: Could not open file: $!"); print REPORT $output; close (REPORT); sbarprint("\n$file report generated"); } } #---------------# # Gui dialogues # #---------------# sub dialogue { my ($message, @buttons) = @_; @buttons = ('OK') unless @buttons; my ($title, $text) = $message =~ /(\w+?)\:\s*/; $title ||= "Warning"; $text ||= $message; my $icon = 'info'; $icon = 'error' if $title =~ /error/i; $icon = 'question' if $#buttons > 1; ### Neither messageBox or Dialog are really suitable here - clunky, ugly, ### different appearances under different OSes. Need to write our own dialogue ### handling code sometime ... # messageBox code my $type = join("", @buttons); my $selected = $top->messageBox( -title=>$title, -message=> $text, -type => $type, -icon=> $icon, ); # dialog code (currently not used) # my $dialogue = $top->Dialog( # -title => $title, # -text => $text, # -bitmap => $pixmap, # -default_button => $buttons[0], # -buttons => \@buttons, # ); # # my $selected = $dialogue->Show(); return $selected; } sub info { if (Exists($info_d)) { $info_d->deiconify(); $info_d->raise(); return; } my $text = <Toplevel(-title=>'About PerlPrimer ...'); my $info_d_f = $info_d->Frame()->pack(-padx=>4, -pady=>4, -expand=>1, -fill=>'both'); my $info_icon = $info_d_f->Label(-image => $pixmap)->pack(-side=>'left', -anchor=>'n'); # Because of a few tweaks and the custom layout, this is the one window that # doesn't use pack_gui() # Want the text widget to have the same background colour as the window # (so the user can't tell it's a text widget!) We're using a text widget # rather than a frame to get some formatting options (i.e. bold text) my $colour = $info_d->cget(-bg); my $info_text = $info_d_f->ROText( -relief => 'flat', -wrap => 'word', -bg => $colour, -width => 50, -height => 12, -font => $gui_font, )->pack( -expand=>0, -fill=>'both', -anchor=>'nw'); $info_text->tagConfigure('bold', -font => "$gui_font bold"); $info_text->tagConfigure('center', -justify => 'center'); $info_text->insert('end', $text, 'bold'); $info_text->insert('end', $text2); $info_text->insert('end', $address); my $info_OK = $info_d->Button( -text => 'OK', -font => $gui_font, -padx=>4, -pady=>$button_pady, -command => sub {$info_d->destroy}, )->pack(-pady=>4, -side=>'bottom'); $info_d->Icon(-image => $pixmap); } sub canvas_info { if (Exists($canvas_info_d)) { $canvas_info_d->deiconify(); $canvas_info_d->raise(); return; } $canvas_info_d = $top->Toplevel(-title=>'PerlPrimer Help'); my $canvas_info_d_f = $canvas_info_d->Frame()->pack(-expand=>1, -fill=>'both'); my $canvas_info_d_fb = $canvas_info_d->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$canvas_info_d_f, $frame_pady, 1); pack_gui('ROText', '', 'canvas_info_text', 80, 35, -scrollbars=>'oe', -wrap=>'word'); nr(\$canvas_info_d_fb); pack_gui('Button', "OK", "canvas_info_OK", sub {$canvas_info_d->destroy}, "active"); $packed_widgets{canvas_info_text}->configure(-font=>"$gui_font"); $packed_widgets{canvas_info_text}->tagConfigure('bold', -font => "$gui_font bold"); $packed_widgets{canvas_info_text}->tagConfigure('center', -justify => 'center'); my $info_text_dna=<insert('1.0', " ", 'center'); $packed_widgets{canvas_info_text}->imageCreate('end', -image=>$top->Pixmap(-data => $dna_canvas_pixmap)); $packed_widgets{canvas_info_text}->insert('end', "\n\n\nDNA sequence\n", 'bold'); $packed_widgets{canvas_info_text}->insert('end', $info_text_dna); $packed_widgets{canvas_info_text}->insert('end', "\n\nAmplified ranges\n", 'bold'); $packed_widgets{canvas_info_text}->insert('end', $info_text_ranges); $packed_widgets{canvas_info_text}->insert('end', "\n\nPrimers\n", 'bold'); $packed_widgets{canvas_info_text}->insert('end', $info_text_primers); $packed_widgets{canvas_info_text}->insert('end', "\n\nOther features\n", 'bold'); $packed_widgets{canvas_info_text}->insert('end', $info_text_other); } sub acknowledgements { # just raise the dialogue if it already exists .... if (Exists($ack_d)) { $ack_d->deiconify(); $ack_d->raise(); return; } $ack_d = $top->Toplevel(-title=>'Acknowledgements'); my $ack_d_f = $ack_d->Frame()->pack(-expand=>1, -fill=>'both'); my $ack_d_fb = $ack_d->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$ack_d_f, $frame_pady, 1); pack_gui('ROText', '', 'ack_text', 70, 35, -scrollbars=>'oe', -wrap=>'word'); nr(\$ack_d_fb); pack_gui('Button', "OK", "ack_OK", sub {$ack_d->destroy}, "active"); $packed_widgets{ack_text}->configure(-font=>"$gui_font"); $packed_widgets{ack_text}->tagConfigure('bold', -font =>"$gui_font bold"); my $text_rebase=<insert('0.1', "Restriction enzyme data\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_rebase); $packed_widgets{ack_text}->insert('end', "\n\nThermodynamic parameters\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_thermo); $packed_widgets{ack_text}->insert('end', "\n\nEntropy corrections\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_entropy); $packed_widgets{ack_text}->insert('end', "\n\nGC clamp parameters\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_gc_clamp); $packed_widgets{ack_text}->insert('end', "\n\nBisulphate PCR primer design\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_cpg); $packed_widgets{ack_text}->insert('end', "\n\nSpecial thanks for comments, testing, suggestions and bugfixes\n(in no particular order)\n\n", 'bold'); $packed_widgets{ack_text}->insert('end', $text_thanks); # icon $ack_d->Icon(-image => $pixmap); } sub view_spidey_out { my ($spidey_out) = run_spidey(1); my $spidey_d = $top->Toplevel(-title=>'Spidey output'); my $spidey_d_f = $spidey_d->Frame()->pack(-expand=>1, -fill=>'both'); my $spidey_d_fb = $spidey_d->Frame()->pack(-side=>'bottom', -fill=>'x'); $spidey_d->Icon(-image => $pixmap); nr(\$spidey_d_f, $frame_pady, 1); pack_gui('ROText', '', 'spidey_text', 100, 25, -scrollbars=>'osoe'); nr(\$spidey_d_fb); pack_gui('Button', "OK", "spidey_OK", sub {$spidey_d->destroy}, "active"); pack_gui('Button', "Full output", "spidey_full", sub { ($spidey_out) = run_spidey(0); $packed_widgets{'spidey_text'}->delete('0.1', 'end'); $packed_widgets{'spidey_text'}->insert('0.1', $spidey_out) }); $packed_widgets{spidey_text}->insert('0.1', $spidey_out); } #-------------# # Preferences # #-------------# sub prefs { # Preferences dialogue # Preference file is $pref_file; all variables saved/restored are given in # the hash %pref_variables. It should be fairly clear that this makes the # preference system easily extensible and yet also extremely simple ... # just raise the dialogue if it already exists .... if (Exists($prefs)) { $prefs->deiconify(); $prefs->raise(); return; } $prefs = $top->Toplevel(-title=>'Preferences'); my $prefs_nb = $prefs->NoteBook( -inactivebackground=>"#$nb_colour", -relief => 'raised', -bd => 1, )->pack( -expand=>1, -padx=>4, -pady=>4, -fill=>'both', ); my $prefs_page_general = $prefs_nb->add('files', -label=>'General', -anchor=>'nw'); my $prefs_page_repeats = $prefs_nb->add('repeats', -label=>'Exclusions', -anchor=>'nw'); my $prefs_page_blast = $prefs_nb->add('blast', -label=>'BLAST', -anchor=>'nw'); my $prefs_page_cloning = $prefs_nb->add('cloning', -label=>'Cloning', -anchor=>'nw'); my $prefs_page_orfcpg = $prefs_nb->add('orfcpg', -label=>'CpG Islands', -anchor=>'nw'); my $prefs_page_dimers = $prefs_nb->add('dimers', -label=>'Dimers', -anchor=>'nw'); my $prefs_page_connection = $prefs_nb->add('connection', -label=>'Network', -anchor=>'nw'); my $prefs_page_gui = $prefs_nb->add('gui', -label=>'GUI', -anchor=>'nw'); my $prefs_page_general_f = $prefs_page_general->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_repeats_f = $prefs_page_repeats->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_blast_f = $prefs_page_blast->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_orfcpg_f = $prefs_page_orfcpg->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_cloning_f = $prefs_page_cloning->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_dimers_f = $prefs_page_dimers->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_connection_f = $prefs_page_connection->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_page_gui_f = $prefs_page_gui->Frame()->pack(-anchor=>'nw', -expand=>0, -fill=>'none'); my $prefs_fb = $prefs->Frame()->pack(-side=>'bottom', -padx=>2, -fill=>'none'); # new Tk choose directory code: my $browse_directory = sub { my ($dir_ref, $title) = @_; my $dir = $prefs->chooseDirectory(); return unless defined($dir); if ($os eq 'win') { # directory separator correction if running through cygwin $dir =~ s/\//\\/g; } $dir = $dir.$dir_sep unless ($dir =~ /$dir_sep$/); $$dir_ref = $dir; }; my $browse_file = sub { my ($file_ref, $title) = @_; my $file = $top->getOpenFile(-initialfile=>$$file_ref); return unless defined($file); if ($os eq 'win') { # directory separator correction if running through cygwin $file =~ s/\//\\/g; } $$file_ref = $file; }; nr(\$prefs_page_general_f, 2); nr(); pack_gui('Label', "Directories", "prefs_directories_l", -font=>$gui_font_bold); nr(); pack_gui('Label', "Home directory", "prefs_files_home"); pack_gui('Label', \$HOME, "prefs_files_home"); # my $open_home = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_directory, (\$HOME, "home")])->pack(-side=>'left'); nr('',0); pack_gui('Label', "Temp directory", "prefs_files_tmp"); pack_gui('Entry', \$tmp, "prefs_files_tmp", 20); my $open_tmp = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_directory, (\$tmp, "tmp")])->pack(-side=>'left'); nr('',0); pack_gui('Label', "Path to Spidey executable", "prefs_files_spidey"); pack_gui('Entry', \$spidey_path, "prefs_files_spidey", 20); my $open_spidey = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_directory, (\$spidey_path, "Spidey")])->pack(-side=>'left'); nr('', 7); nr(); pack_gui('Label', "Opening files", "prefs_files_l", -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Prompt to save existing project before opening new data', "prefs_files_overwrite", \$file_data_overwrite); nr('', 7); nr(); pack_gui('Label', "PCR component concentrations", "prefs_oligo", -font=>$gui_font_bold); nr(); pack_gui('Label', "Mg++: "); pack_gui('Entry', \$mg_conc, "prefs_mg_conc", 5); pack_gui('Label', "mM"); nr(); pack_gui('Label', "Oligos: "); pack_gui('Entry', \$oligo_conc, "prefs_oligo_conc", 5); pack_gui('Label', "nM"); nr(); pack_gui('Label', "dNTPs: "); pack_gui('Entry', \$dntp_conc, "prefs_oligo_conc", 5); pack_gui('Label', "mM"); nr(); pack_gui('Label', "Monovalent cations: "); pack_gui('Entry', \$monovalent_cation_conc, "prefs_monocat_conc", 5,); pack_gui('Label', "mM"); nr('', 7); nr(); pack_gui('Label', "ORF / CpG island finding behaviour", '', -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Defer to capitalised regions', "prefs_defer", \$defer_to_caps); nr('',2); nr(\$prefs_page_repeats_f, 2); nr(); pack_gui('Label', "PCR excluded repeats / runs", '', -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Exclude primers containing more than', "prefs_exclude_rr", \$exclude_rr); nr(); pack_gui('Entry', \$repeat, "prefs_rr_repeats", 3); pack_gui('Label', 'Repeats or'); pack_gui('Entry', \$run, "prefs_rr_runs", 3); pack_gui('Label', 'Runs'); nr('', 7); nr(); pack_gui('Label', "Bisulphite PCR excluded repeats / runs", '', -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Exclude primers containing more than', "prefs_exclude_rr_bs", \$exclude_rr_bs); nr(); pack_gui('Entry', \$repeat, "prefs_rrbs_repeats", 3); pack_gui('Label', 'Repeats or'); pack_gui('Entry', \$run, "prefs_rrbs_repeats", 3); pack_gui('Label', 'Runs'); nr('', 7); nr(); pack_gui('Label', 'Exclude %GC content', '', -font=>$gui_font_bold); nr(); pack_gui('Label', "Only consider primers with ", "prefs_gc_max"); pack_gui('Entry', \$min_gc, "prefs_gc_min", 3); pack_gui('Label', '-'); pack_gui('Entry', \$max_gc, "prefs_gc_max", 3); pack_gui('Label', '% GC content'); nr(); pack_gui('Label', '(Used when "Exclude %GC" is checked on a project page)'); nr('', 7); nr(); pack_gui('Label', "3' GC clamp", '', -font=>$gui_font_bold); nr(); pack_gui('Checkbutton', 'Use Onodera and Melcher (2004) rules', "prefs_onodera", \$onodera_clamp); nr(); pack_gui('Label', '(Selects for WSS or SWS but not WCG at 3\' end)'); nr('',2); nr(\$prefs_page_blast_f, 2); nr(); pack_gui('Label', "BLAST search parameters", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Expect value: ', "prefs_blast_expect"); pack_gui('Entry', \$blast_expect, "prefs_blast_expect", 4); nr(); pack_gui('Label', 'Word size: ', "prefs_blast_wordsize"); pack_gui('Entry', \$blast_word_size, "prefs_blast_wordsize", 4); nr(); pack_gui('Radiobutton', "Use remote BLAST server", 'remote_blast_r', -variable=>\$local_blast, -value=>0); pack_gui('Radiobutton', "Use local BLAST server", 'remote_blast_r', -variable=>\$local_blast, -value=>1); nr('', 7); nr(); pack_gui('Label', "Remote BLAST (NCBI server)", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Database: ', 'prefs_blast_database'); pack_gui('BrowseEntry', \$blast_database, 'prefs_blast_database', \@blast_database_array, 10); nr(); pack_gui('Label', 'Limit to organism (Entrez query): ', 'prefs_blast_entrez'); pack_gui('BrowseEntry', \$blast_entrez_query, 'prefs_blast_entrez', \@blast_entrez_array, 20); nr('',7); nr(); pack_gui('Label', "Local BLAST", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'BLAST server location', "prefs_blast_expect"); pack_gui('Entry', \$local_blast_directory, "prefs_blast_expect", 30); my $open_blast_srv = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_directory, (\$local_blast_directory, "BLAST directory")])->pack(-side=>'left'); nr('',0); pack_gui('Label', 'Database: ', "prefs_blast_expect"); pack_gui('Entry', \$local_blast_database, "prefs_blast_expect", 30); my $open_blast_database = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_file, (\$local_blast_database, "BLAST database")])->pack(-side=>'left'); nr('',0); nr(\$prefs_page_orfcpg_f, 2); nr(); pack_gui('Label', "CpG island prediction", '', -font=>$gui_font_bold); nr(); pack_gui('Label', "Window size: ", "prefs_bioinf_window"); pack_gui('Entry', \$cpg_window, "prefs_bioinf_window", 5); pack_gui('Label', 'bases'); nr(); pack_gui('Label', "Minimum island size: ", "prefs_bioinf_island"); pack_gui('Entry', \$min_cpg_island, "prefs_bioinf_island", 5); pack_gui('Label', 'bases'); nr(); pack_gui('Label', "Minimum obs/exp: ", "prefs_bioinf_oe"); pack_gui('Entry', \$cpg_oe, "prefs_bioinf_oe", 5); nr(); pack_gui('Label', "Minimum GC content: ", "prefs_bioinf_gc"); pack_gui('Entry', \$cpg_gc, "prefs_bioinf_gc", 5); pack_gui('Label', '%'); nr('',0); pack_gui('Checkbutton', 'Emulate cpgplot', "prefs_cpgplot_method", \$cpgplot_method); nr('', 7); nr(\$prefs_page_cloning_f, 2); nr(); pack_gui('Label', "Restriction enzyme cloning sequences", '', -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Use 6-base cutting enzymes only', "prefs_simple_sites", \$simple_sites); nr('',0); pack_gui('Checkbutton', 'Only list enzymes that do not cut sequence', "prefs_exclude_found_sites", \$exclude_found_sites); nr('',7); nr(\$prefs_page_dimers_f, 2); nr(); pack_gui('Label', "Primer-dimer parameters", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Calculate primer-dimer dG at ', 'prefs_dimer_temp'); pack_gui('Entry', \$pd_temperature, 'prefs_dimer_temp', 3); pack_gui('Label', 'C'); nr(\$prefs_page_connection_f, 2); nr(); pack_gui('Label', "Web browser", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Browser: ', 'prefs_connection_browser'); pack_gui('Entry', \$browser, 'prefs_connection_browser', 30); my $open_browser = pack_button($row_counter[-1], $top->Pixmap(-data => $icon_open_small), [$browse_file, (\$browser, "Browser")])->pack(-side=>'left'); nr('', 7); nr(); pack_gui('Label', "Proxy server", '', -font=>$gui_font_bold); nr('',0); pack_gui('Checkbutton', 'Use http proxy server', "prefs_connection_proxy", \$use_proxy); nr(); pack_gui('Label', 'Address: ', 'prefs_connection_address'); pack_gui('Entry', \$http_proxy, 'prefs_connection_address', 30); pack_gui('Label', 'Port', 'prefs_connection_port'); pack_gui('Entry', \$http_port, 'prefs_connection_port', 5); nr('',7); nr(); pack_gui('Label', "Interaction with external applications", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Listen to port ',"prefs_contigviewer_port"); pack_gui('Entry', \$tcp_port, "prefs_contigviewer_port", 6); nr('',0); pack_gui('Checkbutton', 'Automatically find primers upon receiving data', "prefs_contigviewer_autofind", \$ipc_autofind); nr(\$prefs_page_gui_f, 3); nr(); pack_gui('Label', "Fonts", '', -font=>$gui_font_bold); nr(); my @font_families = sort $top->fontFamilies; pack_gui('Label', 'Main font:', 'prefs_gui_family'); pack_gui('BrowseEntry', \$gui_font_face, 'prefs_gui_family', \@font_families); pack_gui('Label', ' Size:', 'prefs_gui_size'); pack_gui('BrowseEntry', \$gui_font_size, 'prefs_gui_size', [(3 .. 32)], 3); nr(); pack_gui('Label', 'List font:', 'prefs_gui_list_family'); pack_gui('BrowseEntry', \$list_font_face, 'prefs_gui_list_family', \@font_families); pack_gui('Label', ' Size:', 'prefs_gui_list_size'); pack_gui('BrowseEntry', \$list_font_size, 'prefs_gui_list_size', [(3 .. 32)], 3); nr(); pack_gui('Label', 'Fixed font:', 'prefs_gui_text_family'); pack_gui('BrowseEntry', \$text_font_face, 'prefs_gui_text_family', \@font_families); pack_gui('Label', ' Size:', 'prefs_gui_text_size'); pack_gui('BrowseEntry', \$text_font_size, 'prefs_gui_text_size', [(3 .. 32)], 3); nr(); pack_gui('Checkbutton', 'Use OS font defaults', 'prefs_gui_override', \$font_override); nr('',7); nr(); pack_gui('Label', "Previously opened files", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'List the last ', "prefs_gui_mru_number"); pack_gui('Entry', \$mru_number, "prefs_gui_mru_number", 3); pack_gui('Label', 'files', "prefs_gui_mru_number"); nr('',7); nr(); pack_gui('Label', "Mouse Wheel", '', -font=>$gui_font_bold); nr(); pack_gui('Label', 'Mouse wheel scrolls ', "prefs_gui_wheel"); pack_gui('Entry', \$scroll_factor, "prefs_gui_wheel", 3); pack_gui('Label', 'lines'); nr('',2); nr(\$prefs_fb); pack_gui('Button', 'OK', 'prefs_ok', sub { # check if [dNTPs] > [Mg++] ... if ($dntp_conc > $mg_conc) { my $answer = dialogue("Setting [dNTPs] > [Mg++] will have unexpected results - Tm calculations may be inaccurate", 'OK', 'Cancel'); return if $answer eq 'Cancel'; } # if the salt concentration has been changed, we need to recalculate %oligo_dG recalculate_dG(); # write the data to the pref_file ... my $file_data = ""; foreach my $i (keys %pref_variables) { my $pointer = $pref_variables{$i}; $file_data .= "$i = $$pointer\n"; } foreach my $i (keys %pref_arrays) { my $pointer = $pref_arrays{$i}; $file_data .= "$i = [".join(",",@$pointer)."]\n"; } open (PREFS, ">$pref_file") || dialogue("Error: Could not open prefs file for writing: $!"); print PREFS $file_data; close (PREFS); # You really don't want to know why we have to read the prefs file # just after writing it ... # (hint: if we don't, and we open and OK the prefs window twice, # perl starts saying that 1.5-0.2 = 1 ... I don't know why ... ) read_prefs(); $prefs->destroy; }, 'active'); pack_gui('Button', 'Revert', 'prefs_cancel', sub { # This is perhaps a bit inefficient, re-reading the pref_file each time # the dialogue is cancelled. However, it does not seem to cause a # noticable time-lag and it's certainly the simplest way to do things # (otherwise we'd have to save each value to a temporary hash and update # each variable on prefs_OK ... to make this extensible we'd have to make # a hash with the same keys as %pref_variables ... # it's definitely possible, but a bit of a fuss!! read_prefs(); $prefs->destroy; }); $prefs->Icon(-image => $pixmap); } sub read_prefs { return unless -e $pref_file; open (PREFS, "<$pref_file") || dialogue("Error: Could not open prefs file for reading: $!"); my $pointer; while () { s/[\n\r]//g; next if /^$/; my ($key, $value) = split / = /; if ($value =~ /\[(.+)\]/) { # an array $pointer = $pref_arrays{$key}; @$pointer = split(",",$1); } else { $pointer = $pref_variables{$key}; $$pointer = $value; } } close (PREFS); } #------------------------# # DNA graphical routines # #------------------------# sub find_gene { # Find genes (or CpG islands, or whatever else you might fancy) based on # case (upper-case marks genes) (can have more than one ORF in the same # sequence, which the ORF-finding sub doesn't allow; also speeds things # up a bit by not having to ORF/CpG-find each time) $_ = shift; return if length($_) == 0; my ($subroutine) = get_variables(qw(find_sub)); # s/\>.*\n//g; #remove FASTA formatting if it exists $_ = clean_seq($_); my @array = (); my $prev = 0; my $nb_page = which_nb_page(); # if the user wishes to defer to capitalised regions, do so ... # i.e. a sequence with lowercase and capitalised regions will be assumed to # use caps to mark the gene/cpg island # NB - this is no longer the default behaviour if ($defer_to_caps) { my $i; while (/([a-z]*)([A-Z]*)(?=[a-z]*)/xg) { $i++; push @array, [$prev+length($1), $prev+length($1)+length($2)] if (length($2)>0 && length($1)>0); $prev += length($1)+length($2); } return @array if @array; } # if we didn't find any marked regions, let's hand it over to our built in, # trusty orf and cpg finding routines ... return &$subroutine($_); } sub find_orf { # no gene marked - try to find ORF since user has requested it $_ = lc(shift); $_ = clean_seq($_); my ($seq_ref) = get_variables('seq'); my @orf=(); my $seq = $_; for my $i (0 .. 2) { $orf[$i][0] = 0; my $init; my $met_init; my $orf_count = 0; my $orf_start = -1; my $j; for ($j = $i; $j<(length($seq)-3); $j+=3) { my $codon = uc(substr($seq, $j, 3)); my $aa = $genetic_code{$codon}; $aa ||= "X"; unless ($aa eq "*") { # only take ORF from initiating Met codon or rare alternatives unless ($init) { if ($codon =~ m/[ATGC]TG/) { $init=1; $orf_start = $j; } else { next; } } # prefer initiation from downstream ATG if it exists ... if (!$met_init && ($aa eq 'M')) { $met_init=1; $orf_start = $j; $orf_count = 0; } $orf_count++; next; } # stop codon if ($orf_count > $orf[$i][0]) { $orf[$i][0]=$orf_count+1; $orf[$i][1]=$orf_start; # end orf $orf[$i][2]=$j+3; # frame $orf[$i][3]=$i; } $orf_count=$met_init=$init=0; $orf_start=-1; } if ($orf_count > $orf[$i][0]) { $orf[$i][0]=$orf_count+1; $orf[$i][1]=$orf_start; # end orf $orf[$i][2]=$j+3; # frame $orf[$i][3]=$i; } $orf_start=$orf_count=$met_init=0; } # sort by orf length: @orf = sort {@$b[0] <=> @$a[0]} @orf; return unless $orf[0][0] > 1; # Recreate DNA sequence based on ORF my $dna3 =""; my $dna1 = substr($seq, 0, $orf[0][1]); my $dna2 = uc(substr($seq, $orf[0][1], $orf[0][0]*3)); $dna3 = substr($seq, $orf[0][0]*3+$orf[0][1], ) unless $orf[0][0]*3+$orf[0][1]>=length($seq); push my @orf_return, [$orf[0][1], $orf[0][2]]; # Re-enter DNA sequence using capitalised ORF, lowercase upstream and downstream regions $$seq_ref->delete(0.1,"end"); $$seq_ref->insert(0.1,$dna1.$dna2.$dna3); return @orf_return; # return $orf[0][1], $orf[0][2]; } sub find_cpg { # no CpG marked - try to find CpG islands from sequence # two methods: correct and cpgplot emulation $_ = lc(shift); $_ = clean_seq($_); my $seq = $_; my $seq_len = length($seq); my @cpg_island =(); my $cpg_flag = 0; my $cpg_start = 0; my $cpg_count = 0; my $island_max = 0; my $last_cpg_pos = 0; my $win_count =0; # when cpgplot method is 1, we're emulating cpgplot behaviour (see comment # below) my $cpg_av = ($cpgplot_method ? 10 : 9); # CpG finding routine # # This is based on the method first described in Gardiner-Garden and Frommer # (1987) and used in many other programs since. The most common utility is # cpgplot, but cpgplot does not give the same results as this routine! # Analysing the source, it's apparent that cpgplot is buggy and in fact does # not use the parameters it claims to, but slightly an underestimate. # # The worst bug of all in cpgplot is that the avg readings for each window are # inflated: they are not an average at all; rather, 11 frames are read and the # totals divided by 10!! The second bug comes in calculating the number of CpG # residues in a window - cpgplot actually adds in an extra base when # performing this calculation, but does not add in an extra base when counting # the c's and g's in a sequence, or the %GC content!! # # PerlPrimer has the option to deliberately allow for this and return the # same results as cpgplot, simply because cpgplot has been around for so long # seems to be the de facto standard for CpG island prediction. However, by # default PerlPrimer uses my algorithmically correct approach. my (@cpg_oe, @cpg_pgc)=(); # We use an average of 10 (or 11 in cpgplot's case) window values at each # point, so to save recalculating the same value 10 times we save it in an # array ... for my $i (0 .. $seq_len-$cpg_window) { $_ = substr($seq, $i, $cpg_window); # calculate O/E my $g = tr/Gg/Gg/; my $c = tr/Cc/Cc/; my $exp_gc = ($c*$g)/(length($_)-1); if ($cpgplot_method) { $_ = substr($seq, $i, $cpg_window+1); } my $cg = s/cg/cg/ig; # save data $cpg_pgc[$i] = gc($_); if ($exp_gc == 0) { $cpg_oe[$i] = 0; } else { $cpg_oe[$i] = $cg/$exp_gc; } } # Calculate the averages and find the islands for my $i (0 .. $seq_len-$cpg_window-10) { # the real position is in the middle of the window... my $cpg_real_pos = int($i+($cpg_window/2)); # of course, this is (10 or 11)/2 bases upstream from the real mid-point, # since we're averaging 10 frames from here (another bug in cpgplot!) $cpg_real_pos += int($cpg_av/2) unless $cpgplot_method; my $sum_cg = 0; my $sum_obs_exp = 0; # Cpgplot uses an 11 window "average" (it thinks it's only using 10 ...) for my $j ($i .. $i+$cpg_av) { $sum_cg += $cpg_pgc[$j]; $sum_obs_exp += $cpg_oe[$j]; } my $av_obs_exp = $sum_obs_exp/10; my $av_cg = $sum_cg/10; if (($av_cg > $cpg_gc) && ($av_obs_exp>$cpg_oe)) { # A possible island: set the start point if it's the first $cpg_start=$cpg_real_pos if $cpg_flag==0; $cpg_flag=1; next; } unless ($cpg_flag==0) { if ($cpg_real_pos-$cpg_start < $min_cpg_island) { # don't save the island if it's less than the minimum size $cpg_flag=0; $cpg_count=0; $cpg_start=0; next; } # Save the island # This should have an end point of $cpg_real_pos-1, as we're # now a base ahead of the last positive, but cpgplot has it # as $cpg_real_pos+1 (?!?) my $cpg_island_end = ($cpgplot_method ? $cpg_real_pos-1 : $cpg_real_pos+1); push @cpg_island, [$cpg_start, $cpg_island_end]; # reset flags $cpg_flag=0; $cpg_count=0; $cpg_start=0; } } $packed_widgets{"bisul_seq"}->delete(0.1,"end"); my $cpg_pos = 0; for my $i (0 .. $#cpg_island) { print "Island No. $i: start $cpg_island[$i][0], end $cpg_island[$i][1], length ",$cpg_island[$i][1]-$cpg_island[$i][0],"\n"; # Recreate DNA sequence based on ORF my $dna1 = substr($seq, $cpg_pos, $cpg_island[$i][0]-$cpg_pos); # print "pos, len = $cpg_pos, $cpg_island[$i][0]\n"; my $dna2 = uc(substr($seq, $cpg_island[$i][0], $cpg_island[$i][1]-$cpg_island[$i][0])); # print "dna2 pos, len = $cpg_island[$i][0], $cpg_island[$i][1]\n"; $cpg_pos = $cpg_island[$i][1]; # print "pos = $cpg_pos\n"; # Re-enter DNA sequence using capitalised ORF, lowercase 5' and 3' regions $packed_widgets{"bisul_seq"}->insert("1.end",$dna1.$dna2); } # Last part of the reconstructed sequence: my $dna3 = substr($seq, $cpg_pos); $packed_widgets{"bisul_seq"}->insert("1.end",$dna3); return (-1) unless @cpg_island; return @cpg_island; # return the start of the first island and the end of the last island return $cpg_island[0][0], $cpg_island[$#cpg_island][0]+$cpg_island[$#cpg_island][1]; } sub draw_dna { my $old_defer = $defer_to_caps; # save state $defer_to_caps = 1 if @_ && $_[0] == 1; my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p, $hlist, $slist, $canvas) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p hlist primers canvas)); my $seq = get_seq(); return unless $seq =~ /[a|t|c|g]/i; # delete old canvas elements $$canvas->delete('dna', 'amp_range', 'gene_range', 'dna_range','primerf','primerr', 'direction', 'ie_boundary'); # get dimensions of canvas my $width = $$canvas->width; my $height= $$canvas->height; my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); my $canv = which_nb_page(); # Amplicon size if (($$max_ampsize)&&($$min_ampsize)&&($$max_range)) { $min_amp_canvas{$canv} = $$max_range_5p * $dna_canvas_size + $dna_canvas_offset if defined($$max_range_5p); $max_amp_canvas{$canv} = $$max_range_3p * $dna_canvas_size + $dna_canvas_offset if defined($$max_range_3p); $$canvas->createRectangle($min_amp_canvas{$canv},$dc_sel_y1-$dc_sel_offset2,$max_amp_canvas{$canv},$dc_sel_y2+$dc_sel_offset2, -fill=>'orange', -outline=>'orange4', -width=>1, -tag=>'amp_range'); } # range if (defined($$max_range)&&defined($$min_range)) { $min_range_canvas{$canv} = $$min_range * $dna_canvas_size + $dna_canvas_offset; $max_range_canvas{$canv} = $$max_range * $dna_canvas_size + $dna_canvas_offset; $$canvas->createRectangle($min_range_canvas{$canv},$dc_sel_y1,$max_range_canvas{$canv},$dc_sel_y2, -fill=>'dodgerblue', -outline=>'dodgerblue4', -tag=>'dna_range'); } # max_range_5p and 3p variables set check_range(); # draw DNA $$canvas->createRectangle($dna_canvas_offset,$dc_dna_y1,$width-($dna_canvas_offset+1),$dc_dna_y2, -fill=>'grey50', -tag=>'dna'); $$canvas->createText(5,$dna_canvas_middle, -text=>"5'", -justify=>'right', -tag=>'direction'); $$canvas->createText($width-6,$dna_canvas_middle, -text=>"3'", -justify=>'right', -tag=>'direction'); # gene (from capitals, if present) my @gene_array = find_gene($seq); for my $i (0 ... $#gene_array) { my($gene_5p,$gene_3p)=($gene_array[$i][0], $gene_array[$i][1]); if (defined($gene_5p)&&defined($gene_3p)) { $min_range_canvas{$canv} = $gene_5p * $dna_canvas_size + $dna_canvas_offset; $max_range_canvas{$canv} = $gene_3p * $dna_canvas_size + $dna_canvas_offset; $$canvas->createRectangle($min_range_canvas{$canv},$dc_dna_y1,$max_range_canvas{$canv},$dc_dna_y2, -fill=>'midnightblue', -tag=>'gene_range'); } } # QPCR specific: if ($canv eq 'qpcr') { foreach my $i (@intron_exon_bounds) { my $iex = $i * $dna_canvas_size + $dna_canvas_offset; $$canvas->createLine($iex, $dc_dna_y1, $iex, $dc_dna_y2, -fill=>'white', -tag=>'ie_boundary'); } } my @sel = $$hlist->selectionGet; my $hlist_sel = shift @sel; if (defined($hlist_sel)) { # my $width = $$canv->width; # my $height= $$canv->height; # my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); $$canvas->delete('primerf','primerr'); my $fprimerpos = $$slist[$hlist_sel][1]; my $rprimerpos = $$slist[$hlist_sel][8]; my $fprimerposl = $$slist[$hlist_sel][2]; my $rprimerposl = $$slist[$hlist_sel][6]; my ($fprimer_x1,$fprimer_x2,$rprimer_x1,$rprimer_x2); if (defined($fprimerpos)) { $fprimer_x1 = $fprimerpos*$dna_canvas_size + $dna_canvas_offset; $fprimer_x2 = ($fprimerpos+$fprimerposl)*$dna_canvas_size + $dna_canvas_offset; } if (defined($rprimerpos)) { $rprimer_x1 = $rprimerpos*$dna_canvas_size + $dna_canvas_offset; $rprimer_x2 = ($rprimerpos-$rprimerposl)*$dna_canvas_size + $dna_canvas_offset; } my $line_arrow_h = 4; my $line_arrow_w = 4; $$canvas->createLine($fprimer_x1,$dc_sel_y1,$fprimer_x2,$dc_sel_y1,$fprimer_x2-$line_arrow_w,$dc_sel_y1-$line_arrow_h, -fill=>'red', -width=>2, -tag=>'primerf') if $fprimerpos; $$canvas->createLine($rprimer_x1,$dc_sel_y2,$rprimer_x2,$dc_sel_y2,$rprimer_x2+$line_arrow_w,$dc_sel_y2+$line_arrow_h, -fill=>'red', -width=>2, -tag=>'primerr') if $rprimerpos; } $defer_to_caps = $old_defer; # restore state } sub view_intron_exon_structure { # Draws a typical genomic DNA diagram showing exons and introns if (Exists($view_ie)) { $view_ie->destroy; } $cancel=0; # Create dialogue $view_ie = $top->Toplevel(-title=>'Intron/Exon Genomic Structure'); my $view_ie_f = $view_ie->Frame()->pack(-expand=>1, -fill=>'both'); my $view_ie_fb = $view_ie->Frame()->pack(-side=>'bottom', -fill=>'none'); nr(\$view_ie_f); pack_gui('Canvas', '', 'view_ie_canvas', 0, 0, -width=>600, -height=>$dna_canvas_height+35); nr(\$view_ie_fb); pack_gui('Button', 'OK', 'view_ie_ok', sub {$view_ie->destroy}, 'active'); $view_ie->Icon(-image => $pixmap); my $canvas = \$packed_widgets{view_ie_canvas}; # get spidey output ($_) = run_spidey(1); if ($cancel==1) { # cannot find spidey $cancel=0; $view_ie->destroy; return; } # isolate genomic structure my @exon_structure; while (m/Exon \d+[\(\-\)]*: (\d+)-(\d+) \(gen\)/g) { push @exon_structure, [$1, $2]; } # Get strand orientation - will need to reverse array if minus strand my ($strand) = m/Strand: (\w+)/; @exon_structure = reverse(@exon_structure) if $strand eq "minus"; # length of genomic sequence my $gen_length = $exon_structure[-1][1]; # get dimensions of canvas my $width = $$canvas->width; my $height= $$canvas->height; my $dna_canvas_size=($width-($dna_canvas_offset*2))/$gen_length; my $intron_y1 = $dc_dna_y1+2; my $intron_y2 = $dc_dna_y2-2; my $text_y = $dna_canvas_height+5; my $previous_canvas_j; my $count; foreach my $a (@exon_structure) { $count++; my ($i, $j) = @$a; my ($canvas_i, $canvas_j) = ($i * $dna_canvas_size + $dna_canvas_offset, $j * $dna_canvas_size + $dna_canvas_offset); if ($previous_canvas_j) { # draw intron $$canvas->createRectangle($previous_canvas_j, $intron_y1, $canvas_i, $intron_y2, -fill=>'grey'); } # draw exon $$canvas->createRectangle($canvas_i, $dc_dna_y1, $canvas_j, $dc_dna_y2, -fill=>'midnightblue'); my $canvas_text = ($canvas_i + $canvas_j)/2; # label exon $$canvas->createText($canvas_text, $text_y, -fill=>'midnightblue', -text=>$count); $previous_canvas_j = $canvas_j; } # draw sequence length info for my $i (0 .. 5) { my $base_real = $i * $gen_length / 5; my $base = int($base_real/1000) * 1000; my $canvas_text = $base * $dna_canvas_size + $dna_canvas_offset; $$canvas->createText($canvas_text, $text_y+20, -justify=>'left', -fill=>'grey30', -text=>int($base/1000) . "kb"); } } sub items_drag { my ($min_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_range_5p min_range max_range max_range_3p)); my $canv = which_nb_page(); my ($x,$ref) = @_; my $width = $$ref->width; my $dna_max = $width-$dna_canvas_offset; my $dna_min = $dna_canvas_offset; my $seq = get_seq(); return unless $seq =~ /[a|t|c|g]/i; my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); $x = sprintf("%.0f", $$ref->canvasx($x)); $x = $dna_min if $x < $dna_min; $x = $dna_max if $x > $dna_max; if ($canv eq 'qpcr') { # get DNA base $x = ($x-$dna_canvas_offset) / $dna_canvas_size; # exon selection my $last_ie = 0; my $exon_count; foreach (@intron_exon_bounds) { $exon_count++; if ($x>$last_ie && $x<$_) { select_exon($exon_count, 5); return; } } $exon_count++; select_exon($exon_count, 5); return; } if (($$max_range)&&($$min_range)) { $min_range_canvas{$canv} = $$min_range * $dna_canvas_size + $dna_canvas_offset; $max_range_canvas{$canv} = $$max_range * $dna_canvas_size + $dna_canvas_offset; } if (($min_amp_canvas{$canv}) && ($max_amp_canvas{$canv})) { $x = $min_amp_canvas{$canv} if $x < $min_amp_canvas{$canv}; $x = $max_amp_canvas{$canv} if $x > $max_amp_canvas{$canv}; } if ($x-$min_range_canvas{$canv}<$max_range_canvas{$canv}-$x) { $$ref->coords('dna_range', $x, $dc_sel_y1, $max_range_canvas{$canv},$dc_sel_y2); $$min_range = sprintf("%.0f", ($x-$dna_canvas_offset) / $dna_canvas_size); $$min_range = $$max_range_5p if $$max_range_5p && $min_range<$$max_range_5p; } else { $$ref->coords('dna_range', $min_range_canvas{$canv}, $dc_sel_y1, $x, $dc_sel_y2); $$max_range = sprintf("%.0f", ($x-$dna_canvas_offset) / $dna_canvas_size); $$max_range = $$max_range_3p if $$max_range_3p && $$max_range>$$max_range_3p; } $$min_ampsize = $$max_range - $$min_range; } sub select_exon { my ($exon, $end) = @_; $ie_limit ||= 1; $ie_limit_5p = $exon if $end == 5; $ie_limit_3p = $exon if $end == 3; } sub amplicon_drag { my ($x,$widget_ref) = @_; my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); my $canv = which_nb_page(); my $width = $$widget_ref->width; my $dna_max = $width-$dna_canvas_offset; my $dna_min = $dna_canvas_offset; my $seq = get_seq(); return unless $seq =~ /[a|t|c|g]/i; my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); $x = sprintf("%.0f", $$widget_ref->canvasx($x)); $x = $dna_min if $x < $dna_min; $x = $dna_max if $x > $dna_max; if ($canv eq 'qpcr') { # get DNA base $x = ($x-$dna_canvas_offset) / $dna_canvas_size; # exon selection my $last_ie = 0; my $exon_count; foreach (@intron_exon_bounds) { $exon_count++; if ($x>$last_ie && $x<$_) { select_exon($exon_count, 3); return; } } $exon_count++; select_exon($exon_count, 3); return; } $seq = clean_seq($seq); my $seq_len = length($seq)-1; my $dna_max2 = $$max_range*$dna_canvas_size + $dna_canvas_offset; my $dna_min2 = $$min_range*$dna_canvas_size + $dna_canvas_offset; # grab default values if not assigned if (($min_ampsize)&&($max_ampsize)) { $min_amp_canvas{$canv} = $$min_range * $dna_canvas_size + $dna_canvas_offset unless $min_amp_canvas{$canv}; $max_amp_canvas{$canv} = ($$max_ampsize+$$min_range) * $dna_canvas_size + $dna_canvas_offset unless $max_amp_canvas{$canv} ; } if ($x-$min_amp_canvas{$canv}<$max_amp_canvas{$canv}-$x) { # Changing amp_size at 5' end $x = $dna_min2 if $x > $dna_min2; $$widget_ref->coords('amp_range', $x, $dc_sel_y1-$dc_sel_offset2, $max_amp_canvas{$canv},$dc_sel_y2+$dc_sel_offset2); $min_amp_canvas{$canv}=$x; $$max_range_5p = sprintf("%.0f", ($x-$dna_canvas_offset) / $dna_canvas_size); } else { # Changing amp_size at 3' end $x = $dna_max2 if $x < $dna_max2; $$widget_ref->coords('amp_range', $min_amp_canvas{$canv}, $dc_sel_y1-$dc_sel_offset2, $x, $dc_sel_y2+$dc_sel_offset2); $max_amp_canvas{$canv}=$x; $$max_range_3p = sprintf("%.0f", ($x-$dna_canvas_offset) / $dna_canvas_size); $$max_range_3p = $seq_len if $$max_range_3p>$seq_len; } $$max_ampsize = sprintf("%.0f", ($max_amp_canvas{$canv}-$min_amp_canvas{$canv}) / $dna_canvas_size); } sub get_gene { my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); my $seq = get_seq(); my @gene_array = find_gene($seq); return unless @gene_array; ($$min_range, $$max_range)=($gene_array[0][0], $gene_array[$#gene_array][1]-1); $$max_range_5p ||= $$min_range; $$max_range_3p ||= $$max_range; $$max_range_5p = $$min_range if $$min_range < $$max_range_5p; $$max_range_3p = $$max_range if $$max_range > $$max_range_3p; $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range - $$min_range; # if ($max_range - $min_range)>$max_ampsize; draw_dna(1); } sub get_cpg { my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); my $seq = get_seq(); my @gene_array = find_gene($seq); if ($gene_array[0] == -1) { sbarprint("\nNo CpG Island found!"); return; } ($$min_range, $$max_range)=($gene_array[0][0], $gene_array[$#gene_array][1]-1); $$max_range_5p ||= $$min_range; $$max_range_3p ||= $$max_range; $$max_range_5p = $$min_range if $$min_range < $$max_range_5p; $$max_range_3p = $$max_range if $$max_range > $$max_range_3p; $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range - $$min_range; # if ($max_range - $min_range)>$max_ampsize; draw_dna(1); } sub reset_bounds { my $page = which_nb_page(); return if $page eq "qpcr"; my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p, $sub_ref) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p subroutine)); $$max_range_5p = $$max_range_3p = $$min_range = $$max_range = $$min_ampsize = $$max_ampsize = undef; &$sub_ref(); } sub step_in { my $step_length=shift; $step_length ||= 10; my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p, $sub_ref, $canv_ref, $seq_ref) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p subroutine canvas seq)); &$sub_ref() unless defined($$max_range_5p) && defined($$max_range_3p); $$min_range += $step_length; $$max_range -= $step_length; if ($$min_range >= $$max_range) { sbarprint("\nNo primers found"); $cancel=1; return; } $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range_3p - $$max_range_5p; draw_dna(1); } sub step_out { my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p, $sub_ref, $canv_ref, $seq_ref) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p subroutine canvas seq)); my $seq_len = length(get_seq()); &$sub_ref() unless defined($$max_range_5p) && defined($$max_range_3p); $cancel=1 if ($$max_range_5p==0) && ($$max_range_3p==$seq_len); $$max_range_5p -= 10; $$max_range_3p += 10; $$max_range_5p = 0 if $$max_range_5p < 0; $$max_range_3p = $seq_len if $$max_range_3p>$seq_len; $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range_3p - $$max_range_5p; draw_dna(1); } sub step_cloning { my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p, $sub_ref, $canv_ref, $seq_ref) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p subroutine canvas seq)); my $seq_len = length(get_seq()); &$sub_ref() unless defined($$max_range_5p) && defined($$max_range_3p); $cancel=1 if ($$max_range_5p==0) && ($$max_range_3p==$seq_len); $$max_range_5p -= 10; $$max_range -= 10; $$max_range_5p = 0 if $$max_range_5p < 0; $$max_range = $$min_range if $$max_range < $$min_range; return if $$max_range_5p == 0 && $$max_range == $$min_range; $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range_3p - $$max_range_5p; draw_dna(1); } sub add_cloning { my ($seq, $fprimer, $fprimerpos, $fprimerposl, $rprimer, $rprimerpos, $rprimerposl) = @_; # Adding an extra sequence is all very well. # But we want to automagically keep it in frame when creating # fusion constructs ... # Warning!! This is clumsy! my @gene_array=find_gene($seq); my $gene_frame=$gene_array[0][0]%3; my $insert_f = ""; my $insert_r = ""; my ($primer_seq_5f_real, $rprimer_seq_5r_real, $primer_seq_5r_real); if ($primer_seq_5f && $fprimer) { # calculate frame position for forward primer + insert $_ = $primer_seq_5f; my ($fspl1, $fspl2)=split('_'); my $renz_offset_f = 3-length($fspl2); s/[\|_]//g; $primer_seq_5f_real = $_; if (defined($primer_seq_5f_frame) && ($primer_seq_5f_frame ne "")) { my $fjoinframe = 3-$primer_seq_5f_frame; my $primer_frame_f= (($fprimerpos - $gene_frame)%3 + $fjoinframe + $renz_offset_f)%3; # Add spacers if needed between primer sequences and the inserts, # and adjust the spacing of the primerpos and primerposl variables # so that the primers are still displayed in the correct positions $insert_f = "A" x $primer_frame_f; } $fprimer = "$primer_seq_5f_real$insert_f$fprimer"; $fprimerpos -= length($insert_f)+length($primer_seq_5f_real); $fprimerposl += length($insert_f)+length($primer_seq_5f); } if ($primer_seq_5r && $rprimer) { # calculate frame position for reverse primer + insert $_ = $primer_seq_5r; my ($rspl1, $rspl2)=split('_'); # my $renz_offset_r = 3-length($rspl2); s/[\|_]//g; $primer_seq_5r_real = $_; $rprimer_seq_5r_real = reverse($primer_seq_5r_real); if (defined($primer_seq_5r_frame) && ($primer_seq_5r_frame ne "")) { my $rjoinframe = $primer_seq_5r_frame; my $primer_frame_r= (2-($rprimerpos - $gene_frame)%3 + $rjoinframe)%3; $insert_r = "A" x $primer_frame_r; } $rprimer = "$rprimer$insert_r$rprimer_seq_5r_real"; $rprimerpos += length($insert_r)+length($primer_seq_5r_real); $rprimerposl += length($insert_r)+length($primer_seq_5r_real); } return ($primer_seq_5f_real, $insert_f, $fprimer, $fprimerpos, $fprimerposl, $primer_seq_5r_real, $insert_r, $rprimer, $rprimerpos, $rprimerposl); } sub dna_magnify { # This is one long, convoluted subroutine that attempts # to please all of the people all of the time :) # A bit ungainly, but it just kept on growing ... # Design could still do with a bit of tweaking ... my ($x, $report) = @_; my $seq = get_seq(); return unless $seq =~ /[a|t|c|g]/i; my $page = which_nb_page(); my ($max_range_5p, $min_range, $max_range, $max_range_3p, $primer_array, $list_ref, $ref) = get_variables(qw(max_range_5p min_range max_range max_range_3p primers hlist canvas)); my ($fprimer, $rprimer, $fprimerpos, $rprimerpos, $fprimerposl, $rprimerposl, $primer_seq_5f_real, $insert_f, $primer_seq_5r_real, $insert_r); # get dimensions of canvas my $width = $$ref->width; my $dna_canvas_size=($width-($dna_canvas_offset*2))/length($seq); # This sub used to jump to the exact position right clicked; however, all I ever # want to do is look at the primers, so I feel the routine would be better jumping # straight to the forward primer ... # my $base_pos = sprintf("%.0f",($x-$dna_canvas_offset)/$dna_canvas_size); my $text_ref = \$packed_widgets{view_base_text}; # Local subroutines: my $copy_report = sub { # Copies the content into 80-column wrapped text my ($clip, $last_i); my $length = 80; for (my $i=$length; $i<=length($seq)+$length; $i+=$length) { $last_i ||= 0; for my $j (0 .. 4) { $clip .= $$text_ref->get("$j.$last_i","$j.$i"); $clip .= "\n"; } $clip .= "\n\n"; $last_i=$i; } return $clip; }; my $copy_printable = sub { # copy report for the clipboard ... $top->clipboardClear; my $clip = &$copy_report(); $top->clipboardAppend($clip); }; my $view_fprimer = sub { $$text_ref->see("1.".($fprimerpos+10)); }; my $view_rprimer = sub { $$text_ref->see("1.".($rprimerpos-10)); }; ### In progress!! # my $multiline_pos = sub { # my ($x,$y) = @_; # # if ($multiline) { # # # single lines to multiple lines # # # # } # }; # if previous window exists we destroy it first, to avoid confusion # (trying to simply erase the contents in the text box and # re-configuring the buttons has about the same effect with Perl/Tk, # so there's not much point in doing so ...) if (Exists($view_base)) { $view_base->destroy; } $view_base = $top->Toplevel(-title=>'Details ...'); my $view_basef = $view_base->Frame()->pack(-expand=>1, -fill=>'both'); my $view_basefb = $view_base->Frame()->pack(-side=>'left'); my $view_basefbr = $view_base->Frame()->pack(-side=>'right'); $view_base->withdraw if $report; nr(\$view_basef); pack_gui('ROText', '', 'view_base_text', 80, 4, -scrollbars=>'os', -wrap=>'none'); nr(\$view_basefb); pack_gui('Button', 'OK', 'view_base_ok', sub {$view_base->destroy}, 'active'); pack_gui('Button', 'Copy printable', 'view_base_copy', \&$copy_printable); nr(\$view_basefbr); pack_gui('Button', 'Forward', 'view_base_pf', \&$view_fprimer, 'disabled' ); pack_gui('Button', 'Reverse', 'view_base_pr', \&$view_rprimer, 'disabled' ); $$text_ref->tagConfigure('red', -foreground => 'red'); $$text_ref->tagConfigure('blue', -foreground => 'midnightblue', -background => '#cee3ee'); $$text_ref->tagConfigure('blue_utr', -foreground => '#459ecc', -background => '#dde8ee'); $$text_ref->tagConfigure('blue_cpg', -foreground => 'royalblue', -background => 'royalblue'); $$text_ref->tagConfigure('numbers', -foreground => 'grey75', -background => 'grey35'); $$text_ref->tagConfigure('grey60', -foreground => 'grey50'); $$text_ref->tagConfigure('grey40', -foreground => 'grey40'); $$text_ref->tagConfigure('black', -foreground => 'black', -background => 'grey89'); $$text_ref->tagConfigure('codon', -foreground => 'black', -background => 'grey81'); $$text_ref->tagConfigure('codon_utr', -foreground => 'black', -background => 'grey84'); $$text_ref->tagConfigure('codonbg', -foreground => 'black', -background => 'grey93'); $$text_ref->tagConfigure('orange', -foreground => 'orange'); $$text_ref->tagConfigure('dodgerblue', -foreground => 'dodgerblue'); $$text_ref->tagConfigure('ie', -foreground => 'white', -background => 'red'); for my $i (1 .. 3) { $$text_ref->insert("$i.0","\n") } # my $list_ref = which_hlist(); my @sel= $$list_ref->selectionGet; my $sel=shift(@sel); # Selected primers, if any ... if (defined($sel)) { $fprimer = $$primer_array[$sel][0]; $rprimer = reverse($$primer_array[$sel][4]) if $$primer_array[$sel][5]; $fprimerpos = $$primer_array[$sel][1]; $rprimerpos = $$primer_array[$sel][8]; $fprimerposl = $$primer_array[$sel][2]; $rprimerposl = $$primer_array[$sel][6]; # need to do this before adding sequences ... my $spacerrp = " "x($rprimerpos-$rprimerposl-$fprimerpos-$fprimerposl-5) if $rprimer; # Added sequences? if ($page eq 'pd' && (($primer_seq_5f) || ($primer_seq_5r))) { ($primer_seq_5f_real, $insert_f, $fprimer, $fprimerpos, $fprimerposl, $primer_seq_5r_real, $insert_r, $rprimer, $rprimerpos, $rprimerposl) = add_cloning($seq, $fprimer, $fprimerpos, $fprimerposl, $rprimer, $rprimerpos, $rprimerposl); } my $spacerfp = " "x($fprimerpos-3); # print "($rprimerpos-$rprimerposl-$fprimerpos-$fprimerposl-5) = ".($rprimerpos-$rprimerposl-$fprimerpos-$fprimerposl-5); $$text_ref->insert('2.0',$spacerfp."5' ", 'grey60'); $$text_ref->insert('2.end',$fprimer, 'red'); if ($rprimer) { $$text_ref->insert('2.end'," 3'".$spacerrp."3' ", 'grey60'); $$text_ref->insert('2.end',$rprimer, 'red'); $$text_ref->insert('2.end'," 5'", 'grey60'); } else { $$text_ref->insert('2.end'," 3'", 'grey60'); } # Make buttons active or inactive: $packed_widgets{view_base_pf}->configure(-state=>($fprimer ? 'active' : 'disabled')); $packed_widgets{view_base_pr}->configure(-state=>($rprimer ? 'active' : 'disabled')); } # Numbering along the top (starts at 0) my $numbers="0........."; for my $i(1 .. length($seq)/10) { $numbers.=($i*10)."."x(9-length($i)); } # insert the numbers and the sequence $$text_ref->insert('1.0',$numbers,'numbers'); $$text_ref->insert('3.0',$seq, 'grey40'); # translation of ORFs # (or if Bisulphite sequencing highlights CpG island) my @gene_array=find_gene($seq); my $prev_end = 0; for my $i (0 .. $#gene_array) { my $peptide=""; my ($start, $end)=($gene_array[$i][0],$gene_array[$i][1]); # if f_primer starts before ORF, translate from there up my ($new_start, $new_peptide); if ($fprimerpos && $fprimerpos < $start) { my $start_mod = $start%3; my $fprimer_mod = $fprimerpos%3; $new_start = $fprimerpos + ($start_mod-$fprimer_mod); # $start = $new_start; } $$text_ref->tagAdd('black', "3.$start", "3.$end"); $$text_ref->tagAdd('black', "3.$new_start", "3.$end") if $new_start; if ($page eq 'bis') { $peptide = "-"x($end-$start); } else { my ($j,$k)=(0,0); if ($new_start) { for ($j=$new_start; $j<$start; $j+=3) { my $codon = uc(substr($seq, $j, 3)); my $aa = $genetic_code{$codon}; $aa ||= "X"; $new_peptide .= $aa." "; if ($k == 1) { my $tend = $j+3; $$text_ref->tagAdd('codon_utr', "3.$j", "3.$tend"); } $k = 1 - $k; } } for ($j=$start; $j<$end; $j+=3) { my $codon = uc(substr($seq, $j, 3)); my $aa = $genetic_code{$codon}; $aa ||= "X"; $peptide .= $aa." "; if ($k == 1) { my $tend = $j+3; $$text_ref->tagAdd('codon', "3.$j", "3.$tend"); } $k = 1 - $k; } } $start = $new_start if $new_start; my $spacer=" "x($start-$prev_end); $prev_end=$end; $$text_ref->insert("4.0",$spacer); unless ($page eq 'bis') { $$text_ref->insert("4.end",$new_peptide,'blue_utr') if $new_peptide; $$text_ref->insert("4.end",$peptide,'blue'); } else { $$text_ref->insert("4.end",$peptide,'blue_cpg'); } } # highlight intron-exon boundaries for QPCR # or ranges otherwise if ($page eq 'qpcr') { foreach my $i (@intron_exon_bounds) { my $tag_start=$i-1; my $tag_end=$i+1; $$text_ref->tagAdd('ie', "3.$tag_start", "3.$tag_end"); } } else { my $sel_range=$$max_range+1; my $sel_range_3p=$$max_range_3p+1 if $$max_range_3p; $$text_ref->tagAdd('orange', "3.$$max_range_5p", "3.$sel_range_3p") if $$max_range_3p; $$text_ref->tagAdd('dodgerblue', "3.$$min_range", "3.$sel_range"); } if ($page eq 'bis') { # CpG labeling for my $i (0 .. length($seq)) { if (substr($seq, $i, 2) =~ /CG|cg/ ) { my $tagcpg = $i+2; $$text_ref->tagAdd('ie', "3.$i", "3.$tagcpg"); } } } # this is very inefficient and messy, but gets the job done ... if ($report) { my $sequence_report = &$copy_report; $view_base->destroy; return $sequence_report; } # icon $view_base->Icon(-image => $pixmap); # Show the position where the user clicked # my $center_base_pos = $base_pos-30; # $$text_ref->see("1.$center_base_pos"); # Show the forward primer if it exists if ($fprimerpos) { my $center_base_pos = $fprimerpos-30; $$text_ref->see("1.$center_base_pos"); } } #---------------# # File commands # #---------------# sub pp_file_open { # NB - there's several reasons why we're not using XML as a file format here. # The first is simplicity, both for the programmer and for the end-user (who # would have to download and install a CPAN module otherwise - which might be # daunting to many). The second is that I can't abide XML. # The system that follows is both simple and highly extensible (adding # support for another varible simple entails a single line in either the # %variables or %arrays hashes - everything else is automatic ... ) my ($file, $widget_ref, @file_data) = @_; $file ||= $top->getOpenFile(-filetypes=>$file_types); return unless defined($file); unless (@file_data) { unless (open (SEQ, "<$file")) { dialogue("Could not open file $file: $!"); return; } sbarprint("\nOpening $file ..."); @file_data = ; close SEQ; } else { sbarprint("\nReading data from port $tcp_port ..."); } # clear previous selections: # new_file(); my ($nb_page, $name) = open_file_type($file, $widget_ref, @file_data); unless ($nb_page) { $cancel = 0; return; } # redraw dna draw_dna(); # reset qpcr_flag $qpcr_flag = 0; sbarprint("\n$file opened successfully"); $file =~ s/\//\\/g if $os eq 'win'; # path bug in Win32 Perl/Tk # since we're taking the name from FASTA files directly ... my $title_filename = ($name ? $name : $file); # my $full_path = $file; # $file =~ s/.*[\/\\]//g; $top->configure(-title=>"PerlPrimer v$version - $title_filename"); $open_file{$nb_page} = $title_filename; recently_used_files($file); } sub open_file_type { my ($file, $widget_ref, @file_data) = @_; my ($page, $name); foreach (@file_data) { # This loop is in case the first line is blank. # It's probably unnecessary .. next if /^$/; if (/nb = .+/) { # file appears to be a perlprimer file return ($page, $name) = open_ppr($file, @file_data); } elsif (/^\>.+/) { # file appears to be FASTA format return ($page, $name) = open_fasta($file, $widget_ref, @file_data); # } elsif (/[efijlopqz\d]+/i) { # # file is unknown format # dialogue("File does not appear to be a PerlPrimer file or in FASTA format.\nIf you are trying to open a DNA sequence file, please use the open icon next to the sequence entry field"); # last; # } else { # return ($page, $name) = open_fasta($file, @file_data); } } } sub open_ppr { my $file = shift; my @file_data = @_; my $nb_page; my $array_flag=0; @save_selection=(); $save_seq=$save_seq2=""; my %flag=(); my $pointer; my ($key, $value, $lines, $open_percent); my $total_lines = @file_data + 1; foreach (@file_data) { # we're supporting both *nix and win32 ... s/[\n\r]//g; $open_percent = sprintf("%.f", $lines++/$total_lines*100); sbarprint("\nOpening $file ... $open_percent\%"); # get nb_page entry (NB: must come before all data entry) # or look for [array] section if (/nb = (.*)/) { $nb->raise($1); $top->update; new_file(); if ($cancel) { sbarprint("\nFile open cancelled"); return; } $nb_page = $1; next; } elsif (/\[arrays\]/) { $array_flag=1; next; } # ignore blank lines next if /^$/; # grab values # my ($key, $value) = split / = /; if (/ = /) { ($key, $value) = split / = /; } elsif ($array_flag==0) { # allow multi-line variables - eg for genomic DNA $$pointer .= "\n$_"; next; } # set variables unless ($array_flag==1) { $pointer = $variables{$nb_page}{$key}; $$pointer = $value; next; } # array parsing $pointer = $arrays{$nb_page}{$key}; unless ($flag{$key}) { # clear the array @$pointer=(); $flag{$key}=1; } # this is so easy! my @temp = split(/ /,$value); if ($#temp == 0) { push (@$pointer, $temp[0]); } else { push (@$pointer, [@temp]); } } # restore sequence(s) my ($seq_ref, $hlist) = get_variables(qw(seq hlist)); $$seq_ref->delete(0.1,"end"); $$seq_ref->insert(0.1,$save_seq); if ($save_seq2) { # only for qpcr $packed_widgets{qdna_seq}->delete(0.1,"end"); $packed_widgets{qdna_seq}->insert(0.1,$save_seq2); } # restore results sort_primers(); # restore intron-exon boundaries if qpcr $qpcr_flag = 1 if $nb_page eq 'qpcr'; # restore selection for my $i (0 .. $#save_selection) { $$hlist->selectionSet("$save_selection[$i]"); } if (@save_selection) { browse_primer($save_selection[$#save_selection]); $$hlist->see($save_selection[$#save_selection]); } return $nb_page; } sub open_fasta { my ($file, $widget_ref, @file_data) = @_; my ($flag, $dna, $name, $range_5a, $range_5b, $range_3a, $range_3b, $page); my $nb_page = which_nb_page(); my ($key, $value, $lines, $open_percent); my $total_lines = @file_data + 1; foreach (@file_data) { $open_percent = sprintf("%.f", $lines++/$total_lines*100); sbarprint("\nOpening $file ... $open_percent\%"); next if /^$/; # if ($flag) { # $dna .= "$_"; # next; # } elsif (/^\>/) { if (/^\>/) { next if $flag; ($name, $range_5a, $range_5b, $range_3a, $range_3b, $page) = /^\>\s*(.+?)\s*(?:5prime_region\[(\d+)-(\d+)\])?\s*(?:3prime_region\[(\d+)-(\d+)\])?\s*(?:page\[(\d+)\])?\s*$/; # process name (remove whitespace, other illegal filename chars) $name = format_file_name($name); # page defaults to 1 if there's other info that suggests this is a socket file $page ||= 1 if ($range_5a || $range_5b || $range_3a || $range_3b); $nb_page = $nb_page_ref{$page} if $page; $nb->raise($nb_page); $top->update; if ($cancel) { sbarprint("\nFile open cancelled"); return; } $flag = 1; } else { $dna .= "$_"; } } # insert dna sequence if present if ($dna && $dna =~ /[agct]/i) { ($widget_ref) = get_variables(qw(seq)) unless ref($widget_ref); $_ = $$widget_ref->get(0.1,"end"); # New file if we're overwriting a previous sequence my ($seq_check) = /atcg/i; new_file($widget_ref) if $seq_check; $$widget_ref->delete(0.1,"end"); $$widget_ref->insert(0.1,$dna); } # set ranges if specified my ($min_ampsize, $max_ampsize, $max_range_5p, $min_range, $max_range, $max_range_3p) = get_variables(qw(min_ampsize max_ampsize max_range_5p min_range max_range max_range_3p)); if ($range_5a && $range_5b && $range_3a && $range_3b) { $$max_range_5p = $range_5a; $$min_range = $range_5b; $$max_range = $range_3a; $$max_range_3p = $range_3b; $$min_ampsize=$$max_range - $$min_range; $$max_ampsize=$$max_range_3p - $$max_range_5p; } # set range to ORF if not reset_bounds() unless $$min_range && $$max_range; return ($nb_page, $name); } sub format_file_name { my $name = shift; $name =~ s/\s+/_/g; # replace spaces with underscores $name =~ s/[\(\)\,\.\;]//g; # remove brackets and punctuation $name =~ s/\|/-/g; # replace pipes with dashes $name =~ s/_$//g; # remove final, terminating underscore $name =~ s/-_/-/g; # fix -_ problems return $name; } sub pp_file_save { my $nb_page = which_nb_page(); if ($nb_page eq "primer") { dialogue("You can't save a file from the primer information page - please save from the project page instead"); return; } # Either prompt for file name if file not saved or "Save as ..." was # called, or save without prompting if file has been saved before # (It's amazing how complicated "simple" behaviour can be ...) my ($flag) = @_ || 0; my $file; if ($open_file{$nb_page} eq 'File not saved') { $file = $top->getSaveFile(-filetypes=>$file_types, -defaultextension=>'.ppr'); } elsif (($flag == 1) || ($open_file{$nb_page} !~ /\.ppr/)) { $file = $top->getSaveFile(-filetypes=>$file_types, -defaultextension=>'.ppr', -initialfile=>$open_file{$nb_page}); } else { $file = $open_file{$nb_page}; } return unless defined($file); $file .= '.ppr' unless $file =~ /\.ppr/; sbarprint("\nSaving $file ..."); my $total_keys = keys(%{ $variables{$nb_page} }) + keys(%{ $arrays{$nb_page} }) + 1; my ($keys_saved, $saved_percent); # Now the guts of the saving routine - see the pp_file_open routine for general # comments ... this is all straightforward stuff # save sequence data $save_seq = get_seq(); $save_seq2 = $packed_widgets{qdna_seq}->get(0.1,"end") if $nb_page eq "qpcr"; # save selection data my ($hlist) = get_variables('hlist');; @save_selection = $$hlist->selectionGet; # save variables my $file_data = "nb = $nb_page\n"; my $pointer; foreach my $i (keys %{ $variables{$nb_page} }) { $pointer = $variables{$nb_page}{$i}; $file_data .= "$i = $$pointer\n" if defined($$pointer); $saved_percent = sprintf("%.f", $keys_saved++/$total_keys*100); sbarprint("\nSaving $file ... $saved_percent\%"); } # save arrays $file_data .= "[arrays]\n"; foreach my $i (keys %{ $arrays{$nb_page} }) { $pointer = $arrays{$nb_page}{$i}; for my $j (0 .. $#$pointer) { unless (ref($$pointer[$j])) { $file_data .= "$i = $$pointer[$j]\n"; } else { $file_data .= "$i = @{$$pointer[$j]}\n"; } } $saved_percent = sprintf("%.f", $keys_saved++/$total_keys*100); sbarprint("\nSaving $file ... $saved_percent\%"); } # write file open (SEQ, ">$file") || dialogue("Could not open file for saving: $!\n"); print SEQ $file_data; close (SEQ); sbarprint("\n$file saved successfully"); # Set the program title to reflect the new file name # $file =~ s/.*\/.*\///g; $file =~ s/\//\\/g if $os eq 'win'; # path bug in Win32 Perl/Tk $top->configure(-title=>"PerlPrimer v$version - $file"); $open_file{$nb_page} = $file; recently_used_files($file); } sub recently_used_files { # recently used files my ($file) = @_; if ($file) { for my $i (0 .. $#mru) { $mru[$i] = "" if $mru[$i] eq $file; } push(@mru, $file); } # the subroutine gets called when the program loads, so we need an escape return if @mru == 0; # clean up the space from before if it exists my @new_mru; for my $i (0 .. $#mru) { push(@new_mru, $mru[$i]) if ($mru[$i]); } # take only the last requested files so the list doesn't grow too big my $start = $#new_mru-($mru_number-1)<0 ? 0 : $#new_mru-($mru_number-1); my $end = $#new_mru; @mru = @new_mru[$start .. $end]; # clear the old menu $menu_mru->delete(1,'end'); # ... and insert the new list for my $i (0 .. $#mru) { $menu_mru->insert(0,'command', -label=>"$mru[$i]", -command =>[sub {pp_file_open($mru[$i])}] ); } } sub open_seq { # opens file and writes contents to text widget my ($widget_ref) = @_; my $file = $top->getOpenFile(-filetypes=>$file_types_dna); if (defined($file)) { pp_file_open($file, $widget_ref); } } sub save_seq { # saves sequence in text widget to file my ($widget_ref) = @_; my $file = $top->getSaveFile(-filetypes=>$file_types_dna, -defaultextension=>'.fasta'); my $seq = $$widget_ref->get(0.1, 'end'); my $nb_page = which_nb_page(); if (defined($file)) { my $out; if ($file =~ /\.fasta$/) { my $description = $open_file{$nb_page}; $description = $file if $description eq 'File not saved'; $description =~ s/.*[\\\/]//g; # open description window my $descript_w = $top->Toplevel(-title=>'Enter FASTA description line'); nr(\$descript_w); pack_gui('Entry',\$description, 'description_text', 35); pack_gui('Button', 'OK', 'description_ok', sub {$descript_w->destroy}, 'active'); $descript_w->Icon(-image => $pixmap); $descript_w->grab; $descript_w->waitWindow; my $fasta = ">$description\n$seq"; $out = $fasta; } else { $out = $seq; } open (SEQ, ">$file") || dialogue("Could not open file: $!"); print SEQ $out; close (SEQ); sbarprint("\n$file saved successfully"); } } #------------------# # Text subroutines # #------------------# sub clear_text { my $ref = $_[0]; $$ref->delete(0.1,'end'); } sub select_all_text { my $ref = $_[0]; $$ref->selectAll; } sub lc_text { my $ref = $_[0]; my $text = $$ref->get(0.1,"end"); my $text_lc = lc($text); $$ref->delete(0.1,'end'); $$ref->insert(0.1,$text_lc); } sub reverse_complement { my $ref = $_[0]; my $text = $$ref->get(0.1,"end"); $text =~ s/[\n\s]//g; my $text_comp_rev = reverse(complement($text)); $$ref->delete(0.1,'end'); $$ref->insert(0.1,$text_comp_rev); } #----------------# # Reference subs # #----------------# sub get_seq { my ($seq_ref) = get_variables('seq'); return 1 unless $$seq_ref; $_ = $$seq_ref->get(0.1,"end"); $_ = clean_seq($_); # s/(\>.*\n)//g; #remove FASTA formatting if it exists # if ($1 && !$open_file{$nb_page}) { # # if FASTA details are present ... # my $name = format_file_name($1); # # $open_file{$nb_page} = $name; # $top->configure(-title=>"PerlPrimer v$version - $name"); # } # # s/[\n\r]//g; # remove line breaks return $_; } sub which_nb_page { return $nb->raised; } sub get_variables { my $note_page = which_nb_page(); $null = undef; my @return_args; foreach my $var (@_) { if ($page_specific_vars{$note_page}{$var}) { push @return_args, $page_specific_vars{$note_page}{$var}; } else { push @return_args, \undef; } } return @return_args; } sub check_packages { my $failed; foreach (@_) { if ($failed_packages =~ / $_ /) { dialogue("PerlPrimer requires the package $_ for this feature to work\n\nPlease download and install $_ from CPAN (http://cpan.org)"); $failed = 1; } } return 1 if $failed; } sub check_packages_no_warn { my $failed; foreach (@_) { if ($failed_packages =~ / $_ /) { $failed = 1; } } return 1 if $failed; } #----------------------# # Balloon state toggle # #----------------------# sub balloon_toggle { $Balloon->configure(-state => 'none') if $balloon_help == 0; $Balloon->configure(-state => 'balloon') if $balloon_help == 1; } sub check_path { $_ = shift; my $regexp_sep = "\\"."$dir_sep"; return $_.$dir_sep unless m/$regexp_sep$/; return $_; } sub load_data { #----- # # NN thermodynamics hashes (AA = 5' AA 3'/3' TT 5') derived from ... # # Allawi HT, SantaLucia J Jr. Thermodynamics and NMR of internal G.T mismatches in DNA. # Biochemistry. 1997 Aug 26;36(34):10581-94 # # SantaLucia J Jr. A unified view of polymer, dumbbell, and oligonucleotide DNA nearest-neighbor thermodynamics. # Proc Natl Acad Sci U S A. 1998 Feb 17;95(4):1460-5. # # ... with mismatch dG data (AGTG = 5' AG 3'/3' TG 5') derived from ... # # Peyret N, Seneviratne PA, Allawi HT, SantaLucia J Jr. Nearest-neighbor thermodynamics and NMR of DNA sequences with internal A.A, C.C, G.G, and T.T mismatches. # Biochemistry. 1999 Mar 23;38(12):3468-77. # # Allawi HT, SantaLucia J Jr. Nearest-neighbor thermodynamics of internal A.C mismatches in DNA: sequence dependence and pH effects. # Biochemistry. 1998 Jun 30;37(26):9435-44. # # Allawi HT, SantaLucia J Jr. Thermodynamics of internal C.T mismatches in DNA. # Nucleic Acids Res. 1998 Jun 1;26(11):2694-701. # # Allawi HT, SantaLucia J Jr. Nearest neighbor thermodynamic parameters for internal G.A mismatches in DNA. # Biochemistry. 1998 Feb 24;37(8):2170-9. # # Allawi HT, SantaLucia J Jr. Thermodynamics and NMR of internal G.T mismatches in DNA. # Biochemistry. 1997 Aug 26;36(34):10581-94 # #----- #-------------------# # deltaH (kcal/mol) # #-------------------# %oligo_dH=qw( AA -7.9 TT -7.9 AT -7.2 TA -7.2 CA -8.5 TG -8.5 GT -8.4 AC -8.4 CT -7.8 AG -7.8 GA -8.2 TC -8.2 CG -10.6 GC -9.8 GG -8.0 CC -8.0 initC 0.1 initG 0.1 initA 2.3 initT 2.3 ); %oligo_dH_full=( qw(AATT -7.9 TTAA -7.9 ATTA -7.2 TAAT -7.2 CAGT -8.5 TGAC -8.5 GTCA -8.4 ACTG -8.4 CTGA -7.8 AGTC -7.8 GACT -8.2 TCAG -8.2 CGGC -10.6 GCCG -9.8 GGCC -8.0 CCGG -8.0 initC 0.1 initG 0.1 initA 2.3 initT 2.3), # Like pair mismatches qw(AATA 1.2 ATAA 1.2 CAGA -0.9 AGAC -0.9 GACA -2.9 ACAG -2.9 TAAA 4.7 AAAT 4.7 ACTC 0.0 CTCA 0.0 CCGC -1.5 CGCC -1.5 GCCC 3.6 CCCG 3.6 TCAC 6.1 CACT 6.1 AGTG -3.1 GTGA -3.1 CGGG -4.9 GGGC -4.9 GGCG -6.0 GCGG -6.0 TGAG 1.6 GAGT 1.6 ATTT -2.7 TTTA -2.7 CTGT -5.0 TGTC -5.0 GTCT -2.2 TCTG -2.2 TTAT 0.2 TATT 0.2 ), # G.T mismatches qw(AGTT 1.0 TTGA 1.0 ATTG -2.5 GTTA -2.5 CGGT -4.1 TGGC -4.1 CTGG -2.8 GGTC -2.8 GGCT 3.3 TCGG 3.3 GGTT 5.8 TTGG 5.8 GTCG -4.4 GCTG -4.4 GTTG 4.1 GTTG 4.1 TGAT -0.1 TAGT -0.1 TGGT -1.4 TGGT -1.4 TTAG -1.3 GATT -1.3), # G.A mismatches qw(AATG -0.6 GTAA -0.6 AGTA -0.7 ATGA -0.7 CAGG -0.7 GGAC -0.7 CGGA -4.0 AGGC -4.0 GACG -0.6 GCAG -0.6 GGCA 0.5 ACGG 0.5 TAAG 0.7 GAAT 0.7 TGAA 3.0 AAGT 3.0), # C.T mismatches qw(ACTT 0.7 TTCA 0.7 ATTC -1.2 CTTA -1.2 CCGT -0.8 TGCC -0.8 CTGC -1.5 CGTC -1.5 GCCT 2.3 TCCG 2.3 GTCC 5.2 CCTG 5.2 TCAT 1.2 TACT 1.2 TTAC 1.0 CATT 1.0), # A.C mismatches qw(AATC 2.3 CTAA 2.3 ACTA 5.3 ATCA 5.3 CAGC 1.9 CGAC 1.9 CCGA 0.6 AGCC 0.6 GACC 5.2 CCAG 5.2 GCCA -0.7 ACCG -0.7 TAAC 3.4 CAAT 3.4 TCAA 7.6 AACT 7.6), ); #--------------------# # deltaS (cal/K.mol) # #--------------------# %oligo_dS=qw( AA -22.2 TT -22.2 AT -20.4 TA -21.3 CA -22.7 TG -22.7 GT -22.4 AC -22.4 CT -21.0 AG -21.0 GA -22.2 TC -22.2 CG -27.2 GC -24.4 GG -19.9 CC -19.9 initC -2.8 initG -2.8 initA 4.1 initT 4.1 sym -1.4 ); %oligo_dS_full=( qw(AATT -22.2 TTAA -22.2 ATTA -20.4 TAAT -21.3 CAGT -22.7 TGAC -22.7 GTCA -22.4 ACTG -22.4 CTGA -21.0 AGTC -21.0 GACT -22.2 TCAG -22.2 CGGC -27.2 GCCG -24.4 GGCC -19.9 CCGG -19.9 initC -2.8 initG -2.8 initA 4.1 initT 4.1 sym -1.4), # Like pair mismatches qw(AATA 1.7 ATAA 1.7 CAGA -4.2 AGAC -4.2 GACA -9.8 ACAG -9.8 TAAA 12.9 AAAT 12.9 ACTC -4.4 CTCA -4.4 CCGC -7.2 CGCC -7.2 GCCC 8.9 CCCG 8.9 TCAC 16.4 CACT 16.4 AGTG -9.5 GTGA -9.5 CGGG -15.3 GGGC -15.3 GGCG -15.8 GCGG -15.8 TGAG 3.6 GAGT 3.6 ATTT -10.8 TTTA -10.8 CTGT -15.8 TGTC -15.8 GTCT -8.4 TCTG -8.4 TTAT -1.5 TATT -1.5), # G.T mismatches qw(AGTT 0.9 TTGA 0.9 ATTG -8.3 GTTA -8.3 CGGT -11.7 TGGC -11.7 CTGG -8.0 GGTC -8.0 GGCT 10.4 TCGG 10.4 GGTT 16.3 TTGG 16.3 GTCG -12.3 GCTG -12.3 GTTG 9.5 GTTG 9.5 TGAT -1.7 TAGT -1.7 TGGT -6.2 TGGT -6.2 TTAG -5.3 GATT -5.3), # G.A mismatches qw(AATG -2.3 GTAA -2.3 AGTA -2.3 ATGA -2.3 CAGG -2.3 GGAC -2.3 CGGA -13.2 AGGC -13.2 GACG -1.0 GCAG -1.0 GGCA 3.2 ACGG 3.2 TAAG 0.7 GAAT 0.7 TGAA 7.4 AAGT 7.4), # C.T mismatches qw(ACTT 0.2 TTCA 0.2 ATTC -6.2 CTTA -6.2 CCGT -4.5 TGCC -4.5 CTGC -6.1 CGTC -6.1 GCCT 5.4 TCCG 5.4 GTCC 13.5 CCTG 13.5 TCAT 0.7 TACT 0.7 TTAC 0.7 CATT 0.7), # A.C mismatches qw(AATC 4.6 CTAA 4.6 ACTA 14.6 ATCA 14.6 CAGC 3.7 CGAC 3.7 CCGA -0.6 AGCC -0.6 GACC 14.2 CCAG 14.2 GCCA -3.8 ACCG -3.8 TAAC 8.0 CAAT 8.0 TCAA 20.2 AACT 20.2), ); # Genetic code hash %genetic_code=qw( TTT F TTC F TTA L TTG L CTT L CTC L CTA L CTG L ATT I ATC I ATA I ATG M GTT V GTC V GTA V GTG V TCT S TCC S TCA S TCG S CCT P CCC P CCA P CCG P ACT T ACC T ACA T ACG T GCT A GCC A GCA A GCG A TAT Y TAC Y TAA * TAG * CAT H CAC H CAA Q CAG Q AAT N AAC N AAA K AAG K GAT D GAC D GAA E GAG E TGT C TGC C TGA * TGG W CGT R CGC R CGA R CGG R AGT S AGC S AGA R AGG R GGT G GGC G GGA G GGG G ); } #----------------------# # Icon and pixmap data # #----------------------# # ... just a little bit of bloat :) sub load_icon_data { $perlprimer_icon = <<'end_of_pixmap'; /* XPM */ static char * dna_icon2_xpm[] = { "32 32 25 1", " c #FFFFFF", ". c #402E33", "+ c #A0302D", "@ c #918FC4", "# c #B89590", "$ c #CAC7EE", "% c #E4B82A", "& c #969096", "* c #E3382B", "= c #924E32", "- c #E76029", "; c #EFD4C7", "> c #69647F", ", c #F9FBF8", "' c #DD6C69", ") c #80696A", "! c #C0817E", "~ c #9F6160", "{ c #E08B27", "] c #F7B6B3", "^ c #D2D1CF", "/ c #B3AEB2", "( c #F92C2B", "_ c #AEACF1", ": c #E7A19D", " *(;,,;:>@_>##!((;=((+ ", " -;,,/>@_@/^,;(-;)=. ", " +*~&#/__&,,,-(({ ", " .!,::#.>*',;*(** ", " ),;~&,'--,,~=++ ", " &/&,!(*%:^) ", " +#,'*({(#& ", " .(:,^+=**/^. ", " +(%%],,#&&&)++ ", " +({*(!&>>....~ ", " +#+{(()$@@@>)!^^ ", " +%;^/))/$____::,,!(+ ", " ({(*;/&&&@)>)!#,,/*+ ", " +-((~^^,__@:],@$^,;= ", " .=~&,,,___:]___,#+*+ ", " =*#/&&&&>.!]$_$^!(*** ", " +**+#__#:@@/^//,,]'%* ", " ++./__::__,,,^+'#/{ ", " &/@#!@_$,^'*(-(+ ", " !,##>&&^/,]({{*+ ", " &,:@_@~'/^,,{{+ ", " /~>@@**-(~,:+ ", " ).&(%-(*,) ", " )#^^,;%{*~/&) ", " (-*+,,'(~))&/. ", " {((*;,;+))!))+ ", " %-(',,,>)!&^^'= ", " =*=!](*!,/>>)~#,,/*+ ", " **(;,''~.>>>&:];':(* ", " *(*,,,^>>@@_$/^'**++ ", " +=*{,,/)~@__>^,,,-* ", " *(''*!)#~.>@$]];,;((+ "}; end_of_pixmap $icon_open = <<'end_of_pixmap'; /* XPM */ static char * open_20_xpm[] = { "20 20 27 1", " c None", ". c #020501", "+ c #37372E", "@ c #5B5D5A", "# c #8E8F7E", "$ c #BABCAC", "% c #B8BB98", "& c #ACB0A3", "* c #1A1B18", "= c #43433B", "- c #CDD0AD", "; c #737561", "> c #A1A68A", ", c #898F73", "' c #D7D9C6", ") c #525548", "! c #AEB293", "~ c #C4C7A4", "{ c #242521", "] c #61624E", "^ c #808673", "/ c #C2C5B4", "( c #959B7E", "_ c #D5D8B6", ": c #B1B39D", "< c #999B8E", "[ c #6C6D59", " ", " ", " ..... ", " +#<<<^= ", " @____~[{ ", "=$_~~~-(+ ", "=/%!!%%>;======+ ", "+->(>>>>>>:::::#. ", "{/,)+++++++++++{.. ", "{:]<$$$///$////$$$@ ", "{$=&__---_-----~~!) ", "{<<'_---------~%!,+ ", "{<<'-~--~~~~%~%:>]* ", "*&/~%%%%%%%!!>>>^+ ", "*$/!!>>>>>>>((,,]. ", "{&,^;;;;[[[]]]))+ ", " ............... ", " ", " ", " "}; end_of_pixmap $icon_open_small = <<'end_of_pixmap'; /* XPM */ static char * open_xpm[] = { "18 18 64 1", " c None", ". c #000000", "+ c #E4E5DF", "@ c #D5D6CB", "# c #D6D7CA", "$ c #A3A39D", "% c #F5F6F0", "& c #8D907B", "* c #92957E", "= c #90937D", "- c #979B84", "; c #6D705F", "> c #EAECDB", ", c #8A8C7D", "' c #8E917B", ") c #91947F", "! c #8B8E7A", "~ c #999B87", "{ c #919480", "] c #989B86", "^ c #B1B4A2", "/ c #A2A394", "( c #F7F7F7", "_ c #878A75", ": c #666858", "< c #4B4D3F", "[ c #4D4F40", "} c #404135", "| c #424337", "1 c #434437", "2 c #404236", "3 c #3C3D32", "4 c #48493C", "5 c #1A1A16", "6 c #C6C6BE", "7 c #848672", "8 c #25261F", "9 c #F1F2E9", "0 c #DDE0C7", "a c #D6DABB", "b c #CDD2AC", "c c #C7CCA7", "d c #989C80", "e c #C6C7BE", "f c #5F6152", "g c #888980", "h c #A7AB8C", "i c #878A70", "j c #9FA19A", "k c #EFF0E5", "l c #9EA284", "m c #80817B", "n c #96968D", "o c #E3E5D1", "p c #83866D", "q c #97998D", "r c #EDEFE2", "s c #A2A688", "t c #767671", "u c #E7E9DA", "v c #D1D3BD", "w c #BBBF9D", "x c #989B80", "y c #6E715C", " ", " ", " ", " .... ", " .+@#$. ", " .%&*=-;..... ", " .>,')!~{]{^/. ", " .(_:<[}||12345 ", " .67890abbbbbcd. ", " .efg0bbbbbbbhi. ", " .j8kabbbbbbbl. ", " .mnobbbbbbbbp. ", " .qrbbbbbbbbs. ", " .tuvwwwwwwxy. ", " ........... ", " ", " ", " "}; end_of_pixmap $icon_save = <<'end_of_pixmap'; /* XPM */ static char * save_20_xpm[] = { "20 20 27 1", " c None", ". c #1B2226", "+ c #728BA0", "@ c #ADC9E0", "# c #424B54", "$ c #CA766B", "% c #5F7688", "& c #E3E5E1", "* c #8DAAC4", "= c #2A3138", "- c #AEAEAC", "; c #878A89", "> c #656764", ", c #DF9A8E", "' c #4E3F3B", ") c #55534E", "! c #DCDDDA", "~ c #7D8690", "{ c #3B424A", "] c #F8FAF7", "^ c #C7C6C1", "/ c #6E757D", "( c #4A5E6D", "_ c #7F98AD", ": c #757879", "< c #9BBAD0", "[ c #F5E8E8", " ", " ", " .{='''''''''''={. ", " #@+,,,,,,,,,,,/*# ", " {<%,$$$$$$$$$$/_= ", " #<~![[[[[[[[[[/+= ", " {<~!]]]]][&[[[:_= ", " {<~^!&!!&!&&!!/+= ", " {:-!&&:(%~. ", " .==))>>>::>=={(. ", " "}; end_of_pixmap $icon_clear = <<'end_of_pixmap'; /* XPM */ static char * clear_20_xpm[] = { "20 20 27 1", " c None", ". c #000100", "+ c #504B3D", "@ c #989981", "# c #332B22", "$ c #6D7163", "% c #ADB8A6", "& c #1E1E1B", "* c #CEBF94", "= c #8D745E", "- c #554537", "; c #A6AA95", "> c #847B60", ", c #3D3E32", "' c #5A5C4F", ") c #BDA793", "! c #B5AB88", "~ c #E3D3A4", "{ c #CFBAAA", "] c #48382B", "^ c #282017", "/ c #6A634C", "( c #797E6C", "_ c #6D5644", ": c #A38E79", "< c #C9BE9F", "[ c #11100E", " ", " ", " ^-& ", " -)_. ", " ]))^ ", " ^={_. ", " #=:^ ", " #=_^ .. ", " ]_- .['& ", " ]_[(@%, . ", " -;%%$+# .", " &$%%$>*' . . ", " &(%%$>~~~>@>.. .", " [$'>~<~!'(/.. ", " .'*~)@;@^',.. ", " #>!!/]'+. ", " [,+,&.. ", " ", " "}; end_of_pixmap $icon_info = <<'end_of_pixmap'; /* XPM */ static char * info_20_xpm[] = { "20 20 147 2", " c None", ". c #ABABB1", "+ c #D6D6DE", "@ c #E5E5EF", "# c #E5E5F2", "$ c #D8D8E6", "% c #BBBBC9", "& c #6F7078", "* c #CECED4", "= c #F4F4F9", "- c #F8F8FB", "; c #F0F0F7", "> c #E9E9F4", ", c #E4E4F2", "' c #DEDFF0", ") c #D7D8EA", "! c #8B8B97", "~ c #C4C4CA", "{ c #F6F6FB", "] c #FCFCFD", "^ c #F9F9FC", "/ c #F1F1F8", "( c #ECECF6", "_ c #E6E6F3", ": c #E0E1F1", "< c #DADDEF", "[ c #D6D9EB", "} c #6C6D77", "| c #EEEEF3", "1 c #FBFBFD", "2 c #F3F3F9", "3 c #EEEEF7", "4 c #E8E8F5", "5 c #E2E2F2", "6 c #D7DBED", "7 c #C5C8DA", "8 c #BFBFC4", "9 c #FAFAFC", "0 c #F6F6FA", "a c #F2F3F9", "b c #E7E8F5", "c c #DCE0EF", "d c #D6DCEC", "e c #D5DBED", "f c #55575E", "g c #CBCBD2", "h c #F2F2F9", "i c #F7F7FB", "j c #F9F9FB", "k c #F7F8FB", "l c #F4F4FA", "m c #E0E4F1", "n c #D6DFEC", "o c #D4DCED", "p c #6A6C77", "q c #C6C6CD", "r c #EFEFF7", "s c #F6F6F8", "t c #F9F9FA", "u c #F6F7FA", "v c #ECECF2", "w c #E1E6F1", "x c #D6E3EB", "y c #D4DEEC", "z c #676973", "A c #A8A8AF", "B c #EAEAF4", "C c #EDEEF5", "D c #F1F1F7", "E c #E7E7E9", "F c #F2F2F8", "G c #EBECF3", "H c #E3E6ED", "I c #DCE5EC", "J c #D5E8EA", "K c #D3E0EC", "L c #404248", "M c #DADBE6", "N c #E3EAEF", "O c #E5EBF1", "P c #E0E3E6", "Q c #E5EAF2", "R c #E3E8F2", "S c #D8DDE6", "T c #CED6DC", "U c #D6E9E9", "V c #D3E9E9", "W c #B2BBC8", "X c #8D8F96", "Y c #DEE3ED", "Z c #DCE7EB", "` c #C6D0D0", " . c #D8E2E9", ".. c #D7E0E9", "+. c #CED5D4", "@. c #D4E4E6", "#. c #D3EBE9", "$. c #C8D6DF", "%. c #43454B", "&. c #8D8E98", "*. c #D5D8E6", "=. c #CBCFD2", "-. c #C5CBC5", ";. c #CFD5CE", ">. c #C1C9C2", ",. c #D1DDDE", "'. c #C4D0DA", "). c #55585F", "!. c #847F75", "~. c #C9C3B4", "{. c #C7C5C0", "]. c #CDC9BE", "^. c #D0CAB5", "/. c #C1B99B", "(. c #2E2E2C", "_. c #F0E4BA", ":. c #EBE0B7", "<. c #E1D29A", "[. c #D5C58C", "}. c #AE9E66", "|. c #13110A", "1. c #F1E5BC", "2. c #F1E7C5", "3. c #E6D69E", "4. c #D8C890", "5. c #AFA06C", "6. c #15130C", "7. c #EFE4BF", "8. c #E7DEBE", "9. c #DACD9C", "0. c #CDBE8A", "a. c #B0A16D", "b. c #14120B", "c. c #E7DDB6", "d. c #E2D8B4", "e. c #D1C28C", "f. c #C3B47D", "g. c #9A8D5F", "h. c #0F0D08", "i. c #898060", "j. c #BCB18A", "k. c #AD9F6C", "l. c #82764C", "m. c #312C1A", "n. c #2D2D2D", "o. c #404040", "p. c #060606", " ", " . + @ # $ % & ", " * = - ; > , ' ) ! ", " ~ { ] ^ / ( _ : < [ } ", " | ^ 1 - 2 3 4 5 < 6 7 ", " 8 = ^ ] 9 0 a 3 b c d e f ", " g h i j j ^ k l 3 m n o p ", " q r 2 s t 9 9 u v w x y z ", " A B C D E F ; G H I J K L ", " M N O P Q R S T U V W ", " X Y Z ` ...+.@.#.$.%. ", " &.*.=.-.;.>.,.'.). ", " !.~.{.].^./.(. ", " _.:.<.[.}.|. ", " 1.2.3.4.5.6. ", " 7.8.9.0.a.b. ", " c.d.e.f.g.h. ", " i.j.k.l.m. ", " n.o.p. ", " "}; end_of_pixmap $icon_magnify = <<'end_of_pixmap'; /* XPM */ static char * stock_zoom_in_20_xpm[] = { "20 20 108 2", " c None", ". c #3E3E3E", "+ c #535353", "@ c #585858", "# c #565656", "$ c #525252", "% c #3F3F3F", "& c #484848", "* c #797979", "= c #A7A7A7", "- c #BDBDBD", "; c #C1C1C1", "> c #B5B5B5", ", c #8C8C8C", "' c #2B2B2B", ") c #4D4D4D", "! c #9A9A9A", "~ c #CBCBCB", "{ c #D9D9D9", "] c #E2E2E2", "^ c #E6E6E6", "/ c #E4E4E4", "( c #D5D5D5", "_ c #6E6E6E", ": c #272727", "< c #D1D1D1", "[ c #F3F3F3", "} c #F8F8F8", "| c #F9F9F9", "1 c #F7F7F7", "2 c #F1F1F1", "3 c #C6C6C6", "4 c #626262", "5 c #1A1A1A", "6 c #757575", "7 c #CFCFCF", "8 c #FAFAFA", "9 c #FCFCFC", "0 c #A1A1A1", "a c #454545", "b c #DDDDDD", "c c #F0F0F0", "d c #E8E8E8", "e c #C3C3C3", "f c #343434", "g c #434343", "h c #AAAAAA", "i c #E0E0E0", "j c #FDFDFD", "k c #A2A2A2", "l c #F2F2F2", "m c #EAEAEA", "n c #DFDFDF", "o c #808080", "p c #080808", "q c #4C4C4C", "r c #C9C9C9", "s c #F4F4F4", "t c #9F9F9F", "u c #737373", "v c #909090", "w c #9D9D9D", "x c #B7B7B7", "y c #060606", "z c #D4D4D4", "A c #E9E9E9", "B c #444444", "C c #424242", "D c #7A7A7A", "E c #DBDBDB", "F c #C0C0C0", "G c #050505", "H c #EDEDED", "I c #D7D7D7", "J c #8E8E8E", "K c #D2D2D2", "L c #A3A3A3", "M c #030303", "N c #929292", "O c #979797", "P c #414141", "Q c #D6D6D6", "R c #C8C8C8", "S c #BCBCBC", "T c #D8D8D8", "U c #DEDEDE", "V c #CDCDCD", "W c #949494", "X c #171717", "Y c #181818", "Z c #6A6A6A", "` c #CECECE", " . c #D3D3D3", ".. c #3B3B3B", "+. c #ACACAC", "@. c #C2C2C2", "#. c #898989", "$. c #393939", "%. c #000000", "&. c #0A0A0A", "*. c #262626", "=. c #656565", "-. c #A0A0A0", ";. c #878787", ">. c #474747", ",. c #141414", "'. c #101010", "). c #313131", " ", " . + @ # $ % ", " & * = - ; > , # ' ", " ) ! ~ { ] ^ / ( > _ : ", " % ! < / [ } | 1 2 ^ 3 4 5 ", " ' 6 7 ^ 8 9 0 a b 1 c d e f ", " g h i [ 8 j k a b } l m n o p ", " q r d s t 0 u a v w x m n > y ", " ) z A l g B B B B C D ^ E F G ", " a 3 ^ H z I J g - K I i ( L M ", " ' N E / m H O P 7 ^ i Q R # ", " ) S T U ] h u V E z ~ W X ", " Y Z F ` .z .K ` 3 0 ..y ", " Y # +.; @.; ; x #.$.p %. ", " &.*.=.N -.;.>.,.y %.%.%.%. ", " %.%.%.%.%. 5 *.y %. ", " %.>.$.&.%. ", " '.B ).G ", " ,.X G ", " "}; end_of_pixmap $icon_new = <<'end_of_pixmap'; /* XPM */ static char * new_20_xpm[] = { "20 20 160 2", " c None", ". c #2D2D2D", "+ c #4E4E4E", "@ c #545454", "# c #555555", "$ c #505050", "% c #3D3D3D", "& c #727272", "* c #F3F3F3", "= c #FEFEFE", "- c #FFFFFF", "; c #EEEEEE", "> c #B2B2B2", ", c #838383", "' c #7E7E7E", ") c #EAEAEA", "! c #CFCFCF", "~ c #E3E3E3", "{ c #7D7D7D", "] c #808080", "^ c #FDFDFC", "/ c #FDFDFD", "( c #FCFCFB", "_ c #CDCDCC", ": c #F6F6F6", "< c #DADADA", "[ c #666666", "} c #FEFEFD", "| c #FCFCFC", "1 c #FBFBFB", "2 c #FAFAF9", "3 c #FBFBFA", "4 c #FAFAF8", "5 c #DCDCDB", "6 c #B6B6B5", "7 c #D0D0D0", "8 c #CDCDCD", "9 c #A6A6A6", "0 c #626262", "a c #F9F9F8", "b c #F8F8F7", "c c #F9F8F7", "d c #F8F7F6", "e c #E3E3E2", "f c #828282", "g c #484747", "h c #3F3E3E", "i c #323232", "j c #242423", "k c #141412", "l c #FAFAFA", "m c #F7F7F6", "n c #F6F6F4", "o c #F7F6F5", "p c #F6F5F4", "q c #F4F4F2", "r c #EAEAE8", "s c #DAD9D7", "t c #CACAC8", "u c #B0AFAD", "v c #878582", "w c #3B3A35", "x c #F5F4F3", "y c #F4F3F2", "z c #F2F2F0", "A c #EDEDEB", "B c #E8E7E5", "C c #E4E3E1", "D c #CFCECC", "E c #ADABA7", "F c #464440", "G c #F3F3F1", "H c #F1F1EF", "I c #F0EFED", "J c #EDECEA", "K c #EBEAE8", "L c #E6E5E3", "M c #DFDEDB", "N c #BAB7B2", "O c #47443F", "P c #F3F2F1", "Q c #F0F0EE", "R c #F1F0EE", "S c #EEEDEB", "T c #EAE9E7", "U c #E6E5E2", "V c #E4E3E0", "W c #E2E1DE", "X c #C9C6C1", "Y c #4D4A45", "Z c #F2F1F0", "` c #EFEFED", " . c #EFEEEC", ".. c #E9E8E6", "+. c #E5E4E2", "@. c #E4E2DF", "#. c #E2E0DD", "$. c #CCC9C3", "%. c #524F49", "&. c #F4F4F3", "*. c #EEEEEC", "=. c #ECEBE9", "-. c #EBEAE7", ";. c #E7E5E2", ">. c #E1E0DC", ",. c #DFDEDA", "'. c #CAC8C1", "). c #514E48", "!. c #E8E7E4", "~. c #E9E8E5", "{. c #E4E3DF", "]. c #E2E1DD", "^. c #DDDCD8", "/. c #C9C6C0", "(. c #E7E6E3", "_. c #E6E4E1", ":. c #E5E4E0", "<. c #DBD9D5", "[. c #C6C3BC", "}. c #504D47", "|. c #7C7C7C", "1. c #E3E2DF", "2. c #E3E2DE", "3. c #DDDBD7", "4. c #DCDAD6", "5. c #D9D7D3", "6. c #C5C1BB", "7. c #4F4C46", "8. c #E2E1DF", "9. c #E1DFDC", "0. c #E0DFDB", "a. c #E0DEDB", "b. c #DFDDD9", "c. c #DEDCD8", "d. c #D7D6D1", "e. c #C3C0B9", "f. c #4E4B45", "g. c #5B5A59", "h. c #BEBBB5", "i. c #C2BEB7", "j. c #C1BDB6", "k. c #C1BDB5", "l. c #C0BDB5", "m. c #C0BCB4", "n. c #BFBBB3", "o. c #BEBAB2", "p. c #BBB7AF", "q. c #BAB6AE", "r. c #A6A299", "s. c #413E39", "t. c #20201F", "u. c #353431", "v. c #373531", "w. c #363431", "x. c #363430", "y. c #353330", "z. c #35332F", "A. c #34322F", "B. c #2E2C29", "C. c #151412", " ", " . + @ # # # # # # $ % ", " & * = - - - - - = ; > , ", " ' = - - - - - - = ) ! ~ { ", " ] - - - - = ^ / ( ~ _ : < [ ", " ] - - } | 1 2 3 4 5 6 7 8 9 0 ", " ] = | 3 2 a b c d e f g h i j k ", " ] | l a b m n o p q r s t u v w ", " ] 3 b m n x q x y z A B C D E F ", " ] a p q y G z z H I J K L M N O ", " ] d P z Q Q I R I S T U V W X Y ", " ] o Z H ` ` .I .J ..+.@.#.$.%. ", " ] &.*.S J J K =.-...;.V >.,.'.). ", " ] z K T ....!.~.!.;.{.].,.^./.). ", " ] ` (.L U U _._.:.{.>.,.^.<.[.}. ", " |.T 1.2.2.2.].].].>.,.3.4.5.6.7. ", " & 8.9.0.0.0.0.a.0.b.c.4.5.d.e.f. ", " g.h.i.j.j.j.k.l.k.m.n.o.p.q.r.s. ", " t.u.v.w.x.x.x.x.x.x.y.z.A.A.B.C. ", " "}; end_of_pixmap $icon_report = <<'end_of_pixmap'; /* XPM */ static char * report_20_xpm[] = { "20 20 95 2", " c None", ". c #323232", "+ c #282828", "@ c #292929", "# c #2C2C2C", "$ c #E8E8E8", "% c #ECECEC", "& c #EDEDED", "* c #EEEEEE", "= c #EFEFEF", "- c #F0F0F0", "; c #F1F1F1", "> c #F2F2F2", ", c #F3F3F3", "' c #F4F4F4", ") c #000000", "! c #E1E1E1", "~ c #E2E2E2", "{ c #E3E3E3", "] c #E5E4E5", "^ c #E6E6E6", "/ c #E7E6E7", "( c #E8E7E8", "_ c #E9E9E9", ": c #EAEAEA", "< c #EBECEC", "[ c #ECEDED", "} c #EFEEEF", "| c #F0F0F1", "1 c #EDEDEC", "2 c #E4E5E5", "3 c #777777", "4 c #EDEEED", "5 c #E4E4E4", "6 c #E6E5E5", "7 c #E7E7E7", "8 c #EEEDEE", "9 c #E6E7E6", "0 c #EEEEEF", "a c #E5E5E6", "b c #E6E6E7", "c c #E7E7E8", "d c #E9E9E8", "e c #EBEBEB", "f c #EEEDED", "g c #F5F5F5", "h c #E8E8E9", "i c #F7F7F7", "j c #EBECEB", "k c #F6F6F6", "l c #F6F7F7", "m c #F8F8F7", "n c #F8F9F9", "o c #F4F4F5", "p c #F9F9F8", "q c #FAFAFA", "r c #FBFBFB", "s c #EFEFF0", "t c #F4F3F4", "u c #F8F8F8", "v c #F9F8F9", "w c #FAF9F9", "x c #FBFBFC", "y c #FCFCFC", "z c #FDFDFE", "A c #F5F4F4", "B c #F1F0F1", "C c #F1F2F1", "D c #F2F3F3", "E c #F3F3F4", "F c #F7F7F8", "G c #F9F9F9", "H c #FAF9FA", "I c #FEFDFD", "J c #FEFEFE", "K c #F0EFF0", "L c #F1F1F2", "M c #F2F2F3", "N c #F3F4F3", "O c #F4F5F4", "P c #F6F6F5", "Q c #F7F7F6", "R c #FAFAFB", "S c #FCFBFC", "T c #FDFDFD", "U c #9A9A9A", "V c #A2A2A2", "W c #A4A4A4", "X c #A5A5A5", "Y c #A6A6A6", "Z c #A7A7A7", "` c #A8A8A8", " . c #A9A9A9", ".. c #AAAAAA", "+. c #979797", " . + + + + + @ @ @ @ @ @ @ @ @ # ", ". $ % & & * * = - - ; > > , ' ' & ) ", "+ % ! ~ { ] ^ / ( _ : < [ * } - | ) ", "+ 1 ) ) 2 3 3 3 3 3 3 3 3 3 3 3 > ) ", "+ 4 { 5 6 7 $ _ : % [ 8 = - ; > , ) ", "+ * ) ) 9 3 3 3 3 3 3 3 3 3 3 3 ' ) ", "@ 0 a b c d : e % f 0 - ; > , ' g ) ", "@ = ) ) h 3 3 3 3 3 3 3 3 3 3 3 i ) ", "@ - c h : j % & * - ; > , ' k l m ) ", "@ ; ) ) e 3 3 3 3 3 3 3 3 3 3 3 n ) ", "@ ; : e % & * - ; > , o g k m p q ) ", "@ > ) ) & 3 3 3 3 3 3 3 3 3 3 3 r ) ", "@ , % & * s | > , t g k u v w x y ) ", "@ ' ) ) = 3 3 3 3 3 3 3 3 3 3 3 z ) ", "@ A * = B C D E g k F G H r y I J ) ", "@ % K L M N O P Q u G R S y T J ' ) ", "# U V W W X X Y Y Z ` .......` +.) ", " ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ", " ", " "}; end_of_pixmap $icon_prefs = <<'end_of_pixmap'; /* XPM */ static char * preferences_20_xpm[] = { "20 20 138 2", " c None", ". c #201F1E", "+ c #242422", "@ c #1B1B19", "# c #97948D", "$ c #B6B3AC", "% c #5E5C57", "& c #2E2D2C", "* c #9F9C98", "= c #C9C6BE", "- c #20201E", "; c #404040", "> c #676767", ", c #888786", "' c #D6D4CE", ") c #222120", "! c #272727", "~ c #BBBBBB", "{ c #BEBEBE", "] c #373737", "^ c #7C7B77", "/ c #434341", "( c #000000", "_ c #3E3E3E", ": c #CDCCCB", "< c #CECBC4", "[ c #72706B", "} c #262626", "| c #D2D2D2", "1 c #696969", "2 c #111111", "3 c #6C6966", "4 c #BCB8B1", "5 c #CFCBC4", "6 c #E7E6E2", "7 c #D6D2CD", "8 c #BFBBB1", "9 c #BBB6AC", "0 c #161514", "a c #A8A8A8", "b c #6B6863", "c c #827F79", "d c #878580", "e c #8E8C87", "f c #C7C5C0", "g c #D0CDC6", "h c #8B8781", "i c #272624", "j c #3D3D3D", "k c #959595", "l c #5B5B5A", "m c #DEDDDC", "n c #D4D2CD", "o c #716F6A", "p c #2C2C2C", "q c #616161", "r c #1A1A1A", "s c #494949", "t c #D2D2D1", "u c #545352", "v c #585858", "w c #131313", "x c #393939", "y c #6B6B6B", "z c #3A3937", "A c #4B4A45", "B c #0D0D0C", "C c #171B20", "D c #191D21", "E c #040505", "F c #7C7C7C", "G c #232323", "H c #717170", "I c #898885", "J c #85827C", "K c #151413", "L c #4B5967", "M c #9CB0C6", "N c #99AFC5", "O c #343F4A", "P c #3C3C3C", "Q c #AAAAA9", "R c #CAC8C3", "S c #242321", "T c #53606E", "U c #A9BACC", "V c #99A8B8", "W c #94ACC3", "X c #6B839D", "Y c #575757", "Z c #D9D9D8", "` c #D6D3CE", " . c #87847E", ".. c #292725", "+. c #5A6673", "@. c #BBC9D8", "#. c #8C9DAF", "$. c #7A90A6", "%. c #859FBB", "&. c #637A93", "*. c #484848", "=. c #EFEEED", "-. c #CBC9C5", ";. c #74726F", ">. c #1D1C1B", ",. c #5F6A76", "'. c #D1DAE2", "). c #899BAF", "!. c #748BA4", "~. c #87A1BC", "{. c #69819B", "]. c #1D232B", "^. c #474747", "/. c #C0BFBE", "(. c #6A6968", "_. c #7D7D7C", ":. c #4B5661", "<. c #D2DCE5", "[. c #A3AEBC", "}. c #7289A2", "|. c #8AA5C0", "1. c #5E758D", "2. c #1B2128", "3. c #3A3A3A", "4. c #969695", "5. c #8F8F8E", "6. c #4D5864", "7. c #BFCFDF", "8. c #A0ACB9", "9. c #92ABC4", "0. c #5C728A", "a. c #202831", "b. c #212932", "c. c #6A8097", "d. c #93A9C0", "e. c #6D8299", "f. c #242C36", "g. c #1D242C", " . + ", " @ # $ % ", " & * = - ; > ", " , ' ) ! ~ { ] ", " ^ / ( _ : < [ ( } | 1 2 ", " 3 4 5 6 7 8 9 0 } a ( ( ", " ( b c d e f g h i j k ( ", " ( ( ( l m n o p q r ", " s t u v w ", " x y z A B ", " C D E F G H I J K ", " L M N O ( P Q R h S ", " T U V W X ( Y Z ` ... ", " +.@.#.$.%.&.( *.=.-.;.>. ", " ,.'.).!.~.{.]. ^./.(._.r ", " :.<.[.}.|.1.2. 3.4.5.r ", " 6.7.8.9.0.a.( ( ( ", " b.c.d.e.a.( ", " ].f.g.( ", " "}; end_of_pixmap $icon_save_as = <<'end_of_pixmap'; /* XPM */ static char * stock_save_as_20_xpm[] = { "20 20 274 2", " c None", ". c #272309", "+ c #29200C", "@ c #3F3B0F", "# c #C2A930", "$ c #CA9F41", "% c #000000", "& c #2A2E34", "* c #5B6976", "= c #43494F", "- c #6D5C58", "; c #78645F", "> c #78615C", ", c #775E5A", "' c #77605B", ") c #614C48", "! c #2A270A", "~ c #BCA22F", "{ c #CAA13E", "] c #44381C", "^ c #282A2C", "/ c #4A555F", "( c #2D353B", "_ c #3F474F", ": c #A9C6DE", "< c #76818F", "[ c #D0968C", "} c #E69989", "| c #E49586", "1 c #E29384", "2 c #E19284", "3 c #DC9488", "4 c #5C4C36", "5 c #C1A72F", "6 c #C59B3C", "7 c #493B1E", "8 c #492820", "9 c #53565D", "0 c #7891A4", "a c #34424D", "b c #3E474E", "c c #A1BED6", "d c #727B8B", "e c #BB7E79", "f c #CE7D73", "g c #CE7B72", "h c #CE7C72", "i c #CD7C71", "j c #3F2623", "k c #D6B932", "l c #CB9A3C", "m c #45361D", "n c #552D27", "o c #8B4E4B", "p c #5E626C", "q c #6D899F", "r c #293742", "s c #3D464D", "t c #9FBED4", "u c #7A858D", "v c #E8E8E8", "w c #FFFFFF", "x c #E9E9E9", "y c #625F4D", "z c #D5B631", "A c #C29643", "B c #433C26", "C c #6E6E6E", "D c #CBCBCB", "E c #E3E3E3", "F c #72777B", "G c #718DA4", "H c #293640", "I c #3E464D", "J c #9FBBD3", "K c #79838C", "L c #DADADA", "M c #EEEEEE", "N c #D7D7D7", "O c #7E785E", "P c #BE9C2C", "Q c #C8963A", "R c #403A23", "S c #5C5C5C", "T c #BCBCBC", "U c #D3D3D3", "V c #E2E2E2", "W c #757A7E", "X c #728EA4", "Y c #26333D", "Z c #9EBAD2", "` c #737D86", " . c #D2D2D2", ".. c #E6E6E6", "+. c #989898", "@. c #73602A", "#. c #BC9341", "$. c #42371D", "%. c #6B6B6B", "&. c #B6B6B6", "*. c #CDCDCD", "=. c #DEDEDE", "-. c #DFDFDF", ";. c #767B7F", ">. c #728DA3", ",. c #24303A", "'. c #3D444D", "). c #9BB7D0", "!. c #747F87", "~. c #E7E7E7", "{. c #4A473D", "]. c #3E331A", "^. c #2D2617", "/. c #6D6C6A", "(. c #CACACA", "_. c #F6F6F6", ":. c #FDFDFD", "<. c #718CA1", "[. c #232E37", "}. c #3A444C", "|. c #96B6CE", "1. c #76828A", "2. c #C6C6C6", "3. c #B0B0B0", "4. c #040401", "5. c #474642", "6. c #828282", "7. c #A3A3A3", "8. c #BEBEBE", "9. c #D4D4D4", "0. c #D5D5D5", "a. c #D0D0D0", "b. c #758C9F", "c. c #293035", "d. c #3A454C", "e. c #98B8CD", "f. c #797880", "g. c #D5D3D3", "h. c #E4E4E4", "i. c #C4C4C4", "j. c #D6D6D7", "k. c #757B80", "l. c #71899C", "m. c #272D31", "n. c #39444C", "o. c #91B3CA", "p. c #7B95A8", "q. c #7D8992", "r. c #788289", "s. c #747E86", "t. c #727B84", "u. c #707C87", "v. c #768087", "w. c #79828A", "x. c #79838A", "y. c #757E86", "z. c #798A98", "A. c #6D879A", "B. c #1C272E", "C. c #39424B", "D. c #8EADC8", "E. c #7696B0", "F. c #7A99B3", "G. c #637684", "H. c #647686", "I. c #6C8091", "J. c #708596", "K. c #6B8192", "L. c #708494", "M. c #6E8291", "N. c #6D8190", "O. c #637B8D", "P. c #627585", "Q. c #829FB8", "R. c #778D9F", "S. c #1B252B", "T. c #8EADC7", "U. c #7090A9", "V. c #5B6F7E", "W. c #828380", "X. c #ADAFAF", "Y. c #B8BBBC", "Z. c #BDC0C2", "`. c #BBBFC0", " + c #B1B3B4", ".+ c #A6A9A8", "++ c #939491", "@+ c #5F696E", "#+ c #465A69", "$+ c #6C8AA3", "%+ c #8299AA", "&+ c #1A2328", "*+ c #39434B", "=+ c #8BACC4", "-+ c #6D8DA5", ";+ c #49545D", ">+ c #D3D0CB", ",+ c #C2C1BC", "'+ c #74716B", ")+ c #6C6962", "!+ c #C9C7C4", "~+ c #CAC7C2", "{+ c #C0BCB5", "]+ c #C1BDB7", "^+ c #71777A", "/+ c #4C6478", "(+ c #607F94", "_+ c #879BAB", ":+ c #192025", "<+ c #38424B", "[+ c #89AAC3", "}+ c #6F8EA4", "|+ c #47525A", "1+ c #E4E3DF", "2+ c #C1BFBB", "3+ c #5A5850", "4+ c #524F47", "5+ c #B8B5B0", "6+ c #C2BEB8", "7+ c #C6C2BC", "8+ c #D6D3CF", "9+ c #7C8286", "0+ c #4D6578", "a+ c #607E92", "b+ c #8A9CAB", "c+ c #191F24", "d+ c #323B42", "e+ c #809DB4", "f+ c #6C89A0", "g+ c #3F4950", "h+ c #DEDCD8", "i+ c #B3B1AC", "j+ c #4F4C44", "k+ c #4E4B43", "l+ c #B1AEA7", "m+ c #C8C5BE", "n+ c #DAD8D3", "o+ c #E7E6E3", "p+ c #7C8386", "q+ c #4B6272", "r+ c #5B798D", "s+ c #7F96A8", "t+ c #1C1F21", "u+ c #1E2226", "v+ c #505F6C", "w+ c #5F7889", "x+ c #465159", "y+ c #C0BCB7", "z+ c #B3AFA9", "A+ c #88847D", "B+ c #8C8982", "C+ c #C1BEB8", "D+ c #DDDAD7", "E+ c #DAD8D5", "F+ c #717679", "G+ c #485D6F", "H+ c #597385", "I+ c #7C95A6", "J+ c #1B1E20", "K+ c #14181B", "L+ c #1D2327", "M+ c #1B1F22", "N+ c #383735", "O+ c #3F3E3B", "P+ c #413F3D", "Q+ c #444341", "R+ c #474645", "S+ c #4C4C4B", "T+ c #4D4C4B", "U+ c #222425", "V+ c #181F25", "W+ c #222B32", "X+ c #3A4853", "Y+ c #0B0D0E", " . + ", " @ # $ % ", " & * = - ; > , ' ' ) ! ~ { ] ^ / ( ", " _ : < [ } | 1 2 3 4 5 6 7 8 9 0 a ", " b c d e f g h i j k l m n o p q r ", " s t u v w w x y z A B C D E F G H ", " I J K L M N O P Q R S T U V W X Y ", " s Z ` ...+.@.#.$.%.&.*.=.-.;.>.,. ", " '.).!.~.V {.].^./.(.E _.:.w ;.<.[. ", " }.|.1.2.3.4.5.6.7.8.*.9.0.a.;.b.c. ", " d.e.f.g.h.9.i.(.j.V ~.v v ..k.l.m. ", " n.o.p.q.r.s.t.s.u.v.w.x.w.y.z.A.B. ", " C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S. ", " C.T.U.V.W.X.Y.Z.`. +.+++@+#+$+%+&+ ", " *+=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+:+ ", " <+[+}+|+1+2+3+4+5+6+7+8+9+0+a+b+c+ ", " d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+ ", " u+v+w+x+y+z+A+B+C+D+o+E+F+G+H+I+J+ ", " K+L+M+N+O+P+Q+R+S+T+Q+U+V+W+X+Y+ ", " "}; end_of_pixmap $icon_separator = <<'end_of_pixmap'; /* XPM */ static char * sep_20_xpm[] = { "4 20 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ", " ", " ", " ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " .+ ", " ", " ", " ", " "}; end_of_pixmap $icon_ensembl = <<'end_of_pixmap'; /* XPM */ static char * ensembl_20_xpm[] = { "20 20 236 2", " c None", ". c #2D2D2D", "+ c #4E4E4E", "@ c #545454", "# c #555555", "$ c #505050", "% c #3D3D3D", "& c #CC0000", "* c #950000", "= c #727272", "- c #F3F3F3", "; c #FEFEFE", "> c #FFFFFF", ", c #EEEEEE", "' c #B2B2B2", ") c #838383", "! c #DC0000", "~ c #AB0000", "{ c #6A0000", "] c #7E7E7E", "^ c #EAEAEA", "/ c #CFCFCF", "( c #E3E3E3", "_ c #867373", ": c #9B0000", "< c #530000", "[ c #808080", "} c #FDFDFC", "| c #FAFCFD", "1 c #F4F8FB", "2 c #DDE0E3", "3 c #CDCDCC", "4 c #F6F6F6", "5 c #DDB8B8", "6 c #B60C0C", "7 c #850000", "8 c #FEFEFD", "9 c #F9FBFC", "0 c #DBEAF9", "a c #B2CEF1", "b c #90B4EB", "c c #7EA5E5", "d c #7598D7", "e c #7D93BB", "f c #C7C9CD", "g c #D49F9F", "h c #B70B0B", "i c #620C0C", "j c #FCFCFC", "k c #D6E8F9", "l c #97BBEB", "m c #5F8DD7", "n c #4168AF", "o c #4D68A0", "p c #5A73A8", "q c #3E66B8", "r c #234A9B", "s c #313A4E", "t c #6F2C2C", "u c #9F0202", "v c #360E0E", "w c #131311", "x c #B2CFF2", "y c #6594DB", "z c #3D63AF", "A c #61759F", "B c #9EA3AF", "C c #D6D6D7", "D c #C7D7E6", "E c #5F89D2", "F c #37589E", "G c #9399A4", "H c #D38281", "I c #840D0D", "J c #665C5A", "K c #3B3A35", "L c #B3D0F2", "M c #5385D4", "N c #3A5EA7", "O c #7D8BA7", "P c #D6D5D4", "Q c #EFF2F2", "R c #CADEF3", "S c #80A6DF", "T c #395EA6", "U c #7080A0", "V c #D5D4D3", "W c #DC7E7D", "X c #843A3A", "Y c #A19F9B", "Z c #464440", "` c #718292", " . c #6695DB", ".. c #416BBF", "+. c #5573AE", "@. c #ABBCD4", "#. c #ADC7E7", "$. c #83A4D3", "%. c #557BB4", "&. c #566C9A", "*. c #8D95A8", "=. c #D6D5D3", "-. c #EBE9E7", ";. c #C76969", ">. c #9A7C7A", ",. c #BAB7B2", "'. c #47443F", "). c #5978A1", "!. c #4572C7", "~. c #3A5DA6", "{. c #5E729A", "]. c #7589A8", "^. c #808FA9", "/. c #939BAC", "(. c #B4B6BD", "_. c #D8D7D6", ":. c #EEEDEB", "<. c #EAE9E7", "[. c #E6E0DD", "}. c #B76968", "|. c #C0B9B6", "1. c #C9C6C1", "2. c #4D4A45", "3. c #446DA9", "4. c #416CC0", "5. c #516B9E", "6. c #D0D0D1", "7. c #EAEAE8", "8. c #EEEEEC", "9. c #EFEEEC", "0. c #F0EFED", "a. c #EDECEA", "b. c #E9E8E6", "c. c #E3D5D3", "d. c #BC8684", "e. c #DCDAD7", "f. c #CCC9C3", "g. c #524F49", "h. c #3862AB", "i. c #416ABF", "j. c #5570A6", "k. c #DBDBDB", "l. c #EBEAE8", "m. c #ECEBE9", "n. c #E5E8E7", "o. c #D5D8DD", "p. c #E7E5E2", "q. c #E2D9D6", "r. c #CFC1BD", "s. c #DFDEDA", "t. c #CAC8C1", "u. c #514E48", "v. c #3960A7", "w. c #406BBE", "x. c #3E65B4", "y. c #93A7D0", "z. c #CED7E6", "A. c #D5DDE7", "B. c #C9D7E5", "C. c #ABC4E3", "D. c #7B95B6", "E. c #A3AEBF", "F. c #E8908D", "G. c #D55E5C", "H. c #CA918E", "I. c #DDDCD8", "J. c #C9C6C0", "K. c #4F6694", "L. c #3E67B8", "M. c #3D67BC", "N. c #406BC0", "O. c #547FCD", "P. c #5883CB", "Q. c #4B6FAC", "R. c #637A9F", "S. c #9A9EA8", "T. c #E0B6B3", "U. c #CC2221", "V. c #A40101", "W. c #912827", "X. c #C4BAB6", "Y. c #C6C3BC", "Z. c #504D47", "`. c #71747A", " + c #8595B7", ".+ c #4C669C", "++ c #405E99", "@+ c #475F92", "#+ c #687695", "$+ c #979BA4", "%+ c #D0CFCB", "&+ c #E2E1DD", "*+ c #DFB3B0", "=+ c #B11211", "-+ c #9C0101", ";+ c #6F1716", ">+ c #B7B1AE", ",+ c #C5C1BB", "'+ c #4F4C46", ")+ c #DFDEDC", "!+ c #C9C8C8", "~+ c #BDBDBE", "{+ c #C7C6C4", "]+ c #D9D8D4", "^+ c #E0DFDB", "/+ c #E0DEDB", "(+ c #DCD5D1", "_+ c #AA6463", ":+ c #712323", "<+ c #836765", "[+ c #D1D0CC", "}+ c #C3C0B9", "|+ c #4E4B45", "1+ c #5B5A59", "2+ c #BEBBB5", "3+ c #C2BEB7", "4+ c #C1BDB6", "5+ c #C1BDB5", "6+ c #C0BDB5", "7+ c #C0BCB4", "8+ c #BFBBB3", "9+ c #BEBAB2", "0+ c #BBB7AF", "a+ c #BAB6AE", "b+ c #A6A299", "c+ c #413E39", "d+ c #20201F", "e+ c #353431", "f+ c #373531", "g+ c #363431", "h+ c #363430", "i+ c #353330", "j+ c #35332F", "k+ c #34322F", "l+ c #2E2C29", "m+ c #151412", " ", " . + @ # # # # # # $ % & * ", " = - ; > > > > > ; , ' ) ! ~ { ", " ] ; > > > > > > ; ^ / ( _ & : < ", " [ > > > > ; } | 1 2 3 4 5 6 7 ", " [ > > 8 9 0 a b c d e f g h i ", " [ ; j k l m n o p q r s t u v w ", " [ 9 x y z A B C D E F G H I J K ", " [ L M N O P Q R S T U V W X Y Z ", " ` ...+.@.#.$.%.&.*.=.-.;.>.,.'. ", " ).!.~.{.].^./.(._.:.<.[.}.|.1.2. ", " 3.4.5.6.7.8.9.0.9.a.b.c.d.e.f.g. ", " h.i.j.k.a.a.l.m.n.o.p.q.r.s.t.u. ", " v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.u. ", " K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z. ", " `. +.+++@+#+$+%+&+*+=+-+;+>+,+'+ ", " = )+!+~+{+]+^+/+^+(+_+:+<+[+}+|+ ", " 1+2+3+4+4+4+5+6+5+7+8+9+0+a+b+c+ ", " d+e+f+g+h+h+h+h+h+h+i+j+k+k+l+m+ ", " "}; end_of_pixmap $icon_dna_save = <<'end_of_pixmap'; /* XPM */ static char * save_dna_20_xpm[] = { "20 20 247 2", " c None", ". c #2A3034", "+ c #525860", "@ c #4E545A", "# c #756966", "$ c #7E7370", "% c #877D7A", "& c #908784", "* c #9A918F", "= c #A29A98", "- c #ACA5A3", "; c #B6AFAE", "> c #BEB8B7", ", c #C8C3C2", "' c #D1CDCC", ") c #D2D4D5", "! c #E1E2E3", "~ c #E6E7E8", "{ c #4E575F", "] c #B6CFE3", "^ c #899EB0", "/ c #E6B0A7", "( c #E7B5AC", "_ c #E9BAB2", ": c #EBC0B8", "< c #ECC5BE", "[ c #EECAC4", "} c #F0CFCA", "| c #F1D5D0", "1 c #F3DAD5", "2 c #F5DFDB", "3 c #F6E4E1", "4 c #E0E2E3", "5 c #EDF2F5", "6 c #EAEBEC", "7 c #484E56", "8 c #A6C2D5", "9 c #798D9C", "0 c #D89B93", "a c #DBA29B", "b c #DDA9A2", "c c #E0B0AA", "d c #E3B8B2", "e c #E6BFB9", "f c #E8C6C1", "g c #EBCDC9", "h c #EED4D1", "i c #F1DBD8", "j c #EAEEF2", "k c #E9E9EA", "l c #A7C2D5", "m c #929AA2", "n c #E3E4E2", "o c #F7EEEE", "p c #F8EFEF", "q c #F8F0F0", "r c #F9F1F1", "s c #F9F3F3", "t c #FAF4F4", "u c #FAF5F5", "v c #FBF6F6", "w c #FBF7F7", "x c #FCF9F9", "y c #E8ECF0", "z c #E8E9E9", "A c #F9FBF9", "B c #FAFBF9", "C c #FAFBFA", "D c #7C8BDE", "E c #6E6F72", "F c #726F6F", "G c #6F6F6F", "H c #737070", "I c #737171", "J c #766765", "K c #862A21", "L c #B6382B", "M c #E4D3D2", "N c #939AA3", "O c #D3D2CE", "P c #E5E6E4", "Q c #ECEDEA", "R c #E9E9E7", "S c #7483E0", "T c #5769DE", "U c #DCDFEA", "V c #F0F1EF", "W c #F2F3F2", "X c #F2F2F1", "Y c #F3F4F3", "Z c #DEE0E1", "` c #DB675A", " . c #DB7E77", ".. c #878C93", "+. c #F7EDED", "@. c #F1F2F8", "#. c #5E6FE1", "$. c #485CD3", "%. c #484F80", "&. c #757675", "*. c #747576", "=. c #757676", "-. c #696A6B", ";. c #6D6E71", ">. c #BF8B87", ",. c #E7E8E6", "'. c #EAEBE9", "). c #8F9BE4", "!. c #455ADD", "~. c #5568DE", "{. c #8A8FB1", "]. c #707070", "^. c #676869", "/. c #CD7C6C", "(. c #DD958C", "_. c #868C92", ":. c #FAFCFA", "<. c #FBFCFA", "[. c #FBFCFB", "}. c #E2E5F6", "|. c #5768DF", "1. c #4156DC", "2. c #6F75DA", "3. c #DA775D", "4. c #DA6544", "5. c #E6DEDD", "6. c #95A9BB", "7. c #99A0A8", "8. c #959AA0", "9. c #9DA1A7", "0. c #A4A8AD", "a. c #ABAFB4", "b. c #B3B7BB", "c. c #BBBEC2", "d. c #C2C5C8", "e. c #CACACD", "f. c #9A6F98", "g. c #C85F55", "h. c #A05B80", "i. c #ACACDF", "j. c #E7E8E8", "k. c #9AB4CA", "l. c #94A9BA", "m. c #9BAFBF", "n. c #98AABA", "o. c #9FB0BE", "p. c #A7B6C3", "q. c #AEBCC8", "r. c #B6C2CD", "s. c #BDC8D2", "t. c #D5AEA1", "u. c #DF7F5D", "v. c #D16846", "w. c #8E6163", "x. c #3645AC", "y. c #3647B4", "z. c #C6CAE5", "A. c #8294A2", "B. c #9A9D9D", "C. c #AEB0AF", "D. c #CCCCCB", "E. c #D0D0CF", "F. c #D8B7AD", "G. c #E08768", "H. c #E28664", "I. c #DE8B6C", "J. c #BCB3B4", "K. c #9DA1A4", "L. c #ACB0B3", "M. c #6471BF", "N. c #909BE2", "O. c #8A9FB0", "P. c #6C737A", "Q. c #D6D5D2", "R. c #D9D8D5", "S. c #B4B6B5", "T. c #C78B7D", "U. c #DF6F51", "V. c #DF8468", "W. c #796058", "X. c #666666", "Y. c #616161", "Z. c #5F6164", "`. c #67696D", " + c #5960B6", ".+ c #A0A6E3", "++ c #4D565E", "@+ c #959390", "#+ c #D9654B", "$+ c #C98373", "%+ c #C6C7C8", "&+ c #CBCCCA", "*+ c #CDCDCC", "=+ c #C9CACA", "-+ c #969BDB", ";+ c #5960DF", ">+ c #5962DF", ",+ c #E0E1E7", "'+ c #6B7279", ")+ c #EAECE9", "!+ c #C8C8C7", "~+ c #C1665B", "{+ c #747473", "]+ c #7A7B7C", "^+ c #7C7D7E", "/+ c #5E5E90", "(+ c #4F50BD", "_+ c #535ADF", ":+ c #7378E0", "<+ c #E1E6F0", "[+ c #292F33", "}+ c #718696", "|+ c #9FA09E", "1+ c #C05452", "2+ c #7A3434", "3+ c #786D89", "4+ c #7777E4", "5+ c #6565E1", "6+ c #6D6FDE", "7+ c #B0B7DA", "8+ c #DDE2E6", "9+ c #EAECED", "0+ c #353C3F", "a+ c #4D5359", "b+ c #585E63", "c+ c #83817D", "d+ c #8C8A87", "e+ c #9EA09E", "f+ c #A99EA0", "g+ c #9E5796", "h+ c #7770DF", "i+ c #9363A9", "j+ c #B38FA9", "k+ c #BCBEC0", "l+ c #C7C8CA", "m+ c #D6D7D9", "n+ c #E2E5E8", "o+ c #6360E1", "p+ c #6B67E2", "q+ c #6A4E8B", "r+ c #942D22", "s+ c #D7402D", "t+ c #D34434", "u+ c #5F5EE0", "v+ c #D7402F", "w+ c #D63B2F", "x+ c #D34441", " ", " . + @ # $ % & * = - ; > , ' ) ! ~ ", " { ] ^ / ( _ : < [ } | 1 2 3 4 5 6 ", " 7 8 9 / 0 a b c d e f g h i 4 j k ", " { l m n o p q r s t u v w x 4 y z ", " 7 8 m n A B C D E F G H I J K L M ", " 7 8 N O P Q R S T U V W X Y Z ` . ", " 7 8 ..+.A B C @.#.$.%.&.*.=.-.;.>. ", " 7 8 N O P ,.R '.V ).!.~.{.].^./.(. ", " 7 8 _.n A B C :.<.[.}.|.1.2.3.4.5. ", " 7 8 6.7.8.9.0.a.b.c.d.e.f.g.h.i.j. ", " 7 k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z. ", " 7 k.l.A.B.C.D.E.F.G.H.I.J.K.L.M.N. ", " 7 k.O.P.Q.R.S.T.U.V.W.X.Y.Z.`. +.+ ", " ++k.^ P.o R.@+#+$+%+&+*+=+-+;+>+,+ ", " 7 k.^ '+)+!+@+~+{+]+^+/+(+_+:+<+~ ", " [+}+9 '+Q.!+|+1+2+3+4+5+6+7+8+9+j. ", " 0+a+b+c+d+e+f+g+h+i+j+k+l+m+n+j. ", " o+p+q+r+s+t+ ", " u+ v+w+x+ "}; end_of_pixmap $icon_dna_open = <<'end_of_pixmap'; /* XPM */ static char * open_dna_20_iii_xpm[] = { "20 20 214 2", " c None", ". c #1C1F1B", "+ c #292C28", "@ c #373936", "# c #444643", "$ c #515350", "% c #414138", "& c #999A8B", "* c #A9AA9F", "= c #AEAFA5", "- c #B3B5AB", "; c #A7AB9E", "> c #888883", ", c #5569D6", "' c #0E0F15", ") c #000000", "! c #210B08", "~ c #84251B", "{ c #B53528", "] c #646563", "^ c #D9DCBD", "/ c #DBDEC1", "( c #DDE0C5", "_ c #E0E2C9", ": c #D6D8C0", "< c #A2A296", "[ c #80817F", "} c #4B60DE", "| c #485CDD", "1 c #D74130", "2 c #D5463A", "3 c #43433B", "4 c #BDBFB0", "5 c #CDD0B2", "6 c #D0D2B6", "7 c #D3D5BB", "8 c #DCDEC7", "9 c #BCBFAD", "0 c #8A8A85", "a c #4B5EDE", "b c #485CD3", "c c #212A65", "d c #040407", "e c #030405", "f c #020102", "g c #A44F48", "h c #C5C8B8", "i c #BFC2A2", "j c #BBBEA4", "k c #BEC2A9", "l c #CACCB3", "m c #CED0B8", "n c #C3C6B5", "o c #AEAFA3", "p c #9B9B97", "q c #A6A6A2", "r c #7580C9", "s c #4459DC", "t c #5063DA", "u c #787D9E", "v c #616160", "w c #010102", "x c #C65E48", "y c #D65848", "z c #37372E", "A c #CFD2B1", "B c #AAAF95", "C c #A5AA92", "D c #B4B8A2", "E c #B9BDA8", "F c #BEC1AE", "G c #C8CBBB", "H c #CDD0C1", "I c #DADBD0", "J c #DEDFD5", "K c #CBCEDA", "L c #5466DC", "M c #4156DC", "N c #6B71D6", "O c #D8755B", "P c #DA5F3D", "Q c #242521", "R c #C5C7B7", "S c #959A81", "T c #6D6F64", "U c #61615A", "V c #6B6B64", "W c #767670", "X c #80807B", "Y c #8B8B86", "Z c #959591", "` c #A0A09B", " . c #ABABA7", ".. c #B5B5B1", "+. c #C0BEBC", "@. c #9A6E97", "#. c #C85F55", "$. c #A05B80", "%. c #AAA8D8", "&. c #B5B7A2", "*. c #717260", "=. c #C8CABD", "-. c #CCCDC1", ";. c #CFD1C6", ">. c #D8DACF", ",. c #DBDDD3", "'. c #DEE0D7", "). c #DEDFD7", "!. c #E5E6DF", "~. c #E5B8A5", "{. c #E07F5E", "]. c #D16846", "^. c #926465", "/. c #3645AC", "(. c #3647B4", "_. c #D1D5F0", ":. c #56564F", "<. c #B9BCB1", "[. c #DFE1CB", "}. c #E1E3CF", "|. c #E8EAD8", "1. c #E4C4B0", "2. c #E18968", "3. c #E28664", "4. c #E28F6F", "5. c #DED1C7", "6. c #B7B8B3", "7. c #BBBBB7", "8. c #6773BF", "9. c #97A1E8", "0. c #9EA093", "a. c #A3A599", "b. c #DDDECE", "c. c #DADCC2", "d. c #E0A58D", "e. c #DF6F51", "f. c #DF8568", "g. c #7A6257", "h. c #6D6D69", "i. c #6D6E69", "j. c #6E6E6B", "k. c #6E6E6F", "l. c #5B61B6", "m. c #A7ACE9", "n. c #9EA094", "o. c #D7D9BE", "p. c #DCDEC6", "q. c #DCDECA", "r. c #DC684D", "s. c #C98470", "t. c #C8CABF", "u. c #C9CAC0", "v. c #CED0C7", "w. c #DADBD2", "x. c #A6A8E4", "y. c #5A61DF", "z. c #5962DE", "A. c #EBECF2", "B. c #1A1B18", "C. c #B0B4A7", "D. c #C7C9AD", "E. c #D2D4BD", "F. c #D5D7C3", "G. c #CE7566", "H. c #787970", "I. c #767873", "J. c #797A77", "K. c #595A89", "L. c #4F50BD", "M. c #535ADF", "N. c #767AE0", "O. c #E0E1E7", "P. c #BFC2A9", "Q. c #C65A55", "R. c #7A3433", "S. c #716680", "T. c #7575E1", "U. c #6565E1", "V. c #6F71DF", "W. c #C1C4E0", "X. c #E6E6E3", "Y. c #E4E4E4", "Z. c #B0B4A8", "`. c #939888", " + c #909282", ".+ c #97998A", "++ c #9EA092", "@+ c #A6A79B", "#+ c #A9AA9E", "$+ c #B2A8A0", "%+ c #9E5796", "&+ c #7770DF", "*+ c #9363A9", "=+ c #B592A9", "-+ c #D1D2CE", ";+ c #DADBD8", ">+ c #DFDFDE", ",+ c #0E110D", "'+ c #363835", ")+ c #454744", "!+ c #525451", "~+ c #5F615E", "{+ c #6B6C70", "]+ c #6562D7", "^+ c #6B67DF", "/+ c #77648D", "(+ c #942F24", "_+ c #D7402D", ":+ c #D05142", "<+ c #C9A7A6", "[+ c #5F5EE0", "}+ c #D7402F", "|+ c #D63B2F", "1+ c #D34441", " ", " ", " . + @ # $ ", " % & * = - ; > , ' ) ) ) ) ! ~ { ", " ] ^ / ( _ : < [ } | 1 2 ", "3 4 ^ 5 6 7 8 9 0 a b c ) d e ) f g ", "3 h i j k l m n o p q r s t u v w x y ", "z A B C D E F n G H I J K L M N O P ", "Q R S T U V W X Y Z ` ...+.@.#.$.%. ", "Q &.*.* =.-.;.>.,.'.).!.~.{.].^./.(._. ", "Q 4 :.<.( _ 8 [.}.|.1.2.3.4.5.6.7.8.9. ", "Q 0.a.b.( c.8 [.}.d.e.f.g.h.i.j.k.l.m. ", "Q n.a.b.o.7 p.[.q.r.s.t.u.v.w.x.y.z.A. ", "B.C.G 5 D.l m E.F.G.H.I.J.K.L.M.N.O. ", "B.4 G j P.E F n G Q.R.S.T.U.V.W.X.Y. ", "Q Z.S `. +.+++@+#+$+%+&+*+=+-+;+>+ ", " ,+. + '+)+!+~+{+]+^+/+(+_+:+<+ ", " [+ }+|+1+ ", " ", " "}; end_of_pixmap $info_pixmap = <<'end_of_pixmap'; /* XPM */ static char * info_qt_ii_xpm[] = { "32 32 5 1", " c None", ". c #CECECE", "+ c #FFFFFF", "@ c #2C4DA0", "# c #000000", " ........ ", " ...++++++++... ", " ..++++++++++++++.. ", " .++++++++++++++++++. ", " .++++++++@@@@++++++++# ", " .++++++++@@@@@@++++++++# ", " .+++++++++@@@@@@+++++++++# ", " .+++++++++++@@@@+++++++++++# ", " .++++++++++++++++++++++++++#. ", ".++++++++++++++++++++++++++++#. ", ".++++++++++@@@@@@@+++++++++++#. ", ".++++++++++++@@@@@+++++++++++#..", ".++++++++++++@@@@@+++++++++++#..", ".++++++++++++@@@@@+++++++++++#..", ".++++++++++++@@@@@+++++++++++#..", ".++++++++++++@@@@@+++++++++++#..", " .+++++++++++@@@@@++++++++++#...", " .+++++++++++@@@@@++++++++++#...", " .++++++++++@@@@@+++++++++#... ", " #+++++++@@@@@@@@@++++++#.... ", " #++++++++++++++++++++#.... ", " #++++++++++++++++++#.... ", " ##++++++++++++++##.... ", " .###++++++++###..... ", " ...###++++#....... ", " ....#+++#..... ", " .#+++#.. ", " #++#.. ", " #+#.. ", " ##.. ", " ... ", " .. "}; end_of_pixmap $error_pixmap = <<'end_of_pixmap'; /* XPM */ static char * error_xpm[] = { "34 34 4 1", " c None", ". c #000000", "+ c #F9BD3B", "@ c #CECECE", " ", " ... ", " .+++. ", " .+++++.@ ", " .+++++.@@ ", " .+++++++.@@ ", " .+++++++.@@ ", " .+++++++++.@@ ", " .+++++++++.@@ ", " .+++++++++++.@@ ", " .++++...++++.@@ ", " .++++.....++++.@@ ", " .++++.....++++.@@ ", " .+++++.....+++++.@@ ", " .+++++.....+++++.@@ ", " .++++++.....++++++.@@ ", " .++++++.....++++++.@@ ", " .++++++++...++++++++.@@ ", " .++++++++...++++++++.@@ ", " .+++++++++...+++++++++.@@ ", " .++++++++++.++++++++++.@@ ", " .+++++++++++.+++++++++++.@@ ", " .+++++++++++++++++++++++.@@ ", " .++++++++++++..+++++++++++.@@ ", " .+++++++++++....++++++++++.@@ ", " .++++++++++++....+++++++++++.@@ ", " .+++++++++++++..++++++++++++.@@ ", " .+++++++++++++++++++++++++++.@@@ ", " .+++++++++++++++++++++++++++.@@@ ", " .+++++++++++++++++++++++++.@@@@ ", " .........................@@@@@ ", " @@@@@@@@@@@@@@@@@@@@@@@@@@@ ", " @@@@@@@@@@@@@@@@@@@@@@@@@ ", " "}; end_of_pixmap $dna_canvas_pixmap = <<'end_of_pixmap'; /* XPM */ static char * perlprimer_dna_canvas_2_aa_xpm[] = { "528 52 306 2", " c None", ". c #EEEEEE", "+ c #000000", "@ c #1B1B1B", "# c #1C1C1C", "$ c #545454", "% c #DBDBDB", "& c #DEDEDE", "* c #C8C8C8", "= c #484848", "- c #1A1A1A", "; c #181818", "> c #424242", ", c #C4C4C4", "' c #E8E8E8", ") c #D0D0D0", "! c #191919", "~ c #9A9A9A", "{ c #ECECEC", "] c #A2A2A2", "^ c #171717", "/ c #E3E3E3", "( c #212121", "_ c #595959", ": c #7C7C7C", "< c #858585", "[ c #474747", "} c #EEE7E7", "| c #F2AFAF", "1 c #525252", "2 c #555555", "3 c #DFDFDF", "4 c #4F4F4F", "5 c #AEAEAE", "6 c #9B9B9B", "7 c #060606", "8 c #E5E5E5", "9 c #5D5D5D", "0 c #8B8B8B", "a c #C7C7C7", "b c #373737", "c c #151515", "d c #777777", "e c #333333", "f c #626262", "g c #2A2A2A", "h c #C3C3C3", "i c #A5A5A5", "j c #6E6E6E", "k c #232323", "l c #131313", "m c #878787", "n c #676767", "o c #DDDDDD", "p c #4E4E4E", "q c #6B6B6B", "r c #EBEBEB", "s c #D2D2D2", "t c #070707", "u c #7E7E7E", "v c #727272", "w c #141414", "x c #848484", "y c #353535", "z c #5E5E5E", "A c #EFD6D6", "B c #F67B7B", "C c #FE0000", "D c #D7D7D7", "E c #616161", "F c #EAEAEA", "G c #606060", "H c #505050", "I c #909090", "J c #767676", "K c #585858", "L c #161616", "M c #ACACAC", "N c #EDEDED", "O c #5F5F5F", "P c #969696", "Q c #C9C9C9", "R c #E4E4E4", "S c #444444", "T c #7F7F7F", "U c #888888", "V c #707070", "W c #8E8E8E", "X c #B1B1B1", "Y c #ADADAD", "Z c #949494", "` c #464646", " . c #8A8A8A", ".. c #030303", "+. c #D8D8D8", "@. c #E9E9E9", "#. c #818181", "$. c #BABABA", "%. c #E6E6E6", "&. c #494949", "*. c #7A7A7A", "=. c #F1C4C4", "-. c #F86262", ";. c #FB3030", ">. c #F39B9B", ",. c #0A0A0A", "'. c #CFCFCF", "). c #090909", "!. c #D3D3D3", "~. c #272727", "{. c #1F1F1F", "]. c #808080", "^. c #6C6C6C", "/. c #B9B9B9", "(. c #383838", "_. c #A9A9A9", ":. c #565656", "<. c #DCDCDC", "[. c #0B0B0B", "}. c #E2E2E2", "|. c #0F0F0F", "1. c #C6C6C6", "2. c #D5D5D5", "3. c #D6D6D6", "4. c #F2B5B5", "5. c #CDCDCD", "6. c #0D0D0D", "7. c #A6A6A6", "8. c #414141", "9. c #CECECE", "0. c #D1D1D1", "a. c #E7E7E7", "b. c #E0E0E0", "c. c #5C5C5C", "d. c #7B7B7B", "e. c #DADADA", "f. c #F48F8F", "g. c #FD0E0E", "h. c #6D6D6D", "i. c #252525", "j. c #828282", "k. c #C0C0C0", "l. c #8C8C8C", "m. c #4C4C4C", "n. c #787878", "o. c #919191", "p. c #3B3B3B", "q. c #4B4B4B", "r. c #E1E1E1", "s. c #303030", "t. c #F0CCCC", "u. c #9C9C9C", "v. c #A7A7A7", "w. c #111111", "x. c #AAAAAA", "y. c #8F8F8F", "z. c #8D8D8D", "A. c #B8B8B8", "B. c #323232", "C. c #242424", "D. c #B0B0B0", "E. c #393939", "F. c #1D1D1D", "G. c #CCCCCC", "H. c #EEE0E0", "I. c #FF0000", "J. c #959595", "K. c #F49494", "L. c #636363", "M. c #BCBCBC", "N. c #F0CDCD", "O. c #F1BABA", "P. c #1E1E1E", "Q. c #2D2D2D", "R. c #B6B6B6", "S. c #F76666", "T. c #8B5A00", "U. c #A44600", "V. c #CF2400", "W. c #9A4D00", "X. c #FFA500", "Y. c #FFA100", "Z. c #FF7200", "`. c #FF6600", " + c #FF9400", ".+ c #104E8B", "++ c #FF6A00", "@+ c #FF5A00", "#+ c #FF9B00", "$+ c #1E90FF", "%+ c #FFA400", "&+ c #FF9900", "*+ c #FF9C00", "=+ c #191970", "-+ c #916D6D", ";+ c #391560", ">+ c #9D6060", ",+ c #F60707", "'+ c #551252", ")+ c #F00107", "!+ c #956868", "~+ c #E41919", "{+ c #B14C4C", "]+ c #827B7B", "^+ c #8B7272", "/+ c #CA3434", "(+ c #070000", "_+ c #650000", ":+ c #CB0000", "<+ c #2D0000", "[+ c #430000", "}+ c #EF0000", "|+ c #F1BEBE", "1+ c #FE0E0E", "2+ c #596ABC", "3+ c #F00910", "4+ c #248CF8", "5+ c #77569A", "6+ c #3481E6", "7+ c #F85E5E", "8+ c #EFDDDD", "9+ c #536DC2", "0+ c #FC0103", "a+ c #AF325A", "b+ c #4B73CC", "c+ c #2D85ED", "d+ c #4E3966", "e+ c #F00408", "f+ c #553762", "g+ c #A01E37", "h+ c #F10407", "i+ c #3F3E6F", "j+ c #204881", "k+ c #FF7900", "l+ c #FF0A00", "m+ c #FF8400", "n+ c #FF4100", "o+ c #FF0900", "p+ c #A94200", "q+ c #F70500", "r+ c #935300", "s+ c #A24800", "t+ c #D12300", "u+ c #F80400", "v+ c #EEEDED", "w+ c #F0D2D2", "x+ c #FE0D0D", "y+ c #F76B6B", "z+ c #FB2F2F", "A+ c #EEE8E8", "B+ c #F1B9B9", "C+ c #F49999", "D+ c #FE0505", "E+ c #F3A5A5", "F+ c #FE0303", "G+ c #FE0404", "H+ c #F85555", "I+ c #F48E8E", "J+ c #F85C5C", "K+ c #979797", "L+ c #C1C1C1", "M+ c #515151", "N+ c #202020", "O+ c #050505", "P+ c #C2C2C2", "Q+ c #3C3C3C", "R+ c #343434", "S+ c #282828", "T+ c #939393", "U+ c #363636", "V+ c #5B5B5B", "W+ c #D4D4D4", "X+ c #0E0E0E", "Y+ c #B7B7B7", "Z+ c #747474", "`+ c #989898", " @ c #BEBEBE", ".@ c #9F9F9F", "+@ c #222222", "@@ c #2F2F2F", "#@ c #535353", "$@ c #BDBDBD", "%@ c #CBCBCB", "&@ c #D9D9D9", "*@ c #010101", "=@ c #717171", "-@ c #BFBFBF", ";@ c #292929", ">@ c #3E3E3E", ",@ c #313131", "'@ c #9E9E9E", ")@ c #9D9D9D", "!@ c #5A5A5A", "~@ c #080808", "{@ c #B2B2B2", "]@ c #040404", "^@ c #BBBBBB", "/@ c #A4A4A4", "(@ c #6F6F6F", "_@ c #CACACA", ":@ c #404040", "<@ c #646464", "[@ c #0C0C0C", "}@ c #2B2B2B", "|@ c #868686", "1@ c #A8A8A8", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # # # $ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . # & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * = - ; > , . . . . . . . . . . ' . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ) ! ~ { . ] ^ * . . . . . . . . / ( . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . _ : . . . . < [ . . . . . . . . % + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } | . . . + % . . . . & 1 ! - 2 3 . . + 4 ; + 5 . 6 7 8 . 9 0 a b # c d . . + 4 ; < e # f + % . . . . + f # g h . . + 4 ; i + % . + j k l m $ c n . o p @ - q r . + 4 ; i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; , . . . . s t r . + % . . + % @ + b o p @ - q r . + 4 ; i . . . + 4 ; u b # c d . . + v k w x . s y # z + % o p @ - q r . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A B C | . . + @ # # # D 4 E F F G H . . + I . J q . K L M N ; D O P . Q + R . + I . S T . U + % . . . . + V . W e . . + I . { + % . + : . X + Y s + R H Z . 8 ` .. + I . { . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..+.. . . . @.+ 3 . + % . . + % % + . H Z . 8 ` .. + I . { . . . + I . 9 P . Q + R . + #.. $...%.&.*.. U + % H Z . 8 ` .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . =.-.;.>.. . . + % . . . . ,.'.. . '.).. . + !.. , ~.. {.].^./.(.. r _.u :.+ <.. + !.. [.s . }.+ % . . . . + ) . R + 8 . + !.. . + % . + s . % + F . + % [.@ # # |.H . + !.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; 1.. . . . 2.7 r . + % . . + % % + . [.@ # # |.H . + !.. . . . . + !.. r _.u :.+ <.. + 3.. N + <.[.s . / + % [.@ # # |.H . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } 4.;.-.=.. . . . + % . . . . ,.'.. . 5.6.. . + % . . ( 7.8.* g V < . ` :.] 9.+ % . + % . [.0.. 8 + % . . . . + s . / + a.. + % . . + % . + % . % + . . + % ).9.. . b.8 . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . c.d.. . . . .` . . + e.. %.+ % % + . ).9.. . b.8 . + % . . . . . + % . ` :.] 9.+ % . + % . . + % ).s . R + % ).9.. . b.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A f.g.f.A . . . . . + % . . . . p E F F G 4 . . + % . . h.i.j.. # @ s . t k.. l.+ 3.. + % . m.n.. o.+ % . . . . + u . 0 p.. . + % . . + % . + % . % + . . + % q.f { r.s.U . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } t.. !.- u.N . v.^ a . . w.x.. y.+ % & + @.q.f { r.s.U . + % . . . . . + % . t k.. l.+ 3.. + % . . + % = u . z.+ <.q.f { r.s.U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . =.-.;.4.} . . . . . . + % . . . . <.H ! # 2 o . . + % . . A.+ h . 9 B.. . ].c C.d ~.D.. + % . 2.E.# O + % . . . . + _ F.e G.. . + % . . + % . + % . % + . . + % % m.! L n { . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H.f.I.t.. 5.= ! - > , . . . J.; k u + % N s.C.% m.! L n { . + % . . . . . + % . ].c C.d ~.D.. + % . . + % s e @ :.+ r.% m.! L n { . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.;.-.=.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A K.C 4.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L.M.. 5 @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . t.g.f.A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.-.C O.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . l.P.F.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I.. . . . t.} . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.S.C =.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I.I.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . =.C C N.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . T.T.T.T.I.I.I.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.U.C V.W.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . T.X.X.I.I.I.I.I.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.Y.Z.I.`. +T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . T.X.X.I.I.I.I.I.X.X.X.X.X.X.X..+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+X.X.X.X.X.X.X.X.Y.++I.@+#+X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . + + + + + . . + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X..+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+.+X.X.X.X.X.X.X.X.%+&+++*+X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + . . . + . . . . . ", ". . . . . . + . . . . . . + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X..+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+.+X.X.X.X.X.X.X.X.X.%+Y.X.X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + . . . + . . + . . . . . ", ". . . . . . + . . . . . . + . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . . . . . . . + . . + . . . . . ", ". . . . . . + + + + . . . . . . . . . . + T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=++ T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + . . . . . . . + + . . . . . . . . . ", ". . . . . . . . . . + . . . . . . . . . + T T T T T T T T T T T T T T T T T T T T T T T T T T T T T -+T T T T T T T T T T T T T T T T T T T T T + =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+;+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=++ T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + . . . . . . . . . + . . . . . . . . ", ". . . . . . . . . . + . . . . . . . . . + T T T T T T T T T T T T T T T T T T T T T T T T T T T T >+,+-+T T T T T T T T T T T T T T T T T T T T + =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+'+)+;+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=++ T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + . . . . . . . . . + . . . . . . . . ", ". . . . . . + . . . + . . . . . . . . . + T T T T T T T T T T T T T T T T T T T T T T T T T T T !+~+{+]+T T T T T T T T T T T T T T T T T T T T + =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+'+)+'+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=++ T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + . . . . . + . . . + . . . . . . . . ", ". . . . . . + . . . + . . . . . . . . . + T T T T T T T T T T T T T T T T T T T T T T T T T T ^+/+/+^+T T T T T T T T T T T T T T T T T T T T T + =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+'+)+'+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=++ T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T + . . . . . + . . . + . . . . . . . . ", ". . . . . . . + + + . . . . . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + (+_+:+<++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [+}+[++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . . . . + + + . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . |+1+4.. . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X..+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+2+3+2+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+4+5+6+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+.+X.X.X.X.X.X.X.X.X.X.X.X.X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . N.S.7+8+. . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X..+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+2+3+2+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+9+0+a+b+c+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+$+I.I.I.I.I.X.X.X.X.X.X.X.X.X.X.X.X.X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+7+S.N.. . . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X..+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+d+e+d+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+f+g+h+g+i+j+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+.+I.I.I.I.I.I.X.X.X.X.X.X.X.X.X.X.X.X.X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1+|+. . . . . . . . . . . . . . . . . . . . . . T.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.k+l+k+X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.&+m+n+o+n+m+&+X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.I.I.I.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . =.;.f.} . . . . . . . . . . . . . . . . . . . . . . T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.p+q+p+T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.r+s+t+u+t+s+r+T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.I.I.I.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.T.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v+w+-.-.A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } | g.| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+x+7+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.I.I.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v+A y+z+=.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } B g.| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+x+7+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A+B+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A+C+D+E+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.F+>.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+x+7+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A+C+G+H+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A+B+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+x+7+|+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v+8+I+J+x+7+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+1+t.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v+A+8+|+7+x+7+|+8+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # # ~.K+. . . + n.. . . % + . . . { - [ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * = - ; > , . . . + @ # F.L y a . . + @ # # # $ . . . . . 1 L+. . . . 5.M+@ # k u { . . . . . . . . . . 5.M+@ # k u { . . . . . # & . . . . . . . . + % . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+t.. . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+x+7+|+. . . + @ # F.L y a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . 5.N+v.. . + O+P+. . % + . . . Y Q+w 3.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ) ! ~ { . ] ^ * . . + % . . N *.R+. . + % . . . . . . . . { S+N . . . s ; T+F . s U+*.. . . . . . . . . s ; T+F . s U+*.. . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+|+7+1+t.. . + % . . N *.R+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . ] U+. . + V+g ' . % + . . . 2 U G : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . _ : . . . . < [ . . + % . . . s O+. . + % . . . . . . . . M.2 . . . . O V . . . . 1.0 . . . . . . . . . O V . . . . 1.0 . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8+t.. . . + % . . . s O+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . & ..r . + W+~.v . % + . . }.X+e.Y+N+N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; , . . . . s t r . + % . . { Z+B.. . + % . . . . . . . . d.`+. . . . ! @. . . . . . . . + f # g h . . ! @. . . . . . . . . . . + % . .@+@@ @@k.. . + % a b # c d . . + v k w x . 9.e # f + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + v k w x . . + v k w x . o p @ - q r . + 4 ; i . . . + 4 ; u b # c d . . + v k w x . s y # z + % o p @ - q r . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . { Z+B.. o p @ - q r #@#.. . l.H o p @ - q r . + 4 ; i .@+@@ @@k.. o p @ - q r . . . . + f # g h . . + 4 ; i + % . + j k l m $ c n . o p @ - q r . + 4 ; i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . r + b.. + % $@t $@% + . . P [ . . +@D.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..+.. . . . @.+ 3 . + @ # # w ` %@. . + @ # # # D . . . . E.&@. . . . *@3.. . =@# # # . . + V . W e . . *@3.. . =@# # # . . . . . + % . 6.-@. `+: . . + % O P . Q + R . + #.. $...%.` T . U + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + #.. $...%.. + #.. $...%.H Z . 8 ` .. + I . { . . . + I . 9 P . Q + R . + #.. $...%.&.*.. U + % H Z . 8 ` .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # # w ` %@. H Z . 8 ` .X ;@. . B.5 H Z . 8 ` .. + I . { 6.-@. `+: . H Z . 8 ` .. . . . + V . W e . . + I . { + % . + : . X + Y s + R H Z . 8 ` .. + I . { . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . &@O+r . + % . v ~.W++ . . >@X+# # ,.#@. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; 1.. . . . 2.7 r . + % N $.! m . . . + % . . . . . . . r.,@. . . . . c L+. . . . % + . . + ) . R + 8 . c L+. . . . % + . . . . . + % . :.# G '@%.. . + % r _.u :.+ <.. + 3.. N + <.[.s . }.+ % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + 3.. N + <.. + 3.. N + <.[.@ # # |.H . + !.. . . . . + !.. r _.u :.+ <.. + 3.. N + <.[.s . / + % [.@ # # |.H . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % N $.! m . . [.@ # # |.H N ( $@1.P.N [.@ # # |.H . + !.. . :.# G '@%.. [.@ # # |.H . . . . + ) . R + 8 . + !.. . + % . + s . % + F . + % [.@ # # |.H . + !.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . )@E.. . + % . ' g !@+ . !.X+a.. . s ~@& . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . c.d.. . . . .` . . + % . . {@]@^@. . + % . . . . . . . /@(@. . . . . _ =@. . . . % + . . + s . / + a.. _ =@. . . . % + . . . . . + % . . _@l.:@y . . + % ` :.] 9.+ % . + % . . + % [.0.. 8 + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + % . . + % . + % . . + % ).9.. . b.8 . + % . . . . . + % . ` :.] 9.+ % . + % . . + % ).s . R + % ).9.. . b.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . {@]@^@. ).9.. . b.8 . u <@^.d.. ).9.. . b.8 . + % . . . _@l.:@y . ).9.. . b.8 . . . . + s . / + a.. + % . . + % . + % . % + . . + % ).9.. . b.8 . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . _@N+_.. . + % . . L+]@+ . u H . . . . y m . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . !.- u.N . v.^ a . . + % . . . f @@{ . + % . . . . . . . f X . . . . . s ! l.a.. ) p [@. . + u . 0 p.. . s ! l.a.. ) p [@. . . . . + % / |. @. A.w.. . + % t k.. l.+ 3.. + % . . + % m.n.. o.+ % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + % . . + % . + % . . + % q.f { r.s.U . + % . . . . . + % . t k.. l.+ 3.. + % . . + % = u . z.+ <.q.f { r.s.U . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . f @@{ q.f { r.s.U . D # # 2.. q.f { r.s.U . + % . / |. @. A.w.. q.f { r.s.U . . . . + u . 0 p.. . + % . . + % . + % . % + . . + % q.f { r.s.U . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + @ # @ }@u.. . . + % . . . n.+ . ~.6 . . . . |@g . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.= ! - > , . . . + % . . . 3 w z.. + % . . . . . . . g ' . . . . . . s !@# - P.<@s . . + _ F.e G.. . . s !@# - P.<@s . . . . . + % . `+F.@ ~.1@. . + % ].c C.d ~.D.. + % . . + % 2.E.# O + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . + % . . + % . + % . . + % % m.! L n { . + % . . . . . + % . ].c C.d ~.D.. + % . . + % s e @ :.+ r.% m.! L n { . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . 3 w z.% m.! L n { . . q.[ . . % m.! L n { . + % . . `+F.@ ~.1@. % m.! L n { . . . . + _ F.e G.. . + % . . + % . + % . % + . . + % % m.! L n { . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L.M.. 5 @ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . l.P.F.Q.R.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "}; end_of_pixmap } # And here endeth the incredibly convoluted code ... # Can I just say that seven years on, I'm amazed at how well this software has # held its own in the primer-design-verse. PerlPrimer started its life back in # 2003 as a quick project to help a labmate; I never expected it to be useful # for so long. Many thanks to all who have written to me over the years with # thanks, suggestions and bugfixes; maybe one day I'll finish off the # unequal-loop dimer code ... # # Cheers and best wishes, # Owen # # (17 March 2010) perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial.html000066400000000000000000000473651304100213300237730ustar00rootroot00000000000000 PerlPrimer


PerlPrimer - open-source PCR primer design

About | News | Download | Tutorial | Methodology | Links | Screenshots | Contact
SourceForge.net Logo

Contents

Introduction (and sample files)

Standard PCR
Adding restriction enzyme cloning sequences
Saving a printable summary of the selected primer pair
Exporting selected primers to a spreadsheet

Real-time PCR

Sequencing Primers

Advanced Features
Finding primers
Amplifying user-defined regions
Specifying PCR component concentrations
Advanced BLAST searching
Using a local BLAST server
Using other programs to communicate with PerlPrimer

Introduction

This tutorial covers most of the commonly used features of PerlPrimer, for standard PCR, real-time PCR and sequencing, using the human gene PGR (encoding the Progesterone Receptor protein) as an example. Example files for each section may be downloaded below (right-click on the links and choose either "Save Target As ..." or "Save Link to Disk ...", depending on your browser).

Human PGR cDNA sequence
Human PGR genomic sequence

standard PCR perlprimer file
real-time PCR perlprimer file
sequencing perlprimer file

The tutorial is based upon PerlPrimer 1.1 - users of older versions may find some features unavailable and are advised to upgrade to the latest version. All screenshots were made using the program running under Linux, however, the program should behave identically under Windows and MacOS X.

In addition to this tutorial, PerlPrimer also comes with extensive "Balloon Help", enabled by default (and which can be turned on or off in the Help menu). Simply hold the mouse pointer over a button or feature and a explanatory message should be displayed.

Standard PCR

While a DNA sequence may be opened from the sequence input area (click on the icon), the simplest means of retrieving a sequence is to use the Ensembl database tool. Click on the "Retrieve gene from Ensembl" icon or select it from the File menu. Enter "pgr" as the gene name and select "Homo_sapiens" as the organism. Make sure that "cdna" is set as the retrieval type, and click OK.

You should see a screen similar to that shown below (note that on earlier versions the amplification range will not be set automatically - click the "Set from ORF" button to do so) The range will now be set by default to the largest Open Reading Frame (or ORF) present in the sequence. The range is displayed by two boxes around the DNA sequence - one orange (the outer range) and one blue (the inner range). All primers will be found within the orange box and outside of the blue box.

(The following example considers cloning a gene by PCR amplification into an expression plasmid. Note, however, that primers may be designed around other regions of interest as well as genes or that amplification boundaries may be entered manually (see the section "Advanced uses" below))

To clone the cDNA ORF into an expression plasmid, we will generally want a forward primer before or encompassing the initiating ATG, and a reverse primer before the terminal stop codon. The simplest way to do this to use the "Find primers for cloning" command from the Tools menu - this will automatically decrease the 5' outer range and 3' inner range until primers pairs are found. (There is another way to do this - see the "Advanced Features" section below). Using the standard default parameters, this should find four primer pairs, the first one amplifying from bases 1443 -> 4226. (see screen shot below)

Double-clicking on this primer pair (or pressing the right arrow key on the keyboard) will switch to the Primers tab, showing primer information and stable primer-dimers. The most stable extensible primer-dimer is fairly weak (dG37 of -2.00 kcal/mol), and although the non-extensible Forward vs. Forward dimer may slightly reduce the primer population, it should not cause problems for the purpose of cloning the gene. (screen shot)

There is also a button on this page to BLAST primers using the NCBI BLAST server. Click on this and wait for the BLAST results to come up (this may take a minute or two - the server can be quite slow at times) Limit the results to human only by typing "Homo" into the search box and clicking the Search button. (screen shot) The forward primer is fairly unique, and although the reverse primer is less so it should be OK to amplify from. Return to the Standard PCR page and select this primer pair.

Adding restriction enzyme cloning sequences

The example provided here will clone the PGR ORF into GFP-fusion constructs (Clontech plasmids EGFP-C1 and EGFP-C2), creating both an N-terminal and a C-terminal fusion protein when translated. Select "Add cloning sequences" from the Tools menu. This will bring up a dialogue box with the ability to select restriction enzyme sites to add to the 5' end of each primer. By default, only those 6-base cutting enzymes that will not cut the DNA sequence within the outer range are listed. Looking at the restriction sites present in the plasmid MCS and comparing to the list of non-cutting enzymes, we can see that BglII and EcoRI can be used. Select these (see below) and click OK. Finally, we need to add the frame that the sites lie in, relevant to the EGFP gene (this is obviously only necessary when creating fusion proteins). Looking at the plasmid MCS, it should be clear that both BglII for EGFP-C1 and EcoRI for EGFP-N2 are in frame 0 (where the frame can be one of 0, 1 or 2 and 0 represents a sequence in frame). Enter "0" into each frame box.

EGFP-C1 MCS:

EGFP-N2 MCS:

Clicking on the icon next to the DNA graphic (also by right-clicking on the DNA graphic) will bring up an alignment of the DNA sequence and primers with cloning sequences attached. The ORF is translated and codons are highlighted (see below). Note the inserted Adenine base between the forward primer and the cloning site.

Saving a printable summary of the selected primer pair

Clicking on the icon (or selecting "Generate Report" from the Tools menu) will save a text file listing the selected primer pair, statistics and primer-dimers of each primer and an alignment of the primers against the DNA sequence.

Exporting selected primers to a spreadsheet

If satisfied with a primer pair or a group of primer pairs, right-click on the primer list and choose "Copy", or simply press Ctrl-C. The primers are copied in a tab-delimited format that can be pasted into any spreadsheet application.

Real-time PCR

Designing real-time PCR primers with PerlPrimer is a simple and straightforward process. This tutorial uses the Human PGR gene as an example.

Click on the "Retrieve gene from Ensembl" icon (picture) or select it from the File menu, enter "pgr" as the gene name and select "Homo_sapiens" as the organism. Click OK. PerlPrimer will automatically download the genomic and cDNA sequences for the gene. You should have a screen similar to that below.

Click on the "Find Primers" button. PerlPrimer runs the application Spidey, which finds intron/exon boundaries (displayed as white lines across the DNA graphic). It then selects primer pairs that will produce an amplicon within the bounds specified, which span at least one intron/exon boundary and in which at least one primer pair lies across an intron/exon boundary. (Thus the primers should only amplify cDNA and will not amplify genomic DNA) Primers are sorted by extensible primer-dimer stability. In this case, no primers are found on the first run - probably because the regions around the intron/exon boundaries have an abnormally high or low GC content. To test this, try changing the cutoffs for GC% from 40%-60% to 35%-65% (you can do this in the Preferences, under the "Exclusions" tab).

Searching for primers again now yields over 400 primer pairs (see below).

Of these, the first primer pair should be fine - BLAST searching as detailed above suggests that both primers are specific. Clicking on the icon now displays the intron/exon boundaries with the primers aligned (see below).

Sequencing Primers

Finding sequencing primers for any sequence is also extremely simple. Using the example above of the human PGR cDNA sequence, open the sequence (or retrieve it from Ensembl), check that the default "Primers every ... bases" is appropriate for your average sequence read-length, and click the "Find Primers" button. Suitable primers will be listed, or if a gap is found where no primers match the required parameters, a dialogue will displayed informing the user that the search parameters will need to be relaxed. By default, primers are only listed if all primer-dimers have a dG37 less than -5 kcal/mol - this can be varied if required. In this case, we need to increase the maximum read length to 800bp to find primers (screenshot).

Advanced Features

Finding primers

Two other buttons in the "Standard PCR" and "Bisulphite PCR" pages that have not been mentioned above are the "Find Inwards" and "Find Outwards" buttons - useful for amplifying regions of interest. The "Find Inwards" button reduces the inner amplified range by 10 base increments until suitable primers are found; the "Find Outwards" does the opposite - increasing the outer amplified range by 10 base increments until primers are found.

The inner and outer ranges can also be adjusted by using the mouse - dragging with the left mouse button affects the inner range; dragging with the middle mouse button (or the left button with the Ctrl key held down) affects the outer range.

Amplifying user-defined regions

While PerlPrimer will by default automatically find the largest ORF (or CpG islands) in a sequence, it is also possible to use a user-defined region, or number of regions. This can be achieved by capitalising the region of interest (most word processing/text editor programs should be able to do this) before opening the sequence in PerlPrimer, and selecting "Defer to capitalised regions" in the Preferences under the "General" tab. (click on the icon or select "Preferences" from the Tools menu). The internal ORF and CpG island detection algorithms are now bypassed.

It is also possible to manually specify the base positions to amplify from by typing in the numbers into the Amplified Range section.

Specifying PCR component concentrations

PCR component concentrations are set by default to the standard PCR conditions of 1.5mM Mg++, 50mM Monovalent cations (i.e. Na+, K+, etc), 0.2 mM dNTPs and a final concentration of each primer as 200mM.

These concentrations can be changed in the Preferences under the "General" tab (click on the icon or select "Preferences" from the Tools menu). Please note that changing any of these values (including the primer concentration) will affect the Tm.

Advanced BLAST searching

Most parameters of BLAST searches (including the Expect value, the database and the ability to limit searches to an organism) can be set in the Preferences, under the "Bioinformatics" tab.

Using a local BLAST server

As of version 1.1.5, users may install and use a local BLAST server and data files. Binaries and databases are available from the NCBI ftp site - please see the README file for stand-alone BLAST included with the BLAST distribution for details on setting up the server and database files.

Once a local BLAST server has been setup, select "Use local BLAST server" from the "BLAST" tab in the Preferences dialogue, select the directory where BLAST server executables are located and select the relevant database file.

Using other programs to communicate with PerlPrimer

As of version 1.1.3, PerlPrimer now establishes a listening socket on local port 2500 which other applications can use to send data directly to PerlPrimer (see Contig Viewer for an example of an application that uses this feature). The data format uses a modified FASTA format with optional PCR amplification boundaries set in the FASTA header; the syntax for the modified description line is

>Name of DNA sequence 5prime_region[?-?] 3prime_region[?-?] page[?]

where the optional page argument represents the PCR tab (thus 1 = Standard PCR, 2 = Bisulphite PCR, 3 = Real-time PCR, etc). An example is:

>Contig2942 test file 5prime_region[53-185] 3prime_region[324-464] GGTTCCAATGGAGTTGTGATAAACGAAGAGCAGCACAAGCTCCCAATACCAAATTTAGTACTACTGACCAATTATAAAGAGTAAATATAGAAGATT AGGGTTTTAAGATCTCTAACAAAATTGCACTGGGAAGAATCTGGTTCTTCAATTTTCTGGGATTTATCAGATCTGAAGAAACTGAAGAGTGAAGTG GTAATCGTTGATAAGTGTATGTTCAAGGGAGGATTGTTTGGATTTACTGAACTGTTGTTTGATGTGGTTTTGGAGAAGATAAGATCTGTGATTTGT AAAACCCAAAACGGATTCTTTTTGTACTTGATTGATAGATCTTCAGTTTTTTTGGGAGTTGAATCAGTTATTGTTTAATTTAGAAATGGAGACACT TGTTGTTGTTTCTCAGCATAAGAATCACTACTATGATAGAACTAGGGGTCAAGCTCCTATTCGATTTGGATCCTTTGGGTCACCTCCTTCTGTAGG GTTCAAAGAGATAAATTGCCGGAATTTTGAATCAAGTGCCGGTATACTTCCTACCCCGTTAAAGGCCTATTCCACACCAGTCACTAAAAAGGGTTA TTCATCCTCGTTTCGTTCCAAAACACCTTCACCCCCATCATCTATCCAGGGTAAATCTCACTCTGAAAGTCAAAAGAAGTCG

To change the PCR amplification boundaries without resending the DNA sequence, simply send the FASTA description line only. The modified FASTA above can also be opened manually using the button in the sequence boxes.

perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/000077500000000000000000000000001304100213300242505ustar00rootroot00000000000000perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/Thumbs.db000066400000000000000000000330001304100213300260150ustar00rootroot00000000000000ࡱ>  Root EntryK**1 2833M  !"#$%&'()*+,-./012345679:;<=>?@ABCDEFGHIJKLNOPQRSTUVWXYZ[\]^_`bcdefghijklmnopqstuvwxyz{|}~  JFIF``C     C   `N" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_V=Po>.ǎc8!b%6*O M V&ݱ\mo%y#=k4b(.4/c"Sd#5WSSK65̍2ܬ"p\;(9ce6S9fF.uF3"KS+ר{hfYU~Ia>^k`jFxjTgQXw)2HPy-9&XBڣcf((45xG~5 ?Mvpk!I<(~7(J?|G~l~Ńgq^cG3WXe&X4)p>=X 8u>h)N}{?yދ2K %V[rjNjt7/Ce >\2(e.q1D;f+ՠJdzzuVu$4QC d|l ǯOF[k1$q$&isK>N1zsL-1^ʯ;L*#$߿0MH엁 h$6q?'sVCp\Aqo`p2w s=*Y̳ Q ا'$lӌ[$3[xGeq[zeC` #ЃXq"=4f;1wF8} %{ MK;m&<́{Z;QXhzլmV|FP#1 QsV̚cG3Iϵ.? ǡ3QUt."hWs}HARs{F qu[Ԇ)/lVM9)c4XA.hKV0ё#NI{ %kաi+{>ZJA9U-w<`}ެ;-Gtg}ep03i3q%A¹rHϥsi{ ;sz zO{PHxCIS(Xcl0{UСH|-B7ɷ|?3j~I+*t ㊯q=^H;llƋ)Zj2  n71sM{'[J7Lu"5e[b#`[Ͷ98a8`ҽu$58asp_es7cs!iF03,nجmOKt}r٨rpU;`&ƝlyB/"t~cENF^'bbH=8Hn'9I"cfO'#>Q ¿1iR;a"ƶ| 9$M8cIqj*l')IS=O_ O?b-?ƚN⩈u#D3iwͦYF]c,R[)L;չ.WW ?9f_ O?b-?Ƶ>T:M^(b! Vh>_O ^o\ǜc˃ ¿1Qw :?5"t}n_ʿ"m*ȼQ=lg7"2GOG]-?ӣl^s^\u[:V,BD n:E6Fw%-rd\7̙\R$#щ<\f2,+^Z^F}|7dr3]o嶡l6kvBGpx'.a\&A:2]dgvq `&Go:+Ǵ ǖP܆lmdž:a!)l28b7Nz~9z&Kgc?mXxkzaUY Dp#xz xa\6YRK=i?o<tvJ7,.5U?3?Ì.57Km/Л=\#rH;8[HO/`[k3c'-v2rs\/imeli‹=6tp[ &W QJr劻ڳ 'JFIF``C     C   `" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6d!/uW1"6c{RW |Vl&ӿk<==r-bc۬{G2;5?f4T" 3|ŝ(_< ʹ#v ,9\ *m Cۋ9fkXX"T2>A'lGCj֓hv/nИ'WnrvIenF<=.Po=٭,49یeOzk~!NݭHۺܕvmhs[Zxo_.aQ!\juR8˨I쵔xo89lیdZޭmk%jGpjH?6H-[឵Ek]EOYIS*WOۢWW%鑌sw\T="Ojr.f%s`P=3xGC,mm-mYLI1 nD=[1)<k[KKX1a/|g{=9c $OךEPEPXw֥3y*W1'f}W}@4Q@6e[Yil ''p9$5=PEPQ_7vѻE JFIF``C     C    `" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Jl i@1\mqo-ycS"d#v@۵33UikӧZd$\c $` (q RXYj'/*Ft:"yB4J*6dz-/mCmb6!YA ﹭ 06vk|"wok-)HQJJƳgf.7/$_nXqrF0W+C2O 8zVޒrAn|cUg7ŤkK Cn8=87kf㨮1L$Pp3=xĐ}J'T7?7 1T:-#z߽ԡ5ۿ[3ᛁ2I4>zowu+J,=GEbMm'hmZ8nҰ?*Nu3J'Ra81WoE 1JFIF``C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz 4a=5rh Catalogw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ăšfs>%huXmĺJ`mC ,N,I^-7~ڽ?i:m9!T F唅1e,k[ѬkoRHѬlf\ gl3FF@!zVUM[ 3RXK]Cc$D%22Frq^/G RM]|n.ǎc8!b%6*O M V&ݱ\mo%y#=k4b(.4/c"Sd#5WSSK65̍2ܬ"p\;(9ce6S9fF.uF3"KS+ר{hfYU~Ia>^k`jFxjTgQXw)2HPy-9&XBڣcf((45xG~5 ?Mvpk!I<(~7(J?|G~l~Ńgq^cG3WXe&X4)p>=X 8u>h)N}{?yދ2K %V[rjNjt7/Ce >\2(e.q1D;f+ՠJdzzuVu$4QC d|l ǯOF[k1$q$&isK>N1zsQNQ`}";%H.s\&vK_[4q8H+!MYYIm8;c9~,YlSTHG~yq[zeC` #ЃXq"=4f;1wF8} %{ MK;m&<́{Z;QXhzլmV|FP#1 QsV̚cG3Iϵ.? ǡ3QUt."hWs}HARs{F qu[Ԇ)/lVM9)c4XA.hKV0ё#NI{ %kաi+{>ZJA9U-w<`}ެ;-Gtg}ep03i3q%A¹rHϥsi{ ;sz zO{PHxCIS(Xcl0{UСH|-B7ɷ|?3j~I+*t ㊯q=^H;llƋ)Zj2  n71sM{'[J7Lu"5e_wURVy96+~{VQ-~Wqӷ9Pԭe%L$RJv5Ӧ+^ӆk<#\4,ŽÜW]eY.w}gn8:5k^5XDKFm('ҋjg|0S?Go]q? ?mTPQEQEQEWxCڿ}E^^9j%ΛGE4 8kۘQ)$DN= bOkjOHthw˕V/-/#WVA9LaPy5!#< z֓#^|E]2d?r.uMK-q\OhRlGom[AǴ ǖP܆lmdž:a!)l28b7Nz~9z&C}O7 <+m[+g C[V}WX\@NG`:p*燠u3lEJ_$U_r>kB@O :m[z-7^ZyAvSQ{o[l%/|NoX\k-f]{j%.n*1k=b8- y( (KAyw>$ݤA>sp;A䞊8uP[ x cFldNNk 7:Mz8QgΒ~#> BS,U{j``$z} img2.png2z} egfp_c1_mcs.png2z} egfp_n2_mcs.png0z} ensembl_20.png$z} img1.pngperlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/egfp_c1_mcs.png000066400000000000000000000357031304100213300271340ustar00rootroot00000000000000PNG  IHDR$g0YbKGD pHYs ,1tIME + IDATx{y{NUtM $ f@ c$N,"#`&`sI6#KN%d Kf0A2BE aI3=3]U~tU=`r6 D$KL`g q!#fJt+SJIK\53 Y3,:esPU!D}|_}9D٨[:c& Ǘ qGl%--Vso xҶ-ϓj`)Azzqp~?3yvTv MdB$-"Zj\qgU3H"D6٘I6:yfY@5\w!!߯_ƯI\܏E7AZimy{+]k)%W6W .'j_4Dx&mKHs~Oy@[*NxSK&߹`B˷&b B5P`~jm\."cY_ S_&)pjm|&N@13 M}"r -79;-B}d'5: FV; *"%{Y=?<K#gR )Ko5xrS,h<dzzMo,gbX,ǨDZh%,y~73˓GU*>IKKs.JIAҒ-d#/T7i8E0܎EbX,3msHN>ضcaߓ/2C֔$P`KTWmR`{OX,`=$lJsK( ƶcaY,b91vE{x߉luʍ-bXdL)};CqjJA!"*fJLkbX,'ӶP$OX,rlro '{bX10ϓŕ[Df<%NRgqecJR]2xeJ{ٔ*so;0?f:v[ yxW9iPsLΰ֍L+@̆~_kb{ho멥(Ⱥ:ꖌe]fy33*ӟǕh=K-|Vuԝ6\Ks!㹸Ol${k0-#g,nc0svAd6e9 X,\fS3\Af'L ReC i F&N\\$Q; h+€aeSdɮΐ?Q9sc:.>x3<wX|dXGKj@h%n4] 4tҺ`npk*JSc%pY@C~XJ.\ଯ)LM>тR#aA+KrҺ$yU.UD6qRT,Q~2KvXyxҺF_=7h)a]^0lM7RzSIs^sho~C O981%gRo,.o,o1kY9YKkWYʬ2}f| ;ލ 4zgqgq:/8ӛiho?x|`.7pa`OZim0z"3,31|xD֔gs.'5ps#JkYsZi#CjR'9{XW3h8MN!,F$WI 4VZ'n`p"Y)a)I! @$ ,0ے{:Q9\96لêU_-U=Ԉ԰#b]n4ʡףwd65NM,OU`)sÏ шoF51\p,e9\#iIq{":* d)Hwpxzlvg J:ZLTZ>#?'#T .IfOIfx4C|ÝY+Fv 'iU 2#KSxEKޞ E'OK faY0:`3x̬ eNKȕn`1ug;d, q^w y.)Iea\@t X›HR9'i)aEɎ'c0,.bLW٘pJBg.'y.®$uVޣP3U^NJ)PcsVARA,eVuph4^n1 > 41$`_%xN{.Ƭrd~ )KOcA2BX$,XN .)JxԔ$eiI$#e.X" ]e8|`Bsluꎊ7{9CZf3@vgƧoZ'Z&mKN/"xmM[t˛Θi}N_ow4pN5J\ٝs2d6Kr5cyڤmI5}$%3Xp.}^wb}H~')紴G^V*?/s٬tˏmveeZYG_(#;~h 5ΐ̗_+kkFZ'sۤm |gid{9 E"Nup~ |q}Wocۇ7xjv~g$U0DeM~$5sy2F$Z*Q;9"Ua7O(fҩ)2'Nwsa%)qe؂ґǧH& ƯL`v\=&T1'գ\1\kemm@p}qJk\mҶd+]#WT,Ħa +[4U6v\*nlUꦱ̵(2)[QXr,#7mr%kinDO\+hy_o%`kwcIX4!J5իl1Q dX|h>w >ڀ//P\x_fGxe{&(sʦD׀_|_)2F6n"v34h~x/xnflfS+ȢDK's )df`VٰЄX "Hr W2m)^.{7@&.W ?In5/pSFyxN\όËOԬ[3o<9κj r:md.n3ଓD\Iǟg 9j~|)8߯apܽ9ݱܪ*ta)QGdY4vd'ɵS>_S9^:d=gJ__KZZډsSpn,iĕTWx?mFi<HKTuي Sp>$i&mg+<o0#BI< w tP_"CŒ5S}vgvn3_T)U]'^wN"#E2}]*ȫ:oVuSp΋揜$̫xgIZ$- 5ƉZqdyMUUUt>kV5g#iɈ'55_%z:I> 2Ww)8YIi6G\q+2MڤQ}Yڤ I 588GH#m>)̺U<'U'>hIpa9)ڤ<юvq%!WVY{qXWmYvj-WQ63i{F> .u8%S-(+*嗃~,K';&=UI0}+Η^hGұH;Ö'WmztnǓ&V-o?F*)|Uv4)[?wjup%i2[y'ZE,VǕ8U<\:Z?˒} ثzIO7аlKi0!Ǖ.z~ȹi Mj0 |N 4̭G˹N\B#g،L+"G;X3Dp*297iwj]%NC&,A6PmUdKN h%cx+WbX,*:A;aMX4˚G2\t\v[v#kkqء#zUrB5|97ie6V%냈hJR] o,Flg˜uҦn)Imz4uY!t"$r춽ïy22j %8\rߤ;Kd/=*0`.l탵{྿g}sih\%"*9+b1:َ|:~'mduT:#$N^,'|q̒t $|Z˥ G~r|WQbX_Rfr-1r;hdOs]o\+54)I+"ioW("KIj%ZYkwI/nxX" Ɇc +VIep=>|"NL\D頸CaC|o7[ߧ=͑&ϵBe$4udfx罺;dQ ca[㶥ءhڷjO=4] =Q6OR bzQH Fre9G-E'oc]!8|y$$|;0EyQ5UH]>x`qHVlؙsbhXd#@/F救>R^Q#f}91b֋z][+kk]t7lgn##> ztF/(S4JꞨ;Gv1t]w=Qm:f6H(r"ulCEo"Y4O 1tm-o+OĕrO$e[+z9[)5$_$S犻ҽN:M1hy2@M\q߉#Kd) ^I1_tPQ}2M?|l! @=12/ hwq7k/Me5r>6OzkL- xTDGq) ȕ dX^/ "zswHxoi wDF⩔̄'? : ;8$;N;5m ]Qh7/a ٿLȃQ#w׾Kc'#j taHo `eBr}ڷEU?Xf"#E;"-s s^~o?EN%7K_ 1"Lˆ9OV#*䌔.HIpJPRtMDQ xp 5kٌru;ٝD+2P#p_Մgt:>2CPFI(Kǵ EC3yZYk9M༥hS75GڨA&$E_ӾGz˻56B#W 1 .J<$p"Ky1@rHrɉu^'Owj Ox\}+ ?,dfA\f46Ӛݾ,u"}G٤}&EuB3BG{ĝ6o۔%ַxϧ#<$2 =NyQEnn96ߛ zyߋہW"C+[Bd=p赏 9IuuMUT 4G{گGi!G`>KwFQ<G{8+OW,dUM(ޔUt q?xt]y;̅Al$m6Q!E]@i?r\0AYd5'#p _H&K}u=u{elnМ"H҈׃hyȠ軝'Y8`~ Ή\sqŃGd&͑'Ӵd b=񜉸3LsygtĽ$=S:ǟuP?PeL|?S#51[ZX6= 4m~YΖ9ʭ9E$ڃR=W!v-y+W+jK/_ɕ3d>)i9˄fq4h>2,E3%-7 ߎnM qn7_oБ!EU}~̨ܽxTTH=@MX?3q D\4h=-dsEizgz-̝nA2 sčY%rux,ȓO>#K\D=7rSd|?b0Ќ^=qp6pZ.jɓx$~7Go6+̥8wfwNu]X weZY[ @sBHVwΗ!~hˏTg&mKidBYI֥ݦ88.&0uPw$˿īspW* Xa#!/yH}.<|K׬|&ьyOeU6E:n7o!Lvŀ2>ɟ5G4498NљdQ6ZTrO?=4ף8Bg?!5QS80ґʐU~&ˍ.2 dI~ƒn:@qXU?{5uѽ{UҵGG bؘ%j깙y ҅Uz@]릤NQ:~=8 7`>ǩQ\ g0kq*9pOr+g%ڕw;8 \i KIɸ-=YiC9ɭ^7]ܴo:#*&[,C4吏-_w鬑Lu^k`= ͅI,#~$A? +1Z!1(b􉈺▶̖{'7ZZ,G4}pܩ2FAX@G)ހD4Jc~CW6~O H4WMl=$֖%,ye" 7x,M.EMF#cxQ4JDD0^kXAb rgYO^1Y!<\R hNIjs 3Վ,ٝ\ -]ɽAAb9~ZQl_Szt>i~pexBWH6ETZs,Jw;ݧ2 7wBYm*ƕO\HiSEb91X$u{uhp"w+ǚh}~' ]"Qꑆ2x/}%bsV0 8|T&PiGB0Kɫr#àjdM=`nvKDg27BR"/~- g639`Nؿxl{Cb9޼# L&sD;1t}O^@߀+=-Fk\xT\>GajrµPC\-FB4Xlqgʹ{~0,pS{-!p|x%|@D|7{d W߉ȝx˧Cb fi^1k%'/Eŭ\ "W{-3b Av)rWW&A~htT:aq5H,]_Y\c~L}N8R|&n!>XKwyC> -cxwmeM@lܪ[_MvB2 7J hs%-sE}?.; Ò<$j4FwOtPJ>_/srF՝^wwpC… D'dl.5P͸Xy!,1$MT磝Cb DavW_}QwݴY-${hL5Y\;Srhz7!B~?onqEyf+xU jFtlPҜxljkZ `0ooR1 Ot7\n;SZ`|^9 Lu6ZbX,ǯwlBqs==]r>=Pz7ǧ!l 0=>4uHG>@6̊|!r.9z=UO:ųnRG^U_Ѽ8)u/̮`!;dbX,-vn23y$To (:}"R{'EMO/)R'Kv3x%W>aw@pn54CxspFXҡh]H'_ X,qIt7aRU5zqMwվ ^8w~ylr}R/ߪWy襷i@rݪ[GG3(') aCX8Oـ88oLV 3OkX,gC<{QMIk,TW^F-Efsb98^@C9e_) \^ő"2 Y mIzG[rQ7PwAFUh⩽v%F60Z{XDnāҎHlrohngi g+{ׁN:yf>T 9'~`zB0_ȏUX,Ž ja `^? AnM^ XaSddJNtb91X$\I>>h`0xgRj0@Q4-H~_@聡>J^!X3й8$^$:C7ݦ,wǿ!.pWlDkFğ{_c0$ 6߰a8fL ?='!y^dx$)D!h<$g ܡ!r^y.ȓpS{ D}4 x yj^EiuLssN7'f'\@6K 67,m1C`ȠR(ECk!HtMZ"l7FX,ł%)df0`ҷUuApq~Zi}kn %-$#aޑ6FbX,jX,$bX, X,b bX,WIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/egfp_n2_mcs.png000066400000000000000000000336071304100213300271510ustar00rootroot00000000000000PNG  IHDR fkfbKGD pHYs ,1tIME ,&e IDATxxT}g4IHCC(acK_q@H7m8wwq7nupwDڸlr@qLHCc`䩝@m'4sFcFf4HyfΜs~WT0 0 08 &0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0 0 0P0 0 0 0C0 0 02 _| =@Bob2fN\/gq[ #= DDMZA}J+-O+l׶I3ՠ|@'ޭ>8q}2_ {}kl̸_f ?̓y9 bԄT Il$Mė&ŗ=~ge}|d4Џ]?6aMqjM;ŗ=;M_m4+o5=\ڽFnܕ.ZK>.ꨞe$Vzw Џj۬ޭ>a! pF32م٨tư\iMg]LJU/]ҝ~/_oiIF'fL¤5J4p/ ACNr ]Ikj&ʐY~=_3z?0:َ$F}F3 KX㟍>"@ZӏZ27gҚ~Ζ֠uFGd\VΠ>dSF y2"-uI'Ӛ~T3TZӏG7Ѵ~٤5cH^̊/lerm+lDΖH' &T/HkG NTw˞heef5}C&pDDŗCR8w_x-]|ƿHA_4ZVE#I_h#նX쾙8VzA=krIG L85h$B!:x3#>hfȍfv o ݜ4ջ3π L$XA23>mE`̦<=r= x0n\Pf&9= qkM4 *74O.Oi.z ;/ђ.*B]e #6";V/>1x=aa;ʈzx=aMrMZs1N먞fd0[REM4{Y 騞ԧu(f( #dC!?ڣr'ԫ}AϾo>3:~W ?49OY)D_гR$TQ*,{|;!mQ!W$21zGzX[}~fM8 tHtITEWG3s/k[sIKKnR"/[r 33rv@Ӆ&|𸙛#vFL:b#N\_e 8˭ ‘r}푞;(W&*~vUUe,aFdk̗Y~F|*ڞMdhmGėg־^}_p$×r=p>}= gy0ߊSܞ#BCrAC!:hXRXV3/ ! )G$]w4gݳg8e\'N\_&rӢ~I_8qgyaEۣP}`zֻg-&ߺM>)RwJk{~:oLۣ0e!bjڍC5 0 0 c:"<<3f4 dy2?\9IbN 0 0 d2Fnn. lg +UUGJ (0.pc3 ,5qFrm.wfnaQJZ> + z֤bzk5=3}69BC87rA/ |m >ckgH05Z{f1gдl3aaa܁aaaQUr/}aZ%oxxאďwha1[^'I6^IZ+ |DصN:0eAT<~p8?ҴVc׳ ݍk8ӄWv (2id ]Ԧ3⡙}a< I if_HmRUH*l3)>L|^l$-znoQxl4ߗi$8M5S1}4Ml)M)y+=?YGZ. )?t.ʳ >)@~>6z&s%J럨-t_(񼚐ȯL}_6ݳ4%w=o m'K&WV[-ۧ :6Y&O 7( 鏥g+h/񞭂 vo*Q#7[ '}T/Xc_K~㿣^~^֤Kk86 ,*y؈NJ0oRJ|0;D qp~I\G=SFwdV{Zi]Pqz;r-%J?׭-U iAXCVq숌=8csId;N;,i(IU^XACv 8:fV($v* evG=Ss^OUjZ_R=l_5t7GWQHNTZes֨_C>% ppm%+Ojč]];oP <^Mw , 3n(nAZ(Q'K">'ܹ`(:t-b(㵎hĢT`!k4E5n*2*r j^ al2*)f(\Bڿr+ ajje(xlf읨^*-վKU|Qgȭ"Y7^"rWd}ZG$ɓdY<K]_S?ÓIҤQ(v| zVǛM7M0*uM9K2H>C8ɲD~*\3TD"87o&nM1SI4U)٢8P93*+$Z{S)uoub*Wf=R7KL8pe)ɢQG;UCbzDUWpƮTտ*άYayaE+^Rۣ7YJe}Y/L~sӰJ%GSA|!u(Ly3Fy*>X*MG},KSM}<ޔߧӳE/'cri2S*E' p̰|jM|Muԉ^KzgA=aky<;:S=6Nۈ"uGD)Zf6e(,H7\>N5~Wc ,v9oG\\]TV"zgSޅRI)(H:t: c:USPnSoTq5.p3*~ _WRSio'W=UicJ?pwNwzRcԙf)N,Ejp89g̗/['ۤZhFk2_ڣdYc~{ %b3 z^')Uݧ{Us8Nz=!ffiIϯ{\-TPzn ac=N߂_bUVUu=5<$N6-&rA'C٥Y?3)-Wl(}dNIl4)̿*elI> .I.|wyS[9ʭ%nB3Aظ߮c~>*ײַ}9\u>ЃJp|RLc%czSJ z-ˋrx \b U[S^,Os&h8^H~J.76y G_ٚJVBN*vI3~vBl}ṿl̟s 5[lQs/'vtU>Bʿ}DLO9EDX(ͅzKETf:I@,N>$S_a|*ɧJdy:UI璘C "E2U#KTW,]E볭+JҿRm`\,ߥ5VtbTUS GFSñg2x>6GOs»zmEB{J_jdaa`"0 0 0 g"(O4}hčxTD昱uH3:PG˾WA_o>5n˺w\ .y2oywWl;㔞zu?aOpRXK˦T;fĮol16h& 0 0 32I$Ikf^O8sg+^!R!Ӏ^IxNwu_GbY&˖ &[>DIsZUGs.+aQgzg޳ƙJ;m$럴)ѷ@%(]dɼ kcY}g;ޘvlO+1 0P0 Moa urET|+c:z˓э&"KwλdQaY_It])=.FVv}{nq[D>-lR_GLf7ֵ SX&ڌd\)'jdq)Y0CaFۍz*/d 3By5|!0Vlc2?W^7O%˓ EÉY!E$Yx?4FzNW=֌>uus>xp gǺ9w Ջz~nҺ+zHeS^%L{WJAҤf[Y=hk֖ F >C3ӿ1=F&~/#& .e^F&/F'6HCr$!mᒈ7QQ8c ~!/@_Q'},[⋿3VNIw㩪#˂eqُ: a,0a!J4M "&ŗ#d/GYA^wsW+#\t+5;E Nhx y/p ?j1|ҼI|9$"$MI:ؖ!H=Q7c4ܹQFw٢iE$d46 0W,5A/9}7KT.e{Kp+y'@rr)bX"-Rztvi/o8қX۸#,~s1*F`T/ŗ#Hq_.Ϟ. 6j:c r $S 8D7t._w*!zn&Uu*@m=;)Ȁ $qnLױUW$?<-̯$#yyx}ҺL t - ;aퟀO@t}? dZI ,#~N:/2a[y zl]qXȚ|XȚ/h:cF}^'#I:&+/3MS3^W Iy|-vWvumǸ|닧~wpA8. >6VPV(/X=}3Offd,[$Ƀ)Rw4t'FGͥHWթ ոSzUiWtpeG/einҡ_IDATct{*:']^xۀ$#(?p H݁FAG^_:FuTOf\鯄_ǣ ak!>I8y3|u j1ܺV $1볯o-{^j;6nmÚԭ(h>lME=2RwC V'` q]{zu7rW֡,{p HEW+$G״'!\TP}RTznjH{DHe3w5}mTN*~MGߩY$EiNT{xбhV3|9zWT/Hq '5gUU=$tuwy|: Jp;͹b8Z]˿?EjtDqd9Q|~4_QFʺ^P_UiͶ{`gA?; }v3]宅()=ᵾZ(D37sQOrͨsT%L]| -7̾6z*3fދ-_/.&hƤTyGZ|?k;gz6!?c}\yD.Jq8KoK-At$ QDz~wyokaS,suF!EjS!/7s`3 lFa4]%#ɀ3Wrh:'[GZ &X8Kt1!{?F~orrwMkoel :f=_( ;ga~Da8_hxyINpžO~ _c Zw7`i]wʝ-7MP}%×Z9]9A}Zh872G.,ƅ TP^1%˻-חTO]Nw9?c'imlVɼK"OOZɑqӁDqnO\o l?G3W[a n&4'1[4D3 Ѭ[5Gt0CJ.yPd^0|Q?W$Yp>y.'m{F1sO,@ns޹܆;$.)?p{MK~kF k]wdRJ^y2oSUܢG`W`#&4oU?,ycoOz#N~ ܲ(_z 9{8ٚ! G/Q!Q/ }:M:];483|?#y?¥No%Lrjݩ1=ҳ C%%ɥT~_<_7r|Ǵ\;8H GISM݌chCі?}[b#|(  j{\[xKDiF}*V$G%S`Z-Osєp~)D(Ih뙉%F\L{[i]; ?JMqyp$:M1q-=,$md c@60`Mtv60lɖd-3`ʪޖ>e 9nCcO/֓$y 7ngtA| Ǟވ~_:ې[zr$ךhCŻr^i/kKk)aZ(r}%tҹKVU68_j4/l+jgU%EUO?'YSVam*&mP[bf=ss]Rg -ދ]{r1fۨzRD,ol6ĖtvADt[[̿1hz9 pyvΚt c_Rt%P`1OKL.ƜhߛTh  WU)w _e}6TE#.H2G,:Y9iqN{KG\aq;^2^ɥ8EZnYi)MM/q逈t_kR1Z'#cV)dBVG<~NzrtV9VR|aVU OSc[:|)=jq[z7lQJR̮v '^X[&˖ acoW[%!yE=mbKS~4 o5Üsa[YF Ljhq}Ezsa ;}GkwYi26T6n'>EW):!|E$n\`{}M q/t@Z'g G;7<&"*hMˑi MȪєz5 3 *o'iNL|Dk#= rf,["-o#{Q9k!%lBp]^ ;9VYhȾ}?9%|abesQ4sX3Ns (ӊk3'G##R㺵< %F=)=j?{8 |3('݌IJF.{@K\:`%0P0clQAyJRlFi g^K'QwOO釹Ȏlk&UcelI|~|dVC|v֏rMr38W9zh9J*H}+B;ΖӰN 8b:>hfA/#ЙSA/;`<>vzTfi$MS{;:8q[t=h>C1 y3NҩܘLweNC:t,:ɩ3|^ ({iʒ'0lĜIX-A 9NtsHwڣ5ygvCN# o1 s^RAB'9xyFif$IG´Dγe4r:+`0x(`IZ1m'DxEhgK^~Xs'@Tz"'8iV>1'4s׉Z<_|\/;ku s2!X`CO+Y|B_6 V230#oeG'Ka୏fb#{yC(Ș-;JO4v|g8^ $@40n`œG0 Jd}!e^E#@2Jq ׯF($ǯswFǾ)|=6Φxx},d QMfav!?3-`-=pu(M}a%+oKev86ڹo>T@XP ;-É6z Iw|V6[SO7;"ON_w9DCcGڼ1_ʓb7P^!og`Zѫى1kd qDl5JiNx1|w\: x ڃ0!4C #y (R.l%qܮ}j} ִK{9=wi01 c.`{+BkMKಳsk]sP`"# x 9+dM èAy6eБ\d2q_s;ey7 (E޳h'2YDZewd*He Ab4g4딞z5zɜ=15u`F3Tc:'/饯e<t?7e"9 dM>Ja`.Js" D$~r6`E#dْ@%7 GI&"$Mc 0f zA=ޘFiNdnc s=)R\4D`Խ!Zvu3ʦ/\<+@WI+(_~0葞Ēdy2 @KK7 >ٌ8yҍ`9bOk3dPO?NHm2I0z،Q~gpT*,= g|'@>S*$yOs:D)"KveaFD;~I5z8ffŗҼI|9 " YEGS5 Q~/Ί]r;:[Кmɞ-eKhB=g-אӟ[na\$rҚ~}>nq$%#}Yuo%.0ĖugUa!N6)o~Uzs, %I-fFmlҡcYΠUr?J#=yφa`GM00L8Ɠ.2(pA?pPf@@A];]B.q GI3A,٣|:|r0 #1nKd;I-ɐY0:Hǖ)=,{@H wM1Gsfaח%{JVv=ϽĶ G(Y{m=|Ylw'hfW4+k_9wxl,c, cn`{ 0 `EϷxweEWvC<ý*p }}ϼz\.W5p;%ꋌ?DGG;u#FZԪ0SAH4UE eYXE\!Bja!wT*B*DnpeA$)^"w0ܥ2<)vxJ_ /h783rc,=\'yz] OGi=oɚ6fD+'~$@{v-àԻO;`\C^@#n$!O(]O?|vcGDǏ-TvJjݷ=rL,`[O)7c9[jIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img1.png000066400000000000000000000631261304100213300256230ustar00rootroot00000000000000PNG  IHDRDZN pHYs ,1tIMEW] IDATxoםMiC5dP_4"Ȉ+3_xe/T*s9Kv[XeãB0a:WO!dsk՞co.̴OT=$=i8һoEf_u-{?6 dR^YPѕ_ʮT& £N>T39ך9;M{R]•p7-L-ͤT8R|.}c)JG\pn_n.U;--\-˨V>];h+ pç#K?Խ-Wk|ҵ㵶x$){"sܷ^SO?y'epGܺӖj S)uQ=1ZѺ.e0+o̤= 9KHZE^^1ᄍlÚ!c,_ɄTqaUzH9Nf].Q83<LS&S;^9~&)QJRK8osI©p.⃣\JJv%l>R3\< /-&LxZþ$wrWɏ0 1/<6˔'oaοiQkӵegk(]=UaoM+/լrOeR񡭍 *>ԄglZ[˥.)Iʚi{[]j'p^Ϧe-IK#m4w~%vvA1N6[1O~WmReʵ.י\Q~t>,az-^;m;2 #cj^>OrN5e;5#m|X;_T-M6-b3\9h 촅p2rqddەl{83kdNz4ʥp6bGZK\.8*=h 7Q锊ţәGBF"a„5|wJ %~kWwݨuCUK tD2VN(][̓I]}QkO/){+iُrGk깬r,hke sPƿ\Rv3|_ohՋzquMfFZ%̓%m^?"cfUzG)xKgjkrleFp:69juy%t,*3k?DgΞU7߭u]S}pᤑm65;3RvÏ)K*٭ڞfx~T gfJ%GGv-\KmRKZTgFʍpM us51.G4a„-8CθZzu֝ǻn~LUu F6oO;uۀeQ%J;;̭)t%9~EW验nsMbAz dV†Q\Wk;%eO/7/-^xlno\mP(hyӖUY'jQ?x[D.ᓩQLm&.s2u1T?}<\|PnNgT,e?j ?h /d⨶mlrnU#Ltw(?׭?;^VO`CX5WNZ=TMub-Lh9Lf9{-jY/2'RZ}aA4r=);zjvyYrgyYO.#+S;o-uj[%oyAc/]񺜄Q6vݙli=vʞFG+eZ _;dێnlۑe-]PTFSGa2K˧sxԅsLvXmczxn זѪ-f2lJJ˲YJ%N%|"]\6˨%|xReT#[]GcڲZ]/Ïb] &Lxd"9P 懛Z=wOYqC8. eDݹuG/w!/dfSJ-ُl-u]^?^˶6>̫to[e{ͩ&S{O, W5]*|UmZ}a-(w2#3SFQ\Vʝjl&)/h V֬[]h{kJ8_bSkAʝ4d[ٴ _umGܒn~6U$%ߕ}`k.+D1s<ܾLfmX=G-fA(sD0 01o ˞wT0ccWVzXҋ/S *NE-)az,4Ĕ҃|KJ=S-V&w(kP\2u7/yE.\m^69eZn;^,lJNs册(3/\e~6:tdfҲZVbwu[?iPTf1,O8-T:l´}I$mn -#Qsf'mY YK΁SPiK}ΦkpPŮm s~ATFGUΨTn ?J{\Q>۸UAچFxlNJ* & T['uݱmCuZxvܘ~.~dj^AVŇp}f\^$&Lx:ûEJhpv(1)(N3kݬ>:J-t즊PK첒3黬ꎙ2Y[Gv۲}>3N[pv1n{>ΝJK-b(][k᎙LQ#C8*gZ sΑ0a>' Q-oj3DٹeTP'7~ѭV-fnM .u`B~M7}+estaR QS|ׅW.`0Q.r{Cԅ%-eOf.D D"`I|7w;z5=^ 2@8x#~ SH  "juF',n8Xݩ/l3:6=a.Lr~%i !4CT8g)X2O3tqD %Nɒd|(.Eq+]?.=mzX2>[f):6tyfRd38wd\wԃ Eq+"zD A @ ;noQB0݂W)!n}xkxAT_`"غlc!A"DSN"?#I] />,cǾǽ59*`R+2VsCGoJ`WwrJ4?7{0kGrH锤guWW=7گ*?ݖIZT|qJB7X4K]RTVyU܎$*oD{J4AdfL1djaaAڏּ͊|^7޹*k&K/s u|Ev}ݻ*ʞ}Q?LJtQ;nimV$$: >*y`TrϼʣMy׷~;7[IIac:;Ӗ3݅ u7dF/kcQ_/R:?g (giU$)3ڏ;~o%[ʹوGJ%ET[7dI{EFR8=+UGJRE#]ZtwEYNJg沺;Te?v7~.Z *M]iyh=?kuߕshˋLFZ93tWZұ4f&W!hQTPZ~vY.^]zm]߸RAvیEtVwwd)Iϯhᥔ5OHgҳiIRQY~,֭r5iېɱs(mG%G[r%aI:3&4ٶtQ7޹̉*l)jem~ؾyu9]۠>3D߼N03F{*ՊG΁(^($Œ&U]ҥֻcƫ֍Q94 7GS;{^+VJj{yer-ig+%JJAI_ZrkU^15 /_kJmZ:o^ML&SFߔtfZ5PspnGN}$5Rʘ򡣝'/ضʁ].igR8Ե<"R©3D]R)ysUӆ*TyE/-ef3G3D-B% IIžOKk疔9Qᑔ߭eAK9i噜̭} Z|vYPS\%]JKZ8;3ڌUBGK~EG b?dr) X1CjߔT痕9u$괅>JiLw4Cٕ̓bt${Fů*}SgZyvY/.+}2-iu4m^љSNik/3C)u􁻆3u!28ٳ+eNdݥs4 TY3FٓKr_mK ɚdfLDL? -RՒ}V,vTB233%eҩZ};UU$eYR 3t}3/J \.?]խoiUe2)--.hyqilmM9sk L QlP!Z_ԅ+T96QqUcx4R[sh6noˮX2ėHOI^rjZ_Զ,͑IeJ'xAd䨪LUW~|Ft&Z:S-R(3X\ջMPu뷴o_믮Ϯ(dIEt6dU*f}!HA-5~9L/١j3ݎ^﮿XBjPy\Ud] IeAŇmvn?lV&}X쭔MՇ:seeUW&y9}ٮ9lF] P*RH$e+vUfY3MzQ$'atWoے6t5[jʣE$_{ZwdZVf1c rz,9%v14h@\U*mz2#QQ(]Rmff{EKyOy,+*(J)w}p/kO _MpWO&~NfƒIJjquˢl[2ORCg+x`i G -T#MpOw=#Ȅw6Ӌ*|YPQrƒS;5]m/g̝Qb3Düq!j{V1i.;6*}Q^A cWT]]U;GTTt}Լk>X#WT*%=z0c)"3VZo *KP1J~ȲR2O$%*ɩ C{嬒R٬vr$`Oٴ锖˚MK{{*U%Us-yMߕw޾JvEYSO=mu݇S6mY}4} 9CoCjϝO>C_.VU*~TXMzNSaUQБ}PRY*eɚM}.F+CG^ilJU%IYьȡ#eWӼ2/+ͪbY6K0I2'uwwGluqFC>Ԙdxң$ Rb082Im<K^(׵=Bٌ2O/HRD/Ud?,Dѣٶ̉RƨX,|-.H,Rz6QZ+זOM\RQ}WףpEΡTzPT^(G>S}~W{D0_;m_$L ȁ3 3T)G)9-?^ݦ ɶv[xe|QଇE'̲dRҡk,f $%tGG%U)9cTj{D?hVg3*B`A :>Otwҍ~8>Wvև)^7LC~}@g7FhkHfXz06nTZǠOjS}7˔eR˾3^~X1گm#L-/X n3BfI͞]i`OYQg,Տ0g+~Uf&;x[tgjK~yPݸMײG &٬<*A!^m1ʠ Z< T5(a}7a&mI'7"ADlֹvqaвY5p1ײwGdX2(%xFג[ag+k O򓖦I({?g²RRa:E̘tn8T`w!yQ=*`0O^'mAdžɧe>L[ %3 ߑ~r1i^ ?njGkTHw|d즭֍wnD({{4Onomku .r{s;. x@t{D3I'Ò (@ !ʝʩp@IDTB𙆖"2h?l˫}a  (@ D"o.7'¾W¾7(`:tFVob?KO3ƿ~q.n헞Ax'= 65:߽׽`awn} Kh$:l:tzKG0J1`Qmra A4"[]6=Aegy~ HqB3'~wi'/.ա}Z jYi'; /YEw~&v>;i#tA4Q8~sz|\7r\w)0~xaA Ò (@ P6Uo̮[C|@I_{f~>ix-K?L $۶ǒɍ6FvR@4` (vT*uJ2XThMcg ^޸G[<^y잤/NRƽRJ* z ~ 5֙a% Qb7mkE?@5-M*RA_빣g>E9Q@|z5}k$&n L_(iG~zP 1ucjT:8u86hq8~C y'׫m]Fhnq~RYё'K?G; 3{)zKf7"":nOo~͐Ʌ1[8U,J_0OPtHWƶ(=*6hz1~әAO^w8j~oMiqѸk)[h< s͠  .^EEE=~tFn5y0:ݞ~vF;z?6maa+^ڕ!+E")'=DlĶv)oD~9uOyv Z9B, (i&"iǷ%[&Ot o\4~SfBX"@Q @ D"A4sQJ3too^l~ĸn򠁻[ݜV^;hɿ v$oQ6mZsn{cQ8kR7K\ BWʿ(r!>|EI4N㒯i{ xOraK0P~bu^+euav^QWPu:J N㐯i P #Fc㨫0 DAu)!K|M&\M7i{CA' o 7ii N[׹WPߓ t_q 2^5ܫ(K.2r0v!/0hネ,Iu Kep''5_) |dDNNjS&6U"A fR5@LhBs+ qoi;PF@>{oPJ^ܷA :?QHsT`lۦ`ネ$Q1=DePjo?81 X7U7ցsPk=߀^:^JG}I_D Q<} @gZ{ :oإ13 yzO/3/eNƑ^ﶞF4TmZmkږ v )e 0{װ-~KZ5Q.V1եu aw0ahR:gC~˸Ng~F}ҏ]r ~m)jBm:0h? ^ , IQ?,(ٯ%fɼ׵nuڣT6v[G/U(ݸ80{" ^~/ {P[klo> d1gq|qإ~rڰ;̒Y5{FY%Dƕ^u^v(o@#: zAwoǽl9lP ?L! NVέp@A!E")'=DS9J9$]R9^0*mS!rnۙ2bS֯˫<[B=miWٷ!$aUWU5~e' ₨WZaw*n18eՎ}0iB/1ktlPz V5L>`rUuk3_މnsցtvn8E|o? l0A٨ƿaek_1ư<ƛayF$f:ծ'~Sn4GQϧ/nE Jk|; j5  g*|}F 0mb5Wnz=xіaX;"n A$)A1]u[򤣈`Q?=DAA R$C6c Ҿ{-0w/cDXv B-l"E?5 }mb&=}*:Pу1O7Qxmk<*<8 ^<@+@L:b`;"/S s+*/P{` mhQ\7A(3Do-J:Ĺn+g Z}y q =D A @ ^y@?S/q NA*~b {ADA @ D"%1ͷS0=h~n@XDbɬ@j&<[[q߰ >>Co) u;vK8y2Co)ή)>ƅLF!ں˫FGFMI7]zm]uE)˒I9myK+/oۮ |V_^g$$$DZTumޔk=֤Kݘ AJW*6Xcu^~a1i6Ѩ5^vyQ^!c^km)tտxݔYm,>$v;jZnfz{kƕ:ȑeRʞ.ۺ+o]yQዛ*~qNkm[$uNu6^ǺU^ӂa/c^^F)3/5^vyQ^!c^:lAD}}Au4>$vD{,Q â%e'%g$"%,ޟjar'vT~6ʊJ:F?%׼.: \Y^~ Qyۖ¶qa%cnc[8*=pڲ,KR2sQ橌sFƤhT[ݯ;QE͍iAM!OHKⒿ REvö(9}]Ǧ5Kdێ#[/r2Y S_TsAvs8Kה !ԣc^1i GRPqzzXuZA͊T,̢2٬LBR")cLHR]U?PVdt/E|oI&Q ˱εn嘛qϼ7ؠzpskY 3zZa٧u[G~`ݿ_G ۭ״Þ%2&ÒlJt-3JJ8FCGJd%J%]'F;_8Z^T6Cn `cn1PT^w:GCD{0K#l0h[ _x+:  zM˰"1;֯ d/>,jUYIêO)ƽ#nR3C0 #v?㍦T:21SHU~*'=Wm_1O5Qф"/pz#li|5}H<Ӓ/9Q9*5RIKAQSSaEJILF?mIů3c){RӤ*JX̆91 -QGkx%hGqdgMdBêt﮴p^uA)VA?Y X2׼tM$e;Rqd1R2a8{iY?-0̣=-/Hg2ƨ9éDa%P֓P1tCF IDATO}?7o*;|3gԼ7ݧd,#3c9XdPk,#.Vb-w7.HZke޻.牌oߟ,A䷓Qv; v Q9MΎGmcA3ll7GAz+nԄQs(9> f:Q_uՠ8mV_;tv?؋ 2cY2ɧ83v8NaD5An((x6D;#4{8$8tm\4uve.O׍rM\>JOQCv:(m!Q*/Fž65('n엞 {]p赬GvpD; a$:;9L^:>Am,Utc^ߵA81溠kY0 юvX9iΎjca8e_t$y}CAНR0a īC6NI/ICDKyDA|. Njҝ(NzMz®0>DA` XYgƥIw 9KɛGi/ו9*713F&5c$c)5#1J&LJ2F֌Q2T e #;/YGJHZ)ad834_fO'\o}Qq81?%$m:nHڦ(O?aزGhca l"v;j;cG"&UyX%cIIV2aL&k9tjL 80(^!ca;J nHF<Ϩ`Ϊ`8Lxq8uzvQY:@~MBK&3%~r<GaYyX،Ӊp·D$vᗃ8M#v0GQkgژ9?WI2rH%4I:<̿lf&A`1 Qb\6JN'b\3J9HbqlK#[I%eJ#U//J͚6uRyT5UuSzi5: )_q8Łd4ՙ4h1 N'As`+w"+k֒dY<#%K*iM]z߯ݷMQpel]Gu+nf8:z, ǚA9Jts̯r l0h~˨6-RGANðA?Xu;D ʮljTxXTf6Ӧ>n]P@:3҃9.Zʶ9w!`k\A/N9GIut s, 4z>ݦ% ]lA͠z3D֬%k:B.9K,i{wA9].m%|ل"^9?ݓeLRIS}ҏxWv-$%A4_]/ m خ1cS$1R]̡|"; z3yA4ߺ]/ m \.*mI)ԾL(y^#=R,KhggD08JZ}0*+*Y R&{U [JJ"k&E(W7p(K\,MB%arĖg\o*fYIR~Aֽ=̪^%]Et]KUu.Kid(dc?-G]STǒ?K uJI+{RXO ۹d^3LZ3'A:3s,(q٠mOb:vLD$9~pd/hnm3|˒lGfoGZSSI/% (sN(ĸ]m#cI%SF'ok;P&=4&׉i✄r ;l0IT˯;V$yrqa; ¡c:SÌ[gQAJPOQIgP6w: $uD;~eww*1:wu{y[|"i3FL{FɁ$Sk+qqK 1Woӿ!.dÒ (h|frlGs8?CL!2k%Csa!,a_lb~3'6C zubd?Vi~.Yyu%}gNEr'bA |4^ÉeWy'_\VBQ+)ZǼD:GJgi*~e2i6W )e6C(a9C&i\V믞Wykt (&4D\F)a4;T {̉Kی*c[Ҳ_܍dT=OBA%oM㤶?¶k:;,*53\ՋEq.8p:Jy_>p6I ڦN) Gq,', 'w>k܋"8K46^,AWL0iwmNSp~A_lb[z,L{ "~խyoʃ}O6ty-3;N6?Xk^9i~Yw>mnsoKf(W)6Őe]xeE.O W&qh\ NB'!ivzvpzi7>+WΞ7.*TFYs|#-hő$}fa6Nŷ\n|xG!c\2O{Q},} -hq{)f_Z~vYb2XӍPgCa^qqW'!n^ӎR{W'O㸒֯Y z^/, hrs/Kf:ǶnoI]΁3,I|]PG*+/hnb̢u~:' ,q?7Q2lGhaOGa;2A86 Y.fnYS/UyTj7ULja!͏ʞ+vhKfcs1.A91jP!?!lgppDEŖvD?%a; \F!Iu< Im,-?w~_ Ϝ-hBY:k.GA_D}qփ8ԏ(x]L( =e_YDvEmha!'% EYsYrYp;5FZtҜU.QjV%I?L6.Cl~ΞQ9e0*=vT|XQ M&eIPy;Ψ98gNL6S[gmamzoNBw6,~EaFq7螣\7l:z??Za8S(h'anyv^3R6DgZGAKfXFa_]bˁ3 slKqi~CkvF{W?(rq;KwM14-d=!i!r{kA\4l)tx0~A4QrX@ &y4(g١:sq8}XAt痱4rBhJ(P/2V[f A0hھh cSVK{ADA @ D"% o?5o`lB8|_c z=A4v1oSt 3/qd1%Ne%6D#u{՗W ~[ݮkU kP t "I՞ I('v|c_\aMn ڼ@ *?[|HD-s;g-suOuL"&tAM|tfɬnF@\w"A @L1A}C"zR7S ~u-Hc<Cd_}DH]lwRlqмـٺzO=Ǒ'l۝ۥ@P4ԃ ENb75렾>9jZ{[>_\(v~}%ݯt`nm7 usqn{4Ղh*m2TdRdQ4h<5A(Sn^3AxxϯMN#Ym2%^?==0lZakv79aɏz@Q p K2 PBTu{q$v]L>(GO=]lۥt mۺ^Cde>v]l? >!A"D"A @ D"A @ D"A @ '43/e A 3{ؐ"@-ȡZvaT= vrHS=N !H{C9, Az0(J$}<3@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@ D@^{*nuS D޾޾3{]!zP1BDJD@ Dc, F;_O˯/$IADzmY( QKR_U5QuoM Ưn1}y:.\7i\ۿ_nJWיE,\]Ei0\*T~%^{wlA ޼,tH$EDT_(Fa!~+vލg"IH""HrQ=+K޿ݍ_jy8KӾ_WQx_i߹r,Wfcd$IYDAD2D!_B^/GRǯB1"wHqpw?m٤[i Y,]]+s1R- #??َ$D2ũx^NuvѠ$璣ɸ=wn7jO(8ɘ{i.L$AשNg1{y66ȼs? % Hrim]izs  5kotcw޽.8ejg{d"ɩ8$ăDjX][D>&/LK173ЃOwHRKC1)FV@tn|D>bчE;)*"=H^ٙHH~t*1uxvw#9<EFT\@ 4bwwv ߏ뱾7ߺ ?؏8T*NeU)Fuw7|"""Mc?"ҽ4t?"JnOғI`*GQݫFQ8_ &< gv&ʱxI-D6zw;ҽK9R[M1Tgcss3n.c/;bcs62m:I"ɝmG5Xx @^hWlED̿<BՏ~TӈB bݚaվ)~kpvvvbdz??nv%inTjTAj 1$L^B162|3/cJ˕Xy}{m/߯]Wb(}VrВ祮._? 3cO "%"1׵Iի&0ށ[7U@xm1O* -sH @ ZOP,^[T@ŢK@8e/XXT.kwTHhzg=F̝w(@[6Ǻ]fRm: V1mF@0rXS}ֳowYwVdhA  '۬s4HeidQɸln;4ƹSF_ uKӧyh.ٲyΣY~эk{RFȸU@5s,iv(֣߭n֭&IDAT7~;7;\߳.ٻq{j;Nڠh Nͻ=wr'my{muvsh,o/XɈO6VvNmYvzsnfo?qwsȲ]k[uZVn/ߩD#D#AO4AhPk=հ^,sf!h6h )~[AH{hIYojw刜c;_e=tK7/=qlz {="4JL72" !:ms;?Baa~je}ڥsh6[NGNI[mA^ԣbe['?9:>y.">}4vvvcR\xXva<rmkJ] hVٱyOggm3Zam7?ߌWgg˭O~;f"!Q}av*:6;n#Dӵΐ@[dӕ;Q>m]#D'eM3m=V1{Gh 0hڢ%FSf@@0ZC"]X"ץFSf@@ U Wfy}-]heR>oz#DEDQyR d[`Pjz+ s9Kϱ3k]}/Aka OQ<=7QR!: ;Nгy\Z|]g73jf$!8yޏGsʏ-u&}p=0rYtES;uBi[3,A9S8wve]Z&j=f} 'PmvУ@T|Zus:y=zr ש7z}u'CQvZ,6&-vvmkWF|v(멑,f-C7P?z QY,rH:H#DgcCԭNh?DqGNNm4ay+?{fYڷ2Q9lg@QoMͺV5Znk',Fy}$n,zv~ BI')<}OM%tL!pݫDw[š3Aj "#dΛTTـ!`Ρ&z{ Îwy晧-Q߫% T` "tqF!|;fXWAp7Oz hap\@u }5f„dojpQB0 3lU&P!ڣTi)g:(0f+oP8!p*{@q-S4.pm ӰCF+?aюCd')H k~m4]Cn}K8.%hFTpAZ; GzGX-Hyt.fQ> qiL>2{>> 1iSXgYNps(M_g|KP("?M6Ƚ7JmZ=kȠ-eofxt LW\߹l?w?ǎt-b#(+xg<2=_#›f;Ra *!偂Wμ!\ĕ]9\`b,X['fo*ؾsB{'n`q 6ҙB 86uԛ#+wX;%vΏgiAkYa.t'[sr" #,w2ᩢ U[:f ;gJ 3dhqCn㲊_m C-#Vj5[1f &_4|WJ/`K((n`a9 <ұeC*P> EyIһ^ iɗO6ljomO|&!' NH0 `+sin~OLqzm|(d_yfYWԛ/ K[נ[TQ__2=3a 5n)]7 2BS,2/Q'FSYl}ڎ0 ̾jMnB* ??׸WzYlsge(* `_8_bf[|ʃith}p, lz;@+麹04 e Puk7f8?.} 7RGXa.j5V;“]):*G=§fQ F БruW\mnxDUu Vnu6` !P<) `k{ .5wbwK2Jآ9>Q@j^3w(*W}U(ujgհ s͘<0pf1R-[X𲯼x}\2tCå_̚_-B /hfhZvw M,O?Er@w7.% ;e?7CM.A`eTk@_{TS'lё7ݰlu4v4閰|˰p~\:V-.PIv #\<3 a%\Ayå)~t3 :3Bgt 3<,CV}ֹ=2K uԕ?uCotiƿ[݅ajMG>dMQqᵵG5Q*Vʝ j5SW Wm2J }u7K`fz#ny| Sm 2yO K9_y+?CH3ENHX-TP>iڮhE.xo7) f9NuniGn 9\k00c,fN:Ο9]t..1LSŏ)N1p$,p~e̴4Yº%|L@ ǚoYďh O6Q+MXա[Ow%\ʡPSaaŰƯ|'ָ2G7f;vt >HnĚaUT(g!2 Ya*Ç $!z IWtX=sr1yU bh[.1#7>Zו~"Qk MkO'וJr<$7w>: JvK6~|uz/oumv(:XƁv꼔ahqme٦GrRTo3c㭷ŎMq yӒœj_<Vҩ<}~h MqdE+.47Tj3Ňz{ 33)deF})16e3>ZFhe3n dhЕ_@~!_?w7Bqsz'/ jyY?^8Tܖ(?B<Õ.cV[jc9H6ib[bhBd َzYu $ZŅx,o+?{KC"#P_7AFIxg@{ԑsɢZPp|/;]aC%7^Ιֆ@? 8nֻ6PwlCݖҋZR)~Mq OznZjiӒ0 fx{[v ­۷:EL j ̠kwGMPt:,}Ӵ>5 &Jᗿ eoB%$ edw.KQs#|w#cYL? ust36c),Ws\s5ZP\kkԶm1#E Yf0 jȘV_\Z_$ߪ?DBI ^[^w8[p[}u6VqUT-D7f`WjBI v{-dIj\Fð0B5w*EB![_7.u]{lޯ! 6nop`{Y!BF "B!P!BAD!BAD!BAD!BAD!BAD!2d`rbݽqàWZo&BF^S0Ǚ "qJ\Bz/B!D*9E'*tOiGE!DA=3l{MOKB(! gr&[@1D!  3AAJ"KfBHp[ CdV(IL.B!QREIjB!I3yh"STjn%3B!$ VoK@rmͤ(M!Y =g2]wB!d "BB!I#+DBy(!BA" B!B!B!B!B(cKB!-[b B!$5wAT_eB!$l@DB!d "BB!DB!#NEۘrDe,c|Cw6!2SjodY!,fbeHcr07PB'G~ۭP)50:drǽ`E^AAq a :|~YC`]_,+P~[LL aܭ`y\\~gΖ͝߾ ÀhZl&k߫2"m6A$Ƅñ(/T*aU,*ra?Z4&">Ctu@j{[iMW{ju0u ~&A as!cj{c2tzCqoMgQM D!֭֫ڨ70`濭[0j;@>?xzg!˲]щ!l]涊齃pqZ*pbk 6`9`O$}-q39q⅋Xpg%1sz+ W>7c?BFZP)D! aΐẉbk UQ<] (5 "$Y<̣ިc/kؼ2Y~<h!(Qڷ*ʧ3ˣT5(?T1Sf_.B^*aLjV[\~2V>ZHET _fp)w}>!'"tZ@v]$I@ V U̞-#K>hnf{}GeE.zZ`@i/H *62͕;CIzFq`tѦ  wUU.ccGMд3Ey(:rgdDiŲ'ױ"JSs33}B!-D>A԰H&j0z{O,K@>1&Cya5 kI0!DS]"h? C=]Ӑ?VpJSdQ7 N`]diL[M̚7LKtjc?3^P( 6^AyṾ9 j ~[_kseLP񴗭<m˴ B[A==Uy#n('uim!M蚻}A%3M! 8^ڷ0Y_sX1a@9 @3D u)syMf::qK]Ăo`5~kݽ],|Ad1xlCBi'-DJAd.~iK:TimzwuR I٦C#o[0^/ I|^mnpϕT@m&xwe4̯#MKQ7/0t5Y)r:1 ` Q/xo]02S?lVXʻ+Xpga~SyXD˝!| EB Y=̌Yg=!$H/+О4<ۈ%2k^Q큊TV|QSheœKg ~0OS%7F Y_*BIY) À t/䐗de[P$l~o\2Pu۩8h`#J*߹Uhc%qWWvk =Q>SF;wmDdZF!$) ߛ[ŐрhhMS-ˑWsaj9݁ig|Uաi+hk f߾ Mcsca`E @nUѠ7H49AkH6jW ea@&`ŢiVJ }w;Pur.ony'ױ\"/] 綅(cuerz5!t?0z&"̍^Q, K`e[Đn,lGIva?]dY> ?Xoȼ {4Bd$,؃'( l(0`)3'!瀝(:e_C0 g-Mwo`٨Փ IDATk+*Yhgg o.C)\[Zzv߱|x!"黪khYgol=ݻ~PFj?֠:y-6*ri4(4 T&qM1`.ZD:,q"0W{`ʎ淉_@h_Uۄ8V,E1}ML$!7GvL@;0`A~EZjZJHT̠mu8 +Q 0nJu[/ܖE&m6:^erwGctu<7ǺA:hX? xӧOVܩ4]OQ[%ؔ(VVB,~i^|k[wR~776"; \|b[W~ ]wBH!$|2,B/A,B(ȀpM]l0 !B(!B(!BA" BȨ;D%UO8f<QK$B(& I70wa.B!d "BB!DB!DB!DB!DB!DB!#L&NNLmwo|aM!Q߫~Qߛ3Ad7: Q p"$}Bnɬ5oq9=HCIFwSz)BHd(cXki /']MIO- .2₨{6=8 c~AAM( $A (F ڸxλl؃ʄy%w, \{K=UW#/aϺʃ8JXBHDzѰA3!-6no >'A-LK , \Ņ5{n7n'i ~fK7;ˀ7W>xrBHu!)DH2ɰ!$8lO! B !BA" B!B!Bhɦͯ7c#vB<!Dn!nĄ1?G1PzIO Onvo_!$" g'&XǻP19QO\KH"d7P8ka w3)&-n ??vK;M &7ޖ?*4z~mg>zn i~L::r2tAO 5X7 iv^ŊaQI 4-Kz^ۘhXD%4~c1שٯHڱSP4dT5h ]+ng>Q0PQUDKwC74ь$34C5X5ĭVU.la[=K-9ukv~bkQz/eoW!^D ZvJ^Ӣ̳]X#z/~~7n9fk|7)uc^'ve6oqڥ1)3ϩ<=ݖa\;Ean Ѓ)DN(XMq7|9 k% AwF,EԹp{3$/U4$}Zq'=Dv,fAEA!BeL͠;eԄ,{IV(i(Hzm~0B)%37*?A+Y+uQ is,%()BCf~6&Msv,"Vg^L&+ =a&2 !?4?B7ɴ%4/"ˤIhf)զ´[vOE93 Mսx'BFFى 87B!i'Kfv?oAqxϵݵiͫ{-!2jBd}H=Ļp:' xxʣ9F+!2*ce=xcKX^QB P;?q{BQY J`R!Q$!Ef8 ?B5b[~_ѷǭu{3۴- DKr%BHuNz.<^<7iw[8AD!dԠ=BHɰr!B()!NdF! "!BFȖ6no !K"]54!BbK*.̱ ![B!D,B!PB!PB!PB!PB!2v/2ZB(lXzoI>O 0waU$!# uS^{;ͱ8m: !,aA>uB!$ BB!D KVByAD!BAD!BAD!BAD!BAD!2(|qM,!CfyNUS-ʯB!1#(?K-gg;t]g#BbƊ3ӶoXԕB!$V;!BpyXgʐ%Ҙ,ĘV(Behq,NyOJZ┇0=)mljx W%`UȒd04l~1eA㺂X<&$(^ "BH]퍁D9[L`ꆁkЍE_ ^Y>[M ei 1cu_~Ni<7-^絎3z[a[~hмGݖHgTMǢC귃:c^{WMtÀdI(C\@P~\=T' (Bm@5*^f1iqױAO=M6J6lԟ€Ѐ!I@F <„2jO?K)?ւN!~a麤/<$ԭ: ٖhgQ/AnǑ(Ƃ =~7-n쨭DBP2r9-34d Y~a QTU,C`[ *Yz_Mr8T޻;coN y<èwE/uT -΢_x+:b MWԲ]P#P,0udw6- ^>_;<~0 y,|B!#1ݶǏ7_Q0 DRdw+/ggq,}EB`g'o팀A@$ZPlK#CTqavP<.#@aCoh@F@0 `llBP%!pNھzye1i%k!LK؎\%aZs?|f<{< "c~L00"(.B!5~ K-:WB7a@!lF28{_fPE~?SuChD# GREj{Pix8}}s4' W7noxso0uzrv/CHbL@8X4߯0C}RduO !B<kmx>٧DA;4N+nX/j:. 3-Q/: >G},tF4uU riu^ʽn 1tqr9 '|~8u2㖿(# IDAT(jga9Ma(5r`/|,I e,('~0!0FmLjha(Deu /)<ѐGn3NcⲎ[Iqv6'i44,gg rMR}fqjA%t>TIiCT.~ 2Em55,'ntJONL^C(:-ALXi:a8 E2ΎcQ9e߼;}>~} YU1tmNL\C:-0Q+8>S(/H7NN78HyE@!QfL!~F1N^fiCFMJy)3 "D1eu_ MoAo(~`@5`<  Bu!BG6#DF S Ì -(j'|pxCE(16uip  }ƭ F:Ilqj/~Ne<OO $^dFȖ0FQfa!cQ3L'a8&p ˍD'ij$: SBءY*7!`%48!߭$ LaʹAb<ĤmbR3Ns&M$䶤" !5.aYChbi{6m.kvFJǯ0ҁd<י(8# N8Qu7jDI>RwXE廋P`$D$۪5v׺. A_\6z9D Iduvx9E4z}MKm0q0>u7jôIq s%,bs"5_\imAD!E:VD !Ȋw ˿=L*jLB~"fƲNAz$6zOv1Hԟc͏0YAT :*N!$/N%),?X9d-se X*^(vf~u޹w13}u "/g#I&-DB : 躹H'?@y C?(A 'Km~:5(hNKZˆ3 'a: 2N׹3Hgaٰ`}%6tF8 Irƥd?h(Jgnơ2!In@l0Yʯ(0E\2,y~%sA_E22FLθ3jhQ0JR91OއWkX|k HcYleBd!r_E[,[9H?}> @FF6"P̢~ # }IL}N Ӿ_$Nm0D48&qj֖VXƇBpB[ 3|@dlhh(:1Si(9V& Hp]l ޞޘ|O#K˳j~G0/ Ix,I3.oŭ Wš%adz8a,i~,~߷||(y?q3u26~HzɟvE+~6I 1qu0z\zL-! "73(3'A/t}m\E+AKc06ca0qG1.E=&Җ=ϲ!2Ì1&)f898BADDR_ ::%dtƗ8ndF! "i&Lz(;&8L!B/3+qwD/tz F]qj^R7, ga=a0֯0lj,6t%CD~(A_کhXy4-m"~3((0֯a8̢v7dl*U*+c~ #a3ĕ2I[o2NySڡj֨9CҴ߮4Q[󐿪`rb{)pt$lFM$iuCը9IqE^+XS0 +-I,mǴςcQku\%wT(5 ;; P>=ɉإqh]8XuacAuNiۼ{3ila R^CuTvƱ0ƞDcOX_ݴa~1 ͹v0%-N׆45 ~FũM Җ62QA!'6v-3mw} Οy|z gXcnnYKfBLUB[ I7fQ>SN/iwGi{eQ灎<_ﬣd_(/`޻<(qno^qhR !isF>`_ﬣ/ױ"n~~B2\ ,?й8{B!d)4e2_?)/ fj{"K~5)\|584R>ϕxr_X -51tbΗ{dF!ȩޯhXJTiœEGQo+ն=#BRk_WP8QfqȖ̆cX+rc԰3C$ACd4:K[JRڑgԎC㠟GL6rC=LNL=[k:*kJ/ƇZDt}] þ.< ~ìqi Qp]uFO=NxIWhwo aXR;[P n}c-vb6gzAKG+lK%ji?L6)Y!+k_S(TDy呆ߪm%|,IXYtqsp'ΨRΒߣnq+k/׵8+,Fa5|/P~Yu8wA砎'/HCd4: ۉim7<YQ; 6dZGaKCqd(]2(N. g`15zۙlQGqnKIACkvF͇1UAQJTi-F.B!'`"u$61ia%lKᦓΎDJ>HAڝ׎r: RIgPhU:BHuN_S+ [!S[b!)fF B!0geF! "!B(!BF- !BAtT'B "R'BH"B!P!BAD!BAD!BAD!BAD!BAD!BAD!BAD!2dvvC_+~߄B!CDvBB!$Kfuf=B m0ve^>!BP̺ ظ\G!P suc~q!B⁝ֈ\{K=UW] (q;A1S!iQPkn+B!$6nof-sݛ-Gv\ݿ;"CD!6 ";%3M!D!tA! "!B(!B(!B(!B(!B(!BFCDB !H !BFоT KB!r2@BI !Bb˵_>D$1q{Dg}~5KRXa#.y{!pBD!B!D,B!:aܴ׬i.|8).na({Lݽ˒c HZ:+o(F/m%y[~w㔖^cI"]2l8 ܠf7@AM#qiqNm!ʴ8NP c;Zہm|v1ꕏ߽L$bm ۫U2CB(f`~fsNv2rb(ygfuAAXo*-a=ƱPx`kvv>8+K~J-=}7 Q+-I(F^#;1mćwKIloіeTz%?Q rA܄g^ɰ-et=ΚBD,-DmPqcݛR$ޮ^2L84HZJN7yz9߹n7V9+^Fy@rNP= K_ }'zgXiW/A B]wB!E@! "B! "B!&M7XdKF:9BBtXdKF:9BgheaS^~9u/Gz8 3=&4xSj;K"=#ظ>ʵ [딝B,˸u[jOߺ NZe`g{߸=?╟MՄB-KQT.Z&0B!B !BbG7U2yug<'Yx _cZO|TNm_+à%~ ! D^^z8]z}P[Z0e˜:ǭ#a?ӮJxN^1%$mh/V#_{z 4^A2qIk2q'N2A'=a'ˠ%!$:b:f$Փis:@ ;NMOXV-Bƿqh< \ !BQЃx ;d`J@lFv{8rQ>)ۨɷy;|/Kw=} ]y~)qW9w}*Epk;DƽJ2:l8<{{K/Dy={rqn<:-oōwo<[g^DDwōwoDDćC,{ōwo /> EYkW#"!:>>ߺ}w'&" ۭ;3?thl9aPMy4Ghr^RףE ˻_VvMerN{\zY2 5 Dya ﹼL9EeG:M8HN?.%EUjʆ2hY2eiC_2lv@eY}uQu{e"nՍBs+uK2 ӝd6:UQԣ ]^F/]ښlQ)*cf$4 C j}/;־:&4U /Es SsL9.7l}ؾq&ۧ' eh#;aL9.\0d6KQAW髼a8wM7Ywv]ѥX@4=EUQ9봯WG/"MD@ D@ lnQQe1yjE=MGj7O=,w#2Xt6^>dEHQFӶ)VeQ0-sy^6U]G}پRNuR߲ˊ:hMZg-K]FY,/u$e:ܪʛe]Dz:v㚁Ny{nM˨,9t@ Z:ӝd@]W˔jv8Fѳ?ymNN}4~ּeL2Ri LwRE=R&4-'U=V%lʱvuysN]fICyNCa2e,*˕}AʤݼQjsU{L9Iu~!>24x*Js\*&50NU92Z C& KՂU@%$|ѼҼhTjGi=RyXbug2|zl'3)N}乶;}4G6ozZSK5Iy#M.TG{!8wM-j)ͧ;D u0EuHU)>u9)ש{mcsLGYgy횬F'^gOh* Aܺ4 D@ D@zuG{CSZhy-Cu]T緯s>j=lm0^ceC칪z^ Dyl΢-kyϕGu5uvy\)볨,ۙmW̪a&.L*ӞET˫KfyH?]j>uF.4qxB #D@ @ "X6bܞ@ .li8V 'Iq|~ G1~a_/O"_~)^ً^z)Ddo~?n޾tٓW.GlEdوى;r3.@y.yG#5Baͷ71 ֙ىލѧiB`IDD8VD .Z|>cܞ}kt[qX7ُ?%龲iy[7DžW/18;8dq18;p('Y\~݈ayDLOylR(C^/\r-.z1.|4 _0>'qx|8[;;xq,t  lE3珇xu[b6u;Pv\8A~:89oğ? \{?W{gr:24 [龲nE^^@7xw wޏ^zKC::p*}gAllǙl uͯ[o0_ڎ _|d'Y=9S %iĤ,drYm3o)aD~A ޏ$WոpBd'Y |_˻q Ȳ?X@,,7N">Gq;q}uA<=yqqxxZ]wc|| ΜD<=,="˞3EA|>Y:Tm.,Gyø?Ge1zq/m?<},{ؿdt훧sN8:~,,"1WO^An޾?ޏǿx'xic8;#"Nagb'I}ʥ=Ȟ<ǑeO#;GN|:oM^dd3ё)u}>}߹Ƚŭ݊_QV+{KXYO(پ/_GNEƛK{ouyb؆r;~X?ُ?K=?ݏK__kb˯8q͸oucdǑ=9__&U~\xBܿ?.}e7a(nܻtd04 bu&bqd[qD=@J| -b+"/( i@R3оE(.8v?I(ʲ,DdOccqe, Lw?Ak2?8<8Ç܏i?ݿ1!ݸd]~k~<8TPWq x i@ p&U- lv lf 8xt%e i@ "V[f~s;W_`/IKfkÌ/jĪ 9fhɼ5p:^f>6ty /FH@ٶ޹?x߫[gY^v_7hQG&cN{0̦?OwnoRye[zYF]Tv:10/,:*h:Ǣe_eۭjFXe[XΪuY40Y6e*SU1{\u}]#k:m_\oe?XSvYN.oeWr)qnMw)XgK1h<;Ҵ.w:Y^e-E"Zx|LwUu9?[!2Ϻ,'鑈)La Xg+5B4osL^Y0;"1{\eF:ڣJ[B{=uڮN5iI֋(o@ Z_ve]*C@UK}.C4H媲ϧh6d ܺZ"!h D&ٷns='y_K9xA\}s=h"`!}yWv5h=`-dD6\9D~@7]u i@ 6[;h!`-\r9w"`7o./;D׿u]K+i8.\""w|jGQVFF9mԱzViuYϺe;~ {?gQ{ޱ^o^W v8>Uu0K_u\u@?3ϥ+7!:x8.IN/s(kFF..L-Z^Y 7cvmNjCR5l{|=e_MQ {CaMwNLݼU8e-Nu(<4ithk"khM!wzDuy^Lҝ*-s'k"bb 'b}&4BЎTѢ>?u з#D|&Λ7!o-sqѥy6es'ow+(TTyO碉},LTTu^gEѢ7߼Ec[f.ѢN'^̺!i{W9E땝 ]WLm&6_D^Rws@ tmo,9 nh8j)`m-d߼V8Bt8xt D;/o7*3Hk0_9/߽+잫j$YؿFD [e7a9k@ruIɤq0гL<"ҕ_jq |5"})XS?[_VIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img2.png000066400000000000000000000720521304100213300256220ustar00rootroot00000000000000PNG  IHDRDZN pHYs ,1tIME:#3Z IDATxhW0.H@t2 6?҅:Bmާp܅n,l % ݦt[.Ra? }b A+<͌fFhfΜ=sf>O@!N_ڇX̄BHh޹oJt7?sy\O=!~_(2ЙL I{ BIyh߯Vu\mSOE:T@L OgO,`6]ϴ:[ aUUa0h|+On G4pA3V,L)V8n 30VZ5k!L?]̔f~Tw%2f*Qs n|VE1T\W*A6Gi!:B6ް!@gu8at!0a6̃- =J#Q[@)#+\0l \*20vӇ qe L965]_6XF9Q, Lif/bL ԰e|m{6 ;"p0kȠ#aofx|]a.uy-k^=?]ZRW<P8R_şCoS^"^ޕFQ 1!˸X~5KoCLM)]@qjn{- Do~琛v?-tPB&d3nvfV V'FU!ǍNy븾j,AhZoX veʢVEᙢ hVZov+10՚M y.~_t4L[ ڟY2nbڟ,[Ԛ~ΊMB((p@X՝%̿rK Ad#WQ{[B ʧrbQE^8NT߶L1{}цc\{|ֿbiOu1 }a--P%VoU]&Ŝ(/~4!_Fkw[TE`:w*)pV'rV},a+ޜ`&DF@|*wZsT",e2~nV>fw M }[xڳ6ª"`6q$`Ax;09Sx`Pq}PlႊZOX{ذ 0ĒGC[#O3'VG0 kIC5P ˮg#/kKb~]`R1o;Ji~_E,VޙE>`qa x ,Wi>aM g (W;W:W@0?\@f'װy^X_+"kTKX~-r3ZW`_+t"oV;񙦉ܩ@(ua/JY"[˝s%doO /0srJ~ڃivhm F'@'4tU { O{V8?)i&= RWXZa.uhv;S=%G}'fq f0Сr}_\7;nxHU vnv6` !P<M`k{ % .1sĥw.o.CڇWPUaN,[O VWQ~iIzW 'ʝkޫP,e*f_Z{T_6+,@92gsč]B01Xw+\g A}Fa8ye{:ԣeM*,^(;XN=0pˣk~ ȝ̡^ĕߜ_d]Gu̾<3|2h]y3LۆiaYz+úa +L[8'Cam)}褱p: S=@}9kϐq=9jp7|"g¥\>Y@ 3<,Cv{}2O uԓ?uyCotiſ[݅iZ@> u s/X?RɲTnWP(!T̿\XQP:QxC\s3]q39XTP'{`ʋS}B(*pLn]3-vfӄ* ua5~w46CDVyTTʵ^MscdECȴxGXp(px'[oٖ,K-lG4[pM{pxK95ƞM¥ h -110vCBWl׎nF[}&SB)Pȴ7NwRLw*P߷ق3%d̃efs%Z e39^FTe3a T{<4CY6C۳uƾ~ #Z ֲZK ZὃpVC 3Xl&;P kX8ວ'O\nbҹztKu,[ /'Ur ={Us2~Wy\|[^7yڽM,C`?wͩ"o^\}GJuC\0  /VEcy EzQ7f5h@6} RV>cp{L8[}](=Ge3c_?O ˒,1cYz]bG?p{xO0.dzh*Kug /AP @~]G>__>]F.şI3GjM][|ىp=:ؾ@mV&?.mܤZppm7LAݰđ}ѳkCu ۖ- 8oVxie^,I 3xkдA4VyfA\mAv ]Ǚ7oX֧Tɱ=7P~` r2uCuL7zđѵ,fw[9Z1Cp׫pxGlb(g-uP5{Ա1dm=[# 3wb-JUBI*nns,DԠ=KfBH쾊iۍFosj[/qK()`\yJbh&DFQMӄ!ɂhy\y aܩ`. vr&Ko: ޯVrۻ`e^CAIA {|^YS`]+;+jX%Wto15**rrWqWqe~nWϽp=m&DF@;e3Y^6 h% "1!\'$P8Z@TꧫXz})U"}xʄ›t` +Q8y}~J^u{K@>iX k==A-D+p! CBhY8vs7  sWloy YZMiZHxҀP1wj\v<1z= gLQf_h6A"2P`شBCd?ܩ ,^YHMuDe?tU!C*jG6o~ %9l}%=]7ZOlBsPE~u//bu,:ş-ܬi ~}:ӻn&|A%*2.0 h5T}Ā:aǹCL`)(P0]juޚگZZlBdӄiZlXykE̝+C-aXõi!"D1ԙőٲ*T!'䦉g/ }_kڮB|] (2t"UE<Ͷ̣l`/ؼ2Y~2`! ('3sɣTuԾb̿P <_2Huй|푆҉ֿXbR0K\d,COD!Y@N|." &oXC*_*#KL4f ?}GQUE.,D}ug@h5h5l~Se^(#w,Ɠs-)w45&fbcZYP'ȽTF>"J+>ז1Sݝh!" Fjt6Qeh6:{TU2و :_QP8zBt#2Τe\.tQ" eBpl0 ( Ąjf)-3g_D[YBajշـN^o.7ys%g|e*Zs+ B[zA;M>ݯECn(&' }R[B45[0toKf@|&p+i'zѵ,cӄPs0nDƾ  'syMV:ѼqKCĂ˥c5 nW{v2."K~gZ"]}Y}h!"T "`ɬK_Z֡,KưSiݭ׹KHm' d[bwUDyL(jklSu{og}Wf:Ѳ B!"i(>Y!Z"oh@e |an` fF` &`U+\yht>U6|+!}ŌDB,>Kffgɬ[ 9O 9ʳy{5OZQ4Ēeٵ}@w5TTv|F(^:ޯ! `;T2 i¨0F0MK/YrEh5F0MCQ3:@`󎎻UO0kO蘝RQ&@A ڷ;{hC[ť_Gh 5nzGװ%\E&2/= 玅(cwcrz5!t?0&&~Qԉ,}ZK`cjĐa,YkW=Kǧsۭ* <\}y%qZ]B>-(*3Yh0 ؇ܽOXQѠ کrs'T̝2vvP3t4m IDATtMj߲Y]z:V>\ݝ]hվv}b}>µ}mYk]gBI 2[zY?}ֿӽ 6zT547M3+Q+NٰEـnh4M?42UULlQVC4q"'U44fL* LWQ]A2tCf,caLM^CZ|!GOjY{ i{GBI je=tj>W`NַZ_xTD^-PYfHjBQ&N&' \mVPwPߍ:Wa6a-P}s_n\wjx9~Xzח{y!$-h{bM-  x?%Wn"?zG{ul~o^[ZH!^(B&fY BgEfD d'j[&՗kVM(MUiƒB(&ەTL}'N\W~yfc6&h ݎcvfawn~6^I+a-]cPZ~pФe={Qs2GAf[Z^$J9YYIYOXWl8{gF^@^'0WD~*3}@}BB%/Au5A~ !0YyXdY{/6Y5-a 3iD!rdA}evAJ%을{mI'/"$Z$촜ֻs(0h)x,8"%!#HHwmiJCهi$J5LYn+v:b1ݸ8⑕5y'sӆH/_Ч_f}tJ{\`tϓqЧ贔z-~0蘟|Q~j\o.~ΏfK?a%TD,;['q676qk=۷4nnlbev2;;8ڹn/E B|2,B/a/uB(Ho%3B!P!BAD!BAD!2 QxUTDYBHȴL-V!Ba.%3B!=DB bB!B!B!B!B!B!dÌSӇ~۽zq>BQ߫~Qߛ "vhNU߇!'!$nɬdzhs{tqkn?8!/3'`IyP8Hݛ2ㄟxn4p  'ǂ  ޛqbxB.ʴBA4$6\;yݡGmJT&$A}CH2ƥ\~rc fOPnAH ڧeCHrƥѨ4OkQPq- BaГAp\ \o'=N4^ ~#d܈\ n'v?o\MAl,';!dB BF "BD!B(XB "B "Bo"Tf,2;f~NnY'؎A߾8}NnY'؎Aa$=X%9$q툐x=DB bB!dɗUU={{v,Jy&xzp;yNxTI/0mEUUi "mЋ gO=vL8H 3(W iXI-BF[FQ8bO_i'>MaMmOړr{{n?&-q߸=E)7a\Cˀ䁯~K+$z0qYd|aͰBA驟99ޞ?yH?z2aR Q_ٟ(Q15.s a;8Y!Iy7.OS -Hγ=9YUK8 g;h/ITml ^W:G$!f)ˆ0t/4 c9Iڠ/QԟtW~\S&C()j Awo6M_W·R*X{qKa^Tq/qLؖ%3B!P!BAD!$VpɃjBI8!7߾B l;D2̟G~AvB{!BA" B!BȘ#}Qx~׃T7IN{<2,_n"`<7U L;@ %ć84Q *lHl$9a偐a1 VS/8m;i*#!"BCAD!{!`zjړ/}n^` "Bƫ( Jи{N.V(R^РoT rP>=m&i_ ylS=K[_C5Is3sAԯBȨtND2v`Ado02S ψ97*CĆ~^o; "n. $iRƜtaɁ"Eܟ|I(bTai8/ťa޿sGfdv? ރQs "*^†1!Eۛ~!Q #_6;tO "'"OVAE =(tOXBbZ<*\@& u<B#tJ! yЗ!$VYmBƛo_d! "BỌzʂ l;!=DB{"m`iǔ$ !0BtWY1%uvE!$,[X8!I+B!a=DB bB!B!B!B!B!d {~ q2YD g qhB!D,B!PB!PB!PB!PB!PB!PB!PB!-Qtzj+{o5A!DSӇoB!Q%3'$ڿۏv#BHrB{o# B"{o9^s B!$9B{o#ZW^!B[. N֖EʆB!5"Dp}UWAQV.B ^12AVb'ܬ3,7A]tLVB!Z 0Mw(Z "BH,]ƭDӘ{,44L`(k |Ajo]׻lcz뽶<4-~緎 SFǂm:6Q%j| :^{)3},1$~;lZ@/֡2.{AL@P‘#=gi ;M MK/ ܽ? 4/4{#[?gzmJSVY[pSIͻuuA)\FYA~Gݖn[HKƩʶ@!LhMM dG(L bvVn(5z xUzc-r'#-n%%2SũF8uQ~+Jd&}m@2Sn|VA*՜#g6U{5=z=~MKT&#Q;MEve8B~뇬Wp00#w'Gp4Q:݌SYpEYGQ;[4ΎGot*k|1wD9q22K#Y2 g8SuQ;Sè-I͟+E%mΎG&dYOqhD#/ǂ'1 qYGۤ8;NJ4mK_;(ΰ暤48=ǩ!}Ʃn72ĩ\f,:7nĹ,'^tK '~0 Az6#+-QO^(jitvF)s=#t~A}X9U۱v '~PCǠeBi:2GQ;LXNt#89Ifߐq8#!B~1uƴ;QL{=Qן~SfBAD s2;QL{=Q_kyR8%3B!=%Yg&;%NG^F(a6(Nf!섀fL@(P'!T lV@LP&,}e%3B!H0U/IK}dDFZPu_&daf &` BT!Pa @V"k %̦iEAQ8< !,AAֱ%ha_q+ϰg`Ϊ`8ĹtZ. qrV n"u-dFdOpECǢfNe8&p )FX45pǩ]m!b@ 0aPTPRffY `1(1)u''ipSggIiIܖ=Yd!`6y: uRt}O,=m?J[ k0 gt tu<m4H;h5 L* E@𞆥s7\:n!?R7>Pz+73S;np^1LHU̹kr A_\69FIdu$\}uF*h^" 5FǢQw}SVHTL*TX86K(+ P7f)DB_#ETn4+"h}HBXjs?t4Wan" DwxHmdq}Ma1@#xZ@ .Nic^Gg({Av$ܺe'xcDz 9BE!LqQ\bl40B!)7ݫaE #PW-(@>{ 3h 9e"8W7p8KJ`(?ɶ4>P|S+jPlSK8 8y,ًhQ~!I2i!"BlD,EF}HڠF48%qj֖V?[uBpv 'rѨ'(:1Si(9V&=F `o.-|&ǑGjAG0OC$u+NYm0,Nu7 w:я?G*:wzXugΜڗ_^B-Bc?~|WOw!,}%˶qQHDziԯ&ŁkR^OC8Wq^ߗbhE_.ws0!tK%3B!DN~ﱍ[SC70`5Lܹ8\ ƕu]j2ua:'y 2d;6-~maѰ!m7<3j2 ñ߼ۧe \Y?)x*¢uC ͢T*b 8Zgk|Sd,DqvXa 0sXǓAa! gtpVvbuۍ:aCke:# e=au$\d##q'a9tkt2:E ˁk ۙvè8!j絲m2ƞ5:Q.^D埔;bh\!B2!`!z,h2#ѵqN .a[N:;߯WS N+9ă;t8Ύ3.!hU:BHuN_S+.}-B)߽B)G B!X82#BB!DB!cNh{틖B :wB!dlwB!$p!B(XB "B "B "B "B "B "B "B[2QpzjovޯoB! "'DB!Q%ο~zs wB!P\ 0N{m]> !BHzH0ظ\G!P ƭ g\ucÊAq !B⁓ֈ\w/=V_$LMƘ[\ny"BH|5"Da% {4(.Y!BH8l̀[z79ZzwENBo"DN7/Kfn{BB!m躃B!D,B!PB!PB!PB!PB!PB!1 !" YB!c/U%BH(\yJ2@BI !Bb^>DĤ31q{Dǚ=2oUv86nmĢ"q{Dǚt"B!=DB bB!dɌf-æ_H[hQQG±&cMT5̌ҁ{`8h?<-i+cA{7]"KfSӝNSK|熥nݣo~ˁɮ_{ Ka8Aevpk Wǚѓkź=^ k|NcvJ0IN;vQ1mq\1~xWǚ1DbOud=H ʇ۽ǂ4ZQ֣{۶GYaᰂ X3&(‘eM}g >-G,eV~mp#A$"Ae:IN6krT8o>'kخRA/̽ qN|KKeup+C?95"޽;Ê |NR$>kmc ǚ "/g t͋^~Ha yi`?lбaA±&z躃B!c!B(XB "Bs"Ts6Bơ"qW5aouv5Qms~{r "a-Q#.y׉9NǼ ?/H rOb\r|8V\e֋l{aaA 7~#m~Q ~{$){"yq' -Pd@. @&h#0 xSNkF²a!A!hrV::`̂3na@{hVgz?TU:BV˙D2l4r5.(G(cunk;BmuapM7QZxV^ݮhy˨& >cV4ԋy|5ʬU9Q*#2*1=;J*d$8ۄXyYXcM e7rTFh6c^4rq2Eʓwu;c*:.g{JLXYc9D-_#,,&lk( \âgBBk6!ƅ/Ez13lǦ >h޵߼Gig'^Guf.a郂`|G_ %fØW)sfG rޟӆTFY]v7X$ye29xjM-7^/YbZnわr+ĬeH1'il1`R!ޓ"L>>jsE5kmr{z["nr9.,3fEJ؎u^d[m5ƛZ߇ 4< ^7Of=wNHzSa_%I= bywS7߳tnItڸ!IoH=@nA:q$iNlI?֕^9{v>ѫ}F~[f#!$D4\i|nsGNZr: _כ^^rXGܶXG(ۚ}*k'@}u X^uv;ZM=rl16:f6^:_vi=dMǚ8!kqdImy?sIclke`0yOܓg̘6$}dyYf{kY`WB鏡q1Ww.۞1ؐ<&xdfE>Ѥx֩^á3CqצgrL2ۍ#fUƌ5֤UB䈠|lk(lR'fwhuq:-YI:{ gMzhÊƕz1qUeU:RF[b9;tZ;d(2Y3;Y.(1>Vԇ嬬PUƚZ!2iXiN+{TFGTQz-zl`b yG?ʲB=5噸V JBn{qI}oںkA$DvQGQr*kg6g{>nܥ@IYW>KL!c/tЏLB)r1˲.[I,]Ƿs2w+-d Crk,Nc _Pl?責vN+ZVW+Weu='k{y$rztq9>I~=i7LCd2LᣯP;Uk!i]nQ3ei:f嘼Vtq(2bYOoIDAT]oU]~Ut<ZW| eb, ^lzTKl5ƛZ߇ 4$D"""""""*Y)4$=#fzyYQ\GܶXGۚ$ &OTgxxٞe0髡P1h\]Ջ3\K'l]YǚZ?,A-%}f|jkIc5`yu h׊PII^Ffzɻ~m/aMƆ0}Y\ț)]N7.uf8Z?3̸rmIy?c>r2fY;e1P&cW^BPflkihuPv;]J; }i˧?Wk\(Wż']qf.Z!t$ٺTiPh&SK\壾cb>L.geM.6zѴImyN B(uZ)VMHE֟ǘeE_z3=m{\%C>6XkQ+ge׶!&`B;um#?o\(&KxɦyC4گI_t9I׹'^GugYw5oH,5o[rh,r\3s3j8M']μmdyOeuP>oYm##}Q{M>ޗrhĬv֘] Ira2~ѼNH|m1WtχRV(> ibm~]Հe2'M/ٟmgq ^lzT6Ev.ƛZ߇ 4$D""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""k^|(f'D72hfB/ۿx143D;w*hvB$Iw7,jm}ٲdKZNevr[+oPPs? BMyG^B(Y֊n{ xɾ?9Ob^.PVO@3M # 11# Gb4;Ϭ2 }~&@V_[ "TB{[dͷښtnuuR7z>.;I󑖿vH"[ܒV_=W-͙ xs>'B\Sh}x ?EW_?kڧKft$OtvDZyeEz =F~cmRdhzd&/] uq29QTGwSVT[guh닅׻8jy6ݒ~NgA7~~k[74[w8͉X?D EuNw>Վ?.~}QKG=񙡶gZm[x]t8YoKYgBw/7o0r4wo̙k[o{33Ͼ3IT[ :>v;#ͭMuNupfA_>/IZeEGQ =4 =Ά&iR+&jVȷH~vٙV_Q wA+:Z{jlY3=UgSE_K@ "i4t$mIu6obWV[Gґ{| TohvGᑤ(ҡ R}"Ino{T\ w~<`(=™ ngAHS EiEHQ4T3No@ۺ}}P pzA\qe2GoeݫV{$I]E{)uuW=mQܤ4iPܳ3y4>ymjʚRKbo%H;wt7w?ڷKݕUx^)nXAŒ]<1#>u|[[<ˬ`V^Y._VuEyO.<hڏjx2hOHbͤj{?=1>йģxB/.~z/t}=C=~4>3e24ŴnZ{ZߓxGm V4b#$iuOu5߁կGѡ}Q~,H  zXte*9Z'$!IKpu;9$EQHFFC ?jtT^ H[[cRtmX+-J%L_/_T=my2vSi]{H$D,#@] Y{YmqE'SuE]f  !"Z>Vtniw>9rM>j9!+Bkz(+~!:+!)x[H}L:Kٍ)w'3kx^stniQt9ӯ%?h~ܴe)s] iq[7>04vm3Vx}d['ߢ} :Cd~6]N:&?g]VWHdp*C#O6gy_k'o<"c%j~h:NkY:Gd}ͤ}&k I;%ˎfhB}Ug-?Xs)3DnY33MuB9A]Uu},Jm(-IkyfRG5hL,2Qԕ띷dg\>|c އ(޲R%T>-uw:+ "UC͛ӺU>Z.cm26>mr׭$.~yŬuZ{5Gq$v&q+I&} 9!ӧO}-_X$ͻZ[_;~] U2ytl5:ȊGM ;ۺ+yΧ;z d$Du0:Tklf  @y}~2CPW0c woɒQ++|hd}JFPP s@! %%3@BD @eCM@"KKf""fKT}>zi55ڻkgׯIPIN'=!גzv> f vN?v%h=.Ҥ>iӺB۪oTͯ7TYzYB;Uܳwft>alyr48! rL<+nƳ9DL;A7/J=,(n}vIQ\=^6ܴv6im"i}E!.NImޤ%$DFLer^=LM^_޳X 􈘝E9;$ru&3DF!lc_* 1 $ߤ >ߓ.-ͳɲ#Ozk&6پ3I3|m{Z;}&_2igIm޴72(iNʳLۈwK{_Iƶ$ڔilS pGw"BcmET2!t:D V%k?F@%!ZQK.7|l owd;=_yCYR3x<*tdW7$D+}}j Xg$L>֏/jnP;CTV..q`'oL˃Nļ)!5fIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img3.png000066400000000000000000000521301304100213300256160ustar00rootroot00000000000000PNG  IHDRD\8٥ pHYs ,1tIME5 IDATxm׹ǡ50jdp$p1?x  `C2ف bȹ`\ȶs ;H&ppElFAa: q A5h 4P55֫_?XS]/VuU]O=yDF:K^ o{A_*SI5)ZAL IoG&mw ; _%@你UphD0l=e͈}V~Zl;| )m3Z{?\~F3rY3mqfZrY-W4k6ݲ%iHnNzRojQLYUѰ>l=lIAqBX!8s{ZAlqOj紏?i4Y}-x5vDc ݣ,N8qny[֬Ƕ6ُm@DDH4NxS.?F2ò)ur{ʳZFSz'u喛-FNVLy1;u2 NvrQ=LTZnջSM<ٶ|jkB?nP(=3sZvWۖN9h#ٌuD)mƶ۶[~-A[D#eq'ҋGf\zHx-nR.mEl|tX6YrhrWnYN{VOnZv˵jxhr(SmYO/Ot#jAw?W~.%"㕿yꩧ ?+o7ʹY٢6+og^:i[k?_[A/ْbZ=HB_+\}}뢦C c477R{隦ga>hDDf4C#ek4xQ?Z6;#Gmgse}Jyzdv޲-WsN;9<,bA|Z4؆-c-"OrS^Q,[rQ}42;c1z-߹6zn;Ŗ-WMOx6ɢae;Qx-wZ|lvʧOLsӵrI1w$,bA76E-_$slEU"Wmۮ4iQ~f~vy?XyN_}d5m۞; 8S_յQ+[Q -|߮k+ڬY-?c\]}G4MVPi}{ke"{*7S3Zwm{]MqK"r᧫zb&W1N뚈W9-hr3=eӲL˶m{fVL>,uE] -k;fmvfgٶDò-? سlۮTh9b3{lZݲ(9!JDtoKDR=tntR(XTOjp5oT4 :,Y"Rj =ʔ,'swz3g{.ɂ󗧢p%c"ηk]ƮmѲ_|+⢈\͵kz۪"RUoʩ5M+.Zu2)SQiw%cO*;ُⱅک޾-b|kZmtmj9}_76* +)Oa73]$Q?\]zuɟ @/֍@|p#/5K;m־_Yow] R}g])^^\â_g:nK"v{8'b;X^\PX_mVUU k7Ƹ{uOw4 +,w˲u]P+(WOtE+//(3.4MnyVe}VݑƢ:+fwhZrhuˆeڝƲ-?wl,kD=˲LOT3E|[n>kYdᭈޥCwͳFG#ncRIƽF1ʹʅjYՍ{[jmE,R}ܙoEp 6ְ,kEw%E\}joVr{V\QX]fr ])\K/ ]l8Cd6;WO]M3Jzrt[;eW rOoq!mkZqU.v]e[V[u7pѴbo;|hZa*3xS~l袧2EoV+7J-Pi>0V۴,tlvU)SrH@nVT1rɜ`{#xgGlո.-0,ֹxPs֯TFަճxĬ0jAJu j[VsKDcfcC?\V'gSZ\z_w?x`k%("Քw>?4gK9ɟtCl<ٶ -tͲɱJnu/\4+t+%tu^';^6-ssecSDk'^|hu߫rlY"2_4_4 FE4Exи}ݽ[_\q=4 6`0v3jA)xf~Twoy')G257ξa\ WK%+gǥ3/ᷫt9淭{mReyfy)+V">+-K4k?\}r(ew|Xsiy4:*F(WMʔ4 O0'@\q0&忠 fMaZƆإ5߱?ܼwiz83[<^c Ój)[f#aNvX.uX61'uˇwrD; 3dw?_jeOϜr'Y4s9|٤LyGb v3VKu8_[>懕DH{_^ t|W`V}/'W90*[ٸ`^uAgr̬nՓ=!pIDݻkfG57h,ڼYy0f3f3fRws)vBJ R_C&5{{LBeBypWHLdC]eBW-U" =oꫝ8.Z7ryw*uZDTogfWyO&܃ّ0&""m?w>>6ۦo7۷{4lMq[-.p'|뱵~C]u_l`DӋ|`ˁ4el|zKM[Oo,kE9ڙƞ]߱nlkhbZ[?^ol{wV[xa7{]DJ23i0?_f.sgo~ys޹pϪߦaqK?OjVliZA=F,65enfϙED=YY;1̎ҋ>.m81>hoi|dn}m.],+w2n߬/ xJV^_(/7m66bU+߫Ϳhg?xCvv7 %Mbs2.;3qn}2]״YM%A':t3meO]V7Xܹ[*==e'3Ww-ԞU|Wk~4[_m/bRq*XYbS Q9ՆUi5d8;_\zZ/> 9\}W]TZa˯ݼU3e1MgEf `fw?Ds8hˁEܶm{f5't3m/޷l8XE+G5xx;^T{]P5DD{Z }`k:EnHR-#cˌ&6iVdNV8+RЊ/KA|W~/aӯ,o~ݫW~}e%[{nq+3# GI#?k0Ìvg0Ekn],3eki>nbqӷ홙fl9Ymܪ_^,W*ed9Xjlrmx\lbu rO7E;{{Ly3e4%1Fkϴ"_y[6EdX|MDJ ZAMTjK;^)jZٔYIaFұ̬fvAVѼivn?8Ҫ~d;w6?ߪ<_mmwˍvbbU/039z^ 'cu IDATl ~g."}̬֗[#"^7"?lZ+|qQW~MfDoۍFۖY j/>X|lqV7mu>J0 >q4|󿠴ί̾&56ٌ0:CfH'M0q|nc` 0l=4]=QޟYh"]tc4+ ofi#Xt>4 MDnl庡/!)G}4 MD#-VZY*If @0˜?w[N.孞Y6I,G3Q}[#D'A ZDy2i(s'>|(G^XT c*~B0Ii7(4h~7P-"}'sRZ?u&N_<'14Ks&:1)8xg- ? zQ{ɇ\FC̪&d7CGq?V=?83ofK0r܉AKYߟ`JҏH_:/& VB1ut'c5OS~.C|`&1tFAa r2.p>&!fPK2Wp'fAEH|,Yw` e̶TRph778(NqG:8>\K|}M>o:# F&C4Q-G1QOFq8OS͒擘mZ憫m 3NDɓ'V~^ ukiѿ qMD$[[V:񲳽sNڽ{3Ch fSS R3f.{f\=q|;gV߷ L|sˡ/1{3 3 3R0S]~`ɹ[L`2 f;9q}e31k'.~̢‰ ~Y_JYS_!JM_T' 37HDRDE$q6olNqT#=E4~LD.w981(C|ވb+&HP ےb+&>>Lft>06ol&KzXAŷl0׈_I␖f9%u F0&5zB7S>o9 ?g @0` 3 T|cdn%Z{{d_%l,dbuN1D\3l\_*n4MKǬjpbH`3\`*x\L{0y$E2_m F=_rT\aH gTXe).aF_`s3ŝW-.N=#Y]+j0p7Llffo施b^dRCSQNXWx0>v- `BCZ&UpgV`2M'1 fMҬp}m; ddn:VM1q f1x).y%adXص0 {f$)4mʥsV랪{UNR4J'dd+eYx=3D߫1E:Y̆TR4og^{v_JT6鷢) f1"tzD9^UPLg՟v1}1[ɰWW7lUnG`sVWfg: 7%1w 3f3fBoV>%!/ Qܻ !G_gʕ'>y fny]ahbE|njPoÌNlpϠ2{>Sq=ٞ^r ;͹sοaPwJ Ɏ{]ߩ4̬iq Ɲ )yh&f(4C H$X*>K<bҩ"/aJB㭒sM'&[R"Y*n7 0]YNfLJG'Ai`}Fj5$l|#wзWC0#0RR?$EL.jguIOl$j'rը&d΍=D8jƢUZ_dfݡDϫvI_S|d2csIj}7~U3%_a d)yIrߪk"R=Qmoؼyˡ/F|ټ|nYDvwοyމ\wwϼr=` ` 3 At ).7J))/,|\D<7*"# N`~}XGO,tq 2[%CB(81* 3;Iӥҵ<[TN"%pHSZf9PC9YL~ﶼR_R@0ˑ/o˵Gd4`Y9Sr)0⨧]=3_VbAnqx8L~f7!C܃˫ͽ ?-8 6I=`&>:tz|ouluRڪT@ƍ/$D5S @0 @0 3ۼI;]~2m`|nqAF3f3f fB+;9sXɹq* ` `$d0=8wrʡsS|.c]sO&$3 'nEc 1+Wٙ' C0̛dWCg~"S0z,=8F n<ܴIqzJsکSFk(*ը 7!0:&{ffiDT;LZfxbuF u"oų>znQ9MC. ÌT3LH< Ì3f3fK IGK0`ORcCo_NĨ!Vq!aƹs?bGmN6>ݷQ屈OL :+QYO'F51\-1+0Iv?ո'W#P&!Y#/#R詧eW5BӋBWd14*s.HN넌j`YL}~fwI_uGؖ2Ǎ:Jj8 ޿܂(Cd9i∼ӢN6ATFG^3ܹq}?8q`gL쩣nfQi ~.wvQFz<*>j2~L|ofG$>/T*ly)Dɓ'V~^HDq\`.@!fe9y'r=~ 0f3f3f3Z:wex1?HLz<`5ިTNa@޲f;9 uvx\c}MZ R}Zn|ZGT,sf>dfCoNED2T1>"K} K,G-9SW0,ÜI=~ U="@y}̗np"suv"3ݛڡ\p_w ^^kT֖lߨ3 F:zQwϫ̦^/b ,QsczL ~ @0` @0` T~l&m`v˴`\ \3 @0 @0` 0 yt{ޗ0,&0]7W?l;o8y3r2!w18 $3z,ls*u%0c9}Yϔ+tGHP R+[FXۼ,-8a{cQÌK01>Boya 3f3f [/Mo}E#^_D?g koq`%u]gu9+˲80?Ye5/j;8q0h囙j4WBךӬa-=t7cά} t}z䫹ʁ j}/ŤV v3z$v9]9 gu``B7Wly0[OyCʭo$\a2݌iR' i9 Y zlS%*kc~`0wV4̓r0b0=!s_~R& @0` @0` ` af,"b,lUJ_PY'Y}kXi {GL,I߰ΚcVo$SY'i0ӧ]SՈӼ0QYup ș;g9~ )& !Ո{hpQlO|]AEfQ}줼S|`g\<,*t=CmcLYPN]Bӝ #Y< {O|+A ͐Rv8VEŘg˿5#iQ̜^,UT'[n] f f4` ` 3 CT]Ry3BM4a&x4,YQ\L Vf0`L{Uo%YsJd*0 2ftpF;+kfsrrNY_<*;ɢkc*f=s7ho1X(V}Zi `JYT+;)_9n(& ]P[}k*ٳ'39Ff}tC9%v ́]CW3Qb6ׁ#8`y縉o%[aշzzn]7kﹹ<DE9IB役"GLleF%3szWbnaPR~wo9Ӌ;,Dשu݌bX}tSf#bL{*Ll]o2_ /أ)JgYd[W_ߙ.n}c"t~ CG4@0` @0` 3f3f3f Q&@g7G=*OO_Im'q|^`"A蜊k~*l}½c:iwL%qzK4H}\Kqc,Aq7'd̢zdoۛxˡSGR{}ڋ\#YT xq0,b✡qw}7H }b@0KҼH 7IDATC EBϦ4{_o` 4&x {fYK|Wճ#SP517$:i@fJBSk[BkPq}({>=4WppwVed7ܗcN̂\K1UL9f4h Q+j[0F 3f3f3f f VZ;bVTz/81fJ2 Nc fm=;sw3RЉ]o~Uuo(u=JL}@ӓ_y=}1̢Ơ=O<@eJ<֙֠^OG̏ xdfnrq0:PHʫ=t7}of ݨ:'&*GY{NY2q0 S"ɓV]UDzڸ.yc{ݗ>Z{{Ͳ`TcwzԷ2>uTXe]|{fXd}5'.l] →|!&e9y'r=ʙ,`6`Y @0`l*[<` @0` `!Ky< aRnZ}Θ(>b=XgǴlBcaDKN:1C}d{^<֙&&*TBT_|ٴ`)|okLG:SV>NHf@f6n<_yk%xcT_<#}oo1 .[UW|t_ჭ3<$w`|=%"O,";;v"󻨷{=?댺ڑfCQ{ğgPzT؞ =ȯ:A-}CT*=S}$/OlwiB/.3Kp^2rR_瘋Q>QiG}c*^۸T\r러ą"2Ҽʂ("X 1|nYDvwοyމ\wwϼr&``Fho\hLYNɈ+4<003 .]Ϩ[7iل)o<@0` @0` @0` ` _/"blY Ţo6WXq+}m0QBȄ7-*.ΚcVo$SY'Q07y`2ࠍ3wzr1)BT\CrGc_zgP Ņ`w옂7FG}Tm=پ#Y=~?`YK 1Mݣnd/,*jZ<&}@icggcȾ6١!-̒dž׆bVPQwo9SaFŌad=kf1cZ)pxLH8YZ{{Ͳ`[m=nӽ_=oOO!ţr<$|{lלycSD_WYpEk|!&-7;kWd3~ 0f3fGYV# @0` @0`3nF f#-*8͍ `69Ӹ+ `,uE-{5STɷ± 6uhSDLԱ >l&3Ss )KR'*TfVW1ogELԂ0LS3n!2#u:sH`샙0j±JpL% 8lUF+ :cfG6G7iң") qӡYfg$ ߫B E]3ZZTuܾ 3`gW蔨EB>Hon( 3QqgW~ zYf0, @0 # fܼI\0ƙz`3e̬zJ{8-[no^0#` ⮙U_x3  3f ] [o4`GfX* GDf{;[MwG E43X,R. gәo]}?9F*2;Ub'UO';]֗wBlsQhzifp[*M(w{^` ↭?~sͬqό[>e(c.Շsr ?Yvm 7:qQ}I|իLŢ_'(1%CB7YƳ`+g0vGp QG]g_U ]FTnHq~W"䍧%pGpYJR`ž4=TT?ؖdf=d}<C#r8xW蝙yov w)(x#8 yCbW'*b=Cs l*_SMJ*u4s7fFpzN@} 1WBנu+'Agԫ*7S$^$k:@0`2U0 4Dⷩ:b~l94#3[z}qA2UNT.Sk?> }fOK=ɓzn/TNTԸgs#HV;ylY~'Oyj84N89im qΰ{̉g"ℴݥY)2kfNsSnSOѬ ^?yo KKIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img4.png000066400000000000000000000517111304100213300256230ustar00rootroot00000000000000PNG  IHDR=k> pHYs ,1tIME 'B IDATx_G %Ё*@7f Sb A` pe[h< ܾ0GY{v#ƽpŶf.2}Cׂւ ]`A'@WtVvfdDdD|@nGefd/"2sJ9q*##;=˵#' Y^x\N:bx*֟Zh8Mv@8ϽcMZ踦zԩSGSQ?3%JD`D *#:-2zIPqiHFIwZr/MJ?ђ8}%p%ÁvnOOe/e8$L0a„> 3 25f( ?ȹ޹#95eZ -=n=>ْu~pyLhNBpK?fqNdXH? NLwZrsb 8|?^wrx4Nthˁ*m8 ?ݖo`Hǃoýe„ &L~X2h2i&1;Խ0ц|UZgÃoFr} O/HpWr,[Nŗr϶d4Pw({y/_VlHo)AuZOd4}=!sd&<ޕY^^;ZHr="#gs yR}#3"5=ߓ3î\ynwC eWwZquڧ# }-}]V^DѲזa[6tsZ>'+ٹ#?ݻ}#_|!Y{i8girOmrwВV{9D/yDFI:=HwΜ iDŽKgO p:tZoOh ·'g>-pg>ܑA0$\G8B0a„ OxN[d Ъİn\aq_iة׭ONxTbx[ڊ""*g!}fj؃nIcYfB+6+Ycim?]i?qAڏ "2W[e魼+{3[y76i] rbWYւ@6ݗc,?YG W^;'e,/wdݒ7~+[-[v%os@r g:qmCrG%GoW['ÇB31s8id`- ٖ߆‹-ƅ~P>^p#HqEtg"G3q|p/s [ޒ &Lp:H ЇT1WXϦŵ/N>F-/ۗwtn6a"@F/w^kyyIz[~ qh$-~$;-iY9v&[s佟HՋd|ɕˋ[֙=>rwjuƂع+>~!L.*/]ʳϭ7Oޖs]Oݶ)ܘaY0ɄP8,~[;"!A0}iOa0ٴs%K1YpOe8>ٖaHv//ex/^d;Dr ' &L0gj(t!Oĥ!GW?~]g7>=;_}rBUG4[7qZ,?5~|zDVW" "K welKá\GGؒ7r._7Kr3lb ~ dy2͗Y`4W6($~[cO3'ÇA(ܞ0 CbxlD`N~0 wrx?xS1qxp/^^Y ?(|$ܛ·k„ &Lۿ8 bx鹯jWg8I ֯\1 "{'p?(>&[73;wGFtZq]|iYo۱#—ܓ\%YW䍟/5鴟OϿܑѣ{MQD^k&}3\ٺ'?ޑݱG7Rڕp$6ȅ.L<;ؽ({N.tvs84A(xKB#Bh&^&x~&&|Hp?$~{poQ'x9XLυ &Lp='1lXƕeǵpr"}c\-͍._בכ_C2ny[Zg>sZ.֛]U9|P>C E/i-d8ؖNBjZ Mƛ>{rB[DDɫ2zԑd6 v+VKDJulΙ9;L8|;|՚>YmX χgNi2.qsp";O$t;q< ?ٙxO-=ɓ qO-M$p=$C{poi"c{w'߬r0$L0a„鉐bܽ+7~sC;W+INTУ8V rr^xlpWΞo>y4:>wj/| }-ࡴdO,^8ٚLͧg2~8.:q u_\ۿoJO>N#+/!+=O3 t{';re;^X~fq<$qD&'.&BvtW_'L,x;_L&ޗnJ'Ι3+Ez{qE^8qkܑsωlꚴϴ$Fy)?:NptWKw]ZWq%a ޕwd{A;O֍-Ά޿MO_ސ{?ߒ3<8to$c0i hŇ'z՞9 ϶ƞhx6L#&u0n'"~'鰉"g`x& wIap4S< we0@sL0a„ W<| Y*Dd9ە)R3<հNdc.[~́|e鉓D<ʰmufQZ9'v7 'Ve,>wEN1{7 `,Zn/KNg%( 65G#9+`0_wvx dxo1>ӕQЗVkQ_d,| [gx,CṥO-N!181Ë2#xX=á7镕Ã0a„ OX5 V,_\.o{O"Fj]Oהsj!^Pvo{?nW=5KusI\vиFFfxg֮Vz {wdL/k/c b1@X9i,pDDdv.vэ ٸAwwNyghŵ`ixafvJwxlEݓC D"K0*t^b1TwJb4.b>b<ݤgfh NP-TT(m6Xܹq-&cZ:kM%~7g f|KV[nZG~ҢkKʵuMMK]\<g3yeWTB'm|pڱ$t]61K:Ճ4-I"EfI&NmZtK\Tq6-Im ͞UZKΕnmۊ=9=diؤoJ/Í7m@0 NbÞEn]μ]t)4I`~|% ꋫqwwI^ܯ햶 <[k=ax_;~I@mT][G֔{W-OuxY7$zѦCsonǻࢱ+sjqF18Ó *fIIԝ(%-=:~+z]{EU_ҩSU&i)#WE'^{򞔦Nlۭ.:CIOolBض}Uyi qIC>8l6lf4DKDN|g-s22=uGa۶[_m (Ngg_4M_'TAP9/%f,Xh떟|Vϧ-@Dq npcm'|Pa b1A @ b1@Y]O_:\/.]cIi _wmfuay= {"":W}e/i\=ACqT Ƥ6NqN;"3mYrөs'D.;*Bw uE^Q cyreTJ t+N 6YB]6=/M]Kq:W2L*^M #YҘt<ҩ::5yHThgiK. Ӣ2#)iKW eZ.YR]rYwGbZe[TɦLK- ,UB7|npoyYDj,8koI8U]+q[4-򘖿W\%lۖTUlJNnlu.ڭ*:C&A\D՝1=X*扉wˀ5&-yo"jKeUUEjWİCQ Q!eEof&޲";a_Ժ4,/eeԥߑAS-fZIIj*!b94tuU,&uInUE0]gyӼyv?iI+# ɂs%T=NstPS+Nnu&)ŧ{h'OQoC\O6o 틈mygwkj_^^.f`g:SOl(3 bL@@X@:N3asxbT= u=4Hn:Ie@<fuPi?'ޏIuPZAuIL/ipXg4ݕ.\OQc3NnM9L<.V¨¾γ<A{,}s@T^1NEMgZ#mu]m݊8igKtKZ^̞Md!Kc $h\4`lfz&Ú/{4-*کqO'PMA&>lڞY/{@%MDX9X c̏θ1ݍG"lN1n(:<&ѴcZDmtҙ:lbs[mO_e[D& Vqω:9RHp )UUP= iQmUx }4AP֪]\ny"D4H@ 8A\7K:جLx| 1tٞw^}Eס2Aϙ(84TҽUgtA5uD!8&<:<ٞ""vқw>ߑA O2]s6n1Ę:KKMYZ ][tl`uBAwX҄$3KgvNtN|,6>ka +n='=V#C9g:TMٸ!}پ-""q;Buޝ=Y>ӽ_kXº8YӾgn꠯tQ:!P*\']ClNsYZ|; R݈2b@;B #nSNӝȦ:f2YM7/h'QӶ [Z,y(u70iҖ1QOWg෭Tٔ*m`6"T.Vʈ5}!]cIt1h6T^1o^oa9 IDATi"1/psT솷j[B,4C #q6.& 4i c`ڪq!&1V:l?tCUY֜YW4>lڞY/{@%]h%6sfd(B6IcTtuх:2msUuBѴcZ:b3)u>٦:۞P41[e:uɎ%A\xMG+wZCcv>3&;I[['/g~qQҢU/y Dӿ2J/D(>xbܪJdn~.ʽmL4o[4$ 'yNZMХ=޿^*y{mյUծvM`:;<1 rJDf{:ڕJo|G6nHb4:-P ^w=aݵUיOufbkIUE]gy<:Ty͟I:m'zODz:]lÞvH`2TlӗgtNbshn?g:>!'7eEDd消wq 5{wdL/k/ b[fs>4fUȒG:Y:XD|L^m@cZfpU&b\lbDp:edT`B0K]h FB;*!Av%Y(ˀv ䷱ `!Pi[`iU4҂8mrX ICD+t|1&^)6"TN.V.ҒJ:Ӽ7K'Epx1]1\7"Eb_Kth ocܱZ*2gB`, t ø1hBg 89[Xw^ULɟN:m*,kڬkvmϬŗ=um.wt%yd;O6dgZ)qxpXu]{&m R&t6$ڽ,n'lV]P~\w]\'zlΤxT8f|:o{*[-M z"*-^IaP^^hhf,-mG0! :*GL_Vϣ~qQ֢U/E<+L7E47}cj b_$aATl-lYf{Cyq?^6rw HJKR_(D$X /!ZPvg(ҽU;*y{mյ19%"GGGG=EDvoJo7 |#W7$X;64]6%zt\5 u6mbk:OߢcҮISZz H~5'@g uM:]1V&_H9ڦCMzy vLu?m+<m6{Bƺz]9>lqTf=7Y'y\E:î^vlx:%BMr/6۴6/V|QtLQn"mx(ҽ/O5ҵ_ܸ*C7SwxYǩ׭ֽᔈtٽ+,l\ݐ TxA}7Yv-kM(YXU6dNxL]tI։;&W&DY,-϶˲lY\MJCvy/G:wzvt«,uYU:Ztܘpu4蔃K"Eq!oB5ټ)W7/""7EDdq\/$.;{~y}{ڗX9iNd*K?:&6_='@L򘵌NU_+wzS Iaߟ1|eUUlJRT'=N턕 I%W_q1-qWTP8棌lU_}1y̓,,_^={DQ~tT b 3a:ɮȱWJqxp| #z]Y:R_^/j쵫id!4; bE cJ@ 0)+#>lTߑAb O3]8n^z5UŶ+[^vM[t\]u6iJKo)kh:!-gK+U|#uBU~YuN4y] ]kX'-IcMf Zm&Xyal\ݐݾl+q:nÓDDj_^^Ķb|hXγA f:&6_:JĈZ  8Z;$7ciˆ9e|izdvso7Ӊ&oM Ħ P%Pm6ȲF]`\:׫Lԅo>P'I$\LKڬݕ"l^VLvK{I:*OA e)& T^7OXCѶhqt+شaxYhb}TEcQacZ~LKKJ.S 8T}ôixui~]7ċezѴutJ/z<7c2]Gȫ^gi Ny@ פ.s9YgS'6/pthg`)[u]҂qQuqyL_?=T29N>.W}ɂ@sىxuN5e P 06 I|( }W/egeR$6vϣwU>l".꿯x25d\(Lխ(<~"M 蛡JȚ486b0*/M_1ȟUer0LyaH3,cfAcv{ -SLfβ <YQPUl.ff96ٞ""vқw>ߑAb OYBMvoY 6YMZ8U+i/\[t\u6iJKodS<_Z9竌l's1U EcI٤vbxY_|y<g7l^ߔҿ""r~8Wmwޝ=Y>ӽ_kVٜʹNg8A28[4;H4>JN屆g~ÿMuuKX܋j7AyˋnWT#IMa4:4XSJu35Vz^̒?R@^/D蕝Mڶ2foֆ3•9/%Bw\oL>&cOuLn&;ٞ""vқw>ߑAb OYB-I8&]of5&KS#c3݇w0oxi٤)-q4f| @! (Tgw؜&z`gR#j't`3J/(joIuiM'LI?/f9il'eė,RUO6goc:Xg#$#Nɮy:יn桛4SsPn1e\fxmJ87Kjcv+C;nmYC_<ַ{kJ3.oRş]C18ͺus:mÿ:ܸl8MЦ Ó3m $&%yXmcM?: F=l6ƪg+Q8]CQvֵ*%jSNm <,%\ kRgŲB%O:S!.i;P3IGǶ^\\!.v>Dqث/Ch} N ȴv|i^";Z^1 ceMYq+ ˖I^۪ ޢp5vsFj# ˸AY6ݫuQ˪Mm򧻼I9dgRc_vI[Jgm"O~CW6˟T4%igNL&ejg6kd^umjy}S6nHn_DDonV_\I\""{wdL/k/ b[fs>4fl@9 C3d@@p xiwxxN@)1|B<'8TsڴxY͚G=_NP~'m~MQMw<ʢ*TeR]YL@مE26illltVo eOշ\K 3/=i+yAlc2G=uwL:nϕ> ύ2ldžLt+ի8*NձTEoNȗq/U˗-/Rq6.hozt.il=wf}Դ8}/zFey#Yx֝D-dbf_XtNZfYтF.')5\#@˻\q/IqLU;0@䔈tٽ+,l\ݐ Txbq۬ZVg[{Do{5fisgroiיqץ}"ɳ*vR:*,:t'oԍmbk pqHQ\uPM6o 틈mY9bꋫ6LEhdi`.hW LK\gڶ12ҩI}{fqE ޞM=ꁃ'ȭDs5$ĴD=뒆˜2I_#%l⻞iu7sSe[TɦLKڭ6MNgslh Vu7ZOK>Dz^ ;m$[%}f ?ℌuqhZT1-qNKZŕ`S`^F!Kp|:\[U٧ŰJ h]ΧYg4\f[[4K r^ċp}6#e鼊KeP0ڼҵ`y+/ e~j+| @eEՈ{s䷴zc_ԺY,/eeԥ8adu!vvzqxRG]'qFyQ,(JTŞMKeJ'>ӱOڦmN]DH[[; 2^3]1m8'f&H/'-Tō;S]gL*>ӒVFY3l& ҹ.fӦ F۟%lAiCgi:Cž:vsǹhf:o Hcl\ݐݾl+q:.I\""{wdL/k/ boea-3l2:3uAZ3dJm orTCJUCg6=PAF#=sPn2b1@YZ:y&řt A)mO;$K$ kZĠ+Rңn@7He8f9PIDATZ8taR]1-o1  2ar,-θ8v J.-bYEqa:٦7 14p&p@Ca.;Xg:ڸ K(B`qbQD^0ٍ.I -w;b*^vvK6 <9u&%]H@.6i637(l K(uKј5* F\^C p.oU"<PL+,Ca6ȍ>dLCX%13y QiEⴽ !L~3"-\Dmg 0d  qA! @ b1A @ gtoSlSs8}FecEu֦~ڔ_PiAF$uWZ%ߧLC8:6ͣt&٫R?m~y|Ĵӛ#;:۪L66)އL:IqL[\r6VPaA4ǧsl>f|MMxflžm-n"8,&~+LSNݧ)T]T_u*u1XA'8.DgSfz\:Mӛgު! ^&8uUί21y583NWZ+]HkZ9^g\)Ӕ$P'2|L44[V{&yWӆ(̵OJ_tZA|BD":xQ@XG 5DVҺƛtmS0up}x0A\ EU cbR| 5T<P$^&+*1h:!Ϥq?z yy}S6nHn_DDonV_\%"wgO/tW vyt|ҭ}|q? q1tPe*us^QO>Rj(ԫaN^NFgc3uiq&SDZlTGydNm!= W%`_:MwҎ#y+a 8NC>Rr(% ;"P0(^䝖2I""ک.iROܤ.m qIM=LqC9ƴkLA ]&__Sy\M'Ť^n}*=ؖԗv+lf>ukkAJAmSi{|܏vPbAL\odZlm{? 1A @ b1A < ꋫ^C f_v*Cn?mY9b'b(=S/SF@#2#d#R#d#rLS11T,a1TPKzE n޼ WV_\5ݫ ޸A@nwCK_S"_e 141 b1@cZvmv3q񵋔$eO-F2XÐ h4 ,?\z˒nT;TB3/g0vNPK}Yz~ݧ .ʣu_S<);ڇ0dn\oۯ/P] 1 JMټ !2buDtltO?[^$- Ͳ٩IXUZ\(r˚"de*Im:t:rxx;@^Z.<32&W7$ l\ݐSN%ֵioʅWI @v?x 2$0? DO A \6n`2 ,Qtc'[oo!̂__V5 90+ w\N@04mtG ⋯]lV,2b1@C}+o)+?`]p^[w_x><ߣk{ Oۉׇɇ(5pw'a;;9{ 8V!>}AwefwB7{b#pǍevN>p&rB/'  YIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img5.png000066400000000000000000000171001304100213300256160ustar00rootroot00000000000000PNG  IHDRpB pHYs ,1tIME(IDATxohf^ ^Ѐ#H::ΘB]bmZMNNE]zswa]ؒ,RN,]2 -0,8h4#=c?d#$}hDsB!@/3p+Ya !G>p^&ŭ+sG^@ow5K!'49D.5@#4տU= Uve9<00pxo޵ok*fr"ǓCw `Y:g0Svk销_K>#nxZ>#@ӲcDݯ ^<@VD~逇4^(K9q1&EwX)!'ܷ)7~R<"k=129! GN$ o(;&?|;^}>m|Ӄa-tߙaߨu)uf0LOԲ$cZu iY{Z 1s#U݀3sJ֋eMy˗c@~E<"Bg*_kZ B`_Gta%q' PlS;p͖:*!fߜGMCܪi_T \ =όA; .Mv=#.j -Ky&][ɣ"<4J u[ms>H]T$h >oM_dNYn׮Pȧe?^<5K[`Td|~"{1 ?1_ps|xB0 YAPbK|x eR^RBOQ|NP$Ϳl&//C*yǢc؍ R7=u~Q{nb{Zl/:I5k uO֚OA=hÉ Ԁ %mۄfmPZd1Te9fq杴,[W]u۵?5xE.μkE7.G|&7dTQ8=mw `h5h(]leixoPl?3J'K +,7nUvxTX֧T3dL-UW*|-3yĪcPyTwqs(I1}k &Tjk($直|y\!)[!YcwL~w*2 ;\u>OY}mx:/ 0wM 0Z`=Jz}=1k>-硆gҼٗiF {D< w?a7 -3(U<6[%Hfgqu(gE̿)04}Wq7Q|_a쨁yVwӍ /UL\hf m["ft0?3wdiU,STv#"6s; a/NE YM6/ObRQaTQ8mGwj0M(gE{9sI2*R`Lһe/b4 9 hWV;}8>C6\=({!]7 k418 3w<kOöޱ4,@Lw KКOfkYoס鎶̹ i2֠=oϧ,̻,tw4+鑃>pS`Zl9xWCIc " t]X~ 5s΋L`ɚz Dq mL]°`Z WfZ./ KP.9kXC. mO[mD8aHr3(/OC94MTU|k&NyDF x37(\Ml~YY@Ϧ]G@;|7K։Q Vב,Bۧ] w{ϼ14Q0 '`{*@<* 1Gx%ur0A`J:y98ԨO OtAt䂈Q>mMwha7h6OS400( Ӌ%7FNK^A4D ÀX Ο'0q^Q yL&Z.3,™iL^(zf$rfewf^S6amu(+V>X\n2uM]27Pg bS/O|96F!k[O|0YuP.,cXp`*n-4ML İ~J81m>mrU3 xWQZ;lj_K}w7wzAffZq|֣ZtQ)@ m2}˼m'Nʼ;* .(Oę*]A < 2毭YQ, k@%L(b -zF#2=ywQT2U ɉhdYF|-C#۷ U4VׯBJ/Ɔ=;2fP'^CR`z1mvrNt~^`Kr۳0X=,1:e7>}wJnW84s (4ODqRq3=;ۙ y֧$ #y;3oG%+S Q2oGmy m.JyXc =Y2mNXl=/`ĴـfUY0qpYkf 45i+U;g D(a5UԾ }`n!%ԞZS/OPc]+@+y`]9pJ TewxN<@Դ}F2e gןQ*LK`5(K702} 7u.= $fAQb^lG Y&*yD• #" D* ^]Yuq.cfnۧ!K|v96M;!0n}wnLQ߭ZDݨܵua>.C۩pH h6sOwcҖytQ3sպzUաĴy 9ky[,IYq1?7T}&0o4^ج~RA}G_G`.w[?dŷ}տq&3?_2&9ɗ c kS-d}?kPi@M;ڷe@[} BȸL"beԾ W>(cŷqgt'ehO6_zw Zb6aL}Bu_lG-\[/_/WU۬ ҄WӪnX[_V_^h K]|ШMMҠm_DͼHMw/3ws7I!mf!Swc >ˣ!}V׽>>'A5$B[f2Tէ/t[ɤKvfޡ5\;WZxn\Y "uzFўkx{U`Ԭ '@ J%`߄I).udR B{Z ]݀0T46A'Z |w6JTTwzviqT`oWUa^=Mhe}bNXDTX6/ OapH]&(0°s״ @ 4<-NW;)nԽμ}=ݹAw$Jt>'C{^jP,e15 5zu v +I x?]5+4=@YͲe!]yG\|b;4o!'+!I9yp3?\Pg0htNL}9g7\C˴h]ni  kx#fVF71p]uhkg[5Ten&h׼ U-y2RI72p]߭ \BI/Wb̓?Yk=UVaWBȑ"ڧkGZo6}^R wMaR{!*̹>.z1x8!N!4pB!4pB!4pBBBBȉ%<@R3sy_i8!t_2-_ N3(Bmʢ\2'>8!2c'[7p Yy7zŕt]V̢kV,ܬmigmv܇IN&e jwbĸf̬S$if; O t;'VuKR6{YX_ԙ6nZNR^xޤg$*7|dVqr{չvj]=n%݆4f۰D4X(ogC6IwE}Ns)7*KIĕYwY4+tIvJ)[]\7*cΪLrL ~Iͫ˯09AՁɥw<Ӗf{;Qg̐{#Ym?"n֟QNͽtn+nbi[:wo`eQ lL}9ei fҧ0'C&:Ya+'}p2Žf/rz~yz:y w;+Ҵxx9ތS(+ -$9 ϓI;;iI">q/  +MKZf\͖o\ٝ,?II9e^/מfYYmWNa7a]KK[z"o'؝t%I>mt $%i\iU9Hڟ9tZ&쵌1.M,aK/:IIZ^*u&-/"w1n쏇yuE67QiAZd8&}n0^(Y0NGY mȁ=WV&̫L/u'ͮ1L8m ·muPڗ+/]+$wǮ'*cHzR l<(3vbwe۹+iAkJ jLSr v;xIkviq˶I5+n.NiNnf'Ӝ2Q\D8qpppp]/(!'y|l}!B:y{|8ߥy?V727[El&G?%f84pB&G֍@A'<cN,ޢB>ߨBH0'$%K3$$-D''~](B'r ЧL_f sg7>e89 _I4>pBB9Lw=t+u|>zXCȉ7paia ׏r9.q/*\xz'kUv>lQ:븴YRg8!P ;t܋T\\]i7Pkd2IK;̢;%]&wvI2,2^+BZĸEIBNwz9uY&UndR&!7#nPӲ;m_u3pvu{^TraѽN\l>up7_&eվ4mbA6!83_Q,3^|7~vB#gaV'fsW\f _ȟBHB'8x!O0_e~/ NKӼc !N!N!N!4pB!4pB!4pBBBBB1B!>\F"N$Q"$vB3pr\PFGYwԻWAr$ƃ0'P=nj0fBhB'B'5$'[ ӓ|k>u< @nל{ż{B iָd^5fWQӜIk58;fۙFHp!5Ҡ 3p4BOINdNHģ;#'# O;%oƭ Mzgq"e:Uϧ˟_ \C˻3o|#Ͱ,WLYӓ7j$Iߣ.A^JI"w 4~n$j,H»P!N!N!N!4pB!=Cүb;$ 3pr׷89Ԟ0sy&8!fफ( @ 3gP{Zc #؅B!4pB!Id<*˩ ^x#BrUlxB;L64l;Ԟqin5BpnZSy@.B>U:!8>&>USBN^ד_ )&XL;@yrB)J.& IENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img6.png000066400000000000000000000171001304100213300256170ustar00rootroot00000000000000PNG  IHDRH pHYs ,1tIME8oIDATx_hf9 $pa Lh66݋:/X=.:Eh t/z.ta.lI ԅvz>CВYH@F5Xp >34F6~I>Gϼ'y B!d4kSg/bBF\oognu,XcccM #QJixC; BB,߷!C$DsTQ^xO/X&AsV +J?+a/Z`#Q Q 8J0w-6ð#l;|B. ]fСX(L!1蔝 ;Ƶ7+'%\}w F1 >jy7x揂j 揖C`hنfmfC+!iC`M ־л.ZaZvGe*7t9OȨ?1}ZMYҞ4tLMMMMw1ȮV=po3&FAߵPݪz_Kn0 @*يݟ<)AAK*c, 2 e/ a7̎4֑qX?>^yY?Z^>d%D ]|Yן ZR`=GU`&@č?=ke̼X/kT[ۂm6"K,,I0M'4)|*=L5OgyW ZUt}g3'3KA{f)BVi }tnwJ 5:`Ȥe|9jK7bh 7Hr6% eXV,TqY\g ia~n# n&^-SFds 2*Sp*j۷ Oc&J&$IӶY2 Kũ3 .q1 aׄrRgΨsio̙`-h I3dk=U" eRХ3h!Okz]dOfE=&C̏z¯eK>hiA]G=uGRw.R)Vsu۸XwNҾ&4Fne \$ՙ`sk:,ۙ X&$ XyjZ ĒsUQbgP>im MncfJ @IVV15'%ZP:4MZz6P%0O_4QfyYܙ{/G%!ɮ}'a Zv&-Ҥܤ>$kzZ-0vMQ]E{ΑڎEh;uOWtfn~_OWT>B{DMMMMΪr$z`{wbEȖQ>^%1GM|-XE:sE+XD,[sk1*NJXyw[װ[X~kUXJſ3!^…2n~xo2~-`Pp̗$Vµ[eY zүKRhW=A>".ht`]33A]>%W u`zGЕ"ӫyf3}6W>QSSSSg*8iQXH1 }3ēVƨy" "M@aRXE+2;iTP]AT4P.>@G,eӎAڼ iB>־6q_pY_D4A:v$l s8=<24#q%ũ$9e#N41K>8}dW %9dvtOWNut%gUgcSSSSSg75ATUzRᅕ FU 6i{1?݆5osaxKs$$}WbBEĊ3GnŅEfq~ Q[iRAhj+0M ;BQTAT\zߒ&V?h%uW߹ٗd\uԾpaWX{$:4M̝ۮH*VޝMP^+!IrPzLFj۷P>}n6ԬjRvMIv, lГ]i joXM/LV`3?J 9f;'K0ϕP0ZPW*%{NUhϪv5Mgʩ,QSSSSg# :{2-o-c9r=M\18/ԟ9:~~=6pq2֛)D ri ", *,S֓Paַai"Jyem{u i;,پ茿OwJ'fi YIa3~3'Jo¶jwqsx!BCD!d/7)B! fc`efB!Oq-;i2BHV?YM\)қaB! >lFM^z}חi$Q %M;~a]e$HxG:mX.ĸĭ?y¼deF9/Q:y:^^/ C^r ~aOZyiݨK¼ܵߣl2eT !B0S:;N%7$떾:vq Q μ#QQ%e~#t<Ky, 4H ,cЭ恴2:`u\< KV1C;}"fK3c_ujZ!^%˘LcCo= 2qҋ~ClꕫMY&2Gp{8ݘaK\\,鸘PKycC_lu y1Β~px7Iyb]9iV:*Mܬ~zٗ1Ӟ~K'/8F!$BzH/vLBA"d<ҿ{9\'f_0&eL2b#B tXy{;/nð$V`7/ibNz?oB ]]/j獟m7m..kwQ˓ۗucWlM󞤯$B!KM(-,n~+M7qWc/Kۗ3~&=lǵn}/_!jyڶcb)M\/i ~cv1olm3m̸h\˃Q4/Y%% & f1EǐG_ϸO% Rio 4M[h1B̨8ylN\vU̸h\ XTAK*K.oE.g%,*{f4~fyv-n.KڶnciymsՄ^ƌ.au\%(i}"8d&Vڭ7(q(JbdYeUoq}ɢ%[ye?9 Үɉc~3GqC/݌]mÐaKVUq,vlF$]}q~/CZ(v80n^q7mycfɻՋJZ"&!C-ޫ2%= 8F! b#cuƷbF1 $N\NsW_g} ;Y+V!lQV7ʍoy1ğ;+3|1 su93i{+ND!ddɲڑw?/ku?ai!tƓՍ2*F7dY Bx#7(dy3;*+k !$gYE˲B6)κy !݄03SA"j2Dj fw̬\(~F/ o&o&ߤM!!4HB!!s@H[iUfB!4H"o-3cBAWf}3 !BN&B t!B s-aB!#o7kkԋSmubTV 3Ll;;B@;"f$]!Qj$u'y"$t%B% BiB!E|Q B!dR*DcD!a6Jmo|&y!BH/Rp^jnOM8{gw9)B!}GПTClNJ V!2ƨ#DD!a4FVFB!dQ)(_ B!IV(DD N!dmRvIBL?tB!HTA0ǷeB!ddx) BA"BA"BA"BA"BɎDOۊ!u&|WB!$S8F!V>^[5~:BbT?y?'OYA)Lx!$[nS iw֫J@Y@7BIi#q2O9s!Bh!Bh!Bh!B:gS'NGړ,6O$Wù!NP%ta}^+,O4HiO~4mQаBH{ڍ><Ұ\آ G3nLȩ(nqE?*_I#ABQr=-L'\w;GT,)Ci&8qI: ӤC;}"A2Gb$J'1' )|ξ [!+'m[uht!nDڹ&)l[qyQ혰~SZmzn 82*tf;(Cfqy3wQ&2i>D6@$( I B( 2bǒhe +kSٛV&"mӮۍ87 BFivKc$$%mK^n7|P T>|q26~h/&)`?v4o혘cd߭@!lzTŋ$S;E e}\2MTb=ʾ\v2poNbsvMB]'Ԥ]պ|PcMǤfnF!B!4HҬK!$B!$B!nEw?;<&OVcRU{yoB!ɸL mWjk07p:&s~Y&yA"B T>^f!B2wa5fB!#!Bh!B bS|Q_*.!Br-C{~MǜW>lZ|"ɩ=2p>BH!|ǭXC0VHQ;h5γF!IWp ]}sc2!BȠzOf^~k7B!qq1F>_4!SN!?ÒnΛqIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img7.png000066400000000000000000000620431304100213300256260ustar00rootroot00000000000000PNG  IHDRDZN pHYs ,1tIME" IDATx׽W Gl@YcrG6B6YBnoS{o?R'_N?Bnp)7T× x/6l ,FHz>xF3s̙9-u&ӷ}$}xhuk3P%}@w:ڹ/[qP5jB MJgY#{Q5mKJYtnR5T.&Mt-](OC0qXVQYJHww©o$}}>3eFaG ʞ0QA{P˺YF!P@i#\I9Z$ztbS]_3OUl-*َsoIgٌʾSˣqK%tR|u,mbٖrItkBQucwk՞׮^io+>uO&TH/o^5kT|P׵|惱 O'u]ՎJ5y$ ,+meOXrƟjήdZpqbNږNY1ҏ1jwsY,/*k˻f^rE]vϤky)-lۑIS+zOi՜r'jH֌q;nS=l?ۂF%LWZ3>;mi(%82Qz=5nIG}_WU=p7RK:R#]zXYRv.UfpTzؒnG='S*ҧNfj酌 EҤIOknSP2P0$C}[ַD]k?Z W?jp뿅\ّrrGtmWeNꛋʤ-]]ӏrA_K/q͟]8JWZ uItHՎtŶkGb;tʒJvk'k̬Q䨲o`-]jIKt6kT~XQHׂ^t K*=>I4iK;N{ '%-Gj}LkYo:kwODUP#7n7;ueQK:w[S.gKr]z.ⲌwEbAzDV†_Q\Wk=%eO.7?/-뻮lnuYmP(hM-ն__R:.7⽛_bWڷNz*%۩Vj-0.{e9N^ʚ[}y>xwEGӊl]xsǣ'#E(}ٴ-]y#]+WDQY/_> INh8mf;-M15db}ZOK- \$)TٷyMoOX>@=ҹgҵ>CQ%KL/tOkmB=}:=ҤIOc:Xsz9=wvej Ա7~홛`W?m}|+qRVή>%-=W *hZX?˫\v=yQƤR:پ<,hiT۲Std٢QjvN0%-ʯRa,sʞ7Ul|b;y9R&u_t ?R8#qKKR6]+r,:Zj-^h{tCJfS N5,<>hI϶3_<41RK:{뱖৲ؙ=SgK3%VRg+Z0Ֆ.&Mzn-4~!_˒eنe%찚leBdG.hÍ%cͭ$BwG.w򲬬1Ϸ?ߖee=kiq}YwefOts}f٩>d}oK7NukK*?{Weef| ߳LVJy֞v-,aYY?O$cɪ?m[lV 'Srч볎_Cik&)ry+)ǩGz0TulrTGzKQwX>VرT=8O-G=dj-I8L >.>,7 '3*ʲ[[ zpT닶p:[PY9S|h7&Mz_oFu7]믯k奕 ':X[VgN];Uw:PR*O-ǖ,<ݿXFc)%/%\OmYWji3}z|2X[0Xڹu.-Sosg=CO>̦N[m-u},sy8?5*mm|WޖϬi?wkSMg3G8?w]mtYV᫂lKmNr'223weeO3-fʚtbGٹsͲ͚ǎ%=?|SkAʝ/̓d{~屙}P9JϚZKRg,AZ:mN#͞H?Q\iyL֑nFrX3)j*[ҧ^іݸS\&Mz= 1L뻬.}1۲WFhG%;=@ũ\)%LG.`Qa^]l[%ΩzPRަI+;{Ͻ߲d,esk]^ٖ^vQb)s|mzpCr%?/7dLFq҂;hБI:d/P.J?iPTfl1,W8-T:lyôI$unI-#Qe'mYYK΁S $Ӗ(M:\7閖]90]ojK-3dTگ lFrKYUŅ]ҧk9=,\&M%Ϯ5o] n޺ Vk!j63+mժ}D{[bskGi|ζ^:"ItuuӖ^TyVSO|wYZP*>(\֟kKdwCRk{Ճ-lk=Zka:mr`Y-ਵсCu{02lmzL=G*>*-Crb%4iә)Tn޺`Y(1쳻'uqt(=x?^W*c)]Vr&̵CuGKQ1Y[bA{P[:mɶӥ{ڃtOm3zt%J5鎖fKQ# Xjoj3\\+GҤIOk:9X!~:j!*-YZ Qvn Q\_5VMe?]\O=Lwt֍@R#]stoaRG Q3,s(7us͸g˽>D](906om*{"uڷ)0EXbw|^sz+/0 4u=z zb!n2h]A 4 ͵Dk~# kug|591ʍEm^#@1yMNV$ 0\pb P xd@p1Cg<0 B#3&#>{ƥh[Wn #k!j}U4Nb+~ :7|ygR`a>`"G@(@@@@@@@@0\ؼI .u7 6=:>D`"LE0~ξxJtF/?,c'/{krwOA0)W*2V8J_%NrJ4?7{{a>37mIS[֕_^RXVW߾+h0ٖL tF58RTd r@⊮}EL mk֦V_]*'>+sQY~v+egWw7.(LV~ӲǑIU*ϒd*adZE%D,LZ@dfűqjaaAnhkZE^=k]5j~V/܂ο}U۟u޲=_G >+gZ2 IG+ogҲ z]GW#znyU:Y>h-DW߾J=I}]jE΁#'($ŒcI23)-= owoylk:,Wê2-IHJ+ZPŮ$Jn=_;(*=WVei{H¾VTWQEIx`T~TZ:(jq7ctzI_lR{䕙MKɤ2I]}nz[믯\*=K[:ήC2)u*۶UzXR᫂>?qUJ8[TdfRQ[)SKsCu.>cOas(i"ooζ;}V+gVJj}ye]0J[SVtn1K.I%}eM墶\+ҽb$k7sGϽzNW߽3+G%]}Pd2뒮G Lu@ ȩfRJSC>t-C'l!]ȝf"Xe3puI/ ZyqYgF5ɣ SuҍZJiLH-/+-_Hw*}];Zy~Y/.+}"-iFAg*2ںSѩSNis/-C)KN?QO=qȤҲ~@d䨪LU~tJtK'Z>Lv)N~~xC:6~}U􏗔Z2ξn6k2OZ*@>DD#'i:TU%cK8wxPy\Ud=*R˂٬Lc0cfou>Q++;qS~Wav%ȩ־fإHʁ41IcAӶ+vUZ[3ʻ׵yU쒜ѩ_οoI6>7ʻWrfE_ T-A7HmmGHC@㑙|d k=~!*d=_Tq?j`2,*'k$*JR=p/kO v_/bN*ݜ̌%?qT/EٶdN+c.S⁥/-/ԺPَT[P6m]9_ܕst,QZZ`OΫeAGKN?^ҍ7˪|mke?vQÀA[E`\Z}^5riCv4mT\.1-G{a _/k9URY+YPdRzg8rQ+o\PŮjksShRRVlNbE_Xm+ʥZ_JI%cdY)].{|JJe-9]eFK'SZ:=/k6-hWTTܯ(8J%{l7|_W߻{*eeuwwW?nhok>[Z~h=w[}3i rcWu!rk-FsOtCo.VU*>TX`f^V:;N/JUUGCGAkR)Kl`x+::RN˧S>*LʚZDi.+} yqYlVcD(sb^ww Wo4_E oK("T/Q$DrIBZI+rx7MQ,u}>Bٌ2.HRD7Ud?*ւ̱RƨX,~[\IYlFʁ#qV-[\RVU>h+râGa69Χ7TjS3ovۛ}y`R|ٷe>*x%?*UC >w^Y_.p֣cY{nY2)ѕk-wG%U)9cT}}6c*/xDL~'\|0mahmL7>D6)!0Q~ARB`bvGf`"L;/f=B9)e>}p7(p:# r<<)Ei-omStƇy P~9C\ Y@@@@@@@@@0A,K۴tP bnܦ^1ly]^W~i~}0?7g{YrZnUFn ܊C^ (r./Vٸ-7eT:/a(^;v~FMJ3RCDk܅ލB_;n>L/ .GrU6nw^Kr L>h\֯զ~ ^yD{K:K/ߺmA_^rr+QH[Y)5ztv{˙TA=-c`RxiibƴF}cJ:': 5u?]cukDh= Yfr&rm+830=r^0u uI-TaKAy5~~ z@ԭr'v2̧Gj5 _bRYH ,u+qgF%/A: 3J+0 hMqkeg{(Dъ~s8ƸXW^<2C{j.{a] 0.s\AX#Sk_og DyNCq4LYQ Su;F߭׻{L儵QU0^Ju>ZA4i :[: rⷕ-OAEQ@nǘ׺K='u Q_$!%oFra$IgrG斮w-6e>M[[2hk[}]w#;!Aos`(;:ڹfܳ^_" 13"o L:"L7O!Q~B ^!J  0n6omjU L="@@D""""")zsO|won̗z#uu/uD\F۲ɗzroܖD3vט8jB^@' cutn't4?'0Ű0}ưm @@4[ӻ. <~eL~#HO{A}ij < o]9pyɭ=`E;:JzmTFnѹvz{o1t  ^x Q`Eҩz[ؕ3+Swq ?8 q;eRop\SIm#ȍ69q>D"n$oNRO|VTNo<.}{et[NF螤7Nz;a0€h ! bJW{[췜`Jw樂8n7 Zpׅ1es}wkE܂A+Pp -iؿrMZRA0QD]:7}Mq+PvwK0 mq!^4}oTy>:w,/Ey:<8A@c~;U^QVnTtw^&`o`q5=/(ugd =ohW^#3!ZP-$ATjQF Ԥ_DRk|Er7?w*q1FnPsq4<y XW */l9 8~:*Iۿؚ2.oޢ؆Ʊ<@Gf"DS.>DmmAs;Dzql j=O Z9X8{ʟ>D".Gf6)M; eJ V_] c>D"DDDDDDDD0?7rZG\&smƽ^Qx~n^{%ec>q:##N|dָ{sk^zkuu.m]~~i MzSw}U`rrJڂ{i/ww]^׹\/0xۭ5?h b۩rU [ 2T@uGT<-D6]3ʸUk`_+o j0q:9WxCDt=<r5L3zﻕ50 x8뜫@QU`Qaa~2 qP`mxp7Wù G=x@43/}siaۺ0:Ϗ^9-{ Z"/n0yR A L{P{~~|&Cw")P/ h[/3+lDӋ.ٟ($mǮ6>:`ZЇQ`ڍtT*Jnoޣܮn#~@@4 _Ԛv`O]@T6omzꫫ}n{31MHc;Zw[^vw/7}@Lq bUOiAu˞~A9\w`/ :P_`dѤkzqU* 6[vTpxY~P^ZC'z-^|EGaqzR90Oc|v3ZK@ۯ# jZ/n}/<2:0cqz=6=n[ >^X{:}H&3tfr wPx7^AaQ(;.eh"{T8{?l ߋqnˀR(J5M缏bi罞{z8\ ;S7n7PAMmm}oF@:YFȬׅ- #)yrZPM,@ |=;/ƒtxt5so]nBZӃ}$ TA~ O6i~em ga0gYw\Mo]?@h!zz1?XDmLd!NvQP.DnDZ92ͬ]L:U"L5^@ȶٺVάPDSTu0DĚmۡ-{ ^<"@L{ﻃ~oTE>˞/ne1oJ7-W!HJDѢNwk^ְ{sg>;k=n׫W!ZWۇ~M^aR'0ϛA>y"k輈7nlqWڭM>Y2Hwvf kڭœ˲] |}}O^?w$eРegU.#3s# `"7@""@@tӋi̊ DчQ`l& ztDߺLi0ꫫt@`чQ`j%F޽|,`:^u~9P8T>T~xWxg#(GzU0*ƿnwݦyetZtD͠ wx 89cյ{nnwYOߣ,w(/oPupDuk%ruAfނk Qrz0`[W_]tbk&{Ezvb3Wst{Qo  z(htݝᙶ>D~+[wn~f `dټ9rUP8.7/Z1xͯia !p2w8"DDDDDD#ʋE]Y`A f'Dt9A u9@=4q$ٶ͑Hh~Ů|[貢F>D@Lzz(hR)U*o>{͗JwNw6L^{-o^3 nwgUl/+edXJ=tu^hFuB-Xh-_}wCkm0u K>|Wvu]%,KJ$%cTM6?^sPn/k֏YZR޺n@q8mCz(fQwNP-pwA/ݖk$;jwgg$$$DZTum޺!Y+ۺKX~.v2VE vukY 1-yqkꗏ^ׯl[ӦU,_m\xc]ߺT|l9rd2˶FvT⺊_m nY{뽦:W[ 9o^ݾAѰo>nC~a@5xqo;n'nusg^sknA07.i2Uq- ΒO-))9#ه)i}&Z{y/*wl[G;hCۯ:qu1z}aMzM Qgz(Q="o^"¸w{Cn>K~[AߕIR-c:rmY%o)8R(s<윑1)-.mGhe-iR(1Qi0qn2reyi8Y|EnwsaO~EswfێҾ—E9USuG)S_TSA>W+ʍyPvȋ|}aløghZ(qj'ߦ+(7,X(jEeYDR)!1җ;~ԡE]͒LIMP޴A4\CЈeI1P4~}>Rd΃w>/}^ }nyq֯tMu|6zSqx<9ݙ1)dfSJkС#(i$/z'/:R*%+tEFmhy1+A5k>{]_ӂv״AΝ| agXz(f`^,_>ÖKP7ȶyw z];z'ʪr٬$GaUFg[ζh^U>q4`Ex+"˜Զo^Qa! kS(  ;44rCG4N^9jnF|ZUUOdCDCr{3Jh<=kUfS(qjg}XFF3'%gsKFRAF c,eOHTe41ͻU~cz#ݙvdcMJɄGU]i뜒3R*-@?QŹG6Pm@@ì;zGߺ|Iv]92#cdªee̊2Z^2geQ1r9LG=I@^@WS[-SѥLd,#3c9X6eP|uYb]fqz}9 o^E5hLmJƫ_u~mo:V$Y\TFsꡩCXX?uJѥ?]br릤0y CQ z~9}K=4 BAzuǸ;8h|Q@~ z0X5iCSy9)퍥~*pq jۣ4AS|0-DaWtIo@(8 TPg (;AF }ֺ1.uN-T*xgA گ[aL j~:ivҨ j۽Nb (ApT+lRGEX2Fdl%FJ$ka2rFxu { ;@%dݾ =F=b IDAT.3z(Qz roL7̻3r L:&Zf,ՇNHӋʚԓg~}n(=x]fGl2㼏U:WwI2rHkI:ׂet_obJ2u0YDYGq181ƤI@4H'CxoG =r c6/<̨%,2Fr%Ǒ*uIfM[Pٯy*vCPO=鸘Veq NW={iA;;mޙIW~j%]eI{}G2>RfύAyY~u7A77n\/Pdc :_cδAZLo>;^0tˋ2޾0%čTxTTf6v_7[I??ҁtjnWr$Y3fdݪ鶿P5/dn_qQ 뷬9ΨbB4hW/E90l{~/r 3/q3f-Yg0Z=c%m,(w"3W.1W:5,s=_4s=AhoYzQ( 0:ֱq2&H^vGF{r^ۇJ}fͻL~=D@Lcnj%cLgLR;FHwev2nsҿSxnW0xuq)Q.6Rʤdjo}XHOT9&˶uɒ>7ޭjxnOwMqz΁ҽV8+%Ju[Âh^U儤¦ƨȚLG=Xf"a\bPXeg0]I76dYK*/({oW9z%ɨwj3.?xf08{\%N9v~vwpа1bPذbZ6eܒ'm9ڳ,m=DJH) 2& ?=&ٸ ;AzZeMXP:1hl cYnKg5%MU)KZ~ɔFGXA>3]-m^eAU ;_e==z([ˑϽw8 ; bz)SwtC믯+tVLR-ci/(BbEm-YJ>s )R21c a J4na zƶG=,P3C,NӺyiz,qFXo+ԒmA%֏&a0T {p(qĠqizh$o"KǾ(u:-nҸePC⛎?ɼqVz22 иmӀalR* /W~۵ iLW^q0vĩJDcZwPvkqbp(uwب5ЬAaKa3 [Wn5p0;_TyQAg^aDa oz=-2U~-kFY6EAqt3C.3. 38 c0YmL;*2$<mz1,` 2椿l0xdXI*jbڰ%z}q:~$Cb5kP`BcRwy9x Aein_u| D4nξ z ϨT #/ϴ08ŶmCm}C1 F9(0ZRk~n^{&)iqKu[7؏OG}>PŬhT 0Ƕ$ie忸;1<PuM&g++ݽlV˧Oi~n~/Өv2L $lmrQ3];Q8 L۟veIrcJ@~ǠuPQӆ3Z_ox6ӏqF>kXH OveOuZƝZ\9۴0fZuf$%]ÀqxdLQP[C6+!2:ڊ_Lm=Xfm]zdzZ:}J:R'*Kmߝ_yƀ c>LV=D 0QŮt%Nd4S_jo؃hE41&" J}w^ƪ?+c+\[EٯzE*|0Xn~0~*5~s2k%Yzn:wnY寫Z;|Gf1ރra?PI QkSdg[3e)]z=-/~r,|L c?ݾrf%tJHd;O7-Lo=N~ic02e?UӼ*%4O'Ӎ?=Wt٨ZX3iA @^|>}4lyZ&PL"?tY0e0ޫtw5?7shIRl+ei%ZxnA;;Gga [O&/<(ӭƤ 0`lF^^su{{Z;󲶎eζrn~CA Qͧ[EE}cuyFmX8Cxdwq0vmcA,3c* Ɵ?S}7SFǎ**GYY0GgM±uykYǭŌ1ͼ,c)arPH~AZiX?ƶwհa}k>e~}^-e=fqh2T a-PaY Ao{ae)=@TwtS]D=1=f.3z`fD+/ykS"z( X39 m ~mk" hNQCD L}=ؾ q:^8Az(>D ţsˀqZ_sԦPc2E1(qor`qۦJsڤ8U &T0x@ݩ E=`$vYiq9^Xz(NAUs 2.sw4ei!F5G9lGih(CKRPyA G.z4̓-RoC]E4izʀh̊q[}uszD r!ͺ7L}@DOu6 : >D"DDDDDDDS+ l^k,oD 0J|d6?7kZgnZZ y0wo3g~&Hu ~u e֦WW][kL6鷬~[y@$IߺsZ~S`;AbmYn_Y@Tfg^[VXyk3D6sgQ\E:SӇy@-#3ϽM Q`jjFA bDD#`=:@lw"Y6qrNʱq<">.g룩&i rztb}anۤ#U뗿8w$"bl~n[46[:Fulr̃,aq#]naQ}ȱ˱zƸwu%<1l!$z`{]^È{mG] >k;{y1xm~'m.<}=BLa{;^v{fA YsTwQd^#Neʱ;z.i׵ c Ӥg|wv2rTa,uۂZ2fAn qrݷQkݠ߉:OA?荡;EG2 Pu޼IiGle>䋤_CIGle>tȶmSEO:9v9v9v90zz$zD"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD{kP``/v h6PA ŅH,R.lӺ4NBiZ=ʇpiag)$ A#@AƲ}f>-yw>3#@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "@ "or^ytVe`:(U{@ D@ LZ}g5f_$I""ң4vo7>Ϗv9W棐/DDD J+~ DbŸ|r\p!O"(w~s'kk}]fzt})"LDrsssXzsQ }/\#G$q.w.""bb+?h|$IQDDI.j""Xzs%V'/]t{iϟ^ϝ۫pu!fF2nEDE$3I( b,=#r'a$$G?'W^ ~K_@0TlԹvZ,^ Cgcei%vv#%$1wq.bGs=/w+y""W\rs>?w~sg΍QG:fgg">L<վ\~ Wb[{ZPIP.$֖F/7Q0;Y_m)vq- ~pg7V$ff\r.\?y+"b}c=30 /DDGzG4"HRKC1)FV@tΡً_ۿK(J4"҃ը~[Qx>χG\ʳJ|vo=DEDQ٩4l3, QVBi9 03k]0qk۳ gǟW}{J7OQR!NNZ>g=wjt}]f?3i',uv'!HCQyߏ͓1DgՎNq ?8rYԨ٭F'fs;]ױEaudh6zsͺ /(:7{y0@Ur:=Qjs;6[m_PԬ%mfDnh^YZYF]7k~Q>uۋѰY,Fs4TO"!IDoFY gq N=DhJ͞w?=?lM''V:-Kl_V㏺l,ilgj@,e?kgmw|Mo506|;KjY?;yf?{\YjhZ~SAOw0l>c60J9%` d *Ln*e(U @4wq.n[iƫm)_|Tv*Ӆ(?8WKBMY5WW==>Tv*QڸJ'uS=]|WŵTzwyw?uX~}飴ΛAJ~AgP_IENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/img8.png000066400000000000000000000762671304100213300256440ustar00rootroot00000000000000PNG  IHDRDZN pHYs ,1tIME3t\ IDATxohf2k \271tL;nLBrCmSx6vu]dt讷 .ܥ.?0r7B!CaV[9rY[bz_U5XHr<6<`{Ϸiڲ,4 n =wf-}L@1. 4f҄do ]hЦMiaMЧICX ۾3G)0 l4ީt\u}uHߢH;zhSS O S?lk~`]bH M޵nTfIPq`e+ E)J6mڣn;> i 9:rs~{x*XPPu44C?ȣt\0AeV?^ҸP_Q=IfA<.YAnAך\Dayq#0f!0mƾ^i찭?5:Yl.pVXe24̀ײОt.Pd7N'ر؏-veWEu4IxЦM{tgş_tv6Ѳ՞aY.t%+^+rnCO?{g͵4]Jd1@yӯ@ŷq7F߂+z8'$V 3}kW5lݾ!IߟAa,;偂?-r"& sft~V sö씵,9ځ]4;q*^Пh)Cʁ=SMڑ9B%eW?:-eϫFr^bV̟YԚ| v!$T^^Ʀ\P:^B}{_\  9@yc:G)TMz2wk(iɗz듏˯MoLzj K0 `Ksjn~Mqr-|(Pi,~m՗K%oPfxޭa//+1\|g R9!m1(7ŗnyW4kphm1d==f[="t];ǻ⸀bNƓ&{FP-vIbGM3JrK,q>qb(ɢy|eW+iU޶KC?S ˪gz?k bz]5`Jh[RaT0_G<ߝF a~n?EKX|}Wa<ΖP>!as{[wt~ݩA<_y/c3?\`c5,}g ƀ7(ڧ5,2(p$BŞozA{:7˨cJ'<00Y65Za)/|Sμ*CyXC^6 S gP~={gsp@fkn.j6 M7[U暠=޲wM8.]ݴ[bV-1V[v$xAmmSٓ6ln[>I6Q {}pbPX?rd:M5㎋?RUv!Ԏa'XBOH>e ߞg򻗱%\ Jآ>^B( 4 ̽1gP:Y( {ޫTx::_3{_Q(A::cs]F4ɗ1ܿ/JʇOQ( ?/C74\9EcU/W[ې&z.pz'΢t`:MyGK[[[(4_/lo.@{WPn_r_?9Fw]e0GzKcmMRm/bvq\:d u ]Qx>.k\򉂹fH?G\H}`]w[v۞S%(iӦ=vT#CV`?<}@a l \jל1dkO+ P:jwWhy~w ~wpmq0tT*Ov KBkr*'K=uWK`fz+ly| S'0r Zk݇(򇛨WG|sTkTlsc߀,Jea%5O` (t{AL+?jwkP*h4}3bTSe!ZB#~Lqڱǜ5$I,x]o=f3G,n tEhv|%CߵbW h<ҡrڴi5BV !kX V]+ ;khӦ=ZJwgEv 7ry"f_u8q[`" ˾ڎuݳj'fEb+êr*ZVQ,V"6(1ZrkMMHfX~m ';5H/ hWy'[,̈́K !+1&p᧋0rLuSR[~wY5a1տ|jfn|zX n@**((| ?DqDKd9Bjc6њi3nO4˴ Mڮ>Zk0:fEa[k1C58j/ZK윴ٕ{`7FMHN7C"'*y =jp~e0{^Z>)⸌BA~RyqZjX8W…Kh4t|Qzճmsq(׿X<}J{u]V:t]kծeQDxbFQ@OXQ*BōJpv ^xzZ_v`}|]4\ql0^@.jnT7q)iC[߂c_'z}LɄjBj9L{ځ=.̑$ݙ&5;b]bGݞ6;a_,Bmhi2RjCLJh)%(>U27E)NFhӦ=rAo}3gXk+#h}p5BS8sO@NLŔQ tzRa e4T4CmW $ q-LZh xYQXij߀+@:VEH=uLi,duʼn#l} (-_;9Եb$@gd IЍӅq ƞa (C?Ksu>ڲ-#C,n uٺEho/>QD@"Ԇ~m744ZӕG)sAuۮ*c瑊F6mڣf:nz7"VX:fv6c0k3,lIh X~߼W~MC"'PPQ,AFQ(Hxǯ@{Zԡs>aÍ3ބB((!dv}397&=uOIr BE1WDLEy+"(!4CZùAF(LIRqʌBN3 A.m!2 5!hQG!܍<"SRj~)3B!$ Uo]t6xfR&BYu=g2 BCAD! "f!BFW(!2PB!Y@! "B! "B! "B! "B!Qs[!B! Z"BpQA9G!`:JKǸB!#!B(B "Bqr̂1`=f gNW!Dk|y o,0-n5! }R \1 OT|œ7GedLNLbN2",dyPM(P}+]Fh /{W"C7 r"PjodYN!,f_ŕ@s@꺎oc\_,OmC5 1){$PG__D4Q PBz WBXl˸2EڭxEg&Xkwk}~u 3V߹҉. À h{Z|.ܪr"d !dCtJVBQs1[cWwg r_.HP:u~RQ[y{ @1`@sz5|~ = R= /?mxYoL-Y5Hއ/װuHSBy\DN`"VqTUŵ]}]UBR {4x o-G}ĭ̞-BUU̿v&u"MCP4+~ 4]*5 ,CncS C ˟) S%MO+Wyh 8Kyng`9?`ۨݩaGӸ0 ̽>:X,J`_]jI8*D!+F7y}z^ǥw,-wXOpok;kLI,*}CBW<LE nW~%d|pSEyz o-P(@xRq}@ PT{> =V}@Iu,}]WX{ sy,kzŽ-LC!* !5!_۸755{=_O_(EaH xڄ1szYtycV7zmLP(ZBe9*DN@mX2ΟV?&1ݥLw ԶulCC~$!ChfU=ΣCBHQ>V,bt]HE:?Ouc):= bLKRFR찾Ey`>F0 MmXĕ?|jgg!˲ɉ!c-;19S3!㓯T`O 6l<\s}%ce̞5X_̩,כ^bFOT\#D2҂#:nqdFM1fyyt݀'7Tlnա*JkErbyIz5~[Dob/+Оhظ4Yqx0Bd*IoTmTO ggPe|so//.X ry̿zk9BiZQBP "`ʬ> F0G2}taz=F{ڄ|%mW8b# r`B_pqJ*q&x\we_G[#AFOV¿v~/Q~ 1&A㟦3 ꮂ{)u@P@Qih @ٓj\B7qoRAmރqj 8ZL(nV Wg ?&h6tq7WQ} ':WGDN z1 Q7!de>V1d#Rh<ۭ=#G:;6{mheU,V:4MCwaw.BӛX_aXxk&P/CU4d*ӠK6jW☆ Ua@ *P̋esX))1\lC ȅԇ rͳk_Op7`ۋ\-;#D9v WBAgu-WdX:4oѽrbvzXJE ,ͭ>J?ȢTk4B$<78ܹjB.m 3'e̜4^] eÀ6m6~߽取qo{x{ۨ IDATeuko-UϵLv5}! "cwm 1ov6_VPU7UQ,J^T(4Ea 07oNɲi\>eKj}_+zJFi|Ҹt0"o%yoP|bMW6˙k㓸oVx-VX|ׇs_=z*! R*zw2i|i: }x+`~jNJ)vPyyd!(t!I E=ӦOčP?(f@rޘ0P߾6Pz l} m_"'׾\v(~BEmmdϑ }%n<?zG l|o^ߚ8h)^B \b@QΌ̞#Mȏ (ߛMV2:  B :xݮ uƸ;qҡ_.~3od!bB!PB!PB!6ko3!2ss!B! [ێsʌB!#!B(Bu||܊W) ?}2AT>QF5VTg/?zH7?}WSftLQ{ +0SPCɳ^dL!DB!DB!DB!DB!DB!L.@^~_YE^dOߴ :^+'e091y距;xsH<9qQ2 qƨVJHfSC+]~Wx*i.=NqoOSzőGs yH1DFڽkoW:z,NǼ~pݥq8[xKԁa*ÿ/ȱ^6{}$l~|^iGa}WA_˯'_&d#Dk"۝Rԉw +QM])Cցak4Hۈ׹cmc??}8sQ^=KwCԾ "î~=?#-Q 7콽y 6q5UdėD9BW[2νڅ߸ABbDNN*bv*(wWozPVP{MKKH*➽(.%?3(yD$ӂ(gN+ GĐpچi:ԖPHb)izMEsc|XǗ_2#i s!k85H? :uJ5:28Y*=^HMD"A' IHU ڪiޞ} ''`͋(@V `ُ9 ;DQ#Pܫ%~L)5DIQH2#igϞ=CX]Ce((s*!67pkQX߀ o-B&ۋؼXb;l(/qݳ^+ܺB.!cBHzzBADB:cBXOBNB!Y@! "B! "B!wĒ!ݾC !PBu̝ShdX{2ՠ4~E&7\K2nPdֺqen:' vuADH:P.?'11y!F6>*~ѠY)yAHZa;j~#L0QZϷs~p[6\2$.z5b~GgWB^s]ou! ~S1 !$]p "B!A$!BA, B!B!BhȢꍯ6Rٳ#wz`"d\奁'6'wz`"dD>D|ҷsrIz-˗~t5DB bB!dȗeY>iǭ%noS8]v/S[goXω;6K pA[prxa`pbmϲ(BF@ Vvnƨoȅm ӄPrz{a'(zd#.{Ptip_۰ 8QǰoGSR>aDu'L; BdNB!Y@! "B!'5D+d6w²g3 Kٌr ˞ϸBR f΢ r ˞ϸB5DB bB!dԉll:sBAmAtuI ;7Ń BH"B!P1 !BAD!BAD!BAD!BAD!BAD!BAD!BAD!BA4&'&c7{%]!!7ONLbN3,ƃ,!{; ? !vR9eyxs{z+LQCma'mS^mma #DV6JdAr;j)>g= !Kom?!.:7Gӯ r}t; 혐us\:cVP1+_z7!IYjlE.`o!0,~{ׄ {od~? WD!¬e r^VdYCo}p^a[%$~(rA4ޙ)x#NN* #Fڠ BHI\Y o99~S uQ0~yl ܺB!DB!PeUB!#/!B(!B(!B(!B"!m|=;tB!Dr闗Ra??"B(bBe+}aM!2*p !B(Bu۽,46^@ظ'. ! ^"j{K Xܹss=tv&鄴a+<{sND|$DV!0LdM89Zq;5e꒯7(^߽ĐS% ~'-%d`hi 4 $'GEy !^?FZm/Q~T~F!3~<'VQܦ҄[ۨ4~: Fp)ğr3?WGA42jT^k/LAv=[7Dy ,g=oPeaX 6oy~(A,Av: B16J2Y {Α_c=];oxkFR)4M,˝cY vN#=\>aha;^>*Q^ BX%8FA:\ t%>%6I˺븓f.B RpAmV׽~q7LY y5D+QZT^=&-AkqO S~n^Bhx"BHXL`8!"sspLX>dZ|28" (// e&k! =;;ìÚ.B!djB!P1 !BAD!BAD!BAD!2ÌBHl|ؽf2)!t79 "B!$[+C}5DB bB!BRĤ zޠҔD$l7A>k G!?۶^%a9ݯ%{)=A q?=^B1:A>k(!$!!!8\lczQ)lE]P&)$GgZ4EKADI(Qz$G0S:ZK$BH&Y7Ѱ ^yV'# !$ ,4vq0^ƲLr,llDV G!A) _>n)^[&^׹^ldee$HS^Da~ AO0{Ô]sZ$PмSQˠ!.~(!! "f!h5DBHB|L "BFKL "BFٳ?3#R B!Y@!Q')[MB!d%&!~2hB7!k!BA, B!B!B!B!B!B!B!BȒԍ''&;όɉTăҿOwBQ# ·Ώl˯3(2ss0Nϩtun=J'>n5Q+lM+!ډ_Gg'$^:n^= =0=ȼ ۋ&d~O?!DFzya^48e5<$dPi~&9O [jָ10{3]z8W%1B"DtuXA=Vzv'^haz1m¶8F& ¼qOH6P(HdTI! lMDQp~k UTSUldE.qD~FG?{na8- z,2Q"B-7^v;c0:A9BnA! "f!B(H*5!l "B! "B! "B! "B! "B!ˇ7`Ylfi~Dp闗X՞PKWr) B臆NyX|a?}z;[Ǫ?u^oB?41삇B?Fr,de>.av,˝ǽWpOV!e 3u4ր}eH? ֿ@u:de۬2 [/cQ߸ v*0' qg\N?2Ag!s*߰O|/Q/H{]\F}M"+XA9K%&&P,b~>[=87 y,#EaDZïD~w~UC)Dg8;[?&:Fƾ!=4[CXxug^9/,O h,'B臆Q״ ᷉AoH>Xe_)0 "~ EFBH(.<mWxrW|`a8qo( g>1 0@>qÐ#?`'V.m>iʬ r^߀z#~GHT|tu_{C7ma@!|N2x_fPEqw P<]MCg]&JDŵW3w%(H~k=Pk|SSҝ]eI@ cE{ JNNURgiz}=MixCe90~(AW<$j:.km;/x',BFq !&W]piwL^[x! qhiO*qn~Ԧ^i{,΍_Ô-" Ap%η;NA8vиKrָ6* {]ԛƱ"_A)닥a8!$66o.'iқFсNSg>cdFvtNl!DpKrаaiRniJ_AVP= tmt#+>' tkQlk_ Y[NH4cQm8+~7p&nTQ$(H9 7+LsXɎ?ıqhTƽi&Q~aXeHo~, BȨEՄ#Ygz'BAD!FV@cdNB!dIl潾Dm=kظ&qK4ꍅM_OY.c$&y!!?& yHc1@bL c2 1|.m/{8ްIzaϤK:akQ??w{i8>OM{EJ_U\Bi_/q DNi Dou" `2~(k}A[<æ!Mi#.iʗa(#G @30X!#@^"o:}뜓vwcM?~G %dM_iA_:68PQ6-02_ӸDRaV<5\~"h uQOzӰaƱ mfiR_K IDAT~hHF7O턍g4 2IonSlA"4WϬ*G7 `̡k!߭" La쭥d(0lEIt4.A!#~h ("Cx^Av!ri*_ثO~_0ʗwu䑇_EWv5_[H{Ӭ0 fXG;DBmOGh3R…wU\|O _,(by!HAk|,mo]g PwӚ2 }nݤ5d^1εdzG/f#_KcNj]Ƈ2VnQs05 & $ҘزrzwFLoܤ5l96l^gC)! FuInO$b(n) ip44pZT "BHC5!eD}]ⒶlY4\c-(6uqbs$7:vؤŵlMa֥8B!&Au87݌j~6~%"g~`?$)DqlxE},<T|받&Y5(M784lQ]tDQu,dI8~, BȨ5D"+nHVҐtű, "@dea.(׉86%)3B!P%qY;8﵄t6<_8$%UFu,˟8'BȐ " G !33l E\Kc^/ʍXQ_Ϙ $þ*!^̿ZEeZ]AN-S6fa6~2̛"?~(##Da7i cN{qI:̠3Y`xΞ"*26H%%b#u뇢X3cU OQ8Kct~&QY&~(Sfa7i↱AG((ŒNEiL_~5fNMRԧwUVcЬl8G֗t!J|sWcA6cCθޤ5 \g a,{gqaCKV6JfqCUb6Qg86~y]қ/~(eȭQ\G\_Z6iMz~8ʸ@Nh 9h6F 8җiڄ6M%uqTPkŃؐ3H\al4v(=vXgZfA:=qo6*~(7NS\XldžQ86{~q/jl:IoX9gƝiڨ4+%+uiPb{ޠ*ĹEqJ<f 7q\9(Ku)`hvs mQl~D~y5FiV BFR͞ Qg!C)[wB!Y@! "B!'5D.!~2+ !D\N d!"BB!DB!DB!DB!DB!DB!DB!DB!#K.NNLmNkoB!d`IQB!drlrb7/=!BA4p1s.`~C{>}&,B! 2 0eQNqB [y -1.B!' K꾄Ux( +MBIFb(8yzWz! #ȥ-qE#i.^i15B!MI|3e)!҆[wB!Y@! "B! "B! "B! "B! "B!&qxB!Xwr,&B!MGkhe>!2\yJda-]]uXGc=>u~ #ޘDz9a{=E1u/#`-4/>'AoDK ;(M8qN Kb]v >,萖:2!kwm$N5>EÒd4GQO9a]]έ\T/,]]NAY g9hqpo~8SuOvw i~Fѿ8ʽ}#DBm;~^fBIrpB! ַ8BD!'eҞ47BO"#Di+$$= "i%o 9ri@eڴiҞ. /$z]q+,ڴ3` #={ Fcv |[X9tu Gd!Bmr[]xissm|G|W~ UB!.8:D!DB!i'Eղ,;_/rS^{a -n~Ä4gce'?,~ۘW=.?q2^[Ϣȇ~eslQN[9$ዒG>mhQ4P*p9A$o~9q1tęQGmj=-8A뼰^FNQ/-吤oO#$-Ґi[T,c!8 +wJyA;,吶{d9pAwi+a6l7 NSWЭc3kaD]/G]'YI'L l Ii r~YNSa~g89n}0cQ?þ&9qqeq8s~˵W=}?:zDD='y'_ռ7/|z4a9E:y9.h}bOaB^\:ieΪ|q^eO0{J9/M_>^O*\s\ރ_m>˲'bDPX&(6uQi*7=emå=mڸT7MU֣NhR%PїMٹu/HM1=2ټmW(fal2U7\7[uN >hkQ93Ur, u*gS3)TkR~u`υ(Y$UiיZmF>|/~>qt;QjOQ(L1VٹeHUT+.U|]L!߾*NS:&Bc׳6pH2Ş1 |N;*}|,xuڪ~a> `,Ck{ tlυHaFY??x_CT,741MeP61b=]^,;8^(BtP~i\ɇbe>st_՞6C,uTUG\5Vl+!TV('/]3>U,,/IAj6}]v]*9.\\o*|ouyqWWc|دqAiTV~u`O]QwNMIgn8mϲ} Q\7fv*ty}Ȳ{8m/Br}%N>" QYĢcNv*tyYb܊y7qr}u>c[R:+T;km_)f|Wyniz6+כ"i&:x\'>2UqQ7[esqϰu 0 D"A @ݗ櫻r>*_'79emѴTi?BRNڸ.*[nCݣzKP9OgWu!qF6y[gYL c ȑ*vn:yT]i*7=emå=mGh1T3uČz*`yeK7ٺ\Iv|lƙƖ.өGfUJg9)#?O¢*(6ٷ3CDzP~m\O!ND P])?tۺ}xɲxK5k#Oqh9JԨggk~џ{ G=cS*/>P٣1BP~b|JLflLVg5lrcJ=c)ܷL H?ٷ_וk.>0JYZֵ=]$\é{g?O|ZT]8v*ty}VWȲűbAy(g|aXba :SSRWKUzwV4UJhP]LOy)ĂQVe:2IDATQq۾g;F}m W.-|Ϋ(jV<"TϚ8*۲M~9[ [w@AA @ D"t/zuYGu/Wݶ&l:=mGH$IEe z{TRyiz*t⳾Oyϲlǐ Q@1TtbMV9.h.i?BYV'flUS%hT+^޷{m,1$qtYU*uN )PNUսl?3үBr}?REY"Jֵ=mKeGǫM]Y+F,~3GiUF<|6Y%e3` (xs=RyTUux)m-xdbvȷ_cWW-F(v=a !SXsĀO1-fbXgʵU|la@tꢰݶ_R.&1zN<|ֽmTYq;tjQuݳT_dSC!:}Z]!~b}V>c[R}ѹ5De.ֿ=Mm mXbO׻o覙bTLDK7Җgz }ǘr}.%Υ>6vRUu@Wm(U޺K㲍!E4yEmӊUDP2Y10T|*gc7 [wA @ D"@_u1˟W݋l`ݶ&l:=mGI"Mtxw?,w^^FݞguĻ&ҽ\RWڈ ϲlǢW9Bձbiym)&]UeyײU+_&XWXj3؎QXeydV)tJmB;BCvu:eYtWI8Br}?XRF %B}_s8KeYm(tӚČyw|g3n>*Xz=Ry{] b#b^eퟡ|?{+qG \.`~3Hb 1XʵU~}DEu1YLy~}g"_Ő۳?*~ﻏ >G3~Hf Q3T_zcSC!:qK }*xkf:,%u\U{QŞ.]5oե_W^Q"Ƽ}3U,216YATgdim_%lsi޺9㲍!)"iH* a&o#[\1U?sOWa="@a@ D"A[nݡUY)F:y9.h}bO;uHө"{=-w^^Gݞgu;{et{VY8؂ D:yT+oJMqFpiO[Z˿Hӭ+Jq]+_&aWm,q&$ȬRL:'v썭t˲Ʈ.Up5k[ q* Q£鯇hO3EEaec,mw,+?2[džF%e3"0P3({ܥKP#1Gfżγ9;BuSWb2c !b"ǘ=cT)|,xuڪJ>0 <!g1ў1o|C6fnlmϲ̢sՐ_ dSC!:iqK ׶|*X鋤f:5%]U{QŞ..Қfus֢m_1r}7%>~vZu94oW!MqƐ}D4]$Mup6TUum1-};A A @ D"xݺ}U/|lr}Y;>tJY:1϶hk[='uUKlHK]C9k]u[LeUUiU02r/;RGLۓB[m+[Y1T:¦*NzM[IUim/_+1F*#XhN<2 2v]]'aaa.Pln_\o}WP>oܮŹXʈ9" 9c__6Yo+z)lgyTg\A,I>1c՛%R]Sokm]xJQU1sbCULȲHs. uxv%il rO%6庬k[wbh!Z\Q\Cd3KB;-2/NִB5꾎}!> !%6>G3tb QٳҶޗW~b6K]LuKlyewm۵8=Ǧ Q.(+;ߕV~b6kb*iy6 b>*imk}'nPM5}˲BݻZўcSoQm}=P';m*;nkC:(:mz" }6[s]get5"vEף3D吝οu/b0{0LpKDD.|낗0C^YML,!, 쐈]mW&חM稔S[lvusBPXUڸ40o{^c䡳Y*}ǞuU>>esOe#1 1:\V$,S*c*u4)˴=)զ?u[CE,lhѭZ)˿TߔGUu_=||?ʈ%΅#SDme}bkkh%9SƬtg[v-RF8.8!"?~,{{{O+pND:5}Irͣsή;?{G6mͷ6ĉ""sq}ݏV-?+ꑢ*W7DDdtg;_tlMp&Oߤ@D$asWpՍG[D;wuH '3D%qX;|ҋUNG_8cjcWb1|` b"(:i1mغ?.a-&.D59iH@:,əI焑WA@V!FDw?{| 72[·.{~- t;101@F!B"Ea*adxOPNzxN !/+w秴C.@ } F L"A @@W]5  D@5Lmfw`8+d""A.~'g2yGcsݕCƏߐe59}_eǙwdmy2}K/ Dg|Jdxv"r7Km z xiCέh4p(''EDduuU&_\Q4b.qNá"" rP.tEwG?=AVz_'kg׎ `V(=t5kryYyfEDDD2e<K 2djܞ'"b,~~pͻ-޹sfiQW⅋rbhHFFȕKWC2<53߽,_[5.#5|24çF+OjA`ǏĉήW{\]lʊ\E=3a:88V]9y=rO~pfh(ӹP\Ѣ܁J7Qմ_Soɝc}]N~C>zom[M{qו6cgSCYY]Ó2 _} 28|KDd֖Ndy9$?zpL"jDŎc@19:5W_pthzk*A.ꂜ?w^\_]Y=3y*s.%D"uO@$"@"7~~CDDnt[nߺ-omٱ CytH@dww.ssr@с<|?<$gNJ."wˢj1v~+w>#~&y^9Z<5LGXߑ~}E廲?ݟʫ'7{3>;5]E.~2%"+WѱϹg&7n5Y9{zP4 fkU=vZL?\E<7h${ړ;ߑ{gppRdP~(=D`, MZbKDdugV_G"A5fI>/gFm+(syt %3ɾd~eƢh3FLw>#㯎ljڒLvݏʽ{ XEu;ޑ tuYyvELeggD`MAޝZ/+e zDD=ڢ&[m5h̰$$ kA A @?6n`&g&|<2caə VLY |tf"1π>k]AA|}U!&A䜵k?ڪ__Pua_u! |sG+-tݺ&m-;'q=_)4#٢U2g9J>]mєE{4Ӫvkk:5B1ӶOmU5 b`Uo|\-X[5/nt{ulƟs=:MXY Qgu-kbR*F>]v;]4uYЕőuLgvcWm gJ~$Cn}\rֵ3 zM`#3İ^{uKEg /B$6 C%g"\ׯ/93c<Vf}K>>3D3ٸ%B` @ў!`= ڸ!3Gf  9JkXZ}:<2&.}6Np4fצ+}+װ$$h4j<"=>=p^X,szx<*E>.Yg=uR店!Tbᄡ M&gzէhrv,Oݫ8uYU֧m6cX>L4C7݇n/ܛtab$ ԅ >d/Yh A #3fFHciՑ~:;͌5uB!c6^Ξ?1̄B0r\{Hvlb镥ckll1FF!'49o$UV B!d1ߕW!ǝIyQjOU5Yhl(DĮa@Ғ$f0t.6hh5LW>-Z.yuQQSSSSCWFGd!/.KuccJ 0:(Ap%ȧÓ56~`hyMҬuhh~Ѕ3Ga¬ } "*#5L"TA}ZiE3`x̒KEjjjj4ug|!yy7YG1}Z[op9 ((WpsrvBE`r) 4 i/rpafGi}aAm蘗7ke_> _…/s톦.~Ͱ._Ajm @A/@q?na3خP >]F酛ceܻA(@>)w ŶN~mPo4ѬÚ[fIl.L7'5L0'ǡѲ_uܳq(>-uQE ըy$(A4l: hO#8a(j^^uKq̑g6>h0sFKsK;i=c] AQ,yX|iA>5B^<<6IʾwߞC>oSY ;X6pdgQ<%9@څ~`e4_/(;ہ8YDaX4SqR~0pۉڑ?>3h9 jУ'5?>m4$ 0MY`zCOuaRģVH{RCtz,A huf|Z͓%uߣ]F( =}`zT@UNCVH&+sSeQS^(.lewklmJłeJ@0Mꁕ1ho~%UA<5555uy~8k0GMyE0G҃y6.z)4;wZ\}M.+hllZ-9w! 0Luai`B?\GUœX2uuXR@>aB5ws32 #?)0 TU̼MӠ( *q2B2 ΃84}12L D L¤ؤUY"-|J7P ïl]ْe(IyC0gťŮj7r7.p ]sd lفi5B 'Lk=pٶM<ޞllVPno.| 4Q*1;3"ϪXXouor$yGZkgdHf}ѓ~],HyцGz|~;c53A]͒ϴЪczߣKyh;Rjy\BMMMMkb兘yuo^QGƨy! "L@!,X5bBY{y.'bv&5\,.H*EAھ a£ҁ6q_Wpyo^B0A:j?&mȯʼnqzU}q{Ҹm&푥m${$yJ.+ӝNHY)Yj H8U͑4ke_su\8}.l[l/ t 5555uz\826? YW0|끕v -Nyd]ʱMRKs˥eN{MyS.j+WA{A>sTSPzV+F@ᄄJxe̳ܘi:I0eڧ| gJ|zw sny *ca2 Ruxk)˯G.U/b 1jG5wAlsdk ji6v&%臺gMnxt^~' P ݳO5k]3` st`0YI[p9%[4h~sDMMMMnύw4GBN'Xxy!t}QQ6y#'ef, ug#)¤|Z`&v[Tv!DWE_7hOPl [w1w v 7} W^:`yhb9)a`%K; cE4\z *O~WEr s;ۍi3?&vD{͒g8zRFڝV< %F@̏0Cv٦L{TMuтT*Xk\-CQZӡ gJ2}ZE4jjjj$5Ul[s?0槛hek~v/j744w|c^]xoƅ/ RLs$TVʻ(F#(XZmBh{t~z;[ gv^~h4 ѧl]mdhQptJלg ,zYc#Mkg&jjjjΎU 槛~Suy?VnʱFhFi5>4fM#΋0 V Yȣ}}8y1Gyk5Gy(^)rG#a?rd33S35555uw^^$M5$)"BA_~YdT&_T^{ҭXYa!Bel|H^(k^\}#HɹWFiZ@!S+Kv^B!?4HB!rQv|"BP0|2 V\eD !2TV3H`J!d>n3eFB!ŭJ0]k+X`i=i҃ys\zIc\7.3.]¸DkGq$ Q9.qKNq-f/ = 58 t\Fƥ>QKq^A"B 0Sl H6.|7 ¸dzMN˸dF2|0Ÿk G#ƅMڭ;9Aq 0?bgl\:¸d}I6.ρ6H^ced\Ҏ˰œqW\y:ɸd^+IyӃgV^[qu3Rv\=M]Źߣ^_7f`[b;b;T_>ߓP4XWFiϷ* 7iB!d-FV٩`%} /^kB!Ah!BtH{#BzDȠޚ22$B>,n$1ͻ-38kq/.|ayNtq4{X;]_?Q4Qr[8{:ة'<[>7{iqϨFM 7]n :ϰ4y:,aia{ QcMZԲF<,,ƳgȻ-Ӿ8qKiLus▥=Q7u0n9 ؜&FU?ɲ=.,fqӆ nGYꗆᾳ~&[~/~>Aj?jpaRe+Kر.6Kz +SXYۥ(1s ԗ$cvLO1#PӸƭmlP4QrҎ:=nYxnZw{[r&N=.whWrqv\X hQ61 ^_(xf).iRFԧ$ﲤQ]T'}NAȬS@n1laWE_q\lM +KR0xzvSƬv^Q\r\'ݎ/-g)}keG{OyI/?{kB!-?+uUUo[uGBwne[moU?jYVÎiWڝ۴;47fa ;_QnM8it{[=n<ݎ/юz)gvo[~^~>^6]]?8o|W>f$h:2J3%Wo}7WaId9&md9Gֱ҄*NK,>h=\x!B B!-6R3MJH#",'9+,'rfݖ#HB!r>[\L^ r:2}i3DϤ9((kO bVxߎH{r Akwu;;Qd F\<RϣAq$vpfCكKG J]/4d R;δQ7A'Q(ץQ.gyJI14(Ν#If]-602 7W{}E9kWip~>(24ѠKKI I?QRu?QZ'uI@WwhB%Bmvk-6v@~MA"B <B!euFB!4H^V\e!B/V|B! 3\M! O2RB ..t܇#HB~o_Wgs͡2!et>tNSiz9wx ! -|e(uc|K4B\=$7KP*!BHƵ8UL(dh!2F)A1"B0 ;GB!0Jub6U"5B!d?bM9'qFBpDB!lz2H4JBFcAd!B%j%B!dX$[$eR1H4JB:DvK(ANmB!Dx2<)VB!?F..۲ !22<B! B! B! B! B!$Gز7B! eل B!$ !Bt[lũb& QV=pyNr?8BAS.Ɏk$@7Ba?a?5HB!4HB!4HB!4HB!K3ރ(MrgJb,BIr?}n8ZtbhAB!7Q[ =k޳jU_+SL|;ݾwW!dH_7})L~<rY `Yt/Oi0oǘF/5tS&B$s`f$}8ā6H6M 'nZ}ht!nyξL@8%;ұ$F[ʠSNBFQ5F=o< 6Ȭ\_Ad!$z3f)x-qqF:w7<na;D~W^R?897G!F4$AxVqQb入cA^A*P<-;o}و>8GZ:;,gؐ`7OrZ]CFvNq A6=Im۰~?&)n9ݳ(ͽ|ϯ4تT'.bNsv+kW-,! O~S>8u  jUxB!d0A9"B RzDq%B !B !BHEw?;<&~qF;yZnB!Ra B܄B3Nk!BDA*N-B! 9,\\@a"BH)6B!$B!pZNS2\-/%FB!RP*iz1v(grt~owB!wz:} ʟ_\ԃAy@45B!N\iP*= SB! qFx7{>oߛD!typ6F>_4f:CN!?ňIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/magnify.png000066400000000000000000000014311304100213300264070ustar00rootroot00000000000000PNG  IHDR pHYs  tIME&  gYIDATxڭ?K+MlB"k"V"iVvBS !$kE aw3syћW- <3g/CL#+2Mp8 x}}lC]U*X[[ò,Bt:t:#z:Rd2aB!ͱAn+V^M&affp8)%DT*E*u]rQ `eeEmmma6ibYapzzJVq"XSKrmJH$%Bg@ihFPVFԒ àX,FBprrimD|>O\m{( Ou||9RJvvvX__'8J那ш^G:}uK...888^zyCMYy"ȇ3h&py2Xf-x%k lV " 2IENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/open_dna_20.png000066400000000000000000000017361304100213300270510ustar00rootroot00000000000000PNG  IHDR pHYs  tIMEp|}IDATxՔoTU?~ϛjKjM_6hh`\J`҅&]X 1.hb@jfigޛw]Jܜܜz(OeyD(y'N) w$/D t`!bQ;0(~S+]=kx)H#@U*TA:A~yHnrx* uQwLΓJΓJ%1 LW o}pCVn'))5K@֝U; l @E:sEMz:;JsCo>^DW1z?|xvEXXH8&hߗ@MJRQi16Lφ|ʥۯi]>QgEG ݼB28nqq]q~Rst;0#:#Ֆ榸;4k[ذ 4c6&4Lӂ}OPz'r本++hE%HPTl ѴU ~A}5w=}˫ 5UlUc^(.^1T@z0d3 m\1]VF60.W>#&{N/(\ڒBd3>f94 ݋< FGӯ  (**yBI"Nc!,2/"cq$dN*0;cZ\f5$bia6a4)HS=5bAee9D <>vG,;NMM-"&SPDʿBSl76KWz8vIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/preferences_20.png000066400000000000000000000014531304100213300275630ustar00rootroot00000000000000PNG  IHDR pHYs  tIME'$E mIDATxڵS[Ha~M]"irCJd]H`]a+ѕFVVvU]EC%j,Ok53KPI8Xno7X:}yy_c(!!/I׮^MT߈nٜL5dBS4##zh~ia>(5x DhINRRUUUA,L&7BU%;T*h4s:ouIDdhllbJB}ύ6hm0/DFi"d@"@T"pU:;PS{ott(++[JcIxra{Ty)%''˲$>,2@~tSYRT']EEˉȢK~VqR)Reٓ ]=ݧu.Oiii`gtEJv]x b{ٞhd D=(bccߗ[RAlQ1,V'BĘs:8Tw25;1mcf'&/H9=%uJ@ x?0IKNI0#ƇIb3eX,mm,۶3S}ȿ;drxNIENDB`perlprimer-02c59f31dd3637fefef865edb6dff779d077dce7/tutorial_files/sflogo.png000066400000000000000000000117201304100213300262500ustar00rootroot00000000000000PNG  IHDR>}gAMA  cHRMnt$m_l<XGO[IDATxb` Q0###@1 FH2aa~c @;~ F+Y;wlNNNiiiULW20``ggfPee`bb`QB`@޽}6BFFFJJJUUnPA$ =J FQPQQͅsܸ{a+f ZN 6 <u9<}[tuu:0m߾9u}ӧ.0 2j?,Y͐><3\ dT"'`聮 01K$10h_`2 ozun@eCσ4@!8:X`6 N:yT@V,K"/oaIO<怩5jLSIm@YO}cp+@n#0C `}mΤ[S B+  ^O9QF@,@dѐ2HF9t?4 MyʼnFilEE`5`EL90t@U<_K{ ZN@ OB8ƃ, "vF-֔v" 3e@T,>څ\c8d)NxAב дC2Zq8YCF-E}`{_^̪gQQ2^9w};x#;d-Ze!d6ӝ;*d=%d! 4dq3Asނ[p*F303!!Qq"Oy"RZ1ujDZSZ07 _\5gV[$հ*d"wXL&Ē$!Ϻs\-b`z P|^RqPL,>Xp NJ7Uwxð-UXMlѮ6^·iFz+D-x5:wژ7vJa~^<]O#p&;`rZt(0q@ou (3ܟ/~$}K40"6e 3 F ;h||!*1pjXGCbf} oƠ c:u.ßoOtv B@@3 aL"]D`ѝc?0=p} G,ud2pI00iNk[`d7elF:`$`d7  :$&;ݰ8  ,hll$o @&Q@Caa!h4ٍr0Sh4ٍ0Mv4l%&&Rh@&Q@r@'> j'>;s6lx"D}@@g@+.\1Y@@@__ @~ >|ٙ BGCF0^__9EAAn~zdȲ4 ٸq#Z_)00RAab`] 4~:@h $MxS45###@A1 3@;4,B{#D;PP 8-d[.MvC'0@!`*\4I e#rY(Ov4@;ܫyKP"Wh-Eʓ@ =,̾#2\ǻ2ɮ h4 =|L8\p!MpC^^vkt F?632aR֧!:8L֩h4\]pu965ïߌ_?2,Vc  ", + iЎ@ɎHc | Ï >1g{u4@#4Ofx~ATAPS쾽b`aeV_#ƗJ\H @v)eOA@AԞAԎAP_33VyA+ h4 SOWC 20cdq29G 6h%K'f 2Ï ~(׹pڈŋia FK;:1;× 29B3|?}c 1b 6P*B@ɎN'2| /0:!w >'?v4i`94~ o0x)o_oaAj绣%oILL$oc".@ɎN?Zh><2H´(~0q-pE\{$'ÿO0I0 pe7.hhhl&Q(b{{{bqʀs  4UA.x022h+̋?@/I%A *P/`[HR  L|FR&??(F`ʁ+K FMi`o'T ,I $0  {FZ` J\; F i+IENDB`