mspire-molecular_formula 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8aa1b3d4118e17b3ca2c9092e9c355f6e376a46c
4
+ data.tar.gz: 14d52e1251c536369476ec68bab41f903a366a11
5
+ SHA512:
6
+ metadata.gz: ee4729ebc0ff0276ffbd299bdec26a3eea81a05603895c1061b333031c8c22b53c1bac56789734a7e071dd776df728041f1dd67f0ca2ae4f61be6192aca87a77
7
+ data.tar.gz: d6301ed9ef9b458c80b80ea92615c059f6a49bbedc15ed76c1a5f089aac3766cb2d52d7063858bbb4704080fff8ade679bc18049f0d223e44e945c33d8f4ea65
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mspire-molecular_formula.gemspec
4
+ gemspec
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2014 Brigham Young University
2
+ Author: John T. Prince
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,58 @@
1
+ # Mspire::MolecularFormula
2
+
3
+ mspire library to handle molecular formulas (including an optional charge state), complete with relevant chemical properties such as mass, m/z, and isotope distribution.
4
+
5
+ ## Installation
6
+
7
+ gem install mspire-molecular_formula
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ require 'mspire/molecular_formula'
13
+ ```
14
+
15
+ ### Create with a hash
16
+
17
+ ```ruby
18
+ mf = Mspire::MolecularFormula.new( C:3, H:4, O:2 )
19
+
20
+ # with a +2 charge
21
+ mf = Mspire::MolecularFormula.new( {C:3, H:4, O:2}, 2)
22
+ ```
23
+
24
+ ### Create with a string formula
25
+
26
+ ```ruby
27
+ mf = Mspire::MolecularFormula[ 'C3H4O2' ]
28
+
29
+ # with a +2 charge
30
+ mf = Mspire::MolecularFormula[ 'C3H4O2', 2 ]
31
+ mf = Mspire::MolecularFormula[ 'C3H4O2+2' ] # alternatively
32
+ mf = Mspire::MolecularFormula[ 'C3H4O2++' ] # alternatively
33
+ ```
34
+
35
+ ### Arithmetic
36
+
37
+ Walk through the arithmetic of combustion of ethene using this equation:
38
+
39
+ CH2=CH2 + 3 O2 -> 2 CO2 + 2 H2O
40
+
41
+ ```ruby
42
+ ethene = Mspire::MolecularFormula['C2H4']
43
+ oxygen = Mspire::MolecularFormula['O2']
44
+ water = Mspire::MolecularFormula['H2O']
45
+
46
+ combustion = ethene + (oxygen*3)
47
+ two_carbon_dioxide = combustion - (water*2)
48
+ carbon_dioxide = two_carbon_dioxide / 2
49
+ ```
50
+
51
+ Note that there are no methods defined on fixnum to deal with MolecularFormula
52
+ objects, so fixnums need to follow the MolecularFormula (i.e., "3 * oxygen"
53
+ will throw an error but "oxygen * 3" is fine)
54
+
55
+
56
+ require 'mspire/isotope/distribution' # requires fftw gem
57
+ puts butane.isotope_distribution # :total, :max, :first as arg to normalize
58
+
@@ -0,0 +1,24 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ @module_name = Mspire::MolecularFormula
4
+ @gem_name = 'mspire-molecular_formula'
5
+ @gem_path_name = @gem_name.gsub('-','/')
6
+
7
+ require "#{@gem_path_name}/version"
8
+
9
+ require 'rspec/core'
10
+ require 'rspec/core/rake_task'
11
+ RSpec::Core::RakeTask.new(:spec) do |spec|
12
+ spec.pattern = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ task :default => :spec
16
+
17
+ require 'rdoc/task'
18
+ Rake::RDocTask.new do |rdoc|
19
+ version = @module_name.const_get('VERSION')
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = "#{@gem_name} #{version}"
22
+ rdoc.rdoc_files.include('README*')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
@@ -0,0 +1,3 @@
1
+ require 'mspire/molecular_formula'
2
+
3
+ Mspire::MF = Mspire::MolecularFormula
@@ -0,0 +1,69 @@
1
+ module Mspire
2
+ class MolecularFormula < Hash
3
+
4
+ # integer desribing the charge state
5
+ # mass calculations will add/remove electron mass from this
6
+ attr_accessor :charge
7
+
8
+ # Takes a hash and an optional Integer expressing the charge
9
+ # {H: 22, C: 12, N: 1, O: 3, S: 2} # case and string/sym doesn't matter
10
+ def initialize(hash={}, charge=0)
11
+ @charge = charge
12
+ self.merge!(hash)
13
+ end
14
+
15
+ def to_s(include_charge_if_nonzero=true, alphabetize=true)
16
+ h = alphabetize ? self.sort : self
17
+ st = ''
18
+ h.each do |k,v|
19
+ if v > 0
20
+ st << k.to_s.capitalize
21
+ st << v.to_s if v > 1
22
+ end
23
+ end
24
+ if include_charge_if_nonzero
25
+ st << "#{charge > 0 ? '+' : '-'}#{charge.abs if charge.abs > 1}" unless charge.zero?
26
+ end
27
+ st
28
+ end
29
+
30
+ def inspect
31
+ "{MolecularFormula #{super[1...-1]}, @charge=#{self.charge}}"
32
+ end
33
+
34
+ # returns a hash (note: does not pass along charge info!)
35
+ def to_h
36
+ Hash[ self ]
37
+ end
38
+
39
+ alias_method :old_equal, '=='.to_sym
40
+
41
+ def ==(other)
42
+ old_equal(other) && self.charge == other.charge
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ require "mspire/molecular_formula/version"
49
+
50
+ # class methods for reading from different string input
51
+ require 'mspire/molecular_formula/reader'
52
+
53
+ # the modules for these 3 are included at the bottom
54
+ require 'mspire/molecular_formula/arithmetic'
55
+ require 'mspire/molecular_formula/mass'
56
+ require 'mspire/molecular_formula/isotope_distribution'
57
+
58
+ module Mspire
59
+ class MolecularFormula
60
+ extend Reader
61
+
62
+ ####################################################
63
+ # include other behaviors
64
+ ####################################################
65
+ include Arithmetic
66
+ include Mass
67
+ include IsotopeDistribution
68
+ end
69
+ end
@@ -0,0 +1,79 @@
1
+ require 'mspire/molecular_formula'
2
+
3
+ module Mspire
4
+ class MolecularFormula
5
+ module AA
6
+ # These represent counts for the individual residues (i.e., no extra H
7
+ # and OH on the ends)
8
+ aa_to_el_hash = {
9
+ 'A' => { C: 3, H: 5, O: 1, N: 1 },
10
+ 'C' => { C: 3, H: 5, O: 1, N: 1, S: 1 },
11
+ 'D' => { C: 4, H: 5, O: 3, N: 1 },
12
+ 'E' => { C: 5, H: 7, O: 3, N: 1 },
13
+ 'F' => { C: 9, H: 9, O: 1, N: 1 },
14
+ 'G' => { C: 2, H: 3, O: 1, N: 1 },
15
+ 'I' => { C: 6, H: 11, O: 1, N: 1 },
16
+ 'H' => { C: 6, H: 7, O: 1, N: 3 },
17
+ 'K' => { C: 6, H: 12, O: 1, N: 2 },
18
+ 'L' => { C: 6, H: 11, O: 1, N: 1 },
19
+ 'M' => { C: 5, H: 9, O: 1, N: 1, S: 1 },
20
+ 'N' => { C: 4, H: 6, O: 2, N: 2 },
21
+ 'O' => { C: 12, H: 19, O: 2, N: 3 },
22
+ 'P' => { C: 5, H: 7, O: 1, N: 1 },
23
+ 'Q' => { C: 5, H: 8, O: 2, N: 2 },
24
+ 'R' => { C: 6, H: 12, O: 1, N: 4 },
25
+ 'S' => { C: 3, H: 5, O: 2, N: 1 },
26
+ 'T' => { C: 4, H: 7, O: 2, N: 1 },
27
+ 'U' => { C: 3, H: 5, O: 1, N: 1, Se: 1 },
28
+ 'V' => { C: 5, H: 9, O: 1, N: 1 },
29
+ 'W' => { C: 11, H: 10, O: 1, N: 2 },
30
+ 'Y' => { C: 9, H: 9, O: 2, N: 1 },
31
+ }
32
+
33
+ # molecular formulas for each amino acid residue (no H or OH on ends)
34
+ # keyed by AA string. Shares formula objects with FORMULAS_SYBMOL and
35
+ # FORMULAS.
36
+ FORMULAS_STRING = aa_to_el_hash.map {|k,v| [k, Mspire::MolecularFormula.new(v)] }.to_h
37
+
38
+ class << self
39
+ # returns hash of molecular formulas keyed by amino acid single letter
40
+ # symbol
41
+ #
42
+ # options:
43
+ #
44
+ # :by = :symbol | :string | :both
45
+ # (:symbol is default)
46
+ def formulas(by: :symbol)
47
+ case by
48
+ when :symbol, :both
49
+ sym_hash = Mspire::MolecularFormula::AA::FORMULAS_STRING.map {|k,v| [k.to_sym, v] }.to_h
50
+ when :string
51
+ return Mspire::MolecularFormula::AA::FORMULAS_STRING
52
+ else
53
+ raise ArgumentError, ":by must be :symbol, :string, or :both"
54
+ end
55
+
56
+ if by == :symbol
57
+ sym_hash
58
+ else
59
+ Mspire::MolecularFormula::AA::FORMULAS_STRING.merge(sym_hash)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module Reader
66
+
67
+ # a linear peptide (so includes all the residue masses plus water)
68
+ def from_aaseq(aaseq, charge=0, aa_formula_hash=Mspire::MolecularFormula::AA::FORMULAS_STRING)
69
+ hash = aaseq.each_char.inject({}) do |hash,aa|
70
+ hash.merge(aa_formula_hash[aa]) {|hash,old,new| (old ? old : 0) + new }
71
+ end
72
+ hash[:H] += 2
73
+ hash[:O] += 1
74
+ self.new(hash, charge)
75
+ end
76
+ end
77
+
78
+ end # molecular_formula
79
+ end # mspire
@@ -0,0 +1,74 @@
1
+ module Mspire
2
+ class MolecularFormula
3
+ module Arithmetic
4
+ # returns a new formula object where all the atoms have been added up
5
+ def +(*others)
6
+ self.dup.add!(*others)
7
+ end
8
+
9
+ # returns self
10
+ def add!(*others)
11
+ others.each do |other|
12
+ self.merge!(other) {|key, oldval, newval| self[key] = oldval + newval }
13
+ self.charge += other.charge
14
+ end
15
+ self
16
+ end
17
+
18
+ # returns a new formula object where all the formulas have been subtracted
19
+ # from the caller
20
+ def -(*others)
21
+ self.dup.sub!(*others)
22
+ end
23
+
24
+ def sub!(*others)
25
+ others.each do |other|
26
+ oth = other.dup
27
+ self.each do |k,v|
28
+ if oth.key?(k)
29
+ self[k] -= oth.delete(k)
30
+ end
31
+ end
32
+ oth.each do |k,v|
33
+ self[k] = -v
34
+ end
35
+ self.charge -= other.charge
36
+ end
37
+ self
38
+ end
39
+
40
+ def *(int)
41
+ self.dup.mul!(int)
42
+ end
43
+
44
+ def mul!(int, also_do_charge=true)
45
+ raise ArgumentError, "must be an integer" unless int.is_a?(Integer)
46
+ self.each do |k,v|
47
+ self[k] = v * int
48
+ end
49
+ self.charge *= int if also_do_charge
50
+ self
51
+ end
52
+
53
+ def /(int)
54
+ self.dup.div!(int)
55
+ end
56
+
57
+ def div!(int, also_do_charge=true)
58
+ raise ArgumentError, "must be an integer" unless int.is_a?(Integer)
59
+ self.each do |k,v|
60
+ quotient, modulus = v.divmod(int)
61
+ raise ArgumentError "all numbers must be divisible by int" unless modulus == 0
62
+ self[k] = quotient
63
+ end
64
+ if also_do_charge
65
+ quotient, modulus = self.charge.divmod(int)
66
+ raise ArgumentError "charge must be divisible by int" unless modulus == 0
67
+ self.charge = quotient
68
+ end
69
+ self
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,109 @@
1
+ require 'mspire/mass'
2
+ require 'mspire/isotope'
3
+
4
+ require 'fftw3'
5
+
6
+ module Mspire
7
+ class MolecularFormula
8
+ module IsotopeDistribution
9
+ NORMALIZE = :total
10
+
11
+ # Returns isotopic distribution beginning with the lightest possible peak.
12
+ # (for most molecules this will also be the monoisotopic peak)
13
+ #
14
+ # Two cutoff protocols may be specified, percent_cutoff or
15
+ # peak_cutoff. Normalization is performed *after* cutoff.
16
+ #
17
+ # percent_cutoff: cuts off when no more peaks contribute more than percent_cutoff
18
+ # to the total distribution.
19
+ # peak_cutoff: cuts off after that many peaks.
20
+ #
21
+ # prefer_lowest_index controls the behavior if both percent_cutoff and
22
+ # peak_cutoff are specified. If true, then the lowest index found between
23
+ # the two methods will be used, otherwise the highest index.
24
+ #
25
+ # all values will be fractional. normalize may be one of:
26
+ #
27
+ # :total normalize to the total intensity
28
+ # :max normalize to the highest peak intensity
29
+ # :first normalize to the intensity of the first peak
30
+ # (this is typically the monoisotopic peak)
31
+ def isotope_intensity_distribution(normalize: NORMALIZE, peak_cutoff: nil, percent_cutoff: nil, prefer_lowest_index: true, isotope_table: Mspire::Isotope::BY_ELEMENT)
32
+ mono_dist = raw_isotope_distribution(isotope_table: isotope_table)
33
+
34
+ cutoff_index = [
35
+ if percent_cutoff
36
+ total_signal = mono_dist.reduce(:+)
37
+ cutoff_index_less1 = (mono_dist.size-1).downto(0).find do |i|
38
+ # finds the index
39
+ (mono_dist[i] / total_signal) >= (percent_cutoff/100.0)
40
+ end
41
+ cutoff_index = cutoff_index_less1 ? (cutoff_index_less1 + 1) : 0
42
+ end,
43
+ peak_cutoff
44
+ ].compact.send( prefer_lowest_index ? :min : :max ) || mono_dist.size
45
+
46
+ # mono_dist.size will result in nothing sliced off (i.e., for no cutoff)
47
+
48
+ mono_dist.slice!(cutoff_index..-1)
49
+
50
+ # normalization
51
+ norm_by =
52
+ case normalize
53
+ when :total
54
+ total_signal || mono_dist.reduce(:+)
55
+ when :max
56
+ mono_dist.max
57
+ when :first
58
+ mono_dist.first
59
+ end
60
+ mono_dist.map do |i|
61
+ v = i / norm_by
62
+ (v > 0) ? v : 0
63
+ end
64
+ end
65
+
66
+ # returns an array of two arrays: mass values (or m/z values of charged)
67
+ # and intensity values. Arguments are passed directly to
68
+ # isotope_intensity_distribution. the molecule has a charge, this will be
69
+ # used to adjust the m/z values (by removing or adding electrons to the
70
+ # m/z and as the z)
71
+ def isotope_distribution(*args)
72
+ intensities = isotope_intensity_distribution(*args)
73
+ #mono = self.map {|el,cnt| Mspire::Mass::Element::MONO[el]*cnt }.reduce(:+)
74
+ mono = self.map {|el,cnt| Mspire::Isotope::BY_ELEMENT[el].find(&:mono).atomic_mass*cnt }.reduce(:+)
75
+ masses = Array.new(intensities.size)
76
+ neutron = Mspire::Mass::NEUTRON
77
+ masses[0] = mono
78
+ (1...masses.size).each {|i| masses[i] = masses[i-1] + neutron }
79
+ if self.charge && self.charge != 0
80
+ masses.map! do |mass|
81
+ (mass - (self.charge * Mspire::Mass::ELECTRON)) / self.charge
82
+ end
83
+ end
84
+ [masses, intensities]
85
+ end
86
+
87
+ # returns relative ratios from low nominal mass to high nominal mass.
88
+ # These are *not* normalized at all.
89
+ def raw_isotope_distribution(isotope_table: Mspire::Isotope::BY_ELEMENT)
90
+ low_nominal = 0
91
+ high_nominal = 0
92
+ self.each do |el,cnt|
93
+ isotopes = isotope_table[el]
94
+ low_nominal += (isotopes.first.mass_number * cnt)
95
+ high_nominal += (isotopes.last.mass_number * cnt)
96
+ end
97
+
98
+ ffts = self.map do |el, cnt|
99
+ isotope_el_ar = NArray.float(high_nominal+1)
100
+ isotope_table[el].each do |isotope|
101
+ isotope_el_ar[isotope.mass_number] = isotope.relative_abundance
102
+ end
103
+ FFTW3.fft(isotope_el_ar)**cnt
104
+ end
105
+ FFTW3.ifft(ffts.reduce(:*)).real.to_a[low_nominal..high_nominal]
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,34 @@
1
+ require 'mspire/mass'
2
+
3
+ module Mspire
4
+ class MolecularFormula
5
+ module Mass
6
+ # gives the monoisotopic mass adjusted by the current charge (i.e.,
7
+ # adds/subtracts electron masses for the charges)
8
+ def mass(consider_electron_masses = true)
9
+ mss = inject(0.0) do |sum,(el,cnt)|
10
+ sum + (Mspire::Mass::Element::MONO_STRING[el.to_s]*cnt)
11
+ end
12
+ mss -= (Mspire::Mass::ELECTRON * charge) if consider_electron_masses
13
+ mss
14
+ end
15
+
16
+ def avg_mass(consider_electron_masses = true)
17
+ mss = inject(0.0) {|sum,(el,cnt)| sum + (Mspire::Mass::Element::AVG_STRING[el.to_s]*cnt) }
18
+ mss -= (Mspire::Mass::ELECTRON * charge) if consider_electron_masses
19
+ mss
20
+ end
21
+
22
+ # the mass to charge ratio (m/z)
23
+ # returns nil if the charge == 0
24
+ def mz(consider_electron_masses = true, negative_mz_allowed = true)
25
+ if charge == 0
26
+ nil
27
+ else
28
+ mass(consider_electron_masses) / (negative_mz_allowed ? charge : charge.abs)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,56 @@
1
+ require 'mspire/molecular_formula/aa'
2
+
3
+ module Mspire
4
+ class MolecularFormula
5
+ module Reader
6
+ # returns the formula portion and the charge portion (signed Int) of a string
7
+ # returns nil for charge if no charge specified.
8
+ # e.g. C2H4+3 => ['C2H4', 3]
9
+ # e.g. C2H4+++ => ['C2H4', 3]
10
+ # e.g. C2H4- => ['C2H4', -1]
11
+ def formula_and_charge(string)
12
+ md = string.match(/([^+-]*)([\+-]+)(\d*)\Z/)
13
+ if md
14
+ charges_string = md[2]
15
+ chrg =
16
+ if md[3] != ''
17
+ md[2] == '-' ? -md[3].to_i : md[3].to_i
18
+ else
19
+ sign = charges_string[0]
20
+ cnt = charges_string.count(sign)
21
+ sign == '-' ? -cnt : cnt
22
+ end
23
+ [md[1], chrg]
24
+ else
25
+ [string, nil]
26
+ end
27
+ end
28
+
29
+
30
+ # takes a string, with properly capitalized elements making up the
31
+ # formula. The elements may be in any order. A charge (e.g., +2, +, -,
32
+ # -3 may be affixed to the end )
33
+ def from_string(arg, charge=nil)
34
+ (mol_form_str, chrg_from_str) = formula_and_charge(arg)
35
+ mf = self.new({}, charge || chrg_from_str || 0)
36
+ mol_form_str.scan(/([A-Z][a-z]?)(\d*)/).each do |k,v|
37
+ mf[k.to_sym] = (v == '' ? 1 : v.to_i)
38
+ end
39
+ mf
40
+ end
41
+
42
+ # arg may be a String, Hash, or MolecularFormula object.
43
+ def from_any(arg, charge=nil)
44
+ if arg.is_a?(String)
45
+ from_string(arg, charge)
46
+ else
47
+ self.new(arg, arg.respond_to?(:charge) ? arg.charge : 0)
48
+ end
49
+ end
50
+ alias_method :[], :from_any
51
+
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,5 @@
1
+ module Mspire
2
+ class MolecularFormula < Hash
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mspire/molecular_formula/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mspire-molecular_formula"
8
+ spec.version = Mspire::MolecularFormula::VERSION
9
+ spec.authors = ["John T. Prince"]
10
+ spec.email = ["jtprince@gmail.com"]
11
+ spec.summary = %q{mspire library to handle molecular formulas (including an optional charge state)}
12
+ spec.description = %q{mspire library to handle molecular formulas (including an optional charge state), complete with relevant chemical properties such as mass, m/z, and isotope distribution.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ [
22
+ ["mspire-mass", "~> 0.1.0"], # which brings in mspire-isotope
23
+ ].each do |args|
24
+ spec.add_dependency(*args)
25
+ end
26
+
27
+ [
28
+ ["bundler", "~> 1.6.2"],
29
+ ["rake"],
30
+ ["rspec", "~> 2.14.1"],
31
+ ["rdoc", "~> 4.1.1"],
32
+ ["simplecov", "~> 0.8.2"],
33
+ # here because bad microsoft OS support
34
+ # TODO: implement w/o FFTW
35
+ ["fftw3", "~> 0.3"],
36
+ ].each do |args|
37
+ spec.add_development_dependency(*args)
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ require 'mspire/mf'
4
+
5
+ describe 'require "mspire/mf" to get Mspire::MF shorthand' do
6
+ specify 'Mspire::MF allows convenient access to MolecularFormula stuff' do
7
+ product = Mspire::MF['H2O'] + Mspire::MF['C2H4']
8
+ Mspire::MF['H2O+'].mass.should == 18.010016083700002
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ require 'mspire/molecular_formula/aa'
4
+
5
+ describe Mspire::MolecularFormula::AA do
6
+ specify '::FORMULAS_STRING holds molecular formulas keyed by AA string' do
7
+ hash = Mspire::MolecularFormula::AA::FORMULAS_STRING
8
+ hash.size.should == 22
9
+ hash.values.each {|mf| mf.should be_a(Mspire::MolecularFormula) }
10
+ hash.values.first.to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
11
+ hash['A'].to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
12
+ hash.keys.all? {|key| key.is_a?(String) }.should be_true
13
+ end
14
+
15
+ specify '::formulas returns them by symbol or string or both (symbol by default)' do
16
+ hash = Mspire::MolecularFormula::AA.formulas
17
+ hash.size.should == 22
18
+ hash.values.each {|mf| mf.should be_a(Mspire::MolecularFormula) }
19
+ hash.values.first.to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
20
+ hash.keys.all? {|key| key.is_a?(Symbol) }.should be_true
21
+ hash[:A].to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
22
+
23
+ hash = Mspire::MolecularFormula::AA.formulas(by: :string)
24
+ hash.keys.all? {|key| key.is_a?(String) }.should be_true
25
+ hash['A'].to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
26
+
27
+ hash = Mspire::MolecularFormula::AA.formulas(by: :both)
28
+ hash.keys.any? {|key| key.is_a?(Symbol) }.should be_true
29
+ hash.keys.any? {|key| key.is_a?(String) }.should be_true
30
+ hash['A'].to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
31
+ hash[:A].to_h.should == { :C=>3, :H=>5, :O=>1, :N=>1 }
32
+ end
33
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ # in this case we need to pull in mol...form... first or we'll get it behaving
4
+ # like a proper hash with ::[] since ::[] hasn't been overridden!
5
+ require 'mspire/molecular_formula'
6
+
7
+ describe 'Mspire::Isotope::Distribution class methods' do
8
+
9
+ def similar_distributions(a_dist, b_dist)
10
+ b_dist.zip(a_dist) do |b,a|
11
+ expect(a).to be_within(1e-9).of b
12
+ end
13
+ end
14
+
15
+ before(:all) do
16
+ @nist = Mspire::Isotope::NIST::BY_ELEMENT
17
+ @norm = :total
18
+ @pcut = nil # percent cutoff
19
+ end
20
+
21
+ before do
22
+ @first = [1.0, 0.08919230588715311, 0.017894161377222138, 0.0013573997600723345, 0.0001398330738144181]
23
+ end
24
+
25
+ # can also be used on a real MolecularFormula object
26
+ subject { Mspire::MolecularFormula['C102H120O15'] }
27
+
28
+ describe 'normalizing isotope distributions' do
29
+
30
+ it 'defaults to normalizing by total signal with no cutoff' do
31
+
32
+ dist = subject.isotope_intensity_distribution(normalize: @norm, percent_cutoff: @pcut, isotope_table: @nist )
33
+ expect(dist.size).to eq(253)
34
+ similar_distributions dist[0,5], [0.31740518639058685, 0.35635707398291416, 0.20793431846543858, 0.08373257192958428, 0.026084566135229446]
35
+ end
36
+
37
+ it 'can normalize by first peak' do
38
+ dist = subject.isotope_intensity_distribution(normalize: :first, percent_cutoff: @pcut, isotope_table: @nist )
39
+ dist.size.should == 253
40
+ dist[0].should == 1.0
41
+ dist[1].should_not == 1.0
42
+ end
43
+
44
+ it 'can normalize by the max peak' do
45
+ dist = subject.isotope_intensity_distribution(normalize: :max, percent_cutoff: @pcut, isotope_table: @nist )
46
+ dist.size.should == 253
47
+ dist[0].should_not == 1.0
48
+ dist[1].should == 1.0
49
+ end
50
+
51
+ it 'can cutoff based on percent of total signal' do
52
+ subject.isotope_intensity_distribution(normalize: :max, percent_cutoff: 100, isotope_table: @nist).should == []
53
+ similar_distributions subject.isotope_intensity_distribution(normalize: :max, percent_cutoff: 20, isotope_table: @nist), [0.8906942209481861, 1.0, 0.5834999040187656]
54
+ similar_distributions subject.isotope_intensity_distribution(normalize: :max, percent_cutoff: 5, isotope_table: @nist), [0.8906942209481861, 1.0, 0.5834999040187656, 0.23496817670469172]
55
+ subject.isotope_intensity_distribution( normalize: :max, percent_cutoff: 0.0001, isotope_table: @nist).size.should == 11
56
+ end
57
+
58
+ it 'can cutoff based on a given number of peaks' do
59
+ subject.isotope_intensity_distribution(normalize: :max, peak_cutoff: 0, isotope_table: @nist).should == []
60
+ similar_distributions subject.isotope_intensity_distribution(normalize: :total, peak_cutoff: 4, isotope_table: @nist), [0.3287710818944283, 0.3691177894299527, 0.2153801947039964, 0.08673093397162249]
61
+ expect(subject.isotope_intensity_distribution(normalize: :max, peak_cutoff: 1, isotope_table: @nist)).to eql([1.0])
62
+ end
63
+ #xspecify 'prefers the lowest of cutoffs' ## need to test
64
+ end
65
+
66
+ describe "calculating an isotope distribution (yielding masses/mz's and intensities)" do
67
+
68
+ it 'gives neutral masses if no charge' do
69
+ (mzs, intensities) = subject.isotope_distribution(normalize: @norm, percent_cutoff: @pcut, isotope_table: @nist )
70
+ [mzs, intensities].each {|ar| ar.size.should == 253 }
71
+ mzs[0,5].should == [1584.8627231418, 1585.8713880574, 1586.8800529730001, 1587.8887178886002, 1588.8973828042003]
72
+ similar_distributions intensities[0,5], [0.31740518639058685, 0.35635707398291416, 0.20793431846543858, 0.08373257192958428, 0.026084566135229446]
73
+ end
74
+
75
+ it 'gives proper m/z values if the molecule is charged' do
76
+ subject.charge = -3
77
+ (mzs, ints) = subject.isotope_distribution(normalize: @norm, percent_cutoff: @pcut, isotope_table: @nist )
78
+ [mzs, ints].each {|ar| ar.size.should == 253 }
79
+ mzs[0,5].should == [-528.2881229806, -528.6243446191334, -528.9605662576668, -529.2967878962, -529.6330095347334]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+
3
+ require 'mspire/molecular_formula'
4
+
5
+ MF = Mspire::MolecularFormula
6
+ describe Mspire::MolecularFormula do
7
+
8
+ describe 'initialization' do
9
+
10
+ it 'is initialized with Hash' do
11
+ data = {H: 22, C: 12, N: 1, O: 3, S: 2}
12
+ mf = MF.new(data)
13
+ mf.to_h.should == {:H=>22, :C=>12, :N=>1, :O=>3, :S=>2}
14
+ mf.to_h.should == data
15
+ end
16
+
17
+ it 'can be initialized with charge, too' do
18
+ mf = MF["H22BeC12N1O3S2Li2", 2]
19
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
20
+ mf.charge.should == 2
21
+ end
22
+
23
+ it 'from_string or ::[] to make from a capitalized string formula' do
24
+ MF.from_string("H22BeC12N1O3S2Li2").to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
25
+
26
+ mf = MF['Ni7Se3', 1]
27
+ mf.charge.should == 1
28
+ mf.to_h.should == {:Ni=>7, :Se=>3}
29
+
30
+ # there is no such thing as the E element, so this is going to get the
31
+ # user in trouble. However, this is the proper interpretation of the
32
+ # formula.
33
+ mf = MF['Ni7SE3']
34
+ mf.charge.should == 0
35
+ mf.to_h.should == {:Ni=>7, :S=>1, :E=>3}
36
+ end
37
+
38
+ specify 'Mspire::MolecularFormula.from_aaseq(seq) from an amino acide sequence' do
39
+ mf = MF.from_aaseq("ACDEFGIHKLMNOPQRSTUVWY")
40
+ # checked to be correct with http://web.expasy.org/protparam/
41
+ mf.to_h.should == {:C=>122, :H=>183, :O=>33, :N=>33, :S=>2, :Se=>1}
42
+ end
43
+
44
+ specify 'Mspire::MolecularFormula.from_aaseq(seq, charge) from an amino acide sequence with charge' do
45
+ mf = MF.from_aaseq("ACDEFGIHKLMNOPQRSTUVWY", 3)
46
+ mf.to_h.should == {:C=>122, :H=>183, :O=>33, :N=>33, :S=>2, :Se=>1}
47
+ mf.charge.should == 3
48
+ end
49
+
50
+ it 'from_string or ::[] to make from a capitalized string formula with charge attached' do
51
+ mf = MF.from_string("H22BeC12N1O3S2Li2+")
52
+ mf.charge.should == 1
53
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
54
+
55
+ mf = MF.from_string("H22BeC12N1O3S2Li2++++")
56
+ mf.charge.should == 4
57
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
58
+
59
+ mf = MF.from_string("H22BeC12N1O3S2Li2+4")
60
+ mf.charge.should == 4
61
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
62
+
63
+ mf = MF.from_string("H22BeC12N1O3S2Li2-")
64
+ mf.charge.should == -1
65
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
66
+
67
+ mf = MF.from_string("H22BeC12N1O3S2Li2-3")
68
+ mf.charge.should == -3
69
+ mf.to_h.should == {:H=>22, :Be=>1, :C=>12, :N=>1, :O=>3, :S=>2, :Li=>2}
70
+ end
71
+ end
72
+
73
+ describe 'conversion (to_s and to_h)' do
74
+
75
+ subject {
76
+ data = {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
77
+ MF.new(data)
78
+ }
79
+
80
+ specify '#to_s a standard molecular formula, alphabetized by default' do
81
+ subject.to_s.should == "BeC12H22NO3S2"
82
+ end
83
+
84
+ specify '#to_s contains the charge by default' do
85
+ subject.charge = 3
86
+ subject.to_s.should == "BeC12H22NO3S2+3"
87
+ subject.charge = -3
88
+ subject.to_s.should == "BeC12H22NO3S2-3"
89
+ end
90
+
91
+ specify '#to_s(false) turns off charge' do
92
+ subject.charge = 3
93
+ subject.to_s(false).should == "BeC12H22NO3S2"
94
+ subject.charge = -3
95
+ subject.to_s(false).should == "BeC12H22NO3S2"
96
+ end
97
+
98
+ specify '#to_s(true, false) does not sort' do
99
+ subject.charge = 2
100
+ subject.to_s(true, false) == "H22C12NO3S2Be+2"
101
+ end
102
+
103
+ specify '#to_h converts to a hash' do
104
+ subject.charge = 2
105
+ subject.to_h.should == {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
106
+ end
107
+ end
108
+
109
+ describe 'equality' do
110
+ subject {
111
+ data = {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
112
+ MF.new(data)
113
+ }
114
+ it 'is only equal if the charge is equal' do
115
+ another = subject.dup
116
+ another.should == subject
117
+ another.charge = 2
118
+ another.should_not == subject
119
+ end
120
+ end
121
+
122
+ describe 'arithmetic' do
123
+ subject {
124
+ data = {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
125
+ MF.new(data, 2)
126
+ }
127
+ it 'can do non-destructive arithmetic' do
128
+ orig = subject.dup
129
+ reply = subject + MF["H2C3P2", 2]
130
+ reply.to_h.should == {H: 24, C: 15, N: 1, O: 3, S: 2, Be: 1, P: 2}
131
+ reply.charge.should == 4
132
+ subject.should == orig
133
+
134
+ reply = subject - MF["H2C3P2", 2]
135
+ reply.to_h.should == {H: 20, C: 9, N: 1, O: 3, S: 2, Be: 1, P: -2}
136
+ reply.charge.should == 0
137
+ subject.should == orig
138
+
139
+ by2 = subject * 2
140
+ by2.to_h.should == {H: 44, C: 24, N: 2, O: 6, S: 4, Be: 2}
141
+ by2.charge.should == 4
142
+ subject.should == orig
143
+
144
+ reply = by2 / 2
145
+ reply.to_h.should == {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
146
+ reply.charge.should == 2
147
+ subject.should == orig
148
+ end
149
+
150
+ it 'can do destructive arithmetic' do
151
+ orig = subject.dup
152
+ subject.sub!(MF["H2C3"]).to_h.should == {H: 20, C: 9, N: 1, O: 3, S: 2, Be: 1}
153
+ subject.should_not == orig
154
+ subject.add!(MF["H2C3"]).to_h.should == {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
155
+ subject.should == orig
156
+
157
+ by2 = subject.mul!(2)
158
+ subject.should_not == orig
159
+ by2.to_h.should == {H: 44, C: 24, N: 2, O: 6, S: 4, Be: 2}
160
+ by2.div!(2).to_h.should == {H: 22, C: 12, N: 1, O: 3, S: 2, Be: 1}
161
+ by2.to_h.should == orig
162
+ end
163
+
164
+ end
165
+
166
+ describe 'reading in a formula and charge from a string' do
167
+ subject { MF }
168
+ specify 'Mspire::MolecularFormula.formula_and_charge' do
169
+ subject.formula_and_charge( 'C2H4+3' ).should == ['C2H4', 3]
170
+ subject.formula_and_charge( 'C2H4+++' ).should == ['C2H4', 3]
171
+ subject.formula_and_charge( 'C2H4-').should == ['C2H4', -1]
172
+ subject.formula_and_charge( 'C2H4-2').should == ['C2H4', -2]
173
+ end
174
+ end
175
+
176
+ describe 'mass and mz' do
177
+ # (for all these, checked to make sure in close ballpark, but not
178
+ # necessarily exact, unless otherwise stated)
179
+
180
+ before do
181
+ @exact = 65.02654910101
182
+ @avg = 65.07332
183
+ @e = 0.0005486 # set with -> Mspire::Mass::ELECTRON
184
+ @exact_plus_2e = @exact + (2*@e)
185
+ end
186
+
187
+ subject {
188
+ data = {H: 3, C: 4, N: 1}
189
+ MF.new(data, -2)
190
+ }
191
+
192
+ specify '#mass (of an uncharged molecule) -> the exact mass' do
193
+ subject.charge = 0
194
+ subject.mass.should == @exact # BMRB databank says: 65.0265491015
195
+ end
196
+
197
+ specify '#mass -> the exact mass (adjusts for electrons)' do
198
+ subject.mass.should == @exact_plus_2e
199
+ end
200
+
201
+ specify '#mass (no charge adjustment)' do
202
+ subject.mass(false).should == @exact # BMRB databank says: 65.0265491015
203
+ end
204
+
205
+ specify '#avg_mass' do
206
+ subject.avg_mass.should == (@avg + 2*@e)
207
+ # changes the value
208
+ subject.charge = 0
209
+ subject.avg_mass.should == @avg # BMRB databank says: 65.073320
210
+ end
211
+
212
+ specify '#mz -> the m/z ratio' do
213
+ subject.mz.should == (@exact_plus_2e / -2.0)
214
+ subject.charge = +2
215
+ subject.mz.should == ((@exact - 2*@e) / 2.0)
216
+ end
217
+
218
+ specify '#mz(true, false) will only yield positive m/z ratio' do
219
+ subject.mz(true, false).should == (@exact_plus_2e / 2.0)
220
+ end
221
+
222
+ specify '#mz(false, true) will not consider electrons in mass determination' do
223
+ subject.mz(false, true).should == (@exact / -2.0)
224
+ end
225
+ end
226
+
227
+ end
@@ -0,0 +1,20 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+
6
+ TESTFILES = File.dirname(__FILE__) + '/testfiles'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
+
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.color_enabled = true
15
+ config.tty = true
16
+ config.formatter = :documentation # :progress, :html, :textmate
17
+ #config.formatter = :progress # :progress, :html, :textmate
18
+ end
19
+
20
+
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mspire-molecular_formula
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John T. Prince
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mspire-mass
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.14.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.14.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.1.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.1.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: fftw3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.3'
111
+ description: mspire library to handle molecular formulas (including an optional charge
112
+ state), complete with relevant chemical properties such as mass, m/z, and isotope
113
+ distribution.
114
+ email:
115
+ - jtprince@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - lib/mspire/mf.rb
126
+ - lib/mspire/molecular_formula.rb
127
+ - lib/mspire/molecular_formula/aa.rb
128
+ - lib/mspire/molecular_formula/arithmetic.rb
129
+ - lib/mspire/molecular_formula/isotope_distribution.rb
130
+ - lib/mspire/molecular_formula/mass.rb
131
+ - lib/mspire/molecular_formula/reader.rb
132
+ - lib/mspire/molecular_formula/version.rb
133
+ - mspire-molecular_formula.gemspec
134
+ - spec/mspire/mf_spec.rb
135
+ - spec/mspire/molecular_formula/aa_spec.rb
136
+ - spec/mspire/molecular_formula/isotope_distribution_spec.rb
137
+ - spec/mspire/molecular_formula_spec.rb
138
+ - spec/spec_helper.rb
139
+ homepage: ''
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.2.2
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: mspire library to handle molecular formulas (including an optional charge
163
+ state)
164
+ test_files:
165
+ - spec/mspire/mf_spec.rb
166
+ - spec/mspire/molecular_formula/aa_spec.rb
167
+ - spec/mspire/molecular_formula/isotope_distribution_spec.rb
168
+ - spec/mspire/molecular_formula_spec.rb
169
+ - spec/spec_helper.rb