<commit_before>#ifndef GROUP2_H
#define GROUP2_H

#include <ostream>

namespace Group2 {

namespace Elements {
class A {};
class B {};
class C {};
class D {};
}

template <typename LHS, typename RHS>
struct Mult {};

template <>
struct Mult<Elements::A, Elements::A> {
  using type = Elements::A;
};
template <>
struct Mult<Elements::A, Elements::B> {
  using type = Elements::B;
};
template <>
struct Mult<Elements::A, Elements::C> {
  using type = Elements::C;
};
template <>
struct Mult<Elements::A, Elements::D> {
  using type = Elements::D;
};

template <>
struct Mult<Elements::B, Elements::A> {
  using type = Elements::B;
};
template <>
struct Mult<Elements::B, Elements::B> {
  using type = Elements::A;
};
template <>
struct Mult<Elements::B, Elements::C> {
  using type = Elements::D;
};
template <>
struct Mult<Elements::B, Elements::D> {
  using type = Elements::C;
};

template <>
struct Mult<Elements::C, Elements::A> {
  using type = Elements::C;
};
template <>
struct Mult<Elements::C, Elements::B> {
  using type = Elements::D;
};
template <>
struct Mult<Elements::C, Elements::C> {
  using type = Elements::B;
};
template <>
struct Mult<Elements::C, Elements::D> {
  using type = Elements::A;
};

template <>
struct Mult<Elements::D, Elements::A> {
  using type = Elements::D;
};
template <>
struct Mult<Elements::D, Elements::B> {
  using type = Elements::C;
};
template <>
struct Mult<Elements::D, Elements::C> {
  using type = Elements::A;
};
template <>
struct Mult<Elements::D, Elements::D> {
  using type = Elements::B;
};

template <typename T>
class Element {
 public:
  using type = T;
  template <typename U, typename R = typename Mult<T, U>::type>
  auto operator*(Element<U>) -> Element<R> {
    return {};
  }
};
using A = Element<Elements::A>;
using B = Element<Elements::B>;
using C = Element<Elements::C>;
using D = Element<Elements::D>;

auto operator<<(std::ostream& os, const A&) -> std::ostream & {
  return (os << 'A');
}
auto operator<<(std::ostream& os, const B&) -> std::ostream & {
  return (os << 'B');
}
auto operator<<(std::ostream& os, const C&) -> std::ostream & {<commit_msg>Use a macro to save some LoC.<commit_after>#ifndef GROUP2_H
#define GROUP2_H

#include <ostream>

namespace Group2 {

namespace Elements {
class A {};
class B {};
class C {};
class D {};
}

template <typename LHS, typename RHS>
struct Mult {};

#define DEFINE_RELATION(X, Y, Z)          \
  template <>                             \
  struct Mult<Elements::X, Elements::Y> { \
    using type = Elements::Z;             \
  }
DEFINE_RELATION(A, A, A);
DEFINE_RELATION(A, B, B);
DEFINE_RELATION(A, C, C);
DEFINE_RELATION(A, D, D);
DEFINE_RELATION(B, A, B);
DEFINE_RELATION(B, B, A);
DEFINE_RELATION(B, C, D);
DEFINE_RELATION(B, D, C);
DEFINE_RELATION(C, A, C);
DEFINE_RELATION(C, B, D);
DEFINE_RELATION(C, C, B);
DEFINE_RELATION(C, D, A);
DEFINE_RELATION(D, A, D);
DEFINE_RELATION(D, B, C);
DEFINE_RELATION(D, C, A);
DEFINE_RELATION(D, D, B);

template <typename T>
class Element {
 public:
  using type = T;
  template <typename U, typename R = typename Mult<T, U>::type>
  auto operator*(Element<U>) -> Element<R> {
    return {};
  }
};
using A = Element<Elements::A>;
using B = Element<Elements::B>;
using C = Element<Elements::C>;
using D = Element<Elements::D>;

auto operator<<(std::ostream& os, const A&) -> std::ostream & {
  return (os << 'A');
}
auto operator<<(std::ostream& os, const B&) -> std::ostream & {
  return (os << 'B');
}
auto operator<<(std::ostream& os, const C&) -> std::ostream & {
{'fraction_non_alphanumeric': 0.18436703483432457, 'fraction_numerical': 0.0016992353440951572, 'mean_word_length': 4.579778830963665, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3411643', 'n_tokens_mistral': 1299, 'n_tokens_neox': 1215, 'n_words': 341}