Skip to content

Instantly share code, notes, and snippets.

@hejmsdz
Last active July 1, 2017 10:06
Show Gist options
  • Save hejmsdz/c9e33d610ff61bd79d29a51a668556a4 to your computer and use it in GitHub Desktop.
Save hejmsdz/c9e33d610ff61bd79d29a51a668556a4 to your computer and use it in GitHub Desktop.
Resistance calculator with LaTeX-compatible step by step derivation
require 'set'
def ohm(value, decimals=2)
value.round(decimals).to_s.sub('.', ',').sub(/,0+$/, '') << " \\Omega"
end
def frac(num, den)
"\\frac{#{num}}{#{den}}"
end
class BaseResistor
def value_str
ohm(value)
end
end
class Resistor < BaseResistor
attr_reader :sym, :value, :derived
def initialize(sym, value)
@sym = sym
@value = value
@derived = true
end
def syms
Set.new([sym])
end
def inspect
"R_#{@sym}"
end
def full_sym
inspect
end
end
class Connection < BaseResistor
attr_reader :derived
def initialize(contents)
@contents = contents
@derived = false
end
def syms
@contents.map(&:syms).reduce(:+)
end
def full_sym
sym
end
def sym
syms_list = syms.to_a.sort.join(",")
"R_(#{syms_list})"
end
def inspect
name = self.class.name.to_s.downcase
contents = @contents.map(&:inspect).join(", ")
"#{name}(#{contents})"
end
def write_value
puts "#{sym} = #{derive_value} = #{value_str}"
end
def derive
derive_me
complex = @contents.select {|x| x.kind_of?(Connection) }
complex.each(&:derive)
@derived = true
write_value if @contents.all?(&:derived)
end
end
class Series < Connection
def value
@contents.map(&:value).reduce(:+)
end
def derive_me
terms = @contents.map(&:full_sym).join(' + ')
puts "#{sym} = #{terms}"
end
def derive_value
@contents.map(&:value_str).join(' + ')
end
end
class Parallel < Connection
def value
1.0 / @contents.map { |x| 1.0 / x.value }.reduce(:+)
end
def derive_me
terms = @contents.map { |x| frac(1, x.full_sym) }.join(' + ')
puts "#{sym} = #{frac(1, terms)}"
end
def derive_value
terms = @contents.map { |x| frac(1, x.value_str) }.join(' + ')
recipr = @contents.map { |x| 1.0 / x.value }.reduce(:+)
"#{frac(1, terms)} = #{frac(ohm(1, 0), recipr.round(4))}"
end
end
def method_missing(meth, *args, &block)
return unless meth.to_s.start_with? 'R'
Resistor.new(meth.to_s.sub(/^R/, ''), args[0])
end
def series(*contents)
Series.new(contents)
end
def parallel(*contents)
Parallel.new(contents)
end
# Usage example
circuit = series(R1(30), parallel(R2(20), series(R3(10), R4(15))), R5(10))
circuit.value
circuit.derive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment