asciimath2unitsml 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2eabcf099e41ebc168ea5904e903893af61c95667e0f778fdf17e39e18fd867a
4
- data.tar.gz: 6316d4d968b656249cd69b255e8c68f9f0c7307055fe55d205902c9409d21a9b
3
+ metadata.gz: 99b101395ac516561d586cc12807b99bb025323daced77316aad5550ae502b05
4
+ data.tar.gz: a98eaf190abeabea34d79a2b8df3de76bedbf6264e380ff98f52a0bebfb9f1e8
5
5
  SHA512:
6
- metadata.gz: 4450d314da42fa0941730c668090c7d3bef43ccd8da4a9ad7e5b29a008dcb8abbf9935732229f988a58cbe624ec10eba52e2400efddb7b09a71b6fe51d9ee579
7
- data.tar.gz: 66e66fee285dc9788006a58b98e0e3660b8e829aa29c601eb45a3d2ad263da31582df9267a2a93053f24d0ff89503092395b0ec1ff3ffe8d99ec6161b05a260c
6
+ metadata.gz: e8a0959f44bd66cc3ffe611a5f2ba06fb965357542ed5c29b46ca54d9634af3f8c36f9c396874c041f4212ab086a082f915a84195cb81d0429520acc0494818a
7
+ data.tar.gz: 7a72a21670bf51c98cb0e8ede318e96069f003eea31500cab710f45a48bb5494377650c348b6a1b1d8ba5a444d9b650d4258f9e47a9a8513af3c0c5e6ab9c88f
@@ -16,21 +16,11 @@ jobs:
16
16
  strategy:
17
17
  fail-fast: false
18
18
  matrix:
19
- ruby: [ '2.7', '2.6', '2.5', '2.4' ]
19
+ ruby: [ '3.0', '2.7', '2.6', '2.5' ]
20
20
  os: [ ubuntu-latest, windows-latest, macos-latest ]
21
21
  experimental: [ false ]
22
- include:
23
- - ruby: '3.0'
24
- os: 'ubuntu-latest'
25
- experimental: true
26
- - ruby: '3.0'
27
- os: 'windows-latest'
28
- experimental: true
29
- - ruby: '3.0'
30
- os: 'macos-latest'
31
- experimental: true
32
22
  steps:
33
- - uses: actions/checkout@master
23
+ - uses: actions/checkout@v2
34
24
  with:
35
25
  submodules: true
36
26
 
@@ -40,14 +30,3 @@ jobs:
40
30
  bundler-cache: true
41
31
 
42
32
  - run: bundle exec rake
43
-
44
- tests-passed:
45
- needs: rake
46
- runs-on: ubuntu-latest
47
- steps:
48
- - uses: peter-evans/repository-dispatch@v1
49
- with:
50
- token: ${{ secrets.METANORMA_CI_PAT_TOKEN || secrets.GITHUB_TOKEN }}
51
- repository: ${{ github.repository }}
52
- event-type: notify
53
- client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
data/.hound.yml ADDED
@@ -0,0 +1,5 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
3
+ ruby:
4
+ enabled: true
5
+ config_file: .rubocop.yml
data/.rubocop.yml CHANGED
@@ -1,14 +1,10 @@
1
- # This project follows the Ribose OSS style guide.
2
- # https://github.com/riboseinc/oss-guides
3
- # All project-specific additions and overrides should be specified in this file.
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
4
3
  inherit_from:
5
4
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
6
5
 
7
6
  # local repo-specific modifications
7
+ # ...
8
8
 
9
9
  AllCops:
10
- DisplayCopNames: false
11
- StyleGuideCopsOnly: false
12
- TargetRubyVersion: 2.4
13
- Rails:
14
- Enabled: true
10
+ TargetRubyVersion: 2.5
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "https://rubygems.org"
2
-
2
+
3
3
  # Specify your gem's dependencies in ruby-vobject.gemspec
4
4
  gemspec
