LCOV - code coverage report
Current view: top level - src/Unit - Unit.H (source / functions) Coverage Total Hit
Test: coverage_merged.info Lines: 80.1 % 211 169
Test Date: 2025-12-10 23:45:13 Functions: 90.7 % 43 39

            Line data    Source code
       1              : #ifndef UNIT_UNIT_H
       2              : #define UNIT_UNIT_H
       3              : 
       4              : 
       5              : #include <array>
       6              : #include <charconv>
       7              : #include <cstdlib>
       8              : #include <iostream>
       9              : #include <system_error>
      10              : #include <unordered_map>
      11              : #include <string>
      12              : #include <stdexcept>
      13              : #include <cmath>
      14              : #include <regex>
      15              : 
      16              : #include "Util/Util.H"
      17              : //#include "Set/Base.H"
      18              : //#include "String.H"
      19              : 
      20              : struct Unit : std::pair<double,std::array<int,7>>
      21              : {
      22              : 
      23              :     //
      24              :     // STATIC CONST DEFINITIONS, TRUE EVERYWHERE
      25              :     // 
      26              :     enum class Type {
      27              :         Length=0, Time=1, Mass=2, Temperature=3, Current=4, Amount=5, LuminousIntensity=6
      28              :     };    
      29              :     // Fundamental units
      30              :     inline static const std::map<std::string, std::pair<double,std::array<int,7>>> base_units =
      31              :     {
      32              :         {"1",  {1.0, {0,0,0,0,0,0,0}}},
      33              :         {"m",  {1.0, {1,0,0,0,0,0,0}}},
      34              :         {"s",  {1.0, {0,1,0,0,0,0,0}}},
      35              :         {"kg", {1.0, {0,0,1,0,0,0,0}}},
      36              :         {"K",  {1.0, {0,0,0,1,0,0,0}}},
      37              :         {"A",  {1.0, {0,0,0,0,1,0,0}}},
      38              :         {"mol",{1.0, {0,0,0,0,0,1,0}}},
      39              :         {"cd", {1.0, {0,0,0,0,0,0,1}}}
      40              :     };
      41              :     //
      42              :     // Compound unit relationships and nomenclature
      43              :     // Any desired unit relationship can be added to this list.
      44              :     // The unit conversion can be specified in any desired format, as long as
      45              :     // all of the units on the right hand side are resolvable.
      46              :     //
      47              :     inline static const std::map<std::string, std::pair<double,std::string>> compound = {
      48              :         // Lengths
      49              :         {"km",        {1e+3,    "m"        }},
      50              :         {"hm",        {1e+2,    "m"        }},
      51              :         {"dam",       {1e+1,    "m"        }},
      52              :         {"dm",        {1e-1,    "m"        }},
      53              :         {"cm",        {1e-2,    "m"        }},
      54              :         {"mm",        {1e-3,    "m"        }},
      55              :         {"um",        {1e-6,    "m"        }},
      56              :         {"nm",        {1e-9,    "m"        }},
      57              :         {"pm",        {1e-12,   "m"        }},
      58              :         {"in",        {0.0254,  "m"        }},
      59              :         {"ft",        {0.3048,  "m"        }},
      60              :         {"yd",        {0.9144,  "m"        }},
      61              :         {"mi",        {5280.0,  "ft"       }},
      62              :         {"furlong",   {660.0,   "ft"       }},
      63              :         {"au",        {1.495978707e11, "m" }}, // astronomical unit
      64              :         {"ly",        {9.4607304725808e15, "m"}}, // lightyear
      65              :         {"pc",        {3.08567758149137e16, "m"}}, // parsec
      66              : 
      67              :         // Angles
      68              :         {"rad",       {1.0,     "1"}},
      69              :         {"deg",       {0.01745329251, "rad" }},
      70              : 
      71              :         // Times
      72              :         {"ms",        {1e-3,    "s"        }},
      73              :         {"us",        {1e-6,    "s"        }},
      74              :         {"ns",        {1e-9,    "s"        }},
      75              :         {"ps",        {1e-12,   "s"        }},
      76              :         {"min",       {60.0,    "s"        }},
      77              :         {"hr",        {60.0,    "min"      }},
      78              :         {"day",       {24.0,    "hr"       }},
      79              :         {"week",      {7.0,     "day"      }},
      80              :         {"fortnight", {14.0,    "day"      }},
      81              :         {"year",      {365.25,  "day"      }},
      82              :         {"decade",    {10.0,    "year"     }},
      83              : 
      84              :         // Mass
      85              :         {"g",         {1e-3,    "kg"       }},
      86              :         {"mg",        {1e-6,    "kg"       }},
      87              :         {"ug",        {1e-9,    "kg"       }},
      88              :         {"tonne",     {1000.0,  "kg"       }},
      89              :         {"lb",        {0.45359237, "kg"    }},
      90              :         {"oz",        {0.0283495, "kg"     }},
      91              :         {"slug",      {14.5939, "kg"       }},
      92              : 
      93              :         // Speed
      94              :         {"mph",       {1.0,     "mi/hr"    }},
      95              :         {"kph",       {1.0,     "km/hr"    }},
      96              :         {"mps",       {1.0,     "m/s"      }},
      97              :         {"fps",       {1.0,     "ft/s"     }},
      98              :         {"knot",      {1852.0 / 3600.0, "m/s"}},
      99              : 
     100              :         // Area
     101              :         {"ha",        {1e4,     "m^2"      }},
     102              :         {"acre",      {4046.85642, "m^2"   }},
     103              :         {"sqft",      {1.0,     "ft^2"     }},
     104              :         {"sqin",      {1.0,     "in^2"     }},
     105              :         {"sqkm",      {1.0,     "km^2"     }},
     106              :         {"sqmi",      {1.0,     "mi^2"     }},
     107              : 
     108              :         // Volume
     109              :         {"L",         {1e-3,    "m^3"      }},
     110              :         {"mL",        {1e-6,    "m^3"      }},
     111              :         {"cc",        {1e-6,    "m^3"      }},
     112              :         {"gal",       {3.78541e-3, "m^3"   }},
     113              :         {"qt",        {0.946353e-3, "m^3"  }},
     114              :         {"pt",        {0.473176e-3, "m^3"  }},
     115              :         {"cup",       {0.24e-3, "m^3"      }},
     116              :         {"floz",      {29.5735e-6, "m^3"   }},
     117              :         {"tbsp",      {14.7868e-6, "m^3"   }},
     118              :         {"tsp",       {4.92892e-6, "m^3"   }},
     119              : 
     120              :         // Force
     121              :         {"N",         {1.0,     "kg*m/s^2" }},
     122              :         {"kN",        {1e3,     "N"        }},
     123              :         {"lbf",       {4.44822162, "N"     }},
     124              :         {"dyn",       {1e-5,    "N"        }},
     125              : 
     126              :         // Energy
     127              :         {"J",         {1.0,     "N*m"      }},
     128              :         {"mJ",        {1e-3,    "J"        }},
     129              :         {"kJ",        {1e3,     "J"        }},
     130              :         {"MJ",        {1e6,     "J"        }},
     131              :         {"eV",        {1.602176634e-19, "J"}},
     132              :         {"cal",       {4.184,   "J"        }},
     133              :         {"kcal",      {4184.0,  "J"        }},
     134              :         {"BTU",       {1055.06, "J"        }},
     135              :         {"ft*lbf",    {1.35582, "J"        }},
     136              : 
     137              :         // Power
     138              :         {"W",         {1.0,     "J/s"      }},
     139              :         {"mW",        {1e-3,    "W"        }},
     140              :         {"kW",        {1e3,     "W"        }},
     141              :         {"MW",        {1e6,     "W"        }},
     142              :         {"hp",        {745.7,   "W"        }},
     143              : 
     144              :         // Pressure
     145              :         {"Pa",        {1.0,     "N/m^2"    }},
     146              :         {"kPa",       {1e3,     "Pa"       }},
     147              :         {"MPa",       {1e6,     "Pa"       }},
     148              :         {"GPa",       {1e9,     "Pa"       }},
     149              :         {"bar",       {1e5,     "Pa"       }},
     150              :         {"atm",       {101325.0,"Pa"       }},
     151              :         {"psi",       {6894.76, "Pa"       }},
     152              :         {"mmHg",      {133.322, "Pa"       }},
     153              :         {"torr",      {133.322, "Pa"       }},
     154              : 
     155              :         // Temperature differences (not absolute temperatures)
     156              :         {"K",         {1.0,     "K"        }},
     157              :         {"degR",      {5.0/9.0, "K"        }},
     158              : 
     159              :         // Charge
     160              :         {"C",         {1.0,     "A*s"      }},
     161              :         {"mAh",       {3.6,     "C"        }},
     162              : 
     163              :         // Voltage
     164              :         {"V",         {1.0,     "J/C"      }},
     165              : 
     166              :         // Capacitance
     167              :         {"F",         {1.0,     "C/V"      }},
     168              : 
     169              :         // Resistance
     170              :         {"ohm",       {1.0,     "V/A"      }},
     171              :         {"kOhm",      {1e3,     "ohm"      }},
     172              :         {"MOhm",      {1e6,     "ohm"      }},
     173              : 
     174              :         // Conductance
     175              :         {"S",         {1.0,     "1/ohm"    }},
     176              : 
     177              :         // Magnetic
     178              :         {"T",         {1.0,     "kg/(A*s^2)"}},
     179              :         {"G",         {1e-4,    "T"        }}, // gauss
     180              : 
     181              :         // Frequency
     182              :         {"Hz",        {1.0,     "1/s"      }},
     183              :         {"kHz",       {1e3,     "Hz"       }},
     184              :         {"MHz",       {1e6,     "Hz"       }},
     185              :         {"GHz",       {1e9,     "Hz"       }},
     186              : 
     187              :         // Moles
     188              :         {"kmol",      {1e3,     "mol"      }}
     189              :     };
     190              : 
     191              : 
     192              :     //
     193              :     // These static factory functions return unit types.
     194              :     // They are used to check unit compliance, mainly at the parsing stage.
     195              :     // 
     196              :     // Primary
     197         3125 :     static Unit Less()              { return Unit{1.0, {0,0,0,0,0,0,0}}; } // 1
     198          323 :     static Unit Length()            { return Unit{1.0, {1,0,0,0,0,0,0}}; } // m
     199          464 :     static Unit Time()              { return Unit{1.0, {0,1,0,0,0,0,0}}; } // s
     200          101 :     static Unit Mass()              { return Unit{1.0, {0,0,1,0,0,0,0}}; } // kg
     201           77 :     static Unit Temperature()       { return Unit{1.0, {0,0,0,1,0,0,0}}; } // K
     202           42 :     static Unit Current()           { return Unit{1.0, {0,0,0,0,1,0,0}}; } // A
     203           42 :     static Unit Amount()            { return Unit{1.0, {0,0,0,0,0,1,0}}; } // mol
     204           42 :     static Unit LuminousIntensity() { return Unit{1.0, {0,0,0,0,0,0,1}}; } // cd
     205              : 
     206              :     static Unit Angle() {return Unit::Less();}
     207              : 
     208              :     // Geometry and kinematics
     209           26 :     static Unit Area()        { return Length()*Length(); }
     210            9 :     static Unit Volume()      { return Length()*Length()*Length(); }
     211           52 :     static Unit Velocity()    { return Length() / Time();}
     212           44 :     static Unit Acceleration(){ return Velocity() / Time(); }
     213              :     // Mechanics
     214           44 :     static Unit Force()       { return Mass() * Acceleration(); } // kg·m/s² = N
     215              :     static Unit Momentum()    { return Mass() * Velocity(); }
     216              :     static Unit Impulse()     { return Force() * Time(); }
     217           15 :     static Unit Pressure()    { return Force() / Area(); }         // N/m² = Pa
     218           29 :     static Unit Energy()      { return Force() * Length(); }       // N·m = J
     219            8 :     static Unit Power()       { return Energy() / Time(); }        // J/s = W
     220            9 :     static Unit Density()     { return Mass() / Volume(); }
     221              :     static Unit SpecificWeight() { return Force() / Volume(); }
     222              :     static Unit Work()        { return Energy(); }
     223              :     // Thermodynamics
     224            6 :     static Unit SpecificHeatCapacity()    {return Energy() / Mass() / Temperature() ;}
     225              :     static Unit MolarHeatCapacity()       {return Energy() / Amount() / Temperature() ;}
     226            6 :     static Unit ThermalConductivity()     {return Power()/Length()/Temperature();}
     227            4 :     static Unit ThermalDiffusivity()      {return ThermalConductivity() / Density() / SpecificHeatCapacity();}
     228              :     static Unit HeatFlux()                { return Power() / Area(); }
     229              :     static Unit HeatTransferCoefficient() { return HeatFlux() / Temperature(); }
     230              :     // Electromagnetism
     231              : 
     232              :     static Unit Charge()      { return Current() * Time(); }                    // A·s = C
     233              :     static Unit Voltage()     { return Power() / Current(); }                   // W/A = V
     234              :     static Unit Resistance()  { return Voltage() / Current(); }                 // V/A = Ω
     235              :     static Unit Capacitance() { return Charge() / Voltage(); }                  // C/V = F
     236              :     static Unit Conductance() { return Less() / Resistance(); }                 // S = 1/Ω
     237              :     static Unit MagneticFlux(){ return Voltage() * Time(); }                    // V·s = Wb
     238              :     static Unit MagneticField(){ return MagneticFlux() / Area(); }              // Wb/m² = T
     239              :     static Unit Inductance()  { return MagneticFlux() / Current(); }            // Wb/A = H
     240              :     // Chemistry
     241              :     static Unit MolarMass()   { return Mass() / Amount(); }                     // kg/mol
     242              :     static Unit Concentration(){ return Amount() / Volume(); }                  // mol/m³
     243              :     static Unit MolarEnergy() { return Energy() / Amount(); }                   // J/mol
     244              :     // Optics and radiation
     245              :     static Unit LuminousFlux()      { return LuminousIntensity(); }             // for scalar usage (lm)
     246              :     static Unit Illuminance()       { return LuminousFlux() / Area(); }         // lux = lm/m²
     247              : 
     248              : 
     249              :     //
     250              :     // This is where SYSTEM NORMALIZATION is stored.
     251              :     // Should usually be set once (at the beginning of the simulation) and then
     252              :     // left constant throughout the simulation.
     253              :     // All normalizations are in terms of standard SI units: m,s,kg,K,A,mol,cd.
     254              :     //
     255              :     inline static std::array<std::pair<std::string,double>, 7> normalization = {
     256              :         std::pair{"m",  1.0},
     257              :         std::pair{"s",  1.0},
     258              :         std::pair{"kg", 1.0},
     259              :         std::pair{"K",  1.0},
     260              :         std::pair{"A",  1.0},
     261              :         std::pair{"mol",1.0},
     262              :         std::pair{"cd", 1.0}
     263              :     };
     264              : 
     265              :     using std::pair<double, std::array<int,7>>::pair;
     266         1745 :     Unit(const std::pair<double, std::array<int, 7>>& p) : std::pair<double, std::array<int, 7>>(p) {}
     267              :     static std::map<std::string, int> ParseUnitString(const std::string& input);
     268              : 
     269              :     
     270          524 :     static Unit Parse(double val, std::string unitstring, bool verbose = false)
     271              :     {
     272          524 :         Unit ret = StringParse(unitstring, verbose);
     273          524 :         ret.first *= val;
     274          524 :         return ret;
     275              :     }
     276         1133 :     static Unit Parse(std::string unit, bool verbose = false)
     277              :     {
     278         1133 :         if (unit.size() == 0) return Unit::Less();
     279         1107 :         std::vector<std::string> split;
     280         1107 :         std::stringstream ss(unit);
     281         1107 :         std::string item;
     282         2738 :         while (std::getline(ss, item, '_')) {
     283         1631 :             split.push_back(item);
     284              :         }
     285         1107 :         if (split.size() == 2)
     286              :         {        
     287          524 :             return Parse(std::stod(split[0]),split[1], verbose);
     288              :         }
     289          583 :         else if (split.size() == 1)
     290              :         {
     291              :             // Attempt to parse the string as if it's a unitless number
     292          583 :             double value = NAN;
     293              : 
     294              :             // auto first = unit.data();
     295              :             // auto last = unit.data() + unit.size();
     296              :             // auto result = std::from_chars(first, last, value); // apparently this fails on mac
     297              :             
     298          583 :             char *end = nullptr;
     299          583 :             value = std::strtod(unit.c_str(), &end);
     300              : 
     301              :             // If it is a untless number, then return a unitless result
     302              :             //if (result.ec == std::errc() && result.ptr == last) // fails on mac
     303          583 :             if (end != unit.data() && *end == '\0')
     304              :             {
     305          581 :                 Unit ret = Unit::Less();
     306          581 :                 ret.first = value;
     307          581 :                 return ret;
     308              :             }
     309              :             // Otherwise, it must be a pure unit, so return that.
     310              :             else
     311              :             {
     312            2 :                 return StringParse(unit);
     313              :             }
     314              :         }
     315              :         else
     316              :         {
     317            0 :             throw std::runtime_error("Invalid unit string "+unit);
     318              :         }
     319         1107 :     }
     320              : 
     321              : 
     322         1420 :     static Unit StringParse(std::string unitstring, bool verbose = false)
     323              :     {
     324         1420 :         Unit type = Unit::Less();
     325              : 
     326              :         // Get the map of base tokens with signed exponents from ParseUnitString
     327         1420 :         std::map<std::string,int> parsed_units = ParseUnitString(unitstring);
     328         1420 :         if (verbose)
     329              :         {
     330            0 :             for (auto parsed_unit : parsed_units)
     331              :             {
     332            0 :                 std::cout << parsed_unit.first << " " << parsed_unit.second << std::endl;
     333            0 :             }
     334              :         }
     335              : 
     336         3765 :         for (auto &p : parsed_units)
     337              :         {
     338         2345 :             const std::string &token = p.first;
     339         2345 :             int exp = p.second;
     340              : 
     341         2345 :             if (Unit::base_units.find(token) != base_units.end())
     342              :             {
     343              :                 // Base unit: apply exponent
     344         1745 :                 Unit t = Unit(Unit::base_units.at(token)) ^ exp;
     345         1745 :                 type = type * t;
     346              :             }
     347          600 :             else if (compound.find(token) != compound.end())
     348              :             {
     349              :                 // Compound unit: expand recursively
     350          600 :                 auto pair = compound.at(token);
     351          600 :                 double factor = pair.first;
     352          600 :                 const std::string &subunit_str = pair.second;
     353              : 
     354          600 :                 Unit t = StringParse(subunit_str);  // recursive expansion
     355          600 :                 t = t ^ exp;                         // apply exponent to both factor and dimension
     356          600 :                 t.first *= std::pow(factor, exp);    // scale numeric factor correctly
     357              : 
     358          600 :                 type = type * t;
     359          600 :             }
     360              :             else
     361              :             {
     362            0 :                 throw std::runtime_error("Unknown unit token: " + token);
     363              :             }
     364              :         }
     365              : 
     366         2840 :         return type;
     367         1420 :     }
     368              : 
     369           42 :     static void setLengthUnit(std::string unit)
     370              :     {
     371           42 :         Unit parsed = StringParse(unit);
     372           42 :         if (parsed.second != Length().second) throw std::runtime_error("Not a unit of length: " + unit);
     373           42 :         normalization[int(Type::Length)].first = unit;
     374           42 :         normalization[int(Type::Length)].second = 1.0 / parsed.first;
     375           42 :     }
     376              :     
     377           42 :     static void setTimeUnit(std::string unit)
     378              :     {
     379           42 :         Unit parsed = StringParse(unit);
     380           42 :         if (parsed.second != Time().second) throw std::runtime_error("Not a unit of time: " + unit);
     381           42 :         normalization[int(Type::Time)].first = unit;
     382           42 :         normalization[int(Type::Time)].second = 1.0 / parsed.first;
     383           42 :     }
     384              :     
     385           42 :     static void setMassUnit(std::string unit)
     386              :     {
     387           42 :         Unit parsed = StringParse(unit);
     388           42 :         if (parsed.second != Mass().second) throw std::runtime_error("Not a unit of mass: " + unit);
     389           42 :         normalization[int(Type::Mass)].first = unit;
     390           42 :         normalization[int(Type::Mass)].second = 1.0 / parsed.first;
     391           42 :     }
     392              :     
     393           42 :     static void setTemperatureUnit(std::string unit)
     394              :     {
     395           42 :         Unit parsed = StringParse(unit);
     396           42 :         if (parsed.second != Temperature().second) throw std::runtime_error("Not a unit of temperature: " + unit);
     397           42 :         normalization[int(Type::Temperature)].first = unit;
     398           42 :         normalization[int(Type::Temperature)].second = 1.0 / parsed.first;
     399           42 :     }
     400              :     
     401           42 :     static void setCurrentUnit(std::string unit)
     402              :     {
     403           42 :         Unit parsed = StringParse(unit);
     404           42 :         if (parsed.second != Current().second) throw std::runtime_error("Not a unit of current: " + unit);
     405           42 :         normalization[int(Type::Current)].first = unit;
     406           42 :         normalization[int(Type::Current)].second = 1.0 / parsed.first;
     407           42 :     }
     408              :     
     409           42 :     static void setAmountUnit(std::string unit)
     410              :     {
     411           42 :         Unit parsed = StringParse(unit);
     412           42 :         if (parsed.second != Amount().second) throw std::runtime_error("Not a unit of amount: " + unit);
     413           42 :         normalization[int(Type::Amount)].first = unit;
     414           42 :         normalization[int(Type::Amount)].second = 1.0 / parsed.first;
     415           42 :     }
     416              :     
     417           42 :     static void setLuminousIntensityUnit(std::string unit)
     418              :     {
     419           42 :         Unit parsed = StringParse(unit);
     420           42 :         if (parsed.second != LuminousIntensity().second) throw std::runtime_error("Not a unit of luminous intensity: " + unit);
     421           42 :         normalization[int(Type::LuminousIntensity)].first = unit;
     422           42 :         normalization[int(Type::LuminousIntensity)].second = 1.0 / parsed.first;
     423           42 :     }
     424              : 
     425         1368 :     bool isType(const Unit &test) const
     426              :     {
     427         1368 :         return this->second == test.second;
     428              :     }
     429              : 
     430         2462 :     Unit operator * (const Unit &rhs)
     431              :     {
     432         2462 :         Unit ret;
     433         2462 :         ret.first = this->first * rhs.first;
     434        22158 :         for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i] + rhs.second[i];
     435         2462 :         return ret;
     436              :     }
     437            0 :     friend Unit operator*(double lhs, const Unit& rhs) {
     438            0 :         Unit ret;
     439            0 :         ret.first = lhs * rhs.first;
     440            0 :         ret.second = rhs.second;
     441            0 :         return ret;
     442              :     }
     443              :     friend Unit operator*(const Unit& lhs, double rhs) {
     444              :         Unit ret;
     445              :         ret.first = rhs * lhs.first;
     446              :         ret.second = lhs.second;
     447              :         return ret;
     448              :     }
     449          186 :     Unit operator / (const Unit &rhs)
     450              :     {
     451          186 :         Unit ret;
     452          186 :         ret.first = this->first / rhs.first;
     453         1674 :         for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i] - rhs.second[i];
     454          186 :         return ret;
     455              :     }
     456            3 :     friend Unit operator / (double lhs, const Unit& rhs) {
     457            3 :         Unit ret = Unit::Less() / rhs;
     458            3 :         ret.first = lhs * rhs.first;
     459            3 :         return ret;
     460              :     }
     461              :     friend Unit operator / (const Unit& lhs, double rhs) {
     462              :         Unit ret;
     463              :         ret.first = lhs.first / rhs;
     464              :         ret.second = lhs.second;
     465              :         return ret;
     466              :     }
     467              :     Unit operator + (const Unit &rhs)
     468              :     {
     469              :         Util::Assert(INFO, TEST(this->second == rhs.second), "Error adding values with unequal units");
     470              :         Unit ret;
     471              :         ret.first = this->first + rhs.first;
     472              :         for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i];
     473              :         return ret;
     474              :     }
     475              :     Unit operator - (const Unit &rhs)
     476              :     {
     477              :         Util::Assert(INFO, TEST(this->second == rhs.second), "Error subtracting values with unequal units");
     478              :         Unit ret;
     479              :         ret.first = this->first - rhs.first;
     480              :         for (unsigned int i = 0; i < this->second.size(); i++) ret.second[i] = (this->second)[i];
     481              :         return ret;
     482              :     }
     483         2345 :     Unit operator ^ (const int &rhs)
     484              :     {
     485         2345 :         Unit ret;
     486         2345 :         ret.first = std::pow(this->first,rhs);
     487        37520 :         for (unsigned int i = 0; i < this->second.size(); i++) 
     488              :         {
     489        16415 :             ret.second[i] = (this->second)[i]*rhs;
     490              :         }
     491         2345 :         return ret;
     492              :     }
     493              : 
     494            0 :     friend std::ostream& operator<<(std::ostream &out, const Unit &a) 
     495              :     {
     496            0 :         out << a.normalized_value() << "_";
     497            0 :         out << a.normalized_unitstring();
     498            0 :         return out;
     499              :     }
     500              : 
     501         1135 :     double normalized_value() const
     502              :     {
     503         1135 :         double fac = 1.0;
     504        18160 :         for (unsigned int i = 0 ; i < this->second.size(); i++)
     505         7945 :             fac *= std::pow(normalization[i].second, this->second[i]);
     506              :         
     507         1135 :         return this->first * fac;
     508              :     }
     509              : 
     510            0 :     std::string normalized_unitstring() const
     511              :     {
     512              :         // little utility to join strings with delimiter
     513            0 :         auto join = [] (std::vector<std::string>vec,std::string sep){
     514            0 :             std::ostringstream oss;
     515            0 :             for (size_t i = 0; i < vec.size(); ++i) {
     516            0 :                 oss << vec[i];
     517            0 :                 if (i < vec.size() - 1) {
     518            0 :                     oss << sep;
     519              :                 }
     520              :             }
     521            0 :             return oss.str();
     522            0 :         };
     523              : 
     524            0 :         std::vector<std::string> num, den;
     525            0 :         for (unsigned int i = 0 ; i < this->second.size(); i++)
     526              :         {
     527            0 :             if (this->second[i] > 1)        
     528            0 :                 num.push_back(normalization[i].first + "^" + std::to_string(this->second[i]));
     529            0 :             else if (this->second[i] == 1)  
     530            0 :                 num.push_back(normalization[i].first);
     531            0 :             else if (this->second[i] == -1) 
     532            0 :                 den.push_back(normalization[i].first);
     533            0 :             else if (this->second[i] < -1)  
     534            0 :                 den.push_back(normalization[i].first + "^" + std::to_string(std::abs(this->second[i])));
     535              :         }
     536              :         
     537            0 :         std::string ret;
     538            0 :         if (num.size()) ret = join(num,"*");
     539            0 :         else ret = "1";
     540            0 :         if (den.size()) ret += "/" + join(den,"/");
     541              :         
     542            0 :         return ret;
     543            0 :     }
     544              : };
     545              : 
     546              : 
     547              : inline std::map<std::string, int> 
     548         1420 : Unit::ParseUnitString(const std::string& input) {
     549         1420 :     std::map<std::string, int> result;
     550              :     enum class Op { Mul = 1, Div = -1 };
     551         1420 :     Op currentOp = Op::Mul;
     552         1420 :     std::string token;
     553         1420 :     size_t i = 0;
     554         2345 :     auto parse_unit = [&](const std::string& t, Op op) {
     555         2345 :         if (t.empty()) return;
     556              : 
     557         2345 :         size_t caret = t.find('^');
     558         2345 :         std::string base = t;
     559         2345 :         int exp = 1;
     560              : 
     561         2345 :         if (caret != std::string::npos) {
     562          333 :             base = t.substr(0, caret);
     563          333 :             std::string exp_str = t.substr(caret + 1);
     564              :             try {
     565          333 :                 exp = std::stoi(exp_str);
     566            0 :             } catch (...) {
     567            0 :                 throw std::runtime_error("Invalid exponent: " + exp_str);
     568            0 :             }
     569          333 :         }
     570         2345 :         int signed_exp = (op == Op::Mul ? +1 : -1) * exp;
     571         2345 :         result[base] += signed_exp;
     572         2345 :     };
     573              : 
     574         6203 :     while (i < input.size()) {
     575         4783 :         char c = input[i];
     576              : 
     577         4783 :         if (c == '*') 
     578              :         {
     579          357 :             parse_unit(token, currentOp);
     580          357 :             token.clear();
     581          357 :             currentOp = Op::Mul;
     582          357 :             ++i;
     583              :         } 
     584         4426 :         else if (c == '/') 
     585              :         {
     586          568 :             parse_unit(token, currentOp);
     587          568 :             token.clear();
     588          568 :             currentOp = Op::Div;
     589          568 :             ++i;
     590              :         } 
     591              :         else 
     592              :         {
     593         3858 :             token += c;
     594         3858 :             ++i;
     595              :         }
     596              :     }
     597              : 
     598              :     // Parse final token
     599         1420 :     parse_unit(token, currentOp);
     600              : 
     601         2840 :     return result;
     602         1420 : }
     603              : 
     604              : 
     605              : 
     606              : 
     607              : 
     608              : 
     609              : 
     610              : 
     611              : 
     612              : 
     613              : #endif
        

Generated by: LCOV version 2.0-1