Add capture regs from Chip Data File to IsolationNode

Change-Id: Ia91821f2b14e12ed917c97b622789710ffb8bdb4
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/src/chip_data/hei_chip_data.cpp b/src/chip_data/hei_chip_data.cpp
index 87044db..752014b 100644
--- a/src/chip_data/hei_chip_data.cpp
+++ b/src/chip_data/hei_chip_data.cpp
@@ -75,7 +75,8 @@
 
 //------------------------------------------------------------------------------
 
-void __readExpr(ChipDataStream& io_stream, RegisterType_t i_regType)
+void __readExpr(ChipDataStream& io_stream, const IsolationChip::Ptr& i_isoChip,
+                IsolationNode::Ptr& io_isoNode)
 {
     uint8_t exprType;
     io_stream >> exprType;
@@ -86,11 +87,23 @@
             RegisterId_t regId;
             Instance_t regInst;
             io_stream >> regId >> regInst;
+
+            // Find the hardware register that is stored in this isolation chip
+            // and add it to the list of capture registers. This ensures all
+            // registers referenced in the rules are are captured by default.
+            // Note that this will assert that the target register must exist in
+            // the isolation chip.
+            auto hwReg = i_isoChip->getHardwareRegister({regId, regInst});
+
+            // Add the register to the isolation node.
+            io_isoNode->addCaptureRegister(hwReg);
+
             break;
         }
         case 0x02: // integer constant
         {
-            if (REG_TYPE_SCOM == i_regType || REG_TYPE_ID_SCOM == i_regType)
+            if (REG_TYPE_SCOM == io_isoNode->getRegisterType() ||
+                REG_TYPE_ID_SCOM == io_isoNode->getRegisterType())
             {
                 uint64_t constant; // 8-byte value
                 io_stream >> constant;
@@ -107,7 +120,7 @@
             io_stream >> numSubExpr;
             for (uint8_t i = 0; i < numSubExpr; i++)
             {
-                __readExpr(io_stream, i_regType);
+                __readExpr(io_stream, i_isoChip, io_isoNode);
             }
             break;
         }
@@ -117,27 +130,27 @@
             io_stream >> numSubExpr;
             for (uint8_t i = 0; i < numSubExpr; i++)
             {
-                __readExpr(io_stream, i_regType);
+                __readExpr(io_stream, i_isoChip, io_isoNode);
             }
             break;
         }
         case 0x12: // NOT operation
         {
-            __readExpr(io_stream, i_regType);
+            __readExpr(io_stream, i_isoChip, io_isoNode);
             break;
         }
         case 0x13: // left shift operation
         {
             uint8_t shiftValue;
             io_stream >> shiftValue;
-            __readExpr(io_stream, i_regType);
+            __readExpr(io_stream, i_isoChip, io_isoNode);
             break;
         }
         case 0x14: // right shift operation
         {
             uint8_t shiftValue;
             io_stream >> shiftValue;
-            __readExpr(io_stream, i_regType);
+            __readExpr(io_stream, i_isoChip, io_isoNode);
             break;
         }
         default:
@@ -162,30 +175,51 @@
         uint8_t numCapRegs, numIsoRules, numChildNodes;
         io_stream >> nodeInst >> numCapRegs >> numIsoRules >> numChildNodes;
 
+        // There must be at least one isolation rule defined.
+        HEI_ASSERT(0 != numIsoRules);
+
+        // Allocate memory for this isolation node.
+        auto isoNode =
+            std::make_shared<IsolationNode>(nodeId, nodeInst, regType);
+
         // Add capture registers.
         for (unsigned int j = 0; j < numCapRegs; j++)
         {
+            // Read the capture register metadata.
             RegisterId_t regId;
             Instance_t regInst;
             io_stream >> regId >> regInst;
+
+            // Find the hardware register that is stored in this isolation chip
+            // and add it to the list of capture registers. Note that this will
+            // assert that the target register must exist in the isolation chip.
+            auto hwReg = io_isoChip->getHardwareRegister({regId, regInst});
+
+            // Add the register to the isolation node.
+            isoNode->addCaptureRegister(hwReg);
         }
 
         // Add isolation rules.
         for (unsigned int j = 0; j < numIsoRules; j++)
         {
+            // Read the rule metadata.
             AttentionType_t attnType;
             io_stream >> attnType;
-            __readExpr(io_stream, regType);
+            __readExpr(io_stream, io_isoChip, isoNode);
         }
 
         // Add child nodes.
         for (unsigned int j = 0; j < numChildNodes; j++)
         {
+            // Read the child node metadata.
             BitPosition_t bit;
             NodeId_t childId;
             Instance_t childInst;
             io_stream >> bit >> childId >> childInst;
         }