data/README.adoc CHANGED
@@ -59,6 +59,24 @@ units. The options are an XML entity, or the values `space` or `nospace` (for wh
59
59
  Standalone prefixes can be recognised by replacing the unit with hyphen; so `unitsml(p-)` corresponds
60
60
  to the standalone prefix "pico" (and is rendered as "p").
61
61
 
62
+ The gem also supports fundamental units, e.g. `unitsml(e)` for the atomic unit of charge, _e_,
63
+ and symbols for dimensions. The latter are entered as `dim_XXX`, where `XXX` is their established symbol:
64
+
65
+ |===
66
+ |Symbol | Dimension
67
+
68
+ |dim_L | Length
69
+ |dim_M | Mass
70
+ |dim_T | Time
71
+ |dim_I | Electric Current
72
+ |dim_Theta | Thermodynamic Temperature
73
+ |dim_N | Amount of Substance
74
+ |dim_J | Luminous Intensity
75
+ |dim_phi | Plane Angle (dimensionless)
76
+ |===
77
+
78
+ e.g. `unitsml(dim_I)` for the dimension of electric current, 𝖨.
79
+
62
80
  == Rendering
63
81
 
64
82
  The output of the gem is MathML, with MathML unit expressions (expressed as `<mi>`,
@@ -141,7 +159,7 @@ The converter is run as:
141
159
  [source,ruby]
142
160
  ----
143
161
  c = Asciimath2UnitsML::Conv.new()
144
- c.Asciimath2UnitsML(1 "unitsml(mm*s^-2)") # AsciiMath string containing UnitsML
162
+ c.Asciimath2UnitsML('1 "unitsml(mm*s^-2)"') # AsciiMath string containing UnitsML
145
163
  c.MathML2UnitsML("<math xmlns='http://www.w3.org/1998/Math/MathML'><mn>7</mn>"\
146
164
  "<mtext>unitsml(kg^-2)</mtext></math>") # AsciiMath string containing <mtext>unitsml()</mtext>
147
165
  c.MathML2UnitsML(Nokogiri::XML("<math xmlns='http://www.w3.org/1998/Math/MathML'><mn>7</mn>"\
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "asciimath2unitsml/version"
6
6
 
@@ -22,32 +22,30 @@ Gem::Specification.new do |spec|
22
22
  spec.require_paths = ["lib"]
23
23
  spec.files = `git ls-files`.split("\n")
24
24
  spec.test_files = `git ls-files -- {spec}/*`.split("\n")
25
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
25
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
26
26
 
27
27
  # get an array of submodule dirs relatively to root repo
28
28
  `git config --file .gitmodules --get-regexp '\\.path$'`
29
29
  .split("\n")
30
30
  .map { |kv_str| kv_str.split(" ") }
31
31
  .each do |(_, submodule_path)|
32
+ # for each submodule, change working directory to that submodule
33
+ Dir.chdir(submodule_path) do
34
+ # issue git ls-files in submodule's directory
35
+ submodule_files = `git ls-files | grep -i '.yaml$'`.split($\)
32
36
 
33
- # for each submodule, change working directory to that submodule
34
- Dir.chdir(submodule_path) do
35
-
36
- # issue git ls-files in submodule's directory
37
- submodule_files = `git ls-files | grep -i '.yaml$'`.split($\)
38
-
39
- submodule_files_paths = submodule_files.map do |filename|
40
- File.join submodule_path, filename
41
- end
42
-
43
- # add relative paths to gem.files
44
- spec.files += submodule_files_paths
37
+ submodule_files_paths = submodule_files.map do |filename|
38
+ File.join submodule_path, filename
45
39
  end
40
+
41
+ # add relative paths to gem.files
42
+ spec.files += submodule_files_paths
43
+ end
46
44
  end
47
45
 
48
46
  spec.add_dependency "asciimath"
49
47
  spec.add_dependency "htmlentities"
50
- spec.add_dependency "nokogiri", "~> 1.10.4"
48
+ spec.add_dependency "nokogiri", "~> 1.12"
51
49
  spec.add_dependency "rsec", "~> 1.0.0"
52
50
 
53
51
  spec.add_development_dependency "bundler"
@@ -56,10 +54,9 @@ Gem::Specification.new do |spec|
56
54
  spec.add_development_dependency "guard", "~> 2.14"
57
55
  spec.add_development_dependency "guard-rspec", "~> 4.7"
58
56
  spec.add_development_dependency "rake", "~> 12.0"
57
+ spec.add_development_dependency "rexml"
59
58
  spec.add_development_dependency "rspec", "~> 3.6"
60
59
  spec.add_development_dependency "rubocop", "~> 1.5.2"
61
60
  spec.add_development_dependency "simplecov", "~> 0.15"
62
61
  spec.add_development_dependency "timecop", "~> 0.9"
63
- spec.add_development_dependency "rexml"
64
62
  end
65
-
data/bin/rspec CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+
3
3
  # This file was generated by Bundler.
4
4
  #
5
5
  # The application 'rspec' is installed as part of a gem, and
@@ -15,4 +15,3 @@ require "rubygems"
15
15
  require "bundler/setup"
16
16
 
17
17
  load Gem.bin_path("rspec-core", "rspec")
18
-
@@ -3,10 +3,13 @@ require "nokogiri"
3
3
  require "htmlentities"
4
4
  require "yaml"
5
5
  require "rsec"
6
+ require_relative "read"
7
+ require_relative "dimensions"
6
8
  require_relative "string"
7
9
  require_relative "parse"
8
10
  require_relative "render"
9
11
  require_relative "unit"
12
+ require_relative "validate"
10
13
 
11
14
  module Asciimath2UnitsML
12
15
  MATHML_NS = "http://www.w3.org/1998/Math/MathML".freeze
@@ -14,120 +17,86 @@ module Asciimath2UnitsML
14
17
 
15
18
  class Conv
16
19
  def initialize(options = {})
17
- @dimensions_id = read_yaml("../unitsdb/dimensions.yaml").
18
- each_with_object({}) do |(k, v), m|
20
+ @dimensions_id = read_yaml("../unitsdb/dimensions.yaml")
21
+ .each_with_object({}) do |(k, v), m|
19
22
  m[k.to_s] = UnitsDB::Dimension.new(k, v)
20
23
  end
21
- @prefixes_id = read_yaml("../unitsdb/prefixes.yaml").
22
- each_with_object({}) do |(k, v), m|
24
+ @dimensions = flip_name_and_symbols(@dimensions_id)
25
+ @prefixes_id = read_yaml("../unitsdb/prefixes.yaml")
26
+ .each_with_object({}) do |(k, v), m|
23
27
  m[k] = UnitsDB::Prefix.new(k, v)
24
28
  end
25
29
  @prefixes = flip_name_and_symbol(@prefixes_id)
26
- @quantities = read_yaml("../unitsdb/quantities.yaml").
27
- each_with_object({}) do |(k, v), m|
30
+ @quantities = read_yaml("../unitsdb/quantities.yaml")
31
+ .each_with_object({}) do |(k, v), m|
28
32
  m[k.to_s] = UnitsDB::Quantity.new(k, v)
29
33
  end
30
- @units_id = read_yaml("../unitsdb/units.yaml").
31
- each_with_object({}) do |(k, v), m|
34
+ @units_id = read_yaml("../unitsdb/units.yaml")
35
+ .each_with_object({}) do |(k, v), m|
32
36
  m[k.to_s] = UnitsDB::Unit.new(k.to_s, v)
33
37
  end
34
38
  @units = flip_name_and_symbols(@units_id)
35
- @symbols = @units.each_with_object({}) do |(k, v), m|
39
+ @symbols = @units.merge(@dimensions).each_with_object({}) do |(_k, v), m|
36
40
  v.symbolids.each { |x| m[x] = v.symbols_hash[x] }
37
41
  end
38
- @parser = parser
39
- @multiplier = multiplier(options[:multiplier] || "\u00b7")
42
+ @parser, @dim_parser = parsers
43
+ @multiplier = multiplier(options[:multiplier] || "\u22c5")
40
44
  end
41
45
 
42
- def float_to_display(f)
43
- ret = f.to_f.round(1).to_s.sub(/\.0$/, "")
46
+ def float_to_display(float)
47
+ float.to_f.round(1).to_s.sub(/\.0$/, "")
44
48
  end
45
49
 
46
50
  def prefix(units)
47
- units.map { |u| u[:prefix] }.reject { |u| u.nil? }.uniq.map do |p|
48
- <<~END
49
- <Prefix xmlns='#{UNITSML_NS}' prefixBase='#{@prefixes[p].base}'
50
- prefixPower='#{@prefixes[p].power}' xml:id='#{@prefixes[p].id}'>
51
- <PrefixName xml:lang="en">#{@prefixes[p].name}</PrefixName>
52
- <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol>
53
- <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol>
54
- <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol>
55
- <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol>
56
- </Prefix>
57
- END
51
+ units.map { |u| u[:prefix] }.reject(&:nil?).uniq.map do |p|
52
+ <<~XML
53
+ <Prefix xmlns='#{UNITSML_NS}' prefixBase='#{@prefixes[p].base}'
54
+ prefixPower='#{@prefixes[p].power}' xml:id='#{@prefixes[p].id}'>
55
+ <PrefixName xml:lang="en">#{@prefixes[p].name}</PrefixName>
56
+ <PrefixSymbol type="ASCII">#{@prefixes[p].ascii}</PrefixSymbol>
57
+ <PrefixSymbol type="unicode">#{@prefixes[p].unicode}</PrefixSymbol>
58
+ <PrefixSymbol type="LaTeX">#{@prefixes[p].latex}</PrefixSymbol>
59
+ <PrefixSymbol type="HTML">#{htmlent @prefixes[p].html}</PrefixSymbol>
60
+ </Prefix>
61
+ XML
58
62
  end.join("\n")
59
63
  end
60
64
 
61
- def dimension_components(dims)
62
- return if dims.nil? || dims.empty?
63
- <<~END
64
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
65
- #{dims.map { |u| dimension1(u) }.join("\n") }
66
- </Dimension>
67
- END
68
- end
69
-
70
- U2D = {
71
- "m" => { dimension: "Length", order: 1, symbol: "L" },
72
- "g" => { dimension: "Mass", order: 2, symbol: "M" },
73
- "kg" => { dimension: "Mass", order: 2, symbol: "M" },
74
- "s" => { dimension: "Time", order: 3, symbol: "T" },
75
- "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
76
- "K" => { dimension: "ThermodynamicTemperature", order: 5,
77
- symbol: "Theta" },
78
- "degK" => { dimension: "ThermodynamicTemperature", order: 5,
79
- symbol: "Theta" },
80
- "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
81
- "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
82
- "deg" => { dimension: "PlaneAngle", order: 8, symbol: "Phi" },
83
- }.freeze
84
-
85
- def units2dimensions(units)
86
- norm = decompose_units(units)
87
- return if norm.any? do |u|
88
- u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil?
89
- end
90
- norm.map do |u|
91
- { dimension: U2D[u[:unit]][:dimension],
92
- unit: u[:unit],
93
- exponent: u[:exponent] || 1,
94
- symbol: U2D[u[:unit]][:symbol] }
95
- end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
96
- end
97
-
98
- def dimension1(u)
99
- %(<#{u[:dimension]} symbol="#{u[:symbol]}"
100
- powerNumerator="#{float_to_display(u[:exponent])}"/>)
101
- end
102
-
103
- def dim_id(dims)
104
- return nil if dims.nil? || dims.empty?
105
- dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
106
- dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
107
- AmountOfSubstance LuminousIntensity PlaneAngle)
108
- .map { |h| dimhash.dig(h, :exponent) }.join(":")
109
- id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }&.
110
- first&.id and return id.to_s
111
- "D_" + dims.map do |d|
112
- U2D[d[:unit]][:symbol] +
113
- (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
114
- end.join("")
115
- end
116
-
117
65
  def decompose_units(units)
118
66
  gather_units(units_only(units).map { |u| decompose_unit(u) }.flatten)
119
67
  end
120
68
 
121
69
  def gather_units(units)
122
- units.sort { |a, b| a[:unit] <=> b[:unit] }.each_with_object([]) do |k, m|
70
+ if units[0][:dim] then gather_dimensions(units)
71
+ else gather_units1(units)
72
+ end
73
+ end
74
+
75
+ def gather_units1(units)
76
+ units.sort_by { |a| a[:unit] }.each_with_object([]) do |k, m|
123
77
  if m.empty? || m[-1][:unit] != k[:unit] then m << k
124
78
  else
125
79
  m[-1] = {
126
80
  prefix: combine_prefixes(
127
- @prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]),
81
+ @prefixes[m[-1][:prefix]], @prefixes[k[:prefix]]
82
+ ),
128
83
  unit: m[-1][:unit],
129
84
  exponent: (k[:exponent]&.to_f || 1) +
130
- (m[-1][:exponent]&.to_f || 1) }
85
+ (m[-1][:exponent]&.to_f || 1),
86
+ }
87
+ end
88
+ end
89
+ end
90
+
91
+ def gather_dimensions(units)
92
+ units.sort_by { |a| a[:dim] }.each_with_object([]) do |k, m|
93
+ if m.empty? || m[-1][:dim] != k[:dim] then m << k
94
+ else
95
+ m[-1] = {
96
+ dim: m[-1][:dim],
97
+ exponent: (k[:exponent]&.to_f || 1) +
98
+ (m[-1][:exponent]&.to_f || 1),
99
+ }
131
100
  end
