isotope 0.1.0.0 → 0.3.3.0
raw patch · 7 files changed
+915/−590 lines, 7 filesdep ~megaparsecPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: megaparsec
API changes (from Hackage documentation)
- Isotope: [getComposition] :: ElementalComposition -> Map ElementSymbol Int
- Isotope: class ChemicalMass a where monoisotopicMass = getFormulaSum elementMonoisotopicMass nominalMass = getFormulaSum elementNominalMass averageMass = getFormulaSum elementAverageMass
- Isotope: infixl 6 |-|
- Isotope: infixl 7 |*|
- Isotope: type AverageMass = Double
- Isotope: type IsotopicAbundance = Double
- Isotope: type IsotopicMass = Double
- Isotope: type MonoisotopicMass = Double
- Isotope: type NominalMass = Int
+ Isotope: AverageMass :: Double -> AverageMass
+ Isotope: IsotopicAbundance :: Double -> IsotopicAbundance
+ Isotope: IsotopicMass :: Double -> IsotopicMass
+ Isotope: MonoisotopicMass :: Double -> MonoisotopicMass
+ Isotope: NominalMass :: Int -> NominalMass
+ Isotope: [getAverageMass] :: AverageMass -> Double
+ Isotope: [getElementalComposition] :: ElementalComposition -> Map ElementSymbol Int
+ Isotope: [getIsotopicAbundance] :: IsotopicAbundance -> Double
+ Isotope: [getIsotopicMass] :: IsotopicMass -> Double
+ Isotope: [getMonoisotopicMass] :: MonoisotopicMass -> Double
+ Isotope: [getNominalMass] :: NominalMass -> Int
+ Isotope: class Operators a
+ Isotope: class ToCondensedFormuala a
+ Isotope: class ToElementalComposition a where monoisotopicMass = getFormulaSum elementMonoisotopicMass nominalMass = getFormulaSum elementNominalMass averageMass = getFormulaSum elementAverageMass
+ Isotope: ele :: QuasiQuoter
+ Isotope: mkElementalComposition :: [(ElementSymbol, Int)] -> ElementalComposition
+ Isotope: newtype AverageMass
+ Isotope: newtype IsotopicAbundance
+ Isotope: newtype IsotopicMass
+ Isotope: newtype MonoisotopicMass
+ Isotope: newtype NominalMass
+ Isotope: toCondensedFormula :: ToCondensedFormuala a => a -> CondensedFormula
+ Isotope.Parsers: ele :: QuasiQuoter
+ Isotope.Parsers: elementalComposition :: Parser ElementalComposition
+ Isotope.Parsers: empiricalFormula :: Parser EmpiricalFormula
+ Isotope.Parsers: instance Language.Haskell.TH.Syntax.Lift Isotope.Base.ElementalComposition
- Isotope: (|*|) :: Int -> MolecularFormula -> MolecularFormula
+ Isotope: (|*|) :: Operators a => a -> Int -> a
- Isotope: (|+|) :: MolecularFormula -> MolecularFormula -> MolecularFormula
+ Isotope: (|+|) :: Operators a => a -> a -> a
- Isotope: (|-|) :: MolecularFormula -> MolecularFormula -> MolecularFormula
+ Isotope: (|-|) :: Operators a => a -> a -> a
- Isotope: CondensedFormula :: [Either MolecularFormula ([MolecularFormula], Int)] -> CondensedFormula
+ Isotope: CondensedFormula :: [Either MolecularFormula (CondensedFormula, Int)] -> CondensedFormula
- Isotope: [getCondensedFormula] :: CondensedFormula -> [Either MolecularFormula ([MolecularFormula], Int)]
+ Isotope: [getCondensedFormula] :: CondensedFormula -> [Either MolecularFormula (CondensedFormula, Int)]
- Isotope: averageMass :: ChemicalMass a => a -> AverageMass
+ Isotope: averageMass :: ToElementalComposition a => a -> AverageMass
- Isotope: class ChemicalMass a => ToEmpiricalFormula a
+ Isotope: class ToEmpiricalFormula a
- Isotope: class (ChemicalMass a) => ToMolecularFormula a
+ Isotope: class ToMolecularFormula a
- Isotope: elementAverageMass :: Element -> IsotopicMass
+ Isotope: elementAverageMass :: Element -> AverageMass
- Isotope: elementMonoisotopicMass :: Element -> IsotopicMass
+ Isotope: elementMonoisotopicMass :: Element -> MonoisotopicMass
- Isotope: elementNominalMass :: Element -> MassNumber
+ Isotope: elementNominalMass :: Element -> NominalMass
- Isotope: monoisotopicMass :: ChemicalMass a => a -> MonoisotopicMass
+ Isotope: monoisotopicMass :: ToElementalComposition a => a -> MonoisotopicMass
- Isotope: nominalMass :: ChemicalMass a => a -> NominalMass
+ Isotope: nominalMass :: ToElementalComposition a => a -> NominalMass
- Isotope: toElementalComposition :: ChemicalMass a => a -> ElementalComposition
+ Isotope: toElementalComposition :: ToElementalComposition a => a -> ElementalComposition
- Isotope.Parsers: subFormula :: Parser MolecularFormula
+ Isotope.Parsers: subFormula :: Parser (ElementSymbol, Int)
Files
- README.md +66/−27
- isotope.cabal +3/−2
- src/Isotope.hs +19/−17
- src/Isotope/Base.hs +525/−421
- src/Isotope/Parsers.hs +75/−58
- test/Isotope/BaseSpec.hs +198/−56
- test/Isotope/ParsersSpec.hs +29/−9
README.md view
@@ -6,11 +6,12 @@ * [Design](#design) * [Isotopic, integer, monoisotopic, nominal and average masses](#isotopic-integer-monoisotopic-nominal-and-average-masses) * [Element symbols](#element-symbols)- * [Molecular, condensed and empirical formulae](#molecular-condensed-and-empirical-formulae)- * [Molecular, condensed and empirical formula QuasiQuoters](#molecular-condensed-and-empirical-formula-QuasiQuoters)- * [Conversion between CondensedFormula, MolecularFormula and EmpiricalFormula data types](#conversion-between-condensedformula-molecularformula-and-empiricalformula-data-types)+ * [Elemental composition and molecular, condensed and empirical formulae](#elemental-composition-and-molecular-condensed-and-empirical-formulae)+ * [ElementalComposition, MolecularFormula, CondensedFormula, EmpiricalFormula quasiquoters](#elementalcomposition-molecularformula-condensedformula-empiricalformula-quasiquoters)+ * [Conversion between ElementalComposition, MolecularFormula, CondensedFormula and EmpiricalFormula data types](#conversion-between-elementalcomposition-molecularformula-condensedformula-and-empiricalformula-data-types) * [Operators for working with molecular formulae](#operators-for-working-with-molecular-formulae)- * [ChemicalMass type class](#ElementalComposition-type-class)+ * [ToElementalComposition type class](#elementalcomposition-type-class)+ * [Behaviour of ElementalComposition, MolecularFormula, CondensedFormula and EmpiricalFormula data types](#behaviour-of-elementalcomposition-molecularformula-condensedformula-and-empiricalformula-data-types) * [Additional functions accepting an ElementSymbol as input](#additional-functions-accepting-an-elementsymbol-as-input) * [Comparison to other chemistry libraries](#comparison-to-other-chemistry-libraries) * [Radium](#radium)@@ -37,22 +38,24 @@ Integer mass | The mass of an isotope rounded to the nearest integer value. Monoisotopic mass | The mass of the most abundant isotope for an element or the sum of the masses of the most abundant isotope of each element for a molecule. Nominal mass | The integer mass of the most abundant isotope for an element or the sum of integer masses of the most abundant isotope of each element for a molecule.-Average mass | The average mass of an element or molecule based on naturally-occurring abundances. In the isotopes library, average mass is used in place of atomic mass and molecular mass.+Average mass | The average mass of an element or molecule based on naturally-occurring isotopic abundances. In the Isotope library, average mass is used in place of atomic mass and molecular mass. For more detailed discussion regarding the concept of mass in mass spectrometry, please refer to "Molecular Weight and the Nominal Mass, Monoisotopic Mass and Average Molar Mass" by Prof. O. David Sparkman [1]. ### Element symbols -In Isotope, element symbols are represented by the enumeration type, `ElementSymbol`, i.e. `data ElementSymbol = H | He | Li | Be .....`. This is advantageous over the use of strings to represent element symbols (i.e. `type ElementSymbol = String`) since it increases type safety. Moreover, values of type `ElementSymbol` can be used as keys within maps as an intuitive way to map elements to their properties. Isotope presently contains information on the isotopic masses and relative abundances for all elements from Hydrogen to Bismuth and Thorium and Uranium.+In Isotope, element symbols are represented by the enumeration type, `ElementSymbol`, i.e. `data ElementSymbol = H | He | Li | Be .....`. This is advantageous over the use of strings to represent element symbols (i.e. `type ElementSymbol = String`) since it increases type safety. Moreover, values of type `ElementSymbol` can be used as keys within maps as an intuitive way to map elements to their properties. Isotope presently contains information on the isotopic masses and relative abundances for all elements from Hydrogen to Bismuth and Thorium and Uranium (excluding Technetium and promethium). -### Molecular, condensed and empirical formulae+### Elemental composition and molecular, condensed and empirical formulae -In the Isotope library, a distinction between molecular, condensed and empirical formulae is made. Molecular formulae contain the total number of atoms for each element of a molecule while condensed formulae give information on the connectivity of atoms within molecules. For example, the molecule trimethylamine has a molecular formula of C3H9N and a condensed formula of N(CH3)3. Here the molecular formula indicates trimethyelamine has a total of 3 carbon atoms, 9 hydrogen atoms and 1 nitrogen whereas the condensed formula indicates trimethylamine has 3 methyl groups bonded to a central nitrogen. Conversely, an empirical formula is the simplest integer ratio for the atoms of a compound. For example, the molecular formula of benzene is C6H6 whereas the empirical formula of benzene is simply CH.+In the Isotope library, a distinction between elemental composition and molecular, condensed and empirical formulae is made. Molecular formulae contain the total number of atoms for each element of a molecule while condensed formulae give information on the connectivity of atoms within molecules. For example, the molecule trimethylamine has a molecular formula of C3H9N and a condensed formula of N(CH3)3. Here the molecular formula indicates trimethyelamine has a total of 3 carbon atoms, 9 hydrogen atoms and 1 nitrogen whereas the condensed formula indicates trimethylamine has 3 methyl groups bonded to a central nitrogen. Conversely, an empirical formula is the simplest integer ratio for the atoms of a compound. For example, the molecular formula of benzene is C6H6 whereas the empirical formula of benzene is simply CH. Molecular, condensed and empirical formulae may all be considered to have an elemental composition. That is, the total number of atoms for each element for a formulae. -### Molecular, condensed and empirical formula QuasiQuoters+In Isotope, `CondensedFormula` is defined as a recursive data type. This allows deep nesting within `CondensedFormula`. For example, triisopropylamine may be expressed as `[con|N(CH(CH3)2)3|]`. This nesting can therefore be used to convey greater structural detail. -The QuasiQuoters, `mol`, `con` and `emp`, are provided for `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` data types, respectively. Therefore, shorthand notation can be used when working with molecular, condensed and empirical formulae. The use of molecular, condensed and empirical formulae QuasiQuoters requires the use of the `QuasiQuotes` language extension (`:set -XQuasiQuotes` can be added to the `.ghci` file when working in GHCi).+### `ElementalComposition`, `MolecularFormula`, `CondensedFormula`, `EmpiricalFormula` quasiquoters++The quasiquoters, `ele`, `mol`, `con` and `emp` are provided for `ElementalComposition`, `MolecularFormula`, `CondensedFormula`, `EmpiricalFormula` and data types, respectively. This allows the use of shorthand notation when working with elemental compositions as well as molecular, condensed and empirical formulae. The use of quasiquoters requires the use of the `QuasiQuotes` language extension (`:set -XQuasiQuotes` can be added to the `.ghci` file when working in GHCi). ```haskell GHCi> [mol|CH4|] MolecularFormula {getMolecularFormula = fromList [(H,4),(C,1)]}@@ -67,30 +70,32 @@ expecting "Ag", "Al", "Ar", "As", "Au", "Ba", "Be", "Bi", "Br", "Ca", "Cd", "Ce", "Cl", "Co", "Cr", "Cs", "Cu", "Dy", "Er", "Eu", "Fe", "Ga", "Gd", "Ge", "He", "Hf", "Hg", "Ho", "In", "Ir", "Kr", "La", "Li", "Lu", "Mg", "Mn", "Mo", "Na", "Nb", "Nd", "Ne", "Ni", "Os", "Pa", "Pb", "Pd", "Pm", "Pr", "Pt", "Rb", "Re", "Rh", "Ru", "Sb", "Sc", "Se", "Si", "Sm", "Sn", "Sr", "Ta", "Tb", "Tc", "Te", "Th", "Ti", "Tl", "Tm", "Xe", "Yb", "Zn", "Zr", '(', 'B', 'C', 'F', 'H', 'I', 'K', 'N', 'O', 'P', 'S', 'U', 'V', 'W', 'Y', or end of input ``` -### Conversion between `CondensedFormula`, `MolecularFormula` and `EmpiricalFormula` data types+### Conversion between `ElementalComposition`, `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` data types -A condensed formula can be converted to a molecular or empirical formula and a molecular formula can be converted to an empirical formula. In Isotope, this functionality is provided by two type classes, `ToMolecularFormula` and `ToEmpiricalFormula`, which contain the methods, `toMolecularFormula` and `toEmpiricalFormula`, respectively.+A condensed formula can be converted to a molecular or empirical formula and a molecular formula can be converted to an empirical formula. All formulae can be converted to an `ElementalComposition` and an `ElementalComposition` may be converted to an `EmpiricalFormula` In Isotope, this functionality is provided by three type classes, `ToElementalComposition`, `ToMolecularFormula` and `ToEmpiricalFormula`, which contain the methods, `ToElementalComposition`, `toMolecularFormula` and `toEmpiricalFormula`, respectively. In addition, a `ToCondensedFormula` type class containing the `toCondensedFormula` method is also provided for users of Isotope. ```haskell GHCi> let butane = [con|CH3(CH2)2CH3|]+GHCi> toElementalComposition butane+ElementalComposition {getElementalComposition = fromList [(H,10),(C,4)]} GHCi> toMolecularFormula butane MolecularFormula {getMolecularFormula = fromList [(H,10),(C,4)]} GHCi> toEmpiricalFormula butane EmpiricalFormula {getEmpiricalFormula = fromList [(H,5),(C,2)]} ```-When using condensed, molecular or empirical formula QuasiQuoters, it is possible to this implicitly. For example, the QuasiQuoter `emp` could be applied to the condensed formula "CH3(CH2)2CH3" used above to yield a value of type `EmpiricalFormula`.+When using quasiquoters, it is possible to do this implicitly. For example, the quasiquoter `emp` could be applied to the condensed formula CH3(CH2)2CH3 used above to yield a value of type `EmpiricalFormula`. ```haskell GHCi> [emp|CH3(CH2)2CH3|] EmpiricalFormula {getEmpiricalFormula = fromList [(H,5),(C,2)]} ``` -### Operators for working with molecular formulae+### Operators for working with formulae and masses -The Isotope library comes with three operators for working with molecular formulae; `|+|`, `|-|` and `|*|`. These operators have the same fixity and associativity as `+`, `-` and `*`, respectively. This allows us to the `|+|`, `|-|` and `|*|` operators in an intuitive manner (i.e., like basic arithmetic). For example, we could define the molecule formula of propane in terms of its building blocks; that is, 2 methyl groups and 1 methylene group.+The Isotope library comes with three operators for working with formulae and masses; `|+|`, `|-|` and `|*|`. These operators are provided in the `Operators` type class and have the same fixity and associativity as `+`, `-` and `*`, respectively. This allows us to the `|+|`, `|-|` and `|*|` operators in an intuitive manner (i.e., like basic arithmetic). For example, we could define the molecule formula of propane in terms of its building blocks; that is, 2 methyl groups and 1 methylene group. ```haskell GHCi> let methyl = [mol|CH3|] GHCi> let methylene = [mol|CH2|]-GHCi> let propane = 2 |*| methyl |+| methylene+GHCi> let propane = methyl |*| 2 |+| methylene GHCi> propane MolecularFormula {getMolecularFormula = fromList [(H,8),(C,3)]} ```@@ -101,22 +106,56 @@ MolecularFormula {getMolecularFormula = fromList [(H,6),(C,3)]} ``` -### `ChemicalMass` type class+### `ToElementalComposition` type class -The `ChemicalMass` type class has four methods; `toElementalComposition`, `monoisotopicMass`, `nominalMass` and `averageMass`, where `toElementalComposition` is the minimal complete definition. `ElementSymbol`, `EmpiricalFormula`, `MolecularFormula` and `CondensedFormula` are instances of `ChemicalMass`. This provides a uniform approach to working with elements, empirical formulae, molecular formulae and condensed formulae.+In addition to the `toElementalComposition` method, the `ToElementalComposition` type class has three other methods; `monoisotopicMass`, `nominalMass` and `averageMass`. (`toElementalComposition` is the minimal complete definition.) `ElementSymbol`, `ElementalComposition`, `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` all have instances of `ToElementalComposition`. This provides a uniform approach to working with elements, elemental compositions, molecular formulae, condensed formulae and empirical formulae. ```haskell-GHCi> nominalMass C-12-GHCi> averageMass [mol|CH4|]-16.042498912958358-GHCi> monoisotopicMass [mol|N(CH3)3|]-59.073499294499996+ghci> nominalMass C+NominalMass {getNominalMass = 12}+ghci> averageMass [mol|CH4|]+AverageMass {getAverageMass = 16.042498912958358}+ghci> monoisotopicMass [mol|N(CH3)3|]+MonoisotopicMass {getMonoisotopicMass = 59.073499294499996} ```++### Behaviour of `ElementalComposition`, `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` data types++It is possible to combine two `MolecularFormula` to form another `MolecularFormula`. For example, we could combine the molecular formulae of two methyl groups and a methylene group and the resulting molecular formula would be that of propane, regardless of the order of operators. Also, if we add an empty molecular formula to a molecular formula, we get the same molecular formula. These properties indicate that combining molecular formulae is monoidal. Consequently, a monoid instance is provided for `MolecularFormula` where `mempty` is `emptyFormula` and `mappend` is `(|+|)`. This monoid instance can be very helpful in writing clear, concise code. An example of this is provided below. (Note that in this example `a`, `b`, `c` have type `FattyAcyl` and it is assumed a `ToMolecularFormula` instance for `FattyAcyl` is already provided.)++```haskell+instance ToMolecularFormula Triacylglycerol where+ toMolecularFormula (Triacylglycerol a b c) =+ mkMolecularFormula [(C, 3), (H, 5)] |+| foldMap toMolecularFormula [a, b, c]+```+Similar to `MolecularFormula`, a monoid instance is also provided for `CondensedFormula`. (Note, however, that `MolecularFormula` is a commutative monoid, i.e., `a mappend b == b mappend a`, whereas `CondensedFormula` is not.) On the other hand, combining `EmpiricalFormula` is not useful. For example, combining the empirical formula for ethene with the empirical formula of water is not the same as the empirical formula for ethanol.+```haskell+[emp|CH2|] `plus` [emp|H2O|] /= [emp|C2H5O|] -- pseudo Haskell code - `plus` is not provided in Isotope+```+Monoid instances are also provide for `ElementalComposition`, `MonoisotopicMass`, `NominalMass`, `AverageMass` and `IsotopicMass`.++#### Laws for `ElementalComposition`, `MolecularFormula`, `EmpiricalFormula` and `CondensedFormula` data types++Instances of `ToElementalComposition`, `ToMolecularFormula`, `ToCondensedFormuala` and `ToEmpiricalFormula` should abide by three laws. 1) Applying `toEmpiricalFormula` to a `CondensedFormula` should give the same result as applying `toMolecularFormula` compose `toEmpiricalFormula`.+2) Applying `toElementalComposition` to a `CondensedFormula` should give the same result as applying `toMolecularFormula` compose `toElementalComposition`.+3) Applying `toElementalComposition . toEmpiricalFormula` to an `EmpiricalFormula` should return the same `EmpiricalFormula`.++```haskell+toEmpiricalFormula x = toEmpiricalFormula . toMolecularFormula $ x++toElementalComposition x = toElementalComposition . toMolecularFormula $ x++(toEmpiricalFormula . toElementalComposition) y = y++where x :: CondensedFormula+ y :: EmpiricalFormula+```+ ### Additional functions accepting an `ElementSymbol` as input+ Isotope also provides a range of addition functions which accepts an `ElementSymbol` as input. For example, to get the masses of all isotopes for titanium, we simply have to pass the element symbol `Ti` to the function `isotopicMasses`. ```haskell-GHCi> isotopicMasses Ti-[45.95262772,46.95175879,47.94794198,48.94786568,49.94478689]+ghci> isotopicMasses Ti+[IsotopicMass {getIsotopicMass = 45.95262772},IsotopicMass {getIsotopicMass = 46.95175879},IsotopicMass {getIsotopicMass = 47.94794198},IsotopicMass {getIsotopicMass = 48.94786568},IsotopicMass {getIsotopicMass = 49.94478689}] ``` ## Comparison to other chemistry libraries@@ -134,7 +173,7 @@ Isotopic profiles currently cannot be calculated for molecular formulae. This is a major limitation since isotopic profiles are important in mass spectrometry. Therefore, this functionality should be added to Isotope in a future version. Since calculating isotopic profiles is computationally expensive, this feature could be introduced using Rust if performance is an issue using pure Haskell code. (Rust is a modern systems programming language with a strong type system and memory safety [6].) -To increase compile-time checks, refinement types could be introduced using LiquidHaskell [7]. For example, not all elements have naturally-occurring isotopes and such elements therefore do not have an average mass. Using LiquidHaskell, the function `averageMass` could be refined to only accept elements with naturally-occurring isotopes. If such a direction is taken, two separate libraries may be maintained; one using LiquidHaskell and the other using only conventional Haskell code.+To increase compile-time checks, refinement types could be introduced using LiquidHaskell [7]. For example, not all elements have naturally-occurring isotopes and such elements therefore do not have an average mass. Using LiquidHaskell, the function `averageMass` could be refined to only accept elements with naturally-occurring isotopes. ## Contributions
isotope.cabal view
@@ -1,5 +1,5 @@ name: isotope-version: 0.1.0.0+version: 0.3.3.0 synopsis: Isotopic masses and relative abundances. description: Please see README.md homepage: https://github.com/Michaelt293/Element-isotopes/blob/master/README.md@@ -20,7 +20,7 @@ , Isotope.Parsers build-depends: base >= 4.7 && < 5 , containers >= 0.5 && < 0.6- , megaparsec >= 4 && < 5+ , megaparsec >= 4 && < 6 , template-haskell , th-lift default-language: Haskell2010@@ -34,6 +34,7 @@ , isotope , hspec , QuickCheck+ , megaparsec >= 4 && < 6 other-modules: Isotope.BaseSpec , Isotope.ParsersSpec ghc-options: -threaded -rtsopts -with-rtsopts=-N
src/Isotope.hs view
@@ -13,15 +13,17 @@ masses. For more information regarding Isotope, please refer to the README. -} module Isotope (- -- * Type synonyms for masses- IntegerMass- , MonoisotopicMass- , NominalMass- , AverageMass- , IsotopicMass- -- * Other type synonyms+ -- * Infix operators used in `Isotope`+ Operators(..)+ -- * Types for masses+ , IntegerMass+ , MonoisotopicMass(..)+ , NominalMass(..)+ , AverageMass(..)+ , IsotopicMass(..)+ -- * Other types , ElementName- , IsotopicAbundance+ , IsotopicAbundance(..) , AtomicNumber , ProtonNumber , NeutronNumber@@ -55,28 +57,28 @@ , isotopicMasses , integerMasses , isotopicAbundances- -- * 'ChemicalMass' type class- , ChemicalMass(..)+ -- * Formula type class+ , Formula(..) -- * Elemental composition , ElementalComposition(..)+ , ToElementalComposition(..)+ , mkElementalComposition -- * Molecular formulae , MolecularFormula(..)- , (|+|)- , (|-|)- , (|*|)- , mkMolecularFormula- , Formula(..) , ToMolecularFormula(..)+ , mkMolecularFormula -- * Condensed formulae , CondensedFormula(..)+ , ToCondensedFormuala(..) -- * Empirical formulae , EmpiricalFormula(..) , ToEmpiricalFormula(..) , mkEmpiricalFormula- -- * QuasiQuoter+ -- * QuasiQuoters+ , ele , mol- , emp , con+ , emp ) where import Isotope.Base
src/Isotope/Base.hs view
@@ -9,24 +9,27 @@ This module defines the majority of the types used in the Isotope library. A large number of type synonyms are provided to improve readability. Of particular-importance are the 'Isotope', 'Element', 'ElementSymbol', `EmpiricalFormula`,-`MolecularFormula` and 'CondensedFormula' types. 'ElementSymbol' is an-enumeration type of all the element symbols used in the Isotopes library.+importance are the 'Isotope', 'Element', 'ElementSymbol', `ElementalComposition`,+`MolecularFormula`,'CondensedFormula' and `EmpiricalFormula` data types.+'ElementSymbol' is an enumeration type of all the element symbols used in the+Isotopes library. -} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# OPTIONS_HADDOCK hide #-} module Isotope.Base (- -- Type synonyms for masses- IntegerMass- , MonoisotopicMass- , NominalMass- , AverageMass- , IsotopicMass- -- Other type synonyms+ -- Infix operators used in `Isotope`+ Operators(..)+ -- Types for masses+ , IntegerMass+ , MonoisotopicMass(..)+ , NominalMass(..)+ , AverageMass(..)+ , IsotopicMass(..)+ -- Other types , ElementName- , IsotopicAbundance+ , IsotopicAbundance(..) , AtomicNumber , ProtonNumber , NeutronNumber@@ -60,20 +63,19 @@ , isotopicMasses , integerMasses , isotopicAbundances- -- 'ChemicalMass' type class- , ChemicalMass(..)+ -- Formula type class+ , Formula(..) -- Elemental composition , ElementalComposition(..)+ , ToElementalComposition(..)+ , mkElementalComposition -- Molecular formulae , MolecularFormula(..)- , (|+|)- , (|-|)- , (|*|)- , mkMolecularFormula- , Formula(..) , ToMolecularFormula(..)+ , mkMolecularFormula -- Condensed formulae , CondensedFormula(..)+ , ToCondensedFormuala(..) -- Empirical formula , EmpiricalFormula(..) , ToEmpiricalFormula(..)@@ -94,38 +96,92 @@ import Data.Ord import Data.List (elemIndex, sortBy) import Data.Maybe (fromJust)+import Data.Monoid ----------------------------------------------------------------------------------- Type synonyms for masses +-- | Infix operators used in `Isotope`. These operators support addition,+-- subtraction and multiplication.+class Operators a where+ (|+|) :: a -> a -> a+ (|-|) :: a -> a -> a+ (|*|) :: a -> Int -> a+ infixl 6 |+|+ infixl 7 |*|+ infixl 6 |-|++--------------------------------------------------------------------------------+-- Types for masses+ -- | Integer mass for an isotope.-type IntegerMass = MassNumber+type IntegerMass = MassNumber -- | The exact mass of the most abundant isotope for an element or the sum of -- the exact masses of the most abundant isotope of each element for a -- molecular formula.-type MonoisotopicMass = Double+newtype MonoisotopicMass = MonoisotopicMass { getMonoisotopicMass :: Double }+ deriving (Show, Eq, Ord) +instance Monoid MonoisotopicMass where+ mempty = MonoisotopicMass 0+ mappend = (|+|)++instance Operators MonoisotopicMass where+ MonoisotopicMass x |+| MonoisotopicMass y = MonoisotopicMass $ x + y+ MonoisotopicMass x |-| MonoisotopicMass y = MonoisotopicMass $ x - y+ MonoisotopicMass x |*| y = MonoisotopicMass $ x * fromIntegral y+ -- | The integer mass of the most abundant isotope for an element or the sum of -- integer mass of the most abundant isotope of each element for a chemical -- formula.-type NominalMass = Int+newtype NominalMass = NominalMass { getNominalMass :: Int }+ deriving (Show, Eq, Ord) +instance Monoid NominalMass where+ mempty = NominalMass 0+ mappend = (|+|)++instance Operators NominalMass where+ NominalMass x |+| NominalMass y = NominalMass $ x + y+ NominalMass x |-| NominalMass y = NominalMass $ x - y+ NominalMass x |*| y = NominalMass $ x * y+ -- | The average mass of an element or molecular formula based on -- naturally-occurring abundances.-type AverageMass = Double+newtype AverageMass = AverageMass { getAverageMass :: Double }+ deriving (Show, Eq, Ord) +instance Monoid AverageMass where+ mempty = AverageMass 0+ mappend = (|+|)++instance Operators AverageMass where+ AverageMass x |+| AverageMass y = AverageMass $ x + y+ AverageMass x |-| AverageMass y = AverageMass $ x - y+ AverageMass x |*| y = AverageMass $ x * fromIntegral y+ -- | The exact mass of an isotope.-type IsotopicMass = Double+newtype IsotopicMass = IsotopicMass { getIsotopicMass :: Double }+ deriving (Show, Eq, Ord) +instance Monoid IsotopicMass where+ mempty = IsotopicMass 0+ mappend = (|+|)++instance Operators IsotopicMass where+ IsotopicMass x |+| IsotopicMass y = IsotopicMass $ x + y+ IsotopicMass x |-| IsotopicMass y = IsotopicMass $ x - y+ IsotopicMass x |*| y = IsotopicMass $ x * fromIntegral y+ ----------------------------------------------------------------------------------- Other type synonyms+-- Other types -- | The name of an element. type ElementName = String -- | The natural abundance of an isotope.-type IsotopicAbundance = Double+newtype IsotopicAbundance = IsotopicAbundance { getIsotopicAbundance :: Double }+ deriving (Show, Eq, Ord) -- | Atomic number of an element. type AtomicNumber = Int@@ -144,6 +200,7 @@ type MassNumber = Int --------------------------------------------------------------------------------+ -- 'Isotope' and 'Element' data types -- | An 'Isotope' has three parameters; 'Nucleons', 'IsotopeMass' and@@ -182,7 +239,8 @@ -- | Returns the most abundant naturally-occurring isotope for an element. elementMostAbundantIsotope :: Element -> Isotope-elementMostAbundantIsotope e = maximumBy (comparing isotopicAbundance) $ isotopes' e+elementMostAbundantIsotope e =+ maximumBy (comparing isotopicAbundance) $ isotopes' e -- | Exact masses for all naturally-occurring isotopes for an element. elementIsotopicMasses :: Element -> [IsotopicMass]@@ -197,17 +255,22 @@ elementIsotopicAbundances e = isotopicAbundance <$> isotopes' e -- | Monoistopic mass for an element.-elementMonoisotopicMass :: Element -> IsotopicMass-elementMonoisotopicMass = isotopicMass . elementMostAbundantIsotope+elementMonoisotopicMass :: Element -> MonoisotopicMass+elementMonoisotopicMass =+ MonoisotopicMass . getIsotopicMass . isotopicMass . elementMostAbundantIsotope -- | Nominal mass for an element.-elementNominalMass :: Element -> MassNumber-elementNominalMass = massNumber . nucleons . elementMostAbundantIsotope+elementNominalMass :: Element -> NominalMass+elementNominalMass =+ NominalMass . massNumber . nucleons . elementMostAbundantIsotope -- | Average mass of an element.-elementAverageMass :: Element -> IsotopicMass-elementAverageMass e = sum [isotopicMass x * isotopicAbundance x |- x <- isotopes' e]+elementAverageMass :: Element -> AverageMass+elementAverageMass e =+ foldMap (\x -> AverageMass $+ getIsotopicMass (isotopicMass x) *+ getIsotopicAbundance (isotopicAbundance x))+ (isotopes' e) -- Mass number for an isotope. Mass number is the number of protons plus the -- number of neutrons.@@ -216,303 +279,309 @@ -------------------------------------------------------------------------------- -- 'elements' - a map containing isotopic data for each element. +-- Helper function to convert a tuple to an `Isotope`.+tupleToIsotope :: (Nucleons, Double, Double) -> Isotope+tupleToIsotope (nucl, mass, abun) =+ Isotope nucl (IsotopicMass mass) (IsotopicAbundance abun)++tupleToElement :: (AtomicNumber, ElementName, [(Nucleons, Double, Double)]) -> Element+tupleToElement (atomNum, name, isotopes) =+ Element atomNum name (tupleToIsotope <$> isotopes)+ -- | Map of the periodic table. All data on isotopic masses and abundances is -- contained within this map. elements :: Map ElementSymbol Element-elements = fromList- [ (H, Element 1 "hydrogen" [ Isotope (1, 0) 1.00782503223 0.999885- , Isotope (1, 1) 2.01410177812 0.000115 ])- , (He, Element 2 "helium" [ Isotope (2, 1) 3.0160293201 0.00000134- , Isotope (2, 2) 4.00260325413 0.99999866 ])- , (Li, Element 3 "lithium" [ Isotope (3, 3) 6.0151228874 0.0759- , Isotope (3, 4) 7.0160034366 0.9241 ])- , (Be, Element 4 "beryllium" [ Isotope (4, 5) 9.012183065 1 ])- , (B, Element 5 "boron" [ Isotope (5, 5) 10.01293695 0.199- , Isotope (5, 6) 11.00930536 0.801 ])- , (C, Element 6 "carbon" [ Isotope (6, 6) 12.0000000 0.9893- , Isotope (6, 7) 13.00335483507 0.0107 ])- , (N, Element 7 "nitrogen" [ Isotope (7, 7) 14.00307400443 0.99636- , Isotope (7, 8) 15.00010889888 0.00364 ])- , (O, Element 8 "oxygen" [ Isotope (8, 8) 15.99491461957 0.99757- , Isotope (8, 9) 16.99913175650 0.00038- , Isotope (8, 10) 17.99915961286 0.00205 ])- , (F, Element 9 "fluorine" [ Isotope (9, 10) 18.99840316273 1 ])- , (Ne, Element 10 "neon" [ Isotope (10, 10) 19.9924401762 0.9048- , Isotope (10, 11) 20.993846685 0.0027- , Isotope (10, 12) 21.991385114 0.0925 ])- , (Na, Element 11 "sodium" [ Isotope (11, 12) 22.9897692820 1 ])- , (Mg, Element 12 "magnesium" [ Isotope (12, 12) 23.985041697 0.7899- , Isotope (12, 13) 24.985836976 0.1000- , Isotope (12, 14) 25.982592968 0.1101 ])- , (Al, Element 13 "aluminium" [ Isotope (13, 14) 26.98153853 1 ])- , (Si, Element 14 "silicon" [ Isotope (14, 14) 27.97692653465 0.92223- , Isotope (14, 15) 28.97649466490 0.04685- , Isotope (14, 16) 29.973770136 0.03092 ])- , (P, Element 15 "phosphorous" [ Isotope (15, 16) 30.97376199842 1 ])- , (S, Element 16 "sulfur" [ Isotope (16, 16) 31.9720711744 0.9499- , Isotope (16, 17) 32.9714589098 0.0075- , Isotope (16, 18) 33.967867004 0.0425- , Isotope (16, 20) 35.96708071 0.0001 ])- , (Cl, Element 17 "chlorine" [ Isotope (17, 18) 34.968852682 0.7576- , Isotope (17, 20) 36.965902602 0.2424 ])- , (Ar, Element 18 "argon" [ Isotope (18, 18) 35.967545105 0.003336- , Isotope (18, 20) 37.96273211 0.000629- , Isotope (18, 22) 39.9623831237 0.996035 ])- , (K, Element 19 "potassium" [ Isotope (19, 20) 38.9637064864 0.932581- , Isotope (19, 21) 39.963998166 0.000117- , Isotope (19, 22) 40.9618252579 0.067302 ])- , (Ca, Element 20 "calcium" [ Isotope (20, 20) 39.962590863 0.96941- , Isotope (20, 22) 41.95861783 0.00647- , Isotope (20, 23) 42.95876644 0.00135- , Isotope (20, 24) 43.95548156 0.02086- , Isotope (20, 26) 45.9536890 0.00004- , Isotope (20, 28) 47.95252276 0.00187 ])- , (Sc, Element 21 "scandium" [ Isotope (21, 24) 44.95590828 1 ])- , (Ti, Element 22 "titanium" [ Isotope (22, 24) 45.95262772 0.0825- , Isotope (22, 25) 46.95175879 0.0744- , Isotope (22, 26) 47.94794198 0.7372- , Isotope (22, 27) 48.94786568 0.0541- , Isotope (22, 28) 49.94478689 0.0518 ])- , (V, Element 23 "vanadium" [ Isotope (23, 27) 49.94715601 0.00250- , Isotope (23, 28) 50.94395704 0.99750 ])- , (Cr, Element 24 "chromium" [ Isotope (24, 26) 49.94604183 0.04345- , Isotope (24, 28) 51.94050623 0.83789- , Isotope (24, 29) 52.94064815 0.09501- , Isotope (24, 30) 53.93887916 0.02365 ])- , (Mn, Element 25 "manganese" [ Isotope (25, 30) 54.93804391 1 ])- , (Fe, Element 26 "iron" [ Isotope (26, 28) 53.93960899 0.05845- , Isotope (26, 30) 55.93493633 0.91754- , Isotope (26, 31) 56.93539284 0.02119- , Isotope (26, 32) 57.93327443 0.00282 ])- , (Co, Element 27 "cobalt" [ Isotope (27, 32) 58.93319429 1 ])- , (Ni, Element 28 "nickel" [ Isotope (28, 30) 57.93534241 0.68077- , Isotope (28, 32) 59.93078588 0.26223- , Isotope (28, 33) 60.93105557 0.011399- , Isotope (28, 34) 61.92834537 0.036346- , Isotope (28, 36) 63.92796682 0.009255 ])- , (Cu, Element 29 "copper" [ Isotope (29, 34) 62.92959772 0.6915- , Isotope (29, 36) 64.92778970 0.3085 ])- , (Zn, Element 30 "zinc" [ Isotope (30, 34) 63.92914201 0.4917- , Isotope (30, 36) 65.92603381 0.2773- , Isotope (30, 37) 66.92712775 0.0404- , Isotope (30, 38) 67.92484455 0.1845- , Isotope (30, 40) 69.9253192 0.0061 ])- , (Ga, Element 31 "gallium" [ Isotope (31, 38) 68.9255735 0.60108- , Isotope (31, 40) 70.92470258 0.39892 ])- , (Ge, Element 32 "germanium" [ Isotope (32, 38) 69.92424875 0.2057- , Isotope (32, 40) 71.922075826 0.2745- , Isotope (32, 41) 72.923458956 0.0775- , Isotope (32, 42) 73.921177761 0.3650- , Isotope (32, 44) 75.921402726 0.0773 ])- , (As, Element 33 "arsenic" [ Isotope (33, 42) 74.92159457 1 ])- , (Se, Element 34 "selenium" [ Isotope (34, 40) 73.922475934 0.0089- , Isotope (34, 42) 75.919213704 0.0937- , Isotope (34, 43) 76.919914154 0.0763- , Isotope (34, 44) 77.91730928 0.2377- , Isotope (34, 46) 79.9165218 0.4961- , Isotope (34, 48) 81.9166995 0.0873 ])- , (Br, Element 35 "bromine" [ Isotope (35, 44) 78.9183376 0.5069- , Isotope (35, 46) 80.9162897 0.4931 ])- , (Kr, Element 36 "krypton" [ Isotope (36, 42) 77.92036494 0.00355- , Isotope (36, 44) 79.91637808 0.02286- , Isotope (36, 46) 81.91348273 0.11593- , Isotope (36, 47) 82.91412716 0.11500- , Isotope (36, 48) 83.9114977282 0.56987- , Isotope (36, 50) 85.9106106269 0.17279 ])- , (Rb, Element 37 "rubidium" [ Isotope (37, 48) 84.9117897379 0.7217- , Isotope (37, 50) 86.9091805310 0.2783 ])- , (Sr, Element 38 "strontium" [ Isotope (38, 46) 83.9134191 0.0056- , Isotope (38, 48) 85.9092606 0.0986- , Isotope (38, 49) 86.9088775 0.0700- , Isotope (38, 50) 87.9056125 0.8258 ])- , (Y, Element 39 "yttrium" [ Isotope (39, 50) 88.9058403 1 ])- , (Zr, Element 40 "zirconium" [ Isotope (40, 50) 89.9046977 0.5145- , Isotope (40, 51) 90.9056396 0.1122- , Isotope (40, 52) 91.9050347 0.1715- , Isotope (40, 54) 93.9063108 0.1738- , Isotope (40, 56) 95.9082714 0.0280 ])- , (Nb, Element 41 "niobium" [ Isotope (41, 52) 92.9063730 1 ])- , (Mo, Element 42 "molybdenum" [ Isotope (42, 50) 91.90680796 0.1453- , Isotope (42, 52) 93.90508490 0.0915- , Isotope (42, 53) 94.90583877 0.1584- , Isotope (42, 54) 95.90467612 0.1667- , Isotope (42, 55) 96.90601812 0.0960- , Isotope (42, 56) 97.90540482 0.2439- , Isotope (42, 58) 99.9074718 0.0982 ])- , (Ru, Element 44 "ruthenium" [ Isotope (44, 52) 95.90759025 0.0554- , Isotope (44, 54) 97.9052868 0.0187- , Isotope (44, 55) 98.9059341 0.1276- , Isotope (44, 56) 99.9042143 0.1260- , Isotope (44, 57) 100.9055769 0.1706- , Isotope (44, 58) 101.9043441 0.3155- , Isotope (44, 59) 103.9054275 0.1862 ])- , (Rh, Element 45 "rhodium" [ Isotope (45, 58) 102.9054980 1 ])- , (Pd, Element 46 "palladium" [ Isotope (46, 56) 101.9056022 0.0102- , Isotope (46, 58) 103.9040305 0.1114- , Isotope (46, 59) 104.9050796 0.2233- , Isotope (46, 60) 105.9034804 0.2733- , Isotope (46, 62) 107.9038916 0.2646- , Isotope (46, 64) 109.90517220 0.1172 ])- , (Ag, Element 47 "silver" [ Isotope (47, 60) 106.9050916 0.51839- , Isotope (47, 62) 108.9047553 0.48161 ])- , (Cd, Element 48 "cadmium" [ Isotope (48, 58) 105.9064599 0.0125- , Isotope (48, 60) 107.9041834 0.0089- , Isotope (48, 62) 109.90300661 0.1249- , Isotope (48, 63) 110.90418287 0.1280- , Isotope (48, 64) 111.90276287 0.2413- , Isotope (48, 65) 112.90440813 0.1222- , Isotope (48, 66) 113.90336509 0.2873- , Isotope (48, 68) 115.90476315 0.0749 ])- , (In, Element 49 "indium" [ Isotope (49, 64) 112.90406184 0.0429- , Isotope (49, 66) 114.903878776 0.9571 ])- , (Sn, Element 50 "tin" [ Isotope (50, 62) 111.90482387 0.0097- , Isotope (50, 64) 113.9027827 0.0066- , Isotope (50, 65) 114.903344699 0.0034- , Isotope (50, 66) 115.90174280 0.1454- , Isotope (50, 67) 116.90295398 0.0768- , Isotope (50, 68) 117.90160657 0.2422- , Isotope (50, 69) 118.90331117 0.0859- , Isotope (50, 70) 119.90220163 0.3258- , Isotope (50, 72) 121.9034438 0.0463- , Isotope (50, 74) 123.9052766 0.0579 ])- , (Sb, Element 51 "antimony" [ Isotope (51, 70) 120.9038120 0.5721- , Isotope (51, 72) 122.9042132 0.4279 ])- , (Te, Element 52 "tellurium" [ Isotope (52, 68) 119.9040593 0.0009- , Isotope (52, 70) 121.9030435 0.0255- , Isotope (52, 71) 122.9042698 0.0089- , Isotope (52, 72) 123.9028171 0.0474- , Isotope (52, 73) 124.9044299 0.0707- , Isotope (52, 74) 125.9033109 0.1884- , Isotope (52, 76) 127.90446128 0.3174- , Isotope (52, 78) 129.906222748 0.3408 ])- , (I, Element 53 "iodine" [ Isotope (53, 74) 126.9044719 1 ])- , (Xe, Element 54 "xenon" [ Isotope (54, 70) 123.9058920 0.000952- , Isotope (54, 72) 125.9042983 0.000890- , Isotope (54, 74) 127.9035310 0.019102- , Isotope (54, 75) 128.9047808611 0.264006- , Isotope (54, 76) 129.903509349 0.040710- , Isotope (54, 77) 130.90508406 0.212324- , Isotope (54, 78) 131.9041550856 0.269086- , Isotope (54, 80) 133.90539466 0.104357- , Isotope (54, 82) 135.907214484 0.088573 ])- , (Cs, Element 55 "caesium" [ Isotope (55, 78) 132.9054519610 1 ])- , (Ba, Element 56 "barium" [ Isotope (56, 74) 129.9063207 0.00106- , Isotope (56, 76) 131.9050611 0.00101- , Isotope (56, 78) 133.90450818 0.02417- , Isotope (56, 79) 134.90568838 0.06592- , Isotope (56, 80) 135.90457573 0.07854- , Isotope (56, 81) 136.90582714 0.11232- , Isotope (56, 82) 137.90524700 0.71698 ])- , (La, Element 57 "lanthanum" [ Isotope (57, 81) 137.9071149 0.0008881- , Isotope (57, 83) 138.9063563 0.9991119 ])- , (Ce, Element 58 "cerium" [ Isotope (58, 78) 135.90712921 0.00185- , Isotope (58, 80) 137.905991 0.00251- , Isotope (58, 82) 139.9054431 0.88450- , Isotope (58, 84) 141.9092504 0.11114 ])- , (Pr, Element 59 "praseodymium" [ Isotope (59, 82) 140.9076576 1 ])- , (Nd, Element 60 "neodymium" [ Isotope (60, 82) 141.9077290 0.27152- , Isotope (60, 83) 142.9098200 0.12174- , Isotope (60, 84) 143.9100930 0.23798- , Isotope (60, 85) 144.9125793 0.08293- , Isotope (60, 86) 145.9131226 0.17189- , Isotope (60, 88) 147.9168993 0.05756- , Isotope (60, 90) 149.9209022 0.05638 ])- , (Sm, Element 62 "samarium" [ Isotope (62, 82) 143.9120065 0.0307- , Isotope (62, 83) 146.9149044 0.1499- , Isotope (62, 84) 147.9148292 0.1124- , Isotope (62, 85) 148.9171921 0.1382- , Isotope (62, 86) 149.9172829 0.0738- , Isotope (62, 88) 151.9197397 0.2675- , Isotope (62, 90) 153.9222169 0.2275 ])- , (Eu, Element 63 "europium" [ Isotope (63, 88) 150.9198578 0.4781- , Isotope (63, 90) 152.9212380 0.5219 ])- , (Gd, Element 64 "gadolinium" [ Isotope (64, 88) 151.9197995 0.0020- , Isotope (64, 90) 153.9208741 0.0218- , Isotope (64, 91) 154.9226305 0.1480- , Isotope (64, 92) 155.9221312 0.2047- , Isotope (64, 93) 156.9239686 0.1565- , Isotope (64, 94) 157.9241123 0.2484- , Isotope (64, 96) 159.9270624 0.2186 ])- , (Tb, Element 65 "terbium" [ Isotope (65, 94) 158.9253547 1 ])- , (Dy, Element 66 "dysprosium" [ Isotope (66, 90) 155.9242847 0.00056- , Isotope (66, 92) 157.9244159 0.00095- , Isotope (66, 94) 159.9252046 0.02329- , Isotope (66, 95) 160.9269405 0.18889- , Isotope (66, 96) 161.9268056 0.25475- , Isotope (66, 97) 162.9287383 0.24896- , Isotope (66, 98) 163.9291819 0.28260 ])- , (Ho, Element 67 "holmium" [ Isotope (67, 98) 164.9303288 1 ])- , (Er, Element 68 "erbium" [ Isotope (68, 94) 161.9287884 0.00139- , Isotope (68, 96) 163.9292088 0.01601- , Isotope (68, 98) 165.9302995 0.33503- , Isotope (68, 99) 166.9320546 0.22869- , Isotope (68, 100) 167.9323767 0.26978- , Isotope (68, 102) 169.9354702 0.14910 ])- , (Tm, Element 69 "thulium" [ Isotope (69, 100) 168.9342179 1 ])- , (Yb, Element 70 "ytterbium" [ Isotope (70, 98) 167.9338896 0.00123- , Isotope (70, 100) 169.9347664 0.02982- , Isotope (70, 101) 170.9363302 0.1409- , Isotope (70, 102) 171.9363859 0.2168- , Isotope (70, 103) 172.9382151 0.16103- , Isotope (70, 104) 173.9388664 0.32026- , Isotope (70, 106) 175.9425764 0.12996 ])- , (Lu, Element 71 "lutetium" [ Isotope (71, 104) 174.9407752 0.97401- , Isotope (71, 105) 175.9426897 0.02599 ])- , (Hf, Element 72 "hafnium" [ Isotope (72, 102) 173.9400461 0.0016- , Isotope (72, 104) 175.9414076 0.0526- , Isotope (72, 105) 176.9432277 0.1860- , Isotope (72, 106) 177.9437058 0.2728- , Isotope (72, 107) 178.9458232 0.1362- , Isotope (72, 108) 179.9465570 0.3508 ])- , (Ta, Element 73 "tantalum" [ Isotope (73, 107) 179.9474648 0.0001201- , Isotope (73, 108) 180.9479958 0.9998799 ])- , (W, Element 74 "tungsten" [ Isotope (74, 106) 179.9467108 0.0012- , Isotope (74, 108) 181.94820394 0.2650- , Isotope (74, 109) 182.95022275 0.1431- , Isotope (74, 110) 183.95093092 0.3064- , Isotope (74, 112) 185.9543628 0.2843 ])- , (Re, Element 75 "rhenium" [ Isotope (75, 110) 184.9529545 0.3740- , Isotope (75, 112) 186.9557501 0.6260 ])- , (Os, Element 76 "osmium" [ Isotope (76, 108) 183.9524885 0.0002- , Isotope (76, 110) 185.9538350 0.0159- , Isotope (76, 111) 186.9557474 0.0196- , Isotope (76, 112) 187.9558352 0.1324- , Isotope (76, 113) 188.9581442 0.1615- , Isotope (76, 114) 189.9584437 0.2626- , Isotope (76, 116) 191.9614770 0.4078 ])- , (Ir, Element 77 "iridium" [ Isotope (77, 114) 190.9605893 0.373- , Isotope (77, 116) 192.9629216 0.627 ])- , (Pt, Element 78 "platinum" [ Isotope (78, 112) 189.9599297 0.00012- , Isotope (78, 114) 191.9610387 0.00782- , Isotope (78, 116) 193.9626809 0.3286- , Isotope (78, 117) 194.9647917 0.3378- , Isotope (78, 118) 195.96495209 0.2521- , Isotope (78, 120) 197.9678949 0.07356 ])- , (Au, Element 79 "gold" [ Isotope (79, 118) 196.96656879 1 ])- , (Hg, Element 80 "mercury" [ Isotope (80, 116) 195.9658326 0.0015- , Isotope (80, 118) 197.96676860 0.0997- , Isotope (80, 119) 198.96828064 0.1687- , Isotope (80, 120) 199.96832659 0.2310- , Isotope (80, 121) 200.97030284 0.1318- , Isotope (80, 122) 201.97064340 0.2986- , Isotope (80, 124) 203.97349398 0.0687 ])- , (Tl, Element 81 "thallium" [ Isotope (81, 122) 202.9723446 0.2952- , Isotope (81, 124) 204.9744278 0.7048 ])- , (Pb, Element 82 "lead" [ Isotope (82, 122) 203.9730440 0.014- , Isotope (82, 124) 205.9744657 0.241- , Isotope (82, 125) 206.9758973 0.221- , Isotope (82, 126) 207.9766525 0.524 ])- , (Bi, Element 83 "bismuth" [ Isotope (83, 126) 208.9803991 1 ])- , (Th, Element 90 "thorium" [ Isotope (90, 142) 232.0380558 1 ])- , (Pa, Element 91 "protactinium" [ Isotope (91, 140) 231.0358842 1 ])- , (U, Element 92 "uranium" [ Isotope (92, 142) 234.0409523 0.000054- , Isotope (92, 143) 235.0439301 0.007204- , Isotope (92, 146) 238.0507884 0.992742 ])+elements = tupleToElement <$> fromList+ [ (H, (1, "hydrogen", [ ((1, 0), 1.00782503223, 0.999885)+ , ((1, 1), 2.01410177812, 0.000115) ]))+ , (He, (2, "helium", [ ((2, 1), 3.0160293201, 0.00000134)+ , ((2, 2), 4.00260325413, 0.99999866) ]))+ , (Li, (3, "lithium", [ ((3, 3), 6.0151228874, 0.0759)+ , ((3, 4), 7.0160034366, 0.9241) ]))+ , (Be, (4, "beryllium", [ ((4, 5), 9.012183065, 1.0) ]))+ , (B, (5, "boron", [ ((5, 5), 10.01293695, 0.199)+ , ((5, 6), 11.00930536, 0.801) ]))+ , (C, (6, "carbon", [ ((6, 6), 12.0000000, 0.9893)+ , ((6, 7), 13.00335483507, 0.0107) ]))+ , (N, (7, "nitrogen", [ ((7, 7), 14.00307400443, 0.99636)+ , ((7, 8), 15.00010889888, 0.00364) ]))+ , (O, (8, "oxygen", [ ((8, 8), 15.99491461957, 0.99757)+ , ((8, 9), 16.99913175650, 0.00038)+ , ((8, 10), 17.99915961286, 0.00205) ]))+ , (F, (9, "fluorine", [ ((9, 10), 18.99840316273, 1.0) ]))+ , (Ne, (10, "neon", [ ((10, 10), 19.9924401762, 0.9048)+ , ((10, 11), 20.993846685, 0.0027)+ , ((10, 12), 21.991385114, 0.0925) ]))+ , (Na, (11, "sodium", [ ((11, 12), 22.9897692820, 1.0) ]))+ , (Mg, (12, "magnesium", [ ((12, 12), 23.985041697, 0.7899)+ , ((12, 13), 24.985836976, 0.1000)+ , ((12, 14), 25.982592968, 0.1101) ]))+ , (Al, (13, "aluminium", [ ((13, 14), 26.98153853, 1.0) ]))+ , (Si, (14, "silicon", [ ((14, 14), 27.97692653465, 0.92223)+ , ((14, 15), 28.97649466490, 0.04685)+ , ((14, 16), 29.973770136, 0.03092) ]))+ , (P, (15, "phosphorous", [ ((15, 16), 30.97376199842, 1.0) ]))+ , (S, (16, "sulfur", [ ((16, 16), 31.9720711744, 0.9499)+ , ((16, 17), 32.9714589098, 0.0075)+ , ((16, 18), 33.967867004, 0.0425)+ , ((16, 20), 35.96708071, 0.0001) ]))+ , (Cl, (17, "chlorine", [ ((17, 18), 34.968852682, 0.7576)+ , ((17, 20), 36.965902602, 0.2424) ]))+ , (Ar, (18, "argon", [ ((18, 18), 35.967545105, 0.003336)+ , ((18, 20), 37.96273211, 0.000629)+ , ((18, 22), 39.9623831237, 0.996035) ]))+ , (K, (19, "potassium", [ ((19, 20), 38.9637064864, 0.932581)+ , ((19, 21), 39.963998166, 0.000117)+ , ((19, 22), 40.9618252579, 0.067302) ]))+ , (Ca, (20, "calcium", [ ((20, 20), 39.962590863, 0.96941)+ , ((20, 22), 41.95861783, 0.00647)+ , ((20, 23), 42.95876644, 0.00135)+ , ((20, 24), 43.95548156, 0.02086)+ , ((20, 26), 45.9536890, 0.00004)+ , ((20, 28), 47.95252276, 0.00187) ]))+ , (Sc, (21, "scandium", [ ((21, 24), 44.95590828, 1.0) ]))+ , (Ti, (22, "titanium", [ ((22, 24), 45.95262772, 0.0825)+ , ((22, 25), 46.95175879, 0.0744)+ , ((22, 26), 47.94794198, 0.7372)+ , ((22, 27), 48.94786568, 0.0541)+ , ((22, 28), 49.94478689, 0.0518) ]))+ , (V, (23, "vanadium", [ ((23, 27), 49.94715601, 0.00250)+ , ((23, 28), 50.94395704, 0.99750) ]))+ , (Cr, (24, "chromium", [ ((24, 26), 49.94604183, 0.04345)+ , ((24, 28), 51.94050623, 0.83789)+ , ((24, 29), 52.94064815, 0.09501)+ , ((24, 30), 53.93887916, 0.02365) ]))+ , (Mn, (25, "manganese", [ ((25, 30), 54.93804391, 1.0) ]))+ , (Fe, (26, "iron", [ ((26, 28), 53.93960899, 0.05845)+ , ((26, 30), 55.93493633, 0.91754)+ , ((26, 31), 56.93539284, 0.02119)+ , ((26, 32), 57.93327443, 0.00282) ]))+ , (Co, (27, "cobalt", [ ((27, 32), 58.93319429, 1.0) ]))+ , (Ni, (28, "nickel", [ ((28, 30), 57.93534241, 0.68077)+ , ((28, 32), 59.93078588, 0.26223)+ , ((28, 33), 60.93105557, 0.011399)+ , ((28, 34), 61.92834537, 0.036346)+ , ((28, 36), 63.92796682, 0.009255) ]))+ , (Cu, (29, "copper", [ ((29, 34), 62.92959772, 0.6915)+ , ((29, 36), 64.92778970, 0.3085) ]))+ , (Zn, (30, "zinc", [ ((30, 34), 63.92914201, 0.4917)+ , ((30, 36), 65.92603381, 0.2773)+ , ((30, 37), 66.92712775, 0.0404)+ , ((30, 38), 67.92484455, 0.1845)+ , ((30, 40), 69.9253192, 0.0061) ]))+ , (Ga, (31, "gallium", [ ((31, 38), 68.9255735, 0.60108)+ , ((31, 40), 70.92470258, 0.39892) ]))+ , (Ge, (32, "germanium", [ ((32, 38), 69.92424875, 0.2057)+ , ((32, 40), 71.922075826, 0.2745)+ , ((32, 41), 72.923458956, 0.0775)+ , ((32, 42), 73.921177761, 0.3650)+ , ((32, 44), 75.921402726, 0.0773) ]))+ , (As, (33, "arsenic", [ ((33, 42), 74.92159457, 1.0) ]))+ , (Se, (34, "selenium", [ ((34, 40), 73.922475934, 0.0089)+ , ((34, 42), 75.919213704, 0.0937)+ , ((34, 43), 76.919914154, 0.0763)+ , ((34, 44), 77.91730928, 0.2377)+ , ((34, 46), 79.9165218, 0.4961)+ , ((34, 48), 81.9166995, 0.0873) ]))+ , (Br, (35, "bromine", [ ((35, 44), 78.9183376, 0.5069)+ , ((35, 46), 80.9162897, 0.4931) ]))+ , (Kr, (36, "krypton", [ ((36, 42), 77.92036494, 0.00355)+ , ((36, 44), 79.91637808, 0.02286)+ , ((36, 46), 81.91348273, 0.11593)+ , ((36, 47), 82.91412716, 0.11500)+ , ((36, 48), 83.9114977282, 0.56987)+ , ((36, 50), 85.9106106269, 0.17279) ]))+ , (Rb, (37, "rubidium", [ ((37, 48), 84.9117897379, 0.7217)+ , ((37, 50), 86.9091805310, 0.2783) ]))+ , (Sr, (38, "strontium", [ ((38, 46), 83.9134191, 0.0056)+ , ((38, 48), 85.9092606, 0.0986)+ , ((38, 49), 86.9088775, 0.0700)+ , ((38, 50), 87.9056125, 0.8258) ]))+ , (Y, (39, "yttrium", [ ((39, 50), 88.9058403, 1.0) ]))+ , (Zr, (40, "zirconium", [ ((40, 50), 89.9046977, 0.5145)+ , ((40, 51), 90.9056396, 0.1122)+ , ((40, 52), 91.9050347, 0.1715)+ , ((40, 54), 93.9063108, 0.1738)+ , ((40, 56), 95.9082714, 0.0280) ]))+ , (Nb, (41, "niobium", [ ((41, 52), 92.9063730, 1.0) ]))+ , (Mo, (42, "molybdenum", [ ((42, 50), 91.90680796, 0.1453)+ , ((42, 52), 93.90508490, 0.0915)+ , ((42, 53), 94.90583877, 0.1584)+ , ((42, 54), 95.90467612, 0.1667)+ , ((42, 55), 96.90601812, 0.0960)+ , ((42, 56), 97.90540482, 0.2439)+ , ((42, 58), 99.9074718, 0.0982) ]))+ , (Ru, (44, "ruthenium", [ ((44, 52), 95.90759025, 0.0554)+ , ((44, 54), 97.9052868, 0.0187)+ , ((44, 55), 98.9059341, 0.1276)+ , ((44, 56), 99.9042143, 0.1260)+ , ((44, 57), 100.9055769, 0.1706)+ , ((44, 58), 101.9043441, 0.3155)+ , ((44, 59), 103.9054275, 0.1862) ]))+ , (Rh, (45, "rhodium", [ ((45, 58), 102.9054980, 1.0) ]))+ , (Pd, (46, "palladium", [ ((46, 56), 101.9056022, 0.0102)+ , ((46, 58), 103.9040305, 0.1114)+ , ((46, 59), 104.9050796, 0.2233)+ , ((46, 60), 105.9034804, 0.2733)+ , ((46, 62), 107.9038916, 0.2646)+ , ((46, 64), 109.90517220, 0.1172) ]))+ , (Ag, (47, "silver", [ ((47, 60), 106.9050916, 0.51839)+ , ((47, 62), 108.9047553, 0.48161) ]))+ , (Cd, (48, "cadmium", [ ((48, 58), 105.9064599, 0.0125)+ , ((48, 60), 107.9041834, 0.0089)+ , ((48, 62), 109.90300661, 0.1249)+ , ((48, 63), 110.90418287, 0.1280)+ , ((48, 64), 111.90276287, 0.2413)+ , ((48, 65), 112.90440813, 0.1222)+ , ((48, 66), 113.90336509, 0.2873)+ , ((48, 68), 115.90476315, 0.0749) ]))+ , (In, (49, "indium", [ ((49, 64), 112.90406184, 0.0429)+ , ((49, 66), 114.903878776, 0.9571) ]))+ , (Sn, (50, "tin", [ ((50, 62), 111.90482387, 0.0097)+ , ((50, 64), 113.9027827, 0.0066)+ , ((50, 65), 114.903344699, 0.0034)+ , ((50, 66), 115.90174280, 0.1454)+ , ((50, 67), 116.90295398, 0.0768)+ , ((50, 68), 117.90160657, 0.2422)+ , ((50, 69), 118.90331117, 0.0859)+ , ((50, 70), 119.90220163, 0.3258)+ , ((50, 72), 121.9034438, 0.0463)+ , ((50, 74), 123.9052766, 0.0579) ]))+ , (Sb, (51, "antimony", [ ((51, 70), 120.9038120, 0.5721)+ , ((51, 72), 122.9042132, 0.4279) ]))+ , (Te, (52, "tellurium", [ ((52, 68), 119.9040593, 0.0009)+ , ((52, 70), 121.9030435, 0.0255)+ , ((52, 71), 122.9042698, 0.0089)+ , ((52, 72), 123.9028171, 0.0474)+ , ((52, 73), 124.9044299, 0.0707)+ , ((52, 74), 125.9033109, 0.1884)+ , ((52, 76), 127.90446128, 0.3174)+ , ((52, 78), 129.906222748, 0.3408) ]))+ , (I, (53, "iodine", [ ((53, 74), 126.9044719, 1.0) ]))+ , (Xe, (54, "xenon", [ ((54, 70), 123.9058920, 0.000952)+ , ((54, 72), 125.9042983, 0.000890)+ , ((54, 74), 127.9035310, 0.019102)+ , ((54, 75), 128.9047808611, 0.264006)+ , ((54, 76), 129.903509349, 0.040710)+ , ((54, 77), 130.90508406, 0.212324)+ , ((54, 78), 131.9041550856, 0.269086)+ , ((54, 80), 133.90539466, 0.104357)+ , ((54, 82), 135.907214484, 0.088573) ]))+ , (Cs, (55, "caesium", [ ((55, 78), 132.9054519610, 1.0) ]))+ , (Ba, (56, "barium", [ ((56, 74), 129.9063207, 0.00106)+ , ((56, 76), 131.9050611, 0.00101)+ , ((56, 78), 133.90450818, 0.02417)+ , ((56, 79), 134.90568838, 0.06592)+ , ((56, 80), 135.90457573, 0.07854)+ , ((56, 81), 136.90582714, 0.11232)+ , ((56, 82), 137.90524700, 0.71698) ]))+ , (La, (57, "lanthanum", [ ((57, 81), 137.9071149, 0.0008881)+ , ((57, 83), 138.9063563, 0.9991119) ]))+ , (Ce, (58, "cerium", [ ((58, 78), 135.90712921, 0.00185)+ , ((58, 80), 137.905991, 0.00251)+ , ((58, 82), 139.9054431, 0.88450)+ , ((58, 84), 141.9092504, 0.11114) ]))+ , (Pr, (59, "praseodymium", [ ((59, 82), 140.9076576, 1.0) ]))+ , (Nd, (60, "neodymium", [ ((60, 82), 141.9077290, 0.27152)+ , ((60, 83), 142.9098200, 0.12174)+ , ((60, 84), 143.9100930, 0.23798)+ , ((60, 85), 144.9125793, 0.08293)+ , ((60, 86), 145.9131226, 0.17189)+ , ((60, 88), 147.9168993, 0.05756)+ , ((60, 90), 149.9209022, 0.05638) ]))+ , (Sm, (62, "samarium", [ ((62, 82), 143.9120065, 0.0307)+ , ((62, 83), 146.9149044, 0.1499)+ , ((62, 84), 147.9148292, 0.1124)+ , ((62, 85), 148.9171921, 0.1382)+ , ((62, 86), 149.9172829, 0.0738)+ , ((62, 88), 151.9197397, 0.2675)+ , ((62, 90), 153.9222169, 0.2275) ]))+ , (Eu, (63, "europium", [ ((63, 88), 150.9198578, 0.4781)+ , ((63, 90), 152.9212380, 0.5219) ]))+ , (Gd, (64, "gadolinium", [ ((64, 88), 151.9197995, 0.0020)+ , ((64, 90), 153.9208741, 0.0218)+ , ((64, 91), 154.9226305, 0.1480)+ , ((64, 92), 155.9221312, 0.2047)+ , ((64, 93), 156.9239686, 0.1565)+ , ((64, 94), 157.9241123, 0.2484)+ , ((64, 96), 159.9270624, 0.2186) ]))+ , (Tb, (65, "terbium", [ ((65, 94), 158.9253547, 1.0) ]))+ , (Dy, (66, "dysprosium", [ ((66, 90), 155.9242847, 0.00056)+ , ((66, 92), 157.9244159, 0.00095)+ , ((66, 94), 159.9252046, 0.02329)+ , ((66, 95), 160.9269405, 0.18889)+ , ((66, 96), 161.9268056, 0.25475)+ , ((66, 97), 162.9287383, 0.24896)+ , ((66, 98), 163.9291819, 0.28260) ]))+ , (Ho, (67, "holmium", [ ((67, 98), 164.9303288, 1.0) ]))+ , (Er, (68, "erbium", [ ((68, 94), 161.9287884, 0.00139)+ , ((68, 96), 163.9292088, 0.01601)+ , ((68, 98), 165.9302995, 0.33503)+ , ((68, 99), 166.9320546, 0.22869)+ , ((68, 100), 167.9323767, 0.26978)+ , ((68, 102), 169.9354702, 0.14910) ]))+ , (Tm, (69, "thulium", [ ((69, 100), 168.9342179, 1.0) ]))+ , (Yb, (70, "ytterbium", [ ((70, 98), 167.9338896, 0.00123)+ , ((70, 100), 169.9347664, 0.02982)+ , ((70, 101), 170.9363302, 0.1409)+ , ((70, 102), 171.9363859, 0.2168)+ , ((70, 103), 172.9382151, 0.16103)+ , ((70, 104), 173.9388664, 0.32026)+ , ((70, 106), 175.9425764, 0.12996) ]))+ , (Lu, (71, "lutetium", [ ((71, 104), 174.9407752, 0.97401)+ , ((71, 105), 175.9426897, 0.02599) ]))+ , (Hf, (72, "hafnium", [ ((72, 102), 173.9400461, 0.0016)+ , ((72, 104), 175.9414076, 0.0526)+ , ((72, 105), 176.9432277, 0.1860)+ , ((72, 106), 177.9437058, 0.2728)+ , ((72, 107), 178.9458232, 0.1362)+ , ((72, 108), 179.9465570, 0.3508) ]))+ , (Ta, (73, "tantalum", [ ((73, 107), 179.9474648, 0.0001201)+ , ((73, 108), 180.9479958, 0.9998799) ]))+ , (W, (74, "tungsten", [ ((74, 106), 179.9467108, 0.0012)+ , ((74, 108), 181.94820394, 0.2650)+ , ((74, 109), 182.95022275, 0.1431)+ , ((74, 110), 183.95093092, 0.3064)+ , ((74, 112), 185.9543628, 0.2843) ]))+ , (Re, (75, "rhenium", [ ((75, 110), 184.9529545, 0.3740)+ , ((75, 112), 186.9557501, 0.6260) ]))+ , (Os, (76, "osmium", [ ((76, 108), 183.9524885, 0.0002)+ , ((76, 110), 185.9538350, 0.0159)+ , ((76, 111), 186.9557474, 0.0196)+ , ((76, 112), 187.9558352, 0.1324)+ , ((76, 113), 188.9581442, 0.1615)+ , ((76, 114), 189.9584437, 0.2626)+ , ((76, 116), 191.9614770, 0.4078) ]))+ , (Ir, (77, "iridium", [ ((77, 114), 190.9605893, 0.373)+ , ((77, 116), 192.9629216, 0.627) ]))+ , (Pt, (78, "platinum", [ ((78, 112), 189.9599297, 0.00012)+ , ((78, 114), 191.9610387, 0.00782)+ , ((78, 116), 193.9626809, 0.3286)+ , ((78, 117), 194.9647917, 0.3378)+ , ((78, 118), 195.96495209, 0.2521)+ , ((78, 120), 197.9678949, 0.07356) ]))+ , (Au, (79, "gold", [ ((79, 118), 196.96656879, 1.0) ]))+ , (Hg, (80, "mercury", [ ((80, 116), 195.9658326, 0.0015)+ , ((80, 118), 197.96676860, 0.0997)+ , ((80, 119), 198.96828064, 0.1687)+ , ((80, 120), 199.96832659, 0.2310)+ , ((80, 121), 200.97030284, 0.1318)+ , ((80, 122), 201.97064340, 0.2986)+ , ((80, 124), 203.97349398, 0.0687) ]))+ , (Tl, (81, "thallium", [ ((81, 122), 202.9723446, 0.2952)+ , ((81, 124), 204.9744278, 0.7048) ]))+ , (Pb, (82, "lead", [ ((82, 122), 203.9730440, 0.014)+ , ((82, 124), 205.9744657, 0.241)+ , ((82, 125), 206.9758973, 0.221)+ , ((82, 126), 207.9766525, 0.524) ]))+ , (Bi, (83, "bismuth", [ ((83, 126), 208.9803991, 1.0) ]))+ , (Th, (90, "thorium", [ ((90, 142), 232.0380558, 1.0) ]))+ , (Pa, (91, "protactinium", [ ((91, 140), 231.0358842, 1.0) ]))+ , (U, (92, "uranium", [ ((92, 142), 234.0409523, 0.000054)+ , ((92, 143), 235.0439301, 0.007204)+ , ((92, 146), 238.0507884, 0.992742) ])) ] -instance ChemicalMass ElementSymbol where- toElementalComposition x = ElementalComposition . fromList $ [(x, 1)]- -------------------------------------------------------------------------------- -- Functions taking an 'elementSymbol' as input @@ -562,11 +631,26 @@ isotopicAbundances = elementIsotopicAbundances . findElement ----------------------------------------------------------------------------------- 'ElementalComposition' type class+-- Formula type class +-- | Type class with two methods, 'renderFormula' and 'emptyFormula'. The+-- 'renderFormula' method converts a formula to its shorthand notation.+class Formula a where+ renderFormula :: a -> String+ emptyFormula :: a++--------------------------------------------------------------------------------+-- Elemental composition++-- | Provided since 'EmpiricalFormula' can be thought of as an elemenal+-- composition but is not a molecular formula.+newtype ElementalComposition = ElementalComposition {+ getElementalComposition :: Map ElementSymbol Int }+ deriving (Show, Read, Eq, Ord)+ -- | Class containing four methods; 'toElementalComposition', -- 'monoisotopicMass', 'nominalMass' and 'averageMass'.-class ChemicalMass a where+class ToElementalComposition a where toElementalComposition :: a -> ElementalComposition monoisotopicMass :: a -> MonoisotopicMass nominalMass :: a -> NominalMass@@ -578,89 +662,47 @@ -- Helper function for the calculating monoistopic masses, average mass and -- nominal masses for molecular formulae.-getFormulaSum :: (Num a, ChemicalMass b) => (Element -> a) -> b -> a-getFormulaSum f m = sum $- mapWithKey mapFunc (getComposition (toElementalComposition m))- where mapFunc k v = (f . findElement) k * fromIntegral v------------------------------------------------------------------------------------- Molecular formulae---- | 'MolecularFormula' is a newtype to represent a molecular formula.-newtype MolecularFormula = MolecularFormula {- getMolecularFormula :: Map ElementSymbol Int }- deriving (Show, Read, Eq, Ord)---- | Provided since 'EmpiricalFormula' can be thought of as a chemical--- composition but is not a molecular formula.-newtype ElementalComposition = ElementalComposition {- getComposition :: Map ElementSymbol Int }- deriving (Show, Read, Eq, Ord)--instance Monoid MolecularFormula where- mempty = emptyFormula- mappend = (|+|)---- | Infix operator for the addition of molecular formulae. (|+|) is mappend in--- the monoid instance and the same fixity as (+).-(|+|) :: MolecularFormula -> MolecularFormula -> MolecularFormula-(|+|) = combineMolecularFormulae (+)---- | Infix operator for the subtraction of molecular formulae. Has the same--- fixity as (-).-(|-|) :: MolecularFormula -> MolecularFormula -> MolecularFormula-(|-|) = combineMolecularFormulae (-)---- | Infix operator for the multiplication of molecular formulae. Has the same--- fixity as (*).-(|*|) :: Int -> MolecularFormula -> MolecularFormula-n |*| m = MolecularFormula . filterZero $- (fromIntegral n *) <$> getMolecularFormula m--infixl 6 |+|-infixl 7 |*|-infixl 6 |-|---- The function unionWith adapted to work with 'MolecularFormula'.-combineMolecularFormulae :: (Int -> Int -> Int)- -> MolecularFormula -> MolecularFormula -> MolecularFormula-combineMolecularFormulae f m1 m2 = MolecularFormula $- filterZero $ unionWith f- (getMolecularFormula m1)- (getMolecularFormula m2)+getFormulaSum :: (Monoid a, Operators a, ToElementalComposition b)+ => (Element -> a) -> b -> a+getFormulaSum f m = fold $+ mapWithKey mapFunc (getElementalComposition (toElementalComposition m))+ where mapFunc k v = (f . findElement) k |*| v --- Helper function to remove (k, v) pairs where v == 0.-filterZero :: Map k Int -> Map k Int-filterZero = filter (/= 0)+-- | Smart constructor to make values of type 'ElementalComposition'.+mkElementalComposition :: [(ElementSymbol, Int)] -> ElementalComposition+mkElementalComposition = ElementalComposition . filterZero . fromList -instance ChemicalMass MolecularFormula where- toElementalComposition (MolecularFormula m) = ElementalComposition m+instance Monoid ElementalComposition where+ mempty = emptyFormula+ mappend = (|+|) -class (ChemicalMass a) => ToMolecularFormula a where- toMolecularFormula :: a -> MolecularFormula+instance Operators ElementalComposition where+ ElementalComposition x |+| ElementalComposition y =+ ElementalComposition $ combineMaps (+) x y+ ElementalComposition x |-| ElementalComposition y =+ ElementalComposition $ combineMaps (-) x y+ m |*| n = ElementalComposition . filterZero $+ (fromIntegral n *) <$> getElementalComposition m --- | Type class with two methods, 'renderFormula' and 'emptyFormula'. The--- 'renderFormula' method converts a formula to its shorthand notation.-class Formula a where- renderFormula :: a -> String- emptyFormula :: a+instance ToElementalComposition ElementSymbol where+ toElementalComposition sym = mkElementalComposition [(sym, 1)] --- | Smart constructor to make values of type 'MolecularFormula'.-mkMolecularFormula :: [(ElementSymbol, Int)] -> MolecularFormula-mkMolecularFormula = MolecularFormula . filterZero . fromList+instance ToElementalComposition ElementalComposition where+ toElementalComposition = id -instance Formula MolecularFormula where- renderFormula f = foldMap renderFoldfunc ((sortElementSymbolMap . getMolecularFormula) f)- emptyFormula = mkMolecularFormula []+instance Formula ElementalComposition where+ renderFormula f = foldMap renderFoldfunc+ ((sortElementSymbolMap . getElementalComposition) f)+ emptyFormula = mkElementalComposition [] -- Helper function for 'renderFormula'. renderFoldfunc :: (ElementSymbol, Int) -> String-renderFoldfunc (sym, num) = show sym ++ if num == 1+renderFoldfunc (sym, num) = show sym <> if num == 1 then "" else show num --- Use the Hill system for writing molecular formulas. C then H followed by--- elements in alphabetical order.+-- Use the Hill system for writing formulas. C then H followed by elements in+-- alphabetical order. sortElementSymbolMap :: Map ElementSymbol Int -> [(ElementSymbol, Int)] sortElementSymbolMap m = sortBy (hillSystem fst) elementSymbolIntList where@@ -670,34 +712,90 @@ hillSystem f a b = case (f a, f b) of (C, _) -> LT (_, C) -> GT- (H, b') -> if containsC then LT else (show . elementName) H `compare` show b'- (a', H) -> if containsC then GT else show a' `compare` (show . elementName) H+ (H, b') -> if containsC then LT+ else (show . elementName) H `compare` show b'+ (a', H) -> if containsC then GT+ else show a' `compare` (show . elementName) H (a', b') -> show a' `compare` show b' --------------------------------------------------------------------------------+-- Molecular formulae++-- | 'MolecularFormula' is a newtype to represent a molecular formula.+newtype MolecularFormula = MolecularFormula {+ getMolecularFormula :: Map ElementSymbol Int }+ deriving (Show, Read, Eq, Ord)++class ToMolecularFormula a where+ toMolecularFormula :: a -> MolecularFormula++-- The function unionWith adapted to work with 'Map ElementSymbol Int'.+combineMaps :: (Int -> Int -> Int)+ -> Map ElementSymbol Int -> Map ElementSymbol Int -> Map ElementSymbol Int+combineMaps f m1 m2 = filterZero $ unionWith f m1 m2++-- | Smart constructor to make values of type 'MolecularFormula'.+mkMolecularFormula :: [(ElementSymbol, Int)] -> MolecularFormula+mkMolecularFormula = MolecularFormula . filterZero . fromList++-- Helper function to remove (k, v) pairs where v == 0.+filterZero :: Map k Int -> Map k Int+filterZero = filter (/= 0)++instance Monoid MolecularFormula where+ mempty = emptyFormula+ mappend = (|+|)++instance Operators MolecularFormula where+ MolecularFormula x |+| MolecularFormula y =+ MolecularFormula $ combineMaps (+) x y+ MolecularFormula x |-| MolecularFormula y =+ MolecularFormula $ combineMaps (-) x y+ m |*| n = MolecularFormula . filterZero $+ (fromIntegral n *) <$> getMolecularFormula m++instance ToElementalComposition MolecularFormula where+ toElementalComposition (MolecularFormula m) = ElementalComposition m++instance Formula MolecularFormula where+ renderFormula f = foldMap renderFoldfunc+ ((sortElementSymbolMap . getMolecularFormula) f)+ emptyFormula = mkMolecularFormula []++-------------------------------------------------------------------------------- -- Condensed formulae -- | 'CondensedFormula' is a newtype to represent a condensed formula. newtype CondensedFormula = CondensedFormula {- getCondensedFormula :: [Either MolecularFormula ([MolecularFormula], Int)] }+ getCondensedFormula :: [Either MolecularFormula (CondensedFormula, Int)] } deriving (Show, Read, Eq, Ord) -instance ChemicalMass CondensedFormula where- toElementalComposition = toElementalComposition+class ToCondensedFormuala a where+ toCondensedFormula :: a -> CondensedFormula +instance Monoid CondensedFormula where+ mempty = emptyFormula+ CondensedFormula x `mappend` CondensedFormula y = CondensedFormula (x <> y)++instance ToElementalComposition CondensedFormula where+ toElementalComposition =+ ElementalComposition . getMolecularFormula . toMolecularFormula+ instance ToMolecularFormula CondensedFormula where toMolecularFormula c = foldMap foldFunc (getCondensedFormula c) where foldFunc = \case Left chemForm -> chemForm- Right (molForm, n) -> n |*| mconcat molForm+ Right (condForm, n) -> toMolecularFormula condForm |*| n instance Formula CondensedFormula where renderFormula c = foldMap foldFunc (getCondensedFormula c) where foldFunc = \case Left chemForm -> renderFormula chemForm Right (chemFormList, n) ->- "(" ++ foldMap renderFormula chemFormList ++ ")" ++ formatNum n- where formatNum n' = if n' == 1 then "" else show n'+ "(" <> renderFormula chemFormList <> ")"+ <> formatNum n+ where formatNum n' = if n' == 1 then ""+ else show n' emptyFormula = CondensedFormula [] --------------------------------------------------------------------------------@@ -706,29 +804,35 @@ getEmpiricalFormula :: Map ElementSymbol Int } deriving (Show, Read, Eq, Ord) -instance ChemicalMass EmpiricalFormula where- toElementalComposition (EmpiricalFormula a) = ElementalComposition a--instance Formula EmpiricalFormula where- renderFormula f = foldMap renderFoldfunc ((sortElementSymbolMap . getEmpiricalFormula) f)- emptyFormula = mkEmpiricalFormula []+-- | Type class with a single method, 'toEmpiricalFormula', which converts a+-- chemical data type to `EmpiricalFormula`.+class ToEmpiricalFormula a where+ toEmpiricalFormula :: a -> EmpiricalFormula -- | Smart constructor to make values of type 'EmpiricalFormula'. mkEmpiricalFormula :: [(ElementSymbol, Int)] -> EmpiricalFormula-mkEmpiricalFormula = EmpiricalFormula . filterZero . fromList+mkEmpiricalFormula l =+ let m = filterZero (fromList l)+ in EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m --- | Type class with a single method, 'toEmpiricalFormula', which converts a--- chemical data type to `EmpiricalFormula`.-class ChemicalMass a => ToEmpiricalFormula a where- toEmpiricalFormula :: a -> EmpiricalFormula+instance ToEmpiricalFormula ElementalComposition where+ toEmpiricalFormula (ElementalComposition m) =+ EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m instance ToEmpiricalFormula MolecularFormula where- toEmpiricalFormula (MolecularFormula m)- | null m = EmpiricalFormula m- | otherwise = EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m+ toEmpiricalFormula (MolecularFormula m) =+ EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m instance ToEmpiricalFormula CondensedFormula where toEmpiricalFormula = toEmpiricalFormula . toMolecularFormula++instance ToElementalComposition EmpiricalFormula where+ toElementalComposition (EmpiricalFormula a) = ElementalComposition a++instance Formula EmpiricalFormula where+ renderFormula f = foldMap renderFoldfunc+ ((sortElementSymbolMap . getEmpiricalFormula) f)+ emptyFormula = mkEmpiricalFormula [] -- Helper function to find the greatest common denominator in a map. greatestCommonDenom :: (Integral v) => Map k v -> v
src/Isotope/Parsers.hs view
@@ -6,8 +6,9 @@ Maintainer : Michael Thomas <Michaelt293@gmail.com> Stability : Experimental -This module provides parsers for element symbols as well molecular, empirical and-condensed formulae. In addition, QuasiQuoters are provided.+This module provides parsers for element symbols and elemental composition as+well molecular, condensed and empirical formulae. In addition, quasiquoters are+provided. -} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuasiQuotes #-}@@ -16,11 +17,14 @@ -- * Parsers elementSymbol , subFormula+ , elementalComposition , molecularFormula , condensedFormula+ , empiricalFormula+ , ele , mol- , emp , con+ , emp ) where import Isotope.Base@@ -32,88 +36,101 @@ import Data.String import Data.List hiding (filter) import Data.Map (Map)+import Data.Monoid ((<>)) -- | Parses an element symbol string. elementSymbol :: Parser ElementSymbol elementSymbol = read <$> choice (try . string <$> elementSymbolStrList)- where elementList = show <$> elementSymbolList- reverseLengthSort x y = length y `compare` length x- elementSymbolStrList = sortBy reverseLengthSort elementList+ where+ elementList = show <$> elementSymbolList+ reverseLengthSort x y = length y `compare` length x+ elementSymbolStrList = sortBy reverseLengthSort elementList -- | Parses an sub-formula (i.e., \"C2\").-subFormula :: Parser MolecularFormula-subFormula = do- sym <- elementSymbol- num <- optional L.integer- return $ case num of- Nothing -> mkMolecularFormula [(sym, 1)]- Just num' -> mkMolecularFormula [(sym, fromIntegral num')]+subFormula :: Parser (ElementSymbol, Int)+subFormula =+ (\sym num -> (sym, fromIntegral num)) <$> elementSymbol <*> option 1 L.integer +-- | Parses an elemental composition (i.e. \"C6H6\").+elementalComposition :: Parser ElementalComposition+elementalComposition = mkElementalComposition <$> many subFormula+ -- | Parses a molecular formula (i.e. \"C6H6\"). molecularFormula :: Parser MolecularFormula-molecularFormula = do- formulas <- many subFormula- return $ mconcat formulas----- Helper function. Parses parenthesed sections in condensed formulae, i.e.,--- the \"(CH3)3\" section of \"N(CH3)3\".-parenFormula :: Parser (Either MolecularFormula ([MolecularFormula], Int))-parenFormula = do- _ <- char '('- formula <- some subFormula- _ <- char ')'- num <- optional L.integer- return $ Right $ case num of- Nothing -> (formula, 1)- Just num' -> (formula, fromIntegral num')---- Helper function. Parses non-parenthesed sections in condensed formulae, i.e.,--- the \"N\" section of \"N(CH3)3\".-leftMolecularFormula :: Parser (Either MolecularFormula ([MolecularFormula], Int))-leftMolecularFormula = do- formula <- subFormula- return $ Left formula+molecularFormula = mkMolecularFormula <$> many subFormula -- | Parses a condensed formula, i.e., \"N(CH3)3\". condensedFormula :: Parser CondensedFormula-condensedFormula = do- result <- many (leftMolecularFormula <|> parenFormula)- return $ CondensedFormula result+condensedFormula = CondensedFormula <$> many (leftCondensedFormula <|> rightCondensedFormula)+ where+ subMolecularFormula :: Parser MolecularFormula+ subMolecularFormula = mkMolecularFormula . pure <$> subFormula+ leftCondensedFormula :: Parser (Either MolecularFormula (CondensedFormula, Int))+ leftCondensedFormula = Left <$> subMolecularFormula+ rightCondensedFormula :: Parser (Either MolecularFormula (CondensedFormula, Int))+ rightCondensedFormula = do+ _ <- char '('+ formula <- condensedFormula+ _ <- char ')'+ num <- option 1 L.integer+ return $ Right (formula, fromIntegral num) +-- | Parses a empirical formula (i.e. \"CH\").+empiricalFormula :: Parser EmpiricalFormula+empiricalFormula = mkEmpiricalFormula <$> many subFormula++-- Helper function for `ElementalComposition` quasiquoter+quoteElementalComposition s =+ case parse (condensedFormula <* eof) "" s of+ Left err -> error $ "Could not parse formula: " <> show err+ Right v -> lift $ toElementalComposition v++-- Helper function for `MolecularFormula` quasiquoter quoteMolecularFormula s =- case parse (condensedFormula <* eof) "" s of- Left err -> fail $ "Could not parse formula: " ++ show err- Right v -> lift $ toMolecularFormula v+ case parse (condensedFormula <* eof) "" s of+ Left err -> fail $ "Could not parse formula: " <> show err+ Right v -> lift $ toMolecularFormula v +-- Helper function for `CondensedFormula` quasiquoter+quoteCondensedFormula s =+ case parse (condensedFormula <* eof) "" s of+ Left err -> error $ "Could not parse formula: " <> show err+ Right v -> lift v++-- Helper function for `EmpiricalFormula` quasiquoter quoteEmpiricalFormula s =- case parse (condensedFormula <* eof) "" s of- Left err -> fail $ "Could not parse formula: " ++ show err- Right v -> lift $ toEmpiricalFormula v+ case parse (condensedFormula <* eof) "" s of+ Left err -> fail $ "Could not parse formula: " <> show err+ Right v -> lift $ toEmpiricalFormula v -quoteCondensedFormula s =- case parse (condensedFormula <* eof) "" s of- Left err -> error $ "Could not parse formula: " ++ show err- Right v -> lift v+-- | Quasiquoter for `ElementalComposition`+ele :: QuasiQuoter+ele = QuasiQuoter+ { quoteExp = quoteElementalComposition } -mol :: QuasiQuoter+-- | Quasiquoter for `MolecularFormula`+mol :: QuasiQuoter mol = QuasiQuoter { quoteExp = quoteMolecularFormula } -emp :: QuasiQuoter+-- | Quasiquoter for `CondensedFormula`+con :: QuasiQuoter+con = QuasiQuoter+ { quoteExp = quoteCondensedFormula }++-- | Quasiquoter for `EmpiricalFormula`+emp :: QuasiQuoter emp = QuasiQuoter { quoteExp = quoteEmpiricalFormula } -con :: QuasiQuoter-con = QuasiQuoter- { quoteExp = quoteCondensedFormula }+$(deriveLift ''ElementSymbol) -$(deriveLift ''MolecularFormula)+$(deriveLift ''ElementalComposition) -$(deriveLift ''EmpiricalFormula)+$(deriveLift ''MolecularFormula) $(deriveLift ''CondensedFormula) -$(deriveLift ''Map)+$(deriveLift ''EmpiricalFormula) -$(deriveLift ''ElementSymbol)+$(deriveLift ''Map)
test/Isotope/BaseSpec.hs view
@@ -1,26 +1,27 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE OverlappingInstances #-} module Isotope.BaseSpec (spec) where import Isotope import Test.Hspec import Test.QuickCheck+import Control.Monad import Data.Char import Data.List-import Data.Map (Map, fromList) spec :: Spec spec = do describe "ElementSymbol" $ do it "should not have more than two characters" $ ((<= 2) . length . show) <$> elementSymbolList `shouldSatisfy` and- it "second character of a two character ElementSymbol should not be upper case" $- (\x -> length x /= 2 || (isLower . last) x) . show <$> elementSymbolList `shouldSatisfy` and- describe "lookupElement" $ do+ it "second character of an ElementSymbol should not be upper case" $+ (\x -> length x /= 2 || (isLower . last) x) . show <$>+ elementSymbolList `shouldSatisfy` and++ describe "lookupElement" . it "should not contain duplicate elements" $ lookupElement <$> elementSymbolList `shouldSatisfy` allUnique+ describe "elementName" $ do it "should not be an empty string" $ elementName <$> elementSymbolList `shouldSatisfy` notElem ""@@ -31,33 +32,34 @@ describe "atomicNumber" $ do it "should be between 1 and 92" $- (\x -> x >= 1 && x <= 92) . atomicNumber <$> elementSymbolList `shouldSatisfy` and+ (\x -> x >= 1 && x <= 92) . atomicNumber <$>+ elementSymbolList `shouldSatisfy` and it "should not have duplicated atomic numbers" $ atomicNumber <$> elementSymbolList `shouldSatisfy` allUnique - describe "isotopes" $ do+ describe "isotopes" . it "should not have duplicate isotopes" $ isotopes <$> elementSymbolList `shouldSatisfy` allUnique - describe "mostAbundantIsotope" $ do+ describe "mostAbundantIsotope" . it "C should be C12 (six protons and six neutrons)" $ (nucleons . mostAbundantIsotope) C `shouldBe` (6, 6) - describe "selectIsotope" $ do+ describe "selectIsotope" . it "calling fuction with the arguments C and 12 should select C12" $ nucleons (selectIsotope C 12) `shouldBe` (6, 6) - describe "monoisotopicMass" $ do+ describe "monoisotopicMass" . it "calling function with C should be 12.0" $- monoisotopicMass C `shouldBe` 12.0+ monoisotopicMass C `shouldBe` MonoisotopicMass 12.0 - describe "nominalMass" $ do+ describe "nominalMass" . it "calling function with C should return 12" $- nominalMass C `shouldBe` 12+ nominalMass C `shouldBe` NominalMass 12 - describe "isotopicMasses" $ do- it "calling function with H should return a list containing 1.007... and 2.014...." $- isotopicMasses H `shouldBe` [1.00782503223, 2.01410177812]+ describe "isotopicMasses" .+ it "H should return a list containing 1.007... and 2.014...." $+ isotopicMasses H `shouldBe` IsotopicMass <$> [1.00782503223, 2.01410177812] describe "integerMasses" $ do it "calling function with H should return [1, 2]" $@@ -65,49 +67,163 @@ it "proton number should be equal to atomic number" $ all protonNumEqAtomicNum elementSymbolList - describe "averageAtomicMass" $ do+ describe "averageMass" . it "calling function with C should return 12.0107" $- withinTolerance (averageMass C) 12.0107 0.0001 `shouldBe` True+ withinTolerance (getAverageMass (averageMass C)) 12.0107 0.0001 `shouldBe` True describe "isotopicAbundances" $ do it "calling function with C should return [0.9893, 0.0107]" $- isotopicAbundances C `shouldBe` [0.9893, 0.0107]+ isotopicAbundances C `shouldBe` IsotopicAbundance <$> [0.9893, 0.0107] it "sum of isotopic abundances for an element should equal 1" $- (\sym -> withinTolerance (sumIsotopicAbundance sym) 1 0.0001) <$> elementSymbolList `shouldSatisfy` and+ (\sym -> withinTolerance (sum (getIsotopicAbundance <$> isotopicAbundances sym)) 1 0.0001) <$>+ elementSymbolList `shouldSatisfy` and + describe "renderFormula for ElementalComposition" $ do+ it "[ele|C6H6O|] should be \"C6H6O\"" $+ renderFormula [ele|C6H6O|] `shouldBe` "C6H6O"+ it "[ele|CCl4|] should be \"CCl4\"" $+ renderFormula [ele|CCl4|] `shouldBe` "CCl4"+ it "[ele|H2O4S|] should be \"H2O4S\"" $+ renderFormula [ele|H2O4S|] `shouldBe` "H2O4S"++ describe "renderFormula for MolecularFormula" $ do+ it "[mol|C6H6O|] should be \"C6H6O\"" $+ renderFormula [mol|C6H6O|] `shouldBe` "C6H6O"+ it "[mol|CCl4|] should be \"CCl4\"" $+ renderFormula [mol|CCl4|] `shouldBe` "CCl4"+ it "[mol|H2O4S|] should be \"H2O4S\"" $+ renderFormula [mol|H2O4S|] `shouldBe` "H2O4S"++ describe "renderFormula for CondensedFormula" $ do+ it "[con|C6H6O|] should be \"C6H6O\"" $+ renderFormula [con|C6H6O|] `shouldBe` "C6H6O"+ it "[con|N(CH3)3|] should be \"N(CH3)3\"" $+ renderFormula [con|N(CH3)3|] `shouldBe` "N(CH3)3"+ it "[con|C6H5OH|] should be \"C6H5OH\"" $+ renderFormula [con|C6H5OH|] `shouldBe` "C6H5OH"++ describe "renderFormula for EmpiricalFormula" $ do+ it "[emp|C6H6O|] should be \"C6H6O\"" $+ renderFormula [emp|C6H6O|] `shouldBe` "C6H6O"+ it "[emp|CCl4|] should be \"CCl4\"" $+ renderFormula [emp|CCl4|] `shouldBe` "CCl4"+ it "[emp|H2O4S|] should be \"H2O4S\"" $+ renderFormula [emp|H2O4S|] `shouldBe` "H2O4S"++ describe "ToElementalComposition - ElementalComposition instance" $ do+ it "toElementalComposition" . property $+ \ec -> toElementalComposition ec == (ec :: ElementalComposition)+ it "monoisotopic mass of ethanol" $+ withinTolerance (getMonoisotopicMass (monoisotopicMass [ele|C2H6O|])) 46.04186 0.0001+ `shouldBe` True+ it "average mass of ethanol" $+ withinTolerance (getAverageMass (averageMass [ele|C2H6O|])) 46.06844 0.0001+ `shouldBe` True+ it "nominalMass mass of ethanol" $+ nominalMass [ele|C2H6O|] `shouldBe` NominalMass 46++ describe "mkElementalComposition" $ do+ it "zero is filtered out" $+ mkElementalComposition [(C, 0), (H, 0), (O, 0)] `shouldBe` emptyFormula+ it "should give the correct formula" $+ mkElementalComposition [(C, 2), (H, 6), (O, 1)] `shouldBe` [ele|C2H6O|]++ describe "ToElementalComposition - ElementSymbol instance" .+ it "monoisotopicMass" . property $+ \sym -> monoisotopicMass sym == monoisotopicMass (mkElementalComposition [(sym, 1)])+ describe "Monoid instance for MolecularFormula" $ do- it "associativity" $ property $- \a b c -> (a |+| b) |+| c == a |+| (b |+| c)- it "identity element" $ property $- \a -> a |+| emptyFormula == a+ it "associativity" . property $+ \a b c -> (a |+| b) |+| c == a |+| (b |+| c :: MolecularFormula)+ it "right identity" . property $+ \a -> (a :: MolecularFormula) |+| emptyFormula == a+ it "left identity" . property $+ \a -> (emptyFormula :: MolecularFormula) |+| a == a - describe "Addition of molecular formulae is commutative" $ do- it "commutative" $ property $- \a b -> a |+| b == b |+| a+ describe "Addition of molecular formulae is commutative" .+ it "commutative" . property $+ \a b -> (a |+| b) == (b |+| a :: MolecularFormula) describe "properties of |+|, |*| and |-|)" $ do- it "a |-| a = emptyFormula" $ property $- \a -> a |-| a == emptyFormula- it "0 |*| a == emptyFormula" $ property $- \a -> 0 |*| a == emptyFormula- it "a |+| a == 2 |*| a" $ property $- \a -> a |+| a == 2 |*| a+ it "a |-| a = emptyFormula" . property $+ \a -> a |-| a == (emptyFormula :: MolecularFormula)+ it "0 |*| a == emptyFormula" . property $+ \a -> a |*| 0 == (emptyFormula :: MolecularFormula)+ it "a |+| a == 2 |*| a" . property $+ \a -> a |+| a == (a :: MolecularFormula) |*| 2 - describe "toEmpiricalFormula" $ do- it "Empty MolecularFormula should return an empty EmpiricalFormula" $- toEmpiricalFormula (emptyFormula :: MolecularFormula) `shouldBe` mkEmpiricalFormula []- it "\"C6H6\" should be \"CH\"" $+ describe "ToElementalComposition - MolecularFormula instance" $ do+ it "[mol|C2H6O|] should be [ele|C2H6O|]" $+ toElementalComposition [mol|C2H6O|] `shouldBe` [ele|C2H6O|]+ it "empty MolecularFormula should return an empty ElementalComposition" $+ toElementalComposition (emptyFormula :: ElementalComposition) `shouldBe`+ mkElementalComposition []++ describe "Monoid instance for CondensedFormula" $ do+ it "associativity" . property $+ \a b c -> (a `mappend` b) `mappend` c ==+ a `mappend` (b `mappend` c :: CondensedFormula)+ it "right identity" . property $+ \a -> (a :: CondensedFormula) `mappend` mempty == a+ it "left identity" . property $+ \a -> emptyFormula `mappend` (a :: CondensedFormula) == a++ describe "ToElementalComposition - CondensedFormula instance" $ do+ it "empty CondensedFormula should return an empty ElementalComposition" $+ toElementalComposition (emptyFormula :: CondensedFormula) `shouldBe`+ [ele||]+ it "[con|N(CH3)3|] should be [ele|C3NH9|]" $+ toElementalComposition [con|N(CH3)3|] `shouldBe` [ele|C3NH9|]++ describe "ToMolecularFormula - CondensedFormula instance" $ do+ it "empty CondensedFormula should return an empty MolecularFormula" $+ toMolecularFormula (emptyFormula :: CondensedFormula) `shouldBe`+ mkMolecularFormula []+ it "[mol|C6H6|] should be [emp|CH|]" $+ toMolecularFormula [con|N(CH3)3|] `shouldBe` [mol|C3NH9|]++ describe "mkEmpiricalFormula" $ do+ it "zero is filtered out" $+ mkEmpiricalFormula [(C, 0), (H, 0), (O, 0)] `shouldBe` emptyFormula+ it "should give the correct formula" $+ mkEmpiricalFormula [(C, 6), (H, 6)] `shouldBe` [emp|CH|]++ describe "ToEmpiricalFormula - ElementalComposition instance" $ do+ it "empty ElementalComposition should return an empty EmpiricalFormula" $+ toEmpiricalFormula (emptyFormula :: ElementalComposition) `shouldBe`+ mkEmpiricalFormula []+ it "[ele|C6H6|] should be [emp|CH|]" $+ toEmpiricalFormula [ele|C6H6|] `shouldBe` [emp|CH|]++ describe "ToEmpiricalFormula - MolecularFormula instance" $ do+ it "empty MolecularFormula should return an empty EmpiricalFormula" $+ toEmpiricalFormula (emptyFormula :: MolecularFormula) `shouldBe`+ mkEmpiricalFormula []+ it "[mol|C6H6|] should be [emp|CH|]" $ toEmpiricalFormula [mol|C6H6|] `shouldBe` [emp|CH|] - describe "renderFormula" $ do- it "\"C6H6O\" should be \"C6H6O\"" $- renderFormula [mol|C6H6O|] `shouldBe` "C6H6O"- it "\"CCl4\" should be \"CCl4\"" $- renderFormula [mol|CCl4|] `shouldBe` "CCl4"- it "\"H2O4S\" should be \"H2O4S\"" $- renderFormula [mol|H2O4S|] `shouldBe` "H2O4S"+ describe "ToEmpiricalFormula - CondensedFormula instance" $ do+ it "empty CondensedFormula should return an empty EmpiricalFormula" $+ toEmpiricalFormula (emptyFormula :: CondensedFormula) `shouldBe`+ mkEmpiricalFormula []+ it "[con|C6H6|] should be [emp|CH|]" $+ toEmpiricalFormula [con|C6H6|] `shouldBe` [emp|CH|] + describe "ToElementalComposition - EmpiricalFormula instance" $ do+ it "empty EmpiricalFormula should return an empty EmpiricalFormula" $+ toElementalComposition (emptyFormula :: EmpiricalFormula) `shouldBe`+ mkElementalComposition []+ it "[emp|CH|] should be [ele|CH|]" $+ toElementalComposition [emp|CH|] `shouldBe` [ele|CH|] + describe "Laws for ElementalComposition, MolecularFormula, EmpiricalFormula and CondensedFormula data types" $ do+ it "applying toEmpiricalFormula to a CondensedFormula should give the same result as applying toMolecularFormula compose toEmpiricalFormula" . property $+ \c -> toEmpiricalFormula c == (toEmpiricalFormula . toMolecularFormula) (c :: CondensedFormula)+ it "applying toElementalComposition to a CondensedFormula should give the same result as applying toMolecularFormula compose toElementalComposition" . property $+ \c -> toElementalComposition c == (toElementalComposition . toMolecularFormula) (c :: CondensedFormula)+ it "applying toElementalComposition compose toEmpiricalFormula to an EmpiricalFormula should return the same EmpiricalFormula" . property $+ \e -> (toEmpiricalFormula . toElementalComposition) e == (e :: EmpiricalFormula)+ allUnique :: (Eq a) => [a] -> Bool allUnique l = l == nub l @@ -115,21 +231,47 @@ withinTolerance n1 n2 err = abs (n1 - n2) < err protonNumEqAtomicNum :: ElementSymbol -> Bool-protonNumEqAtomicNum sym = and $ (== atomicNumber sym) . fst . nucleons <$> isotopes sym+protonNumEqAtomicNum sym =+ and $ (== atomicNumber sym) . fst . nucleons <$> isotopes sym -sumIsotopicAbundance :: ElementSymbol -> IsotopicAbundance-sumIsotopicAbundance = sum . isotopicAbundances+elemSymIntPairGen :: Gen (ElementSymbol, Int)+elemSymIntPairGen = do+ elemSym <- arbitrary+ n <- choose (1,100)+ return (elemSym, n) +elemSymIntPairListGen :: Gen [(ElementSymbol, Int)]+elemSymIntPairListGen = listOf elemSymIntPairGen+ instance Arbitrary ElementSymbol where arbitrary = oneof $ return <$> elementSymbolList -instance Arbitrary (ElementSymbol, Int) where- arbitrary = do- elemSym <- arbitrary :: Gen ElementSymbol- n <- choose (1,100)- return (elemSym, n)+instance Arbitrary ElementalComposition where+ arbitrary = mkElementalComposition <$> elemSymIntPairListGen instance Arbitrary MolecularFormula where- arbitrary = do- symNums <- arbitrary :: Gen [(ElementSymbol, Int)]- return $ MolecularFormula . fromList $ symNums+ arbitrary = mkMolecularFormula <$> elemSymIntPairListGen++instance Arbitrary CondensedFormula where+ arbitrary = do+ n <- choose (0, 4)+ condForm <- vectorOf n+ (oneof [leftCondensedFormulaGen, sized arbRightCondensedFormulaGen])+ return $ CondensedFormula condForm+ where+ leftCondensedFormulaGen :: Gen (Either MolecularFormula (CondensedFormula, Int))+ leftCondensedFormulaGen = Left <$> arbitrary+ arbRightCondensedFormulaGen :: Int -> Gen (Either MolecularFormula (CondensedFormula, Int))+ arbRightCondensedFormulaGen 0 = do+ m <- choose (1, 4)+ condForm <- leftCondensedFormulaGen+ return $ Right (CondensedFormula [condForm], m)+ arbRightCondensedFormulaGen n | n > 0 = do+ m <- choose (1, 4)+ v <- choose (1, 3)+ let n' = n `div` (v + 1)+ condForm' <- replicateM v (arbRightCondensedFormulaGen n')+ return $ Right (CondensedFormula condForm', m)++instance Arbitrary EmpiricalFormula where+ arbitrary = mkEmpiricalFormula <$> elemSymIntPairListGen
test/Isotope/ParsersSpec.hs view
@@ -2,18 +2,38 @@ module Isotope.ParsersSpec (spec) where import Isotope+import Isotope.Parsers import Test.Hspec import Test.QuickCheck import Data.Map (fromList)+import Text.Megaparsec spec :: Spec spec = do- describe "mol" $- it "QuasiQuoter for molecular formulae should produce the correct value" $- [mol|C6H6|] `shouldBe` MolecularFormula {getMolecularFormula = fromList [(H,6),(C,6)]}- describe "emp" $- it "QuasiQuoter for empirical formulae should produce the correct value" $- [emp|CH|] `shouldBe` EmpiricalFormula {getEmpiricalFormula = fromList [(H,1),(C,1)]}- describe "con" $- it "QuasiQuoter for condensed formulae should produce the correct value" $- [con|CH3(CH3)2CH3|] `shouldBe` CondensedFormula {getCondensedFormula = [Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]}),Right ([MolecularFormula {getMolecularFormula = fromList [(C,1)]},MolecularFormula {getMolecularFormula = fromList [(H,3)]}],2),Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]})]}+ describe "elementSymbol" .+ it "Na" $+ parse (elementSymbol <* eof) "" "Na" `shouldBe` Right Na+ describe "subFormula" .+ it "C2" $+ parse (subFormula <* eof) "" "C2" `shouldBe` Right (C, 2)+ describe "molecularFormula" .+ it "CH2O" $+ parse (molecularFormula <* eof) "" "CH2O" `shouldBe` Right (MolecularFormula (fromList [(C, 1), (H, 2), (O, 1)]))+ describe "condensedFormula" .+ it "N(CH3)3" $+ parse (condensedFormula <* eof) "" "N(CH3)3" `shouldBe` Right (CondensedFormula {getCondensedFormula = [Left (MolecularFormula {getMolecularFormula = fromList [(N,1)]}),Right (CondensedFormula {getCondensedFormula = [Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]})]},3)]})+ describe "mol" .+ it "QuasiQuoter for MolecularFormula should produce correct value" $+ [mol|C6H6|] `shouldBe`+ MolecularFormula {getMolecularFormula = fromList [(H,6),(C,6)]}+ describe "emp" .+ it "QuasiQuoter for EmpiricalFormula should produce correct value" $+ [emp|CH|] `shouldBe`+ EmpiricalFormula {getEmpiricalFormula = fromList [(H,1),(C,1)]}+ describe "ele" .+ it "QuasiQuoter for ElementalComposition should produce correct value" $+ [ele|C6H6|] `shouldBe`+ ElementalComposition {getElementalComposition = fromList [(H,6),(C,6)]}+ describe "con" .+ it "QuasiQuoter for CondensedFormula should produce the correct value" $+ [con|CH3(CH3)2CH3|] `shouldBe` CondensedFormula {getCondensedFormula = [Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]}),Right (CondensedFormula {getCondensedFormula = [Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]})]},2),Left (MolecularFormula {getMolecularFormula = fromList [(C,1)]}),Left (MolecularFormula {getMolecularFormula = fromList [(H,3)]})]}