From 176065530878d409a4978a9e0b9d7a512ea87ad4 Mon Sep 17 00:00:00 2001 From: pjht Date: Mon, 9 Jul 2018 16:24:50 -0500 Subject: [PATCH] Add gates --- and.rb | 21 +++++++++++++++++ gate.rb | 22 ++++++++++++++++++ not.rb | 30 +++++++++++++++++++++++++ or.rb | 20 +++++++++++++++++ spec/gate_spec.rb | 57 +++++++++++++++++++++++++++++++++++++++++++++-- xor.rb | 20 +++++++++++++++++ 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 and.rb create mode 100644 not.rb create mode 100644 or.rb create mode 100644 xor.rb diff --git a/and.rb b/and.rb new file mode 100644 index 0000000..a42cfdd --- /dev/null +++ b/and.rb @@ -0,0 +1,21 @@ +require_relative "gate.rb" +require_relative "port.rb" + +# Represents an AND gate +class AndGate < Gate + + # Called when inputs change. + # Calculates AND of all inputs and sets output port to that value. + # @param vals [Array] List of values for connected ports. + def inputs_changed(vals) + andval=nil + vals.each do |val| + if andval==nil + andval=val + else + andval=andval&val + end + end + out.setval(andval) + end +end diff --git a/gate.rb b/gate.rb index 5eeb3c0..a2897da 100644 --- a/gate.rb +++ b/gate.rb @@ -41,6 +41,28 @@ class Gate inputs_changed(vals) end + # Test a truth table for a gate. + # @param table [Array>] The truth table, + # which is an array of entries. The last value in the entry is the expected output, + # the rest are the inputs to the ports. + def self.test_table(table) + ports=[] + (table[0].length-1).times do + ports.push Port.new(1) + end + gate=self.new(*ports) + table.each do |entry| + i=0 + ports.each do |port| + port.setval(entry[i]) + i+=1 + end + return false if gate.out!=entry.last + end + return true + end + + # Called when inputs change. # @abstract Override this to implement a gate. # @param vals [Array] List of values for connected ports. def inputs_changed(vals); raise NotImplementedError; end diff --git a/not.rb b/not.rb new file mode 100644 index 0000000..2cfaf6a --- /dev/null +++ b/not.rb @@ -0,0 +1,30 @@ +require_relative "gate.rb" +require_relative "port.rb" + +# Represents a NOT gate +class NotGate < Gate + + # (see Gate#initialize) + def initialize(*args) + @outmask=0 + super + @outmask=(2**@width)-1 + end + + # Add a port to the gate. As this is a NOT gate, there may only be one port. + # @param (see Gate#add_input) + # @return [void] + def add_input(input_port) + if @inputs.length > 0 then + raise ArgumentError, "Cannot add multiple inputs to a NOT gate" + end + super + end + + # Called when inputs change. + # Calculates NOT of input and sets output port to that value. + # @param vals [Array] List of values for connected ports. + def inputs_changed(vals) + out.setval((~vals[0]) & @outmask) + end +end diff --git a/or.rb b/or.rb new file mode 100644 index 0000000..2e1517c --- /dev/null +++ b/or.rb @@ -0,0 +1,20 @@ +require_relative "gate.rb" +require_relative "port.rb" + +# Represents an OR gate +class OrGate < Gate + + # Called when inputs change. + # Calculates OR of all inputs and sets output port to that value. + def inputs_changed(vals) + orval=nil + vals.each do |val| + if orval==nil + orval=val + else + orval=orval|val + end + end + out.setval(orval) + end +end diff --git a/spec/gate_spec.rb b/spec/gate_spec.rb index 1f11e22..6ef6b23 100644 --- a/spec/gate_spec.rb +++ b/spec/gate_spec.rb @@ -1,6 +1,9 @@ -require_relative "../gate.rb" require_relative "../port.rb" - +require_relative "../gate.rb" +require_relative "../not.rb" +require_relative "../and.rb" +require_relative "../or.rb" +require_relative "../xor.rb" describe Gate do let(:klass) do Class.new(Gate) do @@ -31,4 +34,54 @@ describe Gate do b.setval(10) expect(gate.out.val).to eq(18) end + + context "NotGate" do + it "should NOT the input" do + table=[ + [0,1], + [1,0] + ] + ok=NotGate.test_table(table) + expect(ok).to eq true + end + end + + context "AndGate" do + it "should AND together all inputs" do + table=[ + [0,0,0], + [0,1,0], + [1,0,0], + [1,1,1], + ] + ok=AndGate.test_table(table) + expect(ok).to eq true + end + end + + context "OrGate" do + it "should OR together all inputs" do + table=[ + [0,0,0], + [0,1,1], + [1,0,1], + [1,1,1], + ] + ok=OrGate.test_table(table) + expect(ok).to eq true + end + end + + context "XorGate" do + it "should XOR together all inputs" do + table=[ + [0,0,0], + [0,1,1], + [1,0,1], + [1,1,0], + ] + ok=XorGate.test_table(table) + expect(ok).to eq true + end + end end diff --git a/xor.rb b/xor.rb new file mode 100644 index 0000000..3e9728c --- /dev/null +++ b/xor.rb @@ -0,0 +1,20 @@ +require_relative "gate.rb" +require_relative "port.rb" + +# Represents an XOR gate +class XorGate < Gate + + # Called when inputs change. + # Calculates XOR of all inputs and sets output port to that value. + def inputs_changed(vals) + xorval=nil + vals.each do |val| + if xorval==nil + xorval=val + else + xorval=xorval^val + end + end + out.setval(xorval) + end +end