132
101
  end
133
102
  end
@@ -135,16 +104,18 @@ module Asciimath2UnitsML
135
104
  # treat g not kg as base unit: we have stripped the prefix k in parsing
136
105
  # reduce units down to basic units
137
106
  def decompose_unit(u)
138
- if u[:unit].nil? then u
139
- elsif u[:unit] == "g" then u
140
- elsif @units[u[:unit]].system_type == "SI_base" then u
107
+ if u[:unit].nil? || u[:unit] == "g" ||
108
+ @units[u[:unit]].system_type == "SI_base" then u
141
109
  elsif !@units[u[:unit]].si_derived_bases
142
110
  { prefix: u[:prefix], unit: "unknown", exponent: u[:exponent] }
143
111
  else
144
112
  @units[u[:unit]].si_derived_bases.each_with_object([]) do |k, m|
145
- prefix = !k[:prefix].nil? && !k[:prefix].empty? ?
146
- combine_prefixes(@prefixes_id[k[:prefix]], @prefixes[u[:prefix]]) :
147
- u[:prefix]
113
+ prefix = if !k[:prefix].nil? && !k[:prefix].empty?
114
+ combine_prefixes(@prefixes_id[k[:prefix]],
115
+ @prefixes[u[:prefix]])
116
+ else
117
+ u[:prefix]
118
+ end
148
119
  m << { prefix: prefix,
149
120
  unit: @units_id[k[:id]].symbolid,
150
121
  exponent: (k[:power]&.to_i || 1) * (u[:exponent]&.to_f || 1) }
