changeset 0:86296c048e46 draft

Init repository for [hr2]
author fgiacomoni
date Wed, 05 Jun 2019 09:40:20 -0400
parents
children e2cbcf6fa22e
files HR2.xml README.txt conf_hr2.cfg hr2_manager.pl hr_out.tmpl lib/HR2_v1.04.cpp lib/conf.pm lib/csv.pm lib/hr.pm static/images/hr2.png t/hr2_managerTest.t t/lib/hrTest.pm test-data/out1.html test-data/out1.tabular test-data/out2.html test-data/out2.tabular
diffstat 16 files changed, 3210 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HR2.xml	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,303 @@
+<tool id="hr2" name="HR2 formula" version="1.1.1.7">
+  <description>
+        find a chemical formula from a accurate mass
+  </description>
+  <requirements>
+      <requirement type="package" version="2.161">perl-data-dumper</requirement>
+      <requirement type="package" version="1.97">perl-text-csv</requirement>
+      <requirement type="package" version="2.97">perl-html-template</requirement>
+      <requirement type="package" version="1.04">hr2</requirement>
+  </requirements>
+  <stdio>
+      <exit_code range="1" level="fatal" />
+  </stdio>
+  
+  
+  <command><![CDATA[
+        perl $__tool_directory__/hr2_manager.pl
+    #if str($input_type.choice) == "YES":
+        -input "${input_type.input}"
+        #if str($input_type.header.header_choice) == "yes":
+            -nblineheader "${$input_type.header.nbHeader}"
+        #end if
+        -colId "${input_type.colId}" -colmass "${input_type.colmass}"
+    #else: 
+        -masse "${input_type.masse}"
+    #end if
+    
+    -tolerance "$tol"
+    -mode "${mode_condition.mode}"
+    #if str($mode_condition.mode) == "neutral":
+        -charge "0"
+    #else:
+        -charge "${mode_condition.qtCharge}"
+    #end if
+    
+    -regleOr "$regleOr" -atomes "$atomes_basic" -atomessup "$atomes_sup"
+    -output1 "$variableMetadata" -outputView "$hr2ResView"
+    -verbose "$verbose"
+  ]]></command>
+  
+  <inputs>
+  	<conditional name="input_type">
+      <param name="choice" type="select" display="radio" label="Would you use a file " help="if 'NO' is selected then one or more mass(es) must be entered manually">
+        <option value="YES">YES</option>
+        <option value="NO">NO</option>
+      </param>
+      <when value="YES">
+        <param name="input" label="File of masses" format="tabular" type="data" />
+        <conditional name="header">
+          <param name="header_choice" type="boolean" checked="true" truevalue="yes" falsevalue="no" label="Do you have a header?" help="if 'YES' is selected then enter your number of header lines" />
+          <when value="yes">
+            <param name="nbHeader" label="Number of header lines" type="integer" value="1" min="1" size="10" help="number of lines not containing masses"/>
+          </when>
+          <when value="no"/>
+        </conditional>
+        <param name="colId" label="Column of Id" type="data_column" data_ref="input" accept_default="true" />
+        <param name="colmass" label="Column of masses (MZ)" type="data_column" data_ref="input" accept_default="true" />
+      </when>
+      <when value="NO">
+        <param name="masse" size="20" type="text" label="Mass (MZ) to submit"  help="For a masses list, writes : m1 m2 m3. Your values ​​must be separated by spaces. You should use dot (.) like decimal separator"/>
+      </when>
+    </conditional>
+    
+    <param name="tol" label="Delta of mass (MZ) (in mmu)" type="float" value="1.0" min="0" max="9.0" help="Tolerance should be between 0 and 9.0 mmu. Default value is 1.0 mmu"/>
+    <conditional name="mode_condition">
+      <param name="mode" label="Molecular Species Searched" type="select" display="radio" help="Or ionization type of the molecule list. Use neutral only if only if the masses correspond to molecules and not to ions : query doesn't be a [M+H] or [M-H] ion">
+        <option value="positive">positive</option>
+        <option value="negative">negative</option>
+        <option value="neutral" selected="true">neutral</option>
+      </param>
+      <when value="neutral"/>
+      <when value="positive">
+        <param name="qtCharge" label="Molecule initial charge" type="select" help="Use 'neutral' if the molecule is not charged">
+          <option value="1">1</option>
+          <option value="2">2</option>
+          <option value="3">3</option>
+        </param>
+      </when>
+      <when value="negative">
+        <param name="qtCharge" label="Molecule initial charge" type="select" help="Use 'neutral' if the molecule is not charged">
+          <option value="1">1</option>
+          <option value="2">2</option>
+          <option value="3">3</option>
+        </param>
+      </when>
+    </conditional>
+    <param name="atomes_basic" label="Please unselect basic atom(s) from following list you want to exclude" type="select" display="checkboxes" multiple="True" help="Unselect one or more basic atoms to exclude them from the generated formula proposition">
+      <option value="C" selected="true">C</option>
+      <option value="N" selected="true">N</option>
+      <option value="O" selected="true">O</option>
+      <option value="H" selected="true">H</option>
+      <option value="P" selected="true">P</option>
+    </param>
+    <param name="regleOr" label="Use all 7 golden rules OR only the first 3 golden rules?" type="select" display="radio" help="Use 'only first 3 rules' if you want more empirical formulas">
+      <option value="YES">only first 3 rules</option>
+      <option value="NO" selected="true">all 7 rules</option>
+    </param>
+    <param name="atomes_sup" label="Add some atom(s) from following list if needed" type="select" display="checkboxes" multiple="True" help="C, H, N, O and P are available in basic atom section">
+      <option value="S">S</option>
+      <option value="F">F</option>
+      <option value="L">Cl</option>
+      <option value="K">K</option>
+      <option value="B">Br</option>
+      <option value="A">Na</option>
+      <option value="1">13C</option>
+    </param>
+    <param name="verbose" type="select" label="Verbose level" display="radio" help="">
+        <option value="1" selected="true">Low</option>
+        <option value="3" >High</option>
+    </param>
+  </inputs>
+  <outputs>
+    <data name="variableMetadata" format="tabular" label="${tool.name}_TSV"/>
+    <data name="hr2ResView" format="html" label="${tool.name}_VIEW"/>
+  </outputs>
+  
+  <tests>
+  	<test>
+  		<param name="choice" value="NO"/>
+  		<param name="masse" value="175.125"/>
+  		<param name="tolerance" value="1.0"/>
+  		<param name="mode" value="negative"/>
+  		<param name="qtCharge" value="1"/>
+  		<param name="atomes_basic" value="C,O,N,H"/>
+  		<param name="regleOr" value="NO"/>
+  		<param name="verbose" value="3"/>
+  		<output name="variableMetadata" file="out_test01.tabular"/>
+  		<output name="hr2ResView" file="out_test01.html"/>
+    </test>
+    <test>
+  		<param name="choice" value="NO"/>
+  		<param name="masse" value="88.052"/>
+  		<param name="tolerance" value="1.0"/>
+  		<param name="mode" value="neutral"/>
+  		<param name="qtCharge" value="1"/>
+  		<param name="atomes_basic" value="C,O,H"/>
+  		<param name="regleOr" value="NO"/>
+  		<param name="verbose" value="3"/>
+  		<output name="variableMetadata" file="out_test02.tabular"/>
+  		<output name="hr2ResView" file="out_test02.html"/>
+    </test>
+  </tests>
+  
+  <help><![CDATA[
+
+.. class:: infomark
+
+**Authors**
+  | HR2 original program and its documentation are Copyright (c) 1992-2005 by Joerg Hau under GNU General Public License ("GPL")
+
+.. class:: infomark
+
+**Wrapping**
+  | Marion Landi - FLAME ; PFEM ; INRA ; MetaboHUB (for xml interface and Perl wrapper)
+  | Franck Giacomoni - PFEM ; INRA ; MetaboHUB (for xml interface, conda dependancies and Perl wrapper)
+
+---------------------------------------------------
+
+.. class:: infomark
+
+**Please cite** If you use this tool, please cite
+ | Tobias Kind and Oliver Fiehn. (2007). "Seven Golden Rules for heuristic filtering of molecular formulas obtained by accurate mass spectrometry." BMC Bioinformatics p8:105 http://www.ncbi.nlm.nih.gov/pubmed/17389044
+ | HR2 original program and its documentation are under GNU General Public License ("GPL") : GPL is a"contaminating" license. http://fiehnlab.ucdavis.edu/projects/Seven_Golden_Rules/Software
+
+
+---------------------------------------------------
+
+==============
+HR2 formula
+==============
+
+-----------
+Description
+-----------
+
+ | Find a formula for the masses
+ | only molecules with carbon (C) will be search
+
+
+-----------------
+Workflow position
+-----------------
+
+
+.. image:: ./static/images/metabolomics/hr2.png
+        :width: 800
+
+
+
+
+-----------
+Input files
+-----------
+
++-------------------------+-----------+
+| Parameter : num + label |  Format   |
++=========================+===========+
+| 1 : variableMetadata    |  tabular  |
++-------------------------+-----------+
+
+File variableMetadata must have at least the 2 following columns : 
+  * Id : column to identify masses in the csv/tsv input file
+  * Masses : column with all the masses in the csv/tsv input file
+
+
+----------
+Parameters
+----------
+
+Would you use a file
+  | Choose whether the masses are in a file or entered manually
+  | YES (default) : parameters **File of masses ; Column of Id ; Number of header ; Column of masses** are visible
+  | NO : parameter **Mass of the molecule** is visible
+  |
+
+
+If 'use file'='YES'
+
+Column of Id
+  | Specify the column number for the id in the csv/tsv input file
+  |
+
+Number of header lines
+  | Number of lines not containing values 
+  |
+
+Column of masses
+  | Specify the column number for the mass in the csv/tsv input file
+  |
+
+If 'use file'='NO'
+
+Mass (MZ) to submit 
+  | Specify a list of mass to request
+  | one or more mass(es) entered manually
+  | For a masses list, writes : m1 m2 m3
+  | You must separate yours values with space
+  | dot (.) is for float number
+  |
+
+In all cases :
+
+Delta
+  | Tolerance of the gap in the mass
+  | It should be between 0 and 9.0 mmu
+
+Ionization
+  | Type of ionization of the molecule : *positif, negatif, neutral*
+  | Use neutral if query doesn't be a [M+H] or [M-H] ion
+  | HR2 knows only the weight of uncharged molecules
+  | so we made ​​a correction to the masses of the value of a proton before the search
+  | if the masses are those from a spectrometry in positive or negatif mode.
+  | **neutral** : will do a search on the mass unchanged.
+
+Initial charge
+  | Use 0 if the molecule is not basically charged
+
+Exclude some basic atom(s)
+  List of atoms that can be exclude to the molecule : *C, N, O, H, P*
+
+Golden rules
+  | There are 7 golden rules
+  | Some are too stringent, especially for small molecules
+  | Use 'yes' if you want more empirical formulas
+
+Add some optionnal atom(s)
+  List of atoms that can be searched in addition to the molecule : *S, F, Cl, K, Br, Na, 13C*
+
+------------
+Output files
+------------
+
+Two types of files
+  | hr2_VIEW.HTML : for viewing result via HTML.
+  | hr2_TSV.tabular : for linking with others modules.
+  | an excel-like output will be available. 
+  
+---------------------------------------------------
+
+
+---------------
+Working example
+---------------
+
+
+.. class:: warningmark
+
+Refer to the corresponding "W4M HowTo" in http://workflow4metabolomics.org/howto section
+ | Format Data For Postprocessing
+ | Perform LCMS Annotations
+
+.. class:: warningmark
+
+And their "W4M courses 2018":
+ | Using Galaxy4Metabolomics - W4M table format for Galaxy
+ | Les banques d'annotation - Annotation
+
+  ]]></help>
+  <citations>
+    <citation type="doi">10.1186/1471-2105-8-105</citation>
+  </citations>
+</tool>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.txt	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,63 @@
+## ****** HR2 environnemnt : ****** ##
+# version sept 2014 M Landi / A LIEURADE, Cl LIONNET, P MARIJON, B RADISSON and L VARRAILHON / F Giacomoni
+
+## --- PERL compilator / libraries : --- ##
+$ perl -v
+This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi
+
+# libs CORE PERL : 
+use strict ;
+use warnings ;
+use Carp qw (cluck croak carp) ;
+use Data::Dumper ;
+use Getopt::Long ;
+use FindBin ;
+
+# libs CPAN PERL : 
+$ perl -e 'use Text::CSV'
+$ sudo perl -MCPAN -e shell
+cpan> install Text::CSV
+
+# libs pfem PERL : already include in the package
+use lib::conf  qw( :ALL ) ;
+use lib::csv  qw( :ALL ) ;
+--
+
+## --- R bin and Packages : --- ##
+NA
+--
+
+## --- Binary dependencies --- ##
+Install folowing binaries :
+* HR2 :
+source : HR2_v1.04.cpp available in the tar ball at lib/HR2_v1.04.cpp
+lab : http://jhau.maliwi.de/sci/ms.html for initial project and http://fiehnlab.ucdavis.edu/projects/Seven_Golden_Rules/Software/ for fork
+install : in /opt/prog/metabolomics/hr2 (you can change this path in conf file : conf_hr2.cfg
+compilation : g++ HR2_v1.04.cpp -o HR2-all-res_v1.04.exe
+
+The project is available on :
+http://fiehnlab.ucdavis.edu/projects/Seven_Golden_Rules/Software/seven-golden-rules-supplement-v46.zip (the launch of the download may take some time)
+~/seven-golden-rules-supplement-v46.zip/supplement/HR2-formula-generator/HR2.zip/HR2.cpp
+OR
+http://sourceforge.net/projects/hires/ : This proposed binary is an fork of the HiRes initial project.
+--
+
+## --- Config : --- ##
+Edit the following lines in the config file : conf_hr2.cfg
+lancerHR2=/your_hr2_binary_path/HR2-all-res_v1.04.exe
+JS_GALAXY_PATH=http://YOUR_GALAXY_HOSTNAME/static/scripts/libs/outputs
+CSS_GALAXY_PATH=http://YOUR_GALAXY_HOSTNAME/static/style
+--
+
+## --- XML HELP PART --- ##
+one image : in the static/images dir
+hr2.png
+--
+
+## --- DATASETS --- ##
+No data set ! waiting for galaxy pages
+--
+
+## --- ??? COMMENTS ??? --- ##
+In the PFEM version, the HR2 source (HR2_v*.cpp) is modified to correct atom exact masses.
+For more information, please contact us.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/conf_hr2.cfg	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,36 @@
+## Conf file for hr2.pl script
+#
+## Version -- edited by F.Giacomoni / M Landi
+VERSION=1.1.0
+#
+## ARGVT
+## hr2 binary path
+HR2_EXE=HR2.exe
+HR2_VERSION=hr version 20050617
+#
+##Default parameter values
+tolerance=1.0
+mode=positive
+charge=0
+DEFAULT_ATOMS=C,H,O,N
+#
+## Range in hr cmd for each atom
+DEFAULT_MIN=0
+DEFAULT_MAX=10
+C=100
+H=200
+O=70
+N=40
+P=10
+#
+## MZ value
+proton=1.007825
+electron=0.0005486
+#
+## Galaxy url for HTML JS and CSS path 
+JS_GALAXY_PATH=https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/scripts
+CSS_GALAXY_PATH=https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/style
+#
+## HTML OUTPUT : 
+HTML_ENTRIES_PER_PAGE=4
+HTML_TEMPLATE=hr_out.tmpl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hr2_manager.pl	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,335 @@
+#!perl
+
+## script  : hr2_manager.pl
+## VERSIONS :
+#	- 01/03/2019 : Fix P issue, permit more flexible atom settings and add requirements for conda auto managing.
+
+#=============================================================================
+#                              Included modules and versions
+#=============================================================================
+## Perl modules
+use strict ;
+use warnings ;
+use Carp qw (cluck croak carp) ;
+
+use Data::Dumper ;
+use Getopt::Long ;
+use POSIX ;
+use FindBin ; ## Allows you to locate the directory of original perl script
+
+## Dedicate Perl Modules (Home made...)
+use lib $FindBin::Bin ;
+my $binPath = $FindBin::Bin ;
+use lib::hr qw( :ALL ) ;
+## PFEM Perl Modules
+use lib::conf  qw( :ALL ) ;
+use lib::csv  qw( :ALL ) ;
+
+## Initialized values
+use vars qw(%parametre);
+my $help = undef ; 
+my ( $input_file, $line_header, $col_id, $col_mass ) = ( undef, undef, undef, undef ) ; # manage input option file of masses
+my ( $mass ) = ( undef ) ; # manage input option masses list
+my ( $tolerance, $mode, $charge, $has_golden_rules, $atomes, $atomes_basic ) = ( undef, undef, undef, undef, undef, undef ) ; # manage params
+my ( $output_csv, $output_html ) = ( undef, undef) ; # manage ouputs
+my $verbose = 2 ;
+
+#=============================================================================
+#                                Manage EXCEPTIONS
+#=============================================================================
+&GetOptions ( 	"h"     		=> 	\$help,       # HELP
+				"input:s"		=>	\$input_file,
+				"colId:i"		=>	\$col_id,
+				"nbHeader:i"	=>	\$line_header,
+				"colmass:i"		=>	\$col_mass,
+				"masse:s"		=>	\$mass,
+				"tolerance:f"	=>	\$tolerance,
+				"mode:s"		=>	\$mode,
+				"charge:i"		=>	\$charge,
+				"regleOr:s"		=>	\$has_golden_rules,
+				"atomes:s"		=>	\$atomes_basic, # [basic atoms like CNOHP]
+				"atomessup:s"	=>	\$atomes,
+				"output1:s"		=>	\$output_csv,
+				"outputView:s"	=>	\$output_html,
+				"verbose:i"		=> 	\$verbose,
+            ) ;
+         
+#=============================================================================
+#                                EXCEPTIONS
+#=============================================================================
+$help and &help ;
+
+#=============================================================================
+#                                MAIN SCRIPT
+#=============================================================================
+my %atomsConf = () ;
+$atomsConf{'C'} = {'min' => 0, 'max' =>  0} ;
+$atomsConf{	'H'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'N'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'O'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'P'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'S'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'F'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'L'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'K'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'B'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'A'} = {'min' => 0, 'max' => 0} ;
+$atomsConf{	'1'} = {'min' => 0, 'max' => 0} ;
+
+## -------------- Conf file and verbose ------------------------ :
+my ( $CONF ) = ( undef ) ; ## verbose level is 3 for debugg
+my $time_start = time ;
+
+foreach my $conf ( <$binPath/*.cfg> ) {
+	my $oConf = lib::conf::new() ;
+	$CONF = $oConf->as_conf($conf) ;
+}
+## --------------- Global parameters ---------------- :
+my ( $ids, $masses, $hr_cmds, $results, $parsed_results ) = ( undef, undef, undef, undef, undef ) ;
+my $complete_rows = undef ;
+my ($hr_atoms_list, $hr_atoms_and_ranges, $set_tol, ) = (undef, undef, undef, ) ;
+
+## Check and manage params
+my $ohr = lib::hr->new() ;
+
+## set tolerance
+$set_tol = $ohr->manage_tolerance( \$tolerance, \$CONF->{'tolerance'} ) ;
+
+## check HR exe envt :
+my $hr_check = $ohr->check_hr_exe(\$CONF->{'HR2_EXE'}, \$CONF->{'HR2_VERSION'}) ;
+if (!defined $hr_check ) { croak "No hr exe available (wrong path) or wrong version will be used  -- end of script\n" ; }
+
+## manage atoms and their ranges
+#$hr_atoms_list = $ohr->manage_atoms(\$atomes, \$CONF->{'DEFAULT_ATOMS'}) ; ## DEPRECATED
+
+# manage atoms and their ranges with a hash structure
+my $atomsCurrentConf = $ohr->manage_atoms_and_ranges(\%atomsConf, $CONF, $atomes_basic, $atomes) ;
+
+#if ( defined $hr_atoms_list ) {
+#	## implements range foreach atom
+#	foreach my $atom ( (split(",", $$hr_atoms_list )) ) {
+#		my $range_max = $CONF->{'DEFAULT_MAX'} ; # manage max value in case of
+#		if ( exists $CONF->{$atom} ) 	{ $range_max = $CONF->{$atom} ; }
+#		my $ref_range = $ohr->manage_atom_and_range(\$atom, \$CONF->{'DEFAULT_MIN'}, \$range_max ) ;
+#		$hr_atoms_and_ranges .= $$ref_range ; ## concat ranges
+#	}
+#}
+#else { 	croak "No atom detected with input params\n" ; }
+
+## Parsing input file with masses/ids or unik mass :
+## manage only one mass
+if ( ( defined $mass ) and ( $mass ne "" ) and ( $mass > 0 ) ) {
+	$ids = ['mass_01'] ;
+	$masses = [$mass] ;
+	
+} ## END IF
+## manage csv file containing list of masses
+elsif ( ( defined $input_file ) and ( $input_file ne "" ) and ( -e $input_file ) ) {
+	
+	## parse all csv for later : output csv build
+	my $ocsv_input  = lib::csv->new() ;
+	my $complete_csv = $ocsv_input->get_csv_object( "\t" ) ;
+	$complete_rows = $ocsv_input->parse_csv_object($complete_csv, \$input_file) ;
+	
+	## parse csv ids and masses
+	my $is_header = undef ;
+	my $ocsv = lib::csv->new() ;
+	my $csv = $ocsv->get_csv_object( "\t" ) ;
+	if ( ( defined $line_header ) and ( $line_header > 0 ) ) { $is_header = 'yes' ;	}		else{ $is_header = 'no' ; }
+	$masses = $ocsv->get_value_from_csv_multi_header( $csv, $input_file, $col_mass, $is_header, $line_header ) ; ## retrieve mz values on csv
+	$ids = $ocsv->get_value_from_csv_multi_header( $csv, $input_file, $col_id, $is_header, $line_header ) ; ## retrieve ids values on csv
+	
+}
+else {
+	croak "Can't work with HR2 : missing input file or mass (list of masses, ids)\n" ;
+} ## end ELSE
+
+## check using golden rules
+if ( $has_golden_rules eq 'NO') { $has_golden_rules = undef ; }
+
+## ---------------- launch queries -------------------- :
+
+## prepare cmd
+foreach my $mz (@{ $masses }) {
+	## computes mass
+	my $ohr_mode = lib::hr->new() ;
+	my ( $exact_mass ) = $ohr_mode->manage_mode( \$mode, \$charge, \$CONF->{'electron'}, \$CONF->{'proton'}, \$mz ) ;
+	print "Current MZ send to HR\n"  if $verbose == 3 ;
+	print Dumper $exact_mass if $verbose == 3 ;
+	## build exe line
+	my $ohr_exe = lib::hr->new() ;
+	my $hr_cmd = $ohr_exe->config_hr_exe( \$CONF->{'HR2_EXE'}, \$tolerance, $exact_mass, \$has_golden_rules, \$atomsCurrentConf ) ;
+	print "$hr_cmd\n" if $verbose == 3 ;
+	push(@{$hr_cmds}, $$hr_cmd) ;
+}
+
+## MultiThreading execution of Hr :
+my $threads = lib::hr->new() ;
+my $hr_object = lib::hr->new() ;
+if ( $hr_object->can('hr_exe') ) {
+	my $method = $hr_object->can('hr_exe') ;
+	$results = $threads->threading_hr_exe( $method, $hr_cmds) ;
+
+}
+
+## MultiThreading parsing of Hr outputs :
+my $hrres_object = lib::hr->new() ;
+if ( $hrres_object->can('hr_out_parser') ) {
+	my $method = $hr_object->can('hr_out_parser') ;
+	if ( defined $results ) { 	$parsed_results = $threads->threading_hr_exe( $method, $results ) ; }
+}
+
+## -------------- Produce HTML/CSV output ------------------ :
+my $search_condition = 'Mode used: '.$mode.' / Charge: +'.$charge.' / Mass tolerance: '.$$set_tol.' / Composition: '.$atomsCurrentConf ;
+## Uses N mz and theirs entries per page (see config file).
+# how many pages you need with your input mz list?
+my $nb_pages_for_html_out = ceil( scalar(@{$masses} ) / $CONF->{HTML_ENTRIES_PER_PAGE} )  ;
+
+if ( ( defined $output_html ) and ( defined $parsed_results ) ) {	
+	my $oHtml = lib::hr::new() ;
+	my ($tbody_object) = $oHtml->set_html_tbody_object( $nb_pages_for_html_out, $CONF->{HTML_ENTRIES_PER_PAGE} ) ;
+	($tbody_object) = $oHtml->add_mz_to_tbody_object($tbody_object, $CONF->{HTML_ENTRIES_PER_PAGE}, $masses, $ids, $parsed_results ) ;
+	($tbody_object) = $oHtml->add_entries_to_tbody_object($tbody_object, $parsed_results) ;
+	my $html_file = $binPath.'/'.$CONF->{'HTML_TEMPLATE'} ;
+	my $output = $oHtml->write_html_skel(\$output_html, $tbody_object, $nb_pages_for_html_out, $search_condition, $html_file, $CONF->{'JS_GALAXY_PATH'}, $CONF->{'CSS_GALAXY_PATH'}) ;
+	
+} ## END IF
+else {
+	croak "Can't create a HTML output for HMDB : no result found or your output file is not defined\n" ;
+}
+
+if ( ( defined $output_csv ) and ( defined $parsed_results ) ) {
+	# produce a csv based on METLIN format
+	my $ocsv = lib::hr::new() ;
+	if (defined $input_file) {
+		my $lm_matrix = undef ;
+		if ( ( defined $line_header ) and ( $line_header == 1 ) ) { $lm_matrix = $ocsv->set_hr_matrix_object('hr2', $masses, $parsed_results ) ; }
+		elsif ( ( defined $line_header ) and ( $line_header == 0 ) ) { $lm_matrix = $ocsv->set_hr_matrix_object(undef, $masses, $parsed_results ) ; }
+		$lm_matrix = $ocsv->add_hr_matrix_to_input_matrix($complete_rows, $lm_matrix) ;
+		$ocsv->write_csv_skel(\$output_csv, $lm_matrix) ;
+	}
+	elsif (defined $mass) {
+		$ocsv->write_csv_one_mass($masses, $ids, $parsed_results, $output_csv) ;
+	}
+} ## END IF
+else {
+#	croak "Can't create a tabular output for HR2 : no result found or your output file is not defined\n" ;
+}
+
+
+
+### VERBOSE OUTPUTs
+if ( $verbose == 3 ) {
+	print "-- Conf file contains :\n" ;
+	print Dumper $CONF ;
+	print "-- Atoms input list :\n" ;
+	print Dumper $atomes_basic ;
+	print "-- Suppl. atoms input list :\n" ;
+	print Dumper $atomes ;
+	print "-- HR envt ready  :\n" ;
+	print Dumper $hr_check ;
+	print "-- Atoms and ranges :\n" ;
+	print Dumper $atomsCurrentConf ;
+	print "-- Tolerance :\n" ;
+	print Dumper $set_tol ;
+	print "-- Complete input file :\n" ;
+	print Dumper $complete_rows ;
+	print "-- Inputs initiales masses :\n" ;
+	print Dumper $masses ;
+	print "-- Inputs initiales ids :\n" ;
+	print Dumper $ids ;
+	print "-- Hr_Cmds :\n" ;
+	print Dumper $hr_cmds ;
+	print "-- Hr_Results :\n" ;
+#	print Dumper $results ;
+	print "-- Hr_parsed Results :\n" ;
+	print Dumper $parsed_results ;
+	
+	my $nb_results = scalar (@{$results}) ;
+	print "-- Hr_Results return  : $nb_results\n" ;
+}
+
+my $time_end = time ;
+my $seconds = $time_end-$time_start ;
+print "\n------  Time used in threaded mode by 6 : $seconds seconds --------\n\n" ;
+
+
+
+
+
+
+#====================================================================================
+# Help subroutine called with -h option
+# number of arguments : 0
+# Argument(s)        :
+# Return           : 1
+#====================================================================================
+sub help {
+	print STDERR "
+hr2_manager.pl
+
+# hr2_manager is a script to elucide chemical formula by their accurate masses. The HiRes program is integrate in this package
+# Input : a accurate mass or a file of masses
+# Author : Franck Giacomoni and Marion Landi
+# Email : fgiacomoni\@clermont.inra.fr or mlandi\@clermont.inra.fr
+# Version : 1.1
+# Created : 01/12/2011
+# Last Update : 09032014
+USAGE :		 
+		hr2_manager.pl -h or
+		hr2_manager.pl -input [cvs file of masses] -colId [int] -colmass [int] -nbHeader [int] -tolerance [float] -mode [positive, neutral or negative] -charge [int] -regleOr [yes or no] -atome [P, S, F, Cl, K, B, A, 1 ] -output1 [csv file] -outputView [html file]
+		hr2_manager.pl -masse [double] -tolerance [float] -mode [positive, neutral or negative] -charge [int] -regleOr [yes or no] -atome [P, S, F, Cl, K, B, A, 1 ] -output1 [csv file] -outputView [html file]
+	" ;
+	exit(1);
+}
+
+## END of script - F Giacomoni 
+
+__END__
+
+=head1 NAME
+
+ hr2_manager.pl -- script for launch / manage hr2 binary
+
+=head1 USAGE
+
+ hr2_manager.pl -h or
+ hr2_manager.pl -input [cvs file of masses] -colId [int] -colmass [int] -nbHeader [int] -tolerance [float] -mode [positive, neutral or negative] -charge [int] -regleOr [yes or no] -atome [P, S, F, Cl, K, B, A, 1 ] -output1 [csv file] -outputView [html file]
+ hr2_manager.pl -masse [double] -tolerance [float] -mode [positive, neutral or negative] -charge [int] -regleOr [yes or no] -atome [P, S, F, Cl, K, B, A, 1 ] -output1 [csv file] -outputView [html file]
+
+=head1 SYNOPSIS
+
+This script manages hr2 binary which elucids raw formula with exact masses.
+
+=head1 DESCRIPTION
+
+This main program is a module to elucidate chemical formula with HiRes program. Source is available on Fiehn lab web.
+
+=over 4
+
+=item B<function01>
+
+=item B<function02>
+
+=back
+
+=head1 AUTHOR
+
+Franck Giacomoni E<lt>franck.giacomoni@clermont.inra.frE<gt>
+Marion Landi E<lt>marion.landi@clermont.inra.frE<gt>
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=head1 VERSION
+
+version 1 : 18/07/2012
+
+version 2 : 02/10/2013
+
+version 3 : 20/02/2014
+
+version 4 : 01/03/2019
+
+=cut
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hr_out.tmpl	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html>
+<html lang="en">
+	<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content=""><meta name="author" content="INRA de Clermont-Ferrand"><title>Galaxy HR2 queries - All results</title><link rel="stylesheet" href="css.php" media="all"><link rel="stylesheet" href="<TMPL_VAR NAME=CSS_GALAXY_PATH>/simplePagination.css"/><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script><script src="<TMPL_VAR NAME=JS_GALAXY_PATH>/jquery.simplePagination.js"></script><style>body{padding-top:70px} div.lm-table-warning{font-size:1.4em;font-weight:bold;padding-right:25px;color:#21536a;margin-left:3px;}tr.green td{background-color:#eaf2d3;color:black;} tr.blank td{background-color:#9999CC;color:black;} table{font-family:\"Trebuchet MS\", Arial, Helvetica, sans-serif;width:100%;border-collapse:collapse;}table.detail, table.detail tr.parent, table.detail td, table.detail th, table.detail tr.category {border-collapse:collapse;border:1px solid #98bf21;}table.detail th {font-size:1.2em;text-align:center;padding-top:5px;padding-bottom:10px;background-color:#a7c942;color:#ffffff;}td.ca {text-align:center;}footer{margin:50px 0;}</style><script>function test(pageNumber){var page="#page-id-"+pageNumber;$('.select').hide();$(page).show()}</script></head>
+	<body><div class="container"><div class="lm-table-warning">Results of HR elucidation queries - <TMPL_VAR NAME=CONDITIONS></div><div id="detail_table_source" style="display:none"></div><p><div id="choose"></div><p><div id="ms_search_0" class="ms-search-table"></div><table id="detail_table" class="detail"><col style="width:20px;"><!-- Ids (m/z)--><col style="width:20px;"><!-- Mass (m/z)--><col style="width:20px;"><!-- Formula--><col style="width:60px;"><!-- cpd mw--><col style="width:50px;"><!-- delta--><col style="width:50px;"><!-- total--><thead><th>ID from input</th><th>Mass (m/z)</th><th>Formula</th><th>Compound MW (Da)</th><th>Delta</th><th>Total</th></thead><TMPL_LOOP NAME=PAGES><tbody class="select" id="page-id-<TMPL_VAR NAME=PAGE_NB>"><TMPL_LOOP NAME=MASSES><tr class="<TMPL_VAR NAME=MZ_COLOR>"><td class="ca" ><TMPL_VAR NAME=MASSES_ID_QUERY></td><td id="<TMPL_VAR NAME=MASSES_NB>" class="ca" ><TMPL_VAR NAME=MASSES_MZ_QUERY></td><td class="ca" colspan="3"></td><td class="ca" ><TMPL_VAR NAME=MASSES_TOTAL></td></tr><TMPL_LOOP NAME=ENTRIES><tr class="<TMPL_VAR NAME=ENTRY_COLOR>"><td class="ca" colspan="2"></td><td class="ca"><TMPL_VAR NAME=ENTRY_FORMULA></td><td class="ca"><TMPL_VAR NAME=ENTRY_CPD_MZ></td><td class="ca"><TMPL_VAR NAME=ENTRY_DELTA></td><td class="ca" colspan="1"></td></tr></TMPL_LOOP></TMPL_LOOP></tbody></TMPL_LOOP></table></div><div class="container"><hr><footer><div class="row"><div class="col-lg-12"><p><a href="http://jigsaw.w3.org/css-validator/check/referer" target="_blank"><img style="border:0;width:88px;height:31px"	src="http://jigsaw.w3.org/css-validator/images/vcss-blue" alt="Valid CSS!" /></a></p><p>Copyright &copy; INRA, N Paulhe, F Giacomoni 2014</a></p></div> </div></footer></div><script language="javascript">$(function() {$('#choose').pagination({items: <TMPL_VAR NAME=PAGES_NB>,itemsOnPage: 1,currentPage: 1,onInit: function () { test(1); },cssStyle: 'light-theme',onPageClick: function(pageNumber){test(pageNumber)}}).pagination('redraw');});</script></body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/HR2_v1.04.cpp	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,1018 @@
+/*
+
+ HR2.C
+ V1.04
+ (Changes: meb)
+
+ A program to calculate elemental compositions for a given mass.
+ See the file README for details.
+
+--------------------------------------------------------------------
+ Copyright (c) 2001...2005 Joerg Hau <joerg.hau(at)dplanet.ch>.
+
+ mail: joerg.hau@dplanet.ch
+ www:  http://www.mysunrise.ch/users/joerg.hau/
+
+ *changed version by Tobias Kind (TK), 2006 , Fiehnlab,
+ *added extended valencies, added implementation of
+  seven golden rules of molecular formula filtering
+ 
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of version 2 of the GNU General Public
+ License as published by the Free Software Foundation. See the
+ file LICENSE for details.
+
+ 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.
+--------------------------------------------------------------------
+
+ Creation:	somewhere in 1992 by JHa.
+ Revision:  2001-04-18, GPL'd, first public release (JHa)
+            2001-04-21, improved help text (JHa)
+            2002-06-27, added sodium (JHa)
+            2002-10-09, added 15N (JHa)
+            2005-02-25, added -v option; license now GPL *v2* (JHa)
+            2005-02-27, optimised code in calc loop (JHa)
+            2005-02-28, verified and updated atomic masses (JHa)
+            2005-06-17, added GPL text when "-h" is used (JHa)
+			2006-01-01, extended version for BMC Bioinformatics publication - HR2 (TK)
+			2006-03-03, added element ratio checks, extended valencies, only even electrons - HR2 (TK)
+			2006-09-09,	1000x-10000x speedup hand optimized hehe. - HR2 (TK)
+						-->special version for CHNSOP-F-Cl-Br-Si 
+			2009-05-28, David Enot introduced the concept of 'Adducts' (nadd) for MZedDB and corrected 
+						some inaccuracies in April 2008. Manfred Beckmann has now corrected the use of 
+						'nadd' and 'charge' by calculating 'nadd' from 'charge' using abscharge = abs(charge)
+						(did it manually because I couldn't find 'abs') to correct 'measured_mass' and limits, 
+						but keeping nadd = 0 for rdb calculation of neutral MW (MB)
+			2014-08-29, Marion Landi corrected the mass of atoms.
+
+ This is ANSI C and should compile with any C compiler; use
+ something along the lines of "gcc -Wall -O3 -o hr hr.c".
+ "g++ -O2 -o myhr HR2.cpp"  on a Mac OS X G5 proc
+ Optimize for speed, you may gain factor 3!
+ NOW compiled under Visual C++ Express (faster than GCC) in C++ mode for boolean type.
+
+
+ ---------------------------------------------------------------------
+ Example arguments:
+ 1) -m 1 -t 100000 -C 1-100 -H 1-220 -N 0-10 -O 0-10 -P 0-10 -S 0-10 -L 0-10 -B 0-10
+ 2) -m 500 -t 1 -C 50-100 -H 10-220 -N 0-10 -O 0-10 -P 0-10 -S 0-10 -L 0-10 -B 0-10
+ 3) hr2-all-res -c "Hexaflumuron_459.982882Da_3ppm" -m 459.982882 -t 1.37995 -C 0-39 -H 0-98 -N 0-34 -O 0-30 -P 0-12 -S 0-12 -F 0-12 -L 0-14 -B 0-7 -I 0-0 
+	945 formulas found in    253 seconds. (now 4 seconds, before eternal)
+ 4) hr2 -m 459.982882 -t 1.37995 -C 10-39 -H 28-98 -N 4-34 -O 0-30 -P 1-12 -S 1-12 -F 0-12 -L 1-14 -B 2-6 -I 0-0
+	1 formula in 0 seconds (former eternal time)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+/* Uncomment this for windows
+#include <windows.h>
+#include <process.h> 
+*/
+#include <math.h>
+
+
+#define VERSION "20050617"	/* String ! */
+#define TRUE 	1
+#define FALSE 	0
+#define MAXLEN  181          /* max. length of input string */
+
+#define _CRT_SECURE_NO_DEPRECATE 1
+
+typedef struct 	{
+		const char *sym;	/* symbol */
+		const char *symi;	/* non iso symbol */
+		const double mass;	/* accurate mass */
+		const float val;	/* to calculate unsaturations */
+		const int key;		/* used for decoding cmd line */
+		int min,		/* atom count min */
+		    max,		/* atom count max */
+		    cnt,		/* atom count actual */
+		    save;		/* atom count old  - for loop exiting*/
+		} Element;
+
+
+/* --- atomic masses as published by IUPAC, 2002-10-02 ---------- */
+
+Element el[]=
+/* array of the elements used here:
+   Symbol, exact mass, dbe, keycode, number, default-min, default-max
+   dle: isoptopes are given by iC for 13C, iK for 41K etc....
+*/
+{
+{ "C", "C",  12.000000000,   +2.0, 'C', 0, 100, 0,0 },
+{ "iC", "C", 13.0033548378, +2.0, '1', 0, 0, 0 ,0 },
+{ "H",  "H",   1.0078250321,  -1.0, 'H', 0, 200, 0 ,0},
+{ "iH", "H",   2.0141017780,  -1.0, 'D', 0, 0, 0 ,0},
+{ "N",  "N",  14.0030740052,  +1.0, 'N', 0, 40, 0,0 },          //org +1 = valence = 3: now +3 for valence = 5
+{ "iN",  "N",  15.0001088984,   +1.0, 'M', 0, 0, 0,0 },
+{ "O",  "O",  15.9949146221,   0.0, 'O', 0, 70, 0 ,0},
+{ "F", "F",  18.99840322,    -1.0, 'F', 0, 0, 0 ,0},
+{ "Na", "Na", 22.98976928,    -1.0, 'A', 0, 0, 0 ,0},
+{ "Si", "Si", 27.9769265327,  +2.0, 'I', 0, 0, 0 ,0},
+{ "P", "P",  30.97376163,    +3.0, 'P', 0, 10, 0 ,0},            //org +1 valence = 3: now +3 for valence = 5
+{ "S", "S",  31.97207069,    +4.0, 'S', 0, 0, 0 ,0},            //org 0 = valence = 2; now +4 for valence = 6
+{ "Cl", "Cl", 34.96885268,    -1.0, 'L', 0, 0, 0 ,0},
+{ "iCl", "Cl", 36.96590259,    -1.0, 'E', 0, 0, 0 ,0},  // added dle
+{ "Br", "Br", 78.9183371,     -1.0, 'B', 0, 0, 0 ,0},
+{ "iBr", "Br", 80.9162906, -1.0, 'G', 0, 0, 0 ,0},         // added dle
+{ "K", "K", 38.96370668,    -1.0, 'K', 0, 0, 0 ,0},             // added dle
+{ "iK", "K", 40.96182576,    -1.0, 'J', 0, 0, 0 ,0},            // added dle
+};
+
+const double electron = 0.0005486;
+
+
+/* --- global variables --- */
+
+double  charge,		/* charge on the molecule */
+        tol,		/* mass tolerance in mmu */
+        abscharge,  /* abs(charge): to calculate 'measured_mass' and limits - meb */
+        nadd;       /* nadd now calculated as abs(charge) - meb */
+char    comment[MAXLEN]="";	/* some text ;-) */
+int     single;		/* flag to indicate if we calculate only once and exit */
+int     nr_el;		/* number of elements in array (above) */
+bool	verbose;
+bool	applygr;
+
+/* --- some variables needed for reading the cmd line --- */
+
+char   *optarg;		/* global: pointer to argument of current option */
+static int optind = 1;	/* global: index of which argument is next. Is used
+                as a global variable for collection of further
+                arguments (= not options) via argv pointers. */
+
+
+/* --- function prototypes ------------------- */
+
+int     input(char *text, double *zahl);
+int     readfile(char *whatfile);
+double  calc_mass(void);
+float   calc_rdb(void);
+long     do_calculations(double mass, double tolerance);
+int     clean (char *buf);
+int     getopt(int argc, char *argv[], char *optionS);
+/* you have to compile with C++ or define yourself this bool type (C99 compiler definition) */
+bool calc_element_ratios(bool element_probability);
+
+/* --- threading ------------------- */
+/* mass and RDB calculation could be in several other threads
+however the loop is very fast, context switching takes longer and
+slows down the whole process. Single-Thread multiprocessor (cluster) implementation
+seems superior here. */
+/* Uncomment for windows
+HANDLE hThread2,hThread3; 
+unsigned threadID2,threadID3;
+*/
+
+
+/* --- main --- */
+
+int main (int argc, char **argv)
+{
+double mz;	/* mass */
+char buf[MAXLEN];
+int i, tmp;
+
+static char *id =
+"hr version %s. Copyright (C) by Joerg Hau 2001...2005 & Tobias Kind 2006 :-).\n";
+
+static char *disclaimer =
+"\nThis program is free software; you can redistribute it and/or modify it under\n"
+"the terms of version 2 of the GNU General Public License as published by the\n"
+"Free Software Foundation.\n\n"
+"This program is distributed in the hope that it will be useful, but WITHOUT ANY\n"
+"WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n"
+"PARTICULAR PURPOSE. See the GNU General Public License for details.\n";
+
+static char *msg =
+"Calculates possible elemental compositions for a given mass.\n\n"
+"usage: hr [options] file\n\nValid command line options are:\n"
+"-h      This Help screen.\n"
+"-v      Display version information.\n"
+"-t tol  Set tolerance to 'tol' mmu (default 5).\n"
+"-m mz   Set mass to 'mz'.\n"
+"-s		 Print only the results (dle)"
+"-c      Number of charges i.e -c 1 is equivalent to -p (dle).\n"
+"-p      Positive ions; electron mass is removed from the formula.\n"
+"-n      Negative ions; electron mass is added to the formula.\n"
+"-g      Does not apply 4-7 golden rules (dle).\n"
+"-a      Maximum num. of adducts.  (dle)\n"
+"-X a-b  For element X, use atom range a to b. List of valid atoms:\n\n"
+"           X    key   mass (6 decimals shown)\n"
+"        -------------------------------------\n";
+
+/* initialise variables */
+
+nadd = 0.0; /* added dle: number of adducts*/
+single = FALSE;			/* run continuously */
+charge = 0.0;	       	 	/* default charge is neutral */
+tol = 1.0;			/* default tolerance in mmu */
+nr_el = sizeof(el)/sizeof(el[0]);	/* calculate array size */
+verbose = TRUE;  /* added dle: should everything printed out?*/
+applygr = TRUE;   /* added dle: should rules 4-7 applied?*/
+
+
+/* decode and read the command line */
+
+while ((tmp = getopt(argc, argv, "hvpnsgt:m:c:a:C:H:N:M:O:D:1:S:F:L:B:P:I:A:K:E:G:J:")) != EOF)
+	switch (tmp)
+		{
+		case 'h':     	  		/* help me */
+			printf (id, VERSION);
+            printf (msg);
+			for (i=0; i < nr_el; i++)
+				printf ("        %4s     -%c %15.6lf\n",
+				el[i].sym, el[i].key, el[i].mass);
+			printf(disclaimer, "\n");
+			return 0;
+		case 'v':     	  		/* version */
+			printf (id, VERSION);
+			return 0;
+		case 'p':    			/* positive charge */
+			charge = +1.0;
+			continue;
+		case 's':    			/* simple output dle*/
+			verbose = FALSE;
+			continue;
+		case 'n':			/* negative carge */
+			charge = -1.0;
+			continue;
+		case 'g':			/* apply 7GR dle */
+			applygr = false;
+			continue;
+		case 't':       		/* tolerance */
+			strcpy(buf, optarg);
+			sscanf(buf, "%lf", &tol);
+			continue;
+		case 'a':       		/* num of adducts dle */
+			strcpy(buf, optarg);
+			sscanf(buf, "%lf", &nadd);
+			continue; 
+		case 'm':			/* single mass */
+			strcpy(buf, optarg);
+		        sscanf(buf, "%lf", &mz);
+		        single = TRUE;
+	        	continue;
+		case 'c':			/* comment for single mass */
+			strcpy(buf, optarg);
+			sscanf(buf, "%lf", &charge);
+			continue;
+		        continue;
+		case 'C':      		/* C12 */
+ 		case 'H':      		/* 1H */
+		case 'N':      		/* 14N */
+		case 'M':      		/* 15N */
+		case 'O':      		/* 16O */
+ 		case 'D':      		/* 2H */
+ 		case '1':      		/* 13C */
+		case 'A':      		/* Na ('N' is taken for Nitrogen!) */
+		case 'S':      		/* 32S */
+		case 'F':      		/* 19F */
+		case 'L':      		/* 35Cl ('C' is taken!) */
+		case 'E':      		/* 37Cl (chloridE!) */
+		case 'B':      		/* 79Br */
+		case 'K':      		/* 39K */
+		case 'J':      		/* 41K */
+		case 'G':      		/* 81Br */
+		case 'P':      		/* 31P */
+		case 'I':      		/* 28Si ('S' is taken!) */
+			i = 0;
+			/* compare keys until found */
+			while ((i < nr_el) && (el[i].key != tmp))
+				i++;
+			strcpy(buf, optarg);
+			sscanf(buf, "%d-%d", &el[i].min, &el[i].max);	/* copy over */
+			if (el[i].min > el[i].max)			/* swap them */
+				{
+				tmp = el[i].min;
+				el[i].min = el[i].max;
+				el[i].max = tmp;
+				}
+
+			/* printf ("\n %c = %c ... %s (%d-%d)", \
+			tmp, el[i].key, el[i].sym, el[i].min, el[i].max); */
+			continue;
+		case '~':    	  	/* invalid arg */
+		default:
+			printf ("'%s -h' for help.\n", argv[0]);
+			return 1;
+		}
+
+if (argv[optind] != NULL)	 /* remaining parameter on cmd line? */
+	/* must be a file -- treat it line by line */
+	return (readfile (argv[optind]));
+
+if (single == TRUE)  	   	 	/* only one calculation requested? */
+	do_calculations(mz, tol);       /* do it, then exit ... */
+else
+	{				/* otherwise run a loop */
+	while (input(comment, &mz))
+		{
+		tmp = do_calculations(mz, tol);
+		printf("\n");
+		}
+	}
+
+return 0;
+}
+
+
+/***************************************************************************
+* INPUT:	reads a dataset in "dialog mode".			   *
+* Input: 	Pointer to comment text, pointer to mass.		   *
+* Returns:	Result of sscanf(), 0 if prog is to be finished. 	   *
+* Note:		This is a fairly primitive way to do it, but it works ;-)  *
+****************************************************************************/
+int input(char *txt, double *zahl)
+{
+char buf[MAXLEN];				/* input line */
+
+*zahl = 0.0;				    /* reset */
+
+printf("Comment             : ");	/* display prompt */
+fgets(buf, MAXLEN-1, stdin);	/* read line */
+buf[MAXLEN] = 0x0;				/* terminate string */
+clean (buf);				    /* remove linefeed */
+strcpy(txt, buf);			    /* copy text over */
+
+printf("Mass (ENTER to quit): ");	/* display prompt */
+fgets(buf, MAXLEN-1, stdin);       	/* read line */
+buf[MAXLEN] = 0x0;			    /* terminate string */
+if (!clean (buf))			    /* only a CR ? --> quit */
+	return 0;
+sscanf(buf,"%lf", zahl);		/* scan string */
+
+return 1;
+}
+
+
+/***************************************************************************
+* READFILE:	reads dataset from file.				   *
+* Input: 	Pointer to comment text, pointer to mass.		   *
+* Returns:	0 if OK, 1 if error.					   *
+****************************************************************************/
+int readfile(char *whatfile)
+{
+double mz;		/* measured mass */
+char buf[MAXLEN];		/* input line */
+FILE *infile;
+
+infile = fopen(whatfile, "r");
+if (NULL == infile)
+	{
+	fprintf (stderr, "Error: Cannot open %s.", whatfile);
+	return 1;
+	}
+
+while (fgets(buf, MAXLEN-1, infile))
+	{
+	buf[MAXLEN] = 0x0;			/* terminate string */
+	if (*buf == ';')		/* comment line */
+		continue;
+	if (!clean (buf))		/* only a CR ? --> quit */
+		return 0;
+	sscanf(buf,"%s %lf", comment, &mz);	/* scan string */
+	do_calculations(mz, tol);
+	mz = 0.0;				/* reset */
+	}
+return 0;
+}
+
+
+/************************************************************************
+* CALC_MASS:	Calculates mass of an ion from its composition.	  	*
+* Input: 	nothing (uses global variables) 	      		*
+* Returns. 	mass of the ion.	  				*
+* Note:		Takes care of charge and electron mass!   		*
+* 		(Positive charge means removal of electrons).	 	*
+*************************************************************************/
+double calc_mass(void)
+{
+int i;
+double sum = 0.0;
+
+for (i=0; i < nr_el; i++)
+	sum += el[i].mass * el[i].cnt;
+
+return (sum - (charge * electron));
+}
+
+
+/************************************************************************
+* CALC_RDB:	Calculates rings & double bond equivalents.    		*
+* Input: 	nothing (uses global variables)			   	*
+* Returns. 	RDB.				       			*
+*************************************************************************/
+float calc_rdb(void)
+{
+int i;
+float sum = 2.0;
+
+for (i=0; i < nr_el; i++)
+	sum += el[i].val * el[i].cnt;
+
+return (sum/2.0);
+}
+/************************************************************************
+* Calculates element ratios , CH2 (more than 8 electrons needed is not handled)  		
+* Calculations element probabilities if element_probability = true 
+* Input: 	nothing (uses global variables)			   	
+* Returns. true/false.				       			
+*************************************************************************/
+bool calc_element_ratios(bool element_probability)
+{
+bool CHNOPS_ok;	
+float HC_ratio;
+float NC_ratio;
+float OC_ratio;
+float PC_ratio;
+float SC_ratio;
+
+/* added the number of isotopes to the calculation - dle*/
+float C_count = (float)el[0].cnt+(float)el[1].cnt; // C_count=12C+13C
+float H_count = (float)el[2].cnt+(float)el[3].cnt;
+float N_count = (float)el[4].cnt+(float)el[5].cnt;
+/* modif end here */
+float O_count = (float)el[6].cnt;
+float P_count = (float)el[10].cnt;
+float S_count = (float)el[11].cnt;
+
+
+		/* ELEMENT RATIOS allowed
+			MIN		MAX (99.99%)
+		H/C	0.07	6.00
+		N/C	0.00	4.00
+		O/C	0.00	3.00
+		P/C	0.00	2.00
+		S/C	0.00	6.00
+		*/	
+
+	// set CHNOPS_ok = true and assume all ratios are ok
+	CHNOPS_ok = true;
+	/*element_probability = false;	 */
+	
+	
+	if (C_count && H_count >0)					// C and H  must have one count anyway (remove for non-organics//
+	{	
+		HC_ratio = H_count/C_count;
+		if (element_probability)
+		{
+			if ((HC_ratio <  0.2) || (HC_ratio >  3.0)) // this is the H/C probability check ;
+			CHNOPS_ok = false;
+		}
+		else if (HC_ratio >  6.0) // this is the normal H/C ratio check - type cast from int to float is important
+			CHNOPS_ok = false;
+	}
+
+	if (N_count >0)	// if positive number of nitrogens then thes N/C ratio else just calc normal
+	{
+		NC_ratio = N_count/C_count;
+		if (element_probability)
+		{
+			if (NC_ratio >  2.0) // this is the N/C probability check ;
+			CHNOPS_ok = false;
+		}
+		else if (NC_ratio >  4.0)
+			CHNOPS_ok = false;
+	}	
+	
+	if (O_count >0)	// if positive number of O then thes O/C ratio else just calc normal
+	{	
+		OC_ratio = O_count/C_count;
+		if (element_probability)
+		{
+			if (OC_ratio >  1.2) // this is the O/C  probability check ;
+			CHNOPS_ok = false;		
+		}
+		else if (OC_ratio >  3.0)
+				CHNOPS_ok = false;
+	}	
+
+	if (P_count >0)	// if positive number of P then thes P/C ratio else just calc normal
+	{	
+		PC_ratio = 	P_count/C_count;
+		if (element_probability)
+		{
+			if (PC_ratio >  0.32) // this is the P/C  probability check ;
+			CHNOPS_ok = false;	
+		
+		}
+		else if (PC_ratio >  6.0)
+			CHNOPS_ok = false;
+	}	
+
+	if (S_count >0)	// if positive number of S then thes S/C ratio else just calc normal
+	{	
+		SC_ratio = 	S_count/C_count;
+		if (element_probability)
+		{
+			if (SC_ratio >  0.65) // this is the S/C  probability check ;
+			CHNOPS_ok = false;	
+		}
+		else if (SC_ratio >  2.0)
+			CHNOPS_ok = false;
+	}	
+
+//-----------------------------------------------------------------------------	
+		
+	// check for multiple element ratios together with probability check 
+	//if N<10, O<20, P<4, S<3 then true
+	if (element_probability && (N_count > 10) && (O_count > 20) && (P_count > 4) && (S_count > 1))
+		CHNOPS_ok = false;	
+	
+	// NOP check for multiple element ratios together with probability check
+	// NOP all > 3 and (N<11, O <22, P<6 then true)
+	if (element_probability && (N_count > 3) && (O_count > 3) && (P_count > 3))
+		{
+		if (element_probability && (N_count > 11) && (O_count > 22) && (P_count > 6))
+			CHNOPS_ok = false;	
+		}
+	
+	// OPS check for multiple element ratios together with probability check
+	// O<14, P<3, S<3 then true
+	if (element_probability && (O_count > 14) && (P_count > 3) && (S_count > 3))
+		CHNOPS_ok = false;	
+
+	// PSN check for multiple element ratios together with probability check
+	// P<3, S<3, N<4 then true
+	if (element_probability && (P_count > 3) && (S_count > 3) && (N_count >4))
+		CHNOPS_ok = false;	
+
+	
+	// NOS check for multiple element ratios together with probability check
+	// NOS all > 6 and (N<19 O<14 S<8 then true)
+	if (element_probability && (N_count >6) && (O_count >6) && (S_count >6))
+	{
+		if (element_probability && (N_count >19) && (O_count >14) && (S_count >8))
+			CHNOPS_ok = false;	
+	}	
+
+	// function return value;
+	if (CHNOPS_ok == true)
+		return true;
+	else 
+		return false;
+}
+
+/************************************************************************
+* DO_CALCULATIONS: Does the actual calculation loop.			*
+* Input: 	   measured mass (in amu), tolerance (in mmu)	    	*
+* Returns. 	   number of hits.	       				*
+*************************************************************************/
+long do_calculations (double measured_mass, double tolerance)
+{
+time_t start, finish;
+double elapsed_time;
+
+double mass;			/* calc'd mass */
+double limit_lo, limit_hi;	/* mass limits */
+float rdb, lewis, rdbori;			/* Rings & double bonds */
+long i,j;			
+long long hit;		/* counts the hits, with long declaration, overflow after 25h with all formulas < 2000 Da
+							    long = FFFFFFFFh = 4,294,967,295d*/
+int niso;
+long long counter;
+bool elementcheck;
+bool set_break; 
+
+
+time( &start );		// start time
+
+/* calculate limits */
+/* correct m/z value 'measured_mass' for z>1 using 'abscharge'; 
+keep charge = 0 for neutral mass searches (z=0);
+additionally, keep the idea of 'adducts' for 'rdb', but use charge state instead -meb */
+if (charge<0) {
+	abscharge = -charge;
+	nadd = abscharge;
+} else if (charge>0) {
+	abscharge = charge;
+	nadd = abscharge;
+} else if (charge==0) {
+	abscharge = 1;			/* for limits */
+	nadd = 0;				/* for rdb */
+}
+
+limit_lo = (measured_mass * abscharge) - (tolerance / 1000.0);
+limit_hi = (measured_mass * abscharge) + (tolerance / 1000.0);
+
+if(verbose) /* extended output as in original code - dle */
+{
+    if (strlen(comment))	/* print only if there is some text to print */
+	   printf ("Text      \t%s\n", comment);
+
+	printf("\n");		/* linefeed */
+	printf ("Composition =  \t");
+	for (i=0; i < nr_el; i++)
+		if (el[i].max > 0)
+			printf("%s:%d-%d ", el[i].sym, el[i].min, el[i].max);
+	printf ("\n");
+	
+	printf ("Mass Tolerance [mmu]:  \t%.1f\n",tolerance);
+	printf ("Measured Mass:  \t%.6lf\n", measured_mass);
+	printf ("Charge:  \t%+.0lf\n", charge);  /* dle */
+	printf ("Num. of Adducts/Losses:  \t%.0lf\n", nadd);  /* dle */
+	printf ("Apply 7GR:  \t%d\n\n", applygr);  /* dle */
+}
+
+hit = 0;			/* Reset counter */
+counter = 0;
+set_break = false;	/* set breaker for element counts to false */
+
+/* Now let's run the big big loop ... I'd like to do that
+   recursively but did not yet figure out how ;-) 
+   TK Adds: the loop is just fine.
+*/
+
+/* now comes the "COOL trick" for calculating all formulae:
+sorting the high mass elements to the outer loops, the small weights (H)
+to the inner loops;
+
+This will reduce the computational time by factor ~10-60-1000
+OLD HR: Cangrelor at 1ppm  4465 formulas found in   5866 seconds.
+NEW HR2: Cangrelor at 1ppm 4465 formulas found in     96 seconds.
+NEW2 HR2: Cangrelor at 1ppm 4465 formulas found in     60 seconds.
+NEW3 HR2: Cangrelor at 1ppm 4465 formulas found in     59 seconds.
+HR2 Fast: Cangrelor at 1ppm 4465 formulas found in     41 seconds by evaluating 2,003,436,894 formulae.
+hr2 -c "Cangrelor" -m  774.948 -t 0.77 -C 1-64 -H 1-112 -N 0-30 -O 0-80 -P 0-12 -S 0-9 -F 0-10 -L 0-10
+
+Another additional trick is to end the 2nd.. 3rd.. 4th.. xth innermost loop
+to prevent loops which are just higher and higher in mass.
+*/
+
+ /*dle: process new elements */
+el[17].cnt = el[17].min - 1;  el[17].save = el[17].cnt; 
+while (el[17].cnt++ < el[17].max) /*"iK"*/{ 
+	 
+el[16].cnt = el[16].min - 1;  el[16].save = el[16].cnt; 
+while (el[16].cnt++ < el[16].max) /*"K"*/{ 
+	 
+el[15].cnt = el[15].min - 1;  el[15].save = el[15].cnt; 
+while (el[15].cnt++ < el[15].max) /* "iBr"*/ { 
+
+el[14].cnt = el[14].min - 1;  el[14].save = el[14].cnt; 
+while (el[14].cnt++ < el[14].max) /* "Br"*/ { 
+
+el[13].cnt = el[13].min - 1;  el[13].save = el[13].cnt; 
+while (el[13].cnt++ < el[13].max) /*"iCl"*/ { 
+
+el[12].cnt = el[12].min - 1;  el[12].save = el[12].cnt; 
+while (el[12].cnt++ < el[12].max) /*"Cl"*/ { 
+
+el[11].cnt = el[11].min - 1;  el[11].save = el[11].cnt; 
+while (el[11].cnt++ < el[11].max) /*"S"*/ { 
+	 
+el[10].cnt = el[10].min - 1;  el[10].save = el[10].cnt; 
+while (el[10].cnt++ < el[10].max) /*"P"*/ { 
+	 
+el[9].cnt = el[9].min - 1;  el[9].save = el[9].cnt; 
+while (el[9].cnt++ < el[9].max) /*"Si"*/ { 
+
+el[8].cnt = el[8].min - 1;  el[8].save = el[8].cnt; 
+while (el[8].cnt++ < el[8].max) /*"Na"*/{ 
+
+el[7].cnt = el[7].min - 1;  el[7].save = el[7].cnt; 
+while (el[7].cnt++ < el[7].max) /*"F"*/ { 
+ 
+el[6].cnt = el[6].min - 1;  el[6].save = el[6].cnt; 
+while (el[6].cnt++ < el[6].max) /*"O"*/ { 
+	 
+el[5].cnt = el[5].min - 1;  el[5].save = el[5].cnt; 
+while (el[5].cnt++ < el[5].max) /*"15N"*/{ 
+
+el[4].cnt = el[4].min - 1;  el[4].save = el[4].cnt; 
+while (el[4].cnt++ < el[4].max) /*"N"*/{ 
+	 
+el[1].cnt = el[1].min - 1; el[1].save = el[1].cnt; 
+while (el[1].cnt++ < el[1].max) /*"13C"*/ { 
+
+el[0].cnt = el[0].min - 1; el[0].save = el[0].cnt; 
+while (el[0].cnt++ < el[0].max) /* "C"*/ { 
+
+el[3].cnt = el[3].min - 1; 	el[3].save = el[3].cnt; 
+while (el[3].cnt++ < el[3].max) /*"D"*/{ 
+
+el[2].cnt = el[2].min - 1; el[2].save = el[2].cnt; 
+while (el[2].cnt++ < el[2].max) /*"H"*/{ 
+
+	mass = calc_mass();
+	counter++;
+
+	//just for debug purposes
+	//if (mass > limit_hi)  
+	//printf("mass: %f\tC: %d  H: %d  N: %d O: %d P: %d S: %d Cl: %d Br: %d\n",mass,el[0].cnt,el[2].cnt,el[4].cnt,el[6].cnt,el[10].cnt,el[11].cnt,el[12].cnt,el[13].cnt);
+    	
+	/* if we exceed the upper limit, we can stop the calculation
+       for this particular element (JHa 20050227). <-- comment TK that will only bust the innermost while loop, which is "H"*/
+
+	// break H loop 
+	if (mass > limit_hi)  break;
+
+    //************************************************************************************************************/	
+	//Calculus loop with print out
+	//************************************************************************************************************/	
+
+	if ((mass >= limit_lo) && (mass <= limit_hi)) /* within limits? */
+	{	
+		// element check will be performed always, if variable bool element_probability is true also probabilities will be calculated
+		// not an elegant implementation, but fast.
+			
+		 elementcheck = calc_element_ratios(applygr);  /* pass applygr boolean by dle */
+		   
+		 if (elementcheck)
+		{ 
+		rdbori = calc_rdb();	/* get RDB */
+
+		rdb = rdbori + 0.5*nadd; /* dle: if nadd addcuts */
+		lewis = (float)(fmod(rdb, 1)); /*calc reminder*/
+		if ((rdb >= 0) && (lewis != 0.5) && (lewis !=-0.5))/* less than -0.5 RDB does not make sense */
+			{													/* NO(!) CH3F10NS2 exists , RDB =  -4.0   M= 282.9547*/
+			hit ++;
+			for (i = 0; i < nr_el; i++)			/* print composition */
+			    if (el[i].cnt > 0)				/* but only if useful */
+				  printf("%s%d.", el[i].sym, el[i].cnt);	// print formula
+				  
+			printf("\t");
+	/* dle: print out a more explicit molecular formula for further processing and
+	 variable niso counts number of isotope elements in the solution */
+			niso=0;
+			for (i = 0; i < nr_el; i++)
+			{			/* print composition */
+			    if (el[i].cnt > 0)
+			    {				/* but only if useful */
+				  printf("%s%d", el[i].symi, el[i].cnt);	// print formula
+				  if (el[i].symi != el[i].sym)
+				    niso=niso+el[i].cnt;
+			    }
+				
+			}
+    /* dle: end of molecular print out */
+    
+    /* dle: additionnaly print nadd and niso */
+		    printf("\t%.2f\t%.7lf\t%.0lf\t%d\t%+.2lf\n", rdb, mass, nadd,niso, 1000.0 * ((measured_mass * abscharge) - mass));
+				  
+			}	/* end of 'rdb' loop */
+
+		}	// end of elementcheck loop
+	
+	}	/* end of 'limit' loop */
+	//************************************************************************************************************/
+		
+
+	/*
+	TK: if the current mass is larger than the limit the loop can be exited.
+	Each element must point to the element which is in use and before.
+	This is a static implementation which can be enhanced with a pointer chain to the lower element.
+	Actually now its only allowed for CHNSOP-Fl-Cl-Br-Si !!! Brute-force <> elegance :-)
+	*/
+		} /*"H"*/
+		
+		if ((mass >= limit_lo) && (el[2].save == el[2].cnt-1)) break;
+		} /*"D"*/
+		
+		if ((mass >= limit_lo) && (el[3].save == el[3].cnt-1)) break;  /* dle addons */
+		} /* "C"*/
+		
+		if ((mass >= limit_lo) && (el[0].save == el[0].cnt-1)) break;
+		} /*"13C"*/
+
+		if ((mass >= limit_lo) && (el[1].save == el[1].cnt-1)) break;  /* dle addons */
+		} /*"N"*/
+		
+        if ((mass >= limit_lo) && (el[4].save == el[4].cnt-1)) break;
+		} /*"15N"*/
+
+        if ((mass >= limit_lo) && (el[5].save == el[5].cnt-1)) break;  /* dle addons */
+		} /*"O"*/
+		
+	    if ((mass >= limit_lo) && (el[6].save == el[6].cnt-1)) break;
+		} /*"F"*/
+		
+	    if ((mass >= limit_lo) && (el[7].save == el[7].cnt-1)) break;
+		} /*"Na"*/
+		
+	    if ((mass >= limit_lo) && (el[8].save == el[8].cnt-1)) break;
+		}  /*"Si"*/
+		
+		if ((mass >= limit_lo) && (el[9].save == el[9].cnt-1)) break;
+		} /*"P"*/
+		
+		if ((mass >= limit_lo) && (el[10].save == el[10].cnt-1)) break;
+		} /*"S"*/
+		
+		if ((mass >= limit_lo) && (el[11].save == el[11].cnt-1)) break; 
+		} /*"Cl"*/
+		
+		if ((mass >= limit_lo) && (el[12].save == el[12].cnt-1)) break;
+		} /*"iCl"*/
+				
+		if ((mass >= limit_lo) && (el[13].save == el[13].cnt-1)) break;  /* dle addons */
+		} /*"Br"*/
+
+		if ((mass >= limit_lo) && (el[14].save == el[14].cnt-1)) break;
+		} /*"iBr"*/
+
+		if ((mass >= limit_lo) && (el[15].save == el[15].cnt-1)) break;	 /* dle addons */
+		} /*"K"*/
+
+		if ((mass >= limit_lo) && (el[16].save == el[16].cnt-1)) break;	 /* dle addons */
+		} /*"iK"*/
+
+/* close that giant loop thing started above */
+
+
+	
+
+time(&finish);		// stop timer
+elapsed_time = difftime(finish , start);	// calulate time difference
+
+if(verbose){
+	if (!hit)
+		printf("No matching combination found in %6.0f seconds.\n", elapsed_time );
+	else{
+		printf("\n%llu formulas found in %6.0f seconds by evaluating %llu formulae.\n",hit,elapsed_time,counter);
+		printf("RDBs are overloaded to maximum valence values (N=3,P=5,S=6).\n");
+	}
+}
+return hit;
+}
+
+
+/************************************************************************
+* CLEAN:	"cleans" a buffer obtained by fgets() 			*
+* Input: 	Pointer to text buffer					*
+* Returns:	strlen of buffer.   					*
+*************************************************************************/
+int clean (char *buf)
+{
+int i;
+
+for(i = 0;  i < strlen(buf);  i++)		/* search for CR/LF */
+	{
+	if(buf[i] == '\n' || buf[i] == '\r')
+		{
+		buf[i] = 0;			/* stop at CR or LF */
+		break;
+		}
+	}
+return (strlen(buf));
+}
+
+
+
+/***************************************************************************
+* GETOPT: Command line parser, system V style.
+*
+*  This routine is widely (and wildly) adapted from code that was
+*  made available by Borland International Inc.
+*
+*  Standard option syntax is:
+*
+*    option ::= SW [optLetter]* [argLetter space* argument]
+*
+*  where
+*    - SW is '-'
+*    - there is no space before any optLetter or argLetter.
+*    - opt/arg letters are alphabetic, not punctuation characters.
+*    - optLetters, if present, must be matched in optionS.
+*    - argLetters, if present, are found in optionS followed by ':'.
+*    - argument is any white-space delimited string.  Note that it
+*      can include the SW character.
+*    - upper and lower case letters are distinct.
+*
+*  There may be multiple option clusters on a command line, each
+*  beginning with a SW, but all must appear before any non-option
+*  arguments (arguments not introduced by SW).  Opt/arg letters may
+*  be repeated: it is up to the caller to decide if that is an error.
+*
+*  The character SW appearing alone as the last argument is an error.
+*  The lead-in sequence SWSW ("--") causes itself and all the rest
+*  of the line to be ignored (allowing non-options which begin
+*  with the switch char).
+*
+*  The string *optionS allows valid opt/arg letters to be recognized.
+*  argLetters are followed with ':'.  Getopt () returns the value of
+*  the option character found, or EOF if no more options are in the
+*  command line. If option is an argLetter then the global optarg is
+*  set to point to the argument string (having skipped any white-space).
+*
+*  The global optind is initially 1 and is always left as the index
+*  of the next argument of argv[] which getopt has not taken.  Note
+*  that if "--" or "//" are used then optind is stepped to the next
+*  argument before getopt() returns EOF.
+*
+*  If an error occurs, that is an SW char precedes an unknown letter,
+*  then getopt() will return a '~' character and normally prints an
+*  error message via perror().  If the global variable opterr is set
+*  to false (zero) before calling getopt() then the error message is
+*  not printed.
+*
+*  For example, if
+*
+*    *optionS == "A:F:PuU:wXZ:"
+*
+*  then 'P', 'u', 'w', and 'X' are option letters and 'A', 'F',
+*  'U', 'Z' are followed by arguments. A valid command line may be:
+*
+*    aCommand  -uPFPi -X -A L someFile
+*
+*  where:
+*    - 'u' and 'P' will be returned as isolated option letters.
+*    - 'F' will return with "Pi" as its argument string.
+*    - 'X' is an isolated option.
+*    - 'A' will return with "L" as its argument.
+*    - "someFile" is not an option, and terminates getOpt.  The
+*      caller may collect remaining arguments using argv pointers.
+***************************************************************************/
+int getopt(int argc, char *argv[], char *optionS)
+{
+static char *letP	= NULL;		/* remember next option char's location */
+static char SW		= '-';		/* switch character */
+
+int opterr = 1;				/* allow error message	*/
+unsigned char ch;
+char *optP;
+
+if (argc > optind)
+	{
+	if (letP == NULL)
+		{
+		if ((letP = argv[optind]) == NULL || *(letP++) != SW)
+			goto gopEOF;
+
+		if (*letP == SW)
+			{
+			optind++;
+			goto gopEOF;
+			}
+		}
+	if (0 == (ch = *(letP++)))
+		{
+		optind++;
+		goto gopEOF;
+		}
+	if (':' == ch  ||  (optP = strchr(optionS, ch)) == NULL)
+		goto gopError;
+	if (':' == *(++optP))
+		{
+		optind++;
+		if (0 == *letP)
+			{
+			if (argc <= optind)
+				goto  gopError;
+			letP = argv[optind++];
+			}
+		optarg = letP;
+		letP = NULL;
+	}
+	else
+	{
+	if (0 == *letP)
+		{
+		optind++;
+		letP = NULL;
+		}
+	optarg = NULL;
+	}
+	return ch;
+}
+
+gopEOF:
+	optarg = letP = NULL;
+	return EOF;
+
+gopError:
+	optarg = NULL;
+	errno  = EINVAL;
+	if (opterr)
+		perror ("Command line option");
+	return ('~');
+}
+
+/*
+List of elements sorted according to mass
+_______________________
+INo#	El	Mass
+2		H	1.007825032
+3		D	2.014101778
+0		C	12
+1		13C	13.00335484
+4		N	14.00307401
+5		15N	15.0001089
+6		O	15.99491462
+7		F	18.9984032
+8		Na	22.98976967
+9		Si	27.97692653
+10		P	30.97376151
+11		S	31.97207069
+12		Cl	34.96885271
+13		Br	78.9183376
+------------------------
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/conf.pm	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,259 @@
+package lib::conf ;
+
+use strict;
+use warnings ;
+use Exporter ;
+use Carp ;
+use Data::Dumper ;
+
+use vars qw($VERSION @ISA @EXPORT %EXPORT_TAGS);
+
+our $VERSION = "1.0" ;
+our @ISA = qw(Exporter) ;
+our @EXPORT = qw( as_conf get_value_from_conf check_path_and_file ) ;
+our %EXPORT_TAGS = ( ALL => [qw( as_conf get_value_from_conf )] ) ;
+
+=head1 NAME
+
+conf - A module for manage pfem conf file
+
+=head1 SYNOPSIS
+
+    use conf ;
+    my $object = conf->new() ;
+
+=head1 DESCRIPTION
+
+This module does manage conf file (extract all or selected fields)
+
+=head1 METHODS
+
+Methods are :
+
+=head2 METHOD new
+
+	## Description : new
+	## Input : $self
+	## Ouput : bless $self ;
+	## Usage : new() ;
+
+=cut
+## START of SUB
+sub new {
+    ## Variables
+    my $self={};
+    bless($self) ;
+    return $self ;
+}
+### END of SUB
+
+=head2 METHOD as_conf
+
+	## Description : permet de cr�er l'object conf � partir d'un fichier de conf de type KEY=VALUE
+	## Input : $file
+	## Ouput : $oConf (a hash)
+	## Usage : my ( $oConf ) = as_conf( $file ) ;
+	
+=cut
+## START of SUB
+sub as_conf {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $file, $separator ) = @_ ;
+    
+#    if (!defined $separator) { $separator = ';' } ## set separator to ;
+    
+    if ( !defined $file )  {  croak "Can't create object with an none defined file\n" ; }
+    
+    my %Conf = () ; ## Hash devant contenir l'ensemble des parametres locaux
+	
+	if (-e $file) {
+		open (CFG, "<$file") or die "Can't open $file\n" ;
+		while (<CFG>) {
+			chomp $_ ;
+			if ( $_ =~ /^#(.*)/)  {	next ; }
+			elsif ($_ =~/^(\w+?)=(.*)/) { ## ALPHANUMERIC OR UNDERSCORE ONLY FOR THE KEY AND ANYTHING ELSE FOR VALUE
+				
+				my ($key, $value) = ($1, $2) ;
+				
+				if (defined $separator) {
+					if ( $value=~/$separator/ ) { ## is a list to split
+						my @tmp = split(/$separator/ , $value) ;
+						$Conf{$key} = \@tmp ;
+					}
+				}
+				else {
+					$Conf{$key} = $value ;
+				}
+			}
+		}
+		close(CFG) ;
+	}
+	else { 
+		croak "Can't create object with an none existing file\n" ;
+	}
+	
+    return ( \%Conf ) ;
+}
+## END of SUB
+
+=head2 METHOD as_conf_list
+
+	## Description : permet de charger une liste txt en array
+	## Input : $file
+	## Output : elements
+	## Usage : my ( elements ) = as_conf_list( $conf_file ) ;
+	
+=cut
+## START of SUB
+sub as_conf_list {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $file ) = @_ ;
+    
+    my @elements = () ;
+    if ( !defined $file )  {  croak "Can't create object with an none defined file\n" ; }
+    
+    if (-e $file) {
+		open (CFG, "<$file") or die "Can't open $file\n" ;
+		while (<CFG>) {
+			chomp $_ ;
+			if ( $_ =~ /^#(.*)/)  {	next ; }
+			elsif ($_ =~/^(.*)/) { if (defined $1) { push (@elements, $1) ; } 	}
+		}
+    }
+    else {
+		croak "Can't create object with an none existing file\n" ;
+	}
+    return(\@elements) ;
+}
+## END of SUB
+
+=head2 METHOD get_value_from_conf
+
+	## Description : permet de retourner une valeur du hash de conf � partir d'une key
+	## Input : $oConf, $Key
+	## Ouput : $Value
+	## Usage : my ( $Value ) = get_value_from_conf( $oConf, $Key ) ;
+	
+=cut
+## START of SUB
+sub get_value_from_conf {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $oConf, $Key ) = @_ ;
+    
+    my $Value = undef ;
+    
+    if ( defined $oConf ) {
+    	if ( defined $oConf->{$Key} ) {
+    		$Value = $oConf->{$Key} ;
+    	}
+    }
+    else {
+    	croak "Can't manage value with undefined object\n" ;
+    }
+    return($Value) ;
+}
+## END of SUB
+
+=head2 METHOD get_value_from_conf
+
+	## Description : permet de retourner une valeur du hash de conf � partir d'une key
+	## Input : $oConf, $Key
+	## Ouput : $Value
+	## Usage : my ( $Value ) = get_value_from_conf( $oConf, $Key ) ;
+	
+=cut
+## START of SUB
+sub split_value_from_conf {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $oConf, $Key, $sep ) = @_ ;
+    
+    my $value = undef ;
+    my @values = () ;
+    
+    if ( defined $oConf ) {
+    	if ( defined $oConf->{$Key} ) {
+    		$value = $oConf->{$Key} ;
+    		@values = split ( /$sep/, $value) ;
+    	}
+    }
+    else {
+    	croak "Can't manage value with undefined object\n" ;
+    }
+    return(\@values) ;
+}
+## END of SUB
+
+
+=head2 METHOD check_path_and_file
+
+	## Description : permet de v�rifier les path et la pr�sence des exe d�crits dans le file conf. Bloque le script en cas de probleme
+	## Input : $oConfs
+	## Ouput : NA
+	## Usage : &get_value_from_conf( $oConf ) ;
+	
+=cut
+## START of SUB
+sub check_path_and_file {
+	
+	my $self = shift ;
+	my ( $oConfs ) = @_ ;
+	
+	foreach my $conf ( keys %{ $oConfs } ) {
+		if ( $conf =~ /^FILE/ ) {
+			if ( -e $oConfs->{$conf} ) {
+				if ( -s $oConfs->{$conf} ) { next ; }
+				else { carp "[Warning] : The size of file $oConfs->{$conf} is null\n" ; }
+			}
+			else {
+					carp "[Warning] : The file $oConfs->{$conf} doesn't exist\n" ;
+			}
+		}
+		elsif ( $conf =~ /^PATH/ ) {
+			if ( -d $oConfs->{$conf} ) { next ; }
+			else { carp "[Warning] :  The dir $oConfs->{$conf} doesn't exist\n" ;	}
+		}
+		else { 	next ; 	}
+	}
+	return ;
+}
+## END of SUB
+
+1 ;
+
+
+__END__
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc conf.pm
+
+
+=head1 Exports
+
+=over 4
+
+=item :ALL is as_conf get_value_from_conf
+
+=back
+
+=head1 AUTHOR
+
+Franck Giacomoni E<lt>franck.giacomoni@clermont.inra.frE<gt>
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=head1 VERSION
+
+version 1 : 10 / 02 / 2013
+
+version 2 : ??
+
+=cut
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/csv.pm	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,282 @@
+package lib::csv ;
+
+use strict;
+use warnings ;
+use Exporter ;
+use Carp ;
+
+use Text::CSV ;
+
+use Data::Dumper ;
+
+use vars qw($VERSION @ISA @EXPORT %EXPORT_TAGS);
+
+our $VERSION = "1.0";
+our @ISA = qw(Exporter);
+our @EXPORT = qw( get_csv_object get_value_from_csv );
+our %EXPORT_TAGS = ( ALL => [qw( get_csv_object get_value_from_csv )] );
+
+=head1 NAME
+
+My::Module - An example module
+
+=head1 SYNOPSIS
+
+    use My::Module;
+    my $object = My::Module->new();
+    print $object->as_string;
+
+=head1 DESCRIPTION
+
+This module does not really exist, it
+was made for the sole purpose of
+demonstrating how POD works.
+
+=head1 METHODS
+
+Methods are :
+
+=head2 METHOD new
+
+	## Description : new
+	## Input : $self
+	## Ouput : bless $self ;
+	## Usage : new() ;
+
+=cut
+
+sub new {
+    ## Variables
+    my $self={};
+    bless($self) ;
+    return $self ;
+}
+### END of SUB
+
+=head2 METHOD get_csv_object
+
+	## Description : builds a csv object and etablishes format
+	## Input : $separator
+	## Output : $csv
+	## Usage : my ( $csv ) = get_csv_object( $separator ) ;
+	
+=cut
+## START of SUB
+sub get_csv_object {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $separator ) = @_ ;
+    
+#    my $csv = Text::CSV->new({'sep_char' => "$separator"});
+    my $csv = Text::CSV->new ( {'sep_char' => "$separator", binary => 1 } )  # should set binary attribute.
+    	or die "Cannot use CSV: ".Text::CSV->error_diag ();
+    
+    return($csv) ;
+}
+## END of SUB
+
+=head2 METHOD get_value_from_csv
+
+	## Description : extract a targeted column in a csv file 
+	## Input : $csv, $file, $column, $is_header
+	## Output : $value
+	## Usage : my ( $value ) = get_value_from_csv( $csv, $file, $column, $is_header ) ;
+	
+=cut
+## START of SUB
+sub get_value_from_csv {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv, $file, $column, $is_header ) = @_ ;
+    
+    my @value = () ;
+    
+    ## Adapte the number of the colunm : (nb of column to position in array)
+	$column = $column - 1 ;
+    
+    open (CSV, "<", $file) or die $! ;
+	
+	my $line = 0 ;
+	
+	while (<CSV>) {
+		$line++ ;
+	    chomp $_ ;
+		# file has a header
+		if ( defined $is_header ) { if ($line == 1) { next ; } }
+		# parsing the targeted column
+	    if ( $csv->parse($_) ) {
+	        my @columns = $csv->fields();
+	        push ( @value, $columns[$column] ) ;
+	    }
+	    else {
+	        my $err = $csv->error_input;
+	        die "Failed to parse line: $err";
+	    }
+	}
+	close CSV;
+    return(\@value) ;
+}
+## END of SUB
+
+=head2 METHOD get_value_from_csv_multi_header
+
+	## Description : extract a targeted column in a csv file 
+	## Input : $csv, $file, $column, $is_header, $nb_header
+	## Output : $value
+	## Usage : my ( $value ) = get_value_from_csv_multi_header( $csv, $file, $column, $is_header, $nb_header ) ;
+	
+=cut
+## START of SUB
+sub get_value_from_csv_multi_header {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv, $file, $column, $is_header, $nb_header ) = @_ ;
+    
+    my @value = () ;
+    
+    ## Adapte the number of the colunm : (nb of column to position in array)
+	$column = $column - 1 ;
+    
+    open (CSV, "<", $file) or die $! ;
+	
+	my $line = 0 ;
+	
+	while (<CSV>) {
+		$line++ ;
+	    chomp $_ ;
+		# file has a header
+		if ( defined $is_header and $is_header eq 'yes') { if ($line <= $nb_header) { next ; } }
+		# parsing the targeted column
+	    if ( $csv->parse($_) ) {
+	        my @columns = $csv->fields();
+	        push ( @value, $columns[$column] ) ;
+	    }
+	    else {
+	        my $err = $csv->error_input;
+	        die "Failed to parse line: $err";
+	    }
+	}
+	close CSV;
+    return(\@value) ;
+}
+## END of SUB
+
+=head2 METHOD parse_csv_object
+
+	## Description : parse_all csv object and return a array of rows
+	## Input : $csv, $file
+	## Output : $csv_matrix
+	## Usage : my ( $csv_matrix ) = parse_csv_object( $csv, $file ) ;
+	
+=cut
+## START of SUB
+sub parse_csv_object {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv, $file ) = @_ ;
+    
+    my @csv_matrix = () ;
+    
+	open my $fh, "<:encoding(utf8)", $$file or die "Can't open csv file $$file: $!";
+	
+	while ( my $row = $csv->getline( $fh ) ) {
+	    push @csv_matrix, $row;
+	}
+	$csv->eof or $csv->error_diag();
+	close $fh;
+    
+    return(\@csv_matrix) ;
+}
+## END of SUB
+
+=head2 METHOD parse_allcsv_object
+
+	## Description : parse_all csv object and return a array of rows with or without header
+	## Input : $csv, $file, $keep_header
+	## Output : $csv_matrix
+	## Usage : my ( $csv_matrix ) = parse_csv_object( $csv, $file, $keep_header ) ;
+	
+=cut
+## START of SUB
+sub parse_allcsv_object {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv, $file, $keep_header ) = @_ ;
+    
+    my @csv_matrix = () ;
+    my $line = 1 ;
+    
+	open my $fh, "<:encoding(utf8)", $$file or die "Can't open csv file $$file: $!";
+	
+	while ( my $row = $csv->getline( $fh ) ) {
+		if ( ( $keep_header eq 'n' )  and ($line == 1) ) {  }
+		else { push @csv_matrix, $row; }
+	    $line ++ ;
+	}
+	my $status = $csv->eof or $csv->error_diag();
+	close $fh;
+    
+    return(\@csv_matrix, $status) ;
+}
+## END of SUB
+
+
+=head2 METHOD write_csv_from_arrays
+
+	## Description : write a csv file from list of rows
+	## Input : $csv, $file_name, $rows
+	## Output : $csv_file
+	## Usage : my ( $csv_file ) = write_csv_from_arrays( $csv, $file_name, $rows ) ;
+	
+=cut
+## START of SUB
+sub write_csv_from_arrays {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv, $file_name, $rows ) = @_ ;
+    
+    my $fh = undef ;
+    $csv->eol ("\n"); ##  end-of-line string to add to rows
+    open $fh, ">:encoding(utf8)", "$file_name" or die "$file_name: $!";
+    
+	my $status = $csv->print ($fh, $_) for @{$rows};
+	close $fh or die "$file_name: $!";
+    
+    return(\$file_name) ;
+}
+## END of SUB
+
+1 ;
+
+
+__END__
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc csv.pm
+
+=head1 Exports
+
+=over 4
+
+=item :ALL is get_csv_object, get_value_from_csv
+
+=back
+
+=head1 AUTHOR
+
+Franck Giacomoni E<lt>franck.giacomoni@clermont.inra.frE<gt>
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=head1 VERSION
+
+version 1 : 23 / 10 / 2013
+
+version 2 : ??
+
+=cut
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/hr.pm	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,775 @@
+package lib::hr ;
+
+use strict;
+no strict "refs" ;
+use warnings ;
+use Exporter ;
+use threads ;
+use HTML::Template ;
+use Carp ;
+
+use Data::Dumper ;
+
+use vars qw($VERSION @ISA @EXPORT %EXPORT_TAGS);
+
+our $VERSION = "1.0";
+our @ISA = qw(Exporter);
+our @EXPORT = qw( manage_atoms_and_ranges manage_atoms check_hr_exe manage_atom_and_range manage_tolerance manage_mode config_hr_exe );
+our %EXPORT_TAGS = ( ALL => [qw(manage_atoms_and_ranges manage_atoms check_hr_exe manage_atom_and_range manage_tolerance manage_mode config_hr_exe )] );
+
+=head1 NAME
+
+lib::hr - A module for managing / launching hr binary (structure elucidation c++ progr)
+
+=head1 SYNOPSIS
+
+    use lib::hr;
+    my $object = lib::hr->new();
+    print $object->as_string;
+
+=head1 DESCRIPTION
+
+This module does not really exist, it
+was made for the sole purpose of
+demonstrating how POD works.
+
+=head1 METHODS
+
+Methods are :
+
+=head2 METHOD new
+
+	## Description : new
+	## Input : $self
+	## Ouput : bless $self ;
+	## Usage : new() ;
+
+=cut
+
+sub new {
+    ## Variables
+    my $self={};
+    bless($self) ;
+    return $self ;
+}
+### END of SUB
+
+=head2 METHOD manage_atoms_and_ranges
+
+	## Description : allow from an initial config to add or delete atoms and their range
+	## Input : $atomsconfig, $atombasic, $atomsupp
+	## Output : $atomcleanconfig
+	## Usage : my ( $atomcleanconfig ) = manage_atoms_and_ranges ( $atomsconfig, $atombasic, $atomsupp ) ;
+	
+=cut
+## START of SUB
+sub manage_atoms_and_ranges {
+    ## Retrieve Values
+    my $self = shift ;
+    my ( $atomsconfig, $CONF, $atombasic, $atomsupp ) = @_;
+    my ( $atomcleanconfig ) = ( undef ) ;
+    
+    # basic atoms case:
+    foreach my $atom ( (split(",", $atombasic )) ) {
+    	if ( exists $CONF->{$atom} ) 	{ $atomsconfig->{$atom}{'max'} = $CONF->{$atom} ; }
+    }
+    
+    # suppl. atoms case
+    foreach my $atom ( (split(",", $atomsupp )) ) {
+    	print "*** $atom***\n" ;
+    	if ( exists $atomsconfig->{$atom} ) 	{ $atomsconfig->{$atom} = $CONF->{'DEFAULT_MAX'} ; }
+    }
+    
+    # Create atoms and range parameters:    
+    foreach my $selectedAtom ( keys %{$atomsconfig} ) {
+    	$atomcleanconfig .= ' -'.$selectedAtom.' '.$atomsconfig->{$selectedAtom}{'min'}.'-'.$atomsconfig->{$selectedAtom}{'max'} ;
+    }
+    
+    return ($atomcleanconfig) ;
+}
+### END of SUB
+
+
+     
+=head2 METHOD manage_atoms ### DEPRECATED
+
+	## Description : controles atoms input list and prepare it like hr binary parameter 
+	## Input : $input_atoms, $conf_atoms
+	## Output : $hr_atoms_param
+	## Usage : my ( $hr_atoms_param ) = manage_atoms( $input_atoms, $conf_atoms ) ;
+	### DEPRECATED
+	
+=cut
+## START of SUB
+sub manage_atoms { ### DEPRECATED
+	## Retrieve Values
+    my $self = shift ;
+    my ( $input_atoms, $conf_atoms ) = @_ ;
+    my $hr_atoms_param = undef ;
+    
+    if ( ( defined $$input_atoms ) and ( defined $$conf_atoms )  ) {
+    	if ( ( $$input_atoms eq 'None' ) or ( $$input_atoms eq '' ) or ( $$input_atoms eq ' ' ) )	{  $hr_atoms_param =  $$conf_atoms ; 	}
+    	elsif ( $$input_atoms =~ /[P|S|F|L|K|B|A|1|,]+/ ) { $hr_atoms_param =  $$conf_atoms.','.$$input_atoms ;  }
+    	else 							{  $hr_atoms_param =  $$conf_atoms ;  	}
+    } ## END IF
+    elsif ( !defined $$input_atoms ) { 	$hr_atoms_param =  $$conf_atoms ; }
+    elsif ( !defined $$conf_atoms )  { 	warn "hr module can't manage any atom list (undef values in conf)\n" ; }
+    else {						    	warn "hr module musn't manage any atom list\n" ; }
+    
+    return(\$hr_atoms_param) ;
+}
+## END of SUB
+
+=head2 METHOD manage_atom_and_range ### DEPRECATED
+
+	## Description : build atom range with defined value in conf file
+	## Input : $atom, $min, $max
+	## Output : $hr_range
+	## Usage : my (  ) = manage_atom_and_range( $atom, $min, $max ) ;
+	### DEPRECATED
+	
+=cut
+## START of SUB
+sub manage_atom_and_range { ### DEPRECATED
+	## Retrieve Values
+    my $self = shift ;
+    my ( $atom, $min, $max ) = @_ ;
+    my $hr_range = undef ;
+    
+    if ( ( defined $$atom ) and ( defined $$min ) and ( defined $$max )  ) {
+    	## manage ragne like "-C 0-200"
+    	$hr_range = ' -'.$$atom.' '.$$min.'-'.$$max ;
+    } ## END IF
+    else {
+    	warn "Some argvts are missing to build the current atom range line\n" ;
+    }    
+    return(\$hr_range) ;
+}
+## END of SUB
+
+=head2 METHOD manage_tolerance
+
+	## Description : check range and format of tolerance
+	## Input : $tolerance, $default_value
+	## Output : $set_tol
+	## Usage : my ( $set_tol ) = manage_tolerance( $tolerance, $default_value ) ;
+	
+=cut
+## START of SUB
+sub manage_tolerance {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $tolerance, $default_value ) = @_ ;
+    my ($set_tol, $tmp_tol ) = (undef, undef) ;
+        
+    if ( ( defined $$tolerance ) and ( defined $$default_value )) {
+    	$tmp_tol = $$tolerance ;
+    	$tmp_tol =~ tr/,/./;
+		## tolerance doit etre >0 et <10
+		if ( $tmp_tol <= 0 || $tmp_tol >= 10 ){
+			$set_tol = $$default_value ;
+			warn "The used tolerance is set to $$default_value (out of authorized range)\n" ;
+		}
+		else{ $set_tol = $tmp_tol ; }
+    }
+    else { 	warn "Your tolerance or the default tol are not defined\n" ;   }
+    
+    return(\$set_tol) ;
+}
+## END of SUB
+
+=head2 METHOD manage_mode
+
+	## Description : manage mode and apply mass correction (positive/negative/neutral)
+	## Input : $mode, $charge, $electron, $proton, $mass
+	## Output : $exact_mass
+	## Usage : my ( $exact_mass ) = manage_mode( $mode, $charge, $electron, $proton, $mass ) ;
+	
+=cut
+## START of SUB
+sub manage_mode {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $mode, $charge, $electron, $proton, $mass ) = @_ ;
+    my ($exact_mass, $tmp_mass) = ( undef, undef ) ;
+    
+    ## some explanations :
+    	# MS in + mode = adds H+ (proton) and molecule is positive : el+ => $charge = "positive"
+		# For HR, need to subtrack proton mz and to add electron mz (1 electron per charge) to the input mass which comes neutral!
+    
+    if ( ( defined $$electron ) and ( defined $$proton ) ) {
+    	# check mass
+    	if ( defined $$mass ) {  $tmp_mass = $$mass ;   $tmp_mass =~ tr/,/./ ; } # manage . and , in case of...
+    	else {	warn "No mass is defined\n"  	}
+    	
+    	# manage charge
+    	if ( ( !defined $$charge ) || ($$charge < 0) ){ warn "Charge is not defined or value is less than zero\n" ; }
+    	
+    	# set neutral mass in function of ms mode
+    	if($$mode eq 'positive')	{	$exact_mass = (	$tmp_mass - $$proton + $$electron) * $$charge ; }
+    	elsif($$mode eq 'negative')	{ 	$exact_mass = (	$tmp_mass + $$proton - $$electron) * $$charge ; }
+    	elsif($$mode eq "neutral")	{	$exact_mass = 	$tmp_mass ;  }
+	    else { 	warn "This mode doesn't exist : please select positive/negative or neutral mode\n" ; 	    }
+    }
+    else {
+    	warn "Missing some parameter values (electron, neutron masses), please check your conf file\n" ;
+    } 
+    return(\$exact_mass) ;
+}
+## END of SUB
+
+=head2 METHOD check_hr_exe
+
+	## Description : permit to check the path of hr.exe and its full availability
+	## Input : $hr_path, $hr_version
+	## Output : true/false
+	## Usage : my ( $res ) = check_hr_exe( $hr_path, $hr_version ) ;
+	
+=cut
+## START of SUB
+sub check_hr_exe {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $hr_path, $hr_version ) = @_ ;
+    my $success = undef ;
+    my $check_res = undef ;
+    
+    ## test path :
+    if ( ( defined $$hr_path ) and ( defined $$hr_version ) ) {
+   		if ( defined $$hr_path ) {
+   			$success = `$$hr_path -version`;
+   			print "$success\n" ;
+   			if ($success !~/^$$hr_version/) { 	warn "You do not use the expected version of hr2 ($$hr_version)\n" ;  }
+   			else { 								$check_res = 1 ; }
+   		}
+    	else { warn "Can't use HR because the binary file doesn't exist at the specified path ($$hr_path)\n" ; }
+    	
+    } ## END IF
+    else { 	warn "No HR path or Hr version defined\n" ;   }
+    
+    return($check_res) ;
+}
+## END of SUB
+
+=head2 METHOD config_hr_exe
+
+	## Description : builds hr execute line with needed params
+	## Input : $hr_path, $hr_delta, $mass, $has_goldenrules, $atoms_and_ranks
+	## Output : var2
+	## Usage : my ( var2 ) = config_hr_exe( $hr_path, $hr_delta, $mass, $has_goldenrules, $atoms_and_ranks ) ;
+	
+=cut
+## START of SUB
+sub config_hr_exe {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $hr_path, $hr_delta, $mass, $has_goldenrules, $atoms_and_ranks ) = @_ ;
+    my $hr_cmd = undef ;
+    
+    if ( ( defined $$hr_path ) and ( defined $$hr_delta ) and ( defined $$mass ) and ( defined $$atoms_and_ranks )  ) {
+    	$hr_cmd = $$hr_path.' -t '.$$hr_delta.' -m '.$$mass.' '.$$atoms_and_ranks ;
+    	if ( defined $$has_goldenrules ) { 	$$hr_cmd .= ' -g ' ;   	}
+    } ## END IF
+    else { 	warn "Some argvts are missing to build the current hr exec line\n" ; }
+    
+    return(\$hr_cmd) ;
+}
+## END of SUB
+
+=head2 METHOD threading_hr_exe
+
+	## Description : prepare 5 threads for hr executing
+	## Input : $method, $list
+	## Output : $results
+	## Usage : my ( $results ) = threading_hr_exe( $method, $list ) ;
+	
+=cut
+## START of SUB
+sub threading_hr_exe {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $method, $list ) = @_ ;
+    
+    my @results = () ;
+    
+    if ( ( defined $list ) and ( defined $method )) {
+    	
+    	for (my $i = 0; $i < (scalar @{$list}); $i+=6 ) {
+			my $thr1 = threads->create($method, $self, $list->[$i]) if $list->[$i] ;
+			my $thr2 = threads->create($method, $self, $list->[$i+1]) if $list->[$i+1] ;
+			my $thr3 = threads->create($method, $self, $list->[$i+2]) if $list->[$i+2] ;
+			my $thr4 = threads->create($method, $self, $list->[$i+3]) if $list->[$i+3] ;
+			my $thr5 = threads->create($method, $self, $list->[$i+4]) if $list->[$i+4] ;
+			my $thr6 = threads->create($method, $self, $list->[$i+5]) if $list->[$i+5] ;
+			push ( @results, $thr1->join )  if $list->[$i] ;
+			push ( @results, $thr2->join ) if $list->[$i+1] ;
+			push ( @results, $thr3->join ) if $list->[$i+2] ;
+			push ( @results, $thr4->join ) if $list->[$i+3] ;
+			push ( @results, $thr5->join ) if $list->[$i+4] ;
+			push ( @results, $thr6->join ) if $list->[$i+5] ;
+		}
+    }
+    else {
+    	warn "Your input list or your method is undefined\n" ;
+    }
+    
+    return(\@results) ;
+}
+## END of SUB
+
+=head2 METHOD hr_exe
+
+	## Description : hr_exe launches hr and catches result
+	## Input : $cmd
+	## Output : $res
+	## Usage : my ( $res ) = hr_exe( $cmd ) ;
+	
+=cut
+## START of SUB
+sub hr_exe {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $cmd ) = @_ ;
+    my $res = undef ;
+	
+	if (defined $cmd){
+		#print "\n--CMD used : $cmd\n" ;
+		$res = `$cmd` ;
+		sleep(0.5) ;
+		#print "Results : $res\n" ;
+	}
+
+	return (\$res) ;
+}
+## END of SUB
+
+
+=head2 METHOD hr_out_parser
+
+	## Description : parse output of hr and return a hash of features
+	## Input : $res
+	## Output : $parsed_res
+	## Usage : my ( $parsed_res ) = hr_out_parser( $res ) ;
+	
+=cut
+## START of SUB
+sub hr_out_parser {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $res ) = @_ ;
+    
+    my %parsed_res = () ;
+    my ( @formula, @rings_and_double_bond_equivalents, @formula_mz, @mmus ) = ( (), (), (), () ) ;
+    my ( $formula_nb, $formula_total, $time ) = ( undef, undef, undef ) ;
+    
+    if ( defined $$res ) {
+    	# foreach line
+    	foreach my $line (split(/\n/,$$res)){
+    		## v1.02 - parse result line "C7.H17.N5.		2.0	171.1484	+17.2 mmu"
+    		## v1.03 - parse result line "C10.H25.N5.O5.P2.S2.    C10H25N5O5P2S2  8.00    421.0772333     0       0       +0.40"
+    		## $1 = "C10.H25.N5.O5.P2.S2. " 	$2 = "C10H25N5O5P2S2" 	$3 = "8.00"		$4="421.0772333"	$5="0"	$6="0"	$7="+0.40"
+    		## if ( $line =~ /([\w|\.]+)\s+(\d+.?\d*)\s+(\d+.?\d*)\s+([+|-]\d+.?\d*)\s+(.*)/ ) { ## for hr2 1.02
+    		
+    		if ( $line =~ /([\w|\.]+)\s+(\w+)\s+(\d+.?\d*)\s+(\d+.?\d*)\s+(\d+.?\d*)\s+(\d+.?\d*)\s+([+|-]\d+.?\d*)/ ) { # for hr2 1.03
+    			my ( $formula, $cleanformula, $rings_and_double_bond_equivalent, $formula_mz, $abscharge, $nadd, $mmu_value  ) = ( $1, $2, $3, $4, $5, $6, $7 ) ;
+     			
+    			if (defined $formula ) 		{ $formula =~ s/\.//g ; 	push (@formula, $formula) ;	} # clean \.
+    			if (defined $rings_and_double_bond_equivalent ) {		push (@rings_and_double_bond_equivalents, $rings_and_double_bond_equivalent) ;	} # 
+    			if (defined $formula_mz ) 	{ 						 	push (@formula_mz, $formula_mz) ;	}
+    			if (defined $mmu_value ) 	{ $mmu_value =~ s/\+// ; 	push (@mmus, $mmu_value) ;	} # clean (+)
+    		}
+    		elsif (  $line =~ /(\d+)\s+formulas.+\s+(\d+)\s+seconds.+\s+(\d+)\s+formulae/ ) {
+    			( $formula_nb, $time, $formula_total  ) = ( $1, $2, $3 ) ;
+    		}
+    		else {	next;	}
+    	}
+    	# build parser
+    	if ( scalar(@formula) > 0 ){
+    		$parsed_res{'ENTRY_FORMULA'} = \@formula ; 
+	    	$parsed_res{'rings_and_double_bond_equivalents'} = \@rings_and_double_bond_equivalents ; 
+	    	$parsed_res{'ENTRY_CPD_MZ'} = \@formula_mz ; 
+	    	$parsed_res{'ENTRY_DELTA'} = \@mmus ; 
+	    	$parsed_res{'MASSES_TOTAL'} = \$formula_nb ; 
+	    	$parsed_res{'time'} = \$time ;
+    	}
+    }
+    return(\%parsed_res) ;
+}
+## END of SUB
+
+
+=head2 METHOD set_html_tbody_object
+
+	## Description : initializes and build the tbody object (perl array) need to html template
+	## Input : $nb_pages, $nb_items_per_page
+	## Output : $tbody_object
+	## Usage : my ( $tbody_object ) = set_html_tbody_object($nb_pages, $nb_items_per_page) ;
+	
+=cut
+## START of SUB
+sub set_html_tbody_object {
+	my $self = shift ;
+    my ( $nb_pages, $nb_items_per_page ) = @_ ;
+
+	my ( @tbody_object ) = ( ) ;
+	
+	for ( my $i = 1 ; $i <= $nb_pages ; $i++ ) {
+	    
+	    my %pages = ( 
+	    	# tbody feature
+	    	PAGE_NB => $i,
+	    	MASSES => [], ## end MASSES
+	    ) ; ## end TBODY N
+	    push (@tbody_object, \%pages) ;
+	}
+    return(\@tbody_object) ;
+}
+## END of SUB
+
+=head2 METHOD add_mz_to_tbody_object
+
+	## Description : initializes and build the mz object (perl array) need to html template
+	## Input : $tbody_object, $nb_items_per_page, $mz_list
+	## Output : $tbody_object
+	## Usage : my ( $tbody_object ) = add_mz_to_tbody_object( $tbody_object, $nb_items_per_page, $mz_list ) ;
+	
+=cut
+## START of SUB
+sub add_mz_to_tbody_object {
+	my $self = shift ;
+    my ( $tbody_object, $nb_items_per_page, $mz_list, $ids_list, $totals ) = @_ ;
+
+	my ( $current_page, $mz_index ) = ( 0, 0 ) ;
+	
+	foreach my $page ( @{$tbody_object} ) {
+		
+		my @colors = ('white', 'green') ;
+		my ( $current_index, , $icolor ) = ( 0, 0 ) ;
+		
+		for ( my $i = 1 ; $i <= $nb_items_per_page ; $i++ ) {
+			# 
+			if ( $current_index > $nb_items_per_page ) { ## manage exact mz per html page
+				$current_index = 0 ; 
+				last ; ##
+			}
+			else {
+				$current_index++ ;
+				if ( $icolor > 1 ) { $icolor = 0 ; }
+				
+				if ( exists $mz_list->[$mz_index]  ) {
+					my $total = \0 ;
+					if ( $totals->[$mz_index]{'MASSES_TOTAL'} ) { $total = $totals->[$mz_index]{'MASSES_TOTAL'} }					
+					
+					my %mz = (
+						# mass feature
+						MASSES_ID_QUERY => $ids_list->[$mz_index],
+						MASSES_MZ_QUERY => $mz_list->[$mz_index],
+						MZ_COLOR => $colors[$icolor],
+						MASSES_NB => $mz_index+1,
+						MASSES_TOTAL => $$total ,
+						ENTRIES => [] ,
+					) ;
+					push ( @{ $tbody_object->[$current_page]{MASSES} }, \%mz ) ;
+					# Html attr for mass
+					$icolor++ ;
+				}
+			}
+			$mz_index++ ;
+		} ## foreach mz
+
+		$current_page++ ;
+	}
+    return($tbody_object) ;
+}
+## END of SUB
+
+=head2 METHOD add_entries_to_tbody_object
+
+	## Description : initializes and build the mz object (perl array) need to html template
+	## Input : $tbody_object, $nb_items_per_page, $mz_list, $entries
+	## Output : $tbody_object
+	## Usage : my ( $tbody_object ) = add_entries_to_tbody_object( $tbody_object, $nb_items_per_page, $mz_list, $entries ) ;
+	
+=cut
+## START of SUB
+sub add_entries_to_tbody_object {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $tbody_object, $results ) = @_ ;
+    
+    my $index_page = 0 ;
+    my $index_mz_continous = 0 ;
+    
+    foreach my $page (@{$tbody_object}) {
+    	
+    	my $index_mz = 0 ;
+    	
+    	foreach my $mz (@{ $tbody_object->[$index_page]{MASSES} }) {
+    		
+    		my $index_res = 0 ;
+    		if ( $results->[$index_mz_continous]{ENTRY_FORMULA} ){
+	    			
+    			my $entry_nb = scalar( @{ $results->[$index_mz_continous]{ENTRY_FORMULA} } ) ;
+    			for( my $i = 0 ; $i<$entry_nb; $i++ ) {
+    				my %entry = (
+			    		ENTRY_COLOR 	=> $tbody_object->[$index_page]{MASSES}[$index_mz]{MZ_COLOR},
+			   			ENTRY_FORMULA 	=> $results->[$index_mz_continous]->{ENTRY_FORMULA}[$i],
+						ENTRY_CPD_MZ 	=> $results->[$index_mz_continous]->{ENTRY_CPD_MZ}[$i],
+						ENTRY_DELTA 	=> $results->[$index_mz_continous]->{ENTRY_DELTA}[$i]			
+			    	) ;
+		    		push ( @{ $tbody_object->[$index_page]{MASSES}[$index_mz]{ENTRIES} }, \%entry) ;
+    			}
+    			$index_res++ ;	
+    		}
+    		$index_mz ++ ;
+    		$index_mz_continous ++ ;
+    	}
+    	$index_page++ ;
+    }
+    return($tbody_object) ;
+}
+## END of SUB
+
+=head2 METHOD write_html_skel
+
+	## Description : prepare and write the html output file
+	## Input : $html_file_name, $html_object, $html_template
+	## Output : $html_file_name
+	## Usage : my ( $html_file_name ) = write_html_skel( $html_file_name, $html_object ) ;
+	
+=cut
+## START of SUB
+sub write_html_skel {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $html_file_name,  $html_object, $pages , $search_condition, $html_template, $js_path, $css_path ) = @_ ;
+    
+    my $html_file = $$html_file_name ;
+    
+    if ( defined $html_file ) {
+		open ( HTML, ">$html_file" ) or die "Can't create the output file $html_file " ;
+		
+		if (-e $html_template) {
+			my $ohtml = HTML::Template->new(filename => $html_template);
+			$ohtml->param(  JS_GALAXY_PATH => $js_path, CSS_GALAXY_PATH => $css_path  ) ;
+			$ohtml->param(  CONDITIONS => $search_condition  ) ;
+			$ohtml->param(  PAGES_NB => $pages  ) ;
+			$ohtml->param(  PAGES => $html_object  ) ;
+			print HTML $ohtml->output ;
+		}
+		else {
+			croak "Can't fill any html output : No template available ($html_template)\n" ;
+		}
+		
+		close (HTML) ;
+    }
+    else {
+    	croak "No output file name available to write HTML file\n" ;
+    }
+    return(\$html_file) ;
+}
+## END of SUB
+
+=head2 METHOD write_csv_one_mass
+
+	## Description :  print a csv file
+	## Input : $masses, $ids, $results, $file
+	## Output : N/A
+	## Usage : write_csv_one_mass( $ids, $results, $file ) ;
+	
+=cut
+## START of SUB
+sub write_csv_one_mass {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $masses, $ids, $results, $file,  ) = @_ ;
+    
+    open(CSV, '>:utf8', "$file") or die "Cant' create the file $file\n" ;
+    print CSV "ID\tMASS_SUBMIT\tCPD_FORMULA\tCPD_MW\tDELTA\n" ;
+    	
+    my $i = 0 ;
+    	
+    foreach my $id (@{$ids}) {
+    	my $mass = $masses->[$i] ;
+    	
+    	if ( $results->[$i] ) { ## an requested id has a result in the list of hashes $results.
+    		
+    		my $entry_nb = 0 ;
+    		
+    		## in case of no results -- Hr_parsed Results : $VAR1 = [ { 'ENTRY_FORMULA' => [] } ];
+    		if ( !$results->[$i]{'ENTRY_FORMULA'} ) { 	print CSV "$id\t$mass\tN/A\t0.0\t0.0\n" ; 	}
+    		
+    		foreach (@{$results->[$i]{'ENTRY_FORMULA'}}) {
+
+    			print CSV "$id\t$mass\t" ;
+    			## print cpd formula
+    			if ( $results->[$i]{'ENTRY_FORMULA'}[$entry_nb] ) 	{	print CSV "$results->[$i]{'ENTRY_FORMULA'}[$entry_nb]\t" ; }
+    			else { 							print CSV "N/A\t" ; }
+    			## print cpd name
+    			if ( $results->[$i]{'ENTRY_CPD_MZ'}[$entry_nb] ) 	{ 	print CSV "$results->[$i]{'ENTRY_CPD_MZ'}[$entry_nb]\t" ; }
+    			else { 							print CSV "0.0\t" ; }
+    			## print delta
+    			if ( $results->[$i]{'ENTRY_DELTA'}[$entry_nb] ) 	{ print CSV "$results->[$i]{'ENTRY_DELTA'}[$entry_nb]\n" ; }
+    			else { 							print CSV "0.0\n" ; }
+    			$entry_nb++ ;
+    		}
+    	}
+    	else {
+    		print CSV "$id\t$mass\tN/A\t0.0\t0.0\n" ;
+    	}
+    	$i++ ;
+    }
+   	close(CSV) ;
+    return() ;
+}
+## END of SUB
+
+=head2 METHOD add_hr_matrix_to_input_matrix
+
+	## Description : build a full matrix (input + lm column)
+	## Input : $input_matrix_object, $lm_matrix_object
+	## Output : $output_matrix_object
+	## Usage : my ( $output_matrix_object ) = add_hr_matrix_to_input_matrix( $input_matrix_object, $hr_matrix_object ) ;
+	
+=cut
+## START of SUB
+sub add_hr_matrix_to_input_matrix {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $input_matrix_object, $hr_matrix_object ) = @_ ;
+    
+    my @output_matrix_object = () ;
+    my $index_row = 0 ;
+    
+    foreach my $row ( @{$input_matrix_object} ) {
+    	my @init_row = @{$row} ;
+    	
+    	if ( $hr_matrix_object->[$index_row] ) {
+    		my $dim = scalar(@{$hr_matrix_object->[$index_row]}) ;
+    		
+    		if ($dim > 1) { warn "the add method can't manage more than one column\n" ;}
+    		my $lm_col =  $hr_matrix_object->[$index_row][$dim-1] ;
+
+   		 	push (@init_row, $lm_col) ;
+	    	$index_row++ ;
+    	}
+    	push (@output_matrix_object, \@init_row) ;
+    }
+    return(\@output_matrix_object) ;
+}
+## END of SUB
+
+=head2 METHOD write_csv_skel
+
+	## Description : prepare and write csv output file
+	## Input : $csv_file, $rows
+	## Output : $csv_file
+	## Usage : my ( $csv_file ) = write_csv_skel( $csv_file, $rows ) ;
+	
+=cut
+## START of SUB
+sub write_csv_skel {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $csv_file, $rows ) = @_ ;
+    
+    my $ocsv = lib::csv::new() ;
+	my $csv = $ocsv->get_csv_object("\t") ;
+	$ocsv->write_csv_from_arrays($csv, $$csv_file, $rows) ;
+    
+    return($csv_file) ;
+}
+## END of SUB
+
+=head2 METHOD set_hr_matrix_object
+
+	## Description : build the hr_row under its ref form
+	## Input : $header, $init_mzs, $entries
+	## Output : $hr_matrix
+	## Usage : my ( $hmdb_matrix ) = set_hr_matrix_object( $header, $init_mzs, $entries ) ;
+	
+=cut
+## START of SUB
+sub set_hr_matrix_object {
+	## Retrieve Values
+    my $self = shift ;
+    my ( $header, $init_mzs, $entries ) = @_ ;
+    
+    my @hr_matrix = () ;
+    
+    if ( defined $header ) {
+    	my @headers = () ;
+    	push @headers, $header ;
+    	push @hr_matrix, \@headers ;
+    }
+    
+    my $index_mz = 0 ;
+    
+    foreach my $mz ( @{$init_mzs} ) {
+    	
+    	my $index_entries = 0 ;
+    	my @clusters = () ;
+    	my $cluster_col = undef ;
+    	
+    	my $nb_entries = $entries->[$index_mz]{MASSES_TOTAL} ;
+    	
+    	foreach (@{$entries->[$index_mz]{'ENTRY_FORMULA'}}) {
+    				
+    		my $delta = $entries->[$index_mz]{'ENTRY_DELTA'}[$index_entries] ;
+	    	my $hr_formula = $entries->[$index_mz]{'ENTRY_FORMULA'}[$index_entries] ;
+	    	my $hr_mz = $entries->[$index_mz]{'ENTRY_CPD_MZ'}[$index_entries] ;
+    		
+	    	
+	    	## METLIN data display model 
+	   		## entry1=VAR1::VAR2::VAR3::VAR4|entry2=VAR1::VAR2::VAR3::VAR4|...
+	   		# manage final pipe
+	   		if ($index_entries < $$nb_entries-1 ) { 	$cluster_col .= $delta.'::('.$hr_formula.')::'.$hr_mz.'|' ; }
+	   		else { 						   			$cluster_col .= $delta.'::('.$hr_formula.')::'.$hr_mz ; 	}
+	    		
+	    	$index_entries++ ;
+	    } ## end foreach
+	    if ( !defined $cluster_col ) { $cluster_col = 'No_result_found_with HR' ; }
+    	push (@clusters, $cluster_col) ;
+    	push (@hr_matrix, \@clusters) ;
+    	$index_mz++ ;
+    }
+    return(\@hr_matrix) ;
+}
+## END of SUB
+
+
+
+1 ;
+
+
+__END__
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command.
+
+ perldoc hr.pm
+
+=head1 Exports
+
+=over 4
+
+=item :ALL is manage_atoms, check_hr_exe, manage_tolerance
+
+=back
+
+=head1 AUTHOR
+
+Franck Giacomoni E<lt>franck.giacomoni@clermont.inra.frE<gt>
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=head1 VERSION
+
+version 1 : 02 / 20 / 2014
+
+version 2 : ??
+
+=cut
\ No newline at end of file
Binary file static/images/hr2.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/hr2_managerTest.t	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,64 @@
+#! perl
+use diagnostics;
+use warnings;
+no warnings qw/void/;
+use strict;
+no strict "refs" ;
+#use Test::More qw( no_plan );
+use Test::More tests => 29 ;
+use FindBin ;
+
+## Specific Modules
+use lib $FindBin::Bin ;
+my $binPath = $FindBin::Bin ;
+use lib::hrTest qw( :ALL ) ;
+
+## testing manage_atoms
+print "\n-- Test manage_atoms lib\n\n" ;
+is( manage_atomsTest('', 'C,H,O,N'),'C,H,O,N', 'Works with void argvt' ) ;
+is( manage_atomsTest(undef, 'C,H,O,N'), 'C,H,O,N', 'Works with undef argvt in input');
+isnt( manage_atomsTest('C,H,O,N', undef), 'C,H,O,N', 'Doesn\'t work with undef argvt in conf');
+is( manage_atomsTest(' ', 'C,H,O,N'), 'C,H,O,N', 'Works with \'space\' argvt in input' ) ;
+is( manage_atomsTest('None', 'C,H,O,N'), 'C,H,O,N', 'Works with \'None\' argvt in input' ) ;
+isnt( manage_atomsTest('C,H,O,N', 'C,H,O,N'), 'C,H,O,N', 'Doesn\'t work with same argvt in conf and input' );
+is( manage_atomsTest('P', 'C,H,O,N'), 'C,H,O,N,P', 'Works with P argvt in input');
+is( manage_atomsTest('1', 'C,H,O,N'), 'C,H,O,N,1', 'Works with 13C argvt in input');
+is( manage_atomsTest('P,S', 'C,H,O,N'), 'C,H,O,N,P,S', 'Works with P and S argvt in input');
+is( manage_atomsTest('X', 'C,H,O,N'), 'C,H,O,N', 'Doesn\'t work with other character diff than [P|S|F|L|K|B|A|1|] in input');
+print "\n--\n" ;
+
+print "\n-- Test check_hr_exe lib\n\n" ;
+is ( check_hr_exeTest('J:\\BioInfoTools\\_BINARIES\\HR2-all-res.exe', 'hr version 20050617'), 1, 'Works with WIN path and good version') ;
+is ( check_hr_exeTest('J:\\BioInfoTools\\_BINARIES\\HR2-all-res.exe', undef), undef, 'Doesn\'t work with WIN path and bad version') ;
+is ( check_hr_exeTest('Z:\\BioInfoTools\\TOTO\\HR2-all-res.exe', 'hr version 20050617'), undef, 'Doesn\'t work with bullshit path and good version') ;
+is ( check_hr_exeTest(undef, 'hr version 20050617'), undef, 'Doesn\'t work with undef path and good version') ;
+# need to test unix path
+print "\n--\n" ;
+
+print "\n-- Test manage_tolerance lib --\n\n" ;
+is ( manage_toleranceTest( '5.0', '1.0' ), '5.0', 'Works with tolerance of 5.0' ) ;
+is ( manage_toleranceTest( '5,0', '1.0' ), '5.0', 'Works with tolerance of 5,0 (french number)' ) ;
+is ( manage_toleranceTest( undef, '1.0' ), undef, 'Doesn\'t work with undef tolerance' ) ;
+is ( manage_toleranceTest( '5.0', undef ), undef, 'Doesn\'t work with undef default tolerance' ) ;
+is ( manage_toleranceTest( '20.0', '1.0' ), '1.0', 'Works with hight tolerance (20.0), use default tolerance' ) ;
+is ( manage_toleranceTest( '-10.0', '1.0' ), '1.0', 'Works with negative tolerance (-10.0), use default tolerance' ) ;
+print "\n--\n" ;
+
+print "\n-- Test manage_mode lib\n\n" ;
+is ( manage_modeTest('positive', '1', '0.0005486', '1.007825', '100.00'), '98.9927236', 'Works and computes right mass (98.9927236) with positive mode and complete conf') ;
+is ( manage_modeTest('negative', '1', '0.0005486', '1.007825', '100.00') , '101.0072764', 'Works and computes right mass (101.0072764) with negative mode and complete conf') ;
+is ( manage_modeTest('neutral', '1', '0.0005486', '1.007825', '100.00' ), '100.00', 'Works and computes right mass (100.00) with neutral mode and complete conf') ;
+is ( manage_modeTest('banane', '1', '0.0005486', '1.007825', '100.00' ), undef, 'Works and warns with unbelievable argt mode') ;
+is ( manage_modeTest('positive', '1', '0.0005486', '1.007825', '100,00' ), '98.9927236', 'Works with french mass format in positive mode') ;
+is ( manage_modeTest('neutral', '1', undef, undef, '100.00' ), undef, 'Works and warns when missing some conf paramaters (electron, proton, mass)') ;
+is ( manage_modeTest('negative', '0', '0.0005486', '1.007825', '100.00') , '101.0072764', 'Works and computes right mass (101.0072764) with negative mode and charge = 0 become 1 ') ;
+is ( manage_modeTest('positive', '3', '0.0005486', '1.007825', '100.00'), '298.9938208', 'Works and computes right mass (298.9938208) with positive mode, charge = 3 and complete conf') ;
+is ( manage_modeTest('neutral', undef, '0.0005486', '1.007825', '100.00' ), '100.00', 'Works and warns when missing charge') ;
+print "\n--\n" ;
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/lib/hrTest.pm	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,53 @@
+package lib::hrTest ;
+
+use diagnostics; # this gives you more debugging information
+use warnings;    # this warns you of bad practices
+use strict;      # this prevents silly errors
+use Exporter ;
+use Carp ;
+
+our $VERSION = "1.0";
+our @ISA = qw(Exporter);
+our @EXPORT = qw( manage_atomsTest check_hr_exeTest manage_toleranceTest manage_modeTest );
+our %EXPORT_TAGS = ( ALL => [qw(manage_atomsTest check_hr_exeTest manage_toleranceTest manage_modeTest )] );
+
+use lib '/Users/fgiacomoni/Inra/labs/perl/galaxy_tools/hr2' ;
+use lib::hr qw( :ALL ) ;
+
+sub manage_atomsTest {
+	
+	my ($input_atoms, $conf_atoms, ) = @_ ;
+	
+	my $oAtom = lib::hr->new() ;
+	my $ref_atoms = $oAtom->manage_atoms(\$input_atoms, \$conf_atoms) ;
+	my $atoms = $$ref_atoms ;
+	
+	return ($atoms) ;
+}
+
+sub check_hr_exeTest {
+	my ( $hr_path, $hr_version ) = @_ ;
+	my $oHr = lib::hr->new() ;
+	my $res = $oHr->check_hr_exe(\$hr_path, \$hr_version) ;
+	
+	return ($res) ;
+}
+
+sub manage_toleranceTest {
+	my ( $tolerance, $default_value ) = @_ ;
+	my $oHr = lib::hr->new() ;
+	my $tol = $oHr->manage_tolerance( \$tolerance, \$default_value ) ;
+	return ($$tol) ;
+}
+
+sub manage_modeTest {
+	my ( $mode, $charge, $electron, $proton, $mass ) = @_ ;
+	my $oHr = lib::hr->new() ;
+	my $exact_mass = $oHr->manage_mode( \$mode, \$charge, \$electron, \$proton, \$mass ) ;
+	return ($$exact_mass) ;
+}
+
+
+
+
+1 ;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/out1.html	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html>
+<html lang="en">
+	<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content=""><meta name="author" content="INRA de Clermont-Ferrand"><title>Galaxy HR2 queries - All results</title><link rel="stylesheet" href="css.php" media="all"><link rel="stylesheet" href="https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/style/simplePagination.css"/><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script><script src="https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/scripts/jquery.simplePagination.js"></script><style>body{padding-top:70px} div.lm-table-warning{font-size:1.4em;font-weight:bold;padding-right:25px;color:#21536a;margin-left:3px;}tr.green td{background-color:#eaf2d3;color:black;} tr.blank td{background-color:#9999CC;color:black;} table{font-family:\"Trebuchet MS\", Arial, Helvetica, sans-serif;width:100%;border-collapse:collapse;}table.detail, table.detail tr.parent, table.detail td, table.detail th, table.detail tr.category {border-collapse:collapse;border:1px solid #98bf21;}table.detail th {font-size:1.2em;text-align:center;padding-top:5px;padding-bottom:10px;background-color:#a7c942;color:#ffffff;}td.ca {text-align:center;}footer{margin:50px 0;}</style><script>function test(pageNumber){var page="#page-id-"+pageNumber;$('.select').hide();$(page).show()}</script></head>
+	<body><div class="container"><div class="lm-table-warning">Results of HR elucidation queries - Mode used: negative / Charge: +1 / Mass tolerance: 1.0 / Composition:  -C 0-100 -A 0-0 -1 0-0 -B 0-0 -K 0-0 -N 0-40 -P 0-0 -S 0-0 -O 0-70 -H 0-200 -F 0-0 -L 0-0</div><div id="detail_table_source" style="display:none"></div><p><div id="choose"></div><p><div id="ms_search_0" class="ms-search-table"></div><table id="detail_table" class="detail"><col style="width:20px;"><!-- Ids (m/z)--><col style="width:20px;"><!-- Mass (m/z)--><col style="width:20px;"><!-- Formula--><col style="width:60px;"><!-- cpd mw--><col style="width:50px;"><!-- delta--><col style="width:50px;"><!-- total--><thead><th>ID from input</th><th>Mass (m/z)</th><th>Formula</th><th>Compound MW (Da)</th><th>Delta</th><th>Total</th></thead><tbody class="select" id="page-id-1"><tr class="white"><td class="ca" >mass_01</td><td id="1" class="ca" >175.125</td><td class="ca" colspan="3"></td><td class="ca" >1</td></tr><tr class="white"><td class="ca" colspan="2"></td><td class="ca">C11H16N2</td><td class="ca">176.1313485</td><td class="ca">0.93</td><td class="ca" colspan="1"></td></tr></tbody></table></div><div class="container"><hr><footer><div class="row"><div class="col-lg-12"><p><a href="http://jigsaw.w3.org/css-validator/check/referer" target="_blank"><img style="border:0;width:88px;height:31px"	src="http://jigsaw.w3.org/css-validator/images/vcss-blue" alt="Valid CSS!" /></a></p><p>Copyright &copy; INRA, N Paulhe, F Giacomoni 2014</a></p></div> </div></footer></div><script language="javascript">$(function() {$('#choose').pagination({items: 1,itemsOnPage: 1,currentPage: 1,onInit: function () { test(1); },cssStyle: 'light-theme',onPageClick: function(pageNumber){test(pageNumber)}}).pagination('redraw');});</script></body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/out1.tabular	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,2 @@
+ID	MASS_SUBMIT	CPD_FORMULA	CPD_MW	DELTA
+mass_01	175.125	C11H16N2	176.1313485	0.93
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/out2.html	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html>
+<html lang="en">
+	<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content=""><meta name="author" content="INRA de Clermont-Ferrand"><title>Galaxy HR2 queries - All results</title><link rel="stylesheet" href="css.php" media="all"><link rel="stylesheet" href="https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/style/simplePagination.css"/><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script><script src="https://cdn.rawgit.com/fgiacomoni/galaxy_utils/master/scripts/jquery.simplePagination.js"></script><style>body{padding-top:70px} div.lm-table-warning{font-size:1.4em;font-weight:bold;padding-right:25px;color:#21536a;margin-left:3px;}tr.green td{background-color:#eaf2d3;color:black;} tr.blank td{background-color:#9999CC;color:black;} table{font-family:\"Trebuchet MS\", Arial, Helvetica, sans-serif;width:100%;border-collapse:collapse;}table.detail, table.detail tr.parent, table.detail td, table.detail th, table.detail tr.category {border-collapse:collapse;border:1px solid #98bf21;}table.detail th {font-size:1.2em;text-align:center;padding-top:5px;padding-bottom:10px;background-color:#a7c942;color:#ffffff;}td.ca {text-align:center;}footer{margin:50px 0;}</style><script>function test(pageNumber){var page="#page-id-"+pageNumber;$('.select').hide();$(page).show()}</script></head>
+	<body><div class="container"><div class="lm-table-warning">Results of HR elucidation queries - Mode used: neutral / Charge: +1 / Mass tolerance: 1.0 / Composition:  -K 0-0 -B 0-0 -F 0-0 -H 0-200 -L 0-0 -P 0-0 -C 0-100 -A 0-0 -1 0-0 -O 0-70 -S 0-0 -N 0-0</div><div id="detail_table_source" style="display:none"></div><p><div id="choose"></div><p><div id="ms_search_0" class="ms-search-table"></div><table id="detail_table" class="detail"><col style="width:20px;"><!-- Ids (m/z)--><col style="width:20px;"><!-- Mass (m/z)--><col style="width:20px;"><!-- Formula--><col style="width:60px;"><!-- cpd mw--><col style="width:50px;"><!-- delta--><col style="width:50px;"><!-- total--><thead><th>ID from input</th><th>Mass (m/z)</th><th>Formula</th><th>Compound MW (Da)</th><th>Delta</th><th>Total</th></thead><tbody class="select" id="page-id-1"><tr class="white"><td class="ca" >mass_01</td><td id="1" class="ca" >88.052</td><td class="ca" colspan="3"></td><td class="ca" >1</td></tr><tr class="white"><td class="ca" colspan="2"></td><td class="ca">C4H8O2</td><td class="ca">88.0524295</td><td class="ca">-0.43</td><td class="ca" colspan="1"></td></tr></tbody></table></div><div class="container"><hr><footer><div class="row"><div class="col-lg-12"><p><a href="http://jigsaw.w3.org/css-validator/check/referer" target="_blank"><img style="border:0;width:88px;height:31px"	src="http://jigsaw.w3.org/css-validator/images/vcss-blue" alt="Valid CSS!" /></a></p><p>Copyright &copy; INRA, N Paulhe, F Giacomoni 2014</a></p></div> </div></footer></div><script language="javascript">$(function() {$('#choose').pagination({items: 1,itemsOnPage: 1,currentPage: 1,onInit: function () { test(1); },cssStyle: 'light-theme',onPageClick: function(pageNumber){test(pageNumber)}}).pagination('redraw');});</script></body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/out2.tabular	Wed Jun 05 09:40:20 2019 -0400
@@ -0,0 +1,2 @@
+ID	MASS_SUBMIT	CPD_FORMULA	CPD_MW	DELTA
+mass_01	88.052	C4H8O2	88.0524295	-0.43