Program Listing for File Instruction.hpp

Return to documentation for file (include/sgpl/program/Instruction.hpp)

#pragma once
#ifndef SGPL_PROGRAM_INSTRUCTION_HPP_INCLUDE
#define SGPL_PROGRAM_INSTRUCTION_HPP_INCLUDE

#include <algorithm>
#include <array>
#include <cstddef>
#include <map>
#include <set>
#include <string>
#include <tuple>

#include "../../../third-party/cereal/include/cereal/archives/json.hpp"
#include "../../../third-party/cereal/include/cereal/cereal.hpp"
#include "../../../third-party/cereal/include/cereal/types/array.hpp"
#include "../../../third-party/cereal/include/cereal/types/map.hpp"
#include "../../../third-party/cereal/include/cereal/types/string.hpp"
#include "../../../third-party/conduit/include/uitsl/math/shift_mod.hpp"
#include "../../../third-party/conduit/include/uit_emp/math/math.hpp"
#include "../../../third-party/conduit/include/uit_emp/math/Random.hpp"

#include "OpCodeRectifier.hpp"

namespace sgpl {

template<typename Spec>
struct Instruction {

  using library_t = typename Spec::library_t;
  using rectifier_t = sgpl::OpCodeRectifier<library_t>;
  using tag_t = typename Spec::tag_t;

  unsigned char op_code;

  std::array<unsigned char, 3> args;

  tag_t tag;

  void RectifyArgs() {
    static_assert( uit_emp::IsPowerOf2( Spec::num_registers ) );
    for (auto& arg : args) arg = uitsl::shift_mod(arg, Spec::num_registers);
  }

  void RectifyOpCode(const rectifier_t& r=rectifier_t{}) {
    op_code = r.Rectify(op_code);
  }

  void Rectify(const rectifier_t& r=rectifier_t{}) {
    RectifyArgs(); RectifyOpCode(r);
  }

  Instruction() = default;

  Instruction(uit_emp::Random& rand) {
    rand.RandFill(
      reinterpret_cast<unsigned char*>( this ),
      sizeof( *this )
    );
    Rectify();
  }

  void NopOut() {
    const size_t num_rng_touches
      = library_t::template GetOpNumRngTouches<Spec>( op_code );
    op_code = library_t::GetNopOpCode( num_rng_touches );
  }

  void NopOutIfNotAnchor() {
    if ( !library_t::IsAnchorOpCode( op_code ) ) NopOut();
  }

  bool IsNop() const noexcept { return library_t::IsNopOpCode( op_code ); }

  bool IsOp() const noexcept { return !IsNop(); }

  bool operator==(const Instruction& other) const {
    return (
      std::tuple{ op_code, args, tag }
      == std::tuple{ other.op_code, other.args, other.tag }
    );
  }

  bool operator!=(const Instruction& other) const { return !operator==(other); }

  bool operator<(const Instruction& other) const {
    return (
      std::tuple{ op_code, args, tag }
      < std::tuple{ other.op_code, other.args, other.tag }
    );
  }

  std::string GetOpName() const { return library_t::GetOpName(op_code); }

  const tag_t& GetTag() const noexcept { return tag; }

  auto GetDescriptors() const {
    return library_t::template GetOpDescriptors< Instruction >(op_code, *this);
  }

  auto GetCategories() const {
    return library_t::template GetOpCategories< Instruction >(op_code, *this);
  }

  // human-readable output
  template<
    typename Archive,
    cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
    = cereal::traits::sfinae
  >
  void save( Archive& archive ) const {
    std::stringstream ss;
    tag.Print( ss );

    archive(
      cereal::make_nvp("operation", GetOpName() ),
      CEREAL_NVP( args ),
      cereal::make_nvp("bitstring", ss.str() ),
      cereal::make_nvp("descriptors", GetDescriptors() )
    );

  }

  // human-readable input
  template<
    typename Archive,
    cereal::traits::EnableIf<cereal::traits::is_text_archive<Archive>::value>
    = cereal::traits::sfinae
  >
  void load( Archive& archive ) {
    std::string op_name;
    std::string bitstring;
    std::map<std::string, std::string> descriptors;

    archive(
      cereal::make_nvp( "operation", op_name ),
      CEREAL_NVP( args ),
      cereal::make_nvp( "bitstring", bitstring ),
      cereal::make_nvp( "descriptors", descriptors )
    );

    op_code = library_t::GetOpCode( op_name );
    tag = tag_t( bitstring );
    // descriptors are read into and ignored

  }

  // binary input/output
  template<
    typename Archive,
    cereal::traits::DisableIf<cereal::traits::is_text_archive<Archive>::value>
    = cereal::traits::sfinae
  >
  void serialize( Archive& archive ) {
    archive(
      CEREAL_NVP( op_code ),
      CEREAL_NVP( args ),
      CEREAL_NVP( tag )
    );
  }

};

template<typename Spec>
std::ostream& operator<<(std::ostream& os, const Instruction<Spec>& inst) {
  cereal::JSONOutputArchive archive{ os };
  archive( inst );
  return os;
}

} // namespace sgpl

#endif // #ifndef SGPL_PROGRAM_INSTRUCTION_HPP_INCLUDE