require_relative "eng_formula"
module Eng
module UnitCalc
include Formula
def calc_unit(units)
@error = {}
units = split_by_ari(units) unless units.class == Array
units.push(")").unshift("(")
par = parenthesis_unit(units)
par.reverse_each do |index|
by_value = units[index[0]..index[1]]
unit = plus_minus_split(by_value)
unit.map! do |each_unit|
multi_div_unit each_unit
end
unit = plus_minus_unit(unit)
add = by_value.include?("(") ? 1 : 0
units[(index[0]+add)..(index[1]-add)] = unit + Array.new(units[(index[0]+add)..(index[1]-add)].size-unit.size)
end
units.compact!
units.reject!{ |x| x =~ /\(|\)/ }
{ unit: unit_arrange(units), error: @error }
end
def plus_minus_unit(array)
array.each_with_index do |value, i|
unless i == 0 || (array[i-1].sort == array[i].sort)
@error[:plus_minus] ||= []
@error[:plus_minus] << "#{unit_arrange(array[i-1])} + #{unit_arrange(array[i])}"
end
end
array[0]
end
def plus_minus_split(units)
if units.any?{|x| x=="+" || x=="-"}
num = [0]
array = []
units.each_with_index { |x, i| num << i if x == "+" || x == "-" }
num << units.size - 1
num.each_with_index { |x, i| array << units[num[i-1]..x].reject!{|x| ["+", "-"].include?(x) } unless i==0 }
array
else
[units]
end
end
def multi_div_unit(units)
unit_hash = Hash.new(0)
ari = "+"
end_par = "+"
units.compact! unless units.nil?
units.each do |unit|
case unit
when "*", "・", "⋅"
ari = "+" unless end_par == "-"
when "/"
ari = "-"
when "("
end_par = "-" if ari == "-"
when ")"
end_par = "+"
else
num = unit.match(/(?\D+){1}(?-*\d*)/)
base = num[:base]
num = num[:num].empty? ? 1 : num[:num]
unit_hash[base] = eval(unit_hash[base].to_s + ari + num.to_s)
ari = "+" unless end_par == "-"
end
end
unit_hash.sort{|a,b| b[1] <=> a[1]}.map { |key, value| value == 0 ? nil : key + value.to_s }.compact
end
def parenthesis_unit(formula) #formula = ["(", "m","/","s", ")"]
array = []
count = []
formula.each_with_index do |value, index|
case value
when "("
array.push [index, nil]
count << array.size - 1
when ")"
array[count.pop][1] = index
end
end
array.unshift([0,formula.size-1])
end
def unit_arrange(units)
pos = []
neg = []
units.each do |unit|
num = unit.match(/(?(?:[a-zA-Z]+))(?-*\d*)/)
unless num.nil?
if num[:num].nil? || num[:num].to_i.positive?
number = num[:num].to_i == 1 ? nil : num[:num]
pos << num[:base] + number.to_s
else
number = num[:num].to_i == -1 ? nil : - (num[:num].to_i)
neg << num[:base] + number.to_s
end
end
end
div = neg.size > 1 ? ("/" + "(" + neg.join("*") + ")") : (neg.empty? ? nil : "/" + neg.join.to_s)
pos.join("*") + div.to_s
end
def split_si_unit(si_unit)
unit = si_unit.split(/([\*\/\(\)])/).reject(&:empty?).map do |s_unit|
s_unit =~ /([\*\/\(\)])/ ? s_unit : extraction_metric(s_unit)
end
value = String.new
unit_si = []
unit.each do |each_unit|
case each_unit
when Array
value << each_unit[0].to_s
unit_si << each_unit[1]
when String
value << each_unit.to_s
unit_si << each_unit
end
end
[eval(value.gsub(/^(#{operator_reg(:ari)})/,"")), unit_si]
end
end
end