Program Listing for File space.hpp

Return to documentation for file (SeQuant/core/space.hpp)

//
// Created by Eduard Valeyev on 3/20/18.
//

#ifndef SEQUANT_SPACE_H
#define SEQUANT_SPACE_H

#include <bitset>
#include <cassert>
#include <cmath>

#include <SeQuant/core/attr.hpp>
#include <SeQuant/core/container.hpp>
#include <SeQuant/core/utility/string.hpp>
#include <SeQuant/core/wstring.hpp>

#include <range/v3/algorithm/any_of.hpp>

namespace sequant {

namespace bitset {
using type = int32_t;
constexpr type reserved = 0x80000000;
constexpr type null = 0x00000000;
}  // namespace bitset

using bitset_t = bitset::type;

class QuantumNumbersAttr;  // used to constrain TypeAttr ctor

class Index;  // friend of TypeAttr

class TypeAttr {
 public:
  constexpr TypeAttr() noexcept = default;

  const static TypeAttr null;


  explicit constexpr TypeAttr(bitset_t bitset) noexcept : bitset(bitset) {
    assert((this->bitset & bitset::reserved) == bitset::null);
  }

  template <typename T,
            typename = std::enable_if_t<
                meta::is_statically_castable_v<std::decay_t<T>, bitset_t> &&
                !std::is_same_v<std::decay_t<T>, bool> &&
                !std::is_same_v<std::decay_t<T>, QuantumNumbersAttr> &&
                !std::is_same_v<std::decay_t<T>, TypeAttr>>>
  constexpr TypeAttr(T &&value) noexcept
      : TypeAttr(static_cast<bitset_t>(std::forward<T>(value))) {}

  constexpr explicit operator int64_t() const {
    return static_cast<int64_t>(bitset);
  }
  constexpr explicit operator bitset_t() const { return bitset; }
  constexpr int32_t to_int32() const { return bitset; }

  constexpr explicit operator bool() const { return bitset != 0; }

  constexpr TypeAttr(const TypeAttr &other) { bitset = other.to_int32(); }

  constexpr TypeAttr unIon(TypeAttr other) const {
    return TypeAttr(this->to_int32() | other.to_int32());
  }

  friend constexpr TypeAttr operator|(const TypeAttr a, const TypeAttr b) {
    return a.unIon(b);
  }

  constexpr const TypeAttr xOr(TypeAttr other) const {
    return TypeAttr(this->to_int32() ^ other.to_int32());
  }

  friend constexpr TypeAttr operator^(const TypeAttr a, const TypeAttr b) {
    return a.xOr(b);
  }

  constexpr const TypeAttr intersection(TypeAttr other) const {
    return TypeAttr(this->to_int32() & other.to_int32());
  }

  friend constexpr TypeAttr operator&(const TypeAttr a, const TypeAttr b) {
    return a.intersection(b);
  }

  constexpr TypeAttr operator~() const { return ~this->to_int32(); }

  friend constexpr bool operator==(const TypeAttr lhs, const TypeAttr rhs) {
    return lhs.to_int32() == rhs.to_int32();
  }
  friend constexpr bool operator!=(const TypeAttr lhs, const TypeAttr rhs) {
    return !(lhs == rhs);
  }

  constexpr bool includes(TypeAttr other) const {
    return intersection(other) == other;
  }
  friend constexpr bool operator<(TypeAttr a, TypeAttr b) {
    return a.to_int32() < b.to_int32();
  }

 private:
  bitset_t bitset = 0;

  friend class Index;

  const static TypeAttr reserved;

  static TypeAttr make_reserved() {
    TypeAttr result;
    result.bitset = 0x80000000;
    return result;
  }
};  // struct TypeAttr

inline const TypeAttr TypeAttr::null;
inline const TypeAttr TypeAttr::reserved = TypeAttr::make_reserved();

class QuantumNumbersAttr {
 public:
  constexpr QuantumNumbersAttr() noexcept = default;

  const static QuantumNumbersAttr null;


  explicit constexpr QuantumNumbersAttr(bitset_t bitset) noexcept
      : bitset(bitset) {
    assert((this->bitset & bitset::reserved) == bitset::null);
  }

  template <typename QN,
            typename = std::enable_if_t<
                meta::is_statically_castable_v<std::decay_t<QN>, bitset_t> &&
                !std::is_same_v<std::decay_t<QN>, bool> &&
                !std::is_same_v<std::decay_t<QN>, TypeAttr> &&
                !std::is_same_v<std::decay_t<QN>, QuantumNumbersAttr>>>
  constexpr QuantumNumbersAttr(QN &&value) noexcept
      : bitset(static_cast<bitset_t>(std::forward<QN>(value))) {}

