#ifndef __BLOP_UNITS_H__
#define __BLOP_UNITS_H__

#include "var.h"
#include "function.h"
#include <cmath>
namespace blop
{

namespace cons
{
    static const  double pi   = 3.14159265358979323846; 
}

namespace unit
{
    static const double percent = 0.01;
    static const double permill = 0.001;
    static const double permil  = 0.001;

    static const double deci  = 1e-1;
    static const double centi = 1e-2;
    static const double milli = 1e-3;
    static const double micro = 1e-6;
    static const double nano  = 1e-9;
    static const double pico  = 1e-12;
    static const double femto = 1e-15;
    static const double atto  = 1e-18;
    static const double zepto = 1e-21;
    static const double yocto = 1e-24;

    static const double deca  = 10;
    static const double hecto = 100;
    static const double kilo  = 1e3;
    static const double mega  = 1e6;
    static const double giga  = 1e9;
    static const double tera  = 1e12;
    static const double peta  = 1e15;
    static const double exa   = 1e18;
    static const double zetta = 1e21;
    static const double yotta = 1e24;

    // ---------- mass units ----------------------------------------------------------
    static const  double kilogram = 1;
    static const  double kg = kilogram;
    static const  double g  = kg * 1e-3;
    static const  double gramm = g;
    static const  double amu = 1.66053886e-27*kg;
    static const  double pound = 0.45359237*kg;
    static const  double lbs = pound;

    // ---------- time units ----------------------------------------------------------
    static const  double second = 1;
    static const  double s   = second;
    static const  double ms  = s * 1e-3;
    static const  double mus = s * 1e-6;
    static const  double ns  = s * 1e-9;
    static const  double ps = s * 1e-12;
    static const  double fs = s * 1e-15;
    static const  double minute = 60*second;
    static const  double hour = 60*minute;
    static const  double h    = hour;
    static const  double day = 24*hour;
    static const  double week = 7*day;
    static const  double month = 30*day;
    static const  double year = 365*day;

    // ---------- frequency units -----------------------------------------------------
    static const double Hertz = 1.0/second;
    static const double hertz = Hertz;
    static const double Hz  = Hertz;
    static const double kHz = Hz * 1e3;
    static const double MHz = Hz * 1e6;
    static const double GHz = Hz * 1e9;
    static const double THz = Hz * 1e12;

    // ---------- distance units ------------------------------------------------------
    static const  double meter = 1;
    static const  double m   = meter;
    static const  double dm  = m * 1e-1;
    static const  double cm  = m * 1e-2;
    static const  double mm  = m * 1e-3;
    static const  double mum = m * 1e-6;
    static const  double micron = mum;
    static const  double nm  = nano * m;
    static const  double pm  = pico * m;
    static const  double fm  = femto * m;
    static const  double fermi = fm;
    static const  double angstrom = 1e-10 * meter;
    static const  double km = 1000*meter;
    static const  double mile = 1609.344*meter;
    static const  double inch = 25.4*mm;
    static const  double mil  = 1e-3*inch;
    static const  double foot = 0.3048*meter;

    // ---------- volume units  -------------------------------------------------------
    static const  double m3  = m*m*m;
    static const  double dm3 = dm*dm*dm;
    static const  double liter = dm3;
    static const  double cm3 = cm*cm*cm;
    static const  double mm3 = mm*mm*mm;

    static const  double m2  = m*m;
    static const  double dm2 = dm*dm;
    static const  double cm2 = cm*cm;
    static const  double mm2 = mm*mm;

    // ---------- energy units --------------------------------------------------------
    static const  double Joule   = kg * m*m / (second*second);
    static const  double joule   = Joule;
    static const  double J       = Joule;               // SI unit
    static const  double mJ      = J * 1e-3;
    static const  double kJ      = J * 1e3;
    static const  double MJ      = J * 1e6;
    static const  double cal     = 4.1868 * Joule;
    static const  double kcal    = cal * 1000;

    // ---------- power units ---------------------------------------------------------
    static const  double Watt    = Joule/second;
    static const  double watt    = Watt;
    static const  double W       = Watt;
    static const  double kW      = kilo*Watt;
    static const  double MW      = mega*Watt;
    static const  double GW      = giga*Watt;
    static const  double TW      = tera*Watt;
    static const  double PW      = peta*Watt;
    static const  double mW      = W * 1e-3;
    
