digital circuit simulator (7)
で、キタナいソレを晒しとくのも微妙なんで、ちょっとだけ見直した分をサラしてみる。
えーと、まず本体。add2agenda が微妙にモディファイ。
class Segment attr_accessor :t, :q def initialize(time, queue) @t = time @q = queue end end class Agenda attr_accessor :currentTime, :segments def initialize @currentTime = 0 @segments = Array.new end def empty? @segments.size == 0 end def firstAgenda seg = @segments.first @currentTime = seg.t seg.q.first end def removeFirst @segments.first.q.shift if @segments.first.q.size == 0 @segments.shift end end def add2agenda(time, p) if empty? || @segments.first.t > time @segments.unshift(Segment.new(time, Array[p])) else @segments = add2segments(time, p, @segments) end end def add2segments(time, p, segments) if segments.first.t == time segments.first.q.push p segments else rest = segments.last(segments.size - 1) if rest.empty? || rest.first.t > time ret = Array[segments.first, Segment.new(time, Array[p])] rest.each do |obj| ret.push(obj) end ret else add2segments(time, p, rest).unshift(segments.first) end end end private :add2segments end class Wire attr_accessor :value attr_reader :action def initialize @value = 0 @action = Array.new end def value=(v) if @value != v @value = v @action.each do |i| i.call end end end def addAction(p) @action.unshift(p) p.call end end class Inverter def initialize(input, output, agenda) @i = input; @o = output; @ag = agenda @d = $inverterDelay p = Proc.new { newVal = logicalNot(@i.value); afterDelay(@d, lambda{@o.value = newVal}, @ag) } @i.addAction p end def logicalNot(signal) if signal == 0 1 else 0 end end end class AndGate def initialize(input1, input2, output, agenda) @i1 = input1; @i2 = input2; @o = output; @d = $andDelay; @ag = agenda p = Proc.new { newVal = logicalAnd(@i1.value, @i2.value); afterDelay(@d, lambda{@o.value = newVal}, @ag) } @i1.addAction p @i2.addAction p end def logicalAnd(s1, s2) if s1 == 1 && s2 == 1 1 else 0 end end end class OrGate def initialize(input1, input2, output, agenda) @i1 = input1; @i2 = input2; @o = output; @d = $orDelay; @ag = agenda p = Proc.new { newVal = logicalOr(@i1.value, @i2.value); afterDelay(@d, lambda{@o.value = newVal}, @ag) } @i1.addAction p @i2.addAction p end def logicalOr(s1, s2) if s1 == 1 || s2 == 1 1 else 0 end end end class HalfAdder attr_reader :d, :e def initialize(agenda, a, b, s, c) @a = a; @b = b; @s = s; @c = c; @ag = agenda @d = Wire.new @e = Wire.new @gates = Array[OrGate.new(@a, @b, @d, @ag), AndGate.new(@a, @b, @c, @ag), Inverter.new(@c, @e, @ag), AndGate.new(@d, @e, @s, @ag)] end end class FullAdder def initialize(a, b, cIn, sum, cOut) @a = a; @b = b; @cIn = cIn; @sum = sum; @cOut = cOut; @s = Wire.new @c1 = Wire.new @c2 = Wire.new @gates = Array[HalfAdder.new(@b, @cIn, @s, @c1), HalfAdder.new(@a, @s, @sum, @c2), OrGate.new(@c1, @c2, @cOut)] end end class Sim attr_accessor :ag def initialize $inverterDelay = 2 $andDelay = 3 $orDelay = 5 @ag = Agenda.new end def simulate @input1 = Wire.new @input2 = Wire.new @sum = Wire.new @carry = Wire.new probe("carry", @carry) probe("sum", @sum) @haobj = HalfAdder.new(@ag, @input1, @input2, @sum, @carry) @input1.value = 1 propagate @input2.value = 1 propagate end def probe(name, wire) wire.addAction(lambda {print name; print " "; print @ag.currentTime; print " New-value = "; print wire.value; puts ""}) end def propagate if !@ag.empty? @ag.firstAgenda.call @ag.removeFirst propagate end end end def afterDelay(delay, p, ag) ag.add2agenda(delay + ag.currentTime, p) end
もっと良さげな書き方できるんでしょうが、ruby な書き方とゆーか語彙が (以下略
しかし、java で上記を、なソレは実装可能なのだろうか。
試験
蛇足な上に微妙ですが試験も。ちなみに require してる test2 ですが、本体のファイル名が test2.rb という名前によります。特に意味なし。
Segment
require 'test2' class TestSegment < Test::Unit::TestCase def setup @seg = Segment.new(1, 2) end def test_segment_initialize assert_not_nil @seg end def test_segment_accessor assert_equal 1, @seg.t assert_equal 2, @seg.q @seg.t = 2; @seg.q = 3 assert_equal 2, @seg.t assert_equal 3, @seg.q end end
Agenda
require 'test2' class TestAgenda < Test::Unit::TestCase def setup @ag = Agenda.new end def test_agenda_initialize assert_not_nil @ag end def test_agenda_accessor assert_equal 0, @ag.currentTime assert_not_nil @ag.segments assert_instance_of Array, @ag.segments end def test_agenda_setSegments seg = Segment.new 1, 2 @ag.segments.push seg assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.last.t assert_equal 2, @ag.segments.last.q seg = Segment.new 3, 4 @ag.segments.push seg assert_equal 2, @ag.segments.size assert_equal 1, @ag.segments.first.t assert_equal 2, @ag.segments.first.q assert_equal 3, @ag.segments.last.t assert_equal 4, @ag.segments.last.q end def test_agenda_empty? assert @ag.empty? seg = Segment.new 1, 2 @ag.segments.push seg assert !@ag.empty? end def test_agenda_firstAgenda ary = Array[1, 2] seg = Segment.new 5, ary @ag.segments.push seg ary = Array[3, 4] seg = Segment.new 8, ary @ag.segments.push seg f = @ag.firstAgenda assert_equal 1, f assert_equal 5, @ag.currentTime end def test_agenda_removeFirst ary = Array[1, 2] seg = Segment.new 5, ary @ag.segments.push seg ary = Array[3, 4] seg = Segment.new 8, ary @ag.segments.push seg @ag.removeFirst assert_equal 2, @ag.segments.first.q.first @ag.removeFirst assert_equal 3, @ag.segments.first.q.first @ag.removeFirst assert_equal 4, @ag.segments.first.q.first @ag.removeFirst assert @ag.empty? end def test_agenda_add2segments @ag.segments.push(Segment.new(5, Array[1, 2])) @ag.segments.push(Segment.new(8, Array[3, 4])) @ag.add2agenda(1, 5) assert_equal 1, @ag.segments.first.t assert_equal [5], @ag.segments.first.q assert_equal 3, @ag.segments.size assert_equal 5, @ag.segments[1].t assert_equal 8, @ag.segments[2].t assert_equal [1, 2], @ag.segments[1].q assert_equal [3, 4], @ag.segments[2].q @ag.add2agenda(5, 3) assert_equal 3, @ag.segments.size assert_equal 1, @ag.segments[0].t assert_equal 5, @ag.segments[1].t assert_equal 8, @ag.segments[2].t assert_equal [5], @ag.segments[0].q assert_equal [1, 2, 3], @ag.segments[1].q assert_equal [3, 4], @ag.segments[2].q @ag.add2agenda(6, 6) assert_equal 4, @ag.segments.size assert_equal 1, @ag.segments[0].t assert_equal 5, @ag.segments[1].t assert_equal 6, @ag.segments[2].t assert_equal 8, @ag.segments[3].t assert_equal [5], @ag.segments[0].q assert_equal [1, 2, 3], @ag.segments[1].q assert_equal [6], @ag.segments[2].q assert_equal [3, 4], @ag.segments[3].q @ag.add2agenda(10, 10) assert_equal 5, @ag.segments.size assert_equal 1, @ag.segments[0].t assert_equal 5, @ag.segments[1].t assert_equal 6, @ag.segments[2].t assert_equal 8, @ag.segments[3].t assert_equal 10, @ag.segments[4].t assert_equal [5], @ag.segments[0].q assert_equal [1, 2, 3], @ag.segments[1].q assert_equal [6], @ag.segments[2].q assert_equal [3, 4], @ag.segments[3].q assert_equal [10], @ag.segments[4].q end def test_agenda_add2agenda_first @ag.add2agenda 5, 1 assert_equal 5, @ag.segments.first.t assert_equal 1, @ag.segments.first.q.first assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.add2agenda 2, 3 assert_equal 2, @ag.segments.first.t assert_equal 5, @ag.segments.last.t assert_equal 3, @ag.segments.first.q.first assert_equal 1, @ag.segments.first.q.size assert_equal 1, @ag.segments.last.q.first assert_equal 1, @ag.segments.last.q.size @ag.add2agenda 5, 2 assert_equal 1, @ag.segments.last.q.first assert_equal 2, @ag.segments.last.q.last assert_equal 2, @ag.segments.first.t @ag.add2agenda 3, 6 assert_equal 2, @ag.segments[0].t assert_equal 3, @ag.segments[1].t assert_equal 5, @ag.segments[2].t assert_equal 3, @ag.segments[0].q.first assert_equal 1, @ag.segments[0].q.size assert_equal 6, @ag.segments[1].q.first assert_equal 1, @ag.segments[1].q.size assert_equal 1, @ag.segments[2].q.first assert_equal 2, @ag.segments[2].q.size assert_equal 2, @ag.segments[2].q.last end end
Wire
require 'test2' class TestWire < Test::Unit::TestCase def setup @wire = Wire.new end def test_wire_initialize assert_not_nil @wire end def test_wire_getValue assert_equal 0, @wire.value end def test_wire_addAction assert_equal "call", @wire.addAction(lambda{"call"}) end def test_wire_setValue assert_equal 0, @wire.value p = lambda{"call"} @wire.addAction p @wire.value = 1 assert_equal 1, @wire.value assert_equal p, @wire.action.first # ... end end
Inverter
require 'test2' class TestInverter < Test::Unit::TestCase def setup $inverterDelay = 2 @ag = Agenda.new @i = Wire.new @o = Wire.new @obj = Inverter.new @i, @o, @ag end def test_inverter_initialize assert_not_nil @obj end def test_inverter_action @ag.firstAgenda.call assert_equal 1, @o.value @ag.removeFirst assert @ag.empty? @i.value = 1 assert_equal 1, @ag.segments.size @ag.firstAgenda.call assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? end end
AndGate
require 'test2' class TestAndGate < Test::Unit::TestCase def setup $andDelay = 3 @ag = Agenda.new @i1 = Wire.new @i2 = Wire.new @o = Wire.new @obj = AndGate.new @i1, @i2, @o, @ag end def test_andgate_initialize assert_not_nil @obj assert_not_nil @i1 assert_not_nil @i2 assert_not_nil @ag assert_not_nil @o assert_instance_of AndGate, @obj assert_instance_of Wire, @i1 assert_instance_of Wire, @i2 assert_instance_of Wire, @o assert_instance_of Agenda, @ag end def test_andgate_action assert_equal 1, @ag.segments.size assert_equal 2, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 3, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst @ag.firstAgenda.call assert_equal 3, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? @i1.value = 1 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 6, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? @i2.value = 1 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 9, @ag.currentTime assert_equal 1, @o.value @ag.removeFirst assert @ag.empty? @i1.value = 0 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 12, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? end end
OrGate
require 'test2' class TestOrGate < Test::Unit::TestCase def setup $orDelay = 5 @ag = Agenda.new @i1 = Wire.new @i2 = Wire.new @o = Wire.new @obj = OrGate.new @i1, @i2, @o, @ag end def test_orgate_initialize assert_not_nil @obj assert_not_nil @ag assert_not_nil @i1 assert_not_nil @i2 assert_not_nil @o assert_instance_of OrGate, @obj assert_instance_of Wire, @i1 assert_instance_of Wire, @i2 assert_instance_of Wire, @o assert_instance_of Agenda, @ag end def test_orgate_action assert_equal 1, @ag.segments.size assert_equal 2, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 5, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst @ag.firstAgenda.call assert_equal 5, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? @i1.value = 1 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 10, @ag.currentTime assert_equal 1, @o.value @ag.removeFirst assert @ag.empty? @i1.value = 0 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 15, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? @i2.value = 1 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 20, @ag.currentTime assert_equal 1, @o.value @ag.removeFirst assert @ag.empty? @i2.value = 0 assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call assert_equal 25, @ag.currentTime assert_equal 0, @o.value @ag.removeFirst assert @ag.empty? end end
HalfAdder
require 'test2' class TestHalfAdderGate < Test::Unit::TestCase def setup $inverterDelay = 2 $andDelay = 3 $orDelay = 5 @ag = Agenda.new @i1 = Wire.new @i2 = Wire.new @s = Wire.new @c = Wire.new @obj = HalfAdder.new @ag, @i1, @i2, @s, @c end def test_halfadder_initialize $inverterDelay = 2 $andDelay = 3 $orDelay = 5 assert_not_nil @obj assert_not_nil @ag assert_not_nil @i1 assert_not_nil @i2 assert_not_nil @s assert_not_nil @c assert_instance_of HalfAdder, @obj assert_instance_of Wire, @i1 assert_instance_of Wire, @i2 assert_instance_of Wire, @s assert_instance_of Wire, @c assert_instance_of Agenda, @ag end def test_halfadder_init assert_equal 0, @ag.currentTime assert_equal 3, @ag.segments.size assert_equal 1, @ag.segments[0].q.size assert_equal 4, @ag.segments[1].q.size assert_equal 2, @ag.segments[2].q.size assert_equal 0, @obj.e.value @ag.firstAgenda.call @ag.removeFirst assert_equal 1, @obj.e.value assert_equal 2, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 4, @ag.segments[0].q.size assert_equal 3, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 3, @ag.segments.first.q.size assert_equal 3, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 2, @ag.segments.first.q.size assert_equal 3, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 1, @ag.segments.first.q.size assert_equal 3, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 3, @ag.segments.first.q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 2, @ag.segments.first.q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 0, @ag.segments.size assert_equal 0, @i1.value assert_equal 0, @i2.value assert_equal 0, @s.value assert_equal 0, @c.value end def propagate_sim if !@ag.empty? @ag.firstAgenda.call @ag.removeFirst propagate_sim end end def test_halfadder_action_after_initialize assert_equal 3, @ag.segments.size propagate_sim assert_equal 5, @ag.currentTime assert_equal 0, @ag.segments.size assert_equal 0, @i1.value assert_equal 0, @i2.value assert_equal 0, @s.value assert_equal 0, @c.value end def test_halfadder_action_propagate propagate_sim assert_equal 5, @ag.currentTime @i1.value = 1 propagate_sim assert_equal 13, @ag.currentTime assert_equal 0, @ag.segments.size assert_equal 1, @i1.value assert_equal 0, @i2.value assert_equal 1, @s.value assert_equal 0, @c.value end def test_halfadder_action assert_equal 3, @ag.segments.size assert_equal 1, @ag.segments[0].q.size assert_equal 4, @ag.segments[1].q.size assert_equal 2, @ag.segments[2].q.size @i1.value = 1 assert_equal 3, @ag.segments.size assert_equal 1, @ag.segments[0].q.size assert_equal 5, @ag.segments[1].q.size assert_equal 3, @ag.segments[2].q.size assert_equal 0, @obj.d.value assert_equal 0, @obj.e.value @ag.firstAgenda.call @ag.removeFirst assert_equal 0, @obj.d.value assert_equal 1, @obj.e.value assert_equal 2, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 5, @ag.segments[0].q.size # 3 assert_equal 4, @ag.segments[1].q.size # 5 (include and) @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 4, @ag.segments.first.q.size assert_equal 4, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 3, @ag.segments.first.q.size assert_equal 4, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 2, @ag.segments.first.q.size assert_equal 4, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 1, @ag.segments.first.q.size assert_equal 4, @ag.segments[1].q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 3, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 4, @ag.segments.first.q.size # doubt @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 3, @ag.segments.first.q.size assert_equal 0, @obj.d.value assert_equal 1, @obj.e.value @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 2, @ag.segments.first.q.size assert_equal 0, @obj.d.value assert_equal 1, @obj.e.value @ag.firstAgenda.call @ag.removeFirst assert_equal 1, @obj.d.value assert_equal 1, @obj.e.value assert_equal 5, @ag.currentTime assert_equal 2, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 5, @ag.currentTime assert_equal 1, @ag.segments.size assert_equal 1, @ag.segments.first.q.size @ag.firstAgenda.call @ag.removeFirst assert_equal 8, @ag.currentTime assert_equal 0, @ag.segments.size assert @ag.empty? assert_equal 1, @i1.value assert_equal 0, @i2.value assert_equal 1, @s.value assert_equal 0, @c.value end end
Sim については試験を作ってません。FullAdder も動作確認してません。威張って出せるナニではない事は十分承知してますが (しかもバグってる可能性大)、とりあえずサラしてみる、という事を御理解頂ければ幸いッス。
蛇足
test/run-test.rb も以下に貼っておく
require 'test/unit' test_file = "test/test_*.rb" $:.unshift(File.join(File.expand_path("."), "lib")) $:.unshift(File.join(File.expand_path("."), "test")) Dir.glob(test_file) do |file| require file.sub(/\.rb$/, '') end