  constexpr explicit operator int64_t() const {
    return static_cast<int64_t>(bitset);
  }
  constexpr explicit operator bitset_t() const { return bitset; }
  constexpr int32_t to_int32() const { return bitset; }

  constexpr explicit operator bool() const { return bitset != 0; }

  constexpr QuantumNumbersAttr xOr(QuantumNumbersAttr other) const {
    return QuantumNumbersAttr(this->to_int32() ^ other.to_int32());
  }

  friend constexpr QuantumNumbersAttr operator^(const QuantumNumbersAttr a,
                                                const QuantumNumbersAttr b) {
    return a.xOr(b);
  }

  constexpr QuantumNumbersAttr unIon(QuantumNumbersAttr other) const {
    return QuantumNumbersAttr(this->to_int32() | other.to_int32());
  }

  friend constexpr QuantumNumbersAttr operator|(const QuantumNumbersAttr a,
                                                const QuantumNumbersAttr b) {
    return a.unIon(b);
  }

  constexpr QuantumNumbersAttr intersection(QuantumNumbersAttr other) const {
    return QuantumNumbersAttr(this->to_int32() & other.to_int32());
  }

  friend constexpr QuantumNumbersAttr operator&(const QuantumNumbersAttr a,
                                                const QuantumNumbersAttr b) {
    return a.intersection(b);
  }

  constexpr QuantumNumbersAttr operator~() const { return ~this->to_int32(); }

  friend constexpr bool operator==(QuantumNumbersAttr lhs,
                                   QuantumNumbersAttr rhs) {
    return lhs.to_int32() == rhs.to_int32();
  }
  friend constexpr bool operator!=(QuantumNumbersAttr lhs,
                                   QuantumNumbersAttr rhs) {
    return !(lhs == rhs);
  }

  bool includes(QuantumNumbersAttr other) const {
    return intersection(other) == other;
  }
  friend constexpr bool operator<(QuantumNumbersAttr lhs,
                                  QuantumNumbersAttr rhs) {
    return lhs.to_int32() < rhs.to_int32();
  }

 private:
  bitset_t bitset = bitset::null;

  friend class Index;

  const static QuantumNumbersAttr reserved;

  static QuantumNumbersAttr make_reserved() {
    QuantumNumbersAttr result;
    result.bitset = bitset::reserved;
    return result;
  }
};  // struct QuantumNumbersAttr

inline const QuantumNumbersAttr QuantumNumbersAttr::null;
inline const QuantumNumbersAttr QuantumNumbersAttr::reserved =
    QuantumNumbersAttr::make_reserved();

class IndexSpace {
 public:
  struct Attr : TypeAttr, QuantumNumbersAttr {
    Attr(TypeAttr type, QuantumNumbersAttr qns) noexcept
        : TypeAttr(type), QuantumNumbersAttr(qns){};
    Attr(int32_t type, int32_t qns) noexcept
        : TypeAttr(type), QuantumNumbersAttr(qns){};

    Attr() = default;
    Attr(const Attr &) = default;
    Attr(Attr &&) = default;
    Attr &operator=(const Attr &) = default;
    Attr &operator=(Attr &&) = default;

    const static Attr null;

    constexpr const TypeAttr &type() const {
      return static_cast<const TypeAttr &>(*this);
    }
    constexpr TypeAttr &type() { return static_cast<TypeAttr &>(*this); }
    constexpr const QuantumNumbersAttr &qns() const {
      return static_cast<const QuantumNumbersAttr &>(*this);
    }
    constexpr QuantumNumbersAttr &qns() {
      return static_cast<QuantumNumbersAttr &>(*this);
    }

    constexpr explicit operator int64_t() const {
      return (static_cast<int64_t>(this->type()) << 32) +
             static_cast<int64_t>(this->qns());
    }

    explicit operator bool() const {
      return static_cast<bool>(this->type()) || static_cast<bool>(this->qns());
    }

    Attr unIon(Attr other) const {
      return {this->type().unIon(other.type()), this->qns().unIon(other.qns())};
    }

    friend Attr operator|(Attr a, Attr b) { return a.unIon(b); }

    Attr intersection(Attr other) const {
      return Attr(this->type().intersection(other.type()),
                  this->qns().intersection(other.qns()));
    }
    friend Attr operator&(Attr a, Attr b) { return a.intersection(b); }

    bool includes(Attr other) const {
      return this->type().includes(other.type()) &&
             this->qns().includes(other.qns());
    }

    constexpr bool operator==(Attr other) const {
      return this->type() == other.type() && this->qns() == other.qns();
    }
    constexpr bool operator!=(Attr other) const { return !(*this == other); }

    constexpr bool operator<(Attr other) const {
      if (this->qns() == other.qns()) {
        return this->type() < other.type();
      } else {
        return this->qns() < other.qns();
      }
    }
  };  // struct Attr