+
+        // Add this node to the isolation chip.
+        io_isoChip->addIsolationNode(isoNode);
     }
 }
 
diff --git a/src/isolator/hei_isolation_node.hpp b/src/isolator/hei_isolation_node.hpp
index 2546a93..33df441 100644
--- a/src/isolator/hei_isolation_node.hpp
+++ b/src/isolator/hei_isolation_node.hpp
@@ -51,8 +51,10 @@
      * @param i_id       Unique ID for all instances of this node.
      * @param i_instance Instance of this node.
      */
-    IsolationNode(NodeId_t i_id, Instance_t i_instance) :
-        iv_id(i_id), iv_instance(i_instance)
+    IsolationNode(NodeId_t i_id, Instance_t i_instance,
+                  RegisterType_t i_regType) :
+        iv_id(i_id),
+        iv_instance(i_instance), iv_regType(i_regType)
     {}
 
     /** @brief Destructor. */
@@ -75,6 +77,14 @@
     const Instance_t iv_instance;
 
     /**
+     * A registers referenced by this node's rules must be of this type. No
+     * mixing of register types is allowed because comparing different sized
+     * registers is undefined behavior. Note that it is possible to have capture
+     * registers of mixed types.
+     */
+    const RegisterType_t iv_regType;
+
+    /**
      * The list of register to capture and add to the log for additional
      * debugging.
      */
@@ -85,7 +95,8 @@
      * register 'rule' (value) to find any active attentions for each attention
      * type (key). A 'rule', like "register & ~mask", is a combination of
      * HardwareRegister objects and virtual operator registers (all children
-     * of the Register class).
+     * of the Register class). Note that all registers referenced by these rules
+     * must be the same type as iv_regType.
      */
     std::map<AttentionType_t, const Register::ConstPtr> iv_rules;
 
@@ -166,6 +177,12 @@
         return {iv_id, iv_instance};
     }
 
+    /** @return This node's register type.. */
+    RegisterType_t getRegisterType() const
+    {
+        return iv_regType;
+    }
+
   private: // Isolation stack and supporting functions.
     /** When analyze() is called at the tree root, all recursive calls to
      *  analyze() will target the same chip and attention type. So we only need
diff --git a/src/register/hei_operator_register.hpp b/src/register/hei_operator_register.hpp
index 6bfaca9..5c225b1 100644
--- a/src/register/hei_operator_register.hpp
+++ b/src/register/hei_operator_register.hpp
@@ -72,18 +72,18 @@
      * @brief Constructor from components.
      * @param i_arg Target register for operation.
      */
-    explicit NotRegister(Register& i_arg) :
-        OperatorRegister(i_arg.getSize()), iv_child(&i_arg)
+    explicit NotRegister(Register::ConstPtr i_arg) :
+        OperatorRegister(i_arg->getSize()), iv_child(i_arg)
     {}
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~NotRegister() = default;
 
-    /** @brief Default copy constructor. */
-    NotRegister(const NotRegister&) = default;
+    /** @brief Copy constructor. */
+    NotRegister(const NotRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    NotRegister& operator=(const NotRegister& r) = default;
+    /** @brief Assignment operator. */
+    NotRegister& operator=(const NotRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
@@ -108,7 +108,7 @@
     }
 
   private:
-    Register* iv_child;
+    const Register::ConstPtr iv_child;
 };
 
 /**
@@ -126,18 +126,18 @@
      * @param i_arg    Target register for operation.
      * @param i_amount The shift value.
      */
-    LeftShiftRegister(Register& i_arg, uint16_t i_amount) :
-        OperatorRegister(i_arg.getSize()), iv_child(&i_arg), iv_amount(i_amount)
+    LeftShiftRegister(Register::ConstPtr i_arg, size_t i_amount) :
+        OperatorRegister(i_arg->getSize()), iv_child(i_arg), iv_amount(i_amount)
     {}
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~LeftShiftRegister() = default;
 