@@ -157,6 +128,7 @@ module Asciimath2UnitsML
157
128
  return p1.symbolid if p2.nil?
158
129
  return p2.symbolid if p1.nil?
159
130
  return "unknown" if p1.base != p2.base
131
+
160
132
  @prefixes.each do |_, p|
161
133
  return p.symbolid if p.base == p1.base && p.power == p1.power + p2.power
162
134
  end
@@ -174,43 +146,26 @@ module Asciimath2UnitsML
174
146
  def quantity(normtext, quantity)
175
147
  return unless @units[normtext] && @units[normtext].quantities.size == 1 ||
176
148
  @quantities[quantity]
149
+
177
150
  id = quantity || @units[normtext].quantities.first
178
151
  @units[normtext]&.dimension and
179
152
  dim = %( dimensionURL="##{@units[normtext].dimension}")
180
- <<~END
181
- <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
182
- #{quantityname(id)}
183
- </Quantity>
184
- END
185
- end
186
-
187
- def dimid2dimensions(normtext)
188
- @dimensions_id[normtext].keys.map do |k|
189
- { dimension: k,
190
- symbol: U2D.values.select { |v| v[:dimension] == k }.first[:symbol],
191
- exponent: @dimensions_id[normtext].exponent(k) }
192
- end
193
- end
194
-
195
- def dimension(normtext)
196
- return unless @units[normtext]&.dimension
197
- dims = dimid2dimensions(@units[normtext]&.dimension)
198
- <<~END
199
- <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
200
- #{dims.map { |u| dimension1(u) }.join("\n") }
201
- </Dimension>
202
- END
153
+ <<~XML
154
+ <Quantity xmlns='#{UNITSML_NS}' xml:id="#{id}"#{dim} quantityType="base">
155
+ #{quantityname(id)}
156
+ </Quantity>
157
+ XML
203
158
  end
