-#define ENABLE_OPERATORS_ON(T) \
-inline T operator+ (const T d1, const T d2) { return T(int(d1) + int(d2)); } \
-inline T operator- (const T d1, const T d2) { return T(int(d1) - int(d2)); } \
-inline T operator* (int i, const T d) { return T(i * int(d)); } \
-inline T operator* (const T d, int i) { return T(int(d) * i); } \
-inline T operator/ (const T d, int i) { return T(int(d) / i); } \
-inline T operator- (const T d) { return T(-int(d)); } \
-inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \
-inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \
-inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \
-inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \
-inline void operator*= (T& d, int i) { d = T(int(d) * i); } \
-inline void operator/= (T& d, int i) { d = T(int(d) / i); }
-
-ENABLE_OPERATORS_ON(Value)
-ENABLE_OPERATORS_ON(PieceType)
-ENABLE_OPERATORS_ON(Piece)
-ENABLE_OPERATORS_ON(Color)
-ENABLE_OPERATORS_ON(Depth)
-ENABLE_OPERATORS_ON(Square)
-ENABLE_OPERATORS_ON(File)
-ENABLE_OPERATORS_ON(Rank)
-
-#undef ENABLE_OPERATORS_ON
-
-// Extra operators for adding integers to a Value
-inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
-inline Value operator- (Value v, int i) { return Value(int(v) - i); }
-
-// Extracting the _signed_ lower and upper 16 bits it not so trivial
-// because according to the standard a simple cast to short is
-// implementation defined and so is a right shift of a signed integer.
-inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
-
-// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
-// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
-#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
-inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
-#else
-inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
-#endif
+/// Score enum stores a middlegame and an endgame value in a single integer (enum).
+/// The least significant 16 bits are used to store the middlegame value and the
+/// upper 16 bits are used to store the endgame value. We have to take care to
+/// avoid left-shifting a signed int to avoid undefined behavior.
+enum Score : int { SCORE_ZERO };
+
+constexpr Score make_score(int mg, int eg) {
+ return Score((int)((unsigned int)eg << 16) + mg);
+}
+
+/// Extracting the signed lower and upper 16 bits is not so trivial because
+/// according to the standard a simple cast to short is implementation defined
+/// and so is a right shift of a signed integer.
+inline Value eg_value(Score s) {
+ union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
+ return Value(eg.s);
+}
+
+inline Value mg_value(Score s) {
+ union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
+ return Value(mg.s);
+}
+
+#define ENABLE_BASE_OPERATORS_ON(T) \
+constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
+constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
+constexpr T operator-(T d) { return T(-int(d)); } \
+inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
+inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
+
+#define ENABLE_INCR_OPERATORS_ON(T) \
+inline T& operator++(T& d) { return d = T(int(d) + 1); } \
+inline T& operator--(T& d) { return d = T(int(d) - 1); }
+
+#define ENABLE_FULL_OPERATORS_ON(T) \
+ENABLE_BASE_OPERATORS_ON(T) \
+constexpr T operator*(int i, T d) { return T(i * int(d)); } \
+constexpr T operator*(T d, int i) { return T(int(d) * i); } \
+constexpr T operator/(T d, int i) { return T(int(d) / i); } \
+constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \
+inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
+inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
+
+ENABLE_FULL_OPERATORS_ON(Value)
+ENABLE_FULL_OPERATORS_ON(Direction)
+
+ENABLE_INCR_OPERATORS_ON(Piece)
+ENABLE_INCR_OPERATORS_ON(PieceSquare)
+ENABLE_INCR_OPERATORS_ON(PieceId)
+ENABLE_INCR_OPERATORS_ON(PieceType)
+ENABLE_INCR_OPERATORS_ON(Square)
+ENABLE_INCR_OPERATORS_ON(File)
+ENABLE_INCR_OPERATORS_ON(Rank)
+
+ENABLE_BASE_OPERATORS_ON(Score)
+
+#undef ENABLE_FULL_OPERATORS_ON
+#undef ENABLE_INCR_OPERATORS_ON
+#undef ENABLE_BASE_OPERATORS_ON
+
+/// Additional operators to add a Direction to a Square
+constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
+constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
+inline Square& operator+=(Square& s, Direction d) { return s = s + d; }
+inline Square& operator-=(Square& s, Direction d) { return s = s - d; }
+
+/// Only declared but not defined. We don't want to multiply two scores due to
+/// a very high risk of overflow. So user should explicitly convert to integer.
+Score operator*(Score, Score) = delete;
+
+/// Division of a Score must be handled separately for each term
+inline Score operator/(Score s, int i) {
+ return make_score(mg_value(s) / i, eg_value(s) / i);
+}
+
+/// Multiplication of a Score by an integer. We check for overflow in debug mode.
+inline Score operator*(Score s, int i) {
+
+ Score result = Score(int(s) * i);
+
+ assert(eg_value(result) == (i * eg_value(s)));
+ assert(mg_value(result) == (i * mg_value(s)));
+ assert((i == 0) || (result / i) == s);
+
+ return result;
+}