    // ---------- charge unit ---------------------------------------------------------
    static const  double Coulomb = 1;
    static const  double coulomb = Coulomb;
    static const  double C = Coulomb;     // Coulomb
    static const  double mC  = 1e-3*C;
    static const  double muC = 1e-6*C;
    static const  double nC  = 1e-9*C;
    static const  double pC  = 1e-12*C;
    static const  double fC  = 1e-15*C;

    // ---------- current units -------------------------------------------------------
    static const double Ampere = Coulomb/second;
    static const double ampere = Ampere;
    static const double A      = Ampere;
    static const double mA     = 1e-3 * A;
    static const double muA    = 1e-6 * A;
    static const double nA     = 1e-9 * A;
    static const double kA     = 1e3 * A;
    

    // ---------- force units ---------------------------------------------------------
    static const double Newton  = kilogram * meter / (second*second);
    static const double newton  = Newton;
    static const double N       = Newton;
    static const double lbf     = 4.44822162*Newton; // http://en.wikipedia.org/wiki/Pound-force
    
    // ---------- temperature units ---------------------------------------------------
    static const double Kelvin = 1;
    static const double kelvin = 1;
    static const double K      = Kelvin;
    static const double mK     = K * 1e-3;
    //static const double Fahrenheit = 5./9. * Kelvin;

    // ---------- amount of substance -------------------------------------------------
    static const double mole   = 1;
    static const double mol    = mole;
    
    // ---------- luminous intensity  -------------------------------------------------
    static const double candela = 1;
    static const double cd      = candela;
    
    // ---------- Potential -----------------------------------------------------------
    static const  double Volt = Joule/Coulomb;
    static const  double volt = Volt;
    static const  double V  = Volt;
    static const  double mV = 1e-3 * V;
    static const  double kV = 1e3 * V;
    static const  double MV = 1e6 * V;
    
    // ---------- Magnetic field ------------------------------------------------------
    static const  double T     = 1;  // Tesla
    static const  double Tesla = T;  // move verbose name
    static const  double mT    = 1e-3 * T;
    static const  double Gauss = 1e-4 * Tesla;
    static const  double G     = Gauss;
    static const double mG     = milli*Gauss;
    static const double mGauss = milli*Gauss;
    static const  double kGauss = kilo*Gauss;
    static const  double kG     = kilo*Gauss;
    static const  double Oersted = 1000/(4*cons::pi)*Ampere/meter;

    static const  double Debye = 3.33564e-30 * Coulomb*meter; 
    
    // ---------- resistance units ----------------------------------------------------
    static const double Ohm   = Volt/Ampere;
    static const double ohm   = Volt/Ampere;
    static const double mOhm  = 1e-3 * Ohm;
    static const double muOhm = 1e-6 * Ohm;
    static const double nOhm  = 1e-9 * Ohm;
    static const double nanoOhm = nOhm;
    static const double kOhm  = 1e3  * Ohm;
    static const double MOhm  = 1e6  * Ohm;
    static const double Siemens = 1/Ohm;
    static const double siemens = Siemens;
    static const double S       = Siemens;
    static const double mS      = 1e-3 * Siemens;
    static const double muS     = 1e-6 * Siemens;

    // ---------- Angle units ---------------------------------------------------------
    static const  double radian = 1;
    static const  double rad    = 1;
    static const  double mrad   = milli*radian;
    static const  double mradian = mrad;
    static const  double degree = 3.14159265358979323846/180.0;
    static const  double deg    = degree;
    
    // ---------- capacitance units ---------------------------------------------------
    static const double Farad = s*s*s*s * A*A / kg / (m*m);
    static const double farad = Farad;
    static const double F       = Farad;
    static const double mF      = 1e-3 * F;
    static const double mFarad  = 1e-3 * F;
    static const double muF     = 1e-6 * F;
    static const double muFarad = 1e-6 * F;
    static const double nF      = 1e-9 * F;
    static const double nFarad  = 1e-9 * F;
    static const double pF      = 1e-12 * F;
    static const double pFarad  = 1e-12 * F;