  using Type = TypeAttr;
  using QuantumNumbers = QuantumNumbersAttr;

  struct bad_key : std::invalid_argument {
    bad_key() : std::invalid_argument("bad key") {}
    template <typename S, typename = meta::EnableIfAllBasicStringConvertible<S>>
    bad_key(S &&key)
        : std::invalid_argument(std::string("bad key: ") +
                                sequant::to_string(key)) {}
  };

  struct KeyCompare {
    using is_transparent = void;
    bool operator()(const IndexSpace &a, const IndexSpace &b) const {
      return a.base_key() < b.base_key();
    }
    bool operator()(const std::wstring &a, const IndexSpace &b) const {
      return a < b.base_key();
    }
    bool operator()(const std::wstring_view &a, const IndexSpace &b) const {
      return a < b.base_key();
    }
    bool operator()(const IndexSpace &a, const std::wstring &b) const {
      return a.base_key() < b;
    }
    bool operator()(const IndexSpace &a, const std::wstring_view &b) const {
      return a.base_key() < b;
    }
    bool operator()(const std::wstring &a, const std::wstring &b) const {
      return a < b;
    }
    bool operator()(const std::wstring &a, const std::wstring_view &b) const {
      return a < b;
    }
    bool operator()(const std::wstring_view &a, const std::wstring &b) const {
      return a < b;
    }
  };

  friend constexpr bool operator==(IndexSpace const &,
                                   IndexSpace const &) noexcept;
  friend constexpr bool operator!=(IndexSpace const &,
                                   IndexSpace const &) noexcept;
  friend constexpr bool operator<(IndexSpace const &,
                                  IndexSpace const &) noexcept;

  constexpr Attr attr() const noexcept { return attr_; }
  constexpr Type type() const noexcept { return attr().type(); }
  QuantumNumbers qns() const noexcept { return attr().qns(); }

  IndexSpace() noexcept {}

  const static IndexSpace null;

  explicit operator bool() const { return *this != null; }

  template <typename S, typename = meta::EnableIfAllBasicStringConvertible<S>>
  IndexSpace(S &&type_label, TypeAttr typeattr,
             QuantumNumbersAttr qnattr = QuantumNumbersAttr{0},
             unsigned long approximate_size = 10)
      : attr_(typeattr, qnattr),
        base_key_(sequant::to_wstring(std::forward<S>(type_label))),
        approximate_size_(approximate_size) {}

  IndexSpace(const IndexSpace &other) = default;
  IndexSpace(IndexSpace &&other) = default;
  IndexSpace &operator=(const IndexSpace &other) = default;
  IndexSpace &operator=(IndexSpace &&other) = default;

  const std::wstring &base_key() const { return base_key_; }
  static std::wstring_view reduce_key(std::wstring_view key) {
    const auto underscore_position = key.rfind(L'_');
    if (underscore_position != std::wstring::npos) {  // key can be reduced
      return key.substr(0, underscore_position);
    } else {
      return key;
    }
  }
  static std::wstring reduce_key(std::string_view key) {
    const auto underscore_position = key.rfind(L'_');
    if (underscore_position != std::string::npos) {  // key can be reduced
      return sequant::to_wstring(key.substr(0, underscore_position));
    } else {
      return sequant::to_wstring(key);
    }
  }

  std::size_t approximate_size() const { return approximate_size_; }

  void approximate_size(size_t n) { approximate_size_ = n; }

 private:
  Attr attr_;
  std::wstring base_key_;
  std::size_t approximate_size_;

  static std::wstring to_wstring(std::wstring_view key) {
    return std::wstring(key.begin(), key.end());
  }
};  // class IndexSpace

inline const IndexSpace IndexSpace::null;
inline const IndexSpace::Attr IndexSpace::Attr::null;

inline bool includes(IndexSpace::Type type1, IndexSpace::Type type2) {
  return type1.includes(type2);
}
inline bool includes(IndexSpace::QuantumNumbers qns1,
                     IndexSpace::QuantumNumbers qns2) {
  return qns1.includes(qns2);
}
inline bool includes(const IndexSpace &space1, const IndexSpace &space2) {
  return space1.attr().includes(space2.attr());
}

[[nodiscard]] inline constexpr bool operator<(
    const IndexSpace &space1, const IndexSpace &space2) noexcept {
  return space1.attr() < space2.attr();
}

[[nodiscard]] inline constexpr bool operator==(
    IndexSpace const &space1, IndexSpace const &space2) noexcept {
  return space1.type() == space2.type() && space1.qns() == space2.qns() &&
         space1.base_key() == space2.base_key();
}

[[nodiscard]] inline constexpr bool operator!=(
    IndexSpace const &space1, IndexSpace const &space2) noexcept {
  return !(space1 == space2);
}

}  // namespace sequant

#endif  // SEQUANT_SPACE_H