+}
+
+// Read network parameters
+static bool read_parameters(std::istream& stream) {
+
+ std::uint32_t hashValue;
+ if (!read_header(stream, &hashValue, &netDescription))
+ return false;
+ if (hashValue != HashValue)
+ return false;
+ if (!Detail::read_parameters(stream, *featureTransformer))
+ return false;
+ for (std::size_t i = 0; i < LayerStacks; ++i)
+ if (!Detail::read_parameters(stream, *(network[i])))
+ return false;
+ return stream && stream.peek() == std::ios::traits_type::eof();
+}
+
+// Write network parameters
+static bool write_parameters(std::ostream& stream) {
+
+ if (!write_header(stream, HashValue, netDescription))
+ return false;
+ if (!Detail::write_parameters(stream, *featureTransformer))
+ return false;
+ for (std::size_t i = 0; i < LayerStacks; ++i)
+ if (!Detail::write_parameters(stream, *(network[i])))
+ return false;
+ return bool(stream);
+}
+
+void hint_common_parent_position(const Position& pos) {
+ featureTransformer->hint_common_access(pos);
+}
+
+// Evaluation function. Perform differential calculation.
+Value evaluate(const Position& pos, bool adjusted, int* complexity) {
+
+ // We manually align the arrays on the stack because with gcc < 9.3
+ // overaligning stack variables with alignas() doesn't work correctly.
+
+ constexpr uint64_t alignment = CacheLineSize;
+ constexpr int delta = 24;
+
+#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
+ TransformedFeatureType
+ transformedFeaturesUnaligned[FeatureTransformer::BufferSize
+ + alignment / sizeof(TransformedFeatureType)];
+
+ auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
+#else
+ alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
+#endif
+
+ ASSERT_ALIGNED(transformedFeatures, alignment);
+
+ const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
+ const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
+ const auto positional = network[bucket]->propagate(transformedFeatures);
+
+ if (complexity)
+ *complexity = abs(psqt - positional) / OutputScale;
+
+ // Give more value to positional evaluation when adjusted flag is set
+ if (adjusted)
+ return static_cast<Value>(((1024 - delta) * psqt + (1024 + delta) * positional)
+ / (1024 * OutputScale));
+ else
+ return static_cast<Value>((psqt + positional) / OutputScale);
+}
+
+struct NnueEvalTrace {
+ static_assert(LayerStacks == PSQTBuckets);
+
+ Value psqt[LayerStacks];
+ Value positional[LayerStacks];
+ std::size_t correctBucket;
+};
+
+static NnueEvalTrace trace_evaluate(const Position& pos) {
+
+ // We manually align the arrays on the stack because with gcc < 9.3
+ // overaligning stack variables with alignas() doesn't work correctly.
+ constexpr uint64_t alignment = CacheLineSize;
+
+#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
+ TransformedFeatureType
+ transformedFeaturesUnaligned[FeatureTransformer::BufferSize
+ + alignment / sizeof(TransformedFeatureType)];
+
+ auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
+#else
+ alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
+#endif
+
+ ASSERT_ALIGNED(transformedFeatures, alignment);
+
+ NnueEvalTrace t{};
+ t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
+ for (IndexType bucket = 0; bucket < LayerStacks; ++bucket)
+ {
+ const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket);
+ const auto positional = network[bucket]->propagate(transformedFeatures);
+
+ t.psqt[bucket] = static_cast<Value>(materialist / OutputScale);
+ t.positional[bucket] = static_cast<Value>(positional / OutputScale);
+ }