-    /** @brief Default copy constructor. */
-    LeftShiftRegister(const LeftShiftRegister&) = default;
+    /** @brief Copy constructor. */
+    LeftShiftRegister(const LeftShiftRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    LeftShiftRegister& operator=(const LeftShiftRegister& r) = default;
+    /** @brief Assignment operator. */
+    LeftShiftRegister& operator=(const LeftShiftRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
@@ -164,8 +164,8 @@
     }
 
   private:
-    Register* iv_child;
-    uint16_t iv_amount;
+    const Register::ConstPtr iv_child;
+    const size_t iv_amount;
 };
 
 /**
@@ -183,18 +183,18 @@
      * @param i_arg    Target register for operation.
      * @param i_amount The shift value.
      */
-    RightShiftRegister(Register& i_arg, uint16_t i_amount) :
-        OperatorRegister(i_arg.getSize()), iv_child(&i_arg), iv_amount(i_amount)
+    RightShiftRegister(Register::ConstPtr i_arg, size_t i_amount) :
+        OperatorRegister(i_arg->getSize()), iv_child(i_arg), iv_amount(i_amount)
     {}
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~RightShiftRegister() = default;
 
-    /** @brief Default copy constructor. */
-    RightShiftRegister(const RightShiftRegister&) = default;
+    /** @brief Copy constructor. */
+    RightShiftRegister(const RightShiftRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    RightShiftRegister& operator=(const RightShiftRegister& r) = default;
+    /** @brief Assignment operator. */
+    RightShiftRegister& operator=(const RightShiftRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
@@ -221,8 +221,8 @@
     }
 
   private:
-    Register* iv_child;
-    uint16_t iv_amount;
+    const Register::ConstPtr iv_child;
+    const size_t iv_amount;
 };
 
 /**
@@ -241,19 +241,22 @@
      * @param i_left  Target register for operation.
      * @param i_right Target register for operation.
      */
-    AndRegister(Register& i_left, Register& i_right) :
-        OperatorRegister(std::min(i_left.getSize(), i_right.getSize())),
-        iv_left(&i_left), iv_right(&i_right)
-    {}
+    AndRegister(Register::ConstPtr i_left, Register::ConstPtr i_right) :
+        OperatorRegister(i_left->getSize()), iv_left(i_left), iv_right(i_right)
+    {
+        // The two registers must be the same sizes or it makes for some weird
+        // results.
+        HEI_ASSERT(iv_left->getSize() == iv_right->getSize());
+    }
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~AndRegister() = default;
 
-    /** @brief Default copy constructor. */
-    AndRegister(const AndRegister&) = default;
+    /** @brief Copy constructor. */
+    AndRegister(const AndRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    AndRegister& operator=(const AndRegister& r) = default;
+    /** @brief Assignment operator. */
+    AndRegister& operator=(const AndRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
@@ -281,8 +284,8 @@
     }
 
   private:
-    Register* iv_left;
-    Register* iv_right;
+    const Register::ConstPtr iv_left;
+    const Register::ConstPtr iv_right;
 };
 
 /**
@@ -301,19 +304,22 @@
      * @param i_left  Target register for operation.
      * @param i_right Target register for operation.
      */
-    OrRegister(Register& i_left, Register& i_right) :
-        OperatorRegister(std::min(i_left.getSize(), i_right.getSize())),
-        iv_left(&i_left), iv_right(&i_right)
-    {}
+    OrRegister(Register::ConstPtr i_left, Register::ConstPtr i_right) :
+        OperatorRegister(i_left->getSize()), iv_left(i_left), iv_right(i_right)
+    {
+        // The two registers must be the same sizes or it makes for some weird
+        // results.
+        HEI_ASSERT(iv_left->getSize() == iv_right->getSize());
+    }
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~OrRegister() = default;
 