    // ---------- inductance units ----------------------------------------------------
    static const double Henry  = kg * m*m / (s*s) / (A*A);
    static const double henry  = Henry;
    static const double H      = Henry;
    static const double mH     = 1e-3 * H;
    static const double mHenry = 1e-3 * H;
    static const double muH    = 1e-6 * H;
    static const double muHenry= 1e-6 * H;
    static const double microHenry= 1e-6 * H;
    static const double microH    = 1e-6 * H;
    static const double nH     = 1e-9 * H;
    static const double nHenry = 1e-9 * H;
    static const double nanoHenry = 1e-9 * H;
    static const double nanohenry = 1e-9 * H;
    static const double pH     = 1e-12* H;
    static const double pHenry = 1e-12* H;

    // ---------- pressure units ------------------------------------------------------
    static const double Pascal = Newton / (meter*meter);
    static const double pascal = Pascal;
    static const double Pa     = Pascal;
    static const double kPa    = 1e3 * Pa;
    static const double MPa    = 1e6 * Pa;
    static const double GPa    = 1e9 * Pa;
    static const double mPa    = 1e-3 * Pa;
    static const double muPa   = 1e-6 * Pa;
    static const double Torr   = 133.3223999*Pa;
    static const double mTorr  = 1e-3 * Torr;
    static const double bar    = 1e5 * Pa;  // = 750.062 Torr = 0.9869 atm
    static const double mbar   = 1e-3 * bar;
    static const double atm    = 760 * Torr;
    static const double psi    = lbf/(inch*inch);   // pound per square-inch http://en.wikipedia.org/wiki/Pounds_per_square_inch
    static const double ksi    = 1000*psi;          // kilo-pound per square-inch

    inline static double F2K(double F) { return (F+459.67)*5.0/9.0; } // Fahrenheit to Kelvin
    inline static double K2F(double K) { return K*9.0/5.0-459.67; }  // Kelvin to Fahrenheit
    inline static double F2C(double F) { return (F-32)*5.0/9.0; }    // Fahrenheit to Celsius
    inline static double C2F(double C) { return C*9.0/5.0+32;   }    // Celsius to Fahrenheit
    inline static function F2K(function F) { return (F+459.67)*5.0/9.0; } // Fahrenheit to Kelvin
    inline static function K2F(function K) { return K*9.0/5.0-459.67; }  // Kelvin to Fahrenheit
    inline static function F2C(function F) { return (F-32.0)*5.0/9.0; }    // Fahrenheit to Celsius
    inline static function C2F(function C) { return C*9.0/5.0+32.0;   }    // Celsius to Fahrenheit


    // ---------- radiation -----------------------------------------------------------
    static const double Becquerel = 1/s;
    static const double Bq = Becquerel;
    static const double Curie = 3.7e10*Bq;
    static const double Ci = Curie;
    static const double mCi = 1e-3*Ci;
    static const double muCi = 1e-6*Ci;
    static const double gray = Joule/kg;  // 1 Joule absorbed in 1 kg
    static const double Gy = gray;
    //static const double rad = 0.01*Gy;  this already exists as radian!
    static const double Sievert = 1;
    static const double Sv = Sievert;
    static const double mSv = 1e-3*Sv;
    static const double muSv = 1e-6*Sv;
    static const double rem = 0.01*Sv;
    static const double mrem = 1e-3*rem;

}

namespace cons
{
     static const  double c    = 299792458  * blop::unit::m/blop::unit::s;       // speed of light     [m/s] (CODATA, 2006)
     static const  double h    = 6.6260693e-34 * blop::unit::J*blop::unit::s;    // Planck's constant  [Js] (CODATA, 2006)
     static const  double hbar = h/2/pi;                             // Planck's constant  [Js]
     static const  double kB   = 1.3806505e-23 * blop::unit::J/blop::unit::K;    // Boltzmann constant [J/K] (CODATA, 2006)
     static const  double e    = 1.60217653e-19 * blop::unit::C;           // electron charge  [C] (CODATA, 2006)
     static const  double epsilon0 = 8.854187817e-12;                // permittivity of free space [F/m] (CODATA 2006)
     static const  double mu0      = 12.566370614e-7;                // permeability of free spa [N/A^2] (CODATA 2006)
     static const  double Z0       = ::sqrt(mu0/epsilon0);           // impedance of free space

