├── .github ├── dependabot.yml └── workflows │ └── ruby.yml ├── .gitignore ├── .ruby-version ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── data ├── base_unit.yaml ├── derived_unit.yaml └── prefix.yaml ├── lib ├── unitwise.rb └── unitwise │ ├── atom.rb │ ├── base.rb │ ├── compatible.rb │ ├── errors.rb │ ├── expression.rb │ ├── expression │ ├── composer.rb │ ├── decomposer.rb │ ├── matcher.rb │ ├── parser.rb │ └── transformer.rb │ ├── functional.rb │ ├── measurement.rb │ ├── number.rb │ ├── prefix.rb │ ├── scale.rb │ ├── search.rb │ ├── standard.rb │ ├── standard │ ├── base.rb │ ├── base_unit.rb │ ├── derived_unit.rb │ ├── extras.rb │ ├── function.rb │ ├── prefix.rb │ └── scale.rb │ ├── term.rb │ ├── unit.rb │ └── version.rb ├── test ├── support │ └── scale_tests.rb ├── test_helper.rb ├── unitwise │ ├── atom_test.rb │ ├── base_test.rb │ ├── expression │ │ ├── decomposer_test.rb │ │ ├── matcher_test.rb │ │ └── parser_test.rb │ ├── functional_test.rb │ ├── measurement_test.rb │ ├── number_test.rb │ ├── prefix_test.rb │ ├── scale_test.rb │ ├── search_test.rb │ ├── term_test.rb │ └── unit_test.rb └── unitwise_test.rb └── unitwise.gemspec /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'bundler' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake 6 | # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby 7 | 8 | name: Ruby 9 | 10 | on: [push, pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | test: 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | ruby-version: 21 | - '2.6' 22 | - '2.7' 23 | - '3.0' 24 | - '3.1' 25 | - '3.2' 26 | - '3.3' 27 | - 'jruby' 28 | - 'truffleruby' 29 | 30 | name: ${{ format('Tests (Ruby {0})', matrix.ruby-version) }} 31 | runs-on: ubuntu-latest 32 | env: 33 | BUNDLE_WITHOUT: 'yard pry' 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - name: Set up Ruby 38 | uses: ruby/setup-ruby@v1 39 | with: 40 | ruby-version: ${{ matrix.ruby-version }} 41 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 42 | 43 | - name: Run tests 44 | run: bundle exec rake 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.0 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unitwise Changelog 2 | 3 | All notable changes to Unitwise will be documented in this file, starting at 4 | version 1.0.0. 5 | 6 | Unitwise uses semantic versioning. 7 | 8 | ## Unreleased 9 | 10 | ## 2.3.0 - 2022-10-19 11 | 12 | ### Added 13 | 14 | - Support for newer Rubies (2.6+) & drops support for older ones (2.6 is EOL but will continue to support for now) 15 | - Support for Psych 4. Note: unit data YAML files in the gem are loaded using `unsafe_load` 16 | 17 | ## 2.2.0 - 2017-07-14 18 | 19 | ### Fixed 20 | 21 | - Default units now stored as BigDecimal or Integer to reduce floating point 22 | accuracy loss. 23 | - Added missing conversion for Degree Réaumur. 24 | 25 | ### Changed 26 | 27 | - Performing mathematical operations or converting units will now use Rational 28 | math to prevent accuracy loss. In most cases, this means converted/operated 29 | on `Measurement`s will have a `Rational` `#value`. If you have an explicit 30 | dependency on the exact `Numeric` type that `Measurement#value` returns, 31 | consider using `#to_f` or `#to_i` instead. 32 | 33 | ### Removed 34 | 35 | - Support for MRI 2.1 36 | 37 | ## 2.1.0 - 2017-04-28 38 | 39 | ### Removed 40 | 41 | - Support for Ruby MRI 1.9.3, MRI 2.0, and Rubinius 42 | 43 | ### Added 44 | 45 | - `Unitwise.register` is a new method that allows user defined units 46 | - Support for MRI 2.3 and 2.4 47 | 48 | ### Changed 49 | 50 | - Unit data refreshed from latest UCUM spec. Most notably, some metric atoms 51 | names have seen case changes. 52 | 53 | ## 2.0.0 - 2015-09-13 54 | 55 | ### Fixed 56 | 57 | - Gem dependencies are less restrictive; now works with additional versions of 58 | parslet and blankslate. 59 | 60 | ### Removed 61 | 62 | - 'unitwise/ext' is now officially removed. The core Numeric extensions are no 63 | longer available. Instead of `1.volt` or `2.0.to_joule`, use `Unitwise(1, 64 | 'volt')` and `Unitwise(2.0, 'Joule')`. 65 | - Dropped support for Ruby 1.8.7, 1.9.2, and REE. 66 | 67 | ## 1.1.0 - 2015-09-10 68 | 69 | ### Fixed 70 | 71 | - `#to_s` should no longer return the unexpected ' 1' suffix for dimless measurements. 72 | - `#to_s(mode)` will fall back to using the atom's `primary_code` if the mode 73 | isn't available. 74 | 75 | ### Deprecated 76 | 77 | - `require unitwise/ext` has been deprecated as it is a performance drag and 78 | violates Ruby best practices. Use `require unitwise` instead. Any use of the 79 | Numeric helpers like `1.meter`, `2.to_foot` will need to change to 80 | `Unitwise(1, 'meter')`, and `Unitwise(2, 'foot')`. 81 | 82 | ## 1.0.4 - 2015-01-10 83 | 84 | - Added Ruby 2.2 support. 85 | - Empty strings are no longer valid units. 86 | 87 | ## 1.0.3 - 2014-10-05 88 | 89 | ### Added 90 | - Unitwise.valid? for checking validity of expressions 91 | 92 | ## 1.0.2 - 2014-09-14 93 | 94 | ### Fixed 95 | - Decomposer caching is now a little smarter. This resulted in a mild 96 | performance increase. 97 | 98 | ## 1.0.1 - 2014-08-30 99 | 100 | ### Fixed 101 | - Move conditional dependencies to Gemfile in order to allow proper 102 | installation issues on rbx and jruby. 103 | 104 | ## 1.0.0 - 2014-08-25 105 | 106 | ### Added 107 | - Uniwise() now accepts a Unitwise::Measurement as the first argument. 108 | - Unitwise::Measurement now supports #round. 109 | 110 | ### Fixed 111 | - Respect Rationals when inspecting/printing a Unitwise::Measurement. 112 | - Dynamically created methods from unitwise/ext now work with #respond_to? 113 | and #methods appropriately. 114 | 115 | ### Deprecated 116 | - Unitwise() and Unitwise::Measurement.new() now requires two non-optional 117 | arguments (value and unit). 118 | - Unitwise::Measurement no longer has an implicit Integer conversion. 119 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'covered' 4 | gem 'bigdecimal', '>= 2', :platform => :mri 5 | 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Josh Lewis 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Unitwise](//github.com/joshwlewis/unitwise) 2 | 3 | [![Gem Version](http://img.shields.io/gem/v/unitwise.svg?style=flat)](https://rubygems.org/gems/unitwise) 4 | [![Build Status](https://github.com/joshwlewis/unitwise/actions/workflows/ruby.yml/badge.svg)](https://github.com/joshwlewis/unitwise/actions/workflows/ruby.yml) 5 | [![Coverage Status](http://img.shields.io/coveralls/joshwlewis/unitwise.svg?style=flat)](https://coveralls.io/r/joshwlewis/unitwise) 6 | [![Code Climate](http://img.shields.io/codeclimate/github/joshwlewis/unitwise.svg?style=flat)](https://codeclimate.com/github/joshwlewis/unitwise) 7 | 8 | Unitwise is a Ruby library for unit measurement conversion and math. 9 | 10 | For an over the top example, consider a car (2800 lb) completing the quarter 11 | mile in 10 seconds (with uniform acceleration). 12 | 13 | ```ruby 14 | require 'unitwise' 15 | 16 | distance = Unitwise(0.25, 'mile') # => # 17 | time = Unitwise(10, 'second') # => # 18 | mass = Unitwise(2800, 'pound') # => # 19 | 20 | acceleration = 2.0 * distance / time ** 2 21 | # => # 22 | 23 | force = (mass * acceleration).to_lbf 24 | # => # 25 | 26 | power = (force * distance / time).to_horsepower 27 | # => # 28 | 29 | speed = ((2.0 * acceleration * distance) ** 0.5).convert_to("mile/hour") 30 | # => # 31 | ``` 32 | 33 | [RubyTapas](http://rubytapas.com) subscribers can also view a screencast: 34 | [225-Unitwise](https://rubytapas.dpdcart.com/subscriber/post?id=563) 35 | 36 | ## Rationale 37 | 38 | Unitwise is based on the [Unified Code for Units of Measure(UCUM)](http://unitsofmeasure.org/), 39 | which aims to maintain a cross-platform list of units and their conversions. 40 | This gives Unitwise a few key advantages: 41 | 42 | - An enormous list of units. At the time of writing, there are 96 metric units, 43 | 211 non-metric units, and 24 unit prefixes. Whatever unit/units you need, they 44 | are here. 45 | 46 | - An accurate and up to date set of units. The units, prefixes, and conversions 47 | are maintained by UCUM, and are imported into this library with a rake task. 48 | 49 | One of the objectives of Unitwise was that it should comprehend any combination 50 | of units. For instance it needed to understand that a unit of 51 | 'kilogram.(meter/second)2' was equivalent to 'kilogram.meter.(meter/second2)'. 52 | This resulted in two unique features: 53 | 54 | - An expression grammar built with a PEG parser. This makes expression 55 | parsing more efficient and allows nested parentheses. For example, this is possible: '(kilogram.(meter/second)2)2' 56 | 57 | - Smart compatibility detection. Each unit is reduced down to its most elementary 58 | atoms to determine compatibility with another unit. For example, it knows that 59 | 'meter/second2' should be considered compatible with 'kilogram.foot.minute-2/pound'. 60 | 61 | ## Usage 62 | 63 | ### Initialization: 64 | 65 | Measurements can be instantiated with `Unitwise()`. 66 | 67 | ```ruby 68 | require 'unitwise' 69 | 70 | Unitwise(2.3, 'kilogram') # => # 71 | Unitwise(100, 'pound') # => # 72 | ``` 73 | 74 | ### Conversion 75 | 76 | Unitwise is able to convert any unit within the UCUM spec to any other 77 | compatible unit. 78 | 79 | ```ruby 80 | Unitwise(5.0, 'kilometer').convert_to('mile') 81 | # => # 82 | ``` 83 | 84 | The prettier version of `convert_to(unit)` is appending the unit code, name, etc. 85 | to a `to_` message name. 86 | 87 | ```ruby 88 | Unitwise(26.2, 'mile').to_kilometer 89 | # => # 90 | ``` 91 | 92 | ### Comparison 93 | 94 | It also has the ability to compare measurements with the same or different units. 95 | 96 | ```ruby 97 | Unitwise(12, 'inch') == Unitwise(1, 'foot') # => true 98 | Unitwise(1, 'meter') > Unitwise(1, 'yard') # => true 99 | ``` 100 | 101 | Again, you have to compare compatible units. For example, comparing two 102 | temperatures will work, comparing a mass to a length would fail. 103 | 104 | ### SI abbreviations 105 | 106 | You can use shorthand for SI units. 107 | 108 | ```ruby 109 | Unitwise(1000, 'm') == Unitwise(1, 'km') # => true 110 | Unitwise(1, 'ml') == Unitwise(0.001, 'l') # => true 111 | ``` 112 | 113 | ### Complex Units 114 | 115 | Units can be combined to make more complex ones. There is nothing special about 116 | them -- they can still be converted, compared, or operated on. 117 | 118 | ```ruby 119 | speed = Unitwise(60, 'mile/hour') 120 | # => # 121 | 122 | speed.convert_to('m/s') 123 | # => # 124 | ``` 125 | 126 | Exponents and parenthesis are supported as well. 127 | 128 | ```ruby 129 | Unitwise(1000, 'kg.s-1.(m/s)2').to_kilowatt 130 | # => # 131 | ``` 132 | 133 | ### Math 134 | 135 | You can add or subtract compatible measurements. 136 | 137 | ```ruby 138 | Unitwise(2.0, 'meter') + Unitwise(3.0, 'inch') - Unitwise(1.0, 'yard') 139 | # => # 140 | ``` 141 | 142 | You can multiply or divide measurements and numbers. 143 | 144 | ```ruby 145 | Unitwise(110, 'volt') * 2 146 | # => # 147 | ``` 148 | 149 | You can multiply or divide measurements with measurements. 150 | 151 | ```ruby 152 | Unitwise(20, 'milligram') / Unitwise(1, 'liter') 153 | # => # 154 | ``` 155 | 156 | Exponentiation is also supported. 157 | 158 | ```ruby 159 | (Unitwise(10, 'cm') ** 3).to_liter 160 | # => # 161 | ``` 162 | 163 | ### Unit Names and Atom Codes 164 | 165 | This library is based around the units in the UCUM specification, which is 166 | extensive and well thought out. However, not all of our unit systems throughout 167 | the world and history are consistent or logical. UCUM has devised a system where 168 | each unit has a unique atom code to try and solve this. The previous code examples 169 | don't show this, because for the most part you won't need it. Unitwise can 170 | figure out most of the units by their name or symbol. If you find you need to 171 | (or just want to be explicit) you use the UCUM atom codes without any 172 | modification. 173 | 174 | Just for example, you can see here that there are actually a few versions of inch 175 | and foot: 176 | 177 | ```ruby 178 | Unitwise(1, '[ft_i]') == Unitwise(1, '[ft_us]') # => false 179 | 180 | Unitwise(3, '[in_br]') == Unitwise(3, '[in_i]') # => false 181 | ``` 182 | 183 | ### Available Units 184 | 185 | If you are looking for a particular unit, you can search with a string or 186 | Regexp. 187 | 188 | ```ruby 189 | Unitwise.search('fathom') 190 | # => [ ... ] 191 | ``` 192 | 193 | You can also get the official list from the UCUM website in XML format at 194 | [unitsofmeasure.org/ucum-essence.xml](http://unitsofmeasure.org/ucum-essence.xml) 195 | or a YAML version within this repo 196 | [github.com/joshwlewis/unitwise/tree/master/data](//github.com/joshwlewis/unitwise/tree/master/data). 197 | 198 | ### UCUM designations 199 | UCUM defines several designations for it's units: `names`, 200 | `primary_code`, `secondary_code`, and `symbol`. You can see them all when 201 | inspecting an atom: 202 | 203 | ```ruby 204 | Unitwise::Atom.all.sample 205 | # => #, classification="heat", property="energy", metric=false, special=false, arbitrary=false, dim="L2.M.T-2"> 206 | ``` 207 | 208 | When initializing a measurement, you can use any of the designations: 209 | 210 | ```ruby 211 | Unitwise(1, '[Btu]') == Unitwise(1, 'British thermal unit') 212 | # => true 213 | Unitwise(1, 'btu') == Unitwise(1, "[BTU]") 214 | # => true 215 | ``` 216 | 217 | When inspecting or printing (`to_s`) that measurement, it will remember the 218 | desigation you used. However, if you want to print it with another designation, 219 | that's also possible: 220 | 221 | ```ruby 222 | temperature = Unitwise(10, "Cel") 223 | temperature.to_s # => "10 Cel" 224 | temperature.to_s(:names) # => "10 degree Celsius" 225 | temperature.to_s(:symbol) # => "10 °C" 226 | ``` 227 | 228 | There is on caveat here. You must use the same designation for each atom in a 229 | complex unit. Meaning you can't mix designations within a unit. 230 | 231 | ```ruby 232 | Unitwise(1, "m/s") # Works, both atoms use their primary_code 233 | Unitwise(1, "meter/second") # Works, both atoms use a name 234 | Unitwise(1, "meter/s") # Does not work, mixed designations (name and primary_code) 235 | Unitwise(1, "meter") / Unitwise(1, "s") # Also works 236 | ``` 237 | 238 | 239 | ### Adding custom units 240 | 241 | While UCUM's list of units is rather exhaustive, there may still be occasions 242 | where you need custom or uncommon measurements. You can add them yourself 243 | with `Unitwise.register`, which will allow you to convert to or from the new 244 | unit. 245 | 246 | For example, if your app needed to pour "3 fingers" of bourbon, you could 247 | register an atom for that: 248 | 249 | ```ruby 250 | Unitwise.register( 251 | names: ["finger", "fingers"], 252 | symbol: "🥃", 253 | primary_code: "fng", 254 | secondary_code: "fng", 255 | scale: { 256 | value: 1.0, 257 | unit_code: '[foz_us]' 258 | }, 259 | property: 'fluid volume' 260 | ) 261 | 262 | Unitwise(1, "gallon").to_fingers 263 | # => # 264 | 265 | Unitwise(1, "🥃").to_cup 266 | # => # 267 | ``` 268 | 269 | ## Supported Ruby Versions 270 | 271 | This library aims to support and is tested against the following Ruby 272 | implementations: 273 | 274 | * Ruby 2.6 - 3.1 275 | * [JRuby](http://jruby.org/) 276 | * [TruffleRuby](https://github.com/oracle/truffleruby) 277 | 278 | If something doesn't work on one of these versions, it's a bug. 279 | 280 | This library may inadvertently work (or seem to work) on other Ruby versions or 281 | implementations, however support will only be provided for the implementations 282 | listed above. 283 | 284 | ## Installation 285 | 286 | Add this line to your application's Gemfile: 287 | 288 | gem 'unitwise' 289 | 290 | And then execute: 291 | 292 | $ bundle 293 | 294 | Or install it yourself as: 295 | 296 | $ gem install unitwise 297 | 298 | 299 | ## Contributing 300 | 301 | 1. Fork it 302 | 2. Create your feature branch (`git checkout -b my-new-feature`) 303 | 3. Commit your changes (`git commit -am 'Add some feature'`) 304 | 4. Push to the branch (`git push origin my-new-feature`) 305 | 5. Create new Pull Request 306 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'rake' 4 | require 'rake/testtask' 5 | Rake::TestTask.new do |t| 6 | t.libs << "test" 7 | t.pattern = 'test/**/*_test.rb' 8 | end 9 | 10 | task :default => :test 11 | 12 | namespace :unitwise do 13 | desc "Update Ucum Data" 14 | task :update_standard do 15 | require 'unitwise' 16 | require 'unitwise/standard' 17 | Unitwise::Standard::BaseUnit.write 18 | Unitwise::Standard::DerivedUnit.write 19 | Unitwise::Standard::Prefix.write 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /data/base_unit.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - :names: meter 3 | :symbol: m 4 | :primary_code: m 5 | :secondary_code: M 6 | :property: length 7 | :dim: L 8 | - :names: second 9 | :symbol: s 10 | :primary_code: s 11 | :secondary_code: S 12 | :property: time 13 | :dim: T 14 | - :names: gram 15 | :symbol: g 16 | :primary_code: g 17 | :secondary_code: G 18 | :property: mass 19 | :dim: M 20 | - :names: radian 21 | :symbol: rad 22 | :primary_code: rad 23 | :secondary_code: RAD 24 | :property: plane angle 25 | :dim: A 26 | - :names: kelvin 27 | :symbol: K 28 | :primary_code: K 29 | :secondary_code: K 30 | :property: temperature 31 | :dim: C 32 | - :names: coulomb 33 | :symbol: C 34 | :primary_code: C 35 | :secondary_code: C 36 | :property: electric charge 37 | :dim: Q 38 | - :names: candela 39 | :symbol: cd 40 | :primary_code: cd 41 | :secondary_code: CD 42 | :property: luminous intensity 43 | :dim: F 44 | -------------------------------------------------------------------------------- /data/derived_unit.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - :names: the number ten for arbitrary powers 3 | :symbol: '10' 4 | :primary_code: 10* 5 | :secondary_code: 10* 6 | :scale: 7 | :value: 10 8 | :unit_code: '1' 9 | :classification: dimless 10 | :property: number 11 | :metric: false 12 | :special: false 13 | :arbitrary: false 14 | - :names: the number ten for arbitrary powers 15 | :symbol: '10' 16 | :primary_code: 10^ 17 | :secondary_code: 10^ 18 | :scale: 19 | :value: 10 20 | :unit_code: '1' 21 | :classification: dimless 22 | :property: number 23 | :metric: false 24 | :special: false 25 | :arbitrary: false 26 | - :names: the number pi 27 | :symbol: π 28 | :primary_code: "[pi]" 29 | :secondary_code: "[PI]" 30 | :scale: 31 | :value: !ruby/object:BigDecimal 81:0.31415926535897932384626433832795028841971693993751058209749445923e1 32 | :unit_code: '1' 33 | :classification: dimless 34 | :property: number 35 | :metric: false 36 | :special: false 37 | :arbitrary: false 38 | - :names: percent 39 | :symbol: "%" 40 | :primary_code: "%" 41 | :secondary_code: "%" 42 | :scale: 43 | :value: 1 44 | :unit_code: 10*-2 45 | :classification: dimless 46 | :property: fraction 47 | :metric: false 48 | :special: false 49 | :arbitrary: false 50 | - :names: parts per thousand 51 | :symbol: ppth 52 | :primary_code: "[ppth]" 53 | :secondary_code: "[PPTH]" 54 | :scale: 55 | :value: 1 56 | :unit_code: 10*-3 57 | :classification: dimless 58 | :property: fraction 59 | :metric: false 60 | :special: false 61 | :arbitrary: false 62 | - :names: parts per million 63 | :symbol: ppm 64 | :primary_code: "[ppm]" 65 | :secondary_code: "[PPM]" 66 | :scale: 67 | :value: 1 68 | :unit_code: 10*-6 69 | :classification: dimless 70 | :property: fraction 71 | :metric: false 72 | :special: false 73 | :arbitrary: false 74 | - :names: parts per billion 75 | :symbol: ppb 76 | :primary_code: "[ppb]" 77 | :secondary_code: "[PPB]" 78 | :scale: 79 | :value: 1 80 | :unit_code: 10*-9 81 | :classification: dimless 82 | :property: fraction 83 | :metric: false 84 | :special: false 85 | :arbitrary: false 86 | - :names: parts per trillion 87 | :symbol: pptr 88 | :primary_code: "[pptr]" 89 | :secondary_code: "[PPTR]" 90 | :scale: 91 | :value: 1 92 | :unit_code: 10*-12 93 | :classification: dimless 94 | :property: fraction 95 | :metric: false 96 | :special: false 97 | :arbitrary: false 98 | - :names: mole 99 | :symbol: mol 100 | :primary_code: mol 101 | :secondary_code: MOL 102 | :scale: 103 | :value: !ruby/object:BigDecimal 18:0.60221367e1 104 | :unit_code: 10*23 105 | :classification: si 106 | :property: amount of substance 107 | :metric: true 108 | :special: false 109 | :arbitrary: false 110 | - :names: steradian 111 | :symbol: sr 112 | :primary_code: sr 113 | :secondary_code: SR 114 | :scale: 115 | :value: 1 116 | :unit_code: rad2 117 | :classification: si 118 | :property: solid angle 119 | :metric: true 120 | :special: false 121 | :arbitrary: false 122 | - :names: hertz 123 | :symbol: Hz 124 | :primary_code: Hz 125 | :secondary_code: HZ 126 | :scale: 127 | :value: 1 128 | :unit_code: s-1 129 | :classification: si 130 | :property: frequency 131 | :metric: true 132 | :special: false 133 | :arbitrary: false 134 | - :names: newton 135 | :symbol: N 136 | :primary_code: N 137 | :secondary_code: N 138 | :scale: 139 | :value: 1 140 | :unit_code: kg.m/s2 141 | :classification: si 142 | :property: force 143 | :metric: true 144 | :special: false 145 | :arbitrary: false 146 | - :names: pascal 147 | :symbol: Pa 148 | :primary_code: Pa 149 | :secondary_code: PAL 150 | :scale: 151 | :value: 1 152 | :unit_code: N/m2 153 | :classification: si 154 | :property: pressure 155 | :metric: true 156 | :special: false 157 | :arbitrary: false 158 | - :names: joule 159 | :symbol: J 160 | :primary_code: J 161 | :secondary_code: J 162 | :scale: 163 | :value: 1 164 | :unit_code: N.m 165 | :classification: si 166 | :property: energy 167 | :metric: true 168 | :special: false 169 | :arbitrary: false 170 | - :names: watt 171 | :symbol: W 172 | :primary_code: W 173 | :secondary_code: W 174 | :scale: 175 | :value: 1 176 | :unit_code: J/s 177 | :classification: si 178 | :property: power 179 | :metric: true 180 | :special: false 181 | :arbitrary: false 182 | - :names: ampère 183 | :symbol: A 184 | :primary_code: A 185 | :secondary_code: A 186 | :scale: 187 | :value: 1 188 | :unit_code: C/s 189 | :classification: si 190 | :property: electric current 191 | :metric: true 192 | :special: false 193 | :arbitrary: false 194 | - :names: volt 195 | :symbol: V 196 | :primary_code: V 197 | :secondary_code: V 198 | :scale: 199 | :value: 1 200 | :unit_code: J/C 201 | :classification: si 202 | :property: electric potential 203 | :metric: true 204 | :special: false 205 | :arbitrary: false 206 | - :names: farad 207 | :symbol: F 208 | :primary_code: F 209 | :secondary_code: F 210 | :scale: 211 | :value: 1 212 | :unit_code: C/V 213 | :classification: si 214 | :property: electric capacitance 215 | :metric: true 216 | :special: false 217 | :arbitrary: false 218 | - :names: ohm 219 | :symbol: Ω 220 | :primary_code: Ohm 221 | :secondary_code: OHM 222 | :scale: 223 | :value: 1 224 | :unit_code: V/A 225 | :classification: si 226 | :property: electric resistance 227 | :metric: true 228 | :special: false 229 | :arbitrary: false 230 | - :names: siemens 231 | :symbol: S 232 | :primary_code: S 233 | :secondary_code: SIE 234 | :scale: 235 | :value: 1 236 | :unit_code: Ohm-1 237 | :classification: si 238 | :property: electric conductance 239 | :metric: true 240 | :special: false 241 | :arbitrary: false 242 | - :names: weber 243 | :symbol: Wb 244 | :primary_code: Wb 245 | :secondary_code: WB 246 | :scale: 247 | :value: 1 248 | :unit_code: V.s 249 | :classification: si 250 | :property: magentic flux 251 | :metric: true 252 | :special: false 253 | :arbitrary: false 254 | - :names: degree Celsius 255 | :symbol: "°C" 256 | :primary_code: Cel 257 | :secondary_code: CEL 258 | :scale: 259 | :function_code: cel 260 | :value: 1 261 | :unit_code: K 262 | :classification: si 263 | :property: temperature 264 | :metric: true 265 | :special: true 266 | :arbitrary: false 267 | - :names: tesla 268 | :symbol: T 269 | :primary_code: T 270 | :secondary_code: T 271 | :scale: 272 | :value: 1 273 | :unit_code: Wb/m2 274 | :classification: si 275 | :property: magnetic flux density 276 | :metric: true 277 | :special: false 278 | :arbitrary: false 279 | - :names: henry 280 | :symbol: H 281 | :primary_code: H 282 | :secondary_code: H 283 | :scale: 284 | :value: 1 285 | :unit_code: Wb/A 286 | :classification: si 287 | :property: inductance 288 | :metric: true 289 | :special: false 290 | :arbitrary: false 291 | - :names: lumen 292 | :symbol: lm 293 | :primary_code: lm 294 | :secondary_code: LM 295 | :scale: 296 | :value: 1 297 | :unit_code: cd.sr 298 | :classification: si 299 | :property: luminous flux 300 | :metric: true 301 | :special: false 302 | :arbitrary: false 303 | - :names: lux 304 | :symbol: lx 305 | :primary_code: lx 306 | :secondary_code: LX 307 | :scale: 308 | :value: 1 309 | :unit_code: lm/m2 310 | :classification: si 311 | :property: illuminance 312 | :metric: true 313 | :special: false 314 | :arbitrary: false 315 | - :names: becquerel 316 | :symbol: Bq 317 | :primary_code: Bq 318 | :secondary_code: BQ 319 | :scale: 320 | :value: 1 321 | :unit_code: s-1 322 | :classification: si 323 | :property: radioactivity 324 | :metric: true 325 | :special: false 326 | :arbitrary: false 327 | - :names: gray 328 | :symbol: Gy 329 | :primary_code: Gy 330 | :secondary_code: GY 331 | :scale: 332 | :value: 1 333 | :unit_code: J/kg 334 | :classification: si 335 | :property: energy dose 336 | :metric: true 337 | :special: false 338 | :arbitrary: false 339 | - :names: sievert 340 | :symbol: Sv 341 | :primary_code: Sv 342 | :secondary_code: SV 343 | :scale: 344 | :value: 1 345 | :unit_code: J/kg 346 | :classification: si 347 | :property: dose equivalent 348 | :metric: true 349 | :special: false 350 | :arbitrary: false 351 | - :names: 352 | - gon 353 | - grade 354 | :symbol: "□g" 355 | :primary_code: gon 356 | :secondary_code: GON 357 | :scale: 358 | :value: !ruby/object:BigDecimal 18:0.9e0 359 | :unit_code: deg 360 | :classification: iso1000 361 | :property: plane angle 362 | :metric: false 363 | :special: false 364 | :arbitrary: false 365 | - :names: degree 366 | :symbol: "°" 367 | :primary_code: deg 368 | :secondary_code: DEG 369 | :scale: 370 | :value: 2 371 | :unit_code: "[pi].rad/360" 372 | :classification: iso1000 373 | :property: plane angle 374 | :metric: false 375 | :special: false 376 | :arbitrary: false 377 | - :names: minute 378 | :symbol: "'" 379 | :primary_code: "'" 380 | :secondary_code: "'" 381 | :scale: 382 | :value: 1 383 | :unit_code: deg/60 384 | :classification: iso1000 385 | :property: plane angle 386 | :metric: false 387 | :special: false 388 | :arbitrary: false 389 | - :names: second 390 | :symbol: "''" 391 | :primary_code: "''" 392 | :secondary_code: "''" 393 | :scale: 394 | :value: 1 395 | :unit_code: "'/60" 396 | :classification: iso1000 397 | :property: plane angle 398 | :metric: false 399 | :special: false 400 | :arbitrary: false 401 | - :names: liter 402 | :symbol: l 403 | :primary_code: l 404 | :secondary_code: L 405 | :scale: 406 | :value: 1 407 | :unit_code: dm3 408 | :classification: iso1000 409 | :property: volume 410 | :metric: true 411 | :special: false 412 | :arbitrary: false 413 | - :names: liter 414 | :symbol: L 415 | :primary_code: L 416 | :scale: 417 | :value: 1 418 | :unit_code: l 419 | :classification: iso1000 420 | :property: volume 421 | :metric: true 422 | :special: false 423 | :arbitrary: false 424 | - :names: are 425 | :symbol: a 426 | :primary_code: ar 427 | :secondary_code: AR 428 | :scale: 429 | :value: 100 430 | :unit_code: m2 431 | :classification: iso1000 432 | :property: area 433 | :metric: true 434 | :special: false 435 | :arbitrary: false 436 | - :names: minute 437 | :symbol: min 438 | :primary_code: min 439 | :secondary_code: MIN 440 | :scale: 441 | :value: 60 442 | :unit_code: s 443 | :classification: iso1000 444 | :property: time 445 | :metric: false 446 | :special: false 447 | :arbitrary: false 448 | - :names: hour 449 | :symbol: h 450 | :primary_code: h 451 | :secondary_code: HR 452 | :scale: 453 | :value: 60 454 | :unit_code: min 455 | :classification: iso1000 456 | :property: time 457 | :metric: false 458 | :special: false 459 | :arbitrary: false 460 | - :names: day 461 | :symbol: d 462 | :primary_code: d 463 | :secondary_code: D 464 | :scale: 465 | :value: 24 466 | :unit_code: h 467 | :classification: iso1000 468 | :property: time 469 | :metric: false 470 | :special: false 471 | :arbitrary: false 472 | - :names: tropical year 473 | :symbol: at 474 | :primary_code: a_t 475 | :secondary_code: ANN_T 476 | :scale: 477 | :value: !ruby/object:BigDecimal 18:0.36524219e3 478 | :unit_code: d 479 | :classification: iso1000 480 | :property: time 481 | :metric: false 482 | :special: false 483 | :arbitrary: false 484 | - :names: mean Julian year 485 | :symbol: aj 486 | :primary_code: a_j 487 | :secondary_code: ANN_J 488 | :scale: 489 | :value: !ruby/object:BigDecimal 18:0.36525e3 490 | :unit_code: d 491 | :classification: iso1000 492 | :property: time 493 | :metric: false 494 | :special: false 495 | :arbitrary: false 496 | - :names: mean Gregorian year 497 | :symbol: ag 498 | :primary_code: a_g 499 | :secondary_code: ANN_G 500 | :scale: 501 | :value: !ruby/object:BigDecimal 18:0.3652425e3 502 | :unit_code: d 503 | :classification: iso1000 504 | :property: time 505 | :metric: false 506 | :special: false 507 | :arbitrary: false 508 | - :names: year 509 | :symbol: a 510 | :primary_code: a 511 | :secondary_code: ANN 512 | :scale: 513 | :value: 1 514 | :unit_code: a_j 515 | :classification: iso1000 516 | :property: time 517 | :metric: false 518 | :special: false 519 | :arbitrary: false 520 | - :names: week 521 | :symbol: wk 522 | :primary_code: wk 523 | :secondary_code: WK 524 | :scale: 525 | :value: 7 526 | :unit_code: d 527 | :classification: iso1000 528 | :property: time 529 | :metric: false 530 | :special: false 531 | :arbitrary: false 532 | - :names: synodal month 533 | :symbol: mos 534 | :primary_code: mo_s 535 | :secondary_code: MO_S 536 | :scale: 537 | :value: !ruby/object:BigDecimal 18:0.2953059e2 538 | :unit_code: d 539 | :classification: iso1000 540 | :property: time 541 | :metric: false 542 | :special: false 543 | :arbitrary: false 544 | - :names: mean Julian month 545 | :symbol: moj 546 | :primary_code: mo_j 547 | :secondary_code: MO_J 548 | :scale: 549 | :value: 1 550 | :unit_code: a_j/12 551 | :classification: iso1000 552 | :property: time 553 | :metric: false 554 | :special: false 555 | :arbitrary: false 556 | - :names: mean Gregorian month 557 | :symbol: mog 558 | :primary_code: mo_g 559 | :secondary_code: MO_G 560 | :scale: 561 | :value: 1 562 | :unit_code: a_g/12 563 | :classification: iso1000 564 | :property: time 565 | :metric: false 566 | :special: false 567 | :arbitrary: false 568 | - :names: month 569 | :symbol: mo 570 | :primary_code: mo 571 | :secondary_code: MO 572 | :scale: 573 | :value: 1 574 | :unit_code: mo_j 575 | :classification: iso1000 576 | :property: time 577 | :metric: false 578 | :special: false 579 | :arbitrary: false 580 | - :names: tonne 581 | :symbol: t 582 | :primary_code: t 583 | :secondary_code: TNE 584 | :scale: 585 | :value: 1000 586 | :unit_code: kg 587 | :classification: iso1000 588 | :property: mass 589 | :metric: true 590 | :special: false 591 | :arbitrary: false 592 | - :names: bar 593 | :symbol: bar 594 | :primary_code: bar 595 | :secondary_code: BAR 596 | :scale: 597 | :value: 100000 598 | :unit_code: Pa 599 | :classification: iso1000 600 | :property: pressure 601 | :metric: true 602 | :special: false 603 | :arbitrary: false 604 | - :names: unified atomic mass unit 605 | :symbol: u 606 | :primary_code: u 607 | :secondary_code: AMU 608 | :scale: 609 | :value: !ruby/object:BigDecimal 18:0.16605402e-23 610 | :unit_code: g 611 | :classification: iso1000 612 | :property: mass 613 | :metric: true 614 | :special: false 615 | :arbitrary: false 616 | - :names: electronvolt 617 | :symbol: eV 618 | :primary_code: eV 619 | :secondary_code: EV 620 | :scale: 621 | :value: 1 622 | :unit_code: "[e].V" 623 | :classification: iso1000 624 | :property: energy 625 | :metric: true 626 | :special: false 627 | :arbitrary: false 628 | - :names: astronomic unit 629 | :symbol: AU 630 | :primary_code: AU 631 | :secondary_code: ASU 632 | :scale: 633 | :value: !ruby/object:BigDecimal 27:0.149597870691e6 634 | :unit_code: Mm 635 | :classification: iso1000 636 | :property: length 637 | :metric: false 638 | :special: false 639 | :arbitrary: false 640 | - :names: parsec 641 | :symbol: pc 642 | :primary_code: pc 643 | :secondary_code: PRS 644 | :scale: 645 | :value: 30856780000000000 646 | :unit_code: m 647 | :classification: iso1000 648 | :property: length 649 | :metric: true 650 | :special: false 651 | :arbitrary: false 652 | - :names: velocity of light 653 | :symbol: "c" 654 | :primary_code: "[c]" 655 | :secondary_code: "[C]" 656 | :scale: 657 | :value: 299792458 658 | :unit_code: m/s 659 | :classification: const 660 | :property: velocity 661 | :metric: true 662 | :special: false 663 | :arbitrary: false 664 | - :names: Planck constant 665 | :symbol: "h" 666 | :primary_code: "[h]" 667 | :secondary_code: "[H]" 668 | :scale: 669 | :value: !ruby/object:BigDecimal 18:0.66260755e-33 670 | :unit_code: J.s 671 | :classification: const 672 | :property: action 673 | :metric: true 674 | :special: false 675 | :arbitrary: false 676 | - :names: Boltzmann constant 677 | :symbol: "k" 678 | :primary_code: "[k]" 679 | :secondary_code: "[K]" 680 | :scale: 681 | :value: !ruby/object:BigDecimal 18:0.1380658e-22 682 | :unit_code: J/K 683 | :classification: const 684 | :property: "(unclassified)" 685 | :metric: true 686 | :special: false 687 | :arbitrary: false 688 | - :names: permittivity of vacuum 689 | :symbol: "ε0" 690 | :primary_code: "[eps_0]" 691 | :secondary_code: "[EPS_0]" 692 | :scale: 693 | :value: !ruby/object:BigDecimal 27:0.8854187817e-11 694 | :unit_code: F/m 695 | :classification: const 696 | :property: electric permittivity 697 | :metric: true 698 | :special: false 699 | :arbitrary: false 700 | - :names: permeability of vacuum 701 | :symbol: "μ0" 702 | :primary_code: "[mu_0]" 703 | :secondary_code: "[MU_0]" 704 | :scale: 705 | :value: 1 706 | :unit_code: 4.[pi].10*-7.N/A2 707 | :classification: const 708 | :property: magnetic permeability 709 | :metric: true 710 | :special: false 711 | :arbitrary: false 712 | - :names: elementary charge 713 | :symbol: "e" 714 | :primary_code: "[e]" 715 | :secondary_code: "[E]" 716 | :scale: 717 | :value: !ruby/object:BigDecimal 18:0.160217733e-18 718 | :unit_code: C 719 | :classification: const 720 | :property: electric charge 721 | :metric: true 722 | :special: false 723 | :arbitrary: false 724 | - :names: electron mass 725 | :symbol: "me" 726 | :primary_code: "[m_e]" 727 | :secondary_code: "[M_E]" 728 | :scale: 729 | :value: !ruby/object:BigDecimal 18:0.91093897e-27 730 | :unit_code: g 731 | :classification: const 732 | :property: mass 733 | :metric: true 734 | :special: false 735 | :arbitrary: false 736 | - :names: proton mass 737 | :symbol: "mp" 738 | :primary_code: "[m_p]" 739 | :secondary_code: "[M_P]" 740 | :scale: 741 | :value: !ruby/object:BigDecimal 18:0.16726231e-23 742 | :unit_code: g 743 | :classification: const 744 | :property: mass 745 | :metric: true 746 | :special: false 747 | :arbitrary: false 748 | - :names: Newtonian constant of gravitation 749 | :symbol: "G" 750 | :primary_code: "[G]" 751 | :secondary_code: "[GC]" 752 | :scale: 753 | :value: !ruby/object:BigDecimal 18:0.667259e-10 754 | :unit_code: m3.kg-1.s-2 755 | :classification: const 756 | :property: "(unclassified)" 757 | :metric: true 758 | :special: false 759 | :arbitrary: false 760 | - :names: standard acceleration of free fall 761 | :symbol: "gn" 762 | :primary_code: "[g]" 763 | :secondary_code: "[G]" 764 | :scale: 765 | :value: !ruby/object:BigDecimal 18:0.980665e1 766 | :unit_code: m/s2 767 | :classification: const 768 | :property: acceleration 769 | :metric: true 770 | :special: false 771 | :arbitrary: false 772 | - :names: standard atmosphere 773 | :symbol: atm 774 | :primary_code: atm 775 | :secondary_code: ATM 776 | :scale: 777 | :value: 101325 778 | :unit_code: Pa 779 | :classification: const 780 | :property: pressure 781 | :metric: false 782 | :special: false 783 | :arbitrary: false 784 | - :names: light-year 785 | :symbol: l.y. 786 | :primary_code: "[ly]" 787 | :secondary_code: "[LY]" 788 | :scale: 789 | :value: 1 790 | :unit_code: "[c].a_j" 791 | :classification: const 792 | :property: length 793 | :metric: true 794 | :special: false 795 | :arbitrary: false 796 | - :names: gram-force 797 | :symbol: gf 798 | :primary_code: gf 799 | :secondary_code: GF 800 | :scale: 801 | :value: 1 802 | :unit_code: g.[g] 803 | :classification: const 804 | :property: force 805 | :metric: true 806 | :special: false 807 | :arbitrary: false 808 | - :names: pound force 809 | :symbol: lbf 810 | :primary_code: "[lbf_av]" 811 | :secondary_code: "[LBF_AV]" 812 | :scale: 813 | :value: 1 814 | :unit_code: "[lb_av].[g]" 815 | :classification: const 816 | :property: force 817 | :metric: false 818 | :special: false 819 | :arbitrary: false 820 | - :names: Kayser 821 | :symbol: K 822 | :primary_code: Ky 823 | :secondary_code: KY 824 | :scale: 825 | :value: 1 826 | :unit_code: cm-1 827 | :classification: cgs 828 | :property: lineic number 829 | :metric: true 830 | :special: false 831 | :arbitrary: false 832 | - :names: Gal 833 | :symbol: Gal 834 | :primary_code: Gal 835 | :secondary_code: GL 836 | :scale: 837 | :value: 1 838 | :unit_code: cm/s2 839 | :classification: cgs 840 | :property: acceleration 841 | :metric: true 842 | :special: false 843 | :arbitrary: false 844 | - :names: dyne 845 | :symbol: dyn 846 | :primary_code: dyn 847 | :secondary_code: DYN 848 | :scale: 849 | :value: 1 850 | :unit_code: g.cm/s2 851 | :classification: cgs 852 | :property: force 853 | :metric: true 854 | :special: false 855 | :arbitrary: false 856 | - :names: erg 857 | :symbol: erg 858 | :primary_code: erg 859 | :secondary_code: ERG 860 | :scale: 861 | :value: 1 862 | :unit_code: dyn.cm 863 | :classification: cgs 864 | :property: energy 865 | :metric: true 866 | :special: false 867 | :arbitrary: false 868 | - :names: Poise 869 | :symbol: P 870 | :primary_code: P 871 | :secondary_code: P 872 | :scale: 873 | :value: 1 874 | :unit_code: dyn.s/cm2 875 | :classification: cgs 876 | :property: dynamic viscosity 877 | :metric: true 878 | :special: false 879 | :arbitrary: false 880 | - :names: Biot 881 | :symbol: Bi 882 | :primary_code: Bi 883 | :secondary_code: BI 884 | :scale: 885 | :value: 10 886 | :unit_code: A 887 | :classification: cgs 888 | :property: electric current 889 | :metric: true 890 | :special: false 891 | :arbitrary: false 892 | - :names: Stokes 893 | :symbol: St 894 | :primary_code: St 895 | :secondary_code: ST 896 | :scale: 897 | :value: 1 898 | :unit_code: cm2/s 899 | :classification: cgs 900 | :property: kinematic viscosity 901 | :metric: true 902 | :special: false 903 | :arbitrary: false 904 | - :names: Maxwell 905 | :symbol: Mx 906 | :primary_code: Mx 907 | :secondary_code: MX 908 | :scale: 909 | :value: !ruby/object:BigDecimal 18:0.1e-7 910 | :unit_code: Wb 911 | :classification: cgs 912 | :property: flux of magnetic induction 913 | :metric: true 914 | :special: false 915 | :arbitrary: false 916 | - :names: Gauss 917 | :symbol: Gs 918 | :primary_code: G 919 | :secondary_code: GS 920 | :scale: 921 | :value: !ruby/object:BigDecimal 18:0.1e-3 922 | :unit_code: T 923 | :classification: cgs 924 | :property: magnetic flux density 925 | :metric: true 926 | :special: false 927 | :arbitrary: false 928 | - :names: Oersted 929 | :symbol: Oe 930 | :primary_code: Oe 931 | :secondary_code: OE 932 | :scale: 933 | :value: 250 934 | :unit_code: "/[pi].A/m" 935 | :classification: cgs 936 | :property: magnetic field intensity 937 | :metric: true 938 | :special: false 939 | :arbitrary: false 940 | - :names: Gilbert 941 | :symbol: Gb 942 | :primary_code: Gb 943 | :secondary_code: GB 944 | :scale: 945 | :value: 1 946 | :unit_code: Oe.cm 947 | :classification: cgs 948 | :property: magnetic tension 949 | :metric: true 950 | :special: false 951 | :arbitrary: false 952 | - :names: stilb 953 | :symbol: sb 954 | :primary_code: sb 955 | :secondary_code: SB 956 | :scale: 957 | :value: 1 958 | :unit_code: cd/cm2 959 | :classification: cgs 960 | :property: lum. intensity density 961 | :metric: true 962 | :special: false 963 | :arbitrary: false 964 | - :names: Lambert 965 | :symbol: L 966 | :primary_code: Lmb 967 | :secondary_code: LMB 968 | :scale: 969 | :value: 1 970 | :unit_code: cd/cm2/[pi] 971 | :classification: cgs 972 | :property: brightness 973 | :metric: true 974 | :special: false 975 | :arbitrary: false 976 | - :names: phot 977 | :symbol: ph 978 | :primary_code: ph 979 | :secondary_code: PHT 980 | :scale: 981 | :value: !ruby/object:BigDecimal 18:0.1e-3 982 | :unit_code: lx 983 | :classification: cgs 984 | :property: illuminance 985 | :metric: true 986 | :special: false 987 | :arbitrary: false 988 | - :names: Curie 989 | :symbol: Ci 990 | :primary_code: Ci 991 | :secondary_code: CI 992 | :scale: 993 | :value: 37000000000 994 | :unit_code: Bq 995 | :classification: cgs 996 | :property: radioactivity 997 | :metric: true 998 | :special: false 999 | :arbitrary: false 1000 | - :names: Roentgen 1001 | :symbol: R 1002 | :primary_code: R 1003 | :secondary_code: ROE 1004 | :scale: 1005 | :value: !ruby/object:BigDecimal 18:0.258e-3 1006 | :unit_code: C/kg 1007 | :classification: cgs 1008 | :property: ion dose 1009 | :metric: true 1010 | :special: false 1011 | :arbitrary: false 1012 | - :names: radiation absorbed dose 1013 | :symbol: RAD 1014 | :primary_code: RAD 1015 | :secondary_code: "[RAD]" 1016 | :scale: 1017 | :value: 100 1018 | :unit_code: erg/g 1019 | :classification: cgs 1020 | :property: energy dose 1021 | :metric: true 1022 | :special: false 1023 | :arbitrary: false 1024 | - :names: radiation equivalent man 1025 | :symbol: REM 1026 | :primary_code: REM 1027 | :secondary_code: "[REM]" 1028 | :scale: 1029 | :value: 1 1030 | :unit_code: RAD 1031 | :classification: cgs 1032 | :property: dose equivalent 1033 | :metric: true 1034 | :special: false 1035 | :arbitrary: false 1036 | - :names: inch 1037 | :symbol: in 1038 | :primary_code: "[in_i]" 1039 | :secondary_code: "[IN_I]" 1040 | :scale: 1041 | :value: !ruby/object:BigDecimal 18:0.254e1 1042 | :unit_code: cm 1043 | :classification: intcust 1044 | :property: length 1045 | :metric: false 1046 | :special: false 1047 | :arbitrary: false 1048 | - :names: foot 1049 | :symbol: ft 1050 | :primary_code: "[ft_i]" 1051 | :secondary_code: "[FT_I]" 1052 | :scale: 1053 | :value: 12 1054 | :unit_code: "[in_i]" 1055 | :classification: intcust 1056 | :property: length 1057 | :metric: false 1058 | :special: false 1059 | :arbitrary: false 1060 | - :names: yard 1061 | :symbol: yd 1062 | :primary_code: "[yd_i]" 1063 | :secondary_code: "[YD_I]" 1064 | :scale: 1065 | :value: 3 1066 | :unit_code: "[ft_i]" 1067 | :classification: intcust 1068 | :property: length 1069 | :metric: false 1070 | :special: false 1071 | :arbitrary: false 1072 | - :names: mile 1073 | :symbol: mi 1074 | :primary_code: "[mi_i]" 1075 | :secondary_code: "[MI_I]" 1076 | :scale: 1077 | :value: 5280 1078 | :unit_code: "[ft_i]" 1079 | :classification: intcust 1080 | :property: length 1081 | :metric: false 1082 | :special: false 1083 | :arbitrary: false 1084 | - :names: fathom 1085 | :symbol: fth 1086 | :primary_code: "[fth_i]" 1087 | :secondary_code: "[FTH_I]" 1088 | :scale: 1089 | :value: 6 1090 | :unit_code: "[ft_i]" 1091 | :classification: intcust 1092 | :property: depth of water 1093 | :metric: false 1094 | :special: false 1095 | :arbitrary: false 1096 | - :names: nautical mile 1097 | :symbol: n.mi 1098 | :primary_code: "[nmi_i]" 1099 | :secondary_code: "[NMI_I]" 1100 | :scale: 1101 | :value: 1852 1102 | :unit_code: m 1103 | :classification: intcust 1104 | :property: length 1105 | :metric: false 1106 | :special: false 1107 | :arbitrary: false 1108 | - :names: knot 1109 | :symbol: knot 1110 | :primary_code: "[kn_i]" 1111 | :secondary_code: "[KN_I]" 1112 | :scale: 1113 | :value: 1 1114 | :unit_code: "[nmi_i]/h" 1115 | :classification: intcust 1116 | :property: velocity 1117 | :metric: false 1118 | :special: false 1119 | :arbitrary: false 1120 | - :names: square inch 1121 | :primary_code: "[sin_i]" 1122 | :secondary_code: "[SIN_I]" 1123 | :scale: 1124 | :value: 1 1125 | :unit_code: "[in_i]2" 1126 | :classification: intcust 1127 | :property: area 1128 | :metric: false 1129 | :special: false 1130 | :arbitrary: false 1131 | - :names: square foot 1132 | :primary_code: "[sft_i]" 1133 | :secondary_code: "[SFT_I]" 1134 | :scale: 1135 | :value: 1 1136 | :unit_code: "[ft_i]2" 1137 | :classification: intcust 1138 | :property: area 1139 | :metric: false 1140 | :special: false 1141 | :arbitrary: false 1142 | - :names: square yard 1143 | :primary_code: "[syd_i]" 1144 | :secondary_code: "[SYD_I]" 1145 | :scale: 1146 | :value: 1 1147 | :unit_code: "[yd_i]2" 1148 | :classification: intcust 1149 | :property: area 1150 | :metric: false 1151 | :special: false 1152 | :arbitrary: false 1153 | - :names: cubic inch 1154 | :primary_code: "[cin_i]" 1155 | :secondary_code: "[CIN_I]" 1156 | :scale: 1157 | :value: 1 1158 | :unit_code: "[in_i]3" 1159 | :classification: intcust 1160 | :property: volume 1161 | :metric: false 1162 | :special: false 1163 | :arbitrary: false 1164 | - :names: cubic foot 1165 | :primary_code: "[cft_i]" 1166 | :secondary_code: "[CFT_I]" 1167 | :scale: 1168 | :value: 1 1169 | :unit_code: "[ft_i]3" 1170 | :classification: intcust 1171 | :property: volume 1172 | :metric: false 1173 | :special: false 1174 | :arbitrary: false 1175 | - :names: cubic yard 1176 | :symbol: cu.yd 1177 | :primary_code: "[cyd_i]" 1178 | :secondary_code: "[CYD_I]" 1179 | :scale: 1180 | :value: 1 1181 | :unit_code: "[yd_i]3" 1182 | :classification: intcust 1183 | :property: volume 1184 | :metric: false 1185 | :special: false 1186 | :arbitrary: false 1187 | - :names: board foot 1188 | :primary_code: "[bf_i]" 1189 | :secondary_code: "[BF_I]" 1190 | :scale: 1191 | :value: 144 1192 | :unit_code: "[in_i]3" 1193 | :classification: intcust 1194 | :property: volume 1195 | :metric: false 1196 | :special: false 1197 | :arbitrary: false 1198 | - :names: cord 1199 | :primary_code: "[cr_i]" 1200 | :secondary_code: "[CR_I]" 1201 | :scale: 1202 | :value: 128 1203 | :unit_code: "[ft_i]3" 1204 | :classification: intcust 1205 | :property: volume 1206 | :metric: false 1207 | :special: false 1208 | :arbitrary: false 1209 | - :names: mil 1210 | :symbol: mil 1211 | :primary_code: "[mil_i]" 1212 | :secondary_code: "[MIL_I]" 1213 | :scale: 1214 | :value: !ruby/object:BigDecimal 18:0.1e-2 1215 | :unit_code: "[in_i]" 1216 | :classification: intcust 1217 | :property: length 1218 | :metric: false 1219 | :special: false 1220 | :arbitrary: false 1221 | - :names: circular mil 1222 | :symbol: circ.mil 1223 | :primary_code: "[cml_i]" 1224 | :secondary_code: "[CML_I]" 1225 | :scale: 1226 | :value: 1 1227 | :unit_code: "[pi]/4.[mil_i]2" 1228 | :classification: intcust 1229 | :property: area 1230 | :metric: false 1231 | :special: false 1232 | :arbitrary: false 1233 | - :names: hand 1234 | :symbol: hd 1235 | :primary_code: "[hd_i]" 1236 | :secondary_code: "[HD_I]" 1237 | :scale: 1238 | :value: 4 1239 | :unit_code: "[in_i]" 1240 | :classification: intcust 1241 | :property: height of horses 1242 | :metric: false 1243 | :special: false 1244 | :arbitrary: false 1245 | - :names: foot 1246 | :symbol: ftus 1247 | :primary_code: "[ft_us]" 1248 | :secondary_code: "[FT_US]" 1249 | :scale: 1250 | :value: 1200 1251 | :unit_code: m/3937 1252 | :classification: us-lengths 1253 | :property: length 1254 | :metric: false 1255 | :special: false 1256 | :arbitrary: false 1257 | - :names: yard 1258 | :primary_code: "[yd_us]" 1259 | :secondary_code: "[YD_US]" 1260 | :scale: 1261 | :value: 3 1262 | :unit_code: "[ft_us]" 1263 | :classification: us-lengths 1264 | :property: length 1265 | :metric: false 1266 | :special: false 1267 | :arbitrary: false 1268 | - :names: inch 1269 | :primary_code: "[in_us]" 1270 | :secondary_code: "[IN_US]" 1271 | :scale: 1272 | :value: 1 1273 | :unit_code: "[ft_us]/12" 1274 | :classification: us-lengths 1275 | :property: length 1276 | :metric: false 1277 | :special: false 1278 | :arbitrary: false 1279 | - :names: rod 1280 | :primary_code: "[rd_us]" 1281 | :secondary_code: "[RD_US]" 1282 | :scale: 1283 | :value: !ruby/object:BigDecimal 18:0.165e2 1284 | :unit_code: "[ft_us]" 1285 | :classification: us-lengths 1286 | :property: length 1287 | :metric: false 1288 | :special: false 1289 | :arbitrary: false 1290 | - :names: 1291 | - Gunter's chain 1292 | - Surveyor's chain 1293 | :primary_code: "[ch_us]" 1294 | :secondary_code: "[CH_US]" 1295 | :scale: 1296 | :value: 4 1297 | :unit_code: "[rd_us]" 1298 | :classification: us-lengths 1299 | :property: length 1300 | :metric: false 1301 | :special: false 1302 | :arbitrary: false 1303 | - :names: link for Gunter's chain 1304 | :primary_code: "[lk_us]" 1305 | :secondary_code: "[LK_US]" 1306 | :scale: 1307 | :value: 1 1308 | :unit_code: "[ch_us]/100" 1309 | :classification: us-lengths 1310 | :property: length 1311 | :metric: false 1312 | :special: false 1313 | :arbitrary: false 1314 | - :names: 1315 | - Ramden's chain 1316 | - Engineer's chain 1317 | :primary_code: "[rch_us]" 1318 | :secondary_code: "[RCH_US]" 1319 | :scale: 1320 | :value: 100 1321 | :unit_code: "[ft_us]" 1322 | :classification: us-lengths 1323 | :property: length 1324 | :metric: false 1325 | :special: false 1326 | :arbitrary: false 1327 | - :names: link for Ramden's chain 1328 | :primary_code: "[rlk_us]" 1329 | :secondary_code: "[RLK_US]" 1330 | :scale: 1331 | :value: 1 1332 | :unit_code: "[rch_us]/100" 1333 | :classification: us-lengths 1334 | :property: length 1335 | :metric: false 1336 | :special: false 1337 | :arbitrary: false 1338 | - :names: fathom 1339 | :primary_code: "[fth_us]" 1340 | :secondary_code: "[FTH_US]" 1341 | :scale: 1342 | :value: 6 1343 | :unit_code: "[ft_us]" 1344 | :classification: us-lengths 1345 | :property: length 1346 | :metric: false 1347 | :special: false 1348 | :arbitrary: false 1349 | - :names: furlong 1350 | :primary_code: "[fur_us]" 1351 | :secondary_code: "[FUR_US]" 1352 | :scale: 1353 | :value: 40 1354 | :unit_code: "[rd_us]" 1355 | :classification: us-lengths 1356 | :property: length 1357 | :metric: false 1358 | :special: false 1359 | :arbitrary: false 1360 | - :names: mile 1361 | :primary_code: "[mi_us]" 1362 | :secondary_code: "[MI_US]" 1363 | :scale: 1364 | :value: 8 1365 | :unit_code: "[fur_us]" 1366 | :classification: us-lengths 1367 | :property: length 1368 | :metric: false 1369 | :special: false 1370 | :arbitrary: false 1371 | - :names: acre 1372 | :primary_code: "[acr_us]" 1373 | :secondary_code: "[ACR_US]" 1374 | :scale: 1375 | :value: 160 1376 | :unit_code: "[rd_us]2" 1377 | :classification: us-lengths 1378 | :property: area 1379 | :metric: false 1380 | :special: false 1381 | :arbitrary: false 1382 | - :names: square rod 1383 | :primary_code: "[srd_us]" 1384 | :secondary_code: "[SRD_US]" 1385 | :scale: 1386 | :value: 1 1387 | :unit_code: "[rd_us]2" 1388 | :classification: us-lengths 1389 | :property: area 1390 | :metric: false 1391 | :special: false 1392 | :arbitrary: false 1393 | - :names: square mile 1394 | :primary_code: "[smi_us]" 1395 | :secondary_code: "[SMI_US]" 1396 | :scale: 1397 | :value: 1 1398 | :unit_code: "[mi_us]2" 1399 | :classification: us-lengths 1400 | :property: area 1401 | :metric: false 1402 | :special: false 1403 | :arbitrary: false 1404 | - :names: section 1405 | :primary_code: "[sct]" 1406 | :secondary_code: "[SCT]" 1407 | :scale: 1408 | :value: 1 1409 | :unit_code: "[mi_us]2" 1410 | :classification: us-lengths 1411 | :property: area 1412 | :metric: false 1413 | :special: false 1414 | :arbitrary: false 1415 | - :names: township 1416 | :primary_code: "[twp]" 1417 | :secondary_code: "[TWP]" 1418 | :scale: 1419 | :value: 36 1420 | :unit_code: "[sct]" 1421 | :classification: us-lengths 1422 | :property: area 1423 | :metric: false 1424 | :special: false 1425 | :arbitrary: false 1426 | - :names: mil 1427 | :primary_code: "[mil_us]" 1428 | :secondary_code: "[MIL_US]" 1429 | :scale: 1430 | :value: !ruby/object:BigDecimal 18:0.1e-2 1431 | :unit_code: "[in_us]" 1432 | :classification: us-lengths 1433 | :property: length 1434 | :metric: false 1435 | :special: false 1436 | :arbitrary: false 1437 | - :names: inch 1438 | :primary_code: "[in_br]" 1439 | :secondary_code: "[IN_BR]" 1440 | :scale: 1441 | :value: !ruby/object:BigDecimal 18:0.2539998e1 1442 | :unit_code: cm 1443 | :classification: brit-length 1444 | :property: length 1445 | :metric: false 1446 | :special: false 1447 | :arbitrary: false 1448 | - :names: foot 1449 | :primary_code: "[ft_br]" 1450 | :secondary_code: "[FT_BR]" 1451 | :scale: 1452 | :value: 12 1453 | :unit_code: "[in_br]" 1454 | :classification: brit-length 1455 | :property: length 1456 | :metric: false 1457 | :special: false 1458 | :arbitrary: false 1459 | - :names: rod 1460 | :primary_code: "[rd_br]" 1461 | :secondary_code: "[RD_BR]" 1462 | :scale: 1463 | :value: !ruby/object:BigDecimal 18:0.165e2 1464 | :unit_code: "[ft_br]" 1465 | :classification: brit-length 1466 | :property: length 1467 | :metric: false 1468 | :special: false 1469 | :arbitrary: false 1470 | - :names: Gunter's chain 1471 | :primary_code: "[ch_br]" 1472 | :secondary_code: "[CH_BR]" 1473 | :scale: 1474 | :value: 4 1475 | :unit_code: "[rd_br]" 1476 | :classification: brit-length 1477 | :property: length 1478 | :metric: false 1479 | :special: false 1480 | :arbitrary: false 1481 | - :names: link for Gunter's chain 1482 | :primary_code: "[lk_br]" 1483 | :secondary_code: "[LK_BR]" 1484 | :scale: 1485 | :value: 1 1486 | :unit_code: "[ch_br]/100" 1487 | :classification: brit-length 1488 | :property: length 1489 | :metric: false 1490 | :special: false 1491 | :arbitrary: false 1492 | - :names: fathom 1493 | :primary_code: "[fth_br]" 1494 | :secondary_code: "[FTH_BR]" 1495 | :scale: 1496 | :value: 6 1497 | :unit_code: "[ft_br]" 1498 | :classification: brit-length 1499 | :property: length 1500 | :metric: false 1501 | :special: false 1502 | :arbitrary: false 1503 | - :names: pace 1504 | :primary_code: "[pc_br]" 1505 | :secondary_code: "[PC_BR]" 1506 | :scale: 1507 | :value: !ruby/object:BigDecimal 18:0.25e1 1508 | :unit_code: "[ft_br]" 1509 | :classification: brit-length 1510 | :property: length 1511 | :metric: false 1512 | :special: false 1513 | :arbitrary: false 1514 | - :names: yard 1515 | :primary_code: "[yd_br]" 1516 | :secondary_code: "[YD_BR]" 1517 | :scale: 1518 | :value: 3 1519 | :unit_code: "[ft_br]" 1520 | :classification: brit-length 1521 | :property: length 1522 | :metric: false 1523 | :special: false 1524 | :arbitrary: false 1525 | - :names: mile 1526 | :primary_code: "[mi_br]" 1527 | :secondary_code: "[MI_BR]" 1528 | :scale: 1529 | :value: 5280 1530 | :unit_code: "[ft_br]" 1531 | :classification: brit-length 1532 | :property: length 1533 | :metric: false 1534 | :special: false 1535 | :arbitrary: false 1536 | - :names: nautical mile 1537 | :primary_code: "[nmi_br]" 1538 | :secondary_code: "[NMI_BR]" 1539 | :scale: 1540 | :value: 6080 1541 | :unit_code: "[ft_br]" 1542 | :classification: brit-length 1543 | :property: length 1544 | :metric: false 1545 | :special: false 1546 | :arbitrary: false 1547 | - :names: knot 1548 | :primary_code: "[kn_br]" 1549 | :secondary_code: "[KN_BR]" 1550 | :scale: 1551 | :value: 1 1552 | :unit_code: "[nmi_br]/h" 1553 | :classification: brit-length 1554 | :property: velocity 1555 | :metric: false 1556 | :special: false 1557 | :arbitrary: false 1558 | - :names: acre 1559 | :primary_code: "[acr_br]" 1560 | :secondary_code: "[ACR_BR]" 1561 | :scale: 1562 | :value: 4840 1563 | :unit_code: "[yd_br]2" 1564 | :classification: brit-length 1565 | :property: area 1566 | :metric: false 1567 | :special: false 1568 | :arbitrary: false 1569 | - :names: Queen Anne's wine gallon 1570 | :primary_code: "[gal_us]" 1571 | :secondary_code: "[GAL_US]" 1572 | :scale: 1573 | :value: 231 1574 | :unit_code: "[in_i]3" 1575 | :classification: us-volumes 1576 | :property: fluid volume 1577 | :metric: false 1578 | :special: false 1579 | :arbitrary: false 1580 | - :names: barrel 1581 | :primary_code: "[bbl_us]" 1582 | :secondary_code: "[BBL_US]" 1583 | :scale: 1584 | :value: 42 1585 | :unit_code: "[gal_us]" 1586 | :classification: us-volumes 1587 | :property: fluid volume 1588 | :metric: false 1589 | :special: false 1590 | :arbitrary: false 1591 | - :names: quart 1592 | :primary_code: "[qt_us]" 1593 | :secondary_code: "[QT_US]" 1594 | :scale: 1595 | :value: 1 1596 | :unit_code: "[gal_us]/4" 1597 | :classification: us-volumes 1598 | :property: fluid volume 1599 | :metric: false 1600 | :special: false 1601 | :arbitrary: false 1602 | - :names: pint 1603 | :primary_code: "[pt_us]" 1604 | :secondary_code: "[PT_US]" 1605 | :scale: 1606 | :value: 1 1607 | :unit_code: "[qt_us]/2" 1608 | :classification: us-volumes 1609 | :property: fluid volume 1610 | :metric: false 1611 | :special: false 1612 | :arbitrary: false 1613 | - :names: gill 1614 | :primary_code: "[gil_us]" 1615 | :secondary_code: "[GIL_US]" 1616 | :scale: 1617 | :value: 1 1618 | :unit_code: "[pt_us]/4" 1619 | :classification: us-volumes 1620 | :property: fluid volume 1621 | :metric: false 1622 | :special: false 1623 | :arbitrary: false 1624 | - :names: fluid ounce 1625 | :symbol: oz fl 1626 | :primary_code: "[foz_us]" 1627 | :secondary_code: "[FOZ_US]" 1628 | :scale: 1629 | :value: 1 1630 | :unit_code: "[gil_us]/4" 1631 | :classification: us-volumes 1632 | :property: fluid volume 1633 | :metric: false 1634 | :special: false 1635 | :arbitrary: false 1636 | - :names: fluid dram 1637 | :primary_code: "[fdr_us]" 1638 | :secondary_code: "[FDR_US]" 1639 | :scale: 1640 | :value: 1 1641 | :unit_code: "[foz_us]/8" 1642 | :classification: us-volumes 1643 | :property: fluid volume 1644 | :metric: false 1645 | :special: false 1646 | :arbitrary: false 1647 | - :names: minim 1648 | :primary_code: "[min_us]" 1649 | :secondary_code: "[MIN_US]" 1650 | :scale: 1651 | :value: 1 1652 | :unit_code: "[fdr_us]/60" 1653 | :classification: us-volumes 1654 | :property: fluid volume 1655 | :metric: false 1656 | :special: false 1657 | :arbitrary: false 1658 | - :names: cord 1659 | :primary_code: "[crd_us]" 1660 | :secondary_code: "[CRD_US]" 1661 | :scale: 1662 | :value: 128 1663 | :unit_code: "[ft_i]3" 1664 | :classification: us-volumes 1665 | :property: fluid volume 1666 | :metric: false 1667 | :special: false 1668 | :arbitrary: false 1669 | - :names: bushel 1670 | :primary_code: "[bu_us]" 1671 | :secondary_code: "[BU_US]" 1672 | :scale: 1673 | :value: !ruby/object:BigDecimal 18:0.215042e4 1674 | :unit_code: "[in_i]3" 1675 | :classification: us-volumes 1676 | :property: dry volume 1677 | :metric: false 1678 | :special: false 1679 | :arbitrary: false 1680 | - :names: historical winchester gallon 1681 | :primary_code: "[gal_wi]" 1682 | :secondary_code: "[GAL_WI]" 1683 | :scale: 1684 | :value: 1 1685 | :unit_code: "[bu_us]/8" 1686 | :classification: us-volumes 1687 | :property: dry volume 1688 | :metric: false 1689 | :special: false 1690 | :arbitrary: false 1691 | - :names: peck 1692 | :primary_code: "[pk_us]" 1693 | :secondary_code: "[PK_US]" 1694 | :scale: 1695 | :value: 1 1696 | :unit_code: "[bu_us]/4" 1697 | :classification: us-volumes 1698 | :property: dry volume 1699 | :metric: false 1700 | :special: false 1701 | :arbitrary: false 1702 | - :names: dry quart 1703 | :primary_code: "[dqt_us]" 1704 | :secondary_code: "[DQT_US]" 1705 | :scale: 1706 | :value: 1 1707 | :unit_code: "[pk_us]/8" 1708 | :classification: us-volumes 1709 | :property: dry volume 1710 | :metric: false 1711 | :special: false 1712 | :arbitrary: false 1713 | - :names: dry pint 1714 | :primary_code: "[dpt_us]" 1715 | :secondary_code: "[DPT_US]" 1716 | :scale: 1717 | :value: 1 1718 | :unit_code: "[dqt_us]/2" 1719 | :classification: us-volumes 1720 | :property: dry volume 1721 | :metric: false 1722 | :special: false 1723 | :arbitrary: false 1724 | - :names: tablespoon 1725 | :primary_code: "[tbs_us]" 1726 | :secondary_code: "[TBS_US]" 1727 | :scale: 1728 | :value: 1 1729 | :unit_code: "[foz_us]/2" 1730 | :classification: us-volumes 1731 | :property: volume 1732 | :metric: false 1733 | :special: false 1734 | :arbitrary: false 1735 | - :names: teaspoon 1736 | :primary_code: "[tsp_us]" 1737 | :secondary_code: "[TSP_US]" 1738 | :scale: 1739 | :value: 1 1740 | :unit_code: "[tbs_us]/3" 1741 | :classification: us-volumes 1742 | :property: volume 1743 | :metric: false 1744 | :special: false 1745 | :arbitrary: false 1746 | - :names: cup 1747 | :primary_code: "[cup_us]" 1748 | :secondary_code: "[CUP_US]" 1749 | :scale: 1750 | :value: 16 1751 | :unit_code: "[tbs_us]" 1752 | :classification: us-volumes 1753 | :property: volume 1754 | :metric: false 1755 | :special: false 1756 | :arbitrary: false 1757 | - :names: metric fluid ounce 1758 | :symbol: oz fl 1759 | :primary_code: "[foz_m]" 1760 | :secondary_code: "[FOZ_M]" 1761 | :scale: 1762 | :value: 30 1763 | :unit_code: mL 1764 | :classification: us-volumes 1765 | :property: fluid volume 1766 | :metric: false 1767 | :special: false 1768 | :arbitrary: false 1769 | - :names: metric cup 1770 | :primary_code: "[cup_m]" 1771 | :secondary_code: "[CUP_M]" 1772 | :scale: 1773 | :value: 240 1774 | :unit_code: mL 1775 | :classification: us-volumes 1776 | :property: volume 1777 | :metric: false 1778 | :special: false 1779 | :arbitrary: false 1780 | - :names: metric teaspoon 1781 | :primary_code: "[tsp_m]" 1782 | :secondary_code: "[TSP_M]" 1783 | :scale: 1784 | :value: 5 1785 | :unit_code: mL 1786 | :classification: us-volumes 1787 | :property: volume 1788 | :metric: false 1789 | :special: false 1790 | :arbitrary: false 1791 | - :names: metric tablespoon 1792 | :primary_code: "[tbs_m]" 1793 | :secondary_code: "[TBS_M]" 1794 | :scale: 1795 | :value: 15 1796 | :unit_code: mL 1797 | :classification: us-volumes 1798 | :property: volume 1799 | :metric: false 1800 | :special: false 1801 | :arbitrary: false 1802 | - :names: gallon 1803 | :primary_code: "[gal_br]" 1804 | :secondary_code: "[GAL_BR]" 1805 | :scale: 1806 | :value: !ruby/object:BigDecimal 18:0.454609e1 1807 | :unit_code: l 1808 | :classification: brit-volumes 1809 | :property: volume 1810 | :metric: false 1811 | :special: false 1812 | :arbitrary: false 1813 | - :names: peck 1814 | :primary_code: "[pk_br]" 1815 | :secondary_code: "[PK_BR]" 1816 | :scale: 1817 | :value: 2 1818 | :unit_code: "[gal_br]" 1819 | :classification: brit-volumes 1820 | :property: volume 1821 | :metric: false 1822 | :special: false 1823 | :arbitrary: false 1824 | - :names: bushel 1825 | :primary_code: "[bu_br]" 1826 | :secondary_code: "[BU_BR]" 1827 | :scale: 1828 | :value: 4 1829 | :unit_code: "[pk_br]" 1830 | :classification: brit-volumes 1831 | :property: volume 1832 | :metric: false 1833 | :special: false 1834 | :arbitrary: false 1835 | - :names: quart 1836 | :primary_code: "[qt_br]" 1837 | :secondary_code: "[QT_BR]" 1838 | :scale: 1839 | :value: 1 1840 | :unit_code: "[gal_br]/4" 1841 | :classification: brit-volumes 1842 | :property: volume 1843 | :metric: false 1844 | :special: false 1845 | :arbitrary: false 1846 | - :names: pint 1847 | :primary_code: "[pt_br]" 1848 | :secondary_code: "[PT_BR]" 1849 | :scale: 1850 | :value: 1 1851 | :unit_code: "[qt_br]/2" 1852 | :classification: brit-volumes 1853 | :property: volume 1854 | :metric: false 1855 | :special: false 1856 | :arbitrary: false 1857 | - :names: gill 1858 | :primary_code: "[gil_br]" 1859 | :secondary_code: "[GIL_BR]" 1860 | :scale: 1861 | :value: 1 1862 | :unit_code: "[pt_br]/4" 1863 | :classification: brit-volumes 1864 | :property: volume 1865 | :metric: false 1866 | :special: false 1867 | :arbitrary: false 1868 | - :names: fluid ounce 1869 | :primary_code: "[foz_br]" 1870 | :secondary_code: "[FOZ_BR]" 1871 | :scale: 1872 | :value: 1 1873 | :unit_code: "[gil_br]/5" 1874 | :classification: brit-volumes 1875 | :property: volume 1876 | :metric: false 1877 | :special: false 1878 | :arbitrary: false 1879 | - :names: fluid dram 1880 | :primary_code: "[fdr_br]" 1881 | :secondary_code: "[FDR_BR]" 1882 | :scale: 1883 | :value: 1 1884 | :unit_code: "[foz_br]/8" 1885 | :classification: brit-volumes 1886 | :property: volume 1887 | :metric: false 1888 | :special: false 1889 | :arbitrary: false 1890 | - :names: minim 1891 | :primary_code: "[min_br]" 1892 | :secondary_code: "[MIN_BR]" 1893 | :scale: 1894 | :value: 1 1895 | :unit_code: "[fdr_br]/60" 1896 | :classification: brit-volumes 1897 | :property: volume 1898 | :metric: false 1899 | :special: false 1900 | :arbitrary: false 1901 | - :names: grain 1902 | :primary_code: "[gr]" 1903 | :secondary_code: "[GR]" 1904 | :scale: 1905 | :value: !ruby/object:BigDecimal 18:0.6479891e2 1906 | :unit_code: mg 1907 | :classification: avoirdupois 1908 | :property: mass 1909 | :metric: false 1910 | :special: false 1911 | :arbitrary: false 1912 | - :names: pound 1913 | :symbol: lb 1914 | :primary_code: "[lb_av]" 1915 | :secondary_code: "[LB_AV]" 1916 | :scale: 1917 | :value: 7000 1918 | :unit_code: "[gr]" 1919 | :classification: avoirdupois 1920 | :property: mass 1921 | :metric: false 1922 | :special: false 1923 | :arbitrary: false 1924 | - :names: ounce 1925 | :symbol: oz 1926 | :primary_code: "[oz_av]" 1927 | :secondary_code: "[OZ_AV]" 1928 | :scale: 1929 | :value: 1 1930 | :unit_code: "[lb_av]/16" 1931 | :classification: avoirdupois 1932 | :property: mass 1933 | :metric: false 1934 | :special: false 1935 | :arbitrary: false 1936 | - :names: dram 1937 | :primary_code: "[dr_av]" 1938 | :secondary_code: "[DR_AV]" 1939 | :scale: 1940 | :value: 1 1941 | :unit_code: "[oz_av]/16" 1942 | :classification: avoirdupois 1943 | :property: mass 1944 | :metric: false 1945 | :special: false 1946 | :arbitrary: false 1947 | - :names: 1948 | - short hundredweight 1949 | - U.S. hundredweight 1950 | :primary_code: "[scwt_av]" 1951 | :secondary_code: "[SCWT_AV]" 1952 | :scale: 1953 | :value: 100 1954 | :unit_code: "[lb_av]" 1955 | :classification: avoirdupois 1956 | :property: mass 1957 | :metric: false 1958 | :special: false 1959 | :arbitrary: false 1960 | - :names: 1961 | - long hunderdweight 1962 | - British hundredweight 1963 | :primary_code: "[lcwt_av]" 1964 | :secondary_code: "[LCWT_AV]" 1965 | :scale: 1966 | :value: 112 1967 | :unit_code: "[lb_av]" 1968 | :classification: avoirdupois 1969 | :property: mass 1970 | :metric: false 1971 | :special: false 1972 | :arbitrary: false 1973 | - :names: 1974 | - short ton 1975 | - U.S. ton 1976 | :primary_code: "[ston_av]" 1977 | :secondary_code: "[STON_AV]" 1978 | :scale: 1979 | :value: 20 1980 | :unit_code: "[scwt_av]" 1981 | :classification: avoirdupois 1982 | :property: mass 1983 | :metric: false 1984 | :special: false 1985 | :arbitrary: false 1986 | - :names: 1987 | - long ton 1988 | - British ton 1989 | :primary_code: "[lton_av]" 1990 | :secondary_code: "[LTON_AV]" 1991 | :scale: 1992 | :value: 20 1993 | :unit_code: "[lcwt_av]" 1994 | :classification: avoirdupois 1995 | :property: mass 1996 | :metric: false 1997 | :special: false 1998 | :arbitrary: false 1999 | - :names: 2000 | - stone 2001 | - British stone 2002 | :primary_code: "[stone_av]" 2003 | :secondary_code: "[STONE_AV]" 2004 | :scale: 2005 | :value: 14 2006 | :unit_code: "[lb_av]" 2007 | :classification: avoirdupois 2008 | :property: mass 2009 | :metric: false 2010 | :special: false 2011 | :arbitrary: false 2012 | - :names: pennyweight 2013 | :primary_code: "[pwt_tr]" 2014 | :secondary_code: "[PWT_TR]" 2015 | :scale: 2016 | :value: 24 2017 | :unit_code: "[gr]" 2018 | :classification: troy 2019 | :property: mass 2020 | :metric: false 2021 | :special: false 2022 | :arbitrary: false 2023 | - :names: ounce 2024 | :primary_code: "[oz_tr]" 2025 | :secondary_code: "[OZ_TR]" 2026 | :scale: 2027 | :value: 20 2028 | :unit_code: "[pwt_tr]" 2029 | :classification: troy 2030 | :property: mass 2031 | :metric: false 2032 | :special: false 2033 | :arbitrary: false 2034 | - :names: pound 2035 | :primary_code: "[lb_tr]" 2036 | :secondary_code: "[LB_TR]" 2037 | :scale: 2038 | :value: 12 2039 | :unit_code: "[oz_tr]" 2040 | :classification: troy 2041 | :property: mass 2042 | :metric: false 2043 | :special: false 2044 | :arbitrary: false 2045 | - :names: scruple 2046 | :primary_code: "[sc_ap]" 2047 | :secondary_code: "[SC_AP]" 2048 | :scale: 2049 | :value: 20 2050 | :unit_code: "[gr]" 2051 | :classification: apoth 2052 | :property: mass 2053 | :metric: false 2054 | :special: false 2055 | :arbitrary: false 2056 | - :names: 2057 | - dram 2058 | - drachm 2059 | :primary_code: "[dr_ap]" 2060 | :secondary_code: "[DR_AP]" 2061 | :scale: 2062 | :value: 3 2063 | :unit_code: "[sc_ap]" 2064 | :classification: apoth 2065 | :property: mass 2066 | :metric: false 2067 | :special: false 2068 | :arbitrary: false 2069 | - :names: ounce 2070 | :primary_code: "[oz_ap]" 2071 | :secondary_code: "[OZ_AP]" 2072 | :scale: 2073 | :value: 8 2074 | :unit_code: "[dr_ap]" 2075 | :classification: apoth 2076 | :property: mass 2077 | :metric: false 2078 | :special: false 2079 | :arbitrary: false 2080 | - :names: pound 2081 | :primary_code: "[lb_ap]" 2082 | :secondary_code: "[LB_AP]" 2083 | :scale: 2084 | :value: 12 2085 | :unit_code: "[oz_ap]" 2086 | :classification: apoth 2087 | :property: mass 2088 | :metric: false 2089 | :special: false 2090 | :arbitrary: false 2091 | - :names: metric ounce 2092 | :primary_code: "[oz_m]" 2093 | :secondary_code: "[OZ_M]" 2094 | :scale: 2095 | :value: 28 2096 | :unit_code: g 2097 | :classification: apoth 2098 | :property: mass 2099 | :metric: false 2100 | :special: false 2101 | :arbitrary: false 2102 | - :names: line 2103 | :primary_code: "[lne]" 2104 | :secondary_code: "[LNE]" 2105 | :scale: 2106 | :value: 1 2107 | :unit_code: "[in_i]/12" 2108 | :classification: typeset 2109 | :property: length 2110 | :metric: false 2111 | :special: false 2112 | :arbitrary: false 2113 | - :names: point 2114 | :primary_code: "[pnt]" 2115 | :secondary_code: "[PNT]" 2116 | :scale: 2117 | :value: 1 2118 | :unit_code: "[lne]/6" 2119 | :classification: typeset 2120 | :property: length 2121 | :metric: false 2122 | :special: false 2123 | :arbitrary: false 2124 | - :names: pica 2125 | :primary_code: "[pca]" 2126 | :secondary_code: "[PCA]" 2127 | :scale: 2128 | :value: 12 2129 | :unit_code: "[pnt]" 2130 | :classification: typeset 2131 | :property: length 2132 | :metric: false 2133 | :special: false 2134 | :arbitrary: false 2135 | - :names: Printer's point 2136 | :primary_code: "[pnt_pr]" 2137 | :secondary_code: "[PNT_PR]" 2138 | :scale: 2139 | :value: !ruby/object:BigDecimal 18:0.13837e-1 2140 | :unit_code: "[in_i]" 2141 | :classification: typeset 2142 | :property: length 2143 | :metric: false 2144 | :special: false 2145 | :arbitrary: false 2146 | - :names: Printer's pica 2147 | :primary_code: "[pca_pr]" 2148 | :secondary_code: "[PCA_PR]" 2149 | :scale: 2150 | :value: 12 2151 | :unit_code: "[pnt_pr]" 2152 | :classification: typeset 2153 | :property: length 2154 | :metric: false 2155 | :special: false 2156 | :arbitrary: false 2157 | - :names: 2158 | - pied 2159 | - French foot 2160 | :primary_code: "[pied]" 2161 | :secondary_code: "[PIED]" 2162 | :scale: 2163 | :value: !ruby/object:BigDecimal 18:0.3248e2 2164 | :unit_code: cm 2165 | :classification: typeset 2166 | :property: length 2167 | :metric: false 2168 | :special: false 2169 | :arbitrary: false 2170 | - :names: 2171 | - pouce 2172 | - French inch 2173 | :primary_code: "[pouce]" 2174 | :secondary_code: "[POUCE]" 2175 | :scale: 2176 | :value: 1 2177 | :unit_code: "[pied]/12" 2178 | :classification: typeset 2179 | :property: length 2180 | :metric: false 2181 | :special: false 2182 | :arbitrary: false 2183 | - :names: 2184 | - ligne 2185 | - French line 2186 | :primary_code: "[ligne]" 2187 | :secondary_code: "[LIGNE]" 2188 | :scale: 2189 | :value: 1 2190 | :unit_code: "[pouce]/12" 2191 | :classification: typeset 2192 | :property: length 2193 | :metric: false 2194 | :special: false 2195 | :arbitrary: false 2196 | - :names: 2197 | - didot 2198 | - Didot's point 2199 | :primary_code: "[didot]" 2200 | :secondary_code: "[DIDOT]" 2201 | :scale: 2202 | :value: 1 2203 | :unit_code: "[ligne]/6" 2204 | :classification: typeset 2205 | :property: length 2206 | :metric: false 2207 | :special: false 2208 | :arbitrary: false 2209 | - :names: 2210 | - cicero 2211 | - Didot's pica 2212 | :primary_code: "[cicero]" 2213 | :secondary_code: "[CICERO]" 2214 | :scale: 2215 | :value: 12 2216 | :unit_code: "[didot]" 2217 | :classification: typeset 2218 | :property: length 2219 | :metric: false 2220 | :special: false 2221 | :arbitrary: false 2222 | - :names: degree Fahrenheit 2223 | :symbol: "°F" 2224 | :primary_code: "[degF]" 2225 | :secondary_code: "[DEGF]" 2226 | :scale: 2227 | :function_code: degf 2228 | :value: 5 2229 | :unit_code: K/9 2230 | :classification: heat 2231 | :property: temperature 2232 | :metric: false 2233 | :special: true 2234 | :arbitrary: false 2235 | - :names: degree Rankine 2236 | :symbol: "°R" 2237 | :primary_code: "[degR]" 2238 | :secondary_code: "[degR]" 2239 | :scale: 2240 | :value: 5 2241 | :unit_code: K/9 2242 | :classification: heat 2243 | :property: temperature 2244 | :metric: false 2245 | :special: false 2246 | :arbitrary: false 2247 | - :names: degree Réaumur 2248 | :symbol: "°Ré" 2249 | :primary_code: "[degRe]" 2250 | :secondary_code: "[degRe]" 2251 | :scale: 2252 | :function_code: degre 2253 | :value: 5 2254 | :unit_code: K/4 2255 | :classification: heat 2256 | :property: temperature 2257 | :metric: false 2258 | :special: true 2259 | :arbitrary: false 2260 | - :names: calorie at 15 °C 2261 | :symbol: cal15°C 2262 | :primary_code: cal_[15] 2263 | :secondary_code: CAL_[15] 2264 | :scale: 2265 | :value: !ruby/object:BigDecimal 18:0.41858e1 2266 | :unit_code: J 2267 | :classification: heat 2268 | :property: energy 2269 | :metric: true 2270 | :special: false 2271 | :arbitrary: false 2272 | - :names: calorie at 20 °C 2273 | :symbol: cal20°C 2274 | :primary_code: cal_[20] 2275 | :secondary_code: CAL_[20] 2276 | :scale: 2277 | :value: !ruby/object:BigDecimal 18:0.41819e1 2278 | :unit_code: J 2279 | :classification: heat 2280 | :property: energy 2281 | :metric: true 2282 | :special: false 2283 | :arbitrary: false 2284 | - :names: mean calorie 2285 | :symbol: calm 2286 | :primary_code: cal_m 2287 | :secondary_code: CAL_M 2288 | :scale: 2289 | :value: !ruby/object:BigDecimal 18:0.419002e1 2290 | :unit_code: J 2291 | :classification: heat 2292 | :property: energy 2293 | :metric: true 2294 | :special: false 2295 | :arbitrary: false 2296 | - :names: international table calorie 2297 | :symbol: calIT 2298 | :primary_code: cal_IT 2299 | :secondary_code: CAL_IT 2300 | :scale: 2301 | :value: !ruby/object:BigDecimal 18:0.41868e1 2302 | :unit_code: J 2303 | :classification: heat 2304 | :property: energy 2305 | :metric: true 2306 | :special: false 2307 | :arbitrary: false 2308 | - :names: thermochemical calorie 2309 | :symbol: calth 2310 | :primary_code: cal_th 2311 | :secondary_code: CAL_TH 2312 | :scale: 2313 | :value: !ruby/object:BigDecimal 18:0.4184e1 2314 | :unit_code: J 2315 | :classification: heat 2316 | :property: energy 2317 | :metric: true 2318 | :special: false 2319 | :arbitrary: false 2320 | - :names: calorie 2321 | :symbol: cal 2322 | :primary_code: cal 2323 | :secondary_code: CAL 2324 | :scale: 2325 | :value: 1 2326 | :unit_code: cal_th 2327 | :classification: heat 2328 | :property: energy 2329 | :metric: true 2330 | :special: false 2331 | :arbitrary: false 2332 | - :names: nutrition label Calories 2333 | :symbol: Cal 2334 | :primary_code: "[Cal]" 2335 | :secondary_code: "[CAL]" 2336 | :scale: 2337 | :value: 1 2338 | :unit_code: kcal_th 2339 | :classification: heat 2340 | :property: energy 2341 | :metric: false 2342 | :special: false 2343 | :arbitrary: false 2344 | - :names: British thermal unit at 39 °F 2345 | :symbol: Btu39°F 2346 | :primary_code: "[Btu_39]" 2347 | :secondary_code: "[BTU_39]" 2348 | :scale: 2349 | :value: !ruby/object:BigDecimal 18:0.105967e1 2350 | :unit_code: kJ 2351 | :classification: heat 2352 | :property: energy 2353 | :metric: false 2354 | :special: false 2355 | :arbitrary: false 2356 | - :names: British thermal unit at 59 °F 2357 | :symbol: Btu59°F 2358 | :primary_code: "[Btu_59]" 2359 | :secondary_code: "[BTU_59]" 2360 | :scale: 2361 | :value: !ruby/object:BigDecimal 18:0.10548e1 2362 | :unit_code: kJ 2363 | :classification: heat 2364 | :property: energy 2365 | :metric: false 2366 | :special: false 2367 | :arbitrary: false 2368 | - :names: British thermal unit at 60 °F 2369 | :symbol: Btu60°F 2370 | :primary_code: "[Btu_60]" 2371 | :secondary_code: "[BTU_60]" 2372 | :scale: 2373 | :value: !ruby/object:BigDecimal 18:0.105468e1 2374 | :unit_code: kJ 2375 | :classification: heat 2376 | :property: energy 2377 | :metric: false 2378 | :special: false 2379 | :arbitrary: false 2380 | - :names: mean British thermal unit 2381 | :symbol: Btum 2382 | :primary_code: "[Btu_m]" 2383 | :secondary_code: "[BTU_M]" 2384 | :scale: 2385 | :value: !ruby/object:BigDecimal 18:0.105587e1 2386 | :unit_code: kJ 2387 | :classification: heat 2388 | :property: energy 2389 | :metric: false 2390 | :special: false 2391 | :arbitrary: false 2392 | - :names: international table British thermal unit 2393 | :symbol: BtuIT 2394 | :primary_code: "[Btu_IT]" 2395 | :secondary_code: "[BTU_IT]" 2396 | :scale: 2397 | :value: !ruby/object:BigDecimal 27:0.105505585262e1 2398 | :unit_code: kJ 2399 | :classification: heat 2400 | :property: energy 2401 | :metric: false 2402 | :special: false 2403 | :arbitrary: false 2404 | - :names: thermochemical British thermal unit 2405 | :symbol: Btuth 2406 | :primary_code: "[Btu_th]" 2407 | :secondary_code: "[BTU_TH]" 2408 | :scale: 2409 | :value: !ruby/object:BigDecimal 18:0.105435e1 2410 | :unit_code: kJ 2411 | :classification: heat 2412 | :property: energy 2413 | :metric: false 2414 | :special: false 2415 | :arbitrary: false 2416 | - :names: British thermal unit 2417 | :symbol: btu 2418 | :primary_code: "[Btu]" 2419 | :secondary_code: "[BTU]" 2420 | :scale: 2421 | :value: 1 2422 | :unit_code: "[Btu_th]" 2423 | :classification: heat 2424 | :property: energy 2425 | :metric: false 2426 | :special: false 2427 | :arbitrary: false 2428 | - :names: horsepower 2429 | :primary_code: "[HP]" 2430 | :secondary_code: "[HP]" 2431 | :scale: 2432 | :value: 550 2433 | :unit_code: "[ft_i].[lbf_av]/s" 2434 | :classification: heat 2435 | :property: power 2436 | :metric: false 2437 | :special: false 2438 | :arbitrary: false 2439 | - :names: tex 2440 | :symbol: tex 2441 | :primary_code: tex 2442 | :secondary_code: TEX 2443 | :scale: 2444 | :value: 1 2445 | :unit_code: g/km 2446 | :classification: heat 2447 | :property: linear mass density (of textile thread) 2448 | :metric: true 2449 | :special: false 2450 | :arbitrary: false 2451 | - :names: Denier 2452 | :symbol: den 2453 | :primary_code: "[den]" 2454 | :secondary_code: "[DEN]" 2455 | :scale: 2456 | :value: 1 2457 | :unit_code: g/9/km 2458 | :classification: heat 2459 | :property: linear mass density (of textile thread) 2460 | :metric: false 2461 | :special: false 2462 | :arbitrary: false 2463 | - :names: meter of water column 2464 | :symbol: m H2O 2465 | :primary_code: m[H2O] 2466 | :secondary_code: M[H2O] 2467 | :scale: 2468 | :value: !ruby/object:BigDecimal 18:0.980665e1 2469 | :unit_code: kPa 2470 | :classification: clinical 2471 | :property: pressure 2472 | :metric: true 2473 | :special: false 2474 | :arbitrary: false 2475 | - :names: meter of mercury column 2476 | :symbol: m Hg 2477 | :primary_code: m[Hg] 2478 | :secondary_code: M[HG] 2479 | :scale: 2480 | :value: !ruby/object:BigDecimal 18:0.133322e3 2481 | :unit_code: kPa 2482 | :classification: clinical 2483 | :property: pressure 2484 | :metric: true 2485 | :special: false 2486 | :arbitrary: false 2487 | - :names: inch of water column 2488 | :symbol: in H2O 2489 | :primary_code: "[in_i'H2O]" 2490 | :secondary_code: "[IN_I'H2O]" 2491 | :scale: 2492 | :value: 1 2493 | :unit_code: m[H2O].[in_i]/m 2494 | :classification: clinical 2495 | :property: pressure 2496 | :metric: false 2497 | :special: false 2498 | :arbitrary: false 2499 | - :names: inch of mercury column 2500 | :symbol: in Hg 2501 | :primary_code: "[in_i'Hg]" 2502 | :secondary_code: "[IN_I'HG]" 2503 | :scale: 2504 | :value: 1 2505 | :unit_code: m[Hg].[in_i]/m 2506 | :classification: clinical 2507 | :property: pressure 2508 | :metric: false 2509 | :special: false 2510 | :arbitrary: false 2511 | - :names: peripheral vascular resistance unit 2512 | :symbol: P.R.U. 2513 | :primary_code: "[PRU]" 2514 | :secondary_code: "[PRU]" 2515 | :scale: 2516 | :value: 1 2517 | :unit_code: mm[Hg].s/ml 2518 | :classification: clinical 2519 | :property: fluid resistance 2520 | :metric: false 2521 | :special: false 2522 | :arbitrary: false 2523 | - :names: Wood unit 2524 | :symbol: Wood U. 2525 | :primary_code: "[wood'U]" 2526 | :secondary_code: "[WOOD'U]" 2527 | :scale: 2528 | :value: 1 2529 | :unit_code: mm[Hg].min/L 2530 | :classification: clinical 2531 | :property: fluid resistance 2532 | :metric: false 2533 | :special: false 2534 | :arbitrary: false 2535 | - :names: diopter 2536 | :symbol: dpt 2537 | :primary_code: "[diop]" 2538 | :secondary_code: "[DIOP]" 2539 | :scale: 2540 | :value: 1 2541 | :unit_code: "/m" 2542 | :classification: clinical 2543 | :property: refraction of a lens 2544 | :metric: false 2545 | :special: false 2546 | :arbitrary: false 2547 | - :names: prism diopter 2548 | :symbol: PD 2549 | :primary_code: "[p'diop]" 2550 | :secondary_code: "[P'DIOP]" 2551 | :scale: 2552 | :function_code: 100tan 2553 | :value: 1 2554 | :unit_code: deg 2555 | :classification: clinical 2556 | :property: refraction of a prism 2557 | :metric: false 2558 | :special: true 2559 | :arbitrary: false 2560 | - :names: percent of slope 2561 | :symbol: "%" 2562 | :primary_code: "%[slope]" 2563 | :secondary_code: "%[SLOPE]" 2564 | :scale: 2565 | :function_code: 100tan 2566 | :value: 1 2567 | :unit_code: deg 2568 | :classification: clinical 2569 | :property: slope 2570 | :metric: false 2571 | :special: true 2572 | :arbitrary: false 2573 | - :names: mesh 2574 | :primary_code: "[mesh_i]" 2575 | :secondary_code: "[MESH_I]" 2576 | :scale: 2577 | :value: 1 2578 | :unit_code: "/[in_i]" 2579 | :classification: clinical 2580 | :property: lineic number 2581 | :metric: false 2582 | :special: false 2583 | :arbitrary: false 2584 | - :names: 2585 | - Charrière 2586 | - french 2587 | :symbol: Ch 2588 | :primary_code: "[Ch]" 2589 | :secondary_code: "[CH]" 2590 | :scale: 2591 | :value: 1 2592 | :unit_code: mm/3 2593 | :classification: clinical 2594 | :property: gauge of catheters 2595 | :metric: false 2596 | :special: false 2597 | :arbitrary: false 2598 | - :names: drop 2599 | :symbol: drp 2600 | :primary_code: "[drp]" 2601 | :secondary_code: "[DRP]" 2602 | :scale: 2603 | :value: 1 2604 | :unit_code: ml/20 2605 | :classification: clinical 2606 | :property: volume 2607 | :metric: false 2608 | :special: false 2609 | :arbitrary: false 2610 | - :names: Hounsfield unit 2611 | :symbol: HF 2612 | :primary_code: "[hnsf'U]" 2613 | :secondary_code: "[HNSF'U]" 2614 | :scale: 2615 | :value: 1 2616 | :unit_code: '1' 2617 | :classification: clinical 2618 | :property: x-ray attenuation 2619 | :metric: false 2620 | :special: false 2621 | :arbitrary: false 2622 | - :names: metabolic equivalent 2623 | :symbol: MET 2624 | :primary_code: "[MET]" 2625 | :secondary_code: "[MET]" 2626 | :scale: 2627 | :value: !ruby/object:BigDecimal 18:0.35e1 2628 | :unit_code: mL/min/kg 2629 | :classification: clinical 2630 | :property: metabolic cost of physical activity 2631 | :metric: false 2632 | :special: false 2633 | :arbitrary: false 2634 | - :names: homeopathic potency of decimal series (retired) 2635 | :symbol: X 2636 | :primary_code: "[hp'_X]" 2637 | :secondary_code: "[HP'_X]" 2638 | :scale: 2639 | :function_code: hpX 2640 | :value: 1 2641 | :unit_code: '1' 2642 | :classification: clinical 2643 | :property: homeopathic potency (retired) 2644 | :metric: false 2645 | :special: true 2646 | :arbitrary: false 2647 | - :names: homeopathic potency of centesimal series (retired) 2648 | :symbol: C 2649 | :primary_code: "[hp'_C]" 2650 | :secondary_code: "[HP'_C]" 2651 | :scale: 2652 | :function_code: hpC 2653 | :value: 1 2654 | :unit_code: '1' 2655 | :classification: clinical 2656 | :property: homeopathic potency (retired) 2657 | :metric: false 2658 | :special: true 2659 | :arbitrary: false 2660 | - :names: homeopathic potency of millesimal series (retired) 2661 | :symbol: M 2662 | :primary_code: "[hp'_M]" 2663 | :secondary_code: "[HP'_M]" 2664 | :scale: 2665 | :function_code: hpM 2666 | :value: 1 2667 | :unit_code: '1' 2668 | :classification: clinical 2669 | :property: homeopathic potency (retired) 2670 | :metric: false 2671 | :special: true 2672 | :arbitrary: false 2673 | - :names: homeopathic potency of quintamillesimal series (retired) 2674 | :symbol: Q 2675 | :primary_code: "[hp'_Q]" 2676 | :secondary_code: "[HP'_Q]" 2677 | :scale: 2678 | :function_code: hpQ 2679 | :value: 1 2680 | :unit_code: '1' 2681 | :classification: clinical 2682 | :property: homeopathic potency (retired) 2683 | :metric: false 2684 | :special: true 2685 | :arbitrary: false 2686 | - :names: homeopathic potency of decimal hahnemannian series 2687 | :symbol: X 2688 | :primary_code: "[hp_X]" 2689 | :secondary_code: "[HP_X]" 2690 | :scale: 2691 | :value: 1 2692 | :unit_code: '1' 2693 | :classification: clinical 2694 | :property: homeopathic potency (Hahnemann) 2695 | :metric: false 2696 | :special: false 2697 | :arbitrary: true 2698 | - :names: homeopathic potency of centesimal hahnemannian series 2699 | :symbol: C 2700 | :primary_code: "[hp_C]" 2701 | :secondary_code: "[HP_C]" 2702 | :scale: 2703 | :value: 1 2704 | :unit_code: '1' 2705 | :classification: clinical 2706 | :property: homeopathic potency (Hahnemann) 2707 | :metric: false 2708 | :special: false 2709 | :arbitrary: true 2710 | - :names: homeopathic potency of millesimal hahnemannian series 2711 | :symbol: M 2712 | :primary_code: "[hp_M]" 2713 | :secondary_code: "[HP_M]" 2714 | :scale: 2715 | :value: 1 2716 | :unit_code: '1' 2717 | :classification: clinical 2718 | :property: homeopathic potency (Hahnemann) 2719 | :metric: false 2720 | :special: false 2721 | :arbitrary: true 2722 | - :names: homeopathic potency of quintamillesimal hahnemannian series 2723 | :symbol: Q 2724 | :primary_code: "[hp_Q]" 2725 | :secondary_code: "[HP_Q]" 2726 | :scale: 2727 | :value: 1 2728 | :unit_code: '1' 2729 | :classification: clinical 2730 | :property: homeopathic potency (Hahnemann) 2731 | :metric: false 2732 | :special: false 2733 | :arbitrary: true 2734 | - :names: homeopathic potency of decimal korsakovian series 2735 | :symbol: X 2736 | :primary_code: "[kp_X]" 2737 | :secondary_code: "[KP_X]" 2738 | :scale: 2739 | :value: 1 2740 | :unit_code: '1' 2741 | :classification: clinical 2742 | :property: homeopathic potency (Korsakov) 2743 | :metric: false 2744 | :special: false 2745 | :arbitrary: true 2746 | - :names: homeopathic potency of centesimal korsakovian series 2747 | :symbol: C 2748 | :primary_code: "[kp_C]" 2749 | :secondary_code: "[KP_C]" 2750 | :scale: 2751 | :value: 1 2752 | :unit_code: '1' 2753 | :classification: clinical 2754 | :property: homeopathic potency (Korsakov) 2755 | :metric: false 2756 | :special: false 2757 | :arbitrary: true 2758 | - :names: homeopathic potency of millesimal korsakovian series 2759 | :symbol: M 2760 | :primary_code: "[kp_M]" 2761 | :secondary_code: "[KP_M]" 2762 | :scale: 2763 | :value: 1 2764 | :unit_code: '1' 2765 | :classification: clinical 2766 | :property: homeopathic potency (Korsakov) 2767 | :metric: false 2768 | :special: false 2769 | :arbitrary: true 2770 | - :names: homeopathic potency of quintamillesimal korsakovian series 2771 | :symbol: Q 2772 | :primary_code: "[kp_Q]" 2773 | :secondary_code: "[KP_Q]" 2774 | :scale: 2775 | :value: 1 2776 | :unit_code: '1' 2777 | :classification: clinical 2778 | :property: homeopathic potency (Korsakov) 2779 | :metric: false 2780 | :special: false 2781 | :arbitrary: true 2782 | - :names: equivalents 2783 | :symbol: eq 2784 | :primary_code: eq 2785 | :secondary_code: EQ 2786 | :scale: 2787 | :value: 1 2788 | :unit_code: mol 2789 | :classification: chemical 2790 | :property: amount of substance 2791 | :metric: true 2792 | :special: false 2793 | :arbitrary: false 2794 | - :names: osmole 2795 | :symbol: osm 2796 | :primary_code: osm 2797 | :secondary_code: OSM 2798 | :scale: 2799 | :value: 1 2800 | :unit_code: mol 2801 | :classification: chemical 2802 | :property: amount of substance (dissolved particles) 2803 | :metric: true 2804 | :special: false 2805 | :arbitrary: false 2806 | - :names: pH 2807 | :symbol: pH 2808 | :primary_code: "[pH]" 2809 | :secondary_code: "[PH]" 2810 | :scale: 2811 | :function_code: pH 2812 | :value: 1 2813 | :unit_code: mol/l 2814 | :classification: chemical 2815 | :property: acidity 2816 | :metric: false 2817 | :special: true 2818 | :arbitrary: false 2819 | - :names: gram percent 2820 | :symbol: g% 2821 | :primary_code: g% 2822 | :secondary_code: G% 2823 | :scale: 2824 | :value: 1 2825 | :unit_code: g/dl 2826 | :classification: chemical 2827 | :property: mass concentration 2828 | :metric: true 2829 | :special: false 2830 | :arbitrary: false 2831 | - :names: Svedberg unit 2832 | :symbol: S 2833 | :primary_code: "[S]" 2834 | :secondary_code: "[S]" 2835 | :scale: 2836 | :value: 1 2837 | :unit_code: 10*-13.s 2838 | :classification: chemical 2839 | :property: sedimentation coefficient 2840 | :metric: false 2841 | :special: false 2842 | :arbitrary: false 2843 | - :names: high power field 2844 | :symbol: HPF 2845 | :primary_code: "[HPF]" 2846 | :secondary_code: "[HPF]" 2847 | :scale: 2848 | :value: 1 2849 | :unit_code: '1' 2850 | :classification: chemical 2851 | :property: view area in microscope 2852 | :metric: false 2853 | :special: false 2854 | :arbitrary: false 2855 | - :names: low power field 2856 | :symbol: LPF 2857 | :primary_code: "[LPF]" 2858 | :secondary_code: "[LPF]" 2859 | :scale: 2860 | :value: 100 2861 | :unit_code: '1' 2862 | :classification: chemical 2863 | :property: view area in microscope 2864 | :metric: false 2865 | :special: false 2866 | :arbitrary: false 2867 | - :names: katal 2868 | :symbol: kat 2869 | :primary_code: kat 2870 | :secondary_code: KAT 2871 | :scale: 2872 | :value: 1 2873 | :unit_code: mol/s 2874 | :classification: chemical 2875 | :property: catalytic activity 2876 | :metric: true 2877 | :special: false 2878 | :arbitrary: false 2879 | - :names: Unit 2880 | :symbol: U 2881 | :primary_code: U 2882 | :secondary_code: U 2883 | :scale: 2884 | :value: 1 2885 | :unit_code: umol/min 2886 | :classification: chemical 2887 | :property: catalytic activity 2888 | :metric: true 2889 | :special: false 2890 | :arbitrary: false 2891 | - :names: international unit 2892 | :symbol: IU 2893 | :primary_code: "[iU]" 2894 | :secondary_code: "[IU]" 2895 | :scale: 2896 | :value: 1 2897 | :unit_code: '1' 2898 | :classification: chemical 2899 | :property: arbitrary 2900 | :metric: true 2901 | :special: false 2902 | :arbitrary: true 2903 | - :names: international unit 2904 | :symbol: i.U. 2905 | :primary_code: "[IU]" 2906 | :secondary_code: "[IU]" 2907 | :scale: 2908 | :value: 1 2909 | :unit_code: "[iU]" 2910 | :classification: chemical 2911 | :property: arbitrary 2912 | :metric: true 2913 | :special: false 2914 | :arbitrary: true 2915 | - :names: arbitary unit 2916 | :symbol: arb. U 2917 | :primary_code: "[arb'U]" 2918 | :secondary_code: "[ARB'U]" 2919 | :scale: 2920 | :value: 1 2921 | :unit_code: '1' 2922 | :classification: chemical 2923 | :property: arbitrary 2924 | :metric: false 2925 | :special: false 2926 | :arbitrary: true 2927 | - :names: United States Pharmacopeia unit 2928 | :symbol: U.S.P. 2929 | :primary_code: "[USP'U]" 2930 | :secondary_code: "[USP'U]" 2931 | :scale: 2932 | :value: 1 2933 | :unit_code: '1' 2934 | :classification: chemical 2935 | :property: arbitrary 2936 | :metric: false 2937 | :special: false 2938 | :arbitrary: true 2939 | - :names: GPL unit 2940 | :primary_code: "[GPL'U]" 2941 | :secondary_code: "[GPL'U]" 2942 | :scale: 2943 | :value: 1 2944 | :unit_code: '1' 2945 | :classification: chemical 2946 | :property: biologic activity of anticardiolipin IgG 2947 | :metric: false 2948 | :special: false 2949 | :arbitrary: true 2950 | - :names: MPL unit 2951 | :primary_code: "[MPL'U]" 2952 | :secondary_code: "[MPL'U]" 2953 | :scale: 2954 | :value: 1 2955 | :unit_code: '1' 2956 | :classification: chemical 2957 | :property: biologic activity of anticardiolipin IgM 2958 | :metric: false 2959 | :special: false 2960 | :arbitrary: true 2961 | - :names: APL unit 2962 | :primary_code: "[APL'U]" 2963 | :secondary_code: "[APL'U]" 2964 | :scale: 2965 | :value: 1 2966 | :unit_code: '1' 2967 | :classification: chemical 2968 | :property: biologic activity of anticardiolipin IgA 2969 | :metric: false 2970 | :special: false 2971 | :arbitrary: true 2972 | - :names: Bethesda unit 2973 | :primary_code: "[beth'U]" 2974 | :secondary_code: "[BETH'U]" 2975 | :scale: 2976 | :value: 1 2977 | :unit_code: '1' 2978 | :classification: chemical 2979 | :property: biologic activity of factor VIII inhibitor 2980 | :metric: false 2981 | :special: false 2982 | :arbitrary: true 2983 | - :names: anti factor Xa unit 2984 | :primary_code: "[anti'Xa'U]" 2985 | :secondary_code: "[ANTI'XA'U]" 2986 | :scale: 2987 | :value: 1 2988 | :unit_code: '1' 2989 | :classification: chemical 2990 | :property: biologic activity of factor Xa inhibitor (heparin) 2991 | :metric: false 2992 | :special: false 2993 | :arbitrary: true 2994 | - :names: Todd unit 2995 | :primary_code: "[todd'U]" 2996 | :secondary_code: "[TODD'U]" 2997 | :scale: 2998 | :value: 1 2999 | :unit_code: '1' 3000 | :classification: chemical 3001 | :property: biologic activity antistreptolysin O 3002 | :metric: false 3003 | :special: false 3004 | :arbitrary: true 3005 | - :names: Dye unit 3006 | :primary_code: "[dye'U]" 3007 | :secondary_code: "[DYE'U]" 3008 | :scale: 3009 | :value: 1 3010 | :unit_code: '1' 3011 | :classification: chemical 3012 | :property: biologic activity of amylase 3013 | :metric: false 3014 | :special: false 3015 | :arbitrary: true 3016 | - :names: Somogyi unit 3017 | :primary_code: "[smgy'U]" 3018 | :secondary_code: "[SMGY'U]" 3019 | :scale: 3020 | :value: 1 3021 | :unit_code: '1' 3022 | :classification: chemical 3023 | :property: biologic activity of amylase 3024 | :metric: false 3025 | :special: false 3026 | :arbitrary: true 3027 | - :names: Bodansky unit 3028 | :primary_code: "[bdsk'U]" 3029 | :secondary_code: "[BDSK'U]" 3030 | :scale: 3031 | :value: 1 3032 | :unit_code: '1' 3033 | :classification: chemical 3034 | :property: biologic activity of phosphatase 3035 | :metric: false 3036 | :special: false 3037 | :arbitrary: true 3038 | - :names: King-Armstrong unit 3039 | :primary_code: "[ka'U]" 3040 | :secondary_code: "[KA'U]" 3041 | :scale: 3042 | :value: 1 3043 | :unit_code: '1' 3044 | :classification: chemical 3045 | :property: biologic activity of phosphatase 3046 | :metric: false 3047 | :special: false 3048 | :arbitrary: true 3049 | - :names: Kunkel unit 3050 | :primary_code: "[knk'U]" 3051 | :secondary_code: "[KNK'U]" 3052 | :scale: 3053 | :value: 1 3054 | :unit_code: '1' 3055 | :classification: chemical 3056 | :property: arbitrary biologic activity 3057 | :metric: false 3058 | :special: false 3059 | :arbitrary: true 3060 | - :names: Mac Lagan unit 3061 | :primary_code: "[mclg'U]" 3062 | :secondary_code: "[MCLG'U]" 3063 | :scale: 3064 | :value: 1 3065 | :unit_code: '1' 3066 | :classification: chemical 3067 | :property: arbitrary biologic activity 3068 | :metric: false 3069 | :special: false 3070 | :arbitrary: true 3071 | - :names: tuberculin unit 3072 | :primary_code: "[tb'U]" 3073 | :secondary_code: "[TB'U]" 3074 | :scale: 3075 | :value: 1 3076 | :unit_code: '1' 3077 | :classification: chemical 3078 | :property: biologic activity of tuberculin 3079 | :metric: false 3080 | :special: false 3081 | :arbitrary: true 3082 | - :names: 50% cell culture infectious dose 3083 | :symbol: CCID50 3084 | :primary_code: "[CCID_50]" 3085 | :secondary_code: "[CCID_50]" 3086 | :scale: 3087 | :value: 1 3088 | :unit_code: '1' 3089 | :classification: chemical 3090 | :property: biologic activity (infectivity) of an infectious agent preparation 3091 | :metric: false 3092 | :special: false 3093 | :arbitrary: true 3094 | - :names: 50% tissue culture infectious dose 3095 | :symbol: TCID50 3096 | :primary_code: "[TCID_50]" 3097 | :secondary_code: "[TCID_50]" 3098 | :scale: 3099 | :value: 1 3100 | :unit_code: '1' 3101 | :classification: chemical 3102 | :property: biologic activity (infectivity) of an infectious agent preparation 3103 | :metric: false 3104 | :special: false 3105 | :arbitrary: true 3106 | - :names: 50% embryo infectious dose 3107 | :symbol: EID50 3108 | :primary_code: "[EID_50]" 3109 | :secondary_code: "[EID_50]" 3110 | :scale: 3111 | :value: 1 3112 | :unit_code: '1' 3113 | :classification: chemical 3114 | :property: biologic activity (infectivity) of an infectious agent preparation 3115 | :metric: false 3116 | :special: false 3117 | :arbitrary: true 3118 | - :names: plaque forming units 3119 | :symbol: PFU 3120 | :primary_code: "[PFU]" 3121 | :secondary_code: "[PFU]" 3122 | :scale: 3123 | :value: 1 3124 | :unit_code: '1' 3125 | :classification: chemical 3126 | :property: amount of an infectious agent 3127 | :metric: false 3128 | :special: false 3129 | :arbitrary: true 3130 | - :names: focus forming units 3131 | :symbol: FFU 3132 | :primary_code: "[FFU]" 3133 | :secondary_code: "[FFU]" 3134 | :scale: 3135 | :value: 1 3136 | :unit_code: '1' 3137 | :classification: chemical 3138 | :property: amount of an infectious agent 3139 | :metric: false 3140 | :special: false 3141 | :arbitrary: true 3142 | - :names: colony forming units 3143 | :symbol: CFU 3144 | :primary_code: "[CFU]" 3145 | :secondary_code: "[CFU]" 3146 | :scale: 3147 | :value: 1 3148 | :unit_code: '1' 3149 | :classification: chemical 3150 | :property: amount of a proliferating organism 3151 | :metric: false 3152 | :special: false 3153 | :arbitrary: true 3154 | - :names: index of reactivity 3155 | :symbol: IR 3156 | :primary_code: "[IR]" 3157 | :secondary_code: "[IR]" 3158 | :scale: 3159 | :value: 1 3160 | :unit_code: '1' 3161 | :classification: chemical 3162 | :property: amount of an allergen callibrated through in-vivo testing using the Stallergenes® 3163 | method. 3164 | :metric: false 3165 | :special: false 3166 | :arbitrary: true 3167 | - :names: bioequivalent allergen unit 3168 | :symbol: BAU 3169 | :primary_code: "[BAU]" 3170 | :secondary_code: "[BAU]" 3171 | :scale: 3172 | :value: 1 3173 | :unit_code: '1' 3174 | :classification: chemical 3175 | :property: amount of an allergen callibrated through in-vivo testing based on the 3176 | ID50EAL method of (intradermal dilution for 50mm sum of erythema diameters 3177 | :metric: false 3178 | :special: false 3179 | :arbitrary: true 3180 | - :names: allergen unit 3181 | :symbol: AU 3182 | :primary_code: "[AU]" 3183 | :secondary_code: "[AU]" 3184 | :scale: 3185 | :value: 1 3186 | :unit_code: '1' 3187 | :classification: chemical 3188 | :property: procedure defined amount of an allergen using some reference standard 3189 | :metric: false 3190 | :special: false 3191 | :arbitrary: true 3192 | - :names: allergen unit for Ambrosia artemisiifolia 3193 | :symbol: Amb a 1 U 3194 | :primary_code: "[Amb'a'1'U]" 3195 | :secondary_code: "[AMB'A'1'U]" 3196 | :scale: 3197 | :value: 1 3198 | :unit_code: '1' 3199 | :classification: chemical 3200 | :property: procedure defined amount of the major allergen of ragweed. 3201 | :metric: false 3202 | :special: false 3203 | :arbitrary: true 3204 | - :names: protein nitrogen unit 3205 | :symbol: PNU 3206 | :primary_code: "[PNU]" 3207 | :secondary_code: "[PNU]" 3208 | :scale: 3209 | :value: 1 3210 | :unit_code: '1' 3211 | :classification: chemical 3212 | :property: procedure defined amount of a protein substance 3213 | :metric: false 3214 | :special: false 3215 | :arbitrary: true 3216 | - :names: Limit of flocculation 3217 | :symbol: Lf 3218 | :primary_code: "[Lf]" 3219 | :secondary_code: "[LF]" 3220 | :scale: 3221 | :value: 1 3222 | :unit_code: '1' 3223 | :classification: chemical 3224 | :property: procedure defined amount of an antigen substance 3225 | :metric: false 3226 | :special: false 3227 | :arbitrary: true 3228 | - :names: D-antigen unit 3229 | :primary_code: "[D'ag'U]" 3230 | :secondary_code: "[D'AG'U]" 3231 | :scale: 3232 | :value: 1 3233 | :unit_code: '1' 3234 | :classification: chemical 3235 | :property: procedure defined amount of a poliomyelitis d-antigen substance 3236 | :metric: false 3237 | :special: false 3238 | :arbitrary: true 3239 | - :names: fibrinogen equivalent unit 3240 | :primary_code: "[FEU]" 3241 | :secondary_code: "[FEU]" 3242 | :scale: 3243 | :value: 1 3244 | :unit_code: '1' 3245 | :classification: chemical 3246 | :property: amount of fibrinogen broken down into the measured d-dimers 3247 | :metric: false 3248 | :special: false 3249 | :arbitrary: true 3250 | - :names: ELISA unit 3251 | :primary_code: "[ELU]" 3252 | :secondary_code: "[ELU]" 3253 | :scale: 3254 | :value: 1 3255 | :unit_code: '1' 3256 | :classification: chemical 3257 | :property: arbitrary ELISA unit 3258 | :metric: false 3259 | :special: false 3260 | :arbitrary: true 3261 | - :names: Ehrlich unit 3262 | :primary_code: "[EU]" 3263 | :secondary_code: "[EU]" 3264 | :scale: 3265 | :value: 1 3266 | :unit_code: '1' 3267 | :classification: chemical 3268 | :property: Ehrlich unit 3269 | :metric: false 3270 | :special: false 3271 | :arbitrary: true 3272 | - :names: neper 3273 | :symbol: Np 3274 | :primary_code: Np 3275 | :secondary_code: NEP 3276 | :scale: 3277 | :function_code: ln 3278 | :value: 1 3279 | :unit_code: '1' 3280 | :classification: levels 3281 | :property: level 3282 | :metric: true 3283 | :special: true 3284 | :arbitrary: false 3285 | - :names: bel 3286 | :symbol: B 3287 | :primary_code: B 3288 | :secondary_code: B 3289 | :scale: 3290 | :function_code: lg 3291 | :value: 1 3292 | :unit_code: '1' 3293 | :classification: levels 3294 | :property: level 3295 | :metric: true 3296 | :special: true 3297 | :arbitrary: false 3298 | - :names: bel sound pressure 3299 | :symbol: B(SPL) 3300 | :primary_code: B[SPL] 3301 | :secondary_code: B[SPL] 3302 | :scale: 3303 | :function_code: 2lg 3304 | :value: 2 3305 | :unit_code: 10*-5.Pa 3306 | :classification: levels 3307 | :property: pressure level 3308 | :metric: true 3309 | :special: true 3310 | :arbitrary: false 3311 | - :names: bel volt 3312 | :symbol: B(V) 3313 | :primary_code: B[V] 3314 | :secondary_code: B[V] 3315 | :scale: 3316 | :function_code: 2lg 3317 | :value: 1 3318 | :unit_code: V 3319 | :classification: levels 3320 | :property: electric potential level 3321 | :metric: true 3322 | :special: true 3323 | :arbitrary: false 3324 | - :names: bel millivolt 3325 | :symbol: B(mV) 3326 | :primary_code: B[mV] 3327 | :secondary_code: B[MV] 3328 | :scale: 3329 | :function_code: 2lg 3330 | :value: 1 3331 | :unit_code: mV 3332 | :classification: levels 3333 | :property: electric potential level 3334 | :metric: true 3335 | :special: true 3336 | :arbitrary: false 3337 | - :names: bel microvolt 3338 | :symbol: B(μV) 3339 | :primary_code: B[uV] 3340 | :secondary_code: B[UV] 3341 | :scale: 3342 | :function_code: 2lg 3343 | :value: 1 3344 | :unit_code: uV 3345 | :classification: levels 3346 | :property: electric potential level 3347 | :metric: true 3348 | :special: true 3349 | :arbitrary: false 3350 | - :names: bel 10 nanovolt 3351 | :symbol: B(10 nV) 3352 | :primary_code: B[10.nV] 3353 | :secondary_code: B[10.NV] 3354 | :scale: 3355 | :function_code: 2lg 3356 | :value: 10 3357 | :unit_code: nV 3358 | :classification: levels 3359 | :property: electric potential level 3360 | :metric: true 3361 | :special: true 3362 | :arbitrary: false 3363 | - :names: bel watt 3364 | :symbol: B(W) 3365 | :primary_code: B[W] 3366 | :secondary_code: B[W] 3367 | :scale: 3368 | :function_code: lg 3369 | :value: 1 3370 | :unit_code: W 3371 | :classification: levels 3372 | :property: power level 3373 | :metric: true 3374 | :special: true 3375 | :arbitrary: false 3376 | - :names: bel kilowatt 3377 | :symbol: B(kW) 3378 | :primary_code: B[kW] 3379 | :secondary_code: B[KW] 3380 | :scale: 3381 | :function_code: lg 3382 | :value: 1 3383 | :unit_code: kW 3384 | :classification: levels 3385 | :property: power level 3386 | :metric: true 3387 | :special: true 3388 | :arbitrary: false 3389 | - :names: stere 3390 | :symbol: st 3391 | :primary_code: st 3392 | :secondary_code: STR 3393 | :scale: 3394 | :value: 1 3395 | :unit_code: m3 3396 | :classification: misc 3397 | :property: volume 3398 | :metric: true 3399 | :special: false 3400 | :arbitrary: false 3401 | - :names: Ångström 3402 | :symbol: Å 3403 | :primary_code: Ao 3404 | :secondary_code: AO 3405 | :scale: 3406 | :value: !ruby/object:BigDecimal 18:0.1e0 3407 | :unit_code: nm 3408 | :classification: misc 3409 | :property: length 3410 | :metric: false 3411 | :special: false 3412 | :arbitrary: false 3413 | - :names: barn 3414 | :symbol: b 3415 | :primary_code: b 3416 | :secondary_code: BRN 3417 | :scale: 3418 | :value: 100 3419 | :unit_code: fm2 3420 | :classification: misc 3421 | :property: action area 3422 | :metric: false 3423 | :special: false 3424 | :arbitrary: false 3425 | - :names: technical atmosphere 3426 | :symbol: at 3427 | :primary_code: att 3428 | :secondary_code: ATT 3429 | :scale: 3430 | :value: 1 3431 | :unit_code: kgf/cm2 3432 | :classification: misc 3433 | :property: pressure 3434 | :metric: false 3435 | :special: false 3436 | :arbitrary: false 3437 | - :names: mho 3438 | :symbol: mho 3439 | :primary_code: mho 3440 | :secondary_code: MHO 3441 | :scale: 3442 | :value: 1 3443 | :unit_code: S 3444 | :classification: misc 3445 | :property: electric conductance 3446 | :metric: true 3447 | :special: false 3448 | :arbitrary: false 3449 | - :names: pound per sqare inch 3450 | :symbol: psi 3451 | :primary_code: "[psi]" 3452 | :secondary_code: "[PSI]" 3453 | :scale: 3454 | :value: 1 3455 | :unit_code: "[lbf_av]/[in_i]2" 3456 | :classification: misc 3457 | :property: pressure 3458 | :metric: false 3459 | :special: false 3460 | :arbitrary: false 3461 | - :names: circle 3462 | :symbol: circ 3463 | :primary_code: circ 3464 | :secondary_code: CIRC 3465 | :scale: 3466 | :value: 2 3467 | :unit_code: "[pi].rad" 3468 | :classification: misc 3469 | :property: plane angle 3470 | :metric: false 3471 | :special: false 3472 | :arbitrary: false 3473 | - :names: spere 3474 | :symbol: sph 3475 | :primary_code: sph 3476 | :secondary_code: SPH 3477 | :scale: 3478 | :value: 4 3479 | :unit_code: "[pi].sr" 3480 | :classification: misc 3481 | :property: solid angle 3482 | :metric: false 3483 | :special: false 3484 | :arbitrary: false 3485 | - :names: metric carat 3486 | :symbol: ctm 3487 | :primary_code: "[car_m]" 3488 | :secondary_code: "[CAR_M]" 3489 | :scale: 3490 | :value: !ruby/object:BigDecimal 18:0.2e0 3491 | :unit_code: g 3492 | :classification: misc 3493 | :property: mass 3494 | :metric: false 3495 | :special: false 3496 | :arbitrary: false 3497 | - :names: carat of gold alloys 3498 | :symbol: ctAu 3499 | :primary_code: "[car_Au]" 3500 | :secondary_code: "[CAR_AU]" 3501 | :scale: 3502 | :value: 1 3503 | :unit_code: "/24" 3504 | :classification: misc 3505 | :property: mass fraction 3506 | :metric: false 3507 | :special: false 3508 | :arbitrary: false 3509 | - :names: Smoot 3510 | :primary_code: "[smoot]" 3511 | :secondary_code: "[SMOOT]" 3512 | :scale: 3513 | :value: 67 3514 | :unit_code: "[in_i]" 3515 | :classification: misc 3516 | :property: length 3517 | :metric: false 3518 | :special: false 3519 | :arbitrary: false 3520 | - :names: meter per square seconds per square root of hertz 3521 | :primary_code: "[m/s2/Hz^(1/2)]" 3522 | :secondary_code: "[M/S2/HZ^(1/2)]" 3523 | :scale: 3524 | :function_code: sqrt 3525 | :value: 1 3526 | :unit_code: m2/s4/Hz 3527 | :classification: misc 3528 | :property: amplitude spectral density 3529 | :metric: false 3530 | :special: true 3531 | :arbitrary: false 3532 | - :names: bit 3533 | :symbol: bits 3534 | :primary_code: bit_s 3535 | :secondary_code: BIT_S 3536 | :scale: 3537 | :function_code: ld 3538 | :value: 1 3539 | :unit_code: '1' 3540 | :classification: infotech 3541 | :property: amount of information 3542 | :metric: false 3543 | :special: true 3544 | :arbitrary: false 3545 | - :names: bit 3546 | :symbol: bit 3547 | :primary_code: bit 3548 | :secondary_code: BIT 3549 | :scale: 3550 | :value: 1 3551 | :unit_code: '1' 3552 | :classification: infotech 3553 | :property: amount of information 3554 | :metric: true 3555 | :special: false 3556 | :arbitrary: false 3557 | - :names: byte 3558 | :symbol: B 3559 | :primary_code: By 3560 | :secondary_code: BY 3561 | :scale: 3562 | :value: 8 3563 | :unit_code: bit 3564 | :classification: infotech 3565 | :property: amount of information 3566 | :metric: true 3567 | :special: false 3568 | :arbitrary: false 3569 | - :names: baud 3570 | :symbol: Bd 3571 | :primary_code: Bd 3572 | :secondary_code: BD 3573 | :scale: 3574 | :value: 1 3575 | :unit_code: "/s" 3576 | :classification: infotech 3577 | :property: signal transmission rate 3578 | :metric: true 3579 | :special: false 3580 | :arbitrary: false 3581 | -------------------------------------------------------------------------------- /data/prefix.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - :names: yotta 3 | :symbol: Y 4 | :primary_code: Y 5 | :secondary_code: YA 6 | :scalar: 1000000000000000000000000 7 | - :names: zetta 8 | :symbol: Z 9 | :primary_code: Z 10 | :secondary_code: ZA 11 | :scalar: 1000000000000000000000 12 | - :names: exa 13 | :symbol: E 14 | :primary_code: E 15 | :secondary_code: EX 16 | :scalar: 1000000000000000000 17 | - :names: peta 18 | :symbol: P 19 | :primary_code: P 20 | :secondary_code: PT 21 | :scalar: 1000000000000000 22 | - :names: tera 23 | :symbol: T 24 | :primary_code: T 25 | :secondary_code: TR 26 | :scalar: 1000000000000 27 | - :names: giga 28 | :symbol: G 29 | :primary_code: G 30 | :secondary_code: GA 31 | :scalar: 1000000000 32 | - :names: mega 33 | :symbol: M 34 | :primary_code: M 35 | :secondary_code: MA 36 | :scalar: 1000000 37 | - :names: kilo 38 | :symbol: k 39 | :primary_code: k 40 | :secondary_code: K 41 | :scalar: 1000 42 | - :names: hecto 43 | :symbol: h 44 | :primary_code: h 45 | :secondary_code: H 46 | :scalar: 100 47 | - :names: deka 48 | :symbol: da 49 | :primary_code: da 50 | :secondary_code: DA 51 | :scalar: 10 52 | - :names: deci 53 | :symbol: d 54 | :primary_code: d 55 | :secondary_code: D 56 | :scalar: !ruby/object:BigDecimal 18:0.1e0 57 | - :names: centi 58 | :symbol: c 59 | :primary_code: c 60 | :secondary_code: C 61 | :scalar: !ruby/object:BigDecimal 18:0.1e-1 62 | - :names: milli 63 | :symbol: m 64 | :primary_code: m 65 | :secondary_code: M 66 | :scalar: !ruby/object:BigDecimal 18:0.1e-2 67 | - :names: micro 68 | :symbol: μ 69 | :primary_code: u 70 | :secondary_code: U 71 | :scalar: !ruby/object:BigDecimal 18:0.1e-5 72 | - :names: nano 73 | :symbol: n 74 | :primary_code: n 75 | :secondary_code: N 76 | :scalar: !ruby/object:BigDecimal 18:0.1e-8 77 | - :names: pico 78 | :symbol: p 79 | :primary_code: p 80 | :secondary_code: P 81 | :scalar: !ruby/object:BigDecimal 18:0.1e-11 82 | - :names: femto 83 | :symbol: f 84 | :primary_code: f 85 | :secondary_code: F 86 | :scalar: !ruby/object:BigDecimal 18:0.1e-14 87 | - :names: atto 88 | :symbol: a 89 | :primary_code: a 90 | :secondary_code: A 91 | :scalar: !ruby/object:BigDecimal 18:0.1e-17 92 | - :names: zepto 93 | :symbol: z 94 | :primary_code: z 95 | :secondary_code: ZO 96 | :scalar: !ruby/object:BigDecimal 18:0.1e-20 97 | - :names: yocto 98 | :symbol: y 99 | :primary_code: y 100 | :secondary_code: YO 101 | :scalar: !ruby/object:BigDecimal 18:0.1e-23 102 | - :names: kibi 103 | :symbol: Ki 104 | :primary_code: Ki 105 | :secondary_code: KIB 106 | :scalar: 1024 107 | - :names: mebi 108 | :symbol: Mi 109 | :primary_code: Mi 110 | :secondary_code: MIB 111 | :scalar: 1048576 112 | - :names: gibi 113 | :symbol: Gi 114 | :primary_code: Gi 115 | :secondary_code: GIB 116 | :scalar: 1073741824 117 | - :names: tebi 118 | :symbol: Ti 119 | :primary_code: Ti 120 | :secondary_code: TIB 121 | :scalar: 1099511627776 122 | -------------------------------------------------------------------------------- /lib/unitwise.rb: -------------------------------------------------------------------------------- 1 | require 'liner' 2 | require 'memoizable' 3 | require 'parslet' 4 | require 'signed_multiset' 5 | require 'yaml' 6 | require 'bigdecimal' 7 | 8 | require 'unitwise/version' 9 | require 'unitwise/base' 10 | require 'unitwise/compatible' 11 | require 'unitwise/number' 12 | require 'unitwise/expression' 13 | require 'unitwise/scale' 14 | require 'unitwise/functional' 15 | require 'unitwise/measurement' 16 | require 'unitwise/atom' 17 | require 'unitwise/prefix' 18 | require 'unitwise/term' 19 | require 'unitwise/unit' 20 | require 'unitwise/search' 21 | require 'unitwise/errors' 22 | 23 | # Unitwise is a library for performing mathematical operations and conversions 24 | # on all units defined by the [Unified Code for Units of Measure(UCUM). 25 | module Unitwise 26 | 27 | # Search for available compounds. This is just a helper method for 28 | # convenience 29 | # @param term [String, Regexp] 30 | # @return [Array] 31 | # @api public 32 | def self.search(term) 33 | Search.search(term) 34 | end 35 | 36 | # Determine if a given string is a valid unit expression 37 | # @param expression [String] 38 | # @return [true, false] 39 | # @api public 40 | def self.valid?(expression) 41 | begin 42 | !!Unitwise::Expression.decompose(expression) 43 | rescue ExpressionError 44 | false 45 | end 46 | end 47 | 48 | # Add additional atoms. Useful for registering uncommon or custom units. 49 | # @param properties [Hash] Properties of the atom 50 | # @return [Unitwise::Atom] The newly created atom 51 | # @raise [Unitwise::DefinitionError] 52 | def self.register(atom_hash) 53 | atom = Unitwise::Atom.new(atom_hash) 54 | atom.validate! 55 | Unitwise::Atom.all.push(atom) 56 | Unitwise::Expression::Decomposer.send(:reset) 57 | atom 58 | end 59 | 60 | # The system path for the installed gem 61 | # @api private 62 | def self.path 63 | @path ||= File.dirname(File.dirname(__FILE__)) 64 | end 65 | 66 | # A helper to get the location of a yaml data file 67 | # @api private 68 | def self.data_file(key) 69 | File.join path, 'data', "#{key}.yaml" 70 | end 71 | end 72 | 73 | # Measurement initializer shorthand. Use this to instantiate new measurements. 74 | # @param first [Numeric, String] Either a numeric value or a unit expression 75 | # @param last [String, Nil] Either a unit expression, or nil 76 | # @return [Unitwise::Measurement] 77 | # @example 78 | # Unitwise(20, 'mile') # => # 79 | # Unitwise('km') # => # 80 | # @api public 81 | def Unitwise(*args) 82 | Unitwise::Measurement.new(*args) 83 | end 84 | -------------------------------------------------------------------------------- /lib/unitwise/atom.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # Atoms are the most basic elements in Unitwise. They are named coded and 3 | # scaled units without prefixes, multipliers, exponents, etc. Examples are 4 | # 'meter', 'hour', 'pound force'. 5 | class Atom < Base 6 | liner :classification, :property, :metric, :special, :arbitrary, :dim 7 | include Compatible 8 | 9 | class << self 10 | # Array of hashes representing default atom properties. 11 | # @api private 12 | def data 13 | @data ||= begin 14 | properties = data_files.map do |file| 15 | f = File.open(file) 16 | ::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(f) : ::YAML.load(f) 17 | end 18 | properties.flatten 19 | end 20 | end 21 | 22 | # Data files containing default atom data 23 | # @api private 24 | def data_files 25 | %w(base_unit derived_unit).map { |type| Unitwise.data_file type } 26 | end 27 | end 28 | 29 | # Determine if an atom is base level. All atoms that are not base are 30 | # defined directly or indirectly in reference to a base atom. 31 | # @return [true, false] 32 | # @api public 33 | def base? 34 | !!(@dim && !scale) 35 | end 36 | 37 | # Determine if an atom is derived. Derived atoms are defined with respect 38 | # to other atoms. 39 | # @return [true, false] 40 | # @api public 41 | def derived? 42 | !base? 43 | end 44 | 45 | # Determine if an atom is metric. Metric atoms can be combined with metric 46 | # prefixes. 47 | # @return [true, false] 48 | # @api public 49 | def metric 50 | base? ? true : !!@metric 51 | end 52 | alias_method :metric?, :metric 53 | 54 | # Determine if a unit is special. Special atoms are not defined on a 55 | # traditional ratio scale. 56 | # @return [true, false] 57 | # @api public 58 | def special 59 | !!@special 60 | end 61 | alias_method :special?, :special 62 | 63 | # Determine if a unit is arbitrary. Arbitrary atoms are not of any specific 64 | # dimension and have no general meaning, therefore cannot be compared with 65 | # any other unit. 66 | # @return [true, false] 67 | # @api public 68 | def arbitrary 69 | !!@arbitrary 70 | end 71 | alias_method :arbitrary?, :arbitrary 72 | 73 | # Determine how far away a unit is from a base unit. 74 | # @return [Integer] 75 | # @api public 76 | def depth 77 | base? ? 0 : scale.depth + 1 78 | end 79 | memoize :depth 80 | 81 | # Determine if this is the last atom in the scale chain 82 | # @return [true, false] 83 | # @api public 84 | def terminal? 85 | depth <= 3 86 | end 87 | 88 | # A representation of an atoms composition. Used to determine if two 89 | # different atoms are compatible. 90 | # @return [String] 91 | # @api public 92 | def dim 93 | terminal? ? @dim || property : composition_string 94 | end 95 | 96 | # Set the atom's scale. It can be set as a Scale or a Functional 97 | # @return [Unitwise::Functional, Unitwise::Scale] 98 | # @api public 99 | def scale=(attrs) 100 | @scale = if attrs[:function_code] 101 | Functional.new(attrs[:value], attrs[:unit_code], attrs[:function_code]) 102 | else 103 | Scale.new(attrs[:value], attrs[:unit_code]) 104 | end 105 | end 106 | 107 | # Get a numeric value that can be used to with other atoms to compare with 108 | # or operate on. Base units have a scalar of 1. 109 | # @return [Numeric] 110 | # @api public 111 | def scalar(magnitude = 1) 112 | base? ? magnitude : scale.scalar(magnitude) 113 | end 114 | 115 | def magnitude(scalar = scalar()) 116 | special? ? scale.magnitude(scalar) : 1 117 | end 118 | 119 | # An atom may have a complex scale with several base atoms at various 120 | # depths. This method returns all of this atoms base level terms. 121 | # @return [Array] An array containing base Unitwise::Term 122 | def root_terms 123 | base? ? [Term.new(:atom_code => primary_code)] : scale.root_terms 124 | end 125 | memoize :root_terms 126 | 127 | 128 | # A basic validator for atoms. It checks for the bare minimum properties 129 | # and that it's scalar and magnitude can be resolved. Note that this method 130 | # requires the units it depends on to already exist, so it is not used 131 | # when loading the initial data from UCUM. 132 | # @return [true] returns true if the atom is valid 133 | # @raise [Unitwise::DefinitionError] 134 | def validate! 135 | missing_properties = %i{primary_code names scale}.select do |prop| 136 | val = liner_get(prop) 137 | val.nil? || (val.respond_to?(:empty) && val.empty?) 138 | end 139 | 140 | if !missing_properties.empty? 141 | missing_list = missing_properties.join(',') 142 | raise Unitwise::DefinitionError, 143 | "Atom has missing properties: #{missing_list}." 144 | end 145 | 146 | msg = "Atom definition could not be resolved. Ensure that it is a base " \ 147 | "unit or is defined relative to existing units." 148 | 149 | begin 150 | !scalar.nil? && !magnitude.nil? || raise(Unitwise::DefinitionError, msg) 151 | rescue Unitwise::ExpressionError 152 | raise Unitwise::DefinitionError, msg 153 | end 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /lib/unitwise/base.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # The base class that Atom and Prefix are extended from. This class provides 3 | # shared functionality for said classes. 4 | class Base 5 | liner :names, :primary_code, :secondary_code, :symbol, :scale 6 | include Memoizable 7 | 8 | # The list of tracked items. 9 | # @return [Array] An array of memoized instances. 10 | # @api public 11 | def self.all 12 | @all ||= data.map { |d| new d } 13 | end 14 | 15 | # Find a matching instance by a specified attribute. 16 | # @param string [String] The search term 17 | # @param method [Symbol] The attribute to search by 18 | # @return The first matching instance 19 | # @example 20 | # Unitwise::Atom.find('m') 21 | # @api public 22 | def self.find(string, method = :primary_code) 23 | all.find do |i| 24 | key = i.send(method) 25 | if key.is_a? Array 26 | key.include?(string) 27 | else 28 | key == string 29 | end 30 | end 31 | end 32 | 33 | # Setter for the names attribute. Will always set as an array. 34 | # @api semipublic 35 | def names=(names) 36 | @names = Array(names) 37 | end 38 | 39 | # A set of method friendly names. 40 | # @return [Array] An array of strings 41 | # @api semipublic 42 | def slugs 43 | names.map do |n| 44 | n.downcase.strip.gsub(/\s/, '_').gsub(/\W/, '') 45 | end 46 | end 47 | memoize :slugs 48 | 49 | # String representation for the instance. 50 | # @param mode [symbol] The attribute to for stringification 51 | # @return [String] 52 | # @api public 53 | def to_s(mode = :primary_code) 54 | res = send(mode) || primary_code 55 | res.respond_to?(:each) ? res.first.to_s : res.to_s 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/unitwise/compatible.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # Compatible is used to establish compatibility between units, terms, or 3 | # measurements. This is done by determining the objects atomic composition 4 | # represented as a Signed Multiset. 5 | module Compatible 6 | # @api private 7 | def self.included(base) 8 | base.send :include, Comparable 9 | base.send :include, Memoizable unless base < Memoizable 10 | base.send :memoize, :composition, :composition_string 11 | end 12 | 13 | def initialize(*args) 14 | super(*args) 15 | freeze 16 | end 17 | 18 | # A representation of a unit based on the atoms it's derived from. 19 | # @return [SignedMultiset] 20 | # @api public 21 | def composition 22 | root_terms.reduce(SignedMultiset.new) do |s, t| 23 | s.increment(t.atom.dim, t.exponent) if t.atom 24 | s 25 | end 26 | end 27 | 28 | # Define a default #dim for included classes. 29 | # @return [String] 30 | # @api public 31 | def dim 32 | composition_string 33 | end 34 | 35 | # A string representation of a unit based on the atoms it's derived from 36 | # @return [String] 37 | # @api public 38 | def composition_string 39 | composition.sort.map do |k, v| 40 | v == 1 ? k.to_s : "#{k}#{v}" 41 | end.join('.') 42 | end 43 | 44 | # Determine if this instance is similar to or compatible with other 45 | # @return [true false] 46 | # @api public 47 | def compatible_with?(other) 48 | composition == other.composition 49 | end 50 | 51 | # Compare whether the instance is greater, less than or equal to other. 52 | # @return [-1 0 1] 53 | # @api public 54 | def <=>(other) 55 | if other.respond_to?(:composition) && compatible_with?(other) 56 | scalar <=> other.scalar 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/unitwise/errors.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | class ExpressionError < StandardError 3 | end 4 | 5 | class ConversionError < StandardError 6 | end 7 | 8 | class DefinitionError < StandardError 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/unitwise/expression.rb: -------------------------------------------------------------------------------- 1 | require 'unitwise/expression/matcher' 2 | require 'unitwise/expression/parser' 3 | require 'unitwise/expression/transformer' 4 | require 'unitwise/expression/composer' 5 | require 'unitwise/expression/decomposer' 6 | 7 | module Unitwise 8 | # The Expression module encompases all functions around encoding and decoding 9 | # strings into Measurement::Units and vice-versa. 10 | module Expression 11 | class << self 12 | # Build a string representation of a collection of terms 13 | # @param terms [Array] 14 | # @return [String] 15 | # @example 16 | # Unitwise::Expression.compose(terms) # => "m2/s2" 17 | # @api public 18 | def compose(terms, method = :primary_code) 19 | Composer.new(terms, method).expression 20 | end 21 | 22 | # Convert a string representation of a unit into an array of terms 23 | # @param expression [String] The string you wish to convert 24 | # @return [Array] 25 | # @example 26 | # Unitwise::Expression.decompose("m2/s2") 27 | # # => [, ] 28 | # @api public 29 | def decompose(expression) 30 | Decomposer.parse(expression) 31 | end 32 | 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/unitwise/expression/composer.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | module Expression 3 | # Composer creates string expressions for arrays of terms, following 4 | # UCUM's conventions. 5 | class Composer 6 | attr_reader :terms, :mode 7 | def initialize(terms, mode) 8 | @terms = terms 9 | @mode = mode || :primary_code 10 | end 11 | 12 | def set 13 | @set ||= terms.reduce(SignedMultiset.new) do |s, t| 14 | identifier = { :f => t.factor, 15 | :p => (t.prefix.to_s(mode) if t.prefix), 16 | :a => (t.atom.to_s(mode) if t.atom) } 17 | s.increment(identifier, t.exponent); s 18 | end 19 | end 20 | 21 | def numerator 22 | @numerator ||= set.select{ |_, v| v > 0 }.map do |k, v| 23 | "#{ k[:f] if k[:f] != 1 }#{ k[:p] }#{ k[:a] }#{ v if v != 1 }" 24 | end.select { |t| !t.empty? }.join('.') 25 | end 26 | 27 | def denominator 28 | @denominator ||= set.select{ |_, v| v < 0 }.map do |k, v| 29 | "#{ k[:f] if k[:f] != 1 }#{ k[:p] }#{ k[:a] }#{ -v if v != -1 }" 30 | end.select { |t| !t.empty? }.join('.') 31 | end 32 | 33 | def expression 34 | @expression = [] 35 | @expression << (numerator.empty? ? '1' : numerator) 36 | (@expression << denominator) unless denominator.empty? 37 | @expression.join('/') 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/unitwise/expression/decomposer.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | module Expression 3 | # The decomposer is used to turn string expressions into collections of 4 | # terms. It is responsible for executing the parsing and transformation 5 | # of a string, as well as caching the results. 6 | class Decomposer 7 | 8 | MODES = [:primary_code, :secondary_code, :names, :slugs, :symbol].freeze 9 | 10 | class << self 11 | 12 | # Parse an expression to an array of terms and cache the results 13 | def parse(expression) 14 | expression = expression.to_s 15 | if cache.key?(expression) 16 | cache[expression] 17 | elsif decomposer = new(expression) 18 | cache[expression] = decomposer 19 | end 20 | end 21 | 22 | def parsers 23 | @parsers ||= MODES.reduce({}) do |hash, mode| 24 | hash[mode] = Parser.new(mode); hash 25 | end 26 | end 27 | 28 | def transformer 29 | @transformer = Transformer.new 30 | end 31 | 32 | private 33 | 34 | # A simple cache to prevent re-decomposing the same units 35 | # api private 36 | def cache 37 | @cache ||= {} 38 | end 39 | 40 | # Reset memoized data. Allows rebuilding of parsers, transformers, and 41 | # the cache after list of atoms has been modified. 42 | def reset 43 | @parsers = nil 44 | @transformer = nil 45 | @cache = nil 46 | end 47 | end 48 | 49 | attr_reader :expression, :mode 50 | 51 | def initialize(expression) 52 | @expression = expression.to_s 53 | if expression.empty? || terms.nil? || terms.empty? 54 | fail(ExpressionError, "Could not evaluate '#{ expression }'.") 55 | end 56 | end 57 | 58 | def parse 59 | self.class.parsers.reduce(nil) do |_, (mode, parser)| 60 | parsed = parser.parse(expression) rescue next 61 | @mode = mode 62 | break parsed 63 | end 64 | end 65 | 66 | def transform 67 | @transform ||= self.class.transformer.apply(parse, :mode => mode) 68 | end 69 | 70 | def terms 71 | @terms ||= if transform.respond_to?(:terms) 72 | transform.terms 73 | else 74 | Array(transform) 75 | end 76 | end 77 | 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/unitwise/expression/matcher.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | module Expression 3 | # Matcher is responsible for building up Parslet alternatives of atoms and 4 | # prefixes to be used by Unitwise::Expression::Parser. 5 | class Matcher 6 | class << self 7 | def atom(mode) 8 | new(Atom.all, mode).alternative 9 | end 10 | 11 | def metric_atom(mode) 12 | new(Atom.all.select(&:metric?), mode).alternative 13 | end 14 | 15 | def prefix(mode) 16 | new(Prefix.all, mode).alternative 17 | end 18 | end 19 | 20 | attr_reader :collection, :mode 21 | 22 | def initialize(collection, mode = :primary_code) 23 | @collection = collection 24 | @mode = mode 25 | end 26 | 27 | def strings 28 | collection.map(&mode).flatten.compact.sort do |x, y| 29 | y.length <=> x.length 30 | end 31 | end 32 | 33 | def matchers 34 | strings.map { |s| Parslet::Atoms::Str.new(s) } 35 | end 36 | 37 | def alternative 38 | Parslet::Atoms::Alternative.new(*matchers) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/unitwise/expression/parser.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | module Expression 3 | # Parses a string expression into a hash tree representing the 4 | # expression's terms, prefixes, and atoms. 5 | class Parser < Parslet::Parser 6 | attr_reader :key 7 | def initialize(key = :primary_code) 8 | @key = key 9 | @atom_matcher = Matcher.atom(key) 10 | @metric_atom_matcher = Matcher.metric_atom(key) 11 | @prefix_matcher = Matcher.prefix(key) 12 | end 13 | 14 | private 15 | 16 | attr_reader :atom_matcher, :metric_atom_matcher, :prefix_matcher 17 | 18 | root :expression 19 | 20 | rule (:atom) { atom_matcher.as(:atom_code) } 21 | rule (:metric_atom) { metric_atom_matcher.as(:atom_code) } 22 | rule (:prefix) { prefix_matcher.as(:prefix_code) } 23 | 24 | rule (:simpleton) do 25 | (prefix.as(:prefix) >> metric_atom.as(:atom) | atom.as(:atom)) 26 | end 27 | 28 | rule (:annotation) do 29 | str('{') >> match['^}'].repeat.as(:annotation) >> str('}') 30 | end 31 | 32 | rule (:digits) { match['0-9'].repeat(1) } 33 | 34 | rule (:integer) { (str('-').maybe >> digits).as(:integer) } 35 | 36 | rule (:fixnum) do 37 | (str('-').maybe >> digits >> str('.') >> digits).as(:fixnum) 38 | end 39 | 40 | rule (:number) { fixnum | integer } 41 | 42 | rule (:exponent) { integer.as(:exponent) } 43 | 44 | rule (:factor) { number.as(:factor) } 45 | 46 | rule (:operator) { (str('.') | str('/')).as(:operator) } 47 | 48 | rule (:term) do 49 | ((factor >> simpleton | simpleton | factor) >> 50 | exponent.maybe >> annotation.maybe).as(:term) 51 | end 52 | 53 | rule (:group) do 54 | (factor.maybe >> str('(') >> expression.as(:nested) >> str(')') >> 55 | exponent.maybe).as(:group) 56 | end 57 | 58 | rule (:expression) do 59 | ((group | term).as(:left)).maybe >> 60 | (operator >> expression.as(:right)).maybe 61 | end 62 | 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/unitwise/expression/transformer.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | module Expression 3 | # Transformer is responsible for turning a Unitwise::Expression::Parser 4 | # hash result into a collection of Unitwise::Terms. 5 | class Transformer < Parslet::Transform 6 | 7 | rule(:integer => simple(:i)) { i.to_i } 8 | rule(:fixnum => simple(:f)) { f.to_f } 9 | 10 | rule(:prefix_code => simple(:c)) { |x| Prefix.find(x[:c], x[:mode]) } 11 | rule(:atom_code => simple(:c)) { |x| Atom.find(x[:c], x[:mode]) } 12 | rule(:term => subtree(:h)) { Term.new(h) } 13 | 14 | rule(:operator => simple(:o), :right => simple(:r)) do 15 | o == '/' ? r ** -1 : r 16 | end 17 | 18 | rule(:left => simple(:l), :operator => simple(:o), 19 | :right => simple(:r)) do 20 | o == '/' ? l / r : l * r 21 | end 22 | 23 | rule(:left => simple(:l)) { l } 24 | 25 | rule(:group => { :factor => simple(:f), 26 | :nested => simple(:n), :exponent => simple(:e) }) do 27 | (n ** e) * f 28 | end 29 | 30 | rule(:group => { :nested => simple(:n) , :exponent => simple(:e) }) do 31 | n ** e 32 | end 33 | 34 | rule(:group => { :nested => simple(:n) }) { n } 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/unitwise/functional.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # Functional is an alterative function-based scale for atoms with a 3 | # non-linear (or non-zero y-intercept) scale. This is most commonly used for 4 | # temperatures. Known functions for converting to and from special atoms 5 | # are setup as class methods here. 6 | class Functional < Scale 7 | extend Math 8 | 9 | def self.to_cel(x) 10 | x - 273.15 11 | end 12 | 13 | def self.from_cel(x) 14 | x + 273.15 15 | end 16 | 17 | def self.to_degf(x) 18 | 9.0 * x / 5.0 - 459.67 19 | end 20 | 21 | def self.from_degf(x) 22 | 5.0 / 9.0 * (x + 459.67) 23 | end 24 | 25 | def self.to_degre(x) 26 | 4.0 * x / 5.0 - 218.52 27 | end 28 | 29 | def self.from_degre(x) 30 | 5.0 / 4.0 * (x + 218.52) 31 | end 32 | 33 | def self.to_hpX(x) 34 | -log10(x) 35 | end 36 | 37 | def self.from_hpX(x) 38 | 10.0 ** -x 39 | end 40 | 41 | def self.to_hpC(x) 42 | -log(x) / log(100.0) 43 | end 44 | 45 | def self.from_hpC(x) 46 | 100.0 ** -x 47 | end 48 | 49 | def self.to_tan100(x) 50 | 100.0 * tan(x) 51 | end 52 | 53 | def self.from_tan100(x) 54 | atan(x / 100.0) 55 | end 56 | 57 | def self.to_ph(x) 58 | to_hpX(x) 59 | end 60 | 61 | def self.from_ph(x) 62 | from_hpX(x) 63 | end 64 | 65 | def self.to_ld(x) 66 | Math.log(x) / Math.log(2.0) 67 | end 68 | 69 | def self.from_ld(x) 70 | 2.0 ** x 71 | end 72 | 73 | def self.to_ln(x) 74 | log(x) 75 | end 76 | 77 | def self.from_ln(x) 78 | Math::E ** x 79 | end 80 | 81 | def self.to_lg(x) 82 | log10(x) 83 | end 84 | 85 | def self.from_lg(x) 86 | 10.0 ** x 87 | end 88 | 89 | def self.to_2lg(x) 90 | 2.0 * log10(x) 91 | end 92 | 93 | def self.from_2lg(x) 94 | 10.0 ** (x / 2.0) 95 | end 96 | 97 | attr_reader :function_name 98 | 99 | # Setup a new functional. 100 | # @param value [Numeric] The magnitude of the scale 101 | # @param unit [Unitwise::Unit, String] The unit of the scale 102 | # @param function_name [String, Symbol] One of the class methods above to be 103 | # used for conversion 104 | def initialize(value, unit, function_name) 105 | @function_name = function_name 106 | super(value, unit) 107 | end 108 | 109 | # Get the equivalent scalar value of a magnitude on this scale 110 | # @param magnitude [Numeric] The magnitude to find the scalar value for 111 | # @return [Numeric] Equivalent linear scalar value 112 | # @api public 113 | def scalar(magnitude = value) 114 | self.class.send(:"from_#{function_name}", magnitude) 115 | end 116 | 117 | # Get the equivalent magnitude on this scale for a scalar value 118 | # @param scalar [Numeric] A linear scalar value 119 | # @return [Numeric] The equivalent magnitude on this scale 120 | # @api public 121 | def magnitude(scalar = scalar()) 122 | self.class.send(:"to_#{function_name}", scalar) 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/unitwise/measurement.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # A Measurement is a combination of a numeric value and a unit. You can think 3 | # of this as a type of vector where the direction is the unit designation and 4 | # the value is the magnitude. This is the primary class that outside code 5 | # will interact with. Comes with conversion, comparison, and math methods. 6 | class Measurement < Scale 7 | # Create a new Measurement 8 | # @param value [Numeric] The scalar value for the measurement 9 | # @param unit [String, Measurement::Unit] Either a string expression, or a 10 | # Measurement::Unit 11 | # @example 12 | # Unitwise::Measurement.new(20, 'm/s') 13 | # @api public 14 | def initialize(*args) 15 | super(*args) 16 | terms 17 | end 18 | 19 | # Convert this measurement to a compatible unit. 20 | # @param other_unit [String, Measurement::Unit] Either a string expression 21 | # or a Measurement::Unit 22 | # @example 23 | # measurement1.convert_to('foot') 24 | # measurement2.convert_to('kilogram') 25 | # @api public 26 | def convert_to(other_unit) 27 | other_unit = Unit.new(other_unit) 28 | if compatible_with?(other_unit) 29 | new(converted_value(other_unit), other_unit) 30 | else 31 | fail ConversionError, "Can't convert #{self} to #{other_unit}." 32 | end 33 | end 34 | 35 | # Multiply this measurement by a number or another measurement 36 | # @param other [Numeric, Unitwise::Measurement] 37 | # @example 38 | # measurent * 5 39 | # measurement * some_other_measurement 40 | # @api public 41 | def *(other) 42 | operate(:*, other) || 43 | fail(TypeError, "Can't multiply #{self} by #{other}.") 44 | end 45 | 46 | # Divide this measurement by a number or another measurement 47 | # @param (see #*) 48 | # @example 49 | # measurement / 2 50 | # measurement / some_other_measurement 51 | # @api public 52 | def /(other) 53 | operate(:/, other) || fail(TypeError, "Can't divide #{self} by #{other}") 54 | end 55 | 56 | # Add another measurement to this unit. Units must be compatible. 57 | # @param other [Unitwise::Measurement] 58 | # @example 59 | # measurement + some_other_measurement 60 | # @api public 61 | def +(other) 62 | combine(:+, other) || fail(TypeError, "Can't add #{other} to #{self}.") 63 | end 64 | 65 | # Subtract another measurement from this unit. Units must be compatible. 66 | # @param (see #+) 67 | # @example 68 | # measurement - some_other_measurement 69 | # @api public 70 | def -(other) 71 | combine(:-, other) || 72 | fail(TypeError, "Can't subtract #{other} from #{self}.") 73 | end 74 | 75 | # Raise a measurement to a numeric power. 76 | # @param number [Numeric] 77 | # @example 78 | # measurement ** 2 79 | # @api public 80 | def **(other) 81 | if other.is_a?(Numeric) 82 | new(value ** other, unit ** other) 83 | else 84 | fail TypeError, "Can't raise #{self} to #{other} power." 85 | end 86 | end 87 | 88 | # Round the measurement value. Delegates to the value's class. 89 | # @return [Integer, Float] 90 | # @api public 91 | def round(digits = nil) 92 | rounded_value = digits ? value.round(digits) : value.round 93 | self.class.new(rounded_value, unit) 94 | end 95 | 96 | # Coerce a numeric to a a measurement for mathematical operations 97 | # @param other [Numeric] 98 | # @example 99 | # 2.5 * measurement 100 | # 4 / measurement 101 | # @api public 102 | def coerce(other) 103 | case other 104 | when Numeric 105 | return self.class.new(other, '1'), self 106 | else 107 | fail TypeError, "#{self.class} can't be coerced into #{other.class}" 108 | end 109 | end 110 | 111 | # Convert a measurement to an Integer. 112 | # @example 113 | # measurement.to_i # => 4 114 | # @api public 115 | def to_i 116 | Integer(value) 117 | end 118 | 119 | # Convert a measurement to a Float. 120 | # @example 121 | # measurement.to_f # => 4.25 122 | # @api public 123 | def to_f 124 | Float(value) 125 | end 126 | 127 | # Convert a measurement to a Rational. 128 | # @example 129 | # measurement.to_r # => (17/4) 130 | # @api public 131 | def to_r 132 | Number.rationalize(value) 133 | end 134 | 135 | # Will attempt to convert to a unit by method name. 136 | # @example 137 | # measurement.to_foot # => 138 | # @api semipublic 139 | def method_missing(meth, *args, &block) 140 | if args.empty? && !block_given? && (match = /\Ato_(\w+)\Z/.match(meth.to_s)) 141 | begin 142 | convert_to(match[1]) 143 | rescue ExpressionError 144 | super(meth, *args, &block) 145 | end 146 | else 147 | super(meth, *args, &block) 148 | end 149 | end 150 | 151 | private 152 | 153 | # Helper method to create a new instance from this instance 154 | # @api private 155 | def new(*args) 156 | self.class.new(*args) 157 | end 158 | 159 | # Determine value of the unit after conversion to another unit 160 | # @api private 161 | def converted_value(other_unit) 162 | if other_unit.special? 163 | other_unit.magnitude scalar 164 | else 165 | scalar / other_unit.scalar 166 | end 167 | end 168 | 169 | # Add or subtract other unit 170 | # @api private 171 | def combine(operator, other) 172 | if other.respond_to?(:composition) && compatible_with?(other) 173 | new(value.send(operator, other.convert_to(unit).value), unit) 174 | end 175 | end 176 | 177 | # Multiply or divide other unit 178 | # @api private 179 | def operate(operator, other) 180 | if other.is_a?(Numeric) 181 | new(value.send(operator, other), unit) 182 | elsif other.respond_to?(:composition) 183 | if compatible_with?(other) 184 | converted = other.convert_to(unit) 185 | new(value.send(operator, converted.value), 186 | unit.send(operator, converted.unit)) 187 | else 188 | new(value.send(operator, other.value), 189 | unit.send(operator, other.unit)) 190 | end 191 | end 192 | end 193 | end 194 | end 195 | -------------------------------------------------------------------------------- /lib/unitwise/number.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | class Number 3 | # Attempts to coerce a value to the simplest Numeric that fully expresses 4 | # it's value. For instance a value of 1.0 would return 1, a value of 5 | # # would return 4.5. 6 | # @api public 7 | # @param value [Integer, Float, Rational, String, BigDecimal] 8 | # @return [Integer, Float, Rational, BigDecimal] 9 | def self.simplify(value) 10 | case value 11 | when Integer 12 | value 13 | when Float 14 | (i = value.to_i) == value ? i : value 15 | when Rational 16 | if (i = value.to_i) == value 17 | i 18 | elsif (f = value.to_f) && f.to_r == value 19 | f 20 | else 21 | value 22 | end 23 | else # String, BigDecimal, Other 24 | s = value.is_a?(String) ? value : value.to_s 25 | d = value.is_a?(BigDecimal) ? value : BigDecimal(s) 26 | if (i = d.to_i) == d 27 | i 28 | elsif (f = d.to_f) == d 29 | f 30 | else 31 | d 32 | end 33 | end 34 | end 35 | 36 | # Coerces a string-like number to a BigDecimal or Integer as appropriate 37 | # @api public 38 | # @param value Something that can be represented as a string number 39 | # @return [Integer, BigDecimal] 40 | def self.coefficify(value) 41 | d = BigDecimal(value.to_s) 42 | if (i = d.to_i) == d 43 | i 44 | else 45 | d 46 | end 47 | end 48 | 49 | # Coerce a numeric to a Rational, but avoid inaccurate conversions by 50 | # jruby. More details here: https://github.com/jruby/jruby/issues/4711. 51 | # @api public 52 | # @param number [Numeric] 53 | # @return Rational 54 | def self.rationalize(number) 55 | if number.is_a?(BigDecimal) && RUBY_PLATFORM == 'java' 56 | number.to_s.to_r 57 | else 58 | number.to_r 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/unitwise/prefix.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # A prefix can be used with metric atoms to modify their scale. 3 | class Prefix < Base 4 | liner :scalar 5 | 6 | # The data loaded from the UCUM spec files 7 | # @api semipublic 8 | def self.data 9 | f = File.open(data_file) 10 | @data ||= ::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(f) : ::YAML.load(f) 11 | end 12 | 13 | # The location of the UCUM spec prefix data file 14 | # @api semipublic 15 | def self.data_file 16 | Unitwise.data_file 'prefix' 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/unitwise/scale.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # A Unitwise::Scale represents a value and a unit, sort of like a vector, it 3 | # has two components. In this case, it's a value and unit rather than a 4 | # magnitude and direction. This class should be considered mostly privateish. 5 | class Scale 6 | liner :value, :unit 7 | include Unitwise::Compatible 8 | 9 | def initialize(value, unit) 10 | self.value = if value.is_a? self.class 11 | value.convert_to(unit).value 12 | else 13 | value 14 | end 15 | self.unit = unit 16 | freeze 17 | end 18 | 19 | # Set the unit vector. 20 | # @param value [String, Unitwise::Unit] 21 | # @api public 22 | def unit=(value) 23 | @unit = value.is_a?(Unit) ? value : Unit.new(value) 24 | end 25 | 26 | # List the atoms associated with this scale's unit. 27 | # @return [Array] 28 | # @api public 29 | def atoms 30 | unit.atoms 31 | end 32 | 33 | # List the terms associated with this scale's unit. 34 | # @return [Array] 35 | # @api public 36 | def terms 37 | unit.terms 38 | end 39 | 40 | # Is this scale's unit special? 41 | # @return [true, false] 42 | # @api public 43 | def special? 44 | unit.special? 45 | end 46 | 47 | # Get a scalar value for this scale. 48 | # @param magnitude [Numeric] An optional magnitude on this scale. 49 | # @return [Numeric] A scalar value on a linear scale 50 | # @api public 51 | def scalar(magnitude = value) 52 | if special? 53 | unit.scalar(magnitude) 54 | else 55 | Number.rationalize(value) * Number.rationalize(unit.scalar) 56 | end 57 | end 58 | 59 | # Get a magnitude based on a linear scale value. Only used by scales with 60 | # special atoms in it's hierarchy. 61 | # @param scalar [Numeric] A linear scalar value 62 | # @return [Numeric] The equivalent magnitude on this scale 63 | # @api public 64 | def magnitude(scalar = scalar()) 65 | if special? 66 | unit.magnitude(scalar) 67 | else 68 | value * unit.magnitude 69 | end 70 | end 71 | 72 | # The base terms this scale's unit is derived from 73 | # @return [Array] An array of Unitwise::Term 74 | # @api public 75 | def root_terms 76 | unit.root_terms 77 | end 78 | memoize :root_terms 79 | 80 | # How far away is this instances unit from the deepest level atom. 81 | # @return [Integer] 82 | # @api public 83 | def depth 84 | unit.depth + 1 85 | end 86 | memoize :depth 87 | 88 | # Attempts to coerce the value to the simplest Numeric that fully expresses 89 | # it's value. For instance a value of 1.0 would return 1, a value of 90 | # # would return 4.5. 91 | # @return [Numeric] 92 | # @api public 93 | def simplified_value 94 | Unitwise::Number.simplify(value) 95 | end 96 | memoize :simplified_value 97 | 98 | def expression 99 | unit.expression 100 | end 101 | 102 | # Convert to a simple string representing the scale. 103 | # @api public 104 | def to_s(mode = nil) 105 | unit_string = unit.to_s(mode) 106 | if unit_string && unit_string != '1' 107 | "#{simplified_value} #{unit_string}" 108 | else 109 | simplified_value.to_s 110 | end 111 | end 112 | 113 | def inspect 114 | "#<#{self.class} value=#{simplified_value} unit=#{unit}>" 115 | end 116 | 117 | # Redefine hash for apropriate hash/key lookup 118 | # @api semipublic 119 | def hash 120 | [value, unit.to_s, self.class].hash 121 | end 122 | memoize :hash 123 | 124 | # Redefine hash equality to match the hashes 125 | # @api semipublic 126 | def eql?(other) 127 | hash == other.hash 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /lib/unitwise/search.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # The search module provides a simple search mechanism around known basic 3 | # units. The full list of avaliable units infinite, so this search creates 4 | # a small subset of atoms and prefixes to help users find what they are 5 | # looking for. Thus, there is a multitude of valid units that may be 6 | # constructed that this module will not be aware of. 7 | module Search 8 | class << self 9 | # An abbreviated list of possible units. These are known combinations 10 | # of atoms and prefixes. 11 | # @return [Array] A list of known units 12 | # @api public 13 | def all 14 | @all ||= begin 15 | units = [] 16 | Atom.all.each do |a| 17 | units << build(a) 18 | Unitwise::Prefix.all.each { |p| units << build(a, p) } if a.metric? 19 | end 20 | units 21 | end 22 | end 23 | 24 | # Search the list of known units for a match. 25 | # @param term [String, Regexp] The term to search for 26 | # @return [Array] A list of matching units. 27 | # @api public 28 | def search(term) 29 | all.select do |unit| 30 | unit.aliases.any? { |str| Regexp.new(term).match(str) } 31 | end 32 | end 33 | 34 | private 35 | 36 | # Helper method for building a new unit by a known atom and prefix. 37 | # @param atom [Unitwise::Atom] 38 | # @param prefix [Unitwise::Prefix, nil] 39 | # @return [Unitwise::Unit] 40 | # @api private 41 | def build(atom, prefix = nil) 42 | Unit.new([Term.new(:atom => atom, :prefix => prefix)]) 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/unitwise/standard.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'nori' 3 | require 'unitwise/standard/base' 4 | require 'unitwise/standard/prefix' 5 | require 'unitwise/standard/base_unit' 6 | require 'unitwise/standard/derived_unit' 7 | require 'unitwise/standard/scale' 8 | require 'unitwise/standard/function' 9 | 10 | module Unitwise 11 | # The Standard module is responsible for fetching the UCUM specification unit 12 | # standards and translating them into yaml files. This code is only used for 13 | # by the rake task `rake unitwise:update_standard` and as such is not 14 | # normally loaded. 15 | module Standard 16 | HOST = "unitsofmeasure.org" 17 | PATH = "/ucum-essence.xml" 18 | 19 | class << self 20 | def body 21 | @body ||= Net::HTTP.get HOST, PATH 22 | end 23 | 24 | def hash 25 | Nori.new.parse(body)["root"] 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/unitwise/standard/base.rb: -------------------------------------------------------------------------------- 1 | require 'unitwise/standard/extras' 2 | module Unitwise::Standard 3 | class Base 4 | include Unitwise::Standard::Extras 5 | 6 | attr_accessor :attributes 7 | 8 | def self.local_key 9 | remote_key 10 | end 11 | 12 | def self.all 13 | @all ||= read 14 | end 15 | 16 | def self.read 17 | Unitwise::Standard.hash[remote_key].inject([]){|a,h| a << self.new(h)} 18 | end 19 | 20 | def self.hash 21 | self.all.map(&:to_hash) 22 | end 23 | 24 | def self.path 25 | Unitwise.data_file(local_key) 26 | end 27 | 28 | def self.write 29 | File.open(path, 'w') do |f| 30 | f.write hash.to_yaml 31 | end 32 | end 33 | 34 | def initialize(attributes) 35 | @attributes = attributes 36 | end 37 | 38 | def names 39 | if attributes["name"].respond_to?(:map) 40 | attributes["name"].map(&:to_s) 41 | else 42 | attributes["name"].to_s 43 | end 44 | end 45 | 46 | def symbol 47 | sym = attributes["printSymbol"] 48 | if sym.is_a?(Hash) 49 | hash_to_markup(sym) 50 | elsif sym 51 | sym.to_s 52 | end 53 | end 54 | 55 | def primary_code 56 | attributes["@Code"] 57 | end 58 | 59 | def secondary_code 60 | attributes["@CODE"] 61 | end 62 | 63 | def to_hash 64 | [:names, :symbol, :primary_code, :secondary_code].inject({}) do |h,a| 65 | if v = self.send(a) 66 | h[a] = v 67 | end 68 | h 69 | end 70 | end 71 | 72 | end 73 | end -------------------------------------------------------------------------------- /lib/unitwise/standard/base_unit.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | class BaseUnit < Base 3 | 4 | def self.remote_key 5 | "base_unit" 6 | end 7 | 8 | def property 9 | attributes["property"].to_s 10 | end 11 | 12 | def dim 13 | attributes["@dim"] 14 | end 15 | 16 | def to_hash 17 | super.merge :property => property, :dim => dim 18 | end 19 | 20 | end 21 | end -------------------------------------------------------------------------------- /lib/unitwise/standard/derived_unit.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | class DerivedUnit < Base 3 | 4 | def self.remote_key 5 | "unit" 6 | end 7 | 8 | def self.local_key 9 | "derived_unit" 10 | end 11 | 12 | def property 13 | attributes["property"].to_s 14 | end 15 | 16 | def scale 17 | Scale.new(attributes["value"]) unless special? 18 | end 19 | 20 | def function 21 | Function.new(attributes["value"]) if special? 22 | end 23 | 24 | def classification 25 | attributes["@class"] 26 | end 27 | 28 | def metric? 29 | attributes["@isMetric"] == 'yes' 30 | end 31 | 32 | def special? 33 | attributes["@isSpecial"] == 'yes' 34 | end 35 | 36 | def arbitrary? 37 | attributes["@isArbitrary"] == 'yes' 38 | end 39 | 40 | def to_hash 41 | hash = super() 42 | hash[:scale] = (special? ? function.to_hash : scale.to_hash) 43 | hash.merge({:classification => classification, 44 | :property => property, :metric => metric?, 45 | :special => special?, :arbitrary => arbitrary?}) 46 | end 47 | 48 | end 49 | end -------------------------------------------------------------------------------- /lib/unitwise/standard/extras.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | module Extras 3 | def hash_to_markup(hash) 4 | hash.map do |k,v| 5 | if v.respond_to?(:to_xml) 6 | "<#{k}>#{v.to_xml}" 7 | elsif v.respond_to?(:map) 8 | v.map do |i| 9 | "<#{k}>#{i}" 10 | end.join('') 11 | else 12 | "<#{k}>#{v}" 13 | end 14 | end.join('') 15 | end 16 | end 17 | end -------------------------------------------------------------------------------- /lib/unitwise/standard/function.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | class Function 3 | 4 | attr_accessor :attributes 5 | 6 | def initialize(attributes) 7 | @attributes = attributes 8 | end 9 | 10 | def name 11 | attributes["function"]["@name"] 12 | end 13 | 14 | def value 15 | Unitwise::Number.simplify(attributes["function"]["@value"]) 16 | end 17 | 18 | def unit 19 | attributes["function"]["@Unit"] 20 | end 21 | 22 | def primary 23 | attributes["@Unit"].gsub(/\(.*\)/, '') 24 | end 25 | 26 | def secondary 27 | attributes["@UNIT"] 28 | end 29 | 30 | def to_hash 31 | {:function_code => primary, :value => value, :unit_code => unit} 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/unitwise/standard/prefix.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | class Prefix < Base 3 | 4 | def self.remote_key 5 | "prefix" 6 | end 7 | 8 | def scale 9 | Unitwise::Number.coefficify( 10 | attributes.fetch('value').attributes.fetch('value') 11 | ) 12 | end 13 | 14 | def to_hash 15 | super().merge(:scalar => scale) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/unitwise/standard/scale.rb: -------------------------------------------------------------------------------- 1 | module Unitwise::Standard 2 | class Scale 3 | attr_accessor :nori 4 | 5 | def initialize(nori) 6 | @nori = nori 7 | end 8 | 9 | def value 10 | Unitwise::Number.coefficify(nori.attributes.fetch('value')) 11 | end 12 | 13 | def primary_unit_code 14 | nori.attributes["Unit"] 15 | end 16 | 17 | def secondary_unit_code 18 | nori.attributes["UNIT"] 19 | end 20 | 21 | def to_hash 22 | {:value => value, :unit_code => primary_unit_code} 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/unitwise/term.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # A Term is the combination of an atom, prefix, factor and annotation. 3 | # Not all properties have to be present. Examples: 'g', 'mm', 'mi2', '4[pi]', 4 | # 'kJ{Electric Potential}' 5 | class Term < Liner.new(:atom, :prefix, :factor, :exponent, :annotation) 6 | include Compatible 7 | 8 | # Set the atom. 9 | # @param value [String, Atom] Either a string representing an Atom, or an 10 | # Atom 11 | # @api public 12 | def atom=(value) 13 | value.is_a?(Atom) ? super(value) : super(Atom.find(value.to_s)) 14 | end 15 | 16 | # Set the prefix. 17 | # @param value [String, Prefix] Either a string representing a Prefix, or 18 | # a Prefix 19 | def prefix=(value) 20 | value.is_a?(Prefix) ? super(value) : super(Prefix.find(value.to_s)) 21 | end 22 | 23 | # Is this term special? 24 | # @return [true, false] 25 | def special? 26 | atom.special? rescue false 27 | end 28 | 29 | # Determine how far away a unit is from a base unit. 30 | # @return [Integer] 31 | # @api public 32 | def depth 33 | atom ? atom.depth + 1 : 0 34 | end 35 | memoize :depth 36 | 37 | # Determine if this is the last term in the scale chain 38 | # @return [true, false] 39 | # @api public 40 | def terminal? 41 | depth <= 3 42 | end 43 | 44 | # The multiplication factor for this term. The default value is 1. 45 | # @return [Numeric] 46 | # @api public 47 | def factor 48 | super || 1 49 | end 50 | 51 | # The exponent for this term. The default value is 1. 52 | # @return [Numeric] 53 | # @api public 54 | def exponent 55 | super || 1 56 | end 57 | 58 | # The unitless scalar value for this term. 59 | # @param magnitude [Numeric] The magnitude to calculate the scalar for. 60 | # @return [Numeric] The unitless linear scalar value. 61 | # @api public 62 | def scalar(magnitude = 1) 63 | calculate(atom ? atom.scalar(magnitude) : magnitude) 64 | end 65 | 66 | # Calculate the magnitude for this term 67 | # @param scalar [Numeric] The scalar for which you want the magnitude 68 | # @return [Numeric] The magnitude on this scale. 69 | # @api public 70 | def magnitude(scalar = scalar()) 71 | calculate(atom ? atom.magnitude(scalar) : 1) 72 | end 73 | 74 | # The base units this term is derived from 75 | # @return [Array] An array of Unitwise::Term 76 | # @api public 77 | def root_terms 78 | if terminal? 79 | [self] 80 | else 81 | atom.scale.root_terms.map do |t| 82 | self.class.new(:atom => t.atom, :exponent => t.exponent * exponent) 83 | end 84 | end 85 | end 86 | memoize :root_terms 87 | 88 | # Term multiplication. Multiply by a Unit, another Term, or a Numeric. 89 | # params other [Unit, Term, Numeric] 90 | # @return [Term] 91 | def *(other) 92 | operate('*', other) || 93 | fail(TypeError, "Can't multiply #{ self } by #{ other }.") 94 | end 95 | 96 | # Term division. Divide by a Unit, another Term, or a Numeric. 97 | # params other [Unit, Term, Numeric] 98 | # @return [Term] 99 | def /(other) 100 | operate('/', other) || 101 | fail(TypeError, "Can't divide #{ self } by #{ other }.") 102 | end 103 | 104 | 105 | # Term exponentiation. Raise a term to a numeric power. 106 | # params other [Numeric] 107 | # @return [Term] 108 | def **(other) 109 | if other.is_a?(Numeric) 110 | self.class.new(to_hash.merge(:exponent => exponent * other)) 111 | else 112 | fail TypeError, "Can't raise #{self} to #{other}." 113 | end 114 | end 115 | 116 | def to_s(mode = :primary_code) 117 | [ 118 | (factor if factor != 1), 119 | (prefix.send(mode) if prefix), 120 | (atom.send(mode) if atom), 121 | (exponent if exponent != 1) 122 | ].compact.join('') 123 | end 124 | 125 | private 126 | 127 | # @api private 128 | def calculate(value) 129 | (factor * (prefix ? prefix.scalar : 1) * value) ** exponent 130 | end 131 | 132 | # Multiply or divide a term 133 | # @api private 134 | def operate(operator, other) 135 | exp = operator == '/' ? -1 : 1 136 | if other.respond_to?(:terms) 137 | Unit.new(other.terms.map { |t| t ** exp } << self) 138 | elsif other.respond_to?(:atom) 139 | Unit.new([self, other ** exp]) 140 | elsif other.is_a?(Numeric) 141 | self.class.new(to_hash.merge(:factor => factor.send(operator, other))) 142 | end 143 | end 144 | 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /lib/unitwise/unit.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | # A Unit is essentially a collection of Unitwise::Term. Terms can be combined 3 | # through multiplication or division to create a unit. A unit does not have 4 | # a magnitude, but it does have a scale. 5 | class Unit 6 | liner :expression, :terms 7 | include Compatible 8 | 9 | # Create a new unit. You can send an expression or a collection of terms 10 | # @param input [String, Unit, [Term]] A string expression, a unit, or a 11 | # collection of tems. 12 | # @api public 13 | def initialize(input) 14 | case input 15 | when Compatible 16 | @expression = input.expression 17 | when String, Symbol 18 | @expression = input.to_s 19 | else 20 | @terms = input 21 | end 22 | end 23 | 24 | # The collection of terms used by this unit. 25 | # @return [Array] 26 | # @api public 27 | def terms 28 | unless frozen? 29 | unless @terms 30 | decomposer = Expression.decompose(@expression) 31 | @mode = decomposer.mode 32 | @terms = decomposer.terms 33 | end 34 | freeze 35 | end 36 | @terms 37 | end 38 | 39 | # Build a string representation of this unit by it's terms. 40 | # @param mode [Symbol] The mode to use to stringify the atoms 41 | # (:primary_code, :names, :secondary_code). 42 | # @return [String] 43 | # @api public 44 | def expression(mode=nil) 45 | if @expression && (mode.nil? || mode == self.mode) 46 | @expression 47 | else 48 | Expression.compose(terms, mode || self.mode) 49 | end 50 | end 51 | 52 | # The collection of atoms that compose this unit. Essentially delegated to 53 | # terms. 54 | # @return [Array] 55 | # @api public 56 | def atoms 57 | terms.map(&:atom) 58 | end 59 | memoize :atoms 60 | 61 | # Is this unit special (meaning on a non-linear scale)? 62 | # @return [true, false] 63 | # @api public 64 | def special? 65 | terms.count == 1 && terms.all?(&:special?) 66 | end 67 | memoize :special? 68 | 69 | # A number representing this unit's distance from it's deepest terminal atom. 70 | # @return [Integer] 71 | # @api public 72 | def depth 73 | terms.map(&:depth).max + 1 74 | end 75 | memoize :depth 76 | 77 | # A collection of the deepest terms, or essential composition of the unit. 78 | # @return [Array] 79 | # @api public 80 | def root_terms 81 | terms.map(&:root_terms).flatten 82 | end 83 | memoize :root_terms 84 | 85 | # Get a scalar value for this unit. 86 | # @param magnitude [Numeric] An optional magnitude on this unit's scale. 87 | # @return [Numeric] A scalar value on a linear scale 88 | # @api public 89 | def scalar(magnitude = 1) 90 | terms.reduce(1) do |prod, term| 91 | prod * term.scalar(magnitude) 92 | end 93 | end 94 | 95 | # Get a magnitude for this unit based on a linear scale value. 96 | # Should only be used by units with special atoms in it's hierarchy. 97 | # @param scalar [Numeric] A linear scalar value 98 | # @return [Numeric] The equivalent magnitude on this scale 99 | # @api public 100 | def magnitude(scalar = scalar()) 101 | terms.reduce(1) do |prod, term| 102 | prod * term.magnitude(scalar) 103 | end 104 | end 105 | 106 | # Multiply this unit by another unit, term, or number. 107 | # @param other [Unitwise::Unit, Unitwise::Term, Numeric] 108 | # @return [Unitwise::Unit] 109 | # @api public 110 | def *(other) 111 | operate('*', other) || 112 | fail(TypeError, "Can't multiply #{ self } by #{ other }.") 113 | end 114 | 115 | # Divide this unit by another unit,term, or number. 116 | # @param other [Unitwise::Unit, Unitwise::Term, Numeric] 117 | # @return [Unitwise::Unit] 118 | # @api public 119 | def /(other) 120 | operate('/', other) || 121 | fail(TypeError, "Can't divide #{ self } by #{ other }.") 122 | end 123 | 124 | 125 | # Raise this unit to a numeric power. 126 | # @param other [Numeric] 127 | # @return [Unitwise::Unit] 128 | # @api public 129 | def **(other) 130 | if other.is_a?(Numeric) 131 | self.class.new(terms.map { |t| t ** other }) 132 | else 133 | fail TypeError, "Can't raise #{self} to #{other}." 134 | end 135 | end 136 | 137 | # A string representation of this unit. 138 | # @param mode [:symbol] The mode used to represent the unit 139 | # (:primary_code, :names, :secondary_code) 140 | # @return [String] 141 | # @api public 142 | def to_s(mode = nil) 143 | expression(mode) 144 | end 145 | 146 | # A collection of the possible string representations of this unit. 147 | # Primarily used by Unitwise::Search. 148 | # @return [Array] 149 | # @api public 150 | def aliases 151 | [:names, :primary_code, :secondary_code, :symbol].map do |mode| 152 | to_s(mode) 153 | end.uniq 154 | end 155 | memoize :aliases 156 | 157 | # The default mode to use for inspecting and printing. 158 | # @return [Symbol] 159 | # @api semipublic 160 | def mode 161 | terms 162 | @mode || :primary_code 163 | end 164 | 165 | private 166 | 167 | # Multiply or divide units 168 | # @api private 169 | def operate(operator, other) 170 | exp = operator == '/' ? -1 : 1 171 | if other.respond_to?(:terms) 172 | self.class.new(terms + other.terms.map { |t| t ** exp }) 173 | elsif other.respond_to?(:atom) 174 | self.class.new(terms << other ** exp) 175 | elsif other.is_a?(Numeric) 176 | self.class.new(terms.map { |t| t.send(operator, other) }) 177 | end 178 | end 179 | 180 | end 181 | end 182 | -------------------------------------------------------------------------------- /lib/unitwise/version.rb: -------------------------------------------------------------------------------- 1 | module Unitwise 2 | VERSION = '2.3.0'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /test/support/scale_tests.rb: -------------------------------------------------------------------------------- 1 | # Shared examples for Unitwise::Scale and Unitwise::Measurement 2 | module ScaleTests 3 | def self.included(base) 4 | base.class_eval do 5 | subject { described_class.new(4, "J") } 6 | 7 | let(:mph) { described_class.new(60, '[mi_i]/h') } 8 | let(:kmh) { described_class.new(100, 'km/h') } 9 | let(:mile) { described_class.new(3, '[mi_i]') } 10 | let(:hpm) { described_class.new(6, 'h/[mi_i]') } 11 | let(:cui) { described_class.new(12, "[in_i]3") } 12 | let(:cel) { described_class.new(22, 'Cel') } 13 | let(:k) { described_class.new(373.15, 'K') } 14 | let(:f) { described_class.new(98.6, '[degF]')} 15 | let(:r) { described_class.new(491.67, '[degR]') } 16 | 17 | describe "#new" do 18 | it "must set attributes" do 19 | _(subject.value).must_equal(4) 20 | _(subject.unit.to_s).must_equal('J') 21 | end 22 | end 23 | 24 | describe "#unit" do 25 | it "must be a unit" do 26 | _(subject).must_respond_to(:unit) 27 | _(subject.unit).must_be_instance_of(Unitwise::Unit) 28 | end 29 | end 30 | 31 | describe "#root_terms" do 32 | it "must be a collection of terms" do 33 | _(subject).must_respond_to(:root_terms) 34 | _(subject.root_terms).must_be_kind_of Enumerable 35 | _(subject.root_terms.first).must_be_instance_of(Unitwise::Term) 36 | end 37 | end 38 | 39 | describe "#terms" do 40 | it "must return an array of terms" do 41 | _(subject.terms).must_be_kind_of(Enumerable) 42 | _(subject.terms.first).must_be_kind_of(Unitwise::Term) 43 | end 44 | end 45 | 46 | describe "#atoms" do 47 | it "must return an array of atoms" do 48 | _(subject.atoms).must_be_kind_of(Enumerable) 49 | _(subject.atoms.first).must_be_kind_of(Unitwise::Atom) 50 | end 51 | end 52 | 53 | describe "#scalar" do 54 | it "must return value relative to terminal atoms" do 55 | _(subject.scalar).must_equal 4000 56 | _(mph.scalar).must_almost_equal 26.8224 57 | _(cel.scalar).must_equal 295.15 58 | end 59 | end 60 | 61 | describe "#magnitude" do 62 | it "must return the magnitude" do 63 | _(mph.magnitude).must_equal(60) 64 | _(cel.magnitude).must_equal(22) 65 | end 66 | end 67 | 68 | describe "#special?" do 69 | it "must return true when unit is special, false otherwise" do 70 | _(subject.special?).must_equal false 71 | _(cel.special?).must_equal true 72 | end 73 | end 74 | 75 | describe "#depth" do 76 | it "must return a number indicating how far down the rabbit hole goes" do 77 | _(subject.depth).must_equal 11 78 | _(k.depth).must_equal 3 79 | end 80 | end 81 | 82 | describe "#frozen?" do 83 | it "must be frozen" do 84 | _(subject.frozen?).must_equal true 85 | end 86 | end 87 | 88 | describe "#simplified_value" do 89 | it "must simplify to an Integer" do 90 | result = described_class.new(4.0, 'foot').simplified_value 91 | _(result).must_equal 4 92 | _(result).must_be_kind_of(Integer) 93 | end 94 | 95 | it "must simplify to a Float" do 96 | result = described_class.new(BigDecimal("1.5"), 'foot').simplified_value 97 | _(result).must_equal 1.5 98 | _(result).must_be_kind_of(Float) 99 | end 100 | end 101 | 102 | describe "#inspect" do 103 | it "must show the unit and value" do 104 | result = described_class.new(12, 'meter').inspect 105 | _(result).must_include("value=12") 106 | _(result).must_include("unit=meter") 107 | end 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'covered/minitest' 2 | require 'minitest/autorun' 3 | require 'minitest/pride' 4 | 5 | require 'unitwise' 6 | 7 | module Minitest::Assertions 8 | def assert_almost_equal(expected, actual, range=0.0001) 9 | message = "Expected #{actual} to be almost equal to #{expected}" 10 | assert expected + range > actual && expected - range < actual, message 11 | end 12 | end 13 | 14 | Numeric.infect_an_assertion :assert_almost_equal, :must_almost_equal 15 | -------------------------------------------------------------------------------- /test/unitwise/atom_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Atom do 4 | subject { Unitwise::Atom } 5 | describe "::data" do 6 | it "must have data" do 7 | _(subject.data).must_be_instance_of Array 8 | _(subject.data.count).must_be :>, 0 9 | end 10 | end 11 | 12 | describe "::all" do 13 | it "must be an Array of instances" do 14 | _(subject.all).must_be_instance_of Array 15 | _(subject.all.first).must_be_instance_of Unitwise::Atom 16 | end 17 | end 18 | 19 | describe "::find" do 20 | it "must find atoms" do 21 | _(subject.find("m")).must_be_instance_of Unitwise::Atom 22 | _(subject.find("V")).must_be_instance_of Unitwise::Atom 23 | end 24 | end 25 | 26 | let(:second) { Unitwise::Atom.find("s") } 27 | let(:yard) { Unitwise::Atom.find("[yd_i]")} 28 | let(:pi) { Unitwise::Atom.find("[pi]")} 29 | let(:celsius) { Unitwise::Atom.find("Cel")} 30 | let(:pfu) { Unitwise::Atom.find("[PFU]")} 31 | let(:joule) { Unitwise::Atom.find("J")} 32 | describe "#scale" do 33 | it "must be nil for base atoms" do 34 | _(second.scale).must_be_nil 35 | end 36 | it "sould be a Scale object for derived atoms" do 37 | _(yard.scale).must_be_instance_of Unitwise::Scale 38 | end 39 | it "must be a FunctionalScale object for special atoms" do 40 | _(celsius.scale).must_be_instance_of Unitwise::Functional 41 | end 42 | end 43 | 44 | describe "#base?" do 45 | it "must be true for base atoms" do 46 | _(second.base?).must_equal true 47 | end 48 | it "must be false for derived atoms" do 49 | _(yard.base?).must_equal false 50 | _(pi.base?).must_equal false 51 | end 52 | end 53 | 54 | describe "#derived?" do 55 | it "must be false for base atoms" do 56 | _(second.derived?).must_equal false 57 | end 58 | it "must be true for derived atoms" do 59 | _(yard.derived?).must_equal true 60 | _(celsius.derived?).must_equal true 61 | end 62 | end 63 | 64 | describe "#metric?" do 65 | it "must be true for base atoms" do 66 | _(second.metric?).must_equal true 67 | end 68 | it "must be false for english atoms" do 69 | _(yard.metric?).must_equal false 70 | end 71 | end 72 | 73 | describe "#special?" do 74 | it "must be true for special atoms" do 75 | _(celsius.special?).must_equal true 76 | end 77 | it "must be false for non-special atoms" do 78 | _(second.special?).must_equal false 79 | end 80 | end 81 | 82 | describe "#arbitrary?" do 83 | it "must be true for arbitrary atoms" do 84 | _(pfu.arbitrary?).must_equal true 85 | end 86 | it "must be false for non-arbitrary atoms" do 87 | _(yard.arbitrary?).must_equal false 88 | _(celsius.arbitrary?).must_equal false 89 | end 90 | end 91 | 92 | describe "#terminal?" do 93 | it "must be true for atoms without a valid measurement atom" do 94 | _(second.terminal?).must_equal true 95 | _(pi.terminal?).must_equal true 96 | end 97 | it "must be false for child atoms" do 98 | _(yard.terminal?).must_equal false 99 | end 100 | end 101 | 102 | describe "#scalar" do 103 | it "must return scalar relative to terminal atom" do 104 | _(second.scalar).must_equal 1 105 | _(yard.scalar).must_almost_equal 0.9144 106 | _(pi.scalar).must_almost_equal 3.141592653589793 107 | end 108 | end 109 | 110 | describe "#dim" do 111 | it "must return the dim" do 112 | _(second.dim).must_equal 'T' 113 | _(yard.dim).must_equal 'L' 114 | _(joule.dim).must_equal 'L2.M.T-2' 115 | end 116 | end 117 | 118 | describe "#measurement=" do 119 | it "must create a new measurement object and set attributes" do 120 | skip("need to figure out mocking and stubbing with minitest") 121 | end 122 | end 123 | 124 | describe "#frozen?" do 125 | it "should be frozen" do 126 | _(second.frozen?).must_equal true 127 | end 128 | end 129 | 130 | describe "validate!" do 131 | it "returns true for a valid atom" do 132 | atom = Unitwise::Atom.new( 133 | primary_code: "warp", 134 | secondary_code: "[warp]", 135 | names: ["Warp", "Warp Factor"], 136 | scale: { 137 | value: 1, 138 | unit_code: "[c]" 139 | } 140 | ) 141 | 142 | _(atom.validate!).must_equal true 143 | end 144 | 145 | it "returns an error for an atom with missing properties" do 146 | atom = Unitwise::Atom.new(names: "hot dog") 147 | 148 | assert_raises { atom.validate! } 149 | end 150 | 151 | it "returns an error for an atom that doesn't resolve" do 152 | atom = Unitwise::Atom.new( 153 | primary_code: "feels", 154 | secondary_code: "FEELS", 155 | names: ["feels"], 156 | scale: { 157 | value: 1, 158 | unit_code: "hearts" 159 | } 160 | ) 161 | 162 | assert_raises { atom.validate! } 163 | end 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /test/unitwise/base_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Base do 4 | 5 | 6 | end -------------------------------------------------------------------------------- /test/unitwise/expression/decomposer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Expression::Decomposer do 4 | subject { Unitwise::Expression::Decomposer } 5 | 6 | describe "#terms" do 7 | it "should accept codes" do 8 | fts = subject.new("[ft_i]/s").terms 9 | _(fts.count).must_equal 2 10 | end 11 | it "should accept names" do 12 | kms = subject.new("kilometer/second").terms 13 | _(kms.count).must_equal 2 14 | end 15 | it "should accept spaced names" do 16 | ncg = subject.new("Newtonian constant of gravitation").terms 17 | _(ncg.count).must_equal 1 18 | end 19 | it "should accept parameterized names" do 20 | pc = subject.new("planck_constant").terms 21 | _(pc.count).must_equal 1 22 | end 23 | it "should accept symbols" do 24 | saff = subject.new("gn").terms 25 | _(saff.count).must_equal 1 26 | end 27 | it "should accept complex units" do 28 | complex = subject.new("(mg.(km/s)3/J)2.Pa").terms 29 | _(complex.count).must_equal 5 30 | end 31 | it "should accept more complex units" do 32 | complex = subject.new("4.1(mm/2s3)4.7.3J-2").terms 33 | _(complex.count).must_equal 3 34 | end 35 | it "should accept weird units" do 36 | frequency = subject.new("/s").terms 37 | _(frequency.count).must_equal 1 38 | end 39 | it "should accept units with a factor and unit" do 40 | oddity = subject.new("2ms2").terms 41 | _(oddity.count).must_equal 1 42 | end 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /test/unitwise/expression/matcher_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Expression::Matcher do 4 | describe "::atom(:codes)" do 5 | subject { Unitwise::Expression::Matcher.atom(:primary_code)} 6 | it "must be an Alternative list" do 7 | _(subject).must_be_instance_of Parslet::Atoms::Alternative 8 | end 9 | it "must parse [in_i]" do 10 | _(subject.parse("[in_i]")).must_equal("[in_i]") 11 | end 12 | end 13 | describe "::metric_atom(:names)" do 14 | subject { Unitwise::Expression::Matcher.metric_atom(:names)} 15 | it "must be an Alternative list of names" do 16 | _(subject).must_be_instance_of Parslet::Atoms::Alternative 17 | end 18 | it "must parse 'joule'" do 19 | _(subject.parse('joule')).must_equal('joule') 20 | end 21 | end 22 | 23 | describe "::atom(:slugs)" do 24 | subject { Unitwise::Expression::Matcher.atom(:slugs)} 25 | it "must be an Alternative list of slugs" do 26 | _(subject).must_be_instance_of Parslet::Atoms::Alternative 27 | end 28 | it "must match 'georgian_year'" do 29 | _(subject.parse("mean_gregorian_year")).must_equal("mean_gregorian_year") 30 | end 31 | end 32 | 33 | describe "::prefix(:symbol)" do 34 | subject { Unitwise::Expression::Matcher.prefix(:symbol)} 35 | it "must be an Alternative list of symbols" do 36 | _(subject).must_be_instance_of Parslet::Atoms::Alternative 37 | end 38 | it "must parse 'h'" do 39 | _(subject.parse('h')).must_equal('h') 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/unitwise/expression/parser_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Expression::Parser do 4 | subject { Unitwise::Expression::Parser.new} 5 | describe '#metric_atom' do 6 | it "must match 'N'" do 7 | _(subject.metric_atom.parse('N')[:atom_code]).must_equal('N') 8 | end 9 | end 10 | 11 | describe '#atom' do 12 | it "must match '[in_i]'" do 13 | _(subject.atom.parse('[in_i]')[:atom_code]).must_equal('[in_i]') 14 | end 15 | end 16 | 17 | describe '#prefix' do 18 | it "must match 'k'" do 19 | _(subject.prefix.parse('k')[:prefix_code]).must_equal('k') 20 | end 21 | end 22 | 23 | describe '#annotation' do 24 | it "must match '{foobar}'" do 25 | _(subject.annotation.parse('{foobar}')[:annotation]).must_equal('foobar') 26 | end 27 | end 28 | 29 | describe "#factor" do 30 | it "must match positives and fixnums" do 31 | _(subject.factor.parse('3.2')[:factor]).must_equal(:fixnum => '3.2') 32 | end 33 | it "must match negatives and integers" do 34 | _(subject.factor.parse('-5')[:factor]).must_equal(:integer => '-5') 35 | end 36 | end 37 | 38 | describe "#exponent" do 39 | it "must match positives integers" do 40 | _(subject.exponent.parse('4')[:exponent]).must_equal(:integer => '4') 41 | end 42 | it "must match negative integers" do 43 | _(subject.exponent.parse('-5')[:exponent]).must_equal(:integer => '-5') 44 | end 45 | end 46 | 47 | describe "term" do 48 | it "must match basic atoms" do 49 | _(subject.term.parse('[in_i]')[:term][:atom][:atom_code]).must_equal('[in_i]') 50 | end 51 | it "must match prefixed atoms" do 52 | match = subject.term.parse('ks')[:term] 53 | _(match[:atom][:atom_code]).must_equal('s') 54 | _(match[:prefix][:prefix_code]).must_equal('k') 55 | end 56 | it "must match exponential atoms" do 57 | match = subject.term.parse('cm3')[:term] 58 | _(match[:atom][:atom_code]).must_equal 'm' 59 | _(match[:prefix][:prefix_code]).must_equal 'c' 60 | _(match[:exponent][:integer]).must_equal '3' 61 | end 62 | it "must match factors" do 63 | _(subject.term.parse('3.2')[:term][:factor][:fixnum]).must_equal '3.2' 64 | end 65 | it "must match annotations" do 66 | match = subject.term.parse('N{Normal}')[:term] 67 | _(match[:atom][:atom_code]).must_equal 'N' 68 | _(match[:annotation]).must_equal 'Normal' 69 | end 70 | end 71 | 72 | describe '#group' do 73 | it "must match parentheses with a term" do 74 | match = subject.group.parse('(s2)')[:group][:nested][:left][:term] 75 | _(match[:atom][:atom_code]).must_equal 's' 76 | _(match[:exponent][:integer]).must_equal '2' 77 | end 78 | it "must match nested groups" do 79 | match = subject.group.parse('((kg))')[:group][:nested][:left][:group][:nested][:left][:term] 80 | _(match[:atom][:atom_code]).must_equal 'g' 81 | _(match[:prefix][:prefix_code]).must_equal 'k' 82 | end 83 | it "must pass exponents down" do 84 | match = subject.group.parse('([in_i])3')[:group] 85 | _(match[:exponent][:integer]).must_equal '3' 86 | _(match[:nested][:left][:term][:atom][:atom_code]).must_equal '[in_i]' 87 | end 88 | end 89 | 90 | describe "#expression" do 91 | it "must match left only" do 92 | match = subject.expression.parse('m') 93 | _(match[:left][:term][:atom][:atom_code]).must_equal("m") 94 | end 95 | it "must match left + right + operator" do 96 | match = subject.expression.parse('m.s') 97 | _(match[:left][:term][:atom][:atom_code]).must_equal("m") 98 | _(match[:operator]).must_equal('.') 99 | _(match[:right][:left][:term][:atom][:atom_code]).must_equal('s') 100 | end 101 | it "must match operator + right" do 102 | match = subject.expression.parse("/s") 103 | _(match[:operator]).must_equal('/') 104 | _(match[:right][:left][:term][:atom][:atom_code]).must_equal('s') 105 | end 106 | end 107 | 108 | 109 | end 110 | -------------------------------------------------------------------------------- /test/unitwise/functional_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Functional do 4 | subject { Unitwise::Functional } 5 | %w{cel degf degre hpX hpC tan100 ph ld ln lg 2lg}.each do |function| 6 | describe function do 7 | it 'should convert back and forth' do 8 | number = rand(1000) / 1000.0 9 | there = subject.send "to_#{function}", number 10 | back_again = subject.send "from_#{function}", there 11 | rounded_result = (back_again * 1000).round / 1000.0 12 | _(rounded_result).must_equal number 13 | end 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /test/unitwise/measurement_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require 'test_helper' 3 | require 'support/scale_tests' 4 | 5 | describe Unitwise::Measurement do 6 | let(:described_class) { Unitwise::Measurement } 7 | include ScaleTests 8 | 9 | describe "#new" do 10 | it "should raise an error for unknown units" do 11 | _{ Unitwise::Measurement.new(1,"funkitron") }.must_raise(Unitwise::ExpressionError) 12 | end 13 | end 14 | 15 | describe "#convert_to" do 16 | it "must convert to a similar unit code" do 17 | _(mph.convert_to('km/h').value).must_almost_equal(96.56063) 18 | end 19 | it "must raise an error if the units aren't similar" do 20 | _{ mph.convert_to('N') }.must_raise Unitwise::ConversionError 21 | end 22 | it "must convert special units to their base units" do 23 | _(cel.convert_to('K').value).must_equal 295.15 24 | end 25 | it "must convert base units to special units" do 26 | _(k.convert_to('Cel').value).must_equal 100 27 | end 28 | it "must convert special units to special units" do 29 | _(f.convert_to('Cel').value).must_almost_equal 37 30 | end 31 | it "must convert special units to non-special units" do 32 | _(cel.convert_to("[degR]").value).must_almost_equal(531.27) 33 | end 34 | it "must convert derived units to special units" do 35 | _(r.convert_to("Cel").value).must_almost_equal(0) 36 | end 37 | it "must convert to a unit of another measurement" do 38 | _(mph.convert_to(kmh).value).must_almost_equal(96.56064) 39 | end 40 | it "must convert to and from another unit without losing precision" do 41 | circle = Unitwise::Measurement.new(1, "circle") 42 | _(circle.to_degree.to_circle).must_equal circle 43 | 44 | meter = Unitwise::Measurement.new(10, "meter") 45 | _(meter.to_mile.to_meter).must_equal meter 46 | end 47 | end 48 | 49 | describe "#*" do 50 | it "must multiply by scalars" do 51 | mult = mph * 4 52 | _(mult.value).must_equal 240 53 | _(mult.unit).must_equal Unitwise::Unit.new("[mi_i]/h") 54 | end 55 | it "must multiply similar units" do 56 | mult = mph * kmh 57 | _(mult.value).must_almost_equal 3728.22715342 58 | _(mult.unit).must_equal Unitwise::Unit.new("([mi_i]/h).([mi_i]/h)") 59 | end 60 | it "must multiply unsimilar units" do 61 | mult = mph * mile 62 | _(mult.value).must_equal 180 63 | _(mult.unit).must_equal Unitwise::Unit.new("[mi_i]2/h") 64 | end 65 | it "must multiply canceling units" do 66 | mult = mph * hpm 67 | _(mult.value).must_equal 360 68 | _(mult.unit.to_s).must_equal "1" 69 | end 70 | end 71 | 72 | describe "#/" do 73 | it "must divide by scalars" do 74 | div = kmh / 4 75 | _(div.value).must_equal 25 76 | _(div.unit).must_equal kmh.unit 77 | end 78 | it "must divide by the value of similar units" do 79 | div = kmh / mph 80 | _(div.value).must_almost_equal 1.03561865 81 | _(div.unit.to_s).must_equal '1' 82 | end 83 | it "must divide dissimilar units" do 84 | div = mph / hpm 85 | _(div.value).must_equal 10 86 | _(div.unit.to_s).must_equal "[mi_i]2/h2" 87 | end 88 | end 89 | 90 | describe "#+" do 91 | it "must add values when units are similar" do 92 | added = mph + kmh 93 | _(added.value).must_almost_equal 122.13711922 94 | _(added.unit).must_equal mph.unit 95 | end 96 | it "must raise an error when units are not similar" do 97 | assert_raises(TypeError) { mph + hpm} 98 | end 99 | end 100 | 101 | describe "#-" do 102 | it "must add values when units are similar" do 103 | added = mph - kmh 104 | _(added.value).must_almost_equal(-2.1371192) 105 | _(added.unit).must_equal mph.unit 106 | end 107 | it "must raise an error when units are not similar" do 108 | assert_raises(TypeError) { mph - hpm} 109 | end 110 | end 111 | 112 | describe "#**" do 113 | it "must raise to a power" do 114 | exp = mile ** 3 115 | _(exp.value).must_equal 27 116 | _(exp.unit.to_s).must_equal "[mi_i]3" 117 | end 118 | it "must raise to a negative power" do 119 | exp = mile ** -3 120 | _(exp.value).must_equal 0.037037037037037035 121 | _(exp.unit.to_s).must_equal "1/[mi_i]3" 122 | end 123 | it "must not raise to a weird power" do 124 | _{ mile ** 'weird' }.must_raise TypeError 125 | end 126 | end 127 | 128 | describe "#coerce" do 129 | let(:meter) { Unitwise::Measurement.new(1, 'm') } 130 | it "must coerce numerics" do 131 | _((5 * meter)).must_equal Unitwise::Measurement.new(5, 'm') 132 | end 133 | it "should raise an error for other crap" do 134 | _{ meter.coerce("foo") }.must_raise TypeError 135 | end 136 | end 137 | 138 | describe "equality" do 139 | let(:m) { Unitwise::Measurement.new(1,'m') } 140 | let(:feet) { Unitwise::Measurement.new(20, 'foot') } 141 | let(:mm) { Unitwise::Measurement.new(1000,'mm') } 142 | let(:foot) { Unitwise::Measurement.new(1,'foot') } 143 | let(:g) { Unitwise::Measurement.new(1,'gram') } 144 | it "should be ==" do 145 | assert m == m 146 | assert m == mm 147 | refute m == foot 148 | refute m == g 149 | end 150 | it "should be ===" do 151 | assert m == m 152 | assert m === mm 153 | refute m === foot 154 | refute m == g 155 | end 156 | it "should be equal?" do 157 | assert m.equal?(m) 158 | refute m.equal?(mm) 159 | refute m.equal?(foot) 160 | refute m.equal?(g) 161 | end 162 | it "should be eql?" do 163 | assert m.eql?(m) 164 | refute m.equal?(mm) 165 | refute m.equal?(foot) 166 | refute m.equal?(g) 167 | end 168 | end 169 | 170 | describe "#method_missing" do 171 | let(:meter) { Unitwise::Measurement.new(1, 'm')} 172 | it "must convert 'to_mm'" do 173 | convert = meter.to_mm 174 | _(convert).must_be_instance_of Unitwise::Measurement 175 | _(convert.value).must_equal 1000 176 | end 177 | 178 | it "must convert 'to_foot'" do 179 | convert = meter.to_foot 180 | _(convert).must_be_instance_of Unitwise::Measurement 181 | _(convert.value).must_almost_equal 3.280839895 182 | end 183 | 184 | it "must not convert 'foo'" do 185 | _{ meter.foo }.must_raise NoMethodError 186 | end 187 | 188 | it "must not convert 'to_foo'" do 189 | _{ meter.to_foo }.must_raise NoMethodError 190 | end 191 | 192 | end 193 | 194 | describe "#round" do 195 | it "must round Floats to Integers" do 196 | result = Unitwise::Measurement.new(98.6, "[degF]").round 197 | _(result.value).must_equal(99) 198 | _(result.value).must_be_kind_of(Integer) 199 | end 200 | it "must round Floats to Floats" do 201 | if RUBY_VERSION > '1.8.7' 202 | result = Unitwise::Measurement.new(17.625, "J").round(2) 203 | _(result.value).must_equal(17.63) 204 | _(result.value).must_be_kind_of(Float) 205 | end 206 | end 207 | end 208 | describe "#to_f" do 209 | it "must convert to a float" do 210 | _(f.to_f).must_be_kind_of(Float) 211 | end 212 | end 213 | 214 | describe "#to_i" do 215 | it "must convert to an integer" do 216 | _(k.to_i).must_be_kind_of(Integer) 217 | end 218 | end 219 | 220 | describe "#to_r" do 221 | it "must convert to a rational" do 222 | _(cel.to_r).must_be_kind_of(Rational) 223 | end 224 | end 225 | 226 | describe "#to_s" do 227 | it "should include the simplified value and use the mode it was created with" do 228 | foot = described_class.new(7.00, "foot") 229 | _(foot.to_s).must_equal "7 foot" 230 | meter = described_class.new(BigDecimal("3.142"), "m") 231 | _(meter.to_s).must_equal("3.142 m") 232 | end 233 | it "should accept a mode and print that mode string" do 234 | temp = described_class.new(25, "degree Celsius") 235 | _(temp.to_s(:primary_code)).must_equal("25 Cel") 236 | _(temp.to_s(:symbol)).must_equal("25 °C") 237 | end 238 | it "should fallback when there is no value for the provided mode" do 239 | vol = described_class.new(12, "teaspoon") 240 | _(vol.to_s(:symbol)).must_equal("12 [tsp_us]") 241 | end 242 | it "should not return '1 1' for dimless measurements" do 243 | dimless = described_class.new(1, "1") 244 | _(dimless.to_s).must_equal("1") 245 | end 246 | end 247 | 248 | end 249 | -------------------------------------------------------------------------------- /test/unitwise/number_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Number do 4 | describe ".simplify" do 5 | it "simplifies to an Integer" do 6 | [2, 2.0, BigDecimal(2), Rational(2, 1), '2', '2.0'].each do |n| 7 | assert_equal Unitwise::Number.simplify(n), 2 8 | end 9 | end 10 | 11 | it "simplifies to a Float" do 12 | [2.25, BigDecimal('2.25'), Rational(9, 4), '2.25'].each do |n| 13 | assert_equal Unitwise::Number.simplify(n), 2.25 14 | end 15 | end 16 | 17 | it "doesn't simplify complex BigDecimals" do 18 | s = "1234567890.0123456789" 19 | d = BigDecimal(s) 20 | [s, d].each do |n| 21 | assert_equal Unitwise::Number.simplify(n), d 22 | end 23 | end 24 | 25 | it "doesn't simplify complex Rationals" do 26 | r = Rational(22, 7) 27 | assert_equal Unitwise::Number.simplify(r), r 28 | end 29 | end 30 | 31 | describe ".coefficify" do 32 | it "converts integer values to Integers" do 33 | [1, 1.0, "1.0", "1"].each do |n| 34 | assert Unitwise::Number.coefficify(n).eql?(1) 35 | end 36 | end 37 | 38 | it "converts decimal values to BigDecimals" do 39 | [1.5, "1.5", "1.50", "0.15e1"].each do |n| 40 | assert Unitwise::Number.coefficify(n).eql?(BigDecimal("1.5")) 41 | end 42 | end 43 | end 44 | 45 | describe ".rationalize" do 46 | it "coerces numbers to rationals" do 47 | [1.5, "1.5", BigDecimal("1.5")].each do |n| 48 | assert_equal Unitwise::Number.rationalize(n), Rational(3, 2) 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/unitwise/prefix_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Prefix do 4 | subject { Unitwise::Prefix } 5 | let(:m) { Unitwise::Prefix.find('m')} 6 | describe "::data" do 7 | it "should be an Array" do 8 | _(subject.data).must_be_instance_of Array 9 | end 10 | end 11 | 12 | describe "::all" do 13 | it "should be an array of prefixes" do 14 | _(subject.all).must_be_instance_of Array 15 | _(subject.all.first).must_be_instance_of Unitwise::Prefix 16 | end 17 | end 18 | 19 | describe "#scalar" do 20 | it "should be a number" do 21 | _(m.scalar).must_equal 0.001 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /test/unitwise/scale_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'support/scale_tests' 3 | 4 | describe Unitwise::Scale do 5 | let(:described_class) { Unitwise::Scale } 6 | include ScaleTests 7 | end 8 | -------------------------------------------------------------------------------- /test/unitwise/search_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Search do 4 | describe :class_methods do 5 | describe :all do 6 | subject { Unitwise::Search.all } 7 | it { _(subject).must_be_kind_of Enumerable } 8 | it { _(subject.first).must_be_instance_of Unitwise::Unit } 9 | end 10 | describe :search do 11 | it "should return a list of units" do 12 | search = Unitwise::Search.search('foo') 13 | _(search).must_be_kind_of Enumerable 14 | _(search.first).must_be_instance_of Unitwise::Unit 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/unitwise/term_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise::Term do 4 | describe "instance" do 5 | subject { Unitwise::Term.new(:atom => 'J', :prefix => 'k')} 6 | describe "#atom" do 7 | it "should be an atom" do 8 | _(subject.atom).must_be_instance_of Unitwise::Atom 9 | end 10 | end 11 | 12 | describe "#prefix" do 13 | it "should be a prefix" do 14 | _(subject.prefix).must_be_instance_of Unitwise::Prefix 15 | end 16 | end 17 | 18 | describe "#exponent" do 19 | it "should be an integer" do 20 | _(subject.exponent).must_equal 1 21 | end 22 | end 23 | 24 | describe "#root_terms" do 25 | it "should be an array of terms" do 26 | _(subject.root_terms).must_be_kind_of Array 27 | _(subject.root_terms.first).must_be_instance_of Unitwise::Term 28 | end 29 | end 30 | 31 | describe "#composition" do 32 | it "should be a Multiset" do 33 | _(subject.composition).must_be_instance_of SignedMultiset 34 | end 35 | end 36 | 37 | describe "#scale" do 38 | it "should return value relative to terminal atoms" do 39 | _(subject.scalar).must_equal 1000000.0 40 | end 41 | end 42 | 43 | describe "#frozen?" do 44 | it "should be frozen" do 45 | _(subject.frozen?).must_equal true 46 | end 47 | end 48 | 49 | describe "#to_s" do 50 | it "should return the UCUM code" do 51 | _(subject.to_s).must_equal "kJ" 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /test/unitwise/unit_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require 'test_helper' 3 | 4 | describe Unitwise::Unit do 5 | 6 | let(:ms2) { Unitwise::Unit.new("m/s2") } 7 | let(:kg) { Unitwise::Unit.new("kg") } 8 | let(:psi) { Unitwise::Unit.new("[psi]")} 9 | let(:deg) { Unitwise::Unit.new("deg")} 10 | 11 | 12 | describe "#terms" do 13 | it "must be a collection of terms" do 14 | _(ms2).must_respond_to :terms 15 | _(ms2.terms).must_be_kind_of Enumerable 16 | _(ms2.terms.first).must_be_instance_of Unitwise::Term 17 | end 18 | end 19 | 20 | describe "#root_terms" do 21 | it "must be an array of Terms" do 22 | _(ms2).must_respond_to :terms 23 | _(ms2.root_terms).must_be_kind_of Array 24 | _(ms2.root_terms.first).must_be_instance_of Unitwise::Term 25 | end 26 | end 27 | 28 | describe "#scalar" do 29 | it "must return value relative to terminal atoms" do 30 | _(ms2).must_respond_to :scalar 31 | _(ms2.scalar).must_equal 1 32 | _(psi.scalar).must_almost_equal 6894757.293168361 33 | _(deg.scalar).must_almost_equal 0.0174532925199433 34 | end 35 | end 36 | 37 | describe "#composition" do 38 | it "must be a multiset" do 39 | _(ms2).must_respond_to :terms 40 | _(ms2.composition).must_be_instance_of SignedMultiset 41 | end 42 | end 43 | 44 | describe "#dim" do 45 | it "must be a string representing it's dimensional makeup" do 46 | _(ms2.dim).must_equal 'L.T-2' 47 | _(psi.dim).must_equal 'L-1.M.T-2' 48 | end 49 | end 50 | 51 | describe "#*" do 52 | it "should multiply units" do 53 | mult = kg * ms2 54 | _(mult.expression.to_s).must_match(/kg.*\/s2/) 55 | _(mult.expression.to_s).must_match(/m.*\/s2/) 56 | end 57 | end 58 | 59 | describe "#/" do 60 | it "should divide units" do 61 | div = kg / ms2 62 | _(div.expression.to_s).must_match(/kg.*\/m/) 63 | _(div.expression.to_s).must_match(/s2.*\/m/) 64 | end 65 | end 66 | 67 | describe "#frozen?" do 68 | it "should be frozen" do 69 | kg.scalar 70 | _(kg.frozen?).must_equal true 71 | end 72 | end 73 | 74 | describe "#to_s" do 75 | it "should return an expression in the same mode it was initialized with" do 76 | ['meter','m', 'mm', '%'].each do |name| 77 | _(Unitwise::Unit.new(name).to_s).must_equal(name) 78 | end 79 | end 80 | it "should accept an optional mode to build the expression with" do 81 | temp_change = Unitwise::Unit.new("degree Celsius/hour") 82 | _(temp_change.to_s(:primary_code)).must_equal("Cel/h") 83 | _(temp_change.to_s(:symbol)).must_equal("°C/h") 84 | end 85 | end 86 | 87 | end 88 | -------------------------------------------------------------------------------- /test/unitwise_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | describe Unitwise do 4 | describe '()' do 5 | it "should accept a number and string" do 6 | _(Unitwise(2, 'm/s')).must_be_instance_of Unitwise::Measurement 7 | end 8 | it "should accept a measurement and a string" do 9 | mass = Unitwise(1, "lb") 10 | new_mass = Unitwise(mass, "kg") 11 | _(new_mass.value).must_equal(0.45359237) 12 | _(new_mass.unit.to_s).must_equal("kg") 13 | end 14 | end 15 | 16 | describe 'search' do 17 | it "must return results" do 18 | _(Unitwise.search('foo')).must_be_instance_of(Array) 19 | end 20 | end 21 | 22 | describe 'valid?' do 23 | it 'should reutrn true for valid unit strings' do 24 | _(Unitwise.valid?('millimeter')).must_equal true 25 | end 26 | it 'should return false for invalid unit strings' do 27 | _(Unitwise.valid?('foo')).must_equal false 28 | end 29 | end 30 | 31 | describe 'register' do 32 | it 'should allow custom units to be registered' do 33 | josh = { 34 | names: ["Josh W Lewis", "joshwlewis"], 35 | symbol: "JWL", 36 | primary_code: "jwl", 37 | secondary_code: "jwl", 38 | scale: { 39 | value: 71.875, 40 | unit_code: "[in_i]" 41 | } 42 | } 43 | 44 | Unitwise.register(josh) 45 | 46 | joshes = Unitwise(1, 'mile').to_jwl 47 | 48 | _(joshes.to_i).must_equal(881) 49 | end 50 | end 51 | 52 | it "should have a path" do 53 | _(Unitwise.path).must_match(/unitwise$/) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /unitwise.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'unitwise/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = 'unitwise' 8 | gem.version = Unitwise::VERSION 9 | gem.authors = ['Josh Lewis'] 10 | gem.email = ['josh.w.lewis@gmail.com'] 11 | gem.description = 'Physical quantity and units of measure conversion ' \ 12 | 'and math library' 13 | gem.summary = 'Convert between and perform mathematical operations ' \ 14 | 'on physical quantities and units of measure defined ' \ 15 | 'by the Unified Code for Units of Measure.' 16 | gem.homepage = 'http://github.com/joshwlewis/unitwise' 17 | gem.license = 'MIT' 18 | 19 | gem.files = `git ls-files`.split($RS) 20 | gem.test_files = gem.files.grep(/^test\//) 21 | gem.require_paths = ['lib'] 22 | 23 | gem.required_ruby_version = '>= 2.6' 24 | 25 | gem.add_dependency 'liner', '~> 0.2' 26 | gem.add_dependency 'signed_multiset', '~> 0.2' 27 | gem.add_dependency 'memoizable', '~> 0.4' 28 | gem.add_dependency 'parslet', '~> 2.0' 29 | 30 | gem.add_development_dependency 'nokogiri', '~> 1.13' 31 | gem.add_development_dependency 'pry', '~> 0.14' 32 | gem.add_development_dependency 'minitest', '~> 5.15' 33 | gem.add_development_dependency 'rake', '~> 13.0' 34 | gem.add_development_dependency 'nori', '~> 2.6' 35 | end 36 | --------------------------------------------------------------------------------