seimi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +111 -0
- data/Rakefile +10 -0
- data/exe/seimi +9 -0
- data/lib/seimi/cli.rb +63 -0
- data/lib/seimi/elements.rb +124 -0
- data/lib/seimi/equation.rb +175 -0
- data/lib/seimi/errors.rb +15 -0
- data/lib/seimi/formula/parser.rb +361 -0
- data/lib/seimi/formula.rb +65 -0
- data/lib/seimi/isotopes.rb +1115 -0
- data/lib/seimi/kanji.rb +118 -0
- data/lib/seimi/sangi.rb +66 -0
- data/lib/seimi/version.rb +5 -0
- data/lib/seimi.rb +15 -0
- data/test/test_buntai.rb +26 -0
- data/test/test_cli.rb +25 -0
- data/test/test_equation.rb +73 -0
- data/test/test_formula.rb +78 -0
- data/test/test_helper.rb +6 -0
- data/test/test_kanji.rb +20 -0
- metadata +94 -0
data/lib/seimi/kanji.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Seimi
|
|
4
|
+
module Kanji
|
|
5
|
+
DIGITS = %w[〇 一 二 三 四 五 六 七 八 九].freeze
|
|
6
|
+
PARSE_DIGITS = DIGITS.each_with_index.to_h.merge("零" => 0).freeze
|
|
7
|
+
SMALL_UNITS = { "十" => 10, "百" => 100, "千" => 1000 }.freeze
|
|
8
|
+
LARGE_UNITS = [["兆", 1_000_000_000_000], ["億", 100_000_000], ["万", 10_000]].freeze
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def from_i(number)
|
|
13
|
+
integer = Integer(number)
|
|
14
|
+
return "零" if integer.zero?
|
|
15
|
+
return "負#{from_i(-integer)}" if integer.negative?
|
|
16
|
+
|
|
17
|
+
groups = []
|
|
18
|
+
while integer.positive?
|
|
19
|
+
groups << integer % 10_000
|
|
20
|
+
integer /= 10_000
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
groups.each_with_index.filter_map do |group, index|
|
|
24
|
+
next if group.zero?
|
|
25
|
+
|
|
26
|
+
"#{under_10000(group)}#{large_unit_at(index)}"
|
|
27
|
+
end.reverse.join
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_i(text)
|
|
31
|
+
source = String(text)
|
|
32
|
+
return 0 if source == "零" || source == "〇"
|
|
33
|
+
|
|
34
|
+
negative = source.start_with?("負")
|
|
35
|
+
source = source.delete_prefix("負")
|
|
36
|
+
total = 0
|
|
37
|
+
|
|
38
|
+
LARGE_UNITS.each do |unit, scale|
|
|
39
|
+
next unless source.include?(unit)
|
|
40
|
+
|
|
41
|
+
head, source = source.split(unit, 2)
|
|
42
|
+
total += under_10000_to_i(head) * scale
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
total += under_10000_to_i(source)
|
|
46
|
+
negative ? -total : total
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def rational(value)
|
|
50
|
+
rational = Rational(value)
|
|
51
|
+
return from_i(rational.numerator) if rational.denominator == 1
|
|
52
|
+
|
|
53
|
+
numerator = rational.numerator
|
|
54
|
+
sign = numerator.negative? ? "負" : ""
|
|
55
|
+
"#{sign}#{from_i(rational.denominator)}分の#{from_i(numerator.abs)}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def decimal(value, digits = 3)
|
|
59
|
+
formatted = format("%.#{digits}f", value)
|
|
60
|
+
integer, fraction = formatted.split(".", 2)
|
|
61
|
+
"#{from_i(integer.to_i)}・#{fraction.chars.map { |char| DIGITS[char.to_i] }.join}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def under_10000(number)
|
|
65
|
+
integer = Integer(number)
|
|
66
|
+
raise ArgumentError, "out of range" if integer.negative? || integer >= 10_000
|
|
67
|
+
|
|
68
|
+
thousands, rem = integer.divmod(1000)
|
|
69
|
+
hundreds, rem = rem.divmod(100)
|
|
70
|
+
tens, ones = rem.divmod(10)
|
|
71
|
+
|
|
72
|
+
[
|
|
73
|
+
unit_part(thousands, "千"),
|
|
74
|
+
unit_part(hundreds, "百"),
|
|
75
|
+
unit_part(tens, "十"),
|
|
76
|
+
ones.positive? ? DIGITS[ones] : nil
|
|
77
|
+
].compact.join
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def large_unit_at(index)
|
|
81
|
+
case index
|
|
82
|
+
when 0 then ""
|
|
83
|
+
when 1 then "万"
|
|
84
|
+
when 2 then "億"
|
|
85
|
+
when 3 then "兆"
|
|
86
|
+
else
|
|
87
|
+
raise ArgumentError, "number is too large"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def unit_part(value, unit)
|
|
92
|
+
return nil if value.zero?
|
|
93
|
+
return unit if value == 1
|
|
94
|
+
|
|
95
|
+
"#{DIGITS[value]}#{unit}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def under_10000_to_i(text)
|
|
99
|
+
return 0 if text.empty?
|
|
100
|
+
|
|
101
|
+
total = 0
|
|
102
|
+
current = nil
|
|
103
|
+
|
|
104
|
+
text.each_char do |char|
|
|
105
|
+
if PARSE_DIGITS.key?(char)
|
|
106
|
+
current = PARSE_DIGITS.fetch(char)
|
|
107
|
+
elsif SMALL_UNITS.key?(char)
|
|
108
|
+
total += (current || 1) * SMALL_UNITS.fetch(char)
|
|
109
|
+
current = nil
|
|
110
|
+
else
|
|
111
|
+
raise ArgumentError, "unknown kanji digit: #{char}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
total + (current || 0)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/seimi/sangi.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Seimi
|
|
4
|
+
module Sangi
|
|
5
|
+
CELL_WIDTH = 5
|
|
6
|
+
CELL_HEIGHT = 5
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def render(number)
|
|
11
|
+
digits = Integer(number).abs.digits.reverse
|
|
12
|
+
cells = digits.each_with_index.map do |digit, index|
|
|
13
|
+
power = digits.length - index - 1
|
|
14
|
+
power.even? ? vertical_cell(digit) : horizontal_cell(digit)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
[frame("┌", "┬", "┐", cells.length),
|
|
18
|
+
*body_lines(cells),
|
|
19
|
+
frame("└", "┴", "┘", cells.length)].join("\n")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def vertical_cell(digit)
|
|
23
|
+
return empty_cell if digit.zero?
|
|
24
|
+
|
|
25
|
+
rows = blank_cell
|
|
26
|
+
if digit <= 5
|
|
27
|
+
digit.times { |index| rows[index] = " | " }
|
|
28
|
+
else
|
|
29
|
+
rows[0] = " --- "
|
|
30
|
+
(digit - 5).times { |index| rows[index + 1] = " | " }
|
|
31
|
+
end
|
|
32
|
+
rows
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def horizontal_cell(digit)
|
|
36
|
+
return empty_cell if digit.zero?
|
|
37
|
+
|
|
38
|
+
rows = blank_cell
|
|
39
|
+
if digit <= 5
|
|
40
|
+
digit.times { |index| rows[CELL_HEIGHT - 1 - index] = " --- " }
|
|
41
|
+
else
|
|
42
|
+
rows[0] = " | "
|
|
43
|
+
(digit - 5).times { |index| rows[CELL_HEIGHT - 1 - index] = " --- " }
|
|
44
|
+
end
|
|
45
|
+
rows
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def body_lines(cells)
|
|
49
|
+
(0...CELL_HEIGHT).map do |line|
|
|
50
|
+
"│#{cells.map { |cell| cell[line] }.join("│")}│"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def frame(left, middle, right, count)
|
|
55
|
+
"#{left}#{Array.new(count, "─" * CELL_WIDTH).join(middle)}#{right}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def blank_cell
|
|
59
|
+
Array.new(CELL_HEIGHT, " " * CELL_WIDTH)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def empty_cell
|
|
63
|
+
[" ", " ", " [ ] ", " ", " "]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/seimi.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "seimi/version"
|
|
4
|
+
require_relative "seimi/errors"
|
|
5
|
+
require_relative "seimi/elements"
|
|
6
|
+
require_relative "seimi/kanji"
|
|
7
|
+
require_relative "seimi/sangi"
|
|
8
|
+
require_relative "seimi/formula"
|
|
9
|
+
require_relative "seimi/equation"
|
|
10
|
+
|
|
11
|
+
module Seimi
|
|
12
|
+
def self.molar_mass(formula)
|
|
13
|
+
Formula.parse(formula).molar_mass
|
|
14
|
+
end
|
|
15
|
+
end
|
data/test/test_buntai.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class TestBuntai < Minitest::Test
|
|
6
|
+
PROHIBITED = /(です|ます|である|ください)/
|
|
7
|
+
REQUIRED = /(候|也|べし|給え)/
|
|
8
|
+
|
|
9
|
+
def test_error_messages_have_required_style
|
|
10
|
+
constants = Seimi.constants.grep(/\AMSG_/)
|
|
11
|
+
|
|
12
|
+
refute_empty constants
|
|
13
|
+
constants.each do |constant|
|
|
14
|
+
value = Seimi.const_get(constant)
|
|
15
|
+
refute_match PROHIBITED, value
|
|
16
|
+
assert_match REQUIRED, value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_readme_body_uses_required_style
|
|
21
|
+
readme = File.read(File.expand_path("../README.md", __dir__), encoding: "UTF-8")
|
|
22
|
+
body = readme.gsub(/```.*?```/m, "")
|
|
23
|
+
|
|
24
|
+
refute_match PROHIBITED, body
|
|
25
|
+
end
|
|
26
|
+
end
|
data/test/test_cli.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require_relative "test_helper"
|
|
6
|
+
|
|
7
|
+
class TestCLI < Minitest::Test
|
|
8
|
+
EXE = File.expand_path("../exe/seimi", __dir__)
|
|
9
|
+
|
|
10
|
+
def test_kaibou_success
|
|
11
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-Ilib", EXE, "kaibou", "Ca(OH)2")
|
|
12
|
+
|
|
13
|
+
assert_predicate status, :success?, stderr
|
|
14
|
+
assert_includes stdout, "候"
|
|
15
|
+
assert_includes stdout, "七十四"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_kaibou_error
|
|
19
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-Ilib", EXE, "kaibou", "Xx")
|
|
20
|
+
|
|
21
|
+
refute_predicate status, :success?, stdout
|
|
22
|
+
assert_equal 1, status.exitstatus
|
|
23
|
+
assert_includes stderr, "咎:"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class TestEquation < Minitest::Test
|
|
6
|
+
def test_iron_oxide
|
|
7
|
+
result = Seimi::Equation.balance("Fe + O2 -> Fe2O3")
|
|
8
|
+
|
|
9
|
+
assert_equal [4, 3, 2], result.coefficients
|
|
10
|
+
assert_equal ["Fe", "O2", "Fe2O3"], result.species
|
|
11
|
+
assert_equal 2, result.lhs_size
|
|
12
|
+
assert_equal ["Fe", "O"], result.elements
|
|
13
|
+
assert_equal "4Fe + 3O2 -> 2Fe2O3", result.to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_propane
|
|
17
|
+
result = Seimi::Equation.balance("C3H8 + O2 -> CO2 + H2O")
|
|
18
|
+
|
|
19
|
+
assert_equal [1, 5, 3, 4], result.coefficients
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_permanganate_and_hydrochloric_acid
|
|
23
|
+
result = Seimi::Equation.balance("KMnO4 + HCl -> KCl + MnCl2 + H2O + Cl2")
|
|
24
|
+
|
|
25
|
+
assert_equal [2, 16, 2, 2, 8, 5], result.coefficients
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_unbalanced
|
|
29
|
+
assert_raises(Seimi::UnbalancedError) do
|
|
30
|
+
Seimi::Equation.balance("H2 -> H2O")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_matrix_contains_rational_values
|
|
35
|
+
result = Seimi::Equation.balance("Fe + O2 -> Fe2O3")
|
|
36
|
+
|
|
37
|
+
assert result.matrix.all? { |row| row.all? { |value| value.is_a?(Rational) } }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_ionic_equation_balances_charge
|
|
41
|
+
result = Seimi::Equation.balance("Ag+ + Cl- -> AgCl")
|
|
42
|
+
|
|
43
|
+
assert_equal [1, 1, 1], result.coefficients
|
|
44
|
+
assert_includes result.elements, Seimi::Equation::CHARGE_BALANCE_SYMBOL
|
|
45
|
+
assert_equal [Rational(1), Rational(-1), Rational(0)], result.matrix.last
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_ionic_equation_without_spaces
|
|
49
|
+
result = Seimi::Equation.balance("Ag++Cl-->AgCl")
|
|
50
|
+
|
|
51
|
+
assert_equal [1, 1, 1], result.coefficients
|
|
52
|
+
assert_equal ["Ag+", "Cl-", "AgCl"], result.species
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_multivalent_ionic_equation_without_spaces
|
|
56
|
+
result = Seimi::Equation.balance("Ba^2++SO4^2-->BaSO4")
|
|
57
|
+
|
|
58
|
+
assert_equal [1, 1, 1], result.coefficients
|
|
59
|
+
assert_equal ["Ba^2+", "SO4^2-", "BaSO4"], result.species
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_hydrate_equation
|
|
63
|
+
result = Seimi::Equation.balance("CuSO4·5H2O -> CuSO4 + H2O")
|
|
64
|
+
|
|
65
|
+
assert_equal [1, 1, 5], result.coefficients
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_isotope_labels_are_balanced_separately
|
|
69
|
+
assert_raises(Seimi::UnbalancedError) do
|
|
70
|
+
Seimi::Equation.balance("[13C]O2 -> CO2")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class TestFormula < Minitest::Test
|
|
6
|
+
def test_molar_mass_shortcut
|
|
7
|
+
assert_in_delta 18.015, Seimi.molar_mass("H2O"), 0.001
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_parenthesized_formula
|
|
11
|
+
assert_equal({ "Ca" => 1, "O" => 2, "H" => 2 }, Seimi::Formula.parse("Ca(OH)2").composition)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_nested_count_formula
|
|
15
|
+
assert_equal({ "Mg" => 3, "P" => 2, "O" => 8 }, Seimi::Formula.parse("Mg3(PO4)2").composition)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_unknown_element_message
|
|
19
|
+
error = assert_raises(Seimi::UnknownElementError) do
|
|
20
|
+
Seimi::Formula.parse("Xx")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
assert_equal format(Seimi::MSG_UNKNOWN_ELEMENT, "Xx"), error.message
|
|
24
|
+
assert_equal 118, Seimi::ELEMENTS.length
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_ion_charge
|
|
28
|
+
sulfate = Seimi::Formula.parse("SO4^2-")
|
|
29
|
+
ammonium = Seimi::Formula.parse("NH4+")
|
|
30
|
+
iron = Seimi::Formula.parse("Fe3+")
|
|
31
|
+
|
|
32
|
+
assert_equal({ "S" => 1, "O" => 4 }, sulfate.composition)
|
|
33
|
+
assert_equal(-2, sulfate.charge)
|
|
34
|
+
assert_equal({ "N" => 1, "H" => 4 }, ammonium.composition)
|
|
35
|
+
assert_equal(1, ammonium.charge)
|
|
36
|
+
assert_equal({ "Fe" => 1 }, iron.composition)
|
|
37
|
+
assert_equal(3, iron.charge)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_hydrate_formula
|
|
41
|
+
formula = Seimi::Formula.parse("CuSO4·5H2O")
|
|
42
|
+
|
|
43
|
+
assert_equal({ "Cu" => 1, "S" => 1, "O" => 9, "H" => 10 }, formula.composition)
|
|
44
|
+
assert_in_delta 249.677, formula.molar_mass, 0.001
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_isotope_formula
|
|
48
|
+
formula = Seimi::Formula.parse("[13C]H4")
|
|
49
|
+
|
|
50
|
+
assert_equal({ "C" => 1, "H" => 4 }, formula.composition)
|
|
51
|
+
assert_equal({ "13C" => 1, "H" => 4 }, formula.balance_composition)
|
|
52
|
+
assert_in_delta 17.0353548351, formula.molar_mass, 0.000001
|
|
53
|
+
assert_equal ["13C", 1, 13.0033548351], formula.breakdown.first
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_exact_isotope_formula
|
|
57
|
+
formula = Seimi::Formula.parse("[13C][1H]4")
|
|
58
|
+
|
|
59
|
+
assert_in_delta 17.03465496402, formula.molar_mass, 0.000000001
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_deuterium_and_tritium_aliases
|
|
63
|
+
deuterium_water = Seimi::Formula.parse("D2O")
|
|
64
|
+
tritium = Seimi::Formula.parse("T2")
|
|
65
|
+
|
|
66
|
+
assert_equal({ "H" => 2, "O" => 1 }, deuterium_water.composition)
|
|
67
|
+
assert_equal({ "2H" => 2, "O" => 1 }, deuterium_water.balance_composition)
|
|
68
|
+
assert_in_delta 20.0272035562, deuterium_water.molar_mass, 0.000000001
|
|
69
|
+
assert_equal({ "H" => 2 }, tritium.composition)
|
|
70
|
+
assert_equal({ "3H" => 2 }, tritium.balance_composition)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_unknown_isotope
|
|
74
|
+
assert_raises(Seimi::ParseError) do
|
|
75
|
+
Seimi::Formula.parse("[999C]H4")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/test/test_helper.rb
ADDED
data/test/test_kanji.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class TestKanji < Minitest::Test
|
|
6
|
+
def test_from_i_and_to_i_round_trip
|
|
7
|
+
[0, 7, 10, 58, 100, 1858, 10_005, 27_682_574_402].each do |number|
|
|
8
|
+
assert_equal number, Seimi::Kanji.to_i(Seimi::Kanji.from_i(number))
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_specific_kanji
|
|
13
|
+
assert_equal "千八百五十八", Seimi::Kanji.from_i(1858)
|
|
14
|
+
assert_equal 1858, Seimi::Kanji.to_i("千八百五十八")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_rational
|
|
18
|
+
assert_equal "三分の一", Seimi::Kanji.rational(Rational(1, 3))
|
|
19
|
+
end
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: seimi
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yudai Takada
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
description: 化学式の分子の量を秤り、反応式の割合の数を厳密に釣合はすRubyGemに候
|
|
41
|
+
email:
|
|
42
|
+
- t.yudai92@gmail.com
|
|
43
|
+
executables:
|
|
44
|
+
- seimi
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- CHANGELOG.md
|
|
49
|
+
- Gemfile
|
|
50
|
+
- LICENSE.txt
|
|
51
|
+
- README.md
|
|
52
|
+
- Rakefile
|
|
53
|
+
- exe/seimi
|
|
54
|
+
- lib/seimi.rb
|
|
55
|
+
- lib/seimi/cli.rb
|
|
56
|
+
- lib/seimi/elements.rb
|
|
57
|
+
- lib/seimi/equation.rb
|
|
58
|
+
- lib/seimi/errors.rb
|
|
59
|
+
- lib/seimi/formula.rb
|
|
60
|
+
- lib/seimi/formula/parser.rb
|
|
61
|
+
- lib/seimi/isotopes.rb
|
|
62
|
+
- lib/seimi/kanji.rb
|
|
63
|
+
- lib/seimi/sangi.rb
|
|
64
|
+
- lib/seimi/version.rb
|
|
65
|
+
- test/test_buntai.rb
|
|
66
|
+
- test/test_cli.rb
|
|
67
|
+
- test/test_equation.rb
|
|
68
|
+
- test/test_formula.rb
|
|
69
|
+
- test/test_helper.rb
|
|
70
|
+
- test/test_kanji.rb
|
|
71
|
+
homepage: https://github.com/ydah/seimi
|
|
72
|
+
licenses:
|
|
73
|
+
- MIT
|
|
74
|
+
metadata:
|
|
75
|
+
source_code_uri: https://github.com/ydah/seimi
|
|
76
|
+
changelog_uri: https://github.com/ydah/seimi/blob/main/CHANGELOG.md
|
|
77
|
+
rdoc_options: []
|
|
78
|
+
require_paths:
|
|
79
|
+
- lib
|
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '3.0'
|
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
requirements: []
|
|
91
|
+
rubygems_version: 4.0.6
|
|
92
|
+
specification_version: 4
|
|
93
|
+
summary: 舎密の式を解剖し釣合はする絡繰に候
|
|
94
|
+
test_files: []
|