     static const  double m_proton   = 1.67262171e-27*blop::unit::kg;      // mass of proton (CODATA, 2006)
     static const  double m_electron = 9.1093826e-31*blop::unit::kg;       // mass of electron (CODATA, 2006)
     static const  double m_deuteron = 3.34358335e-27 * blop::unit::kg;    // mass of deuteron (CODATA, 2006)
     static const  double m_helion   = 5.00641214e-27*blop::unit::kg;      // mass of 3He nucleus (CODATA, 2006)

     static const  double bohr_radius = 0.5291772108e-10;            // [m] (CODATA, 2006)
     static const  double bohr_magneton = e*(hbar/(2*m_electron)); 

     static const  double Rydberg = 1.0973731568525e7;    // cons::m_electron*pow(cons::e,4)/(8*pow(cons::epsilon0,2)*pow(cons::h,3)*cons::c)

     // Avogadro's constant
     static const  double Avogadro = 6.02214199e23;

     // fine structure constant
     static const  double alpha = 7.297352568e-3; // e*e/2/epsilon0/h/c;      (CODATA value, 2006)

     // gravitational constant: F = G*m1*m2/r^2
     static const  double G     = 6.67428e-11 * blop::unit::Newton*blop::unit::m*blop::unit::m/(blop::unit::kg*blop::unit::kg);
     // standard gravity (acceleration on the surface of earth)
     static const  double g     = 9.80665 * blop::unit::m/(blop::unit::s*blop::unit::s); // http://en.wikipedia.org/wiki/Pound-force


}

namespace unit
{
   // ---------- atomic units --------------------------------------------------------

     static const  double atomic_mass     = cons::m_electron;

     static const  double atomic_charge   = cons::e;

     static const  double atomic_length   = cons::bohr_radius;

    // atomic unit of velocity: velocity in first bohr orbit
     static const  double atomic_velocity = cons::e*cons::e/(4*cons::pi*cons::epsilon0*cons::hbar);

    // atomic unit of time: period of the first bohr orbit divided by 2pi
     static const  double atomic_time     = cons::bohr_radius / atomic_velocity;
	                                       
     static const  double atomic_frequency = 1.0/atomic_time;

    // atomic unit of energy, twice the ionization otential of atomic hydrogen, 27.2113845 eV
     static const  double atomic_energy   = cons::e*cons::e/(4*cons::pi*cons::epsilon0*cons::bohr_radius);
     static const  double hartree         = atomic_energy;

     static const  double eV      = cons::e * unit::V;   //1.602176462e-19 * J;
     static const  double keV     = eV * 1e3;
     static const  double MeV     = eV * 1e6;
     static const  double GeV     = eV * 1e9;
     static const  double TeV     = eV * 1e12;
     static const  double meV     = eV * 1e-3;

     // units for masses of elementary particles
     static const  double MeVc2   = MeV*cons::c*cons::c;
     static const  double keVc2   = keV*cons::c*cons::c;
     static const  double GeVc2   = GeV*cons::c*cons::c;

    // Rydberg = ionization potential of hydrogen
     static const  double Ry              = 0.5 * hartree;   // 13.60569172 * eV;
     static const  double Rydberg         = Ry;
    
    // electric dipole
     static const  double atomic_dipole   = atomic_charge * atomic_length;  // 2.54175*Debye


     string default_format();
     void default_format(const var &f);

}

namespace cons
{
     static const double m_muon     = 105.658367*unit::MeV/(blop::cons::c*blop::cons::c); // http://pdg.lbl.gov/2010/tables/rpp2010-sum-leptons.pdf
     static const double m_pion0    = 134.9766 * unit::MeV/(blop::cons::c*blop::cons::c); // http://pdg.lbl.gov/2010/tables/rpp2010-sum-mesons.pdf
     static const double m_pionpm   = 139.57018* unit::MeV/(blop::cons::c*blop::cons::c);
     static const double m_pion     = 0.5*(m_pion0+m_pionpm);
     static const double m_kaonpm   = 493.677  * unit::MeV/(blop::cons::c*blop::cons::c);
     static const double m_Kpm      = m_kaonpm;
     static const double m_kaon0    = 497.614  * unit::MeV/(blop::cons::c*blop::cons::c);
     static const double m_K0       = m_kaon0;

}

bool find_unit_or_cons(const var &name, double *value, string *symbol=0);
std::string symbol_of_unit_or_cons(const var &name);
double      value_of_unit_or_cons(const var &name);


}

#endif