204
159
 
205
160
  def unitsml(units, origtext, normtext, quantity, name)
206
161
  dims = units2dimensions(units)
207
- <<~END
208
- #{unit(units, origtext, normtext, dims, name)}
209
- #{prefix(units)}
210
- #{dimension(normtext)}
211
- #{dimension_components(dims)}
212
- #{quantity(normtext, quantity)}
213
- END
162
+ <<~XML
163
+ #{unit(units, origtext, normtext, dims, name)}
164
+ #{prefix(units)}
165
+ #{dimension(normtext)}
166
+ #{dimension_components(dims)}
167
+ #{quantity(normtext, quantity)}
168
+ XML
214
169
  end
215
170
  end
216
171
  end
@@ -0,0 +1,117 @@
1
+ module Asciimath2UnitsML
2
+ class Conv
3
+ def dimension_components(dims)
4
+ return if dims.nil? || dims.empty?
5
+
6
+ <<~XML
7
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{dim_id(dims)}">
8
+ #{dims.map { |u| dimension1(u) }.join("\n")}
9
+ </Dimension>
10
+ XML
11
+ end
12
+
13
+ U2D = {
14
+ "m" => { dimension: "Length", order: 1, symbol: "L" },
15
+ "g" => { dimension: "Mass", order: 2, symbol: "M" },
16
+ "kg" => { dimension: "Mass", order: 2, symbol: "M" },
17
+ "s" => { dimension: "Time", order: 3, symbol: "T" },
18
+ "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
19
+ "K" => { dimension: "ThermodynamicTemperature", order: 5,
20
+ symbol: "Theta" },
21
+ "degK" => { dimension: "ThermodynamicTemperature", order: 5,
22
+ symbol: "Theta" },
23
+ "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
24
+ "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
25
+ "deg" => { dimension: "PlaneAngle", order: 8, symbol: "phi" },
26
+ }.freeze
27
+
28
+ Dim2D = {
29
+ "dim_L" => U2D["m"],
30
+ "dim_M" => U2D["g"],
31
+ "dim_T" => U2D["s"],
32
+ "dim_I" => U2D["A"],
33
+ "dim_Theta" => U2D["K"],
34
+ "dim_N" => U2D["mol"],
35
+ "dim_J" => U2D["cd"],
36
+ "dim_phi" => U2D["deg"],
37
+ }.freeze
38
+
39
+ def units2dimensions(units)
40
+ norm = decompose_units(units)
41
+ return units2dimensions_dim_input(norm) if norm[0][:dim]
42
+ return if norm.any? do |u|
43
+ u[:unit] == "unknown" || u[:prefix] == "unknown" || u[:unit].nil?
44
+ end
45
+
46
+ norm.map do |u|
47
+ { dimension: U2D[u[:unit]][:dimension],
48
+ unit: u[:unit],
49
+ exponent: u[:exponent] || 1,
50
+ symbol: U2D[u[:unit]][:symbol] }
51
+ end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
52
+ end
53
+
54
+ def units2dimensions_dim_input(norm)
55
+ norm.map do |u|
56
+ { dimension: Dim2D[u[:dim]][:dimension],
57
+ exponent: u[:exponent] || 1,
58
+ id: u[:dim],
59
+ symbol: Dim2D[u[:dim]][:symbol] }
60
+ end.sort { |a, b| Dim2D[a[:id]][:order] <=> Dim2D[b[:id]][:order] }
61
+ end
62
+
63
+ def dimension1(dim)
64
+ %(<#{dim[:dimension]} symbol="#{dim[:symbol]}"
65
+ powerNumerator="#{float_to_display(dim[:exponent])}"/>)
66
+ end
67
+
68
+ def dim_id(dims)
69
+ return nil if dims.nil? || dims.empty?
70
+
71
+ dimhash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
72
+ dimsvector = %w(Length Mass Time ElectricCurrent ThermodynamicTemperature
73
+ AmountOfSubstance LuminousIntensity PlaneAngle)
74
+ .map { |h| dimhash.dig(h, :exponent) }.join(":")
75
+ id = @dimensions_id&.values&.select { |d| d.vector == dimsvector }
76
+ &.first&.id and return id.to_s
77
+ "D_" + dims.map do |d|
78
+ (U2D.dig(d[:unit], :symbol) || Dim2D.dig(d[:id], :symbol)) +
79
+ (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
80
+ end.join("")
81
+ end
82
+
83
+ def decompose_units(units)
84
+ gather_units(units_only(units).map { |u| decompose_unit(u) }.flatten)
85
+ end
86
+
87
+ def dimid2dimensions(normtext)
88
+ @dimensions_id[normtext].keys.map do |k|
89
+ { dimension: k,
90
+ symbol: U2D.values.select { |v| v[:dimension] == k }.first[:symbol],
91
+ exponent: @dimensions_id[normtext].exponent(k) }
92
+ end
93
+ end
94
+
95
+ def dimension(normtext)
96
+ return unless @units[normtext]&.dimension
97
+
98
+ dims = dimid2dimensions(@units[normtext]&.dimension)
99
+ <<~XML
100
+ <Dimension xmlns='#{UNITSML_NS}' xml:id="#{@units[normtext]&.dimension}">
101
+ #{dims.map { |u| dimension1(u) }.join("\n")}
102
+ </Dimension>
103
+ XML
104
+ end
105
+
106
+ def unitsml(units, origtext, normtext, quantity, name)
107
+ dims = units2dimensions(units)
108
+ <<~XML
109
+ #{unit(units, origtext, normtext, dims, name)}
110
+ #{prefix(units)}
111
+ #{dimension(normtext)}
112
+ #{dimension_components(dims)}
113
+ #{quantity(normtext, quantity)}
114
+ XML
115
+ end
116
+ end
117
+ end