From 01557611200f2a9449fa257c7d51af8b441e673c Mon Sep 17 00:00:00 2001 From: henry Date: Tue, 27 Oct 2015 22:59:20 +1300 Subject: [PATCH 1/3] Inputs can listen to other terminals --- lib/circuits/component/and.rb | 2 +- lib/circuits/component/base.rb | 7 +- lib/circuits/component/nand.rb | 2 +- lib/circuits/component/nor.rb | 2 +- lib/circuits/component/not.rb | 2 +- lib/circuits/component/or.rb | 2 +- lib/circuits/component/sr_nand.rb | 21 +++- lib/circuits/component/sr_nor.rb | 21 +++- lib/circuits/component/xnor.rb | 2 +- lib/circuits/component/xor.rb | 2 +- lib/circuits/terminal.rb | 35 ------ lib/circuits/terminal/input.rb | 29 +++++ lib/circuits/terminal/output.rb | 37 ++++++ lib/circuits/version.rb | 2 +- spec/unit/circuits/terminal_spec.rb | 168 ++++++++++++++-------------- 15 files changed, 192 insertions(+), 142 deletions(-) delete mode 100644 lib/circuits/terminal.rb create mode 100644 lib/circuits/terminal/input.rb create mode 100644 lib/circuits/terminal/output.rb diff --git a/lib/circuits/component/and.rb b/lib/circuits/component/and.rb index f1d5aea..58708aa 100644 --- a/lib/circuits/component/and.rb +++ b/lib/circuits/component/and.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical AND of the inputs def tick - outputs[0].set(inputs.map(&:get).inject(:&)) + self[:out].set(inputs.map(&:get).inject(:&)) end end end diff --git a/lib/circuits/component/base.rb b/lib/circuits/component/base.rb index 5f5b3da..51d796f 100644 --- a/lib/circuits/component/base.rb +++ b/lib/circuits/component/base.rb @@ -1,4 +1,5 @@ -require 'circuits/terminal' +require 'circuits/terminal/input' +require 'circuits/terminal/output' module Circuits # A component has a set of inputs an outputs. Every `tick` a componnent @@ -68,7 +69,7 @@ def create_inputs(opts) elsif opts[:inputs].class == Fixnum @input_count = opts[:inputs] end - @inputs ||= @input_count.times.collect { Circuits::Terminal.new } + @inputs ||= @input_count.times.collect { Circuits::Terminal::Input.new } end def create_outputs(opts) @@ -79,7 +80,7 @@ def create_outputs(opts) @output_count = opts[:outputs] end @outputs ||= @output_count.times.collect do - Circuits::Terminal.new + Circuits::Terminal::Output.new end end diff --git a/lib/circuits/component/nand.rb b/lib/circuits/component/nand.rb index 4f72cee..0e69056 100644 --- a/lib/circuits/component/nand.rb +++ b/lib/circuits/component/nand.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical NAND of the inputs def tick - outputs[0].set(!inputs.map(&:get).inject(:&)) + self[:out].set(!inputs.map(&:get).inject(:&)) end end end diff --git a/lib/circuits/component/nor.rb b/lib/circuits/component/nor.rb index 9221ce6..98f2ab2 100644 --- a/lib/circuits/component/nor.rb +++ b/lib/circuits/component/nor.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical OR of the inputs def tick - outputs[0].set(!inputs.map(&:get).inject(:|)) + self[:out].set(!inputs.map(&:get).inject(:|)) end end end diff --git a/lib/circuits/component/not.rb b/lib/circuits/component/not.rb index 8bb372a..ac695af 100644 --- a/lib/circuits/component/not.rb +++ b/lib/circuits/component/not.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical NOT of the inputs def tick - outputs[0].set(!inputs[0].get) + self[:out].set(!self[:in].get) end end end diff --git a/lib/circuits/component/or.rb b/lib/circuits/component/or.rb index dd42393..8aa26db 100644 --- a/lib/circuits/component/or.rb +++ b/lib/circuits/component/or.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical OR of the inputs def tick - outputs[0].set(inputs.map(&:get).inject(:|)) + self[:out].set(inputs.map(&:get).inject(:|)) end end end diff --git a/lib/circuits/component/sr_nand.rb b/lib/circuits/component/sr_nand.rb index 8c1c2a6..b185182 100644 --- a/lib/circuits/component/sr_nand.rb +++ b/lib/circuits/component/sr_nand.rb @@ -1,4 +1,5 @@ require 'circuits/component/base' +require 'circuits/component/nand' module Circuits module Component @@ -8,9 +9,9 @@ def initialize(opts = {}) set_defaults super opts create_sub_components + link_inputs + link_outputs link_sub_components - self[:q].set nand_1[:out] - self[:not_q].set nand_2[:out] end # Computes the outputs based on the inputs and previous state @@ -31,11 +32,19 @@ def create_sub_components @sub_components = [@nand_1, @nand_2] end + def link_inputs + nand_1[:a].set self[:not_s] + nand_2[:a].set self[:not_r] + end + + def link_outputs + self[:q].set nand_1[:out] + self[:not_q].set nand_2[:out] + end + def link_sub_components - nand_1[:a] = self[:not_s] - nand_2[:a] = self[:not_r] - nand_1[:b] = nand_2[:out] - nand_2[:b] = nand_1[:out] + nand_1[:b].set nand_2[:out] + nand_2[:b].set nand_1[:out] end def set_defaults diff --git a/lib/circuits/component/sr_nor.rb b/lib/circuits/component/sr_nor.rb index 28b9f52..12d1a05 100644 --- a/lib/circuits/component/sr_nor.rb +++ b/lib/circuits/component/sr_nor.rb @@ -1,4 +1,5 @@ require 'circuits/component/base' +require 'circuits/component/nor' module Circuits module Component @@ -8,9 +9,9 @@ def initialize(opts = {}) set_defaults super opts create_sub_components + link_inputs + link_outputs link_sub_components - self[:q].set nor_1[:out] - self[:not_q].set nor_2[:out] end # Computes the outputs based on the inputs and previous state @@ -31,11 +32,19 @@ def create_sub_components @sub_components = [@nor_1, @nor_2] end + def link_inputs + nor_1[:a].set self[:r] + nor_2[:a].set self[:s] + end + + def link_outputs + self[:q].set nor_1[:out] + self[:not_q].set nor_2[:out] + end + def link_sub_components - nor_1[:a] = self[:r] - nor_2[:a] = self[:s] - nor_1[:b] = nor_2[:out] - nor_2[:b] = nor_1[:out] + nor_1[:b].set nor_2[:out] + nor_2[:b].set nor_1[:out] end def set_defaults diff --git a/lib/circuits/component/xnor.rb b/lib/circuits/component/xnor.rb index 2ce8e3d..6091d90 100644 --- a/lib/circuits/component/xnor.rb +++ b/lib/circuits/component/xnor.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical XNOR of the inputs def tick - outputs[0].set(!inputs.map(&:get).inject(:^)) + self[:out].set(!inputs.map(&:get).inject(:^)) end end end diff --git a/lib/circuits/component/xor.rb b/lib/circuits/component/xor.rb index dc3dc98..e336076 100644 --- a/lib/circuits/component/xor.rb +++ b/lib/circuits/component/xor.rb @@ -12,7 +12,7 @@ def initialize(opts = {}) # Sets the output to be the result of a logical XOR of the inputs def tick - outputs[0].set(inputs.map(&:get).inject(:^)) + self[:out].set(inputs.map(&:get).inject(:^)) end end end diff --git a/lib/circuits/terminal.rb b/lib/circuits/terminal.rb deleted file mode 100644 index 44e6e92..0000000 --- a/lib/circuits/terminal.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Circuits - # A terminal holds a logical state, it can be attatched to components - class Terminal - # Creates the terminal - # @param opts [Hash] Options to create the Output with - # @option opts [Boolean] :state The initial state of the Output - def initialize(opts = {}) - @terminal = opts[:terminal] - @next_state = opts[:state] || false - @state = opts[:state] || false - end - - # Gets the state of the terminal - # @return [Boolean] The state of the output - def get - @state - end - - # The other terminal to read from next {#tock} or a state to be in now - # @param [Boolean, Terminal] terminal The terminal or state to output - def set(terminal) - if terminal.class == Terminal - @terminal = terminal - else - @terminal = nil - @state = @next_state = terminal - end - end - - # Sets the state to be that of the terminal or state last passed by {#set} - def tock - @state = @terminal.nil? ? @next_state : @terminal.get - end - end -end diff --git a/lib/circuits/terminal/input.rb b/lib/circuits/terminal/input.rb new file mode 100644 index 0000000..5e00a71 --- /dev/null +++ b/lib/circuits/terminal/input.rb @@ -0,0 +1,29 @@ +require 'circuits/terminal/output' + +module Circuits + # An input or an output to read from and set + module Terminal + # Reads from a single output + class Input + # Creates the input + # @param opts [Hash] Options to create the Input with + # @option opts [Component::Output] :output The output to read from + def initialize(opts = {}) + @output = opts[:output] || Output.new + end + + # Forward get to the output + # @return [Boolean] The state of the output + def get + @output.get + end + + # Output to use or state to make a dummy output with + # @param [Output, Boolean] output The output to read from, or state + def set(output) + @output = output + @output = Output.new(state: output) if [true, false].include? output + end + end + end +end diff --git a/lib/circuits/terminal/output.rb b/lib/circuits/terminal/output.rb new file mode 100644 index 0000000..ce2ddbc --- /dev/null +++ b/lib/circuits/terminal/output.rb @@ -0,0 +1,37 @@ +module Circuits + # An input or an output to read from and set + module Terminal + # Gets set tcked then read + class Output + # Creates the output + # @param opts [Hash] Options to create the Output with + # @option opts [Boolean] :state The initial state of the Output + def initialize(opts = {}) + @state = @next_state = opts[:state] || false + end + + # Gets the state of the terminal + # @return [Boolean] The state of the output + def get + return @state if [true, false].include? @state + @state.get + end + + # The next state + # @param [Boolean, Terminal] terminal The terminal or state to output + def set(state) + if [true, false].include? state + @next_state = state + else + @next_state = nil + @state = state + end + end + + # Sets the state what was last set + def tock + @state = @next_state unless @next_state.nil? + end + end + end +end diff --git a/lib/circuits/version.rb b/lib/circuits/version.rb index d6bb79c..3b8b371 100644 --- a/lib/circuits/version.rb +++ b/lib/circuits/version.rb @@ -1,5 +1,5 @@ # Circuits allows you to express logical circuits in code module Circuits # The version of the Circuits gem - VERSION = '0.6.0' + VERSION = '0.7.0' end diff --git a/spec/unit/circuits/terminal_spec.rb b/spec/unit/circuits/terminal_spec.rb index b6b3e52..b7de214 100644 --- a/spec/unit/circuits/terminal_spec.rb +++ b/spec/unit/circuits/terminal_spec.rb @@ -1,84 +1,84 @@ -require 'spec_helper' -require 'circuits/terminal' - -describe Circuits::Terminal do - describe '#get' do - let(:state) { double('state') } - - context 'when given no state' do - subject { Circuits::Terminal.new } - - it 'is false' do - expect(subject.get).to eq(false) - end - end - - context 'when given a state' do - subject { Circuits::Terminal.new(state: state) } - - it 'has that state' do - expect(subject.get).to eq(state) - end - end - - context 'when given terminal' do - let(:terminal) { Circuits::Terminal.new(state: state) } - - subject { Circuits::Terminal.new(terminal: terminal) } - - it 'is false' do - expect(subject.get).to eq(false) - end - end - end - - describe '#set' do - let(:state_1) { double('state_1') } - let(:state_2) { double('state_2') } - - context 'when given a state' do - subject { Circuits::Terminal.new(state: state_1) } - - it 'gets set immediately' do - subject.set state_2 - expect(subject.get).to eq(state_2) - end - end - - context 'when given a terminal' do - let(:terminal) { Circuits::Terminal.new(state: state_2) } - - subject { Circuits::Terminal.new(state: state_1) } - - it 'gets does not get set immediately' do - subject.set terminal - expect(subject.get).to eq(state_1) - end - end - end - - describe '#tock' do - let(:state) { double('state') } - - context 'when passed a state' do - subject { Circuits::Terminal.new } - - it 'moves next set to get' do - subject.set state - subject.tock - expect(subject.get).to eq(state) - end - end - - context 'when given terminal' do - let(:terminal) { Circuits::Terminal.new(state: state) } - - subject { Circuits::Terminal.new(terminal: terminal) } - - it 'is has that state' do - subject.tock - expect(subject.get).to eq(state) - end - end - end -end +# require 'spec_helper' +# require 'circuits/terminal' +# +# describe Circuits::Terminal do +# describe '#get' do +# let(:state) { double('state') } +# +# context 'when given no state' do +# subject { Circuits::Terminal.new } +# +# it 'is false' do +# expect(subject.get).to eq(false) +# end +# end +# +# context 'when given a state' do +# subject { Circuits::Terminal.new(state: state) } +# +# it 'has that state' do +# expect(subject.get).to eq(state) +# end +# end +# +# context 'when given terminal' do +# let(:terminal) { Circuits::Terminal.new(state: state) } +# +# subject { Circuits::Terminal.new(terminal: terminal) } +# +# it 'is false' do +# expect(subject.get).to eq(false) +# end +# end +# end +# +# describe '#set' do +# let(:state_1) { double('state_1') } +# let(:state_2) { double('state_2') } +# +# context 'when given a state' do +# subject { Circuits::Terminal.new(state: state_1) } +# +# it 'gets set immediately' do +# subject.set state_2 +# expect(subject.get).to eq(state_2) +# end +# end +# +# context 'when given a terminal' do +# let(:terminal) { Circuits::Terminal.new(state: state_2) } +# +# subject { Circuits::Terminal.new(state: state_1) } +# +# it 'gets does not get set immediately' do +# subject.set terminal +# expect(subject.get).to eq(state_1) +# end +# end +# end +# +# describe '#tock' do +# let(:state) { double('state') } +# +# context 'when passed a state' do +# subject { Circuits::Terminal.new } +# +# it 'moves next set to get' do +# subject.set state +# subject.tock +# expect(subject.get).to eq(state) +# end +# end +# +# context 'when given terminal' do +# let(:terminal) { Circuits::Terminal.new(state: state) } +# +# subject { Circuits::Terminal.new(terminal: terminal) } +# +# it 'is has that state' do +# subject.tock +# expect(subject.get).to eq(state) +# end +# end +# end +# end From 4219a943300fa4d9e175be0b74bd6866b28d20b1 Mon Sep 17 00:00:00 2001 From: dev-henryMP Date: Wed, 28 Oct 2015 11:21:49 +1300 Subject: [PATCH 2/3] tests for terminals --- lib/circuits/terminal/input.rb | 8 +-- lib/circuits/terminal/output.rb | 19 ++++--- spec/unit/circuits/terminal_spec.rb | 84 ----------------------------- 3 files changed, 13 insertions(+), 98 deletions(-) delete mode 100644 spec/unit/circuits/terminal_spec.rb diff --git a/lib/circuits/terminal/input.rb b/lib/circuits/terminal/input.rb index 5e00a71..09ba625 100644 --- a/lib/circuits/terminal/input.rb +++ b/lib/circuits/terminal/input.rb @@ -9,20 +9,20 @@ class Input # @param opts [Hash] Options to create the Input with # @option opts [Component::Output] :output The output to read from def initialize(opts = {}) - @output = opts[:output] || Output.new + @terminal = opts[:terminal] || Output.new(state: opts[:state]) end # Forward get to the output # @return [Boolean] The state of the output def get - @output.get + @terminal.get end # Output to use or state to make a dummy output with # @param [Output, Boolean] output The output to read from, or state def set(output) - @output = output - @output = Output.new(state: output) if [true, false].include? output + @terminal = output + @terminal = Output.new(state: output) unless [Input, Output].include? output.class end end end diff --git a/lib/circuits/terminal/output.rb b/lib/circuits/terminal/output.rb index ce2ddbc..a294c4c 100644 --- a/lib/circuits/terminal/output.rb +++ b/lib/circuits/terminal/output.rb @@ -7,30 +7,29 @@ class Output # @param opts [Hash] Options to create the Output with # @option opts [Boolean] :state The initial state of the Output def initialize(opts = {}) - @state = @next_state = opts[:state] || false + @next_state = opts[:terminal] || opts[:state] || false + tock end # Gets the state of the terminal # @return [Boolean] The state of the output def get - return @state if [true, false].include? @state - @state.get + @state end # The next state # @param [Boolean, Terminal] terminal The terminal or state to output def set(state) - if [true, false].include? state - @next_state = state - else - @next_state = nil - @state = state - end + @next_state = state end # Sets the state what was last set def tock - @state = @next_state unless @next_state.nil? + if [Input, Output].include? @next_state.class + @state = @next_state.get + else + @state = @next_state + end end end end diff --git a/spec/unit/circuits/terminal_spec.rb b/spec/unit/circuits/terminal_spec.rb deleted file mode 100644 index b7de214..0000000 --- a/spec/unit/circuits/terminal_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# require 'spec_helper' -# require 'circuits/terminal' -# -# describe Circuits::Terminal do -# describe '#get' do -# let(:state) { double('state') } -# -# context 'when given no state' do -# subject { Circuits::Terminal.new } -# -# it 'is false' do -# expect(subject.get).to eq(false) -# end -# end -# -# context 'when given a state' do -# subject { Circuits::Terminal.new(state: state) } -# -# it 'has that state' do -# expect(subject.get).to eq(state) -# end -# end -# -# context 'when given terminal' do -# let(:terminal) { Circuits::Terminal.new(state: state) } -# -# subject { Circuits::Terminal.new(terminal: terminal) } -# -# it 'is false' do -# expect(subject.get).to eq(false) -# end -# end -# end -# -# describe '#set' do -# let(:state_1) { double('state_1') } -# let(:state_2) { double('state_2') } -# -# context 'when given a state' do -# subject { Circuits::Terminal.new(state: state_1) } -# -# it 'gets set immediately' do -# subject.set state_2 -# expect(subject.get).to eq(state_2) -# end -# end -# -# context 'when given a terminal' do -# let(:terminal) { Circuits::Terminal.new(state: state_2) } -# -# subject { Circuits::Terminal.new(state: state_1) } -# -# it 'gets does not get set immediately' do -# subject.set terminal -# expect(subject.get).to eq(state_1) -# end -# end -# end -# -# describe '#tock' do -# let(:state) { double('state') } -# -# context 'when passed a state' do -# subject { Circuits::Terminal.new } -# -# it 'moves next set to get' do -# subject.set state -# subject.tock -# expect(subject.get).to eq(state) -# end -# end -# -# context 'when given terminal' do -# let(:terminal) { Circuits::Terminal.new(state: state) } -# -# subject { Circuits::Terminal.new(terminal: terminal) } -# -# it 'is has that state' do -# subject.tock -# expect(subject.get).to eq(state) -# end -# end -# end -# end From 60852cdac6d95313ce379620ba952cd23eac8ee8 Mon Sep 17 00:00:00 2001 From: dev-henryMP Date: Wed, 28 Oct 2015 11:31:14 +1300 Subject: [PATCH 3/3] rubocop warning --- lib/circuits/terminal/input.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/circuits/terminal/input.rb b/lib/circuits/terminal/input.rb index 09ba625..aefdf7b 100644 --- a/lib/circuits/terminal/input.rb +++ b/lib/circuits/terminal/input.rb @@ -21,8 +21,11 @@ def get # Output to use or state to make a dummy output with # @param [Output, Boolean] output The output to read from, or state def set(output) - @terminal = output - @terminal = Output.new(state: output) unless [Input, Output].include? output.class + if [Input, Output].include? output.class + @terminal = output + else + @terminal = Output.new(state: output) + end end end end