-    /** @brief Default copy constructor. */
-    OrRegister(const OrRegister&) = default;
+    /** @brief Copy constructor. */
+    OrRegister(const OrRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    OrRegister& operator=(const OrRegister& r) = default;
+    /** @brief Assignment operator. */
+    OrRegister& operator=(const OrRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
@@ -341,36 +347,38 @@
     }
 
   private:
-    Register* iv_left;
-    Register* iv_right;
+    const Register::ConstPtr iv_left;
+    const Register::ConstPtr iv_right;
 };
 
 /**
  * @brief Contains a constant value that can be used within any of the other
  *        register operators. The value can be retrieved using the
  *        getBitString() function.
- **/
+ */
 class ConstantRegister : public OperatorRegister
 {
   public:
     /**
      * @brief Constructor from components.
-     * @param i_arg A BitStringBuffer containing the constant value.
+     * @param i_val An unsigned integer value. iv_result will be initialized to
+     *              the size of type T and this value will be copied into that
+     *              buffer.
      */
-    explicit ConstantRegister(const BitStringBuffer& i_arg) :
-        OperatorRegister(BitString::getMinBytes(i_arg.getBitLen()))
+    template <class T>
+    explicit ConstantRegister(T i_val) : OperatorRegister(sizeof(i_val))
     {
-        iv_result = i_arg;
+        iv_result.setFieldRight(0, iv_result.getBitLen(), i_val);
     }
 
-    /** @brief Default destructor. */
+    /** @brief Destructor. */
     ~ConstantRegister() = default;
 
-    /** @brief Default copy constructor. */
-    ConstantRegister(const ConstantRegister&) = default;
+    /** @brief Copy constructor. */
+    ConstantRegister(const ConstantRegister&) = delete;
 
-    /** @brief Default assignment operator. */
-    ConstantRegister& operator=(const ConstantRegister& r) = default;
+    /** @brief Assignment operator. */
+    ConstantRegister& operator=(const ConstantRegister& r) = delete;
 
     /** @brief Overloaded from parent class. */
     const BitString* getBitString(const Chip& i_chip) const override
diff --git a/src/util/hei_bit_string.cpp b/src/util/hei_bit_string.cpp
index 9cc2613..a04fb43 100644
--- a/src/util/hei_bit_string.cpp
+++ b/src/util/hei_bit_string.cpp
@@ -286,25 +286,29 @@
 
 bool BitString::operator<(const BitString& i_str) const
 {
-    // The two bit strings must be the same length. Otherwise, the comparison
-    // undefined (i.e. compare from the left vs. right).
-    HEI_ASSERT(getBitLen() == i_str.getBitLen());
-
-    for (uint64_t pos = 0; pos < getBitLen(); pos += UINT64_BIT_LEN)
+    if (getBitLen() < i_str.getBitLen())
     {
-        uint64_t len = std::min(getBitLen() - pos, UINT64_BIT_LEN);
-
-        auto l_str = getFieldRight(pos, len);
-        auto r_str = i_str.getFieldRight(pos, len);
-
-        if (l_str < r_str)
+        return true;
+    }
+    else if (getBitLen() == i_str.getBitLen())
+    {
+        // Can only compare the bit strings if the length is the same.
+        for (uint64_t pos = 0; pos < getBitLen(); pos += UINT64_BIT_LEN)
         {
-            return true;
-        }
-        // The loop can only continue if the values are equal.
-        else if (l_str > r_str)
-        {
-            return false;
+            uint64_t len = std::min(getBitLen() - pos, UINT64_BIT_LEN);
+
+            auto l_str = getFieldRight(pos, len);
+            auto r_str = i_str.getFieldRight(pos, len);
+
+            if (l_str < r_str)
+            {
+                return true;
+            }
+            // The loop can only continue if the values are equal.
+            else if (l_str > r_str)
+            {
+                return false;
+            }
         }
     }
 
diff --git a/src/util/hei_bit_string.hpp b/src/util/hei_bit_string.hpp
index 669e755..59369b7 100644
--- a/src/util/hei_bit_string.hpp
+++ b/src/util/hei_bit_string.hpp
@@ -333,7 +333,15 @@
         return isEqual(i_str);
     }
 
-    /** @brief Less-than operator */
+    /**
+     * @brief Less-than operator.
+     *
+     * IMPORTANT:
+     * The purpose of this function is primarily for sorting these objects in
+     * data structures like map and vector. It does not guarantee a less than
+     * comparison of the bit strings because bit strings can vary in length and
+     * it is difficult to define that kind of comparison.
+     */
     bool operator<(const BitString& i_str) const;
 
     /** @brief Bitwise NOT operator. */
diff --git a/test/hei_operator_register_testcase.cpp b/test/hei_operator_register_testcase.cpp
deleted file mode 100644
index 47cb53e..0000000
--- a/test/hei_operator_register_testcase.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-#include <hei_chip.hpp>
-#include <hei_includes.hpp>
-#include <register/hei_hardware_register.hpp>
-#include <register/hei_operator_register.hpp>
-#include <register/hei_register.hpp>
-
-#include <iomanip>
-#include <iostream>
-
-#define BITSTRING_BIT_WIDTH 16
-#define CONSTANT1 0x1458 // 1458
-#define CONSTANT2 0x5368 // 5468
-
-using namespace std;
-using namespace libhei;
-
-int main()
-{
-
-    Chip my_chip{nullptr, 0};
-    size_t constant1 = CONSTANT1;
-    size_t constant2 = CONSTANT2;
-
-    // Initializing two registers
-
-    cout << "\n\n\nInitializing two constant registers ";
-    cout << "(leftside & rightside):\n\n";
-
-    cout << "\nCreating BitString \"bsa\".\n";
-    BitStringBuffer bsa{BITSTRING_BIT_WIDTH};
-    cout << "Setting BitString \"bsa\" to:  ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << uppercase << constant1 << "\n";
-    bsa.setFieldRight(0, BITSTRING_BIT_WIDTH, constant1);
-
-    cout << "\nCreating BitString \"bsb\".\n";
-    BitStringBuffer bsb{BITSTRING_BIT_WIDTH};
-    cout << "Setting BitString \"bsb\" to:  ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << uppercase << constant2 << "\n\n";
-    bsb.setFieldRight(0, BITSTRING_BIT_WIDTH, constant2);
-
-    cout << "Creating \"leftside\" register from bsa.\n";
-    ConstantRegister leftside(bsa);
-    cout << "Creating \"rightside\" register from bsb.\n";
-    ConstantRegister rightside(bsb);
-
-    cout << "Leftside:  ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << leftside.getBitString(my_chip)->getFieldRight(0,
-                                                          BITSTRING_BIT_WIDTH);
-
-    cout << "  Rightside:  ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << rightside.getBitString(my_chip)->getFieldRight(0,
-                                                           BITSTRING_BIT_WIDTH);
-    cout << "\n\n";
-
-    // Basic functional tests:
-    // AND
-    cout << "Instantiating my_and_register passing in two registers:\n";
-    cout << "AndRegister my_and_register(leftside, rightside);\n";
-    AndRegister my_and_register(leftside, rightside);
-    cout << "AND result:  ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << my_and_register.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n\n";
-
-    // NOT
-    cout << "Instantiating my_not_register passing in my_and_register "
-         << "from above:\n";
-    cout << "NotRegister my_not_register(my_and_register);\n";
-    NotRegister my_not_register(my_and_register);
-    cout << "NOT result:               ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << my_not_register.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n\n";
-
-    // Left Shift
-    cout << "Instantiating my_leftshift_register passing "
-         << "in my_not_register from above and a 4 directing i"
-         << "a left shift of 4 bits:\n";
-    cout << "LeftShiftRegister my_leftshift_register(my_not_register, 4);\n";
-    LeftShiftRegister my_leftshift_register(my_not_register, 4);
-    cout << "LeftShift result:         ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << my_leftshift_register.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n\n";
-
-    // Right Shift
-    cout << "Instantiating my_rightshift_register passing in "
-         << "my_leftshift_register from above and a 4 directing "
-         << "a right shift of 4 bits:\n";
-    cout << "RightShiftRegister my_rightshift_register("
-         << "my_leftshift_register, 4);\n";
-    RightShiftRegister my_rightshift_register(my_leftshift_register, 4);
-    cout << "Right Shift result:         ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << my_rightshift_register.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n\n";
-
-    // OR
-    cout << "Instantiating my_or_register passing in my_leftshift_register"
-         << " and my_rightshift_register from above:\n";
-    cout << "OrRegister my_or_register(my_leftshift_register,"
-         << "my_rightshift_register);\n";
-    OrRegister my_or_register(my_leftshift_register, my_rightshift_register);
-    cout << "OR result:         ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << my_or_register.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n\n";
-
-    // = operator
-
-    // ConstantRegister
-    cout << "\nExercising operator \"=\" (assignment):\n\n";
-
-    cout << "Assigning leftside register to constreg1 register.  (";
-    ConstantRegister constreg1 = leftside;
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << constreg1.getBitString(my_chip)->getFieldRight(0,
-                                                           BITSTRING_BIT_WIDTH)
-         << ")\n";
-    cout << "ConstantRegister constreg1 = leftside;\n";
-
-    cout << "Assigning rightside register to constreg2 register.  (";
-    ConstantRegister constreg2 = rightside;
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << constreg2.getBitString(my_chip)->getFieldRight(0,
-                                                           BITSTRING_BIT_WIDTH)
-         << ")\n";
-    cout << "ConstantRegister constreg2 = rightside;\n";
-
-    // AndRegister
-    cout << "\nCreating and_register1 with leftside & rightside registers.\n";
-    cout << "AndRegister and_register1(leftside,rightside);\n";
-    AndRegister and_register1(leftside, rightside);
-    cout << "Creating and_register2 with the assignment operator (=).\n";
-    cout << "AndRegister and_register2 = and_register1;\n";
-    AndRegister and_register2 = and_register1;
-    cout << "and_register2 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << and_register2.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-
-    // NotRegister
-    cout << "\nCreating not_register1 with leftside register.\n";
-    cout << "NotRegister not_register1(leftside);\n";
-    NotRegister not_register1(leftside);
-    cout << "Creating not_register2 with the assignment operator (=).\n";
-    cout << "NotRegister not_register2 = not_register1;\n";
-    NotRegister not_register2 = not_register1;
-    cout << "not_register2 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << not_register2.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-
-    // LeftShiftRegister
-    cout << "\nCreating leftshift_register1 with leftside register.\n";
-    cout << "LeftShiftRegister leftshift_register1(leftside,4);\n";
-    LeftShiftRegister leftshift_register1(leftside, 4);
-    cout << "Creating leftshift_register2 with the assignment operator (=).\n";
-    cout << "LeftShiftRegister leftshift_register2 = leftshift_register1;\n";
-    LeftShiftRegister leftshift_register2 = leftshift_register1;
-    cout << "leftshift_register2 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << leftshift_register2.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-
-    // RightShiftRegister
-    cout << "\nCreating rightshift_register1 with leftside register.\n";
-    cout << "RightShiftRegister rightshift_register1(leftside,4);\n";
-    RightShiftRegister rightshift_register1(leftside, 4);
-    cout << "Creating rightshift_register2 with the assignment operator (=).\n";
-    cout << "RightShiftRegister rightshift_register2 = rightshift_register1;\n";
-    RightShiftRegister rightshift_register2 = rightshift_register1;
-    cout << "rightshift_register2 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << rightshift_register2.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-
-    // OrRegister
-    cout << "\nCreating or_register1 with leftside & rightside registers.\n";
-    cout << "OrRegister or_register1(leftside,rightside);\n";
-    OrRegister or_register1(leftside, rightside);
-    cout << "or_register1 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << or_register1.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-    cout << "Creating or_register2 with the assignment operator (=).\n";
-    cout << "OrRegister or_register2 = or_register1;\n";
-    OrRegister or_register2 = or_register1;
-    cout << "or_register2 = ";
-    cout << "0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << or_register2.getBitString(my_chip)->getFieldRight(
-                0, BITSTRING_BIT_WIDTH)
-         << "\n";
-
-    //***** == operator *****
-    cout << "\nOperator \"==\" (equality)\n\n";
-    cout << "Operation  Register1 = Register2    Equality\n";
-
-    // Constant Register
-    ConstantRegister cr1(leftside);
-    ConstantRegister cr2(rightside);
-    cout << " Constant    0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << cr1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << cr2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (cr1 == cr2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    // And Register
-    AndRegister and1(leftside, rightside);
-    AndRegister and2(leftside, rightside);
-
-    cout << " And         0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << and1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << and2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (and1 == and2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    // Not Register
-    NotRegister nr1(leftside);
-    NotRegister nr2(rightside);
-    cout << " Not         0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << nr1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << nr2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (nr1 == nr2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    // Left Shift Register
-    LeftShiftRegister lsr1(leftside, 4);
-    LeftShiftRegister lsr2(rightside, 4);
-    cout << " Left Shift  0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << lsr1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << lsr2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (lsr1 == lsr2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    // Right Shift Register
-    RightShiftRegister rsr1(leftside, 6);
-    RightShiftRegister rsr2(rightside, 6);
-    cout << " Right Shift 0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << rsr1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << rsr2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (rsr1 == rsr2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    // Or Register
-    OrRegister or1(leftside, rightside);
-    OrRegister or2(leftside, rightside);
-    cout << " OR          0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << or1.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << or2.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    if (or1 == or2)
-        cout << "      Equal\n";
-    else
-        cout << "      Not Equal\n";
-
-    //***** < operator *****
-    cout << "\nOperator \"<\" (equality)\n\n";
-    cout << "Operation  Register1 < Register2  Equal/Less Than\n";
-
-    // Constant Register
-    ConstantRegister cr1a(leftside);
-    ConstantRegister cr2a(rightside);
-    cout << " Constant    0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << cr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << cr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (cr1a < cr2a)
-    if (cr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        cr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    // And Register
-    AndRegister and1a(leftside, rightside);
-    AndRegister and2a(leftside, rightside);
-    cout << " And         0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << and1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << and2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (and2a < and1a)
-    if (and1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        and2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    // Not Register
-    NotRegister nr1a(leftside);
-    NotRegister nr2a(rightside);
-    cout << " Not         0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << nr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << nr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (nr1a < nr2a) Paul
-    if (nr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        nr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    // Left Shift Register
-    LeftShiftRegister lsr1a(leftside, 4);
-    LeftShiftRegister lsr2a(leftside, 4);
-    cout << " Left Shift  0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << lsr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << lsr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (lsr1a < lsr2a)
-    if (lsr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        lsr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    // Right Shift Register
-    RightShiftRegister rsr1a(leftside, 4);
-    RightShiftRegister rsr2a(leftside, 4);
-    cout << " Right Shift 0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << rsr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << rsr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (rsr1a < rsr2a)
-    if (rsr1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        rsr2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    // Or Register
-    OrRegister or1a(leftside, rightside);
-    OrRegister or2a(leftside, rightside);
-    cout << " Or          0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4)
-         << hex
-         << or1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    cout << "     0x" << setfill('0') << setw(BITSTRING_BIT_WIDTH / 4) << hex
-         << or2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH);
-    // if (or1a < or2a)
-    if (or1a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH) <
-        or2a.getBitString(my_chip)->getFieldRight(0, BITSTRING_BIT_WIDTH))
-        cout << "      Less Than\n";
-    else
-        cout << "      Not Less Than\n";
-
-    return (0);
-}
diff --git a/test/meson.build b/test/meson.build
index 642b827..e780a2e 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -8,7 +8,8 @@
 # build g-test framework unit tests
 gtests = [
     'bit_string_test',
-    'flyweight_test'
+    'flyweight_test',
+    'operator_register_test',
 ]
 
 gtest = dependency('gtest', main : true, required : false, method : 'system')
@@ -21,10 +22,3 @@
   endforeach
 endif
 
-# build unit-test framework
-tests = ['hei_operator_register_testcase']
-
-foreach t : tests
-        test(t, executable(t.underscorify(), t + '.cpp', test_src, \
-                            include_directories : incdir))
-endforeach
diff --git a/test/operator_register_test.cpp b/test/operator_register_test.cpp
new file mode 100644
index 0000000..227a566
--- /dev/null
+++ b/test/operator_register_test.cpp
@@ -0,0 +1,152 @@
+#include <hei_chip.hpp>
+#include <register/hei_operator_register.hpp>
+#include <util/hei_flyweight.hpp>
+
+#include "gtest/gtest.h"
+
+using namespace libhei;
+
+constexpr uint16_t CONST1 = 0x1458;
+constexpr uint16_t CONST2 = 0x5368;
+
+#define MATH(x) static_cast<uint16_t>(x)
+
+uint64_t __getVal(Register::ConstPtr i_reg)
+{
+    Chip CHIP{nullptr, 0};
+    auto bs = i_reg->getBitString(CHIP);
+    return bs->getFieldRight(0, bs->getBitLen());
+}
+
+size_t __getBW(Register::ConstPtr i_reg)
+{
+    Chip CHIP{nullptr, 0};
+    return i_reg->getBitString(CHIP)->getBitLen();
+}
+
+TEST(OperatorRegisterTest, BasicOperations)
+{
+    auto const1 = std::make_shared<const ConstantRegister>(CONST1);
+    ASSERT_EQ(MATH(CONST1), __getVal(const1));
+
+    auto const2 = std::make_shared<const ConstantRegister>(CONST2);
+    ASSERT_EQ(MATH(CONST2), __getVal(const2));
+
+    auto and1 = std::make_shared<const AndRegister>(const1, const2);
+    ASSERT_EQ(MATH(CONST1 & CONST2), __getVal(and1));
+
+    auto or1 = std::make_shared<const OrRegister>(const1, const2);
+    ASSERT_EQ(MATH(CONST1 | CONST2), __getVal(or1));
+
+    auto not1 = std::make_shared<const NotRegister>(const1);
+    ASSERT_EQ(MATH(~CONST1), __getVal(not1));
+
+    auto not2 = std::make_shared<const NotRegister>(const2);
+    ASSERT_EQ(MATH(~CONST2), __getVal(not2));
+}
+
+TEST(OperatorRegisterTest, BasicOperationsWithFlyweights)
+{
+    auto& const_factory = Flyweight<const ConstantRegister>::getSingleton();
+    auto& and_factory   = Flyweight<const AndRegister>::getSingleton();
+    auto& or_factory    = Flyweight<const OrRegister>::getSingleton();
+    auto& not_factory   = Flyweight<const NotRegister>::getSingleton();
+
+    auto const1 = const_factory.get(CONST1);
+    auto const2 = const_factory.get(CONST2);
+
+    ASSERT_EQ(MATH(CONST1), __getVal(const1));
+    ASSERT_EQ(MATH(CONST2), __getVal(const2));
+    ASSERT_EQ(MATH(CONST1 & CONST2), __getVal(and_factory.get(const1, const2)));
+    ASSERT_EQ(MATH(CONST1 | CONST2), __getVal(or_factory.get(const1, const2)));
+    ASSERT_EQ(MATH(~CONST1), __getVal(not_factory.get(const1)));
+    ASSERT_EQ(MATH(~CONST2), __getVal(not_factory.get(const2)));
+
+    const_factory.clear();
+    and_factory.clear();
+    or_factory.clear();
+    not_factory.clear();
+}
+
+TEST(OperatorRegisterTest, ShiftOperations)
+{
+    auto& const_factory  = Flyweight<const ConstantRegister>::getSingleton();
+    auto& lshift_factory = Flyweight<const LeftShiftRegister>::getSingleton();
+    auto& rshift_factory = Flyweight<const RightShiftRegister>::getSingleton();
+
+    auto const1 = const_factory.get(CONST1);
+
+    for (size_t i = 0; i < __getBW(const1); i++)
+    {
+        ASSERT_EQ(MATH(CONST1 << i), __getVal(lshift_factory.get(const1, i)));
+    }
+
+    for (size_t i = 0; i < __getBW(const1); i++)
+    {
+        ASSERT_EQ(MATH(CONST1 >> i), __getVal(rshift_factory.get(const1, i)));
+    }
+
+    const_factory.clear();
+    lshift_factory.clear();
+    rshift_factory.clear();
+}
+
+TEST(OperatorRegisterTest, ComplexOperation)
+{
+    // Something seemingly complex:
+    //   ~(((CONST1 & CONST2) << 12) | ((CONST1 | CONST2) >> 4)))
+
+    auto& const_factory  = Flyweight<const ConstantRegister>::getSingleton();
+    auto& and_factory    = Flyweight<const AndRegister>::getSingleton();
+    auto& or_factory     = Flyweight<const OrRegister>::getSingleton();
+    auto& not_factory    = Flyweight<const NotRegister>::getSingleton();
+    auto& lshift_factory = Flyweight<const LeftShiftRegister>::getSingleton();
+    auto& rshift_factory = Flyweight<const RightShiftRegister>::getSingleton();
+
+    auto const1 = const_factory.get(CONST1);
+    auto const2 = const_factory.get(CONST2);
+
+    auto lshift = lshift_factory.get(and_factory.get(const1, const2), 12);
+    auto rshift = rshift_factory.get(or_factory.get(const1, const2), 4);
+
+    auto expr = not_factory.get(or_factory.get(lshift, rshift));
+
+    ASSERT_EQ(MATH(~(((CONST1 & CONST2) << 12) | ((CONST1 | CONST2) >> 4))),
+              __getVal(expr));
+
+    const_factory.clear();
+    and_factory.clear();
+    or_factory.clear();
+    not_factory.clear();
+    lshift_factory.clear();
+    rshift_factory.clear();
+}
+
+TEST(OperatorRegisterTest, ConstRegConstructor)
+{
+    auto& const_factory = Flyweight<const ConstantRegister>::getSingleton();
+    auto& and_factory   = Flyweight<const AndRegister>::getSingleton();
+
+    // The ConstRegister constructor is a template that requires some integer
+    // type.
+
+    auto const1 = const_factory.get(CONST1); // CONST1 is uint16_t, GOOD
+
+    auto const2 = const_factory.get(uint16_t{0x1458}); // This also works
+
+    ASSERT_EQ(const1, const2); // Should point to the same thing.
+
+    // If you pass in a constant with no type the compiler assumes it is the
+    // implicit int type. This value is the same as const2, but the size will be
+    // different.
+    auto const3 = const_factory.get(0x1458);
+
+    // The values are the same, but the bit strings are are different sizes.
+    ASSERT_NE(const2, const3);
+
+    // AND and OR operators will not like different sizes either.
+    ASSERT_DEATH(and_factory.get(const2, const3), "");
+
+    const_factory.clear();
+    and_factory.clear();
+}