/*
* Botan 3.9.0 Amalgamation
* (C) 1999-2023 The Botan Authors
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "botan_generic.h"

#include <algorithm>
#include <array>
#include <bit>
#include <chrono>
#include <concepts>
#include <cstdint>
#include <cstring>
#include <functional>
#include <istream>
#include <locale>
#include <map>
#include <memory>
#include <optional>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>



namespace Botan {

/**
 * Reduce the values of @p keys into an accumulator initialized with @p acc using
 * the reducer function @p reducer.
 *
 * The @p reducer is a function taking the accumulator and a single key to return the
 * new accumulator. Keys are consecutively reduced into the accumulator.
 *
 * @return the accumulator containing the reduction of @p keys
 */
template <typename RetT, typename KeyT, typename ReducerT>
RetT reduce(const std::vector<KeyT>& keys, RetT acc, ReducerT reducer)
   requires std::is_convertible_v<ReducerT, std::function<RetT(RetT, const KeyT&)>>
{
   for(const KeyT& key : keys) {
      acc = reducer(std::move(acc), key);
   }
   return acc;
}

/**
* Existence check for values
*/
template <typename T, typename OT>
bool value_exists(const std::vector<T>& vec, const OT& val) {
   for(size_t i = 0; i != vec.size(); ++i) {
      if(vec[i] == val) {
         return true;
      }
   }
   return false;
}

template <typename T, typename Pred>
void map_remove_if(Pred pred, T& assoc) {
   auto i = assoc.begin();
   while(i != assoc.end()) {
      if(pred(i->first)) {
         assoc.erase(i++);
      } else {
         i++;
      }
   }
}

/**
 * Helper class to ease unmarshalling of concatenated fixed-length values
 */
class BufferSlicer final {
   public:
      explicit BufferSlicer(std::span<const uint8_t> buffer) : m_remaining(buffer) {}

      template <concepts::contiguous_container ContainerT>
      auto copy(const size_t count) {
         const auto result = take(count);
         return ContainerT(result.begin(), result.end());
      }

      auto copy_as_vector(const size_t count) { return copy<std::vector<uint8_t>>(count); }

      auto copy_as_secure_vector(const size_t count) { return copy<secure_vector<uint8_t>>(count); }

      std::span<const uint8_t> take(const size_t count) {
         BOTAN_STATE_CHECK(remaining() >= count);
         auto result = m_remaining.first(count);
         m_remaining = m_remaining.subspan(count);
         return result;
      }

      template <size_t count>
      std::span<const uint8_t, count> take() {
         BOTAN_STATE_CHECK(remaining() >= count);
         auto result = m_remaining.first<count>();
         m_remaining = m_remaining.subspan(count);
         return result;
      }

      template <concepts::contiguous_strong_type T>
      StrongSpan<const T> take(const size_t count) {
         return StrongSpan<const T>(take(count));
      }

      uint8_t take_byte() { return take(1)[0]; }

      void copy_into(std::span<uint8_t> sink) {
         const auto data = take(sink.size());
         std::copy(data.begin(), data.end(), sink.begin());
      }

      void skip(const size_t count) { take(count); }

      size_t remaining() const { return m_remaining.size(); }

      bool empty() const { return m_remaining.empty(); }

   private:
      std::span<const uint8_t> m_remaining;
};

/**
 * @brief Helper class to ease in-place marshalling of concatenated fixed-length
 *        values.
 *
 * The size of the final buffer must be known from the start, reallocations are
 * not performed.
 */
class BufferStuffer final {
   public:
      constexpr explicit BufferStuffer(std::span<uint8_t> buffer) : m_buffer(buffer) {}

      /**
       * @returns a span for the next @p bytes bytes in the concatenated buffer.
       *          Checks that the buffer is not exceded.
       */
      constexpr std::span<uint8_t> next(size_t bytes) {
         BOTAN_STATE_CHECK(m_buffer.size() >= bytes);

         auto result = m_buffer.first(bytes);
         m_buffer = m_buffer.subspan(bytes);
         return result;
      }

      template <size_t bytes>
      constexpr std::span<uint8_t, bytes> next() {
         BOTAN_STATE_CHECK(m_buffer.size() >= bytes);

         auto result = m_buffer.first<bytes>();
         m_buffer = m_buffer.subspan(bytes);
         return result;
      }

      template <concepts::contiguous_strong_type StrongT>
      StrongSpan<StrongT> next(size_t bytes) {
         return StrongSpan<StrongT>(next(bytes));
      }

      /**
       * @returns a reference to the next single byte in the buffer
       */
      constexpr uint8_t& next_byte() { return next(1)[0]; }

      constexpr void append(std::span<const uint8_t> buffer) {
         auto sink = next(buffer.size());
         std::copy(buffer.begin(), buffer.end(), sink.begin());
      }

      constexpr void append(uint8_t b, size_t repeat = 1) {
         auto sink = next(repeat);
         std::fill(sink.begin(), sink.end(), b);
      }

      constexpr bool full() const { return m_buffer.empty(); }

      constexpr size_t remaining_capacity() const { return m_buffer.size(); }

   private:
      std::span<uint8_t> m_buffer;
};

namespace detail {

/**
 * Helper function that performs range size-checks as required given the
 * selected output and input range types. If all lengths are known at compile
 * time, this check will be performed at compile time as well. It will then
 * instantiate an output range and concatenate the input ranges' contents.
 */
template <ranges::spanable_range OutR, ranges::spanable_range... Rs>
constexpr OutR concatenate(Rs&&... ranges)
   requires(concepts::reservable_container<OutR> || ranges::statically_spanable_range<OutR>)
{
   OutR result{};

   // Prepare and validate the output range and construct a lambda that does the
   // actual filling of the result buffer.
   // (if no input ranges are given, GCC claims that fill_fn is unused)
   [[maybe_unused]] auto fill_fn = [&] {
      if constexpr(concepts::reservable_container<OutR>) {
         // dynamically allocate the correct result byte length
         const size_t total_size = (ranges.size() + ... + 0);
         result.reserve(total_size);

         // fill the result buffer using a back-inserter
         return [&result](auto&& range) {
            std::copy(
               std::ranges::begin(range), std::ranges::end(range), std::back_inserter(unwrap_strong_type(result)));
         };
      } else {
         if constexpr((ranges::statically_spanable_range<Rs> && ... && true)) {
            // all input ranges have a static extent, so check the total size at compile time
            // (work around an issue in MSVC that warns `total_size` is unused)
            [[maybe_unused]] constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
            static_assert(result.size() == total_size, "size of result buffer does not match the sum of input buffers");
         } else {
            // at least one input range has a dynamic extent, so check the total size at runtime
            const size_t total_size = (ranges.size() + ... + 0);
            BOTAN_ARG_CHECK(result.size() == total_size,
                            "result buffer has static extent that does not match the sum of input buffers");
         }

         // fill the result buffer and hold the current output-iterator position
         return [itr = std::ranges::begin(result)](auto&& range) mutable {
            std::copy(std::ranges::begin(range), std::ranges::end(range), itr);
            std::advance(itr, std::ranges::size(range));
         };
      }
   }();

   // perform the actual concatenation
   (fill_fn(std::forward<Rs>(ranges)), ...);

   return result;
}

}  // namespace detail

/**
 * Concatenate an arbitrary number of buffers. Performs range-checks as needed.
 *
 * The output type can be auto-detected based on the input ranges, or explicitly
 * specified by the caller. If all input ranges have a static extent, the total
 * size is calculated at compile time and a statically sized std::array<> is used.
 * Otherwise this tries to use the type of the first input range as output type.
 *
 * Alternatively, the output container type can be specified explicitly.
 */
template <typename OutR = detail::AutoDetect, ranges::spanable_range... Rs>
constexpr auto concat(Rs&&... ranges)
   requires(all_same_v<std::ranges::range_value_t<Rs>...>)
{
   if constexpr(std::same_as<detail::AutoDetect, OutR>) {
      // Try to auto-detect a reasonable output type given the input ranges
      static_assert(sizeof...(Rs) > 0, "Cannot auto-detect the output type if not a single input range is provided.");
      using candidate_result_t = std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Rs...>>>;
      using result_range_value_t = std::remove_cvref_t<std::ranges::range_value_t<candidate_result_t>>;

      if constexpr((ranges::statically_spanable_range<Rs> && ...)) {
         // If all input ranges have a static extent, we can calculate the total size at compile time
         // and therefore can use a statically sized output container. This is constexpr.
         constexpr size_t total_size = (decltype(std::span{ranges})::extent + ... + 0);
         using out_array_t = std::array<result_range_value_t, total_size>;
         return detail::concatenate<out_array_t>(std::forward<Rs>(ranges)...);
      } else {
         // If at least one input range has a dynamic extent, we must use a dynamically allocated output container.
         // We assume that the user wants to use the first input range's container type as output type.
         static_assert(
            concepts::reservable_container<candidate_result_t>,
            "First input range has static extent, but a dynamically allocated output range is required. Please explicitly specify a dynamically allocatable output type.");
         return detail::concatenate<candidate_result_t>(std::forward<Rs>(ranges)...);
      }
   } else {
      // The caller has explicitly specified the output type
      return detail::concatenate<OutR>(std::forward<Rs>(ranges)...);
   }
}

template <typename... Alts, typename... Ts>
constexpr bool holds_any_of(const std::variant<Ts...>& v) noexcept {
   return (std::holds_alternative<Alts>(v) || ...);
}

template <typename GeneralVariantT, typename SpecialT>
constexpr bool is_generalizable_to(const SpecialT& /*unnamed*/) noexcept {
   return std::is_constructible_v<GeneralVariantT, SpecialT>;
}

template <typename GeneralVariantT, typename... SpecialTs>
constexpr bool is_generalizable_to(const std::variant<SpecialTs...>& /*unnamed*/) noexcept {
   return (std::is_constructible_v<GeneralVariantT, SpecialTs> && ...);
}

/**
 * @brief Converts a given variant into another variant-ish whose type states
 *        are a super set of the given variant.
 *
 * This is useful to convert restricted variant types into more general
 * variants types.
 */
template <typename GeneralVariantT, typename SpecialT>
constexpr GeneralVariantT generalize_to(SpecialT&& specific)
   requires(std::is_constructible_v<GeneralVariantT, std::decay_t<SpecialT>>)
{
   return std::forward<SpecialT>(specific);
}

/**
 * @brief Converts a given variant into another variant-ish whose type states
 *        are a super set of the given variant.
 *
 * This is useful to convert restricted variant types into more general
 * variants types.
 */
template <typename GeneralVariantT, typename... SpecialTs>
constexpr GeneralVariantT generalize_to(std::variant<SpecialTs...> specific) {
   static_assert(
      is_generalizable_to<GeneralVariantT>(specific),
      "Desired general type must be implicitly constructible by all types of the specialized std::variant<>");
   return std::visit([](auto s) -> GeneralVariantT { return s; }, std::move(specific));
}

// This is a helper utility to emulate pattern matching with std::visit.
// See https://en.cppreference.com/w/cpp/utility/variant/visit for more info.
template <class... Ts>
struct overloaded : Ts... {
      using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

/**
 * @brief Helper class to create a RAII-style cleanup callback
 *
 * Ensures that the cleanup callback given in the object's constructor is called
 * when the object is destroyed. Use this to ensure some cleanup code runs when
 * leaving the current scope.
 */
template <std::invocable FunT>
class scoped_cleanup final {
   public:
      explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {}

      scoped_cleanup(const scoped_cleanup&) = delete;
      scoped_cleanup& operator=(const scoped_cleanup&) = delete;

      scoped_cleanup(scoped_cleanup&& other) noexcept : m_cleanup(std::move(other.m_cleanup)) { other.disengage(); }

      scoped_cleanup& operator=(scoped_cleanup&& other) noexcept {
         if(this != &other) {
            m_cleanup = std::move(other.m_cleanup);
            other.disengage();
         }
         return *this;
      }

      ~scoped_cleanup() {
         if(m_cleanup.has_value()) {
            m_cleanup.value()();
         }
      }

      /**
       * Disengage the cleanup callback, i.e., prevent it from being called
       */
      void disengage() noexcept { m_cleanup.reset(); }

   private:
      std::optional<FunT> m_cleanup;
};

/**
* Define BOTAN_ASSERT_IS_SOME
*/
template <typename T>
T assert_is_some(std::optional<T> v, const char* expr, const char* func, const char* file, int line) {
   if(v) {
      return *v;
   } else {
      Botan::assertion_failure(expr, "optional had value", func, file, line);
   }
}

// NOLINTNEXTLINE(*-macro-usage)
#define BOTAN_ASSERT_IS_SOME(v) assert_is_some(v, #v, __func__, __FILE__, __LINE__)

/*
 * @brief Helper class to pass literal strings to C++ templates
 */
template <size_t N>
class StringLiteral final {
   public:
      // NOLINTNEXTLINE(*-explicit-conversions)
      constexpr StringLiteral(const char (&str)[N]) : value() { std::copy_n(str, N, value); }

      // NOLINTNEXTLINE(*non-private-member-variable*)
      char value[N];
};

// TODO: C++23: replace with std::to_underlying
template <typename T>
   requires std::is_enum_v<T>
auto to_underlying(T e) noexcept {
   return static_cast<std::underlying_type_t<T>>(e);
}

// TODO: C++23 - use std::out_ptr
template <typename T>
[[nodiscard]] constexpr auto out_ptr(T& outptr) noexcept {
   class out_ptr_t {
      public:
         constexpr ~out_ptr_t() noexcept {
            m_ptr.reset(m_rawptr);
            m_rawptr = nullptr;
         }

         // NOLINTNEXTLINE(*-explicit-conversions) FIXME
         constexpr out_ptr_t(T& outptr) noexcept : m_ptr(outptr), m_rawptr(nullptr) {}

         out_ptr_t(const out_ptr_t&) = delete;
         out_ptr_t(out_ptr_t&&) = delete;
         out_ptr_t& operator=(const out_ptr_t&) = delete;
         out_ptr_t& operator=(out_ptr_t&&) = delete;

         // NOLINTNEXTLINE(*-explicit-conversions) FIXME
         [[nodiscard]] constexpr operator typename T::element_type **() && noexcept { return &m_rawptr; }

      private:
         T& m_ptr;
         typename T::element_type* m_rawptr;
   };

   return out_ptr_t{outptr};
}

template <typename T>
   requires std::is_default_constructible_v<T>
[[nodiscard]] constexpr auto out_opt(std::optional<T>& outopt) noexcept {
   class out_opt_t {
      public:
         constexpr ~out_opt_t() noexcept { m_opt = m_raw; }

         // NOLINTNEXTLINE(*-explicit-conversions) FIXME
         constexpr out_opt_t(std::optional<T>& outopt) noexcept : m_opt(outopt) {}

         out_opt_t(const out_opt_t&) = delete;
         out_opt_t(out_opt_t&&) = delete;
         out_opt_t& operator=(const out_opt_t&) = delete;
         out_opt_t& operator=(out_opt_t&&) = delete;

         // NOLINTNEXTLINE(*-explicit-conversions) FIXME
         [[nodiscard]] constexpr operator T*() && noexcept { return &m_raw; }

      private:
         std::optional<T>& m_opt;
         T m_raw;
   };

   return out_opt_t{outopt};
}

}  // namespace Botan


namespace Botan {

/**
 * Defines the strategy for handling the final block of input data in the
 * handle_unaligned_data() method of the AlignmentBuffer<>.
 *
 * - is_not_special:   the final block is treated like any other block
 * - must_be_deferred: the final block is not emitted while bulk processing (typically add_data())
 *                     but is deferred until manually consumed (typically final_result())
 *
 * The AlignmentBuffer<> assumes data to be "the final block" if no further
 * input data is available in the BufferSlicer<>. This might result in some
 * performance overhead when using the must_be_deferred strategy.
 */
enum class AlignmentBufferFinalBlock : uint8_t {
   is_not_special = 0,
   must_be_deferred = 1,
};

/**
 * @brief Alignment buffer helper
 *
 * Many algorithms have an intrinsic block size in which they consume input
 * data. When streaming arbitrary data chunks to such algorithms we must store
 * some data intermittently to honor the algorithm's alignment requirements.
 *
 * This helper encapsulates such an alignment buffer. The API of this class is
 * designed to minimize user errors in the algorithm implementations. Therefore,
 * it is strongly opinionated on its use case. Don't try to use it for anything
 * but the described circumstance.
 *
 * @tparam T                     the element type of the internal buffer
 * @tparam BLOCK_SIZE            the buffer size to use for the alignment buffer
 * @tparam FINAL_BLOCK_STRATEGY  defines whether the final input data block is
 *                               retained in handle_unaligned_data() and must be
 *                               manually consumed
 */
template <typename T,
          size_t BLOCK_SIZE,
          AlignmentBufferFinalBlock FINAL_BLOCK_STRATEGY = AlignmentBufferFinalBlock::is_not_special>
   requires(BLOCK_SIZE > 0)
class AlignmentBuffer {
   public:
      AlignmentBuffer() = default;

      ~AlignmentBuffer() { secure_scrub_memory(m_buffer.data(), m_buffer.size()); }

      AlignmentBuffer(const AlignmentBuffer& other) = default;
      AlignmentBuffer(AlignmentBuffer&& other) noexcept = default;
      AlignmentBuffer& operator=(const AlignmentBuffer& other) = default;
      AlignmentBuffer& operator=(AlignmentBuffer&& other) noexcept = default;

      void clear() {
         clear_mem(m_buffer.data(), m_buffer.size());
         m_position = 0;
      }

      /**
       * Fills the currently unused bytes of the buffer with zero bytes
       */
      void fill_up_with_zeros() {
         if(!ready_to_consume()) {
            clear_mem(&m_buffer[m_position], elements_until_alignment());
            m_position = m_buffer.size();
         }
      }

      /**
       * Appends the provided @p elements to the buffer. The user has to make
       * sure that @p elements fits in the remaining capacity of the buffer.
       */
      void append(std::span<const T> elements) {
         BOTAN_ASSERT_NOMSG(elements.size() <= elements_until_alignment());
         std::copy(elements.begin(), elements.end(), m_buffer.begin() + m_position);
         m_position += elements.size();
      }

      /**
       * Allows direct modification of the first @p elements in the buffer.
       * This is a low-level accessor that neither takes the buffer's current
       * capacity into account nor does it change the internal cursor.
       * Beware not to overwrite unconsumed bytes.
       */
      std::span<T> directly_modify_first(size_t elements) {
         BOTAN_ASSERT_NOMSG(size() >= elements);
         return std::span(m_buffer).first(elements);
      }

      /**
       * Allows direct modification of the last @p elements in the buffer.
       * This is a low-level accessor that neither takes the buffer's current
       * capacity into account nor does it change the internal cursor.
       * Beware not to overwrite unconsumed bytes.
       */
      std::span<T> directly_modify_last(size_t elements) {
         BOTAN_ASSERT_NOMSG(size() >= elements);
         return std::span(m_buffer).last(elements);
      }

      /**
       * Once the buffer reached alignment, this can be used to consume as many
       * input bytes from the given @p slider as possible. The output always
       * contains data elements that are a multiple of the intrinsic block size.
       *
       * @returns a view onto the aligned data from @p slicer and the number of
       *          full blocks that are represented by this view.
       */
      [[nodiscard]] std::tuple<std::span<const uint8_t>, size_t> aligned_data_to_process(BufferSlicer& slicer) const {
         BOTAN_ASSERT_NOMSG(in_alignment());

         // When the final block is to be deferred, the last block must not be
         // selected for processing if there is no (unaligned) extra input data.
         const size_t defer = (defers_final_block()) ? 1 : 0;
         const size_t full_blocks_to_process = (slicer.remaining() - defer) / m_buffer.size();
         return {slicer.take(full_blocks_to_process * m_buffer.size()), full_blocks_to_process};
      }

      /**
       * Once the buffer reached alignment, this can be used to consume full
       * blocks from the input data represented by @p slicer.
       *
       * @returns a view onto the next full block from @p slicer or std::nullopt
       *          if not enough data is available in @p slicer.
       */
      [[nodiscard]] std::optional<std::span<const uint8_t>> next_aligned_block_to_process(BufferSlicer& slicer) const {
         BOTAN_ASSERT_NOMSG(in_alignment());

         // When the final block is to be deferred, the last block must not be
         // selected for processing if there is no (unaligned) extra input data.
         const size_t defer = (defers_final_block()) ? 1 : 0;
         if(slicer.remaining() < m_buffer.size() + defer) {
            return std::nullopt;
         }

         return slicer.take(m_buffer.size());
      }

      /**
       * Intermittently buffers potentially unaligned data provided in @p
       * slicer. If the internal buffer already contains some elements, data is
       * appended. Once a full block is collected, it is returned to the caller
       * for processing.
       *
       * @param slicer the input data source to be (partially) consumed
       * @returns a view onto a full block once enough data was collected, or
       *          std::nullopt if no full block is available yet
       */
      [[nodiscard]] std::optional<std::span<const T>> handle_unaligned_data(BufferSlicer& slicer) {
         // When the final block is to be deferred, we would need to store and
         // hold a buffer that contains exactly one block until more data is
         // passed or it is explicitly consumed.
         const size_t defer = (defers_final_block()) ? 1 : 0;

         if(in_alignment() && slicer.remaining() >= m_buffer.size() + defer) {
            // We are currently in alignment and the passed-in data source
            // contains enough data to benefit from aligned processing.
            // Therefore, we don't copy anything into the intermittent buffer.
            return std::nullopt;
         }

         // Fill the buffer with as much input data as needed to reach alignment
         // or until the input source is depleted.
         const auto elements_to_consume = std::min(m_buffer.size() - m_position, slicer.remaining());
         append(slicer.take(elements_to_consume));

         // If we collected enough data, we push out one full block. When
         // deferring the final block is enabled, we additionally check that
         // more input data is available to continue processing a consecutive
         // block.
         if(ready_to_consume() && (!defers_final_block() || !slicer.empty())) {
            return consume();
         } else {
            return std::nullopt;
         }
      }

      /**
       * Explicitly consume the currently collected block. It is the caller's
       * responsibility to ensure that the buffer is filled fully. After
       * consumption, the buffer is cleared and ready to collect new data.
       */
      [[nodiscard]] std::span<const T> consume() {
         BOTAN_ASSERT_NOMSG(ready_to_consume());
         m_position = 0;
         return m_buffer;
      }

      /**
       * Explicitly consumes however many bytes are currently stored in the
       * buffer. After consumption, the buffer is cleared and ready to collect
       * new data.
       */
      [[nodiscard]] std::span<const T> consume_partial() {
         const auto elements = elements_in_buffer();
         m_position = 0;
         return std::span(m_buffer).first(elements);
      }

      constexpr size_t size() const { return m_buffer.size(); }

      size_t elements_in_buffer() const { return m_position; }

      size_t elements_until_alignment() const { return m_buffer.size() - m_position; }

      /**
       * @returns true if the buffer is empty (i.e. contains no unaligned data)
       */
      bool in_alignment() const { return m_position == 0; }

      /**
       * @returns true if the buffer is full (i.e. a block is ready to be consumed)
       */
      bool ready_to_consume() const { return m_position == m_buffer.size(); }

      constexpr bool defers_final_block() const {
         return FINAL_BLOCK_STRATEGY == AlignmentBufferFinalBlock::must_be_deferred;
      }

   private:
      std::array<T, BLOCK_SIZE> m_buffer = {};
      size_t m_position = 0;
};

}  // namespace Botan

namespace Botan {

/**
* Barrett Reduction
*/
class BOTAN_TEST_API Barrett_Reduction final {
   public:
      /**
      * Setup for reduction where the modulus itself is public
      *
      * Requires that m > 0
      */
      static Barrett_Reduction for_public_modulus(const BigInt& m);

      /**
      * Setup for reduction where the modulus itself is secret.
      *
      * This is slower than for_public_modulus since it must avoid using
      * variable time division.
      *
      * Requires that m > 0
      */
      static Barrett_Reduction for_secret_modulus(const BigInt& m);

      /**
      * Perform modular reduction of x
      *
      * The parameter must be greater than or equal to zero, and less than 2^(2*b), where
      * b is the bitlength of the modulus.
      */
      BigInt reduce(const BigInt& x) const;

      /**
      * Multiply mod p
      * @param x the first operand in [0..p)
      * @param y the second operand in [0..p)
      * @return (x * y) % p
      */
      BigInt multiply(const BigInt& x, const BigInt& y) const;

      /**
      * Square mod p
      * @param x a value to square must be in [0..p)
      * @return (x * x) % p
      */
      BigInt square(const BigInt& x) const;

      /**
      * Cube mod p
      * @param x the value to cube
      * @return (x * x * x) % p
      *
      * TODO(Botan4) remove this, last few remaining callers go away in Botan4
      */
      BigInt cube(const BigInt& x) const { return this->multiply(x, this->square(x)); }

      /**
      * Return length of the modulus in bits
      */
      size_t modulus_bits() const { return m_modulus_bits; }

   private:
      Barrett_Reduction(const BigInt& m, BigInt mu, size_t mw);

      BigInt m_modulus;
      BigInt m_mu;
      size_t m_mod_words;
      size_t m_modulus_bits;
};

}  // namespace Botan



namespace Botan {

/**
 * Swap the byte order of an unsigned integer
 */
template <std::unsigned_integral T>
   requires(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)
inline constexpr T reverse_bytes(T x) {
   if constexpr(sizeof(T) == 1) {
      return x;
   } else if constexpr(sizeof(T) == 2) {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap16)
      return static_cast<T>(__builtin_bswap16(x));
#else
      return static_cast<T>((x << 8) | (x >> 8));
#endif
   } else if constexpr(sizeof(T) == 4) {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap32)
      return static_cast<T>(__builtin_bswap32(x));
#else
      // MSVC at least recognizes this as a bswap
      return static_cast<T>(((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) |
                            ((x & 0xFF000000) >> 24));
#endif
   } else if constexpr(sizeof(T) == 8) {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap64)
      return static_cast<T>(__builtin_bswap64(x));
#else
      uint32_t hi = static_cast<uint32_t>(x >> 32);
      uint32_t lo = static_cast<uint32_t>(x);

      hi = reverse_bytes(hi);
      lo = reverse_bytes(lo);

      return (static_cast<T>(lo) << 32) | hi;
#endif
   }
}

}  // namespace Botan

namespace Botan {

/**
* If top bit of arg is set, return ~0. Otherwise return 0.
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T expand_top_bit(T a) {
   return static_cast<T>(0) - (a >> (sizeof(T) * 8 - 1));
}

/**
* If arg is zero, return ~0. Otherwise return 0
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) {
   return expand_top_bit<T>(~x & (x - 1));
}

/**
* Power of 2 test. T should be an unsigned integer type
* @param arg an integer value
* @return true iff arg is 2^n for some n > 0
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr bool is_power_of_2(T arg) {
   return (arg != 0) && (arg != 1) && ((arg & static_cast<T>(arg - 1)) == 0);
}

/**
* Return the index of the highest set bit
* T is an unsigned integer type
* @param n an integer value
* @return index of the highest set bit in n
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr size_t high_bit(T n) {
   size_t hb = 0;

   for(size_t s = 8 * sizeof(T) / 2; s > 0; s /= 2) {
      const size_t z = s * ((~ct_is_zero<T>(n >> s)) & 1);
      hb += z;
      n >>= z;
   }

   hb += n;

   return hb;
}

/**
* Return the number of significant bytes in n
* @param n an integer value
* @return number of significant bytes in n
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr size_t significant_bytes(T n) {
   size_t b = 0;

   for(size_t s = 8 * sizeof(T) / 2; s >= 8; s /= 2) {
      const size_t z = s * (~ct_is_zero(n >> s) & 1);
      b += z / 8;
      n >>= z;
   }

   b += (n != 0);

   return b;
}

/**
* Count the trailing zero bits in n
* @param n an integer value
* @return maximum x st 2^x divides n
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr size_t ctz(T n) {
   /*
   * If n == 0 then this function will compute 8*sizeof(T)-1, so
   * initialize lb to 1 if n == 0 to produce the expected result.
   */
   size_t lb = ct_is_zero(n) & 1;

   for(size_t s = 8 * sizeof(T) / 2; s > 0; s /= 2) {
      const T mask = (static_cast<T>(1) << s) - 1;
      const size_t z = s * (ct_is_zero(n & mask) & 1);
      lb += z;
      n >>= z;
   }

   return lb;
}

template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T floor_log2(T n) {
   BOTAN_ARG_CHECK(n != 0, "log2(0) is not defined");
   return static_cast<T>(high_bit(n) - 1);
}

template <std::unsigned_integral T>
constexpr uint8_t ceil_log2(T x)
   requires(sizeof(T) < 32)
{
   if(x >> (sizeof(T) * 8 - 1)) {
      return sizeof(T) * 8;
   }

   uint8_t result = 0;
   T compare = 1;

   while(compare < x) {
      compare <<= 1;
      result++;
   }

   return result;
}

/**
 * Ceil of an unsigned integer division. @p b must not be zero.
 *
 * @param a divident
 * @param b divisor
 *
 * @returns ceil(a/b)
 */
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T ceil_division(T a, T b) {
   return (a + b - 1) / b;
}

/**
 * Return the number of bytes necessary to contain @p bits bits.
 */
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T ceil_tobytes(T bits) {
   return (bits + 7) / 8;
}

// Potentially variable time ctz used for OCB
BOTAN_FORCE_INLINE constexpr size_t var_ctz32(uint32_t n) {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_ctz)
   if(n == 0) {
      return 32;
   }
   return __builtin_ctz(n);
#else
   return ctz<uint32_t>(n);
#endif
}

template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T bit_permute_step(T x, T mask, size_t shift) {
   /*
   See https://reflectionsonsecurity.wordpress.com/2014/05/11/efficient-bit-permutation-using-delta-swaps/
   and http://programming.sirrida.de/bit_perm.html
   */
   const T swap = ((x >> shift) ^ x) & mask;
   return (x ^ swap) ^ (swap << shift);
}

template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr void swap_bits(T& x, T& y, T mask, size_t shift) {
   const T swap = ((x >> shift) ^ y) & mask;
   x ^= swap << shift;
   y ^= swap;
}

/**
* Bitwise selection
*
* If mask is |1| returns a
* If mask is |0| returns b
* If mask is some other value returns a or b depending on the bit
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b) {
   //return (mask & a) | (~mask & b);
   return (b ^ (mask & (a ^ b)));
}

template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T majority(T a, T b, T c) {
   /*
   Considering each bit of a, b, c individually

   If a xor b is set, then c is the deciding vote.

   If a xor b is not set then either a and b are both set or both unset.
   In either case the value of c doesn't matter, and examining b (or a)
   allows us to determine which case we are in.
   */
   return choose(a ^ b, c, b);
}

/**
 * @returns the reversed bits in @p b.
 */
template <std::unsigned_integral T>
inline constexpr T ct_reverse_bits(T b) {
   auto extend = [](uint8_t m) -> T {
      T mask = 0;
      for(size_t i = 0; i < sizeof(T); ++i) {
         mask |= T(m) << i * 8;
      }
      return mask;
   };

   // First reverse bits in each byte...
   // From: https://stackoverflow.com/a/2602885
   b = (b & extend(0xF0)) >> 4 | (b & extend(0x0F)) << 4;
   b = (b & extend(0xCC)) >> 2 | (b & extend(0x33)) << 2;
   b = (b & extend(0xAA)) >> 1 | (b & extend(0x55)) << 1;

   // ... then swap the bytes
   return reverse_bytes(b);
}

/**
 * Calculates the number of 1-bits in an unsigned integer in constant-time.
 * This operation is also known as "population count" or hamming weight.
 *
 * Modern compilers will recognize this pattern and replace it by a hardware
 * instruction, if available. This is the SWAR (SIMD within a register)
 * algorithm. See: https://nimrod.blog/posts/algorithms-behind-popcount/#swar-algorithm
 *
 * Note: C++20 provides std::popcount(), but there's no guarantee that this
 *       is implemented in constant-time.
 *
 * @param x an unsigned integer
 * @returns the number of 1-bits in the provided value
 */
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr uint8_t ct_popcount(T x) {
   constexpr size_t s = sizeof(T);
   static_assert(s <= 8, "T is not a suitable unsigned integer value");
   if constexpr(s == 8) {
      x = x - ((x >> 1) & 0x5555555555555555);
      x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
      x = (x + (x >> 4)) & 0xF0F0F0F0F0F0F0F;
      return (x * 0x101010101010101) >> 56;
   } else if constexpr(s == 4) {
      x = x - ((x >> 1) & 0x55555555);
      x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
      x = (x + (x >> 4)) & 0x0F0F0F0F;
      return (x * 0x01010101) >> 24;
   } else {
      // s < 4
      return ct_popcount(static_cast<uint32_t>(x));
   }
}

}  // namespace Botan

namespace Botan {

/**
* Struct representing a particular date and time
*/
class BOTAN_TEST_API calendar_point {
   public:
      /** The year */
      uint32_t year() const { return m_year; }

      /** The month, 1 through 12 for Jan to Dec */
      uint32_t month() const { return m_month; }

      /** The day of the month, 1 through 31 (or 28 or 30 based on month */
      uint32_t day() const { return m_day; }

      /** Hour in 24-hour form, 0 to 23 */
      uint32_t hour() const { return m_hour; }

      /** Minutes in the hour, 0 to 60 */
      uint32_t minutes() const { return m_minutes; }

      /** Seconds in the minute, 0 to 60, but might be slightly
      larger to deal with leap seconds on some systems
      */
      uint32_t seconds() const { return m_seconds; }

      /**
      * Initialize a calendar_point
      * @param y the year
      * @param mon the month
      * @param d the day
      * @param h the hour
      * @param min the minute
      * @param sec the second
      */
      calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec) :
            m_year(y), m_month(mon), m_day(d), m_hour(h), m_minutes(min), m_seconds(sec) {}

      /**
      * Convert a time_point to a calendar_point
      * @param time_point a time point from the system clock
      */
      explicit calendar_point(const std::chrono::system_clock::time_point& time_point);

      /**
      * Return seconds since epoch
      */
      uint64_t seconds_since_epoch() const;

      /**
      * Returns an STL timepoint object
      *
      * Note this throws an exception if the time is not representable
      * in the system time_t
      */
      std::chrono::system_clock::time_point to_std_timepoint() const;

      /**
      * Returns a human readable string of the struct's components.
      * Formatting might change over time. Currently it is RFC339 'iso-date-time'.
      */
      std::string to_string() const;

   private:
      uint32_t m_year;
      uint32_t m_month;
      uint32_t m_day;
      uint32_t m_hour;
      uint32_t m_minutes;
      uint32_t m_seconds;
};

}  // namespace Botan

namespace Botan {

/**
* Convert a sequence of UCS-2 (big endian) characters to a UTF-8 string
* This is used for ASN.1 BMPString type
* @param ucs2 the sequence of UCS-2 characters
* @param len length of ucs2 in bytes, must be a multiple of 2
*/
BOTAN_TEST_API std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len);

/**
* Convert a sequence of UCS-4 (big endian) characters to a UTF-8 string
* This is used for ASN.1 UniversalString type
* @param ucs4 the sequence of UCS-4 characters
* @param len length of ucs4 in bytes, must be a multiple of 4
*/
BOTAN_TEST_API std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len);

BOTAN_TEST_API std::string latin1_to_utf8(const uint8_t latin1[], size_t len);

/**
* Return a string containing 'c', quoted and possibly escaped
*
* This is used when creating an error message nothing an invalid character
* in some codex (for example during hex decoding)
*
* Currently this function escapes tab, newlines and carriage return
* as "\t", "\n", and "\r", and also escapes characters > 0x7F as
* "\xHH" where HH is the hex code.
*/
std::string format_char_for_display(char c);

}  // namespace Botan

namespace Botan {

/**
* Perform encoding using the base provided
* @param base object giving access to the encodings specifications
* @param output an array of at least base.encode_max_output bytes
* @param input is some binary data
* @param input_length length of input in bytes
* @param input_consumed is an output parameter which says how many
*        bytes of input were actually consumed. If less than
*        input_length, then the range input[consumed:length]
*        should be passed in later along with more input.
* @param final_inputs true iff this is the last input, in which case
         padding chars will be applied if needed
* @return number of bytes written to output
*/
template <class Base>
size_t base_encode(const Base& base,
                   char output[],
                   const uint8_t input[],
                   size_t input_length,
                   size_t& input_consumed,
                   bool final_inputs) {
   input_consumed = 0;

   // TODO(Botan4) Check if we can use just base. or Base:: here instead
   constexpr size_t encoding_bytes_in = std::remove_reference_t<Base>::encoding_bytes_in();
   constexpr size_t encoding_bytes_out = std::remove_reference_t<Base>::encoding_bytes_out();

   size_t input_remaining = input_length;
   size_t output_produced = 0;

   while(input_remaining >= encoding_bytes_in) {
      base.encode(output + output_produced, input + input_consumed);

      input_consumed += encoding_bytes_in;
      output_produced += encoding_bytes_out;
      input_remaining -= encoding_bytes_in;
   }

   if(final_inputs && input_remaining) {
      std::array<uint8_t, encoding_bytes_in> remainder{};
      for(size_t i = 0; i != input_remaining; ++i) {
         remainder[i] = input[input_consumed + i];
      }

      base.encode(output + output_produced, remainder.data());

      const size_t bits_consumed = base.bits_consumed();
      const size_t remaining_bits_before_padding = base.remaining_bits_before_padding();

      size_t empty_bits = 8 * (encoding_bytes_in - input_remaining);
      size_t index = output_produced + encoding_bytes_out - 1;
      while(empty_bits >= remaining_bits_before_padding) {
         output[index--] = '=';
         empty_bits -= bits_consumed;
      }

      input_consumed += input_remaining;
      output_produced += encoding_bytes_out;
   }

   return output_produced;
}

template <typename Base>
std::string base_encode_to_string(const Base& base, const uint8_t input[], size_t input_length) {
   const size_t output_length = base.encode_max_output(input_length);
   std::string output(output_length, 0);

   size_t consumed = 0;
   size_t produced = 0;

   if(output_length > 0) {
      produced = base_encode(base, &output.front(), input, input_length, consumed, true);
   }

   BOTAN_ASSERT_EQUAL(consumed, input_length, "Consumed the entire input");
   BOTAN_ASSERT_EQUAL(produced, output.size(), "Produced expected size");

   return output;
}

/**
* Perform decoding using the base provided
* @param base object giving access to the encodings specifications
* @param output an array of at least Base::decode_max_output bytes
* @param input some base input
* @param input_length length of input in bytes
* @param input_consumed is an output parameter which says how many
*        bytes of input were actually consumed. If less than
*        input_length, then the range input[consumed:length]
*        should be passed in later along with more input.
* @param final_inputs true iff this is the last input, in which case
         padding is allowed
* @param ignore_ws ignore whitespace on input; if false, throw an
                   exception if whitespace is encountered
* @return number of bytes written to output
*/
template <typename Base>
size_t base_decode(const Base& base,
                   uint8_t output[],
                   const char input[],
                   size_t input_length,
                   size_t& input_consumed,
                   bool final_inputs,
                   bool ignore_ws = true) {
   // TODO(Botan4) Check if we can use just base. or Base:: here instead
   constexpr size_t decoding_bytes_in = std::remove_reference_t<Base>::decoding_bytes_in();
   constexpr size_t decoding_bytes_out = std::remove_reference_t<Base>::decoding_bytes_out();

   uint8_t* out_ptr = output;
   std::array<uint8_t, decoding_bytes_in> decode_buf{};
   size_t decode_buf_pos = 0;
   size_t final_truncate = 0;

   clear_mem(output, base.decode_max_output(input_length));

   for(size_t i = 0; i != input_length; ++i) {
      const uint8_t bin = base.lookup_binary_value(input[i]);

      // This call might throw Invalid_Argument
      if(base.check_bad_char(bin, input[i], ignore_ws)) {
         decode_buf[decode_buf_pos] = bin;
         ++decode_buf_pos;
      }

      /*
      * If we're at the end of the input, pad with 0s and truncate
      */
      if(final_inputs && (i == input_length - 1)) {
         if(decode_buf_pos) {
            for(size_t j = decode_buf_pos; j < decoding_bytes_in; ++j) {
               decode_buf[j] = 0;
            }

            final_truncate = decoding_bytes_in - decode_buf_pos;
            decode_buf_pos = decoding_bytes_in;
         }
      }

      if(decode_buf_pos == decoding_bytes_in) {
         base.decode(out_ptr, decode_buf.data());

         out_ptr += decoding_bytes_out;
         decode_buf_pos = 0;
         input_consumed = i + 1;
      }
   }

   while(input_consumed < input_length && base.lookup_binary_value(input[input_consumed]) == 0x80) {
      ++input_consumed;
   }

   size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate);

   return written;
}

template <typename Base>
size_t base_decode_full(const Base& base, uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
   size_t consumed = 0;
   const size_t written = base_decode(base, output, input, input_length, consumed, true, ignore_ws);

   if(consumed != input_length) {
      throw Invalid_Argument(base.name() + " decoding failed, input did not have full bytes");
   }

   return written;
}

template <typename Vector, typename Base>
Vector base_decode_to_vec(const Base& base, const char input[], size_t input_length, bool ignore_ws) {
   const size_t output_length = base.decode_max_output(input_length);
   Vector bin(output_length);

   const size_t written = base_decode_full(base, bin.data(), input, input_length, ignore_ws);

   bin.resize(written);
   return bin;
}

}  // namespace Botan

/**
* @file  target_info.h
*
* Automatically generated from
* 'configure.py --cpu=generic --os=generic --amalgamation --minimized-build --disable-cc-tests --enable-modules=keccak,hmac,sha2_64,ed25519 --name-amalgamation=botan_generic --with-build-dir=/home/runner/work/native-utils/native-utils/cpp/botan_generated'
*
* Target
*  - Compiler: g++ -fstack-protector -pthread -std=c++20 -D_REENTRANT -O3
*  - Arch: generic
*  - OS: generic
*/

/* NOLINTBEGIN(*-macro-usage,*-macro-to-enum) */

/*
* Configuration
*/
#define BOTAN_CT_VALUE_BARRIER_USE_ASM

[[maybe_unused]] static constexpr bool OptimizeForSize = false;



/*
* Compiler Information
*/
#define BOTAN_BUILD_COMPILER_IS_GCC

#define BOTAN_COMPILER_INVOCATION_STRING "g++ -fstack-protector -pthread -O3"

#define BOTAN_USE_GCC_INLINE_ASM


/*
* External tool settings
*/




/*
* CPU feature information
*/
#define BOTAN_TARGET_ARCH "generic"

#define BOTAN_TARGET_ARCH_IS_GENERIC




/*
* Operating system information
*/
#define BOTAN_TARGET_OS_IS_GENERIC

#define BOTAN_TARGET_OS_HAS_ATOMICS
#define BOTAN_TARGET_OS_HAS_SYSTEM_CLOCK
#define BOTAN_TARGET_OS_HAS_THREAD_LOCAL


/*
* System paths
*/
#define BOTAN_INSTALL_PREFIX R"(/usr/local)"
#define BOTAN_INSTALL_HEADER_DIR R"(include/botan-3)"
#define BOTAN_INSTALL_LIB_DIR R"(/usr/local/lib)"
#define BOTAN_LIB_LINK ""
#define BOTAN_LINK_FLAGS "-fstack-protector -pthread"

#define BOTAN_SYSTEM_CERT_BUNDLE "/etc/ssl/certs/ca-certificates.crt"

/* NOLINTEND(*-macro-usage,*-macro-to-enum) */


#if defined(BOTAN_HAS_VALGRIND)
   #include <valgrind/memcheck.h>
#endif

namespace Botan::CT {

/// @name Constant Time Check Annotation Helpers
/// @{

/**
* Use valgrind to mark the contents of memory as being undefined.
* Valgrind will accept operations which manipulate undefined values,
* but will warn if an undefined value is used to decided a conditional
* jump or a load/store address. So if we poison all of our inputs we
* can confirm that the operations in question are truly const time
* when compiled by whatever compiler is in use.
*
* Even better, the VALGRIND_MAKE_MEM_* macros work even when the
* program is not run under valgrind (though with a few cycles of
* overhead, which is unfortunate in final binaries as these
* annotations tend to be used in fairly important loops).
*
* This approach was first used in ctgrind (https://github.com/agl/ctgrind)
* but calling the valgrind mecheck API directly works just as well and
* doesn't require a custom patched valgrind.
*/
template <typename T>
constexpr inline void poison(const T* p, size_t n) {
#if defined(BOTAN_HAS_VALGRIND)
   if(!std::is_constant_evaluated()) {
      VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T));
   }
#endif

   BOTAN_UNUSED(p, n);
}

template <typename T>
constexpr inline void unpoison(const T* p, size_t n) {
#if defined(BOTAN_HAS_VALGRIND)
   if(!std::is_constant_evaluated()) {
      VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
   }
#endif

   BOTAN_UNUSED(p, n);
}

/**
 * Checks whether CT::poison() and CT::unpoison() actually have an effect.
 *
 * If the build is not instrumented and/or not run using an analysis tool like
 * valgrind, the functions are no-ops and the return value is false.
 *
 * @returns true if CT::poison() and CT::unpoison() are effective
 */
inline bool poison_has_effect() {
#if defined(BOTAN_HAS_VALGRIND)
   return RUNNING_ON_VALGRIND;
#else
   return false;
#endif
}

/// @}

/// @name Constant Time Check Annotation Convenience overloads
/// @{

template <typename T>
concept custom_poisonable = requires(const T& v) { v._const_time_poison(); };
template <typename T>
concept custom_unpoisonable = requires(const T& v) { v._const_time_unpoison(); };

/**
 * Poison a single integral object
 */
template <std::integral T>
constexpr void poison(const T& p) {
   poison(&p, 1);
}

template <std::integral T>
constexpr void unpoison(const T& p) {
   unpoison(&p, 1);
}

/**
 * Poison a contiguous buffer of trivial objects (e.g. integers and such)
 */
template <ranges::spanable_range R>
   requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>> && (!custom_poisonable<R>)
constexpr void poison(const R& r) {
   std::span s{r};
   poison(s.data(), s.size());
}

template <ranges::spanable_range R>
   requires std::is_trivially_copyable_v<std::ranges::range_value_t<R>> && (!custom_unpoisonable<R>)
constexpr void unpoison(const R& r) {
   std::span s{r};
   unpoison(s.data(), s.size());
}

/**
 * Poison a class type that provides a public `_const_time_poison()` method
 * For instance: BigInt, CT::Mask<>, FrodoMatrix, ...
 */
template <custom_poisonable T>
constexpr void poison(const T& x) {
   x._const_time_poison();
}

template <custom_unpoisonable T>
constexpr void unpoison(const T& x) {
   x._const_time_unpoison();
}

/**
 * Poison an optional object if it has a value.
 */
template <typename T>
   requires requires(const T& v) { ::Botan::CT::poison(v); }
constexpr void poison(const std::optional<T>& x) {
   if(x.has_value()) {
      poison(x.value());
   }
}

template <typename T>
   requires requires(const T& v) { ::Botan::CT::unpoison(v); }
constexpr void unpoison(const std::optional<T>& x) {
   if(x.has_value()) {
      unpoison(x.value());
   }
}

/// @}

/// @name Higher-level Constant Time Check Annotation Helpers
/// @{

template <typename T>
concept poisonable = requires(const T& v) { ::Botan::CT::poison(v); };
template <typename T>
concept unpoisonable = requires(const T& v) { ::Botan::CT::unpoison(v); };

/**
 * Poison a range of objects by calling `poison` on each element.
 */
template <std::ranges::range R>
   requires poisonable<std::ranges::range_value_t<R>>
constexpr void poison_range(const R& r) {
   for(const auto& v : r) {
      poison(v);
   }
}

template <std::ranges::range R>
   requires unpoisonable<std::ranges::range_value_t<R>>
constexpr void unpoison_range(const R& r) {
   for(const auto& v : r) {
      unpoison(v);
   }
}

/**
 * Poisons an arbitrary number of values in a single call.
 * Mostly syntactic sugar to save clutter (i.e. lines-of-code).
 */
template <poisonable... Ts>
   requires(sizeof...(Ts) > 0)
constexpr void poison_all(const Ts&... ts) {
   (poison(ts), ...);
}

template <unpoisonable... Ts>
   requires(sizeof...(Ts) > 0)
constexpr void unpoison_all(const Ts&... ts) {
   (unpoison(ts), ...);
}

/**
 * Poisons an arbitrary number of poisonable values, and unpoisons them when the
 * returned object runs out-of-scope
 *
 * Use this when you want to poison a value that remains valid longer than the
 * scope you are currently in. For instance, a private key structure that is a
 * member of a Signature_Operation object, that may be used for multiple
 * signatures.
 */
template <typename... Ts>
   requires(sizeof...(Ts) > 0) && (poisonable<Ts> && ...) && (unpoisonable<Ts> && ...)
[[nodiscard]] constexpr auto scoped_poison(const Ts&... xs) {
   auto scope = scoped_cleanup([&] { unpoison_all(xs...); });
   poison_all(xs...);
   return scope;
}

/**
 * Poisons an r-value @p v and forwards it as the return value.
 */
template <poisonable T>
[[nodiscard]] decltype(auto) driveby_poison(T&& v)
   requires(std::is_rvalue_reference_v<decltype(v)>)
{
   poison(v);
   return std::forward<T>(v);
}

/**
 * Unpoisons an r-value @p v and forwards it as the return value.
 */
template <unpoisonable T>
[[nodiscard]] decltype(auto) driveby_unpoison(T&& v)
   requires(std::is_rvalue_reference_v<decltype(v)>)
{
   unpoison(v);
   return std::forward<T>(v);
}

/// @}

/**
* This function returns its argument, but (if called in a non-constexpr context)
* attempts to prevent the compiler from reasoning about the value or the possible
* range of values. Such optimizations have a way of breaking constant time code.
*
* The method that is use is decided at configuration time based on the target
* compiler and architecture (see `ct_value_barrier` blocks in `src/build-data/cc`).
* The decision can be overridden by the user with the configure.py option
* `--ct-value-barrier-type=`
*
* There are three options currently possible in the data files and with the
* option:
*
*  * `asm`: Use an inline assembly expression which (currently) prevents Clang
*    and GCC from optimizing based on the possible value of the input expression.
*
*  * `volatile`: Launder the input through a volatile variable. This is likely
*    to cause significant performance regressions since the value must be
*    actually stored and loaded back from memory each time.
*
*  * `none`: disable constant time barriers entirely. This is used
*    with MSVC, which is not known to perform optimizations that break
*    constant time code and which does not support GCC-style inline asm.
*
*/
template <std::unsigned_integral T>
   requires(!std::same_as<bool, T>)
constexpr inline T value_barrier(T x) {
   if(std::is_constant_evaluated()) {
      return x;
   } else {
#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
      /*
      * We may want a "stronger" statement such as
      *     asm volatile("" : "+r,m"(x) : : "memory);
      * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
      * however the current approach seems sufficient with current compilers,
      * and is minimally damaging with regards to degrading code generation.
      */
      asm("" : "+r"(x) : /* no input */);
      return x;
#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
      volatile T vx = x;
      return vx;
#else
      return x;
#endif
   }
}

/**
* A Choice is used for constant-time conditionals.
*
* Internally it always is either |0| (all 0 bits) or |1| (all 1 bits)
* and measures are taken to block compilers from reasoning about the
* expected value of a Choice.
*/
class Choice final {
   public:
      /**
      * If v == 0 return an unset (false) Choice, otherwise a set Choice
      */
      template <typename T>
         requires std::unsigned_integral<T> && (!std::same_as<bool, T>)
      constexpr static Choice from_int(T v) {
         // Mask of T that is either |0| or |1|
         const T v_is_0 = ct_is_zero<T>(value_barrier<T>(v));

         // We want the mask to be set if v != 0 so we must check that
         // v_is_0 is itself zero.
         //
         // Also sizeof(T) may not equal sizeof(uint32_t) so we must
         // use ct_is_zero<uint32_t>. It's ok to either truncate or
         // zero extend v_is_0 to 32 bits since we know it is |0| or |1|
         // so even just the low bit is sufficient.
         return Choice(ct_is_zero<uint32_t>(static_cast<uint32_t>(v_is_0)));
      }

      /**
      * Create a Choice directly from a mask value - this assumes v is either |0| or |1|
      */
      constexpr static Choice from_mask(uint32_t v) { return Choice(v); }

      constexpr static Choice yes() { return Choice(static_cast<uint32_t>(-1)); }

      constexpr static Choice no() { return Choice(0); }

      constexpr Choice operator!() const { return Choice(~value()); }

      constexpr Choice operator&&(const Choice& other) const { return Choice(value() & other.value()); }

      constexpr Choice operator||(const Choice& other) const { return Choice(value() | other.value()); }

      constexpr Choice operator!=(const Choice& other) const { return Choice(value() ^ other.value()); }

      constexpr Choice operator==(const Choice& other) const { return !(*this != other); }

      /**
      * Unsafe conversion to bool
      *
      * This conversion itself is (probably) constant time, but once the
      * choice is reduced to a simple bool, it's entirely possible for the
      * compiler to perform range analysis on the values, since there are just
      * the two. As a consequence even if the caller is not using this in an
      * obviously branchy way (`if(choice.as_bool()) ...`) a smart compiler
      * may introduce branches depending on the value.
      */
      constexpr bool as_bool() const { return m_value != 0; }

      /// Return the masked value
      constexpr uint32_t value() const { return value_barrier(m_value); }

      constexpr Choice(const Choice& other) = default;
      constexpr Choice(Choice&& other) = default;
      constexpr Choice& operator=(const Choice& other) noexcept = default;
      constexpr Choice& operator=(Choice&& other) noexcept = default;
      constexpr ~Choice() = default;

   private:
      constexpr explicit Choice(uint32_t v) : m_value(v) {}

      uint32_t m_value;
};

/**
* A concept for a type which is conditionally assignable
*/
template <typename T>
concept ct_conditional_assignable = requires(T lhs, const T& rhs, Choice c) { lhs.conditional_assign(c, rhs); };

/**
* A Mask type used for constant-time operations. A Mask<T> always has value
* either |0| (all bits cleared) or |1| (all bits set). All operations in a Mask<T>
* are intended to compile to code which does not contain conditional jumps.
* This must be verified with tooling (eg binary disassembly or using valgrind)
* since you never know what a compiler might do.
*/
template <typename T>
class Mask final {
   public:
      static_assert(std::is_unsigned_v<T> && !std::is_same_v<bool, T>,
                    "Only unsigned integer types are supported by CT::Mask");

      Mask(const Mask<T>& other) = default;
      Mask(Mask<T>&& other) = default;
      Mask<T>& operator=(const Mask<T>& other) = default;
      Mask<T>& operator=(Mask<T>&& other) = default;
      ~Mask() = default;

      /**
      * Derive a Mask from a Mask of a larger type
      */
      template <typename U>
      constexpr explicit Mask(Mask<U> o) : m_mask(static_cast<T>(o.value())) {
         static_assert(sizeof(U) > sizeof(T), "sizes ok");
      }

      /**
      * Return a Mask<T> of |1| (all bits set)
      */
      static constexpr Mask<T> set() { return Mask<T>(static_cast<T>(~0)); }

      /**
      * Return a Mask<T> of |0| (all bits cleared)
      */
      static constexpr Mask<T> cleared() { return Mask<T>(0); }

      /**
      * Return a Mask<T> which is set if v is != 0
      */
      static constexpr Mask<T> expand(T v) { return ~Mask<T>::is_zero(value_barrier<T>(v)); }

      /**
      * Return a Mask<T> which is set if v is true
      */
      static constexpr Mask<T> expand_bool(bool v) { return Mask<T>::expand(static_cast<T>(v)); }

      /**
      * Return a Mask<T> which is set if choice is set
      */
      static constexpr Mask<T> from_choice(Choice c) {
         if constexpr(sizeof(T) <= sizeof(uint32_t)) {
            // Take advantage of the fact that Choice's mask is always
            // either |0| or |1|
            return Mask<T>(static_cast<T>(c.value()));
         } else {
            return ~Mask<T>::is_zero(c.value());
         }
      }

      /**
      * Return a Mask<T> which is set if the top bit of v is set
      */
      static constexpr Mask<T> expand_top_bit(T v) { return Mask<T>(Botan::expand_top_bit<T>(value_barrier<T>(v))); }

      /**
       * Return a Mask<T> which is set if the given @p bit of @p v is set.
       * @p bit must be from 0 (LSB) to (sizeof(T) * 8 - 1) (MSB).
       */
      static constexpr Mask<T> expand_bit(T v, size_t bit) {
         return CT::Mask<T>::expand_top_bit(v << (sizeof(v) * 8 - 1 - bit));
      }

      /**
      * Return a Mask<T> which is set if m is set
      */
      template <typename U>
      static constexpr Mask<T> expand(Mask<U> m) {
         static_assert(sizeof(U) < sizeof(T), "sizes ok");
         return ~Mask<T>::is_zero(m.value());
      }

      /**
      * Return a Mask<T> which is set if v is == 0 or cleared otherwise
      */
      static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(value_barrier<T>(x))); }

      /**
      * Return a Mask<T> which is set if x == y
      */
      static constexpr Mask<T> is_equal(T x, T y) {
         const T diff = value_barrier(x) ^ value_barrier(y);
         return Mask<T>::is_zero(diff);
      }

      /**
      * Return a Mask<T> which is set if x < y
      */
      static constexpr Mask<T> is_lt(T x, T y) {
         T u = x ^ ((x ^ y) | ((x - y) ^ x));
         return Mask<T>::expand_top_bit(u);
      }

      /**
      * Return a Mask<T> which is set if x > y
      */
      static constexpr Mask<T> is_gt(T x, T y) { return Mask<T>::is_lt(y, x); }

      /**
      * Return a Mask<T> which is set if x <= y
      */
      static constexpr Mask<T> is_lte(T x, T y) { return ~Mask<T>::is_gt(x, y); }

      /**
      * Return a Mask<T> which is set if x >= y
      */
      static constexpr Mask<T> is_gte(T x, T y) { return ~Mask<T>::is_lt(x, y); }

      static constexpr Mask<T> is_within_range(T v, T l, T u) {
         //return Mask<T>::is_gte(v, l) & Mask<T>::is_lte(v, u);

         const T v_lt_l = v ^ ((v ^ l) | ((v - l) ^ v));
         const T v_gt_u = u ^ ((u ^ v) | ((u - v) ^ u));
         const T either = value_barrier(v_lt_l) | value_barrier(v_gt_u);
         return ~Mask<T>::expand_top_bit(either);
      }

      static constexpr Mask<T> is_any_of(T v, std::initializer_list<T> accepted) {
         T accept = 0;

         for(auto a : accepted) {
            const T diff = a ^ v;
            const T eq_zero = value_barrier<T>(~diff & (diff - 1));
            accept |= eq_zero;
         }

         return Mask<T>::expand_top_bit(accept);
      }

      /**
      * AND-combine two masks
      */
      Mask<T>& operator&=(Mask<T> o) {
         m_mask &= o.value();
         return (*this);
      }

      /**
      * XOR-combine two masks
      */
      Mask<T>& operator^=(Mask<T> o) {
         m_mask ^= o.value();
         return (*this);
      }

      /**
      * OR-combine two masks
      */
      Mask<T>& operator|=(Mask<T> o) {
         m_mask |= o.value();
         return (*this);
      }

      /**
      * AND-combine two masks
      */
      friend Mask<T> operator&(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() & y.value()); }

      /**
      * XOR-combine two masks
      */
      friend Mask<T> operator^(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() ^ y.value()); }

      /**
      * OR-combine two masks
      */
      friend Mask<T> operator|(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() | y.value()); }

      /**
      * Negate this mask
      */
      constexpr Mask<T> operator~() const { return Mask<T>(~value()); }

      /**
      * Return x if the mask is set, or otherwise zero
      */
      constexpr T if_set_return(T x) const { return value() & x; }

      /**
      * Return x if the mask is cleared, or otherwise zero
      */
      constexpr T if_not_set_return(T x) const { return ~value() & x; }

      /**
      * If this mask is set, return x, otherwise return y
      */
      constexpr T select(T x, T y) const { return choose(value(), x, y); }

      constexpr T select_and_unpoison(T x, T y) const {
         T r = this->select(x, y);
         CT::unpoison(r);
         return r;
      }

      /**
      * If this mask is set, return x, otherwise return y
      */
      Mask<T> select_mask(Mask<T> x, Mask<T> y) const { return Mask<T>(select(x.value(), y.value())); }

      /**
      * Conditionally set output to x or y, depending on if mask is set or
      * cleared (resp)
      */
      constexpr void select_n(T output[], const T x[], const T y[], size_t len) const {
         const T mask = value();
         for(size_t i = 0; i != len; ++i) {
            output[i] = choose(mask, x[i], y[i]);
         }
      }

      /**
      * If this mask is set, zero out buf, otherwise do nothing
      */
      constexpr void if_set_zero_out(T buf[], size_t elems) {
         for(size_t i = 0; i != elems; ++i) {
            buf[i] = this->if_not_set_return(buf[i]);
         }
      }

      /**
     * If this mask is set, swap x and y
     */
      template <typename U>
      void conditional_swap(U& x, U& y) const
         requires(sizeof(U) <= sizeof(T))
      {
         auto cnd = Mask<U>(*this);
         U t0 = cnd.select(y, x);
         U t1 = cnd.select(x, y);
         x = t0;
         y = t1;
      }

      /**
      * Return the value of the mask, unpoisoned
      */
      constexpr T unpoisoned_value() const {
         T r = value();
         CT::unpoison(r);
         return r;
      }

      /**
      * Unsafe conversion to bool
      *
      * This conversion itself is (probably) constant time, but once the
      * mask is reduced to a simple bool, it's entirely possible for the
      * compiler to perform range analysis on the values, since there are just
      * the two. As a consequence even if the caller is not using this in an
      * obviously branchy way (`if(mask.as_bool()) ...`) a smart compiler
      * may introduce branches depending on the value.
      */
      constexpr bool as_bool() const { return unpoisoned_value() != 0; }

      /**
      * Return a Choice based on this mask
      */
      constexpr CT::Choice as_choice() const {
         if constexpr(sizeof(T) >= sizeof(uint32_t)) {
            return CT::Choice::from_mask(static_cast<uint32_t>(unpoisoned_value()));
         } else {
            return CT::Choice::from_int(unpoisoned_value());
         }
      }

      /**
      * Return the underlying value of the mask
      */
      constexpr T value() const { return value_barrier<T>(m_mask); }

      constexpr void _const_time_poison() const { CT::poison(m_mask); }

      constexpr void _const_time_unpoison() const { CT::unpoison(m_mask); }

   private:
      constexpr explicit Mask(T m) : m_mask(m) {}

      T m_mask;
};

/**
* A CT::Option<T> is either a valid T, or not
*
* To maintain constant time behavior a value must always be stored.
* A CT::Choice tracks if the value is valid or not. It is not possible
* to access the inner value if the Choice is unset.
*/
template <typename T>
class Option final {
   public:
      /// Construct an Option which contains the specified value, and is set or not
      constexpr Option(T v, Choice valid) : m_has_value(valid), m_value(std::move(v)) {}

      /// Construct a set option with the provided value
      constexpr explicit Option(T v) : Option(std::move(v), Choice::yes()) {}

      /// Construct an unset option with a default inner value
      constexpr Option()
         requires std::default_initializable<T>
            : Option(T(), Choice::no()) {}

      /// Return true if this Option contains a value
      constexpr Choice has_value() const { return m_has_value; }

      /**
      * Apply a function to the inner value and return a new Option
      * which contains that value. This is constant time only if @p f is.
      *
      * @note The function will always be called, even if the Option is None. It
      *       must be prepared to handle any possible state of T.
      */
      template <std::invocable<const T&> F>
      constexpr auto transform(F f) const -> Option<std::remove_cvref_t<std::invoke_result_t<F, const T&>>> {
         return {f(m_value), m_has_value};
      }

      /// Either returns the value or throws an exception
      constexpr const T& value() const {
         BOTAN_STATE_CHECK(m_has_value.as_bool());
         return m_value;
      }

      /// Returns either the inner value or the alternative, in constant time
      ///
      /// This variant is used for types which explicitly define a function
      /// conditional_assign which takes a CT::Choice as the conditional.
      constexpr T value_or(T other) const
         requires ct_conditional_assignable<T>
      {
         other.conditional_assign(m_has_value, m_value);
         return other;
      }

      /// Returns either the inner value or the alternative, in constant time
      ///
      /// This variant is used for integer types where CT::Mask can perform
      /// a constant time selection
      constexpr T value_or(T other) const
         requires std::unsigned_integral<T>
      {
         auto mask = CT::Mask<T>::from_choice(m_has_value);
         return mask.select(m_value, other);
      }

      /// Convert this Option into a std::optional
      ///
      /// This is not constant time, leaking if the Option had a
      /// value or not
      constexpr std::optional<T> as_optional_vartime() const {
         if(m_has_value.as_bool()) {
            return {m_value};
         } else {
            return {};
         }
      }

      /// Return a new CT::Option that is set if @p also is set as well
      constexpr CT::Option<T> operator&&(CT::Choice also) { return CT::Option<T>(m_value, m_has_value && also); }

   private:
      Choice m_has_value;
      T m_value;
};

/**
* Conditional memory copy (constant time)
*
* If mask is set, then sets dest to if_set, otherwise sets dest to if_unset
*/
template <typename T>
constexpr inline Mask<T> conditional_copy_mem(Mask<T> mask, T* dest, const T* if_set, const T* if_unset, size_t elems) {
   mask.select_n(dest, if_set, if_unset, elems);
   return mask;
}

template <typename T>
constexpr inline Mask<T> conditional_copy_mem(T cnd, T* dest, const T* if_set, const T* if_unset, size_t elems) {
   const auto mask = CT::Mask<T>::expand(cnd);
   return CT::conditional_copy_mem(mask, dest, if_set, if_unset, elems);
}

/**
* Conditional memory assignment (constant time)
*
* If mask is set overwrites dest with src
*/
template <typename T>
constexpr inline Mask<T> conditional_assign_mem(T cnd, T* dest, const T* src, size_t elems) {
   const auto mask = CT::Mask<T>::expand(cnd);
   mask.select_n(dest, src, dest, elems);
   return mask;
}

/**
* Conditional memory assignment (constant time)
*
* If mask is set overwrites dest with src
*/
template <typename T>
constexpr inline Mask<T> conditional_assign_mem(Choice cnd, T* dest, const T* src, size_t elems) {
   const auto mask = CT::Mask<T>::from_choice(cnd);
   mask.select_n(dest, src, dest, elems);
   return mask;
}

template <typename T>
constexpr inline void conditional_swap(bool cnd, T& x, T& y) {
   const auto swap = CT::Mask<T>::expand(cnd);
   swap.conditional_swap(x, y);
}

template <typename T>
constexpr inline void conditional_swap_ptr(bool cnd, T& x, T& y) {
   uintptr_t xp = reinterpret_cast<uintptr_t>(x);
   uintptr_t yp = reinterpret_cast<uintptr_t>(y);

   conditional_swap<uintptr_t>(cnd, xp, yp);

   x = reinterpret_cast<T>(xp);  // NOLINT(*-no-int-to-ptr)
   y = reinterpret_cast<T>(yp);  // NOLINT(*-no-int-to-ptr)
}

template <typename T>
constexpr inline CT::Mask<T> all_zeros(const T elem[], size_t len) {
   T sum = 0;
   for(size_t i = 0; i != len; ++i) {
      sum |= elem[i];
   }
   return CT::Mask<T>::is_zero(sum);
}

/**
* Compare two arrays of equal size and return a Mask indicating if
* they are equal or not. The mask is set if they are identical.
*/
template <typename T>
constexpr inline CT::Mask<T> is_equal(const T x[], const T y[], size_t len) {
   if(std::is_constant_evaluated()) {
      T difference = 0;

      for(size_t i = 0; i != len; ++i) {
         difference = difference | (x[i] ^ y[i]);
      }

      return CT::Mask<T>::is_zero(difference);
   } else {
      volatile T difference = 0;

      for(size_t i = 0; i != len; ++i) {
         difference = difference | (x[i] ^ y[i]);
      }

      return CT::Mask<T>::is_zero(difference);
   }
}

/**
* Compare two spans and return a Mask which is set iff they were identical.
*
* If the spans are of different length then the function returns early without
* looking at either span
*/
template <typename T>
constexpr inline CT::Mask<T> is_equal(std::span<const T> x, std::span<const T> y) {
   if(x.size() != y.size()) {
      return CT::Mask<T>::cleared();
   }

   return is_equal(x.data(), y.data(), x.size());
}

/**
* Compare two arrays of equal size and return a Mask indicating if
* they are equal or not. The mask is set if they differ.
*/
template <typename T>
constexpr inline CT::Mask<T> is_not_equal(const T x[], const T y[], size_t len) {
   return ~CT::is_equal(x, y, len);
}

/**
* Constant time conditional copy out with offset
*
* If accept is set and offset <= input_length, sets output[0..] to
* input[offset:input_length] and returns input_length - offset. The
* remaining bytes of output are zeroized.
*
* Otherwise, output is zeroized, and returns an empty Ct::Option
*
* The input and output spans may not overlap, and output must be at
* least as large as input.
*
* This function attempts to avoid leaking the following to side channels
*  - if accept was set or not
*  - the value of offset
*  - the value of input
*
* This function leaks the length of the input
*/
BOTAN_TEST_API
CT::Option<size_t> copy_output(CT::Choice accept,
                               std::span<uint8_t> output,
                               std::span<const uint8_t> input,
                               size_t offset);

size_t count_leading_zero_bytes(std::span<const uint8_t> input);

secure_vector<uint8_t> strip_leading_zeros(std::span<const uint8_t> input);

}  // namespace Botan::CT

namespace Botan {

/**
* BigInt Division
* @param x an integer
* @param y a non-zero integer
* @param q will be set to x / y
* @param r will be set to x % y
*/
BOTAN_TEST_API
void vartime_divide(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r);

/**
* BigInt division, const time variant
*
* This runs with control flow independent of the values of x/y.
* Warning: the loop bounds still leak the sizes of x and y.
*
* @param x an integer
* @param y a non-zero integer
* @param q will be set to x / y
* @param r will be set to x % y
*/
BOTAN_TEST_API
void ct_divide(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r);

/**
* BigInt division, const time variant, 2^k variant
*
* This runs with control flow independent of the value of y.
* This function leaks the value of k and the length of y.
* If k < bits(y) this returns zero
*
* @param k an integer
* @param y a positive integer
* @return q equal to 2**k / y
*/
BOTAN_TEST_API
BigInt ct_divide_pow2k(size_t k, const BigInt& y);

/**
* BigInt division, const time variant
*
* This runs with control flow independent of the values of x/y.
* Warning: the loop bounds still leak the sizes of x and y.
*
* @param x an integer
* @param y a non-zero integer
* @return x/y with remainder discarded
*/
inline BigInt ct_divide(const BigInt& x, const BigInt& y) {
   BigInt q;
   BigInt r;
   ct_divide(x, y, q, r);
   return q;
}

/**
* Constant time division
*
* This runs with control flow independent of the values of x/y.
* Warning: the loop bounds still leaks the size of x.
*
* @param x an integer
* @param y a non-zero integer
* @param q will be set to x / y
* @param r will be set to x % y
*/
BOTAN_TEST_API
void ct_divide_word(const BigInt& x, word y, BigInt& q, word& r);

/**
* Constant time division
*
* This runs with control flow independent of the values of x/y.
* Warning: the loop bounds still leaks the size of x.
*
* @param x an integer
* @param y a non-zero word
* @return quotient floor(x / y)
*/
BigInt ct_divide_word(const BigInt& x, word y);

/**
* BigInt word modulo, const time variant
*
* This runs with control flow independent of the values of x/y.
* Warning: the loop bounds still leaks the size of x.
*
* @param x a positive integer
* @param y a non-zero word
* @return r the remainder of x divided by y
*/
BOTAN_TEST_API
word ct_mod_word(const BigInt& x, word y);

/**
* BigInt modulo, const time variant
*
* Using this function is (slightly) cheaper than calling ct_divide and
* using only the remainder.
*
* @param x a non-negative integer
* @param modulo a positive integer
* @return result x % modulo
*/
BOTAN_TEST_API
BigInt ct_modulo(const BigInt& x, const BigInt& modulo);

}  // namespace Botan

#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
   #include <intrin.h>
#endif

namespace Botan {

/**
* Perform a 64x64->128 bit multiplication
*/
constexpr inline void mul64x64_128(uint64_t a, uint64_t b, uint64_t* lo, uint64_t* hi) {
   if(!std::is_constant_evaluated()) {
#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_ARCH_IS_X86_64)
      *lo = _umul128(a, b, hi);
      return;

#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
      *lo = a * b;
      *hi = __umulh(a, b);
      return;
#endif
   }

#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
   const uint128_t r = static_cast<uint128_t>(a) * b;
   *hi = (r >> 64) & 0xFFFFFFFFFFFFFFFF;
   *lo = (r) & 0xFFFFFFFFFFFFFFFF;
#else

   /*
   * Do a 64x64->128 multiply using four 32x32->64 multiplies plus
   * some adds and shifts.
   */
   const size_t HWORD_BITS = 32;
   const uint32_t HWORD_MASK = 0xFFFFFFFF;

   const uint32_t a_hi = (a >> HWORD_BITS);
   const uint32_t a_lo = (a & HWORD_MASK);
   const uint32_t b_hi = (b >> HWORD_BITS);
   const uint32_t b_lo = (b & HWORD_MASK);

   const uint64_t x0 = static_cast<uint64_t>(a_hi) * b_hi;
   const uint64_t x1 = static_cast<uint64_t>(a_lo) * b_hi;
   const uint64_t x2 = static_cast<uint64_t>(a_hi) * b_lo;
   const uint64_t x3 = static_cast<uint64_t>(a_lo) * b_lo;

   // this cannot overflow as (2^32-1)^2 + 2^32-1 + 2^32-1 = 2^64-1
   const uint64_t middle = x2 + (x3 >> HWORD_BITS) + (x1 & HWORD_MASK);

   // likewise these cannot overflow
   *hi = x0 + (middle >> HWORD_BITS) + (x1 >> HWORD_BITS);
   *lo = (middle << HWORD_BITS) + (x3 & HWORD_MASK);
#endif
}

}  // namespace Botan

namespace Botan {

class donna128 final {
   public:
      constexpr explicit donna128(uint64_t l = 0, uint64_t h = 0) : m_lo(l), m_hi(h) {}

      template <std::unsigned_integral T>
      constexpr friend donna128 operator>>(const donna128& x, T shift) {
         donna128 z = x;

         if(shift > 64) {
            z.m_lo = z.m_hi >> (shift - 64);
            z.m_hi = 0;
         } else if(shift == 64) {
            z.m_lo = z.m_hi;
            z.m_hi = 0;
         } else if(shift > 0) {
            const uint64_t carry = z.m_hi << static_cast<size_t>(64 - shift);
            z.m_hi >>= shift;
            z.m_lo >>= shift;
            z.m_lo |= carry;
         }

         return z;
      }

      template <std::unsigned_integral T>
      constexpr friend donna128 operator<<(const donna128& x, T shift) {
         donna128 z = x;
         if(shift > 64) {
            z.m_hi = z.m_lo << (shift - 64);
            z.m_lo = 0;
         } else if(shift == 64) {
            z.m_hi = z.m_lo;
            z.m_lo = 0;
         } else if(shift > 0) {
            const uint64_t carry = z.m_lo >> static_cast<size_t>(64 - shift);
            z.m_lo = (z.m_lo << shift);
            z.m_hi = (z.m_hi << shift) | carry;
         }

         return z;
      }

      constexpr friend uint64_t operator&(const donna128& x, uint64_t mask) { return x.m_lo & mask; }

      constexpr uint64_t operator&=(uint64_t mask) {
         m_hi = 0;
         m_lo &= mask;
         return m_lo;
      }

      constexpr donna128& operator+=(const donna128& x) {
         m_lo += x.m_lo;
         m_hi += x.m_hi;

         const uint64_t carry = CT::Mask<uint64_t>::is_lt(m_lo, x.m_lo).if_set_return(1);
         m_hi += carry;
         return *this;
      }

      constexpr donna128& operator+=(uint64_t x) {
         m_lo += x;
         const uint64_t carry = CT::Mask<uint64_t>::is_lt(m_lo, x).if_set_return(1);
         m_hi += carry;
         return *this;
      }

      constexpr uint64_t lo() const { return m_lo; }

      constexpr uint64_t hi() const { return m_hi; }

      constexpr explicit operator uint64_t() const { return lo(); }

   private:
      uint64_t m_lo = 0;
      uint64_t m_hi = 0;
};

template <std::integral T>
constexpr inline donna128 operator*(const donna128& x, T y) {
   BOTAN_ARG_CHECK(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply");

   uint64_t lo = 0;
   uint64_t hi = 0;
   mul64x64_128(x.lo(), static_cast<uint64_t>(y), &lo, &hi);
   return donna128(lo, hi);
}

template <std::integral T>
constexpr inline donna128 operator*(T y, const donna128& x) {
   return x * y;
}

constexpr inline donna128 operator+(const donna128& x, const donna128& y) {
   donna128 z = x;
   z += y;
   return z;
}

constexpr inline donna128 operator+(const donna128& x, uint64_t y) {
   donna128 z = x;
   z += y;
   return z;
}

constexpr inline donna128 operator|(const donna128& x, const donna128& y) {
   return donna128(x.lo() | y.lo(), x.hi() | y.hi());
}

constexpr inline donna128 operator|(const donna128& x, uint64_t y) {
   return donna128(x.lo() | y, x.hi());
}

constexpr inline uint64_t carry_shift(const donna128& a, size_t shift) {
   return (a >> shift).lo();
}

constexpr inline uint64_t combine_lower(const donna128& a, size_t s1, const donna128& b, size_t s2) {
   donna128 z = (a >> s1) | (b << s2);
   return z.lo();
}

#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
inline uint64_t carry_shift(const uint128_t a, size_t shift) {
   return static_cast<uint64_t>(a >> shift);
}

inline uint64_t combine_lower(const uint128_t a, size_t s1, const uint128_t b, size_t s2) {
   return static_cast<uint64_t>((a >> s1) | (b << s2));
}
#endif

}  // namespace Botan

namespace Botan {

/**
* An element of the field \\Z/(2^255-19)
*
* An element t, entries t[0]...t[9], represents the integer
* t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
* Bounds on each t[i] vary depending on context.
*/
class Ed25519_FieldElement final {
   public:
      /**
      * Default zero initialization
      */
      constexpr Ed25519_FieldElement() : m_fe{} {}

      constexpr static Ed25519_FieldElement zero() { return Ed25519_FieldElement(); }

      constexpr static Ed25519_FieldElement one() {
         auto o = Ed25519_FieldElement();
         o.m_fe[0] = 1;
         return o;
      }

      // NOLINTNEXTLINE(*-member-init)
      constexpr explicit Ed25519_FieldElement(std::span<int32_t, 10> fe) { copy_mem(m_fe.data(), fe.data(), 10); }

      // NOLINTNEXTLINE(*-member-init)
      constexpr Ed25519_FieldElement(int64_t h0,
                                     int64_t h1,
                                     int64_t h2,
                                     int64_t h3,
                                     int64_t h4,
                                     int64_t h5,
                                     int64_t h6,
                                     int64_t h7,
                                     int64_t h8,
                                     int64_t h9) {
         m_fe[0] = static_cast<int32_t>(h0);
         m_fe[1] = static_cast<int32_t>(h1);
         m_fe[2] = static_cast<int32_t>(h2);
         m_fe[3] = static_cast<int32_t>(h3);
         m_fe[4] = static_cast<int32_t>(h4);
         m_fe[5] = static_cast<int32_t>(h5);
         m_fe[6] = static_cast<int32_t>(h6);
         m_fe[7] = static_cast<int32_t>(h7);
         m_fe[8] = static_cast<int32_t>(h8);
         m_fe[9] = static_cast<int32_t>(h9);
      }

      static Ed25519_FieldElement deserialize(const uint8_t b[32]);

      void serialize_to(std::span<uint8_t, 32> b) const;

      bool is_zero() const {
         std::array<uint8_t, 32> value = {};
         this->serialize_to(value);
         return CT::all_zeros(value.data(), value.size()).as_bool();
      }

      /*
      return 1 if f is in {1,3,5,...,q-2}
      return 0 if f is in {0,2,4,...,q-1}
      */
      bool is_negative() const {
         // TODO could avoid most of the serialize computation here
         std::array<uint8_t, 32> s = {};
         this->serialize_to(s);
         return (s[0] & 0x01) == 0x01;
      }

      static Ed25519_FieldElement add(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b) {
         Ed25519_FieldElement z;
         for(size_t i = 0; i != 10; ++i) {
            z.m_fe[i] = a.m_fe[i] + b.m_fe[i];
         }
         return z;
      }

      static Ed25519_FieldElement sub(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b) {
         Ed25519_FieldElement z;
         for(size_t i = 0; i != 10; ++i) {
            z.m_fe[i] = a.m_fe[i] - b.m_fe[i];
         }
         return z;
      }

      static Ed25519_FieldElement negate(const Ed25519_FieldElement& a) {
         Ed25519_FieldElement z;
         for(size_t i = 0; i != 10; ++i) {
            z.m_fe[i] = -a.m_fe[i];
         }
         return z;
      }

      static Ed25519_FieldElement mul(const Ed25519_FieldElement& a, const Ed25519_FieldElement& b);

      Ed25519_FieldElement sqr_iter(size_t iter) const;

      Ed25519_FieldElement sqr() const { return sqr_iter(1); }

      // Return 2*a^2
      Ed25519_FieldElement sqr2() const;

      Ed25519_FieldElement invert() const;

      Ed25519_FieldElement pow_22523() const;

      // TODO remove
      int32_t operator[](size_t i) const { return m_fe[i]; }

      int32_t& operator[](size_t i) { return m_fe[i]; }

   private:
      std::array<int32_t, 10> m_fe;
};

inline Ed25519_FieldElement operator+(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) {
   return Ed25519_FieldElement::add(x, y);
}

inline Ed25519_FieldElement operator-(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) {
   return Ed25519_FieldElement::sub(x, y);
}

inline Ed25519_FieldElement operator*(const Ed25519_FieldElement& x, const Ed25519_FieldElement& y) {
   return Ed25519_FieldElement::mul(x, y);
}

inline Ed25519_FieldElement operator-(const Ed25519_FieldElement& x) {
   return Ed25519_FieldElement::negate(x);
}

}  // namespace Botan

/**
 * @file loadstor.h
 *
 * @brief This header contains various helper functions to load and store
 *        unsigned integers in big- or little-endian byte order.
 *
 * Storing integer values in various ways (same for BE and LE):
 * @code {.cpp}
 *
 *   std::array<uint8_t, 8> bytes = store_le(some_uint64);
 *   std::array<uint8_t, 12> bytes = store_le(some_uint32_1, some_uint32_2, some_uint32_3, ...);
 *   auto bytes = store_le<std::vector<uint8_t>>(some_uint64);
 *   auto bytes = store_le<MyContainerStrongType>(some_uint64);
 *   auto bytes = store_le<std::vector<uint8_t>>(vector_of_ints);
 *   auto bytes = store_le<secure_vector<uint8_t>>(some_uint32_1, some_uint32_2, some_uint32_3, ...);
 *   store_le(bytes, some_uint64);
 *   store_le(concatenated_bytes, some_uint64_1, some_uint64_2, some_uint64_3, ...);
 *   store_le(concatenated_bytes, vector_of_ints);
 *   copy_out_le(short_concated_bytes, vector_of_ints); // stores as many bytes as required in the output buffer
 *
 * @endcode
 *
 * Loading integer values in various ways (same for BE and LE):
 * @code {.cpp}
 *
 *   uint64_t some_uint64 = load_le(bytes_8);
 *   auto some_int32s = load_le<std::vector<uint32_t>>(concatenated_bytes);
 *   auto some_int32s = load_le<std::vector<MyIntStrongType>>(concatenated_bytes);
 *   auto some_int32s = load_le(some_strong_typed_bytes);
 *   auto strong_int  = load_le<MyStrongTypedInteger>(concatenated_bytes);
 *   load_le(concatenated_bytes, out_some_uint64);
 *   load_le(concatenated_bytes, out_some_uint64_1, out_some_uint64_2, out_some_uint64_3, ...);
 *   load_le(out_vector_of_ints, concatenated_bytes);
 *
 * @endcode
 */

namespace Botan {

static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little,
              "Mixed endian systems are not supported");

/**
* Byte extraction
* @param byte_num which byte to extract, 0 == highest byte
* @param input the value to extract from
* @return byte byte_num of input
*/
template <typename T>
inline constexpr uint8_t get_byte_var(size_t byte_num, T input) {
   return static_cast<uint8_t>(input >> (((~byte_num) & (sizeof(T) - 1)) << 3));
}

/**
* Byte extraction
* @param input the value to extract from
* @return byte byte number B of input
*/
template <size_t B, typename T>
inline constexpr uint8_t get_byte(T input)
   requires(B < sizeof(T))
{
   const size_t shift = ((~B) & (sizeof(T) - 1)) << 3;
   return static_cast<uint8_t>((input >> shift) & 0xFF);
}

/**
* Make a uint16_t from two bytes
* @param i0 the first byte
* @param i1 the second byte
* @return i0 || i1
*/
inline constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1) {
   return static_cast<uint16_t>((static_cast<uint16_t>(i0) << 8) | i1);
}

/**
* Make a uint32_t from four bytes
* @param i0 the first byte
* @param i1 the second byte
* @param i2 the third byte
* @param i3 the fourth byte
* @return i0 || i1 || i2 || i3
*/
inline constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3) {
   return ((static_cast<uint32_t>(i0) << 24) | (static_cast<uint32_t>(i1) << 16) | (static_cast<uint32_t>(i2) << 8) |
           (static_cast<uint32_t>(i3)));
}

/**
* Make a uint64_t from eight bytes
* @param i0 the first byte
* @param i1 the second byte
* @param i2 the third byte
* @param i3 the fourth byte
* @param i4 the fifth byte
* @param i5 the sixth byte
* @param i6 the seventh byte
* @param i7 the eighth byte
* @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7
*/
inline constexpr uint64_t make_uint64(
   uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7) {
   return ((static_cast<uint64_t>(i0) << 56) | (static_cast<uint64_t>(i1) << 48) | (static_cast<uint64_t>(i2) << 40) |
           (static_cast<uint64_t>(i3) << 32) | (static_cast<uint64_t>(i4) << 24) | (static_cast<uint64_t>(i5) << 16) |
           (static_cast<uint64_t>(i6) << 8) | (static_cast<uint64_t>(i7)));
}

namespace detail {

/**
 * @returns the opposite endianness of the specified endianness
 *
 * Note this assumes that there are only two endian orderings; we
 * do not supported mixed endian systems
 */
consteval std::endian opposite(std::endian endianness) {
   if(endianness == std::endian::big) {
      return std::endian::little;
   } else {
      // We already verified via static assert earlier in this file that we are
      // running on either a big endian or little endian system
      return std::endian::big;
   }
}

/**
 * Models a custom type that provides factory methods to be loaded in big- or
 * little-endian byte order.
 */
template <typename T>
concept custom_loadable = requires(std::span<const uint8_t, sizeof(T)> data) {
   { T::load_be(data) } -> std::same_as<T>;
   { T::load_le(data) } -> std::same_as<T>;
};

/**
 * Models a custom type that provides store methods to be stored in big- or
 * little-endian byte order.
 */
template <typename T>
concept custom_storable = requires(std::span<uint8_t, sizeof(T)> data, const T value) {
   { value.store_be(data) };
   { value.store_le(data) };
};

/**
 * Models a type that can be loaded/stored from/to a byte range.
 */
template <typename T>
concept unsigned_integralish =
   std::unsigned_integral<strong_type_wrapped_type<T>> ||
   (std::is_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>) ||
   (custom_loadable<strong_type_wrapped_type<T>> || custom_storable<strong_type_wrapped_type<T>>);

template <typename T>
struct wrapped_type_helper_with_enum {
      using type = strong_type_wrapped_type<T>;
};

template <typename T>
   requires std::is_enum_v<T>
struct wrapped_type_helper_with_enum<T> {
      using type = std::underlying_type_t<T>;
};

template <unsigned_integralish T>
using wrapped_type = typename wrapped_type_helper_with_enum<T>::type;

template <unsigned_integralish InT>
constexpr auto unwrap_strong_type_or_enum(InT t) {
   if constexpr(std::is_enum_v<InT>) {
      // TODO: C++23: use std::to_underlying(in) instead
      return static_cast<std::underlying_type_t<InT>>(t);
   } else {
      return Botan::unwrap_strong_type(t);
   }
}

template <unsigned_integralish OutT, std::unsigned_integral T>
constexpr auto wrap_strong_type_or_enum(T t) {
   if constexpr(std::is_enum_v<OutT>) {
      return static_cast<OutT>(t);
   } else {
      return Botan::wrap_strong_type<OutT>(t);
   }
}

/**
 * Manually load a word from a range in either big or little endian byte order.
 *
 * This is only used at compile time.
 */
template <std::endian endianness, std::unsigned_integral OutT, ranges::contiguous_range<uint8_t> InR>
inline constexpr OutT fallback_load_any(const InR& in_range) {
   std::span in{in_range};
   // clang-format off
   if constexpr(endianness == std::endian::big) {
      return [&]<size_t... i>(std::index_sequence<i...>) {
         return static_cast<OutT>(((static_cast<OutT>(in[i]) << ((sizeof(OutT) - i - 1) * 8)) | ...));
      } (std::make_index_sequence<sizeof(OutT)>());
   } else {
      static_assert(endianness == std::endian::little);
      return [&]<size_t... i>(std::index_sequence<i...>) {
         return static_cast<OutT>(((static_cast<OutT>(in[i]) << (i * 8)) | ...));
      } (std::make_index_sequence<sizeof(OutT)>());
   }
   // clang-format on
}

/**
 * Manually store a word into a range in either big or little endian byte order.
 *
 * This will be used only at compile time.
 */
template <std::endian endianness, std::unsigned_integral InT, ranges::contiguous_output_range<uint8_t> OutR>
inline constexpr void fallback_store_any(InT in, OutR&& out_range /* NOLINT(*-std-forward) */) {
   std::span out{out_range};
   // clang-format off
   if constexpr(endianness == std::endian::big) {
      [&]<size_t... i>(std::index_sequence<i...>) {
         ((out[i] = get_byte<i>(in)), ...);
      } (std::make_index_sequence<sizeof(InT)>());
   } else {
      static_assert(endianness == std::endian::little);
      [&]<size_t... i>(std::index_sequence<i...>) {
         ((out[i] = get_byte<sizeof(InT) - i - 1>(in)), ...);
      } (std::make_index_sequence<sizeof(InT)>());
   }
   // clang-format on
}

/**
 * Load a word from a range in either big or little endian byte order
 *
 * This is the base implementation, all other overloads are just convenience
 * wrappers. It is assumed that the range has the correct size for the word.
 *
 * Template arguments of all overloads of load_any() share the same semantics:
 *
 *   1.  std::endian     Either `std::endian::big` or `std::endian::little`, that
 *                       will eventually select the byte order translation mode
 *                       implemented in this base function.
 *
 *   2.  Output type     Either `AutoDetect`, an unsigned integer or a container
 *                       holding an unsigned integer type. `AutoDetect` means
 *                       that the caller did not explicitly specify the type and
 *                       expects the type to be inferred from the input.
 *
 *   3+. Argument types  Typically, those are input and output ranges of bytes
 *                       or unsigned integers. Or one or more unsigned integers
 *                       acting as output parameters.
 *
 * @param in_range a fixed-length byte range
 * @return T loaded from @p in_range, as a big-endian value
 */
template <std::endian endianness, unsigned_integralish WrappedOutT, ranges::contiguous_range<uint8_t> InR>
   requires(!custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
inline constexpr WrappedOutT load_any(InR&& in_range) {
   using OutT = detail::wrapped_type<WrappedOutT>;
   ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);

   return detail::wrap_strong_type_or_enum<WrappedOutT>([&]() -> OutT {
      // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
      // internally to copy ranges on a byte-by-byte basis, which is not allowed
      // in a `constexpr` context.
      if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
         return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
      } else {
         std::span in{in_range};
         if constexpr(sizeof(OutT) == 1) {
            return static_cast<OutT>(in[0]);
         } else if constexpr(endianness == std::endian::native) {
            return typecast_copy<OutT>(in);
         } else {
            static_assert(opposite(endianness) == std::endian::native);
            return reverse_bytes(typecast_copy<OutT>(in));
         }
      }
   }());
}

/**
 * Load a custom object from a range in either big or little endian byte order
 *
 * This is the base implementation for custom objects (e.g. SIMD type wrappres),
 * all other overloads are just convenience overloads.
 *
 * @param in_range a fixed-length byte range
 * @return T loaded from @p in_range, as a big-endian value
 */
template <std::endian endianness, unsigned_integralish WrappedOutT, ranges::contiguous_range<uint8_t> InR>
   requires(custom_loadable<strong_type_wrapped_type<WrappedOutT>>)
inline constexpr WrappedOutT load_any(const InR& in_range) {
   using OutT = detail::wrapped_type<WrappedOutT>;
   ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);
   std::span<const uint8_t, sizeof(OutT)> ins{in_range};
   if constexpr(endianness == std::endian::big) {
      return wrap_strong_type<WrappedOutT>(OutT::load_be(ins));
   } else {
      return wrap_strong_type<WrappedOutT>(OutT::load_le(ins));
   }
}

/**
 * Load many unsigned integers
 * @param in   a fixed-length span to some bytes
 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
 */
template <std::endian endianness, typename OutT, ranges::contiguous_range<uint8_t> InR, unsigned_integralish... Ts>
   requires(sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
                                   (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>))
inline constexpr void load_any(const InR& in, Ts&... outs) {
   ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(in);
   auto load_one = [off = 0]<typename T>(auto i, T& o) mutable {
      o = load_any<endianness, T>(i.subspan(off).template first<sizeof(T)>());
      off += sizeof(T);
   };

   (load_one(std::span{in}, outs), ...);
}

/**
 * Load a variable number of words from @p in into @p out.
 * The byte length of the @p out and @p in ranges must match.
 *
 * @param out the output range of words
 * @param in the input range of bytes
 */
template <std::endian endianness,
          typename OutT,
          ranges::contiguous_output_range OutR,
          ranges::contiguous_range<uint8_t> InR>
   requires(unsigned_integralish<std::ranges::range_value_t<OutR>> &&
            (std::same_as<AutoDetect, OutT> || std::same_as<OutT, std::ranges::range_value_t<OutR>>))
inline constexpr void load_any(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) {
   ranges::assert_equal_byte_lengths(out, in);
   using element_type = std::ranges::range_value_t<OutR>;

   auto load_elementwise = [&] {
      constexpr size_t bytes_per_element = sizeof(element_type);
      std::span<const uint8_t> in_s(in);
      for(auto& out_elem : out) {
         out_elem = load_any<endianness, element_type>(in_s.template first<bytes_per_element>());
         in_s = in_s.subspan(bytes_per_element);
      }
   };

   // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
   // internally to copy ranges on a byte-by-byte basis, which is not allowed
   // in a `constexpr` context.
   if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
      load_elementwise();
   } else {
      if constexpr(endianness == std::endian::native && !custom_loadable<element_type>) {
         typecast_copy(out, in);
      } else {
         load_elementwise();
      }
   }
}

//
// Type inference overloads
//

/**
 * Load one or more unsigned integers, auto-detect the output type if
 * possible. Otherwise, use the specified integer or integer container type.
 *
 * @param in_range a statically-sized range with some bytes
 * @return T loaded from in
 */
template <std::endian endianness, typename OutT, ranges::contiguous_range<uint8_t> InR>
   requires(std::same_as<AutoDetect, OutT> ||
            ((ranges::statically_spanable_range<OutT> || concepts::resizable_container<OutT>) &&
             unsigned_integralish<typename OutT::value_type>))
inline constexpr auto load_any(InR&& in_range) {
   auto out = []([[maybe_unused]] const auto& in) {
      if constexpr(std::same_as<AutoDetect, OutT>) {
         if constexpr(ranges::statically_spanable_range<InR>) {
            constexpr size_t extent = decltype(std::span{in})::extent;

            // clang-format off
            using type =
               std::conditional_t<extent == 1, uint8_t,
               std::conditional_t<extent == 2, uint16_t,
               std::conditional_t<extent == 4, uint32_t,
               std::conditional_t<extent == 8, uint64_t, void>>>>;
            // clang-format on

            static_assert(
               !std::is_void_v<type>,
               "Cannot determine the output type based on a statically sized bytearray with length other than those: 1, 2, 4, 8");

            return type{};
         } else {
            static_assert(
               !std::same_as<AutoDetect, OutT>,
               "cannot infer return type from a dynamic range at compile time, please specify it explicitly");
         }
      } else if constexpr(concepts::resizable_container<OutT>) {
         const size_t in_bytes = std::span{in}.size_bytes();
         constexpr size_t out_elem_bytes = sizeof(typename OutT::value_type);
         BOTAN_ARG_CHECK(in_bytes % out_elem_bytes == 0,
                         "Input range is not word-aligned with the requested output range");
         return OutT(in_bytes / out_elem_bytes);
      } else {
         return OutT{};
      }
   }(in_range);

   using out_type = decltype(out);
   if constexpr(unsigned_integralish<out_type>) {
      out = load_any<endianness, out_type>(std::forward<InR>(in_range));
   } else {
      static_assert(ranges::contiguous_range<out_type>);
      using out_range_type = std::ranges::range_value_t<out_type>;
      load_any<endianness, out_range_type>(out, std::forward<InR>(in_range));
   }
   return out;
}

//
// Legacy load functions that work on raw pointers and arrays
//

/**
 * Load a word from @p in at some offset @p off
 * @param in a pointer to some bytes
 * @param off an offset into the array
 * @return off'th T of in, as a big-endian value
 */
template <std::endian endianness, unsigned_integralish OutT>
inline constexpr OutT load_any(const uint8_t in[], size_t off) {
   // asserts that *in points to enough bytes to read at offset off
   constexpr size_t out_size = sizeof(OutT);
   return load_any<endianness, OutT>(std::span<const uint8_t, out_size>(in + off * out_size, out_size));
}

/**
 * Load many words from @p in
 * @param in   a pointer to some bytes
 * @param outs a arbitrary-length parameter list of unsigned integers to be loaded
 */
template <std::endian endianness, typename OutT, unsigned_integralish... Ts>
   requires(sizeof...(Ts) > 0 && all_same_v<Ts...> &&
            ((std::same_as<AutoDetect, OutT> && all_same_v<Ts...>) ||
             (unsigned_integralish<OutT> && all_same_v<OutT, Ts...>)))
inline constexpr void load_any(const uint8_t in[], Ts&... outs) {
   constexpr auto bytes = (sizeof(outs) + ...);
   // asserts that *in points to the correct amount of memory
   load_any<endianness, OutT>(std::span<const uint8_t, bytes>(in, bytes), outs...);
}

/**
 * Load a variable number of words from @p in into @p out.
 * @param out the output array of words
 * @param in the input array of bytes
 * @param count how many words are in in
 */
template <std::endian endianness, typename OutT, unsigned_integralish T>
   requires(std::same_as<AutoDetect, OutT> || std::same_as<T, OutT>)
inline constexpr void load_any(T out[], const uint8_t in[], size_t count) {
   // asserts that *in and *out point to the correct amount of memory
   load_any<endianness, OutT>(std::span<T>(out, count), std::span<const uint8_t>(in, count * sizeof(T)));
}

}  // namespace detail

/**
 * Load "something" in little endian byte order
 * See the documentation of this file for more details.
 */
template <typename OutT = detail::AutoDetect, typename... ParamTs>
inline constexpr auto load_le(ParamTs&&... params) {
   return detail::load_any<std::endian::little, OutT>(std::forward<ParamTs>(params)...);
}

/**
 * Load "something" in big endian byte order
 * See the documentation of this file for more details.
 */
template <typename OutT = detail::AutoDetect, typename... ParamTs>
inline constexpr auto load_be(ParamTs&&... params) {
   return detail::load_any<std::endian::big, OutT>(std::forward<ParamTs>(params)...);
}

namespace detail {

/**
 * Store a word in either big or little endian byte order into a range
 *
 * This is the base implementation, all other overloads are just convenience
 * wrappers. It is assumed that the range has the correct size for the word.
 *
 * Template arguments of all overloads of store_any() share the same semantics
 * as those of load_any(). See the documentation of this function for more
 * details.
 *
 * @param wrapped_in an unsigned integral to be stored
 * @param out_range  a byte range to store the word into
 */
template <std::endian endianness, unsigned_integralish WrappedInT, ranges::contiguous_output_range<uint8_t> OutR>
   requires(!custom_storable<strong_type_wrapped_type<WrappedInT>>)
inline constexpr void store_any(WrappedInT wrapped_in, OutR&& out_range) {
   const auto in = detail::unwrap_strong_type_or_enum(wrapped_in);
   using InT = decltype(in);
   ranges::assert_exact_byte_length<sizeof(in)>(out_range);
   std::span out{out_range};

   // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
   // internally to copy ranges on a byte-by-byte basis, which is not allowed
   // in a `constexpr` context.
   if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
      return fallback_store_any<endianness, InT>(in, std::forward<OutR>(out_range));
   } else {
      if constexpr(sizeof(InT) == 1) {
         out[0] = static_cast<uint8_t>(in);
      } else if constexpr(endianness == std::endian::native) {
         typecast_copy(out, in);
      } else {
         static_assert(opposite(endianness) == std::endian::native);
         typecast_copy(out, reverse_bytes(in));
      }
   }
}

/**
 * Store a custom word in either big or little endian byte order into a range
 *
 * This is the base implementation for storing custom objects, all other
 * overloads are just convenience overloads.
 *
 * @param wrapped_in a custom object to be stored
 * @param out_range  a byte range to store the word into
 */
template <std::endian endianness, unsigned_integralish WrappedInT, ranges::contiguous_output_range<uint8_t> OutR>
   requires(custom_storable<strong_type_wrapped_type<WrappedInT>>)
inline constexpr void store_any(WrappedInT wrapped_in, const OutR& out_range) {
   const auto in = detail::unwrap_strong_type_or_enum(wrapped_in);
   using InT = decltype(in);
   ranges::assert_exact_byte_length<sizeof(in)>(out_range);
   std::span<uint8_t, sizeof(InT)> outs{out_range};
   if constexpr(endianness == std::endian::big) {
      in.store_be(outs);
   } else {
      in.store_le(outs);
   }
}

/**
 * Store many unsigned integers words into a byte range
 * @param out a sized range of some bytes
 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
 */
template <std::endian endianness,
          typename InT,
          ranges::contiguous_output_range<uint8_t> OutR,
          unsigned_integralish... Ts>
   requires(sizeof...(Ts) > 0) && ((std::same_as<AutoDetect, InT> && all_same_v<Ts...>) ||
                                   (unsigned_integralish<InT> && all_same_v<InT, Ts...>))
inline constexpr void store_any(OutR&& out /* NOLINT(*-std-forward) */, Ts... ins) {
   ranges::assert_exact_byte_length<(sizeof(Ts) + ...)>(out);
   auto store_one = [off = 0]<typename T>(auto o, T i) mutable {
      store_any<endianness, T>(i, o.subspan(off).template first<sizeof(T)>());
      off += sizeof(T);
   };

   (store_one(std::span{out}, ins), ...);
}

/**
 * Store a variable number of words given in @p in into @p out.
 * The byte lengths of @p in and @p out must be consistent.
 * @param out the output range of bytes
 * @param in the input range of words
 */
template <std::endian endianness,
          typename InT,
          ranges::contiguous_output_range<uint8_t> OutR,
          ranges::spanable_range InR>
   requires(std::same_as<AutoDetect, InT> || std::same_as<InT, std::ranges::range_value_t<InR>>)
inline constexpr void store_any(OutR&& out /* NOLINT(*-std-forward) */, const InR& in) {
   ranges::assert_equal_byte_lengths(out, in);
   using element_type = std::ranges::range_value_t<InR>;

   auto store_elementwise = [&] {
      constexpr size_t bytes_per_element = sizeof(element_type);
      std::span<uint8_t> out_s(out);
      for(auto in_elem : in) {
         store_any<endianness, element_type>(out_s.template first<bytes_per_element>(), in_elem);
         out_s = out_s.subspan(bytes_per_element);
      }
   };

   // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
   // internally to copy ranges on a byte-by-byte basis, which is not allowed
   // in a `constexpr` context.
   if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
      store_elementwise();
   } else {
      if constexpr(endianness == std::endian::native && !custom_storable<element_type>) {
         typecast_copy(out, in);
      } else {
         store_elementwise();
      }
   }
}

//
// Type inference overloads
//

/**
 * Infer InT from a single unsigned integer input parameter.
 *
 * TODO: we might consider dropping this overload (i.e. out-range as second
 *       parameter) and make this a "special case" of the overload below, that
 *       takes a variadic number of input parameters.
 *
 * @param in an unsigned integer to be stored
 * @param out_range a range of bytes to store the word into
 */
template <std::endian endianness, typename InT, unsigned_integralish T, ranges::contiguous_output_range<uint8_t> OutR>
   requires std::same_as<AutoDetect, InT>
inline constexpr void store_any(T in, OutR&& out_range) {
   store_any<endianness, T>(in, std::forward<OutR>(out_range));
}

/**
 * The caller provided some integer values in a collection but did not provide
 * the output container. Let's create one for them, fill it with one of the
 * overloads above and return it. This will default to a std::array if the
 * caller did not specify the desired output container type.
 *
 * @param in_range a range of words that should be stored
 * @return a container of bytes that contains the stored words
 */
template <std::endian endianness, typename OutR, ranges::spanable_range InR>
   requires(std::same_as<AutoDetect, OutR> ||
            (ranges::statically_spanable_range<OutR> && std::default_initializable<OutR>) ||
            concepts::resizable_byte_buffer<OutR>)
inline constexpr auto store_any(InR&& in_range) {
   auto out = []([[maybe_unused]] const auto& in) {
      if constexpr(std::same_as<AutoDetect, OutR>) {
         if constexpr(ranges::statically_spanable_range<InR>) {
            constexpr size_t bytes = decltype(std::span{in})::extent * sizeof(std::ranges::range_value_t<InR>);
            return std::array<uint8_t, bytes>();
         } else {
            static_assert(
               !std::same_as<AutoDetect, OutR>,
               "cannot infer a suitable result container type from the given parameters at compile time, please specify it explicitly");
         }
      } else if constexpr(concepts::resizable_byte_buffer<OutR>) {
         return OutR(std::span{in}.size_bytes());
      } else {
         return OutR{};
      }
   }(in_range);

   store_any<endianness, std::ranges::range_value_t<InR>>(out, std::forward<InR>(in_range));
   return out;
}

/**
 * The caller provided some integer values but did not provide the output
 * container. Let's create one for them, fill it with one of the overloads above
 * and return it. This will default to a std::array if the caller did not
 * specify the desired output container type.
 *
 * @param ins some words that should be stored
 * @return a container of bytes that contains the stored words
 */
template <std::endian endianness, typename OutR, unsigned_integralish... Ts>
   requires all_same_v<Ts...>
inline constexpr auto store_any(Ts... ins) {
   return store_any<endianness, OutR>(std::array{ins...});
}

//
// Legacy store functions that work on raw pointers and arrays
//

/**
 * Store a single unsigned integer into a raw pointer
 * @param in the input unsigned integer
 * @param out the byte array to write to
 */
template <std::endian endianness, typename InT, unsigned_integralish T>
   requires(std::same_as<AutoDetect, InT> || std::same_as<T, InT>)
inline constexpr void store_any(T in, uint8_t out[]) {
   // asserts that *out points to enough bytes to write into
   store_any<endianness, InT>(in, std::span<uint8_t, sizeof(T)>(out, sizeof(T)));
}

/**
 * Store many unsigned integers words into a raw pointer
 * @param ins a arbitrary-length parameter list of unsigned integers to be stored
 * @param out the byte array to write to
 */
template <std::endian endianness, typename InT, unsigned_integralish T0, unsigned_integralish... Ts>
   requires(std::same_as<AutoDetect, InT> || std::same_as<T0, InT>) && all_same_v<T0, Ts...>
inline constexpr void store_any(uint8_t out[], T0 in0, Ts... ins) {
   constexpr auto bytes = sizeof(in0) + (sizeof(ins) + ... + 0);
   // asserts that *out points to the correct amount of memory
   store_any<endianness, T0>(std::span<uint8_t, bytes>(out, bytes), in0, ins...);
}

}  // namespace detail

/**
 * Store "something" in little endian byte order
 * See the documentation of this file for more details.
 */
template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
inline constexpr auto store_le(ParamTs&&... params) {
   return detail::store_any<std::endian::little, ModifierT>(std::forward<ParamTs>(params)...);
}

/**
 * Store "something" in big endian byte order
 * See the documentation of this file for more details.
 */
template <typename ModifierT = detail::AutoDetect, typename... ParamTs>
inline constexpr auto store_be(ParamTs&&... params) {
   return detail::store_any<std::endian::big, ModifierT>(std::forward<ParamTs>(params)...);
}

namespace detail {

template <std::endian endianness, unsigned_integralish T>
inline size_t copy_out_any_word_aligned_portion(std::span<uint8_t>& out, std::span<const T>& in) {
   const size_t full_words = out.size() / sizeof(T);
   const size_t full_word_bytes = full_words * sizeof(T);
   const size_t remaining_bytes = out.size() - full_word_bytes;
   BOTAN_ASSERT_NOMSG(in.size_bytes() >= full_word_bytes + remaining_bytes);

   // copy full words
   store_any<endianness, T>(out.first(full_word_bytes), in.first(full_words));
   out = out.subspan(full_word_bytes);
   in = in.subspan(full_words);

   return remaining_bytes;
}

}  // namespace detail

/**
 * Partially copy a subset of @p in into @p out using big-endian
 * byte order.
 */
template <ranges::spanable_range InR>
inline void copy_out_be(std::span<uint8_t> out, const InR& in) {
   using T = std::ranges::range_value_t<InR>;
   std::span<const T> in_s{in};
   const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<std::endian::big>(out, in_s);

   // copy remaining bytes as a partial word
   for(size_t i = 0; i < remaining_bytes; ++i) {
      out[i] = get_byte_var(i, in_s.front());
   }
}

/**
 * Partially copy a subset of @p in into @p out using little-endian
 * byte order.
 */
template <ranges::spanable_range InR>
inline void copy_out_le(std::span<uint8_t> out, const InR& in) {
   using T = std::ranges::range_value_t<InR>;
   std::span<const T> in_s{in};
   const auto remaining_bytes = detail::copy_out_any_word_aligned_portion<std::endian::little>(out, in_s);

   // copy remaining bytes as a partial word
   for(size_t i = 0; i < remaining_bytes; ++i) {
      out[i] = get_byte_var(sizeof(T) - 1 - i, in_s.front());
   }
}

}  // namespace Botan

namespace Botan {

inline uint32_t load_3(const uint8_t in[3]) {
   return static_cast<uint32_t>(in[0]) | (static_cast<uint32_t>(in[1]) << 8) | (static_cast<uint32_t>(in[2]) << 16);
}

inline uint32_t load_4(const uint8_t* in) {
   return load_le<uint32_t>(in, 0);
}

template <size_t S, int64_t MUL = 1>
inline void carry(int64_t& h0, int64_t& h1)
   requires(S > 0 && S < 64)
{
   const int64_t X1 = (static_cast<int64_t>(1) << S);
   const int64_t X2 = (static_cast<int64_t>(1) << (S - 1));
   int64_t c = (h0 + X2) >> S;
   h1 += c * MUL;
   h0 -= c * X1;
}

template <size_t S>
inline void carry0(int64_t& h0, int64_t& h1)
   requires(S > 0 && S < 64)
{
   const int64_t X1 = (static_cast<int64_t>(1) << S);
   int64_t c = h0 >> S;
   h1 += c;
   h0 -= c * X1;
}

template <size_t S>
inline void carry0(int32_t& h0, int32_t& h1)
   requires(S > 0 && S < 32)
{
   const int32_t X1 = (static_cast<int64_t>(1) << S);
   int32_t c = h0 >> S;
   h1 += c;
   h0 -= c * X1;
}

inline void redc_mul(int64_t& s1, int64_t& s2, int64_t& s3, int64_t& s4, int64_t& s5, int64_t& s6, int64_t& X) {
   s1 += X * 666643;
   s2 += X * 470296;
   s3 += X * 654183;
   s4 -= X * 997805;
   s5 += X * 136657;
   s6 -= X * 683901;
   X = 0;
}

void ed25519_basepoint_mul(std::span<uint8_t, 32> out, const uint8_t in[32]);

bool signature_check(std::span<const uint8_t, 32> pk, const uint8_t h[32], const uint8_t r[32], const uint8_t s[32]);

/*
The set of scalars is \Z/l
where l = 2^252 + 27742317777372353535851937790883648493.
*/

void sc_reduce(uint8_t* s);
void sc_muladd(uint8_t* s, const uint8_t* a, const uint8_t* b, const uint8_t* c);

}  // namespace Botan

namespace Botan {

class RandomNumberGenerator;

/**
* Encoding Method for Encryption
*/
class BOTAN_TEST_API EME /* NOLINT(*-special-member-functions) */ {
   public:
      virtual ~EME();

      /**
      * Factory method for EME (message-encoding methods for encryption) objects
      * @param algo_spec the name of the EME to create
      * @return pointer to newly allocated object of that type
      */
      static std::unique_ptr<EME> create(std::string_view algo_spec);

      /**
      * Return the maximum input size in bytes we can support
      * @param keybits the size of the key in bits
      * @return upper bound of input in bytes
      */
      virtual size_t maximum_input_size(size_t keybits) const = 0;

      /**
      * Encode an input
      * @param output buffer that is written to
      * @param input the plaintext
      * @param key_length length of the key in bits
      * @param rng a random number generator
      * @return number of bytes written to output
      */
      virtual size_t pad(std::span<uint8_t> output,
                         std::span<const uint8_t> input,
                         size_t key_length,
                         RandomNumberGenerator& rng) const = 0;

      /**
      * Decode an input
      * @param output buffer where output is placed
      * @param input the encoded plaintext
      * @return number of bytes written to output if valid,
      *  or an empty option if invalid. If an empty option is
      *  returned the contents of output are undefined
      */
      virtual CT::Option<size_t> unpad(std::span<uint8_t> output, std::span<const uint8_t> input) const = 0;
};

}  // namespace Botan

namespace Botan {

/**
* No_Filesystem_Access Exception
*/
class No_Filesystem_Access final : public Exception {
   public:
      No_Filesystem_Access() : Exception("No filesystem access enabled.") {}
};

BOTAN_TEST_API bool has_filesystem_impl();

BOTAN_TEST_API std::vector<std::string> get_files_recursive(std::string_view dir);

}  // namespace Botan

namespace Botan {

namespace fmt_detail {

inline void do_fmt(std::ostringstream& oss, std::string_view format) {
   oss << format;
}

template <typename T, typename... Ts>
void do_fmt(std::ostringstream& oss, std::string_view format, const T& val, const Ts&... rest) {
   size_t i = 0;

   while(i < format.size()) {
      if(format[i] == '{' && (format.size() > (i + 1)) && format.at(i + 1) == '}') {
         oss << val;
         return do_fmt(oss, format.substr(i + 2), rest...);
      } else {
         oss << format[i];
      }

      i += 1;
   }
}

}  // namespace fmt_detail

/**
* Simple formatter utility.
*
* Should be replaced with std::format once that's available on all our
* supported compilers.
*
* '{}' markers in the format string are replaced by the arguments.
* Unlike std::format, there is no support for escaping or for any kind
* of conversion flags.
*/
template <typename... T>
std::string fmt(std::string_view format, const T&... args) {
   std::ostringstream oss;
   oss.imbue(std::locale::classic());
   fmt_detail::do_fmt(oss, format, args...);
   return oss.str();
}

}  // namespace Botan

namespace Botan {

/**
* HMAC
*/
class HMAC final : public MessageAuthenticationCode {
   public:
      void clear() override;
      std::string name() const override;
      std::unique_ptr<MessageAuthenticationCode> new_object() const override;

      size_t output_length() const override;

      Key_Length_Specification key_spec() const override;

      bool has_keying_material() const override;

      /**
      * @param hash the hash to use for HMACing
      */
      explicit HMAC(std::unique_ptr<HashFunction> hash);

   private:
      void add_data(std::span<const uint8_t> input) override;
      void final_result(std::span<uint8_t> output) override;
      void key_schedule(std::span<const uint8_t> key) override;

      std::unique_ptr<HashFunction> m_hash;
      secure_vector<uint8_t> m_ikey, m_okey;
      size_t m_hash_output_length;
      size_t m_hash_block_size;
};

}  // namespace Botan

namespace Botan {

template <std::unsigned_integral T>
constexpr inline std::optional<T> checked_add(T a, T b) {
   const T r = a + b;
   if(r < a || r < b) {
      return {};
   }
   return r;
}

template <std::unsigned_integral T>
constexpr std::optional<T> checked_sub(T a, T b) {
   if(b > a) {
      return {};
   }
   return a - b;
}

template <std::unsigned_integral T, std::unsigned_integral... Ts>
   requires all_same_v<T, Ts...>
constexpr inline std::optional<T> checked_add(T a, T b, Ts... rest) {
   if(auto r = checked_add(a, b)) {
      return checked_add(r.value(), rest...);
   } else {
      return {};
   }
}

template <std::unsigned_integral T>
constexpr inline std::optional<T> checked_mul(T a, T b) {
   // Multiplication by 1U is a hack to work around C's insane
   // integer promotion rules.
   // https://stackoverflow.com/questions/24795651
   const T r = (1U * a) * b;
   // If a == 0 then the multiply certainly did not overflow
   // Otherwise r / a == b unless overflow occured
   if(a != 0 && r / a != b) {
      return {};
   }
   return r;
}

template <typename RT, typename ExceptionType, typename AT>
   requires std::integral<strong_type_wrapped_type<RT>> && std::integral<strong_type_wrapped_type<AT>>
constexpr RT checked_cast_to_or_throw(AT i, std::string_view error_msg_on_fail) {
   const auto unwrapped_input = unwrap_strong_type(i);

   const auto unwrapped_result = static_cast<strong_type_wrapped_type<RT>>(unwrapped_input);
   if(unwrapped_input != static_cast<strong_type_wrapped_type<AT>>(unwrapped_result)) [[unlikely]] {
      throw ExceptionType(error_msg_on_fail);
   }

   return wrap_strong_type<RT>(unwrapped_result);
}

template <typename RT, typename AT>
   requires std::integral<strong_type_wrapped_type<RT>> && std::integral<strong_type_wrapped_type<AT>>
constexpr RT checked_cast_to(AT i) {
   return checked_cast_to_or_throw<RT, Internal_Error>(i, "Error during integer conversion");
}

/**
* SWAR (SIMD within a word) byte-by-byte comparison
*
* This individually compares each byte of the provided words.
* It returns a mask which contains, for each byte, 0xFF if
* the byte in @p a was less than the byte in @p b. Otherwise the
* mask is 00.
*
* This implementation assumes that the high bits of each byte
* in both @p a and @p b are clear! It is possible to support the
* full range of bytes, but this requires additional comparisons.
*/
template <std::unsigned_integral T>
constexpr T swar_lt(T a, T b) {
   // The constant 0x808080... as a T
   constexpr T hi1 = (static_cast<T>(-1) / 255) << 7;
   // The constant 0x7F7F7F... as a T
   constexpr T lo7 = static_cast<T>(~hi1);
   T r = (lo7 - a + b) & hi1;
   // Currently the mask is 80 if lt, otherwise 00. Convert to FF/00
   return (r << 1) - (r >> 7);
}

/**
* SWAR (SIMD within a word) byte-by-byte comparison
*
* This individually compares each byte of the provided words.
* It returns a mask which contains, for each byte, 0x80 if
* the byte in @p a was less than the byte in @p b. Otherwise the
* mask is 00.
*
* This implementation assumes that the high bits of each byte
* in both @p lower and @p upper are clear! It is possible to support the
* full range of bytes, but this requires additional comparisons.
*/
template <std::unsigned_integral T>
constexpr T swar_in_range(T v, T lower, T upper) {
   // The constant 0x808080... as a T
   constexpr T hi1 = (static_cast<T>(-1) / 255) << 7;
   // The constant 0x7F7F7F... as a T
   constexpr T lo7 = ~hi1;

   const T sub = ((v | hi1) - (lower & lo7)) ^ ((v ^ (~lower)) & hi1);
   const T a_lo = sub & lo7;
   const T a_hi = sub & hi1;
   return (lo7 - a_lo + upper) & hi1 & ~a_hi;
}

/**
* Return the index of the first byte with the high bit set
*/
template <std::unsigned_integral T>
constexpr size_t index_of_first_set_byte(T v) {
   // The constant 0x010101... as a T
   constexpr T lo1 = (static_cast<T>(-1) / 255);
   // The constant 0x808080... as a T
   constexpr T hi1 = lo1 << 7;
   // How many bits to shift in order to get the top byte
   constexpr size_t bits = (sizeof(T) * 8) - 8;

   return static_cast<size_t>((((((v & hi1) - 1) & lo1) * lo1) >> bits) - 1);
}

}  // namespace Botan

/*
* GCC and Clang use string identifiers to tag ISA extensions (eg using the
* `target` function attribute).
*
* This file consolidates the actual definition of such target attributes
*/

#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)

   #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("ssse3")
   #define BOTAN_FN_ISA_SIMD_2X64 BOTAN_FUNC_ISA("ssse3")
   #define BOTAN_FN_ISA_SIMD_4X64 BOTAN_FUNC_ISA("avx2")
   #define BOTAN_FN_ISA_SIMD_8X64 BOTAN_FN_ISA_AVX512
   #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("pclmul,ssse3")
   #define BOTAN_FN_ISA_AESNI BOTAN_FUNC_ISA("aes,ssse3")
   #define BOTAN_FN_ISA_SHANI BOTAN_FUNC_ISA("sha,ssse3,sse4.1")
   #define BOTAN_FN_ISA_SHA512 BOTAN_FUNC_ISA("sha512,avx2")
   #define BOTAN_FN_ISA_SSE2 BOTAN_FUNC_ISA("sse2")
   #define BOTAN_FN_ISA_AVX2 BOTAN_FUNC_ISA("avx2")
   #define BOTAN_FN_ISA_AVX2_BMI2 BOTAN_FUNC_ISA("avx2,bmi,bmi2")
   #define BOTAN_FN_ISA_AVX2_VAES BOTAN_FUNC_ISA("vaes,avx2")
   #define BOTAN_FN_ISA_AVX2_SM4 BOTAN_FUNC_ISA("sm4,avx2")
   #define BOTAN_FN_ISA_AVX2_GFNI BOTAN_FUNC_ISA("gfni,avx2")
   #define BOTAN_FN_ISA_AVX512 BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw")
   #define BOTAN_FN_ISA_AVX512_BMI2 BOTAN_FUNC_ISA("avx512f,avx512dq,avx512bw,bmi,bmi2")

#endif

#if defined(BOTAN_TARGET_ARCH_IS_ARM64)

   #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("+simd")
   #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("+crypto+aes")
   #define BOTAN_FN_ISA_AES BOTAN_FUNC_ISA("+crypto+aes")
   #define BOTAN_FN_ISA_SHA2 BOTAN_FUNC_ISA("+crypto+sha2")
   #define BOTAN_FN_ISA_SM4 BOTAN_FUNC_ISA("arch=armv8.2-a+sm4")
   #define BOTAN_FN_ISA_SHA512 BOTAN_FUNC_ISA("arch=armv8.2-a+sha3")

#endif

#if defined(BOTAN_TARGET_ARCH_IS_ARM32)
   #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("fpu=neon")
#endif

#if defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY)

   #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("altivec")
   #define BOTAN_FN_ISA_CLMUL BOTAN_FUNC_ISA("vsx,crypto")
   #define BOTAN_FN_ISA_AES BOTAN_FUNC_ISA("vsx,crypto")

#endif

#if defined(BOTAN_TARGET_ARCH_IS_LOONGARCH64)

   #define BOTAN_FN_ISA_SIMD_4X32 BOTAN_FUNC_ISA("lsx")

#endif

namespace Botan {

/**
* KECCAK FIPS
*
* This file implements Keccak[c] which is specified by NIST FIPS 202 [1], where
* "c" is the variable capacity of this hash primitive. Keccak[c] is not  a
* general purpose hash function, but used as the basic primitive for algorithms
* such as SHA-3 and KMAC. This is not to be confused with the "informal" general purpose hash
* function which is referred to as "Keccak" and apparently refers to the final
* submission version of the Keccak submission in the SHA-3 contest, possibly
* what is released by NIST under the name "KECCAK - Final Algorithm Package" [2].
* See also the file keccak.h for the details how the keccak hash function is defined
* in terms of the Keccak[c] – a detail which cannot be found in [1].
*
*
*
* [1] FIPS PUB 202 – FEDERAL INFORMATION PROCESSING STANDARDS PUBLICATION – SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
*       https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=28
* [2] https://csrc.nist.gov/projects/hash-functions/sha-3-project
*/
class Keccak_Permutation final {
   public:
      /**
        * @brief Instantiate a Keccak permutation
        *
        * The @p custom_padding is assumed to be init_pad || 00... || fini_pad
        *
        * @param capacity_bits Keccak capacity
        * @param custom_padding the custom bit padding that is to be appended on the call to finish
        * @param custom_padding_bit_len the bit length of the custom_padd
        */
      Keccak_Permutation(size_t capacity_bits, uint64_t custom_padding, uint8_t custom_padding_bit_len);

      size_t capacity() const { return m_capacity; }

      size_t bit_rate() const { return m_byterate * 8; }

      size_t byte_rate() const { return m_byterate; }

      void clear();
      std::string provider() const;

      /**
      * @brief Absorb input data into the Keccak sponge
      *
      * This method can be called multiple times with arbitrary-length buffers.
      *
      * @param input the input data
      */
      void absorb(std::span<const uint8_t> input);

      /**
      * @brief Expand output data from the current Keccak state
      *
      * This method can be called multiple times with arbitrary-length buffers.
      *
      * @param output the designated output memory
      */
      void squeeze(std::span<uint8_t> output);

      /**
      * @brief Add final padding (as provided in the constructor) and permute
      */
      void finish();

   private:
      void permute();

#if defined(BOTAN_HAS_KECCAK_PERM_BMI2)
      void permute_bmi2();
#endif

   private:
      const size_t m_capacity;
      const size_t m_byterate;
      const uint64_t m_custom_padding;
      const uint8_t m_custom_padding_bit_len;
      secure_vector<uint64_t> m_S;
      uint8_t m_S_inpos;
      uint8_t m_S_outpos;
};

}  // namespace Botan

namespace Botan {

/**
* Keccak[1600], the SHA-3 submission without any final bit padding. Not an official NIST SHA-3-derived hash function.
*
* In the terminology of the official SHA-3 specification [1],
* the instantiations of this hash function
* (with the output bit size in brackets) are given as
*
* Keccak1600[224](M) = KECCAK[448] (M, 224)
* Keccak1600[256](M) = KECCAK[512] (M, 256)
* Keccak1600[384](M) = KECCAK[768] (M, 384)
* Keccak1600[512](M) = KECCAK[1024] (M, 512)
*
* i.e., as raw Keccak[c] without any additional final bit padding.
*
* [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=28
*
*/
class Keccak_1600 final : public HashFunction {
   public:
      /**
      * @param output_bits the size of the hash output; must be one of
      *                    224, 256, 384, or 512
      */
      explicit Keccak_1600(size_t output_bits = 512);

      size_t hash_block_size() const override { return m_keccak.byte_rate(); }

      size_t output_length() const override { return m_output_length; }

      std::unique_ptr<HashFunction> new_object() const override;
      std::unique_ptr<HashFunction> copy_state() const override;
      std::string name() const override;
      void clear() override;
      std::string provider() const override;

   private:
      void add_data(std::span<const uint8_t> input) override;
      void final_result(std::span<uint8_t> out) override;

   private:
      Keccak_Permutation m_keccak;
      size_t m_output_length;
};

}  // namespace Botan


namespace Botan {

/**
 * Integer encoding defined in NIST SP.800-185 that can be unambiguously
 * parsed from the beginning of the string.
 *
 * This function does not allocate any memory and requires the caller to
 * provide a sufficiently large @p buffer. For a given @p x, this will
 * need exactly keccak_int_encoding_size() bytes. For an arbitrary @p x
 * it will generate keccak_max_int_encoding_size() bytes at most.
 *
 * @param buffer  buffer to write the left-encoding of @p x to.
 *                It is assumed that the buffer will hold at least
 *                keccak_int_encoding_size() bytes.
 * @param x       the integer to be left-encoded
 * @return        the byte span that represents the bytes written to @p buffer.
 */
BOTAN_TEST_API std::span<const uint8_t> keccak_int_left_encode(std::span<uint8_t> buffer, size_t x);

/**
 * Integer encoding defined in NIST SP.800-185 that can be unambiguously
 * parsed from the end of the string.
 *
 * This function does not allocate any memory and requires the caller to
 * provide a sufficiently large @p buffer. For a given @p x, this will
 * need exactly keccak_int_encoding_size() bytes. For an arbitrary @p x
 * it will generate keccak_max_int_encoding_size() bytes at most.
 *
 * @param out  buffer to write the right-encoding of @p x to.
 *             It is assumed that the buffer will hold at least
 *             keccak_int_encoding_size() bytes.
 * @param x    the integer to be right-encoded
 * @return     the byte span that represents the bytes written to @p buffer.
 */
BOTAN_TEST_API std::span<const uint8_t> keccak_int_right_encode(std::span<uint8_t> out, size_t x);

/**
 * @returns the required bytes for encodings of keccak_int_left_encode() or
 *          keccak_int_right_encode() given an integer @p x
 */
BOTAN_TEST_API size_t keccak_int_encoding_size(size_t x);

/**
 * @returns the maximum required bytes for encodings of keccak_int_left_encode() or
 *          keccak_int_right_encode()
 */
constexpr size_t keccak_max_int_encoding_size() {
   return sizeof(size_t) + 1 /* the length tag */;
}

template <typename T>
concept updatable_object = requires(T& a, std::span<const uint8_t> span) { a.update(span); };

template <typename T>
concept appendable_object = requires(T& a, std::span<const uint8_t> s) { a.insert(a.end(), s.begin(), s.end()); };

template <typename T>
concept absorbing_object = updatable_object<T> || appendable_object<T>;

/**
 * This is a combination of the functions encode_string() and bytepad() defined
 * in NIST SP.800-185 Section 2.3. Additionally, the result is directly streamed
 * into the provided XOF to avoid unneccessary memory allocation or a byte vector.
 *
 * @param sink         the XOF or byte vector to absorb the @p byte_strings into
 * @param padding_mod  the modulus value to create a padding for (NIST calls this 'w')
 * @param byte_strings a variable-length list of byte strings to be encoded and
 *                     absorbed into the given @p xof
 * @returns the number of bytes absorbed into the @p xof
 */
template <absorbing_object T, typename... Ts>
   requires(std::constructible_from<std::span<const uint8_t>, Ts> && ...)
size_t keccak_absorb_padded_strings_encoding(T& sink, size_t padding_mod, Ts... byte_strings) {
   BOTAN_ASSERT_NOMSG(padding_mod > 0);

   // used as temporary storage for all integer encodings in this function
   std::array<uint8_t, keccak_max_int_encoding_size()> int_encoding_buffer{};

   // absorbs byte strings and counts the number of absorbed bytes
   size_t bytes_absorbed = 0;
   auto absorb = [&](std::span<const uint8_t> bytes) {
      if constexpr(updatable_object<T>) {
         sink.update(bytes);
      } else if constexpr(appendable_object<T>) {
         sink.insert(sink.end(), bytes.begin(), bytes.end());
      }
      bytes_absorbed += bytes.size();
   };

   // encodes a given string and absorbs it into the XOF straight away
   auto encode_string_and_absorb = [&](std::span<const uint8_t> bytes) {
      absorb(keccak_int_left_encode(int_encoding_buffer, bytes.size() * 8));
      absorb(bytes);
   };

   // absorbs as many zero-bytes as requested into the XOF
   auto absorb_padding = [&](size_t padding_bytes) {
      for(size_t i = 0; i < padding_bytes; ++i) {
         const uint8_t zero_byte = 0;
         absorb({&zero_byte, 1});
      }
   };

   // implementation of bytepad(encode_string(Ts) || ...) that absorbs the result
   // staight into the given xof
   absorb(keccak_int_left_encode(int_encoding_buffer, padding_mod));
   (encode_string_and_absorb(byte_strings), ...);
   absorb_padding(padding_mod - (bytes_absorbed % padding_mod));

   return bytes_absorbed;
}

}  // namespace Botan

namespace Botan {

/**
* Bit rotation left by a compile-time constant amount
* @param input the input word
* @return input rotated left by ROT bits
*/
template <size_t ROT, std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T rotl(T input)
   requires(ROT > 0 && ROT < 8 * sizeof(T))
{
   return static_cast<T>((input << ROT) | (input >> (8 * sizeof(T) - ROT)));
}

/**
* Bit rotation right by a compile-time constant amount
* @param input the input word
* @return input rotated right by ROT bits
*/
template <size_t ROT, std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T rotr(T input)
   requires(ROT > 0 && ROT < 8 * sizeof(T))
{
   return static_cast<T>((input >> ROT) | (input << (8 * sizeof(T) - ROT)));
}

/**
* SHA-2 Sigma style function
*/
template <size_t R1, size_t R2, size_t S, std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T sigma(T x) {
   return rotr<R1>(x) ^ rotr<R2>(x) ^ (x >> S);
}

/**
* SHA-2 Sigma style function
*/
template <size_t R1, size_t R2, size_t R3, std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T rho(T x) {
   return rotr<R1>(x) ^ rotr<R2>(x) ^ rotr<R3>(x);
}

/**
* Bit rotation left, variable rotation amount
* @param input the input word
* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1
* @return input rotated left by rot bits
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T rotl_var(T input, size_t rot) {
   return rot ? static_cast<T>((input << rot) | (input >> (sizeof(T) * 8 - rot))) : input;
}

/**
* Bit rotation right, variable rotation amount
* @param input the input word
* @param rot the number of bits to rotate, must be between 0 and sizeof(T)*8-1
* @return input rotated right by rot bits
*/
template <std::unsigned_integral T>
BOTAN_FORCE_INLINE constexpr T rotr_var(T input, size_t rot) {
   return rot ? static_cast<T>((input >> rot) | (input << (sizeof(T) * 8 - rot))) : input;
}

}  // namespace Botan

namespace Botan {

inline void Keccak_Permutation_round(uint64_t T[25], const uint64_t A[25], uint64_t RC) {
   const uint64_t C0 = A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20];
   const uint64_t C1 = A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21];
   const uint64_t C2 = A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22];
   const uint64_t C3 = A[3] ^ A[8] ^ A[13] ^ A[18] ^ A[23];
   const uint64_t C4 = A[4] ^ A[9] ^ A[14] ^ A[19] ^ A[24];

   const uint64_t D0 = rotl<1>(C0) ^ C3;
   const uint64_t D1 = rotl<1>(C1) ^ C4;
   const uint64_t D2 = rotl<1>(C2) ^ C0;
   const uint64_t D3 = rotl<1>(C3) ^ C1;
   const uint64_t D4 = rotl<1>(C4) ^ C2;

   const uint64_t B00 = A[0] ^ D1;
   const uint64_t B01 = rotl<44>(A[6] ^ D2);
   const uint64_t B02 = rotl<43>(A[12] ^ D3);
   const uint64_t B03 = rotl<21>(A[18] ^ D4);
   const uint64_t B04 = rotl<14>(A[24] ^ D0);
   T[0] = B00 ^ (~B01 & B02) ^ RC;
   T[1] = B01 ^ (~B02 & B03);
   T[2] = B02 ^ (~B03 & B04);
   T[3] = B03 ^ (~B04 & B00);
   T[4] = B04 ^ (~B00 & B01);

   const uint64_t B05 = rotl<28>(A[3] ^ D4);
   const uint64_t B06 = rotl<20>(A[9] ^ D0);
   const uint64_t B07 = rotl<3>(A[10] ^ D1);
   const uint64_t B08 = rotl<45>(A[16] ^ D2);
   const uint64_t B09 = rotl<61>(A[22] ^ D3);
   T[5] = B05 ^ (~B06 & B07);
   T[6] = B06 ^ (~B07 & B08);
   T[7] = B07 ^ (~B08 & B09);
   T[8] = B08 ^ (~B09 & B05);
   T[9] = B09 ^ (~B05 & B06);

   const uint64_t B10 = rotl<1>(A[1] ^ D2);
   const uint64_t B11 = rotl<6>(A[7] ^ D3);
   const uint64_t B12 = rotl<25>(A[13] ^ D4);
   const uint64_t B13 = rotl<8>(A[19] ^ D0);
   const uint64_t B14 = rotl<18>(A[20] ^ D1);
   T[10] = B10 ^ (~B11 & B12);
   T[11] = B11 ^ (~B12 & B13);
   T[12] = B12 ^ (~B13 & B14);
   T[13] = B13 ^ (~B14 & B10);
   T[14] = B14 ^ (~B10 & B11);

   const uint64_t B15 = rotl<27>(A[4] ^ D0);
   const uint64_t B16 = rotl<36>(A[5] ^ D1);
   const uint64_t B17 = rotl<10>(A[11] ^ D2);
   const uint64_t B18 = rotl<15>(A[17] ^ D3);
   const uint64_t B19 = rotl<56>(A[23] ^ D4);
   T[15] = B15 ^ (~B16 & B17);
   T[16] = B16 ^ (~B17 & B18);
   T[17] = B17 ^ (~B18 & B19);
   T[18] = B18 ^ (~B19 & B15);
   T[19] = B19 ^ (~B15 & B16);

   const uint64_t B20 = rotl<62>(A[2] ^ D3);
   const uint64_t B21 = rotl<55>(A[8] ^ D4);
   const uint64_t B22 = rotl<39>(A[14] ^ D0);
   const uint64_t B23 = rotl<41>(A[15] ^ D1);
   const uint64_t B24 = rotl<2>(A[21] ^ D2);
   T[20] = B20 ^ (~B21 & B22);
   T[21] = B21 ^ (~B22 & B23);
   T[22] = B22 ^ (~B23 & B24);
   T[23] = B23 ^ (~B24 & B20);
   T[24] = B24 ^ (~B20 & B21);
}

}  // namespace Botan


namespace Botan {

enum class MD_Endian : uint8_t {
   Little,
   Big,
};

template <typename T>
concept md_hash_implementation =
   concepts::contiguous_container<typename T::digest_type> &&
   requires(typename T::digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
      { T::init(digest) } -> std::same_as<void>;
      { T::compress_n(digest, input, blocks) } -> std::same_as<void>;
      T::bit_endianness;
      T::byte_endianness;
      T::block_bytes;
      T::output_bytes;
      T::ctr_bytes;
   } && T::block_bytes >= 64 && is_power_of_2(T::block_bytes) && T::output_bytes >= 16 && T::ctr_bytes >= 8 &&
   is_power_of_2(T::ctr_bytes) && T::ctr_bytes < T::block_bytes;

template <md_hash_implementation MD>
class MerkleDamgard_Hash final {
   public:
      MerkleDamgard_Hash() { clear(); }

      void update(std::span<const uint8_t> input) {
         BufferSlicer in(input);

         while(!in.empty()) {
            if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
               MD::compress_n(m_digest, one_block.value(), 1);
            }

            if(m_buffer.in_alignment()) {
               const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
               if(full_blocks > 0) {
                  MD::compress_n(m_digest, aligned_data, full_blocks);
               }
            }
         }

         m_count += input.size();
      }

      void final(std::span<uint8_t> output) {
         append_padding_bit();
         append_counter_and_finalize();
         copy_output(output);
         clear();
      }

      void clear() {
         MD::init(m_digest);
         m_buffer.clear();
         m_count = 0;
      }

   private:
      void append_padding_bit() {
         BOTAN_ASSERT_NOMSG(!m_buffer.ready_to_consume());
         if constexpr(MD::bit_endianness == MD_Endian::Big) {
            const uint8_t final_byte = 0x80;
            m_buffer.append({&final_byte, 1});
         } else {
            const uint8_t final_byte = 0x01;
            m_buffer.append({&final_byte, 1});
         }
      }

      void append_counter_and_finalize() {
         // Compress the remaining data if the final data block does not provide
         // enough space for the counter bytes.
         if(m_buffer.elements_until_alignment() < MD::ctr_bytes) {
            m_buffer.fill_up_with_zeros();
            MD::compress_n(m_digest, m_buffer.consume(), 1);
         }

         // Make sure that any remaining bytes in the very last block are zero.
         BOTAN_ASSERT_NOMSG(m_buffer.elements_until_alignment() >= MD::ctr_bytes);
         m_buffer.fill_up_with_zeros();

         // Replace a bunch of the right-most zero-padding with the counter bytes.
         const uint64_t bit_count = m_count * 8;
         auto last_bytes = m_buffer.directly_modify_last(sizeof(bit_count));
         if constexpr(MD::byte_endianness == MD_Endian::Big) {
            store_be(bit_count, last_bytes.data());
         } else {
            store_le(bit_count, last_bytes.data());
         }

         // Compress the very last block.
         MD::compress_n(m_digest, m_buffer.consume(), 1);
      }

      void copy_output(std::span<uint8_t> output) {
         BOTAN_ASSERT_NOMSG(output.size() >= MD::output_bytes);

         if constexpr(MD::byte_endianness == MD_Endian::Big) {
            copy_out_be(output.first(MD::output_bytes), m_digest);
         } else {
            copy_out_le(output.first(MD::output_bytes), m_digest);
         }
      }

   private:
      typename MD::digest_type m_digest;
      uint64_t m_count = 0;

      AlignmentBuffer<uint8_t, MD::block_bytes> m_buffer;
};

}  // namespace Botan

namespace Botan {

/**
* Return true if any of the provided arguments are null
*/
template <typename... Ptrs>
bool any_null_pointers(Ptrs... ptr) {
   static_assert((... && std::is_pointer_v<Ptrs>), "All arguments must be pointers");
   return (... || (ptr == nullptr));
}

inline std::span<const uint8_t> as_span_of_bytes(const char* s, size_t len) {
   const uint8_t* b = reinterpret_cast<const uint8_t*>(s);
   return std::span{b, len};
}

inline std::span<const uint8_t> as_span_of_bytes(const std::string& s) {
   return as_span_of_bytes(s.data(), s.size());
}

inline std::span<const uint8_t> as_span_of_bytes(std::string_view s) {
   return as_span_of_bytes(s.data(), s.size());
}

inline std::span<const uint8_t> cstr_as_span_of_bytes(const char* s) {
   return as_span_of_bytes(s, std::strlen(s));
}

inline std::string bytes_to_string(std::span<const uint8_t> bytes) {
   return std::string(reinterpret_cast<const char*>(bytes.data()), bytes.size());
}

}  // namespace Botan

namespace Botan {

/**
* Compute the inverse of x modulo some integer m
*
* Returns nullopt if no such integer exists eg if gcd(x, m) > 1
*
* This algorithm is const time with respect to x, aside from its
* length. It also avoids leaking information about the modulus m,
* except that it does leak which of 3 categories the modulus is in:
*
*  - An odd integer
*  - A power of 2
*  - Some even number not a power of 2
*
* And if the modulus is even, it leaks the power of 2 which divides
* the modulus.
*
* @param x a positive integer less than m
* @param m a positive integer
*
* Throws Invalid_Argument if x or m are negative
*/
std::optional<BigInt> BOTAN_TEST_API inverse_mod_general(const BigInt& x, const BigInt& m);

/**
* Compute the inverse of x modulo a secret prime p
*
* This algorithm is constant time with respect to x and p, aside from
* leaking the length of p. (In particular it should not leak the
* length of x, if x is shorter)
*
* @param x a positive integer less than p
* @param p an odd prime
* @return y such that (x*y) % p == 1
*
* This always returns a result since any integer in [1,p)
* has an inverse modulo a prime p.
*
* This function assumes as a precondition that p truly is prime; the
* results may not be correct if this does not hold.
*
* Throws Invalid_Argument if x is less than or equal to zero,
* or if p is even or less than 3.
*/
BigInt BOTAN_TEST_API inverse_mod_secret_prime(const BigInt& x, const BigInt& p);

/**
* Compute the inverse of x modulo a public prime p
*
* This algorithm is constant time with respect to x. The prime
* p is assumed to be public.
*
* @param x a positive integer less than p
* @param p an odd prime
* @return y such that (x*y) % p == 1
*
* This always returns a result since any integer in [1,p)
* has an inverse modulo a prime p.
*
* This function assumes as a precondition that p truly is prime; the
* results may not be correct if this does not hold.
*
* Throws Invalid_Argument if x is less than or equal to zero,
* or if p is even or less than 3.
*/
BigInt BOTAN_TEST_API inverse_mod_public_prime(const BigInt& x, const BigInt& p);

/**
* Compute the inverse of x modulo a public RSA modulus n
*
* This algorithm is constant time with respect to x. The RSA
* modulus is assumed to be public.
*
* @param x a positive integer less than n
* @param n a RSA public modulus
* @return y such that (x*y) % n == 1
*
* This always returns a result since any integer in [1,n) has an inverse modulo
* a RSA public modulus n, unless you have happened to guess one of the factors
* at random. In the unlikely event of this occuring, Internal_Error will be thrown.
*/
BigInt inverse_mod_rsa_public_modulus(const BigInt& x, const BigInt& n);

/**
* Compute the RSA private exponent d
*
* This algorithm is constant time with respect to phi_n, p, and q,
* aside from leaking their lengths. It may leak the public exponent e.
*
* @param e the public exponent
* @param phi_n is lcm(p-1, q-1)
* @param p is the first secret prime
* @param q is the second secret prime
* @return d inverse of e modulo phi_n
*/
BigInt BOTAN_TEST_API compute_rsa_secret_exponent(const BigInt& e,
                                                  const BigInt& phi_n,
                                                  const BigInt& p,
                                                  const BigInt& q);

}  // namespace Botan


namespace Botan {

class Barrett_Reduction;

/**
* Parameters for Montgomery Reduction
*/
class BOTAN_TEST_API Montgomery_Params final {
   public:
      /**
      * Initialize a set of Montgomery reduction parameters. These values
      * can be shared by all values in a specific Montgomery domain.
      */
      Montgomery_Params(const BigInt& p, const Barrett_Reduction& mod_p);

      /**
      * Initialize a set of Montgomery reduction parameters. These values
      * can be shared by all values in a specific Montgomery domain.
      */
      explicit Montgomery_Params(const BigInt& p);

      bool operator==(const Montgomery_Params& other) const;

      bool operator!=(const Montgomery_Params& other) const { return !((*this) == other); }

      const BigInt& p() const { return m_data->p(); }

      const BigInt& R1() const { return m_data->r1(); }

      const BigInt& R2() const { return m_data->r2(); }

      const BigInt& R3() const { return m_data->r3(); }

      word p_dash() const { return m_data->p_dash(); }

      size_t p_words() const { return m_data->p_size(); }

      BigInt redc(const BigInt& x, secure_vector<word>& ws) const;

      void mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const;

      void mul(BigInt& z, const BigInt& x, std::span<const word> y, secure_vector<word>& ws) const;

      BigInt mul(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const;

      void mul_by(BigInt& x, const BigInt& y, secure_vector<word>& ws) const;

      BigInt sqr(const BigInt& x, secure_vector<word>& ws) const;

      void sqr(BigInt& z, const BigInt& x, secure_vector<word>& ws) const;

      void sqr(BigInt& z, std::span<const word> x, secure_vector<word>& ws) const;

   private:
      BigInt sqr(std::span<const word> x, secure_vector<word>& ws) const;

      class Data final {
         public:
            Data(const BigInt& p, const Barrett_Reduction& mod_p);

            const BigInt& p() const { return m_p; }

            const BigInt& r1() const { return m_r1; }

            const BigInt& r2() const { return m_r2; }

            const BigInt& r3() const { return m_r3; }

            word p_dash() const { return m_p_dash; }

            size_t p_size() const { return m_p_words; }

         private:
            BigInt m_p;
            BigInt m_r1;
            BigInt m_r2;
            BigInt m_r3;
            word m_p_dash;
            size_t m_p_words;
      };

      std::shared_ptr<const Data> m_data;
};

/**
* The Montgomery representation of an integer
*/
class BOTAN_TEST_API Montgomery_Int final {
   public:
      /**
      * Create a zero-initialized Montgomery_Int
      */
      explicit Montgomery_Int(const Montgomery_Params& params) : m_params(params) {}

      /**
      * Create a Montgomery_Int from a BigInt
      */
      Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed = true);

      /**
      * Create a Montgomery_Int
      *
      * The span must be exactly p_words long and encoding a value less than p already
      * in Montgomery form
      */
      Montgomery_Int(const Montgomery_Params& params, std::span<const word> words);

      /**
      * Return the value 1 in Montgomery form
      */
      static Montgomery_Int one(const Montgomery_Params& params);

      /**
      * Wide reduction - input can be at most 2*bytes long
      */
      static Montgomery_Int from_wide_int(const Montgomery_Params& params, const BigInt& x);

      std::vector<uint8_t> serialize() const;

      /**
      * Return the value to normal mod-p space
      */
      BigInt value() const;

      /**
      * Return the Montgomery representation
      */
      const secure_vector<word>& repr() const { return m_v; }

      Montgomery_Int operator+(const Montgomery_Int& other) const;

      Montgomery_Int operator-(const Montgomery_Int& other) const;

      Montgomery_Int mul(const Montgomery_Int& other, secure_vector<word>& ws) const;

      Montgomery_Int& mul_by(const Montgomery_Int& other, secure_vector<word>& ws);

      Montgomery_Int& mul_by(std::span<const word> other, secure_vector<word>& ws);

      Montgomery_Int square(secure_vector<word>& ws) const;

      Montgomery_Int& square_this_n_times(secure_vector<word>& ws, size_t n);

      void _const_time_poison() const { CT::poison(m_v); }

      void _const_time_unpoison() const { CT::unpoison(m_v); }

      const Montgomery_Params& _params() const { return m_params; }

   private:
      Montgomery_Int(const Montgomery_Params& params, secure_vector<word> words);

      Montgomery_Params m_params;
      secure_vector<word> m_v;
};

}  // namespace Botan

namespace Botan {

class BigInt;
class Barrett_Reduction;
class Montgomery_Exponentation_State;

/*
* Precompute for calculating values g^x mod p
*/
std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(const Montgomery_Params& params_p,
                                                                       const BigInt& g,
                                                                       size_t window_bits,
                                                                       bool const_time = true);

/*
* Precompute for calculating values g^x mod p
*/
std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(const Montgomery_Int& g,
                                                                       size_t window_bits,
                                                                       bool const_time = true);

/*
* Return g^k mod p
*/
Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state,
                             const BigInt& k,
                             size_t max_k_bits);

/*
* Return g^k mod p taking variable time depending on k
* @warning only use this if k is public
*/
Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k);

inline Montgomery_Int monty_exp(const Montgomery_Params& params_p,
                                const BigInt& g,
                                const BigInt& k,
                                size_t max_k_bits) {
   auto precomputed = monty_precompute(params_p, g, 4, true);
   return monty_execute(*precomputed, k, max_k_bits);
}

inline Montgomery_Int monty_exp_vartime(const Montgomery_Params& params_p, const BigInt& g, const BigInt& k) {
   auto precomputed = monty_precompute(params_p, g, 4, false);
   return monty_execute_vartime(*precomputed, k);
}

/**
* Return (x^z1 * y^z2) % p
*/
Montgomery_Int monty_multi_exp(
   const Montgomery_Params& params_p, const BigInt& x, const BigInt& z1, const BigInt& y, const BigInt& z2);

}  // namespace Botan

#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
#endif

namespace Botan {

// NOLINTBEGIN(*-macro-usage)

#if defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_X86_64)
   #define BOTAN_MP_USE_X86_64_ASM
#endif

/*
* Expressing an add with carry is sadly quite difficult in standard C/C++.
*
* Compilers will recognize various idioms and generate a reasonable carry
* chain. Unfortunately which idioms the compiler will understand vary, so we
* have to decide what to do based on the compiler. This is fragile; what will
* work varies not just based on compiler but also version, target architecture,
* and optimization flags.
*/
#if defined(__clang__)
static constexpr bool use_dword_for_word_add = false;
#else
static constexpr bool use_dword_for_word_add = true;
#endif

/*
* Concept for allowed multiprecision word types
*/
template <typename T>
concept WordType = (std::same_as<T, uint32_t> || std::same_as<T, uint64_t>);

template <WordType W>
struct WordInfo {};

template <>
struct WordInfo<uint32_t> {
   public:
      static const constexpr size_t bytes = 4;
      static const constexpr size_t bits = 32;
      static const constexpr uint32_t max = 0xFFFFFFFF;
      static const constexpr uint32_t top_bit = 0x80000000;

      typedef uint64_t dword;
      static const constexpr bool dword_is_native = true;
};

template <>
struct WordInfo<uint64_t> {
   public:
      static const constexpr size_t bytes = 8;
      static const constexpr size_t bits = 64;
      static const constexpr uint64_t max = 0xFFFFFFFFFFFFFFFF;
      static const constexpr uint64_t top_bit = 0x8000000000000000;

#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
      typedef uint128_t dword;
      static const constexpr bool dword_is_native = true;
#else
      typedef donna128 dword;
      static const constexpr bool dword_is_native = false;
#endif
};

/*
* Word Multiply/Add
*/
template <WordType W>
inline constexpr auto word_madd2(W a, W b, W* c) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm(R"(
         mulq %[b]
         addq %[c],%[a]
         adcq $0,%[carry]
         )"
          : [a] "=a"(a), [b] "=rm"(b), [carry] "=&d"(*c)
          : "0"(a), "1"(b), [c] "g"(*c)
          : "cc");

      return a;
   }
#endif

   typedef typename WordInfo<W>::dword dword;
   const dword s = dword(a) * b + *c;
   *c = static_cast<W>(s >> WordInfo<W>::bits);
   return static_cast<W>(s);
}

/*
* Word Multiply/Add
*/
template <WordType W>
inline constexpr auto word_madd3(W a, W b, W c, W* d) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm(R"(
         mulq %[b]

         addq %[c],%[a]
         adcq $0,%[carry]

         addq %[d],%[a]
         adcq $0,%[carry]
         )"
          : [a] "=a"(a), [b] "=rm"(b), [carry] "=&d"(*d)
          : "0"(a), "1"(b), [c] "g"(c), [d] "g"(*d)
          : "cc");

      return a;
   }
#endif

   typedef typename WordInfo<W>::dword dword;
   const dword s = dword(a) * b + c + *d;
   *d = static_cast<W>(s >> WordInfo<W>::bits);
   return static_cast<W>(s);
}

#if defined(BOTAN_MP_USE_X86_64_ASM)

   #define ASM(x) x "\n\t"

   #define DO_8_TIMES(MACRO, ARG) \
      MACRO(ARG, 0)               \
      MACRO(ARG, 1)               \
      MACRO(ARG, 2)               \
      MACRO(ARG, 3)               \
      MACRO(ARG, 4)               \
      MACRO(ARG, 5)               \
      MACRO(ARG, 6)               \
      MACRO(ARG, 7)

   #define ADDSUB2_OP(OPERATION, INDEX)        \
      ASM("movq 8*" #INDEX "(%[y]), %[carry]") \
      ASM(OPERATION " %[carry], 8*" #INDEX "(%[x])")

   #define ADDSUB3_OP(OPERATION, INDEX)              \
      ASM("movq 8*" #INDEX "(%[x]), %[carry]")       \
      ASM(OPERATION " 8*" #INDEX "(%[y]), %[carry]") \
      ASM("movq %[carry], 8*" #INDEX "(%[z])")

   #define LINMUL_OP(WRITE_TO, INDEX)      \
      ASM("movq 8*" #INDEX "(%[x]),%%rax") \
      ASM("mulq %[y]")                     \
      ASM("addq %[carry],%%rax")           \
      ASM("adcq $0,%%rdx")                 \
      ASM("movq %%rdx,%[carry]")           \
      ASM("movq %%rax, 8*" #INDEX "(%[" WRITE_TO "])")

   #define MULADD_OP(IGNORED, INDEX)       \
      ASM("movq 8*" #INDEX "(%[x]),%%rax") \
      ASM("mulq %[y]")                     \
      ASM("addq %[carry],%%rax")           \
      ASM("adcq $0,%%rdx")                 \
      ASM("addq 8*" #INDEX "(%[z]),%%rax") \
      ASM("adcq $0,%%rdx")                 \
      ASM("movq %%rdx,%[carry]")           \
      ASM("movq %%rax, 8*" #INDEX " (%[z])")

   #define ADD_OR_SUBTRACT(CORE_CODE) \
      ASM("rorq %[carry]")            \
      CORE_CODE                       \
      ASM("sbbq %[carry],%[carry]")   \
      ASM("negq %[carry]")

#endif

/*
* Word Addition
*/
template <WordType W>
inline constexpr auto word_add(W x, W y, W* carry) -> W {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_addc)
   if(!std::is_constant_evaluated()) {
      if constexpr(std::same_as<W, unsigned int>) {
         return __builtin_addc(x, y, *carry & 1, carry);
      } else if constexpr(std::same_as<W, unsigned long>) {
         return __builtin_addcl(x, y, *carry & 1, carry);
      } else if constexpr(std::same_as<W, unsigned long long>) {
         return __builtin_addcll(x, y, *carry & 1, carry);
      }
   }
#endif

   if constexpr(WordInfo<W>::dword_is_native && use_dword_for_word_add) {
      /*
      TODO(Botan4) this is largely a performance hack for GCCs that don't
      support __builtin_addc, if we increase the minimum supported version of
      GCC to GCC 14 then we can remove this and not worry about it
      */
      const W cb = *carry & 1;
      const auto s = typename WordInfo<W>::dword(x) + y + cb;
      *carry = static_cast<W>(s >> WordInfo<W>::bits);
      return static_cast<W>(s);
   } else {
      const W cb = *carry & 1;
      W z = x + y;
      W c1 = (z < x);
      z += cb;
      *carry = c1 | (z < cb);
      return z;
   }
}

/*
* Eight Word Block Addition, Two Argument
*/
template <WordType W>
inline constexpr auto word8_add2(W x[8], const W y[8], W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm volatile(ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "adcq"))
                   : [carry] "=r"(carry)
                   : [x] "r"(x), [y] "r"(y), "0"(carry)
                   : "cc", "memory");
      return carry;
   }
#endif

   x[0] = word_add(x[0], y[0], &carry);
   x[1] = word_add(x[1], y[1], &carry);
   x[2] = word_add(x[2], y[2], &carry);
   x[3] = word_add(x[3], y[3], &carry);
   x[4] = word_add(x[4], y[4], &carry);
   x[5] = word_add(x[5], y[5], &carry);
   x[6] = word_add(x[6], y[6], &carry);
   x[7] = word_add(x[7], y[7], &carry);
   return carry;
}

/*
* Eight Word Block Addition, Three Argument
*/
template <WordType W>
inline constexpr auto word8_add3(W z[8], const W x[8], const W y[8], W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm volatile(ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "adcq"))
                   : [carry] "=r"(carry)
                   : [x] "r"(x), [y] "r"(y), [z] "r"(z), "0"(carry)
                   : "cc", "memory");
      return carry;
   }
#endif

   z[0] = word_add(x[0], y[0], &carry);
   z[1] = word_add(x[1], y[1], &carry);
   z[2] = word_add(x[2], y[2], &carry);
   z[3] = word_add(x[3], y[3], &carry);
   z[4] = word_add(x[4], y[4], &carry);
   z[5] = word_add(x[5], y[5], &carry);
   z[6] = word_add(x[6], y[6], &carry);
   z[7] = word_add(x[7], y[7], &carry);
   return carry;
}

/*
* Word Subtraction
*/
template <WordType W>
inline constexpr auto word_sub(W x, W y, W* carry) -> W {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_subc)
   if(!std::is_constant_evaluated()) {
      if constexpr(std::same_as<W, unsigned int>) {
         return __builtin_subc(x, y, *carry & 1, carry);
      } else if constexpr(std::same_as<W, unsigned long>) {
         return __builtin_subcl(x, y, *carry & 1, carry);
      } else if constexpr(std::same_as<W, unsigned long long>) {
         return __builtin_subcll(x, y, *carry & 1, carry);
      }
   }
#endif

   const W cb = *carry & 1;
   W t0 = x - y;
   W c1 = (t0 > x);
   W z = t0 - cb;
   *carry = c1 | (z > t0);
   return z;
}

/*
* Eight Word Block Subtraction, Two Argument
*/
template <WordType W>
inline constexpr auto word8_sub2(W x[8], const W y[8], W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm(ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "sbbq"))
          : [carry] "=r"(carry)
          : [x] "r"(x), [y] "r"(y), "0"(carry)
          : "cc", "memory");
      return carry;
   }
#endif

   x[0] = word_sub(x[0], y[0], &carry);
   x[1] = word_sub(x[1], y[1], &carry);
   x[2] = word_sub(x[2], y[2], &carry);
   x[3] = word_sub(x[3], y[3], &carry);
   x[4] = word_sub(x[4], y[4], &carry);
   x[5] = word_sub(x[5], y[5], &carry);
   x[6] = word_sub(x[6], y[6], &carry);
   x[7] = word_sub(x[7], y[7], &carry);
   return carry;
}

/*
* Eight Word Block Subtraction, Three Argument
*/
template <WordType W>
inline constexpr auto word8_sub3(W z[8], const W x[8], const W y[8], W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm volatile(ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbq"))
                   : [carry] "=r"(carry)
                   : [x] "r"(x), [y] "r"(y), [z] "r"(z), "0"(carry)
                   : "cc", "memory");
      return carry;
   }
#endif

   z[0] = word_sub(x[0], y[0], &carry);
   z[1] = word_sub(x[1], y[1], &carry);
   z[2] = word_sub(x[2], y[2], &carry);
   z[3] = word_sub(x[3], y[3], &carry);
   z[4] = word_sub(x[4], y[4], &carry);
   z[5] = word_sub(x[5], y[5], &carry);
   z[6] = word_sub(x[6], y[6], &carry);
   z[7] = word_sub(x[7], y[7], &carry);
   return carry;
}

/*
* Eight Word Block Linear Multiplication
*/
template <WordType W>
inline constexpr auto word8_linmul3(W z[8], const W x[8], W y, W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm(DO_8_TIMES(LINMUL_OP, "z")
          : [carry] "=r"(carry)
          : [z] "r"(z), [x] "r"(x), [y] "rm"(y), "0"(carry)
          : "cc", "%rax", "%rdx");
      return carry;
   }
#endif

   z[0] = word_madd2(x[0], y, &carry);
   z[1] = word_madd2(x[1], y, &carry);
   z[2] = word_madd2(x[2], y, &carry);
   z[3] = word_madd2(x[3], y, &carry);
   z[4] = word_madd2(x[4], y, &carry);
   z[5] = word_madd2(x[5], y, &carry);
   z[6] = word_madd2(x[6], y, &carry);
   z[7] = word_madd2(x[7], y, &carry);
   return carry;
}

/*
* Eight Word Block Multiply/Add
*/
template <WordType W>
inline constexpr auto word8_madd3(W z[8], const W x[8], W y, W carry) -> W {
#if defined(BOTAN_MP_USE_X86_64_ASM)
   if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
      asm(DO_8_TIMES(MULADD_OP, "")
          : [carry] "=r"(carry)
          : [z] "r"(z), [x] "r"(x), [y] "rm"(y), "0"(carry)
          : "cc", "%rax", "%rdx");
      return carry;
   }
#endif

   z[0] = word_madd3(x[0], y, z[0], &carry);
   z[1] = word_madd3(x[1], y, z[1], &carry);
   z[2] = word_madd3(x[2], y, z[2], &carry);
   z[3] = word_madd3(x[3], y, z[3], &carry);
   z[4] = word_madd3(x[4], y, z[4], &carry);
   z[5] = word_madd3(x[5], y, z[5], &carry);
   z[6] = word_madd3(x[6], y, z[6], &carry);
   z[7] = word_madd3(x[7], y, z[7], &carry);
   return carry;
}

/**
* Helper for 3-word accumulators
*
* A number of algorithms especially Comba multiplication and
* Montgomery reduction can take advantage of wide accumulators, which
* consume inputs via addition with outputs extracted from the low
* bits.
*/
template <WordType W>
class word3 final {
#if defined(__BITINT_MAXWIDTH__) && (__BITINT_MAXWIDTH__ >= 3 * 64)

   public:
      constexpr word3() : m_w(0) {}

      inline constexpr void mul(W x, W y) { m_w += static_cast<W3>(x) * y; }

      inline constexpr void mul_x2(W x, W y) { m_w += static_cast<W3>(x) * y * 2; }

      inline constexpr void add(W x) { m_w += x; }

      inline constexpr W extract() {
         W r = static_cast<W>(m_w);
         m_w >>= WordInfo<W>::bits;
         return r;
      }

      inline constexpr W monty_step(W p0, W p_dash) {
         const W w0 = static_cast<W>(m_w);
         const W r = w0 * p_dash;
         mul(r, p0);
         m_w >>= WordInfo<W>::bits;
         return r;
      }

      inline constexpr W monty_step_pdash1() {
         const W r = static_cast<W>(m_w);
         m_w >>= WordInfo<W>::bits;
         m_w += static_cast<W3>(r);
         return r;
      }

   private:
      __extension__ typedef unsigned _BitInt(WordInfo<W>::bits * 3) W3;
      W3 m_w;
#else

   public:
      constexpr word3() : m_w0(0), m_w1(0), m_w2(0) {}

      inline constexpr void mul(W x, W y) {
   #if defined(BOTAN_MP_USE_X86_64_ASM)
         if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
            W z0 = 0, z1 = 0;

            asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc");

            asm(R"(
                 addq %[z0],%[w0]
                 adcq %[z1],%[w1]
                 adcq $0,%[w2]
                )"
                : [w0] "=r"(m_w0), [w1] "=r"(m_w1), [w2] "=r"(m_w2)
                : [z0] "r"(z0), [z1] "r"(z1), "0"(m_w0), "1"(m_w1), "2"(m_w2)
                : "cc");
            return;
         }
   #endif

         typedef typename WordInfo<W>::dword dword;
         const dword s = dword(x) * y + m_w0;
         W carry = static_cast<W>(s >> WordInfo<W>::bits);
         m_w0 = static_cast<W>(s);
         m_w1 += carry;
         m_w2 += (m_w1 < carry);
      }

      inline constexpr void mul_x2(W x, W y) {
   #if defined(BOTAN_MP_USE_X86_64_ASM)
         if(std::same_as<W, uint64_t> && !std::is_constant_evaluated()) {
            W z0 = 0, z1 = 0;

            asm("mulq %[y]" : "=a"(z0), "=d"(z1) : "a"(x), [y] "rm"(y) : "cc");

            asm(R"(
                 addq %[z0],%[w0]
                 adcq %[z1],%[w1]
                 adcq $0,%[w2]

                 addq %[z0],%[w0]
                 adcq %[z1],%[w1]
                 adcq $0,%[w2]
                   )"
                : [w0] "=r"(m_w0), [w1] "=r"(m_w1), [w2] "=r"(m_w2)
                : [z0] "r"(z0), [z1] "r"(z1), "0"(m_w0), "1"(m_w1), "2"(m_w2)
                : "cc");
            return;
         }
   #endif

         W carry = 0;
         x = word_madd2(x, y, &carry);
         y = carry;

         carry = 0;
         m_w0 = word_add(m_w0, x, &carry);
         m_w1 = word_add(m_w1, y, &carry);
         m_w2 += carry;

         carry = 0;
         m_w0 = word_add(m_w0, x, &carry);
         m_w1 = word_add(m_w1, y, &carry);
         m_w2 += carry;
      }

      inline constexpr void add(W x) {
         constexpr W z = 0;

         W carry = 0;
         m_w0 = word_add(m_w0, x, &carry);
         m_w1 = word_add(m_w1, z, &carry);
         m_w2 += carry;
      }

      inline constexpr W extract() {
         W r = m_w0;
         m_w0 = m_w1;
         m_w1 = m_w2;
         m_w2 = 0;
         return r;
      }

      inline constexpr W monty_step(W p0, W p_dash) {
         W r = m_w0 * p_dash;
         mul(r, p0);
         m_w0 = m_w1;
         m_w1 = m_w2;
         m_w2 = 0;
         return r;
      }

      inline constexpr W monty_step_pdash1() {
         // If p_dash == 1 then p[0] = -1 and everything simplifies
         const W r = m_w0;
         m_w0 += m_w1;
         m_w1 = m_w2 + (m_w0 < m_w1);
         m_w2 = 0;
         return r;
      }

   private:
      W m_w0;
      W m_w1;
      W m_w2;
#endif
};

#if defined(ASM)
   #undef ASM
   #undef DO_8_TIMES
   #undef ADD_OR_SUBTRACT
   #undef ADDSUB2_OP
   #undef ADDSUB3_OP
   #undef LINMUL_OP
   #undef MULADD_OP
#endif

// NOLINTEND(*-macro-usage)

}  // namespace Botan

namespace Botan {

/*
* If cond == 0, does nothing.
* If cond > 0, swaps x[0:size] with y[0:size]
* Runs in constant time
*/
template <WordType W>
inline constexpr void bigint_cnd_swap(W cnd, W x[], W y[], size_t size) {
   const auto mask = CT::Mask<W>::expand(cnd);

   for(size_t i = 0; i != size; ++i) {
      const W a = x[i];
      const W b = y[i];
      x[i] = mask.select(b, a);
      y[i] = mask.select(a, b);
   }
}

/*
* If cond > 0 adds x[0:size] and y[0:size] and returns carry
* Runs in constant time
*/
template <WordType W>
inline constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size) {
   const auto mask = CT::Mask<W>::expand(cnd).value();

   W carry = 0;

   for(size_t i = 0; i != size; ++i) {
      x[i] = word_add(x[i], y[i] & mask, &carry);
   }

   return (mask & carry);
}

/*
* If cond > 0 subtracts y[0:size] from x[0:size] and returns borrow
* Runs in constant time
*/
template <WordType W>
inline constexpr auto bigint_cnd_sub(W cnd, W x[], const W y[], size_t size) -> W {
   const auto mask = CT::Mask<W>::expand(cnd).value();

   W carry = 0;

   for(size_t i = 0; i != size; ++i) {
      x[i] = word_sub(x[i], y[i] & mask, &carry);
   }

   return (mask & carry);
}

/*
* 2s complement absolute value
* If cond > 0 sets x to ~x + 1
* Runs in constant time
*/
template <WordType W>
inline constexpr void bigint_cnd_abs(W cnd, W x[], size_t size) {
   const auto mask = CT::Mask<W>::expand(cnd);

   W carry = mask.if_set_return(1);
   for(size_t i = 0; i != size; ++i) {
      const W z = word_add(~x[i], static_cast<W>(0), &carry);
      x[i] = mask.select(z, x[i]);
   }
}

/**
* Two operand addition with carry out
*/
template <WordType W>
inline constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
   W carry = 0;

   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");

   const size_t blocks = y_size - (y_size % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      carry = word8_add2(x + i, y + i, carry);
   }

   for(size_t i = blocks; i != y_size; ++i) {
      x[i] = word_add(x[i], y[i], &carry);
   }

   for(size_t i = y_size; i != x_size; ++i) {
      x[i] = word_add(x[i], static_cast<W>(0), &carry);
   }

   return carry;
}

/**
* Three operand addition with carry out
*/
template <WordType W>
inline constexpr auto bigint_add3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
   if(x_size < y_size) {
      return bigint_add3(z, y, y_size, x, x_size);
   }

   W carry = 0;

   const size_t blocks = y_size - (y_size % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      carry = word8_add3(z + i, x + i, y + i, carry);
   }

   for(size_t i = blocks; i != y_size; ++i) {
      z[i] = word_add(x[i], y[i], &carry);
   }

   for(size_t i = y_size; i != x_size; ++i) {
      z[i] = word_add(x[i], static_cast<W>(0), &carry);
   }

   return carry;
}

/**
* Two operand subtraction
*/
template <WordType W>
inline constexpr auto bigint_sub2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
   W borrow = 0;

   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");

   const size_t blocks = y_size - (y_size % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      borrow = word8_sub2(x + i, y + i, borrow);
   }

   for(size_t i = blocks; i != y_size; ++i) {
      x[i] = word_sub(x[i], y[i], &borrow);
   }

   for(size_t i = y_size; i != x_size; ++i) {
      x[i] = word_sub(x[i], static_cast<W>(0), &borrow);
   }

   return borrow;
}

/**
* Two operand subtraction, x = y - x; assumes y >= x
*/
template <WordType W>
inline constexpr void bigint_sub2_rev(W x[], const W y[], size_t y_size) {
   W borrow = 0;

   for(size_t i = 0; i != y_size; ++i) {
      x[i] = word_sub(y[i], x[i], &borrow);
   }

   BOTAN_ASSERT(borrow == 0, "y must be greater than x");
}

/**
* Three operand subtraction
*
* Expects that x_size >= y_size
*
* Writes to z[0:x_size] and returns borrow
*/
template <WordType W>
inline constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
   W borrow = 0;

   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");

   const size_t blocks = y_size - (y_size % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      borrow = word8_sub3(z + i, x + i, y + i, borrow);
   }

   for(size_t i = blocks; i != y_size; ++i) {
      z[i] = word_sub(x[i], y[i], &borrow);
   }

   for(size_t i = y_size; i != x_size; ++i) {
      z[i] = word_sub(x[i], static_cast<W>(0), &borrow);
   }

   return borrow;
}

/**
* Conditional subtraction for Montgomery reduction
*
* This function assumes that (x0 || x) is less than 2*p
*
* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
*
* If z would be positive, returns z[0:N]
* Otherwise returns original input x
*/
template <WordType W>
inline constexpr void bigint_monty_maybe_sub(size_t N, W z[], W x0, const W x[], const W p[]) {
   W borrow = 0;

   const size_t blocks = N - (N % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      borrow = word8_sub3(z + i, x + i, p + i, borrow);
   }

   for(size_t i = blocks; i != N; ++i) {
      z[i] = word_sub(x[i], p[i], &borrow);
   }

   borrow = (x0 - borrow) > x0;

   CT::conditional_assign_mem(borrow, z, x, N);
}

/**
* Conditional subtraction for Montgomery reduction
*
* This function assumes that (x0 || x) is less than 2*p
*
* Computes z[0:N] = (x0 || x[0:N]) - p[0:N]
*
* If z would be positive, returns z[0:N]
* Otherwise returns original input x
*/
template <size_t N, WordType W>
inline constexpr void bigint_monty_maybe_sub(W z[N], W x0, const W x[N], const W y[N]) {
   W borrow = 0;

   for(size_t i = 0; i != N; ++i) {
      z[i] = word_sub(x[i], y[i], &borrow);
   }

   borrow = (x0 - borrow) > x0;

   CT::conditional_assign_mem(borrow, z, x, N);
}

/**
* Return abs(x-y), ie if x >= y, then compute z = x - y
* Otherwise compute z = y - x
* No borrow is possible since the result is always >= 0
*
* Returns a Mask: |1| if x >= y or |0| if x < y
* @param z output array of at least N words
* @param x input array of N words
* @param y input array of N words
* @param N length of x and y
* @param ws array of at least 2*N words
*/
template <WordType W>
inline constexpr auto bigint_sub_abs(W z[], const W x[], const W y[], size_t N, W ws[]) -> CT::Mask<W> {
   // Subtract in both direction then conditional copy out the result

   W* ws0 = ws;
   W* ws1 = ws + N;

   W borrow0 = 0;
   W borrow1 = 0;

   const size_t blocks = N - (N % 8);

   for(size_t i = 0; i != blocks; i += 8) {
      borrow0 = word8_sub3(ws0 + i, x + i, y + i, borrow0);
      borrow1 = word8_sub3(ws1 + i, y + i, x + i, borrow1);
   }

   for(size_t i = blocks; i != N; ++i) {
      ws0[i] = word_sub(x[i], y[i], &borrow0);
      ws1[i] = word_sub(y[i], x[i], &borrow1);
   }

   return CT::conditional_copy_mem(borrow0, z, ws1, ws0, N);
}

/*
* Shift Operations
*/
template <WordType W>
inline constexpr void bigint_shl1(W x[], size_t x_size, size_t x_words, size_t shift) {
   const size_t word_shift = shift / WordInfo<W>::bits;
   const size_t bit_shift = shift % WordInfo<W>::bits;

   copy_mem(x + word_shift, x, x_words);
   clear_mem(x, word_shift);

   const auto carry_mask = CT::Mask<W>::expand(bit_shift);
   const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);

   W carry = 0;
   for(size_t i = word_shift; i != x_size; ++i) {
      const W w = x[i];
      x[i] = (w << bit_shift) | carry;
      carry = carry_mask.if_set_return(w >> carry_shift);
   }
}

template <WordType W>
inline constexpr void bigint_shr1(W x[], size_t x_size, size_t shift) {
   const size_t word_shift = shift / WordInfo<W>::bits;
   const size_t bit_shift = shift % WordInfo<W>::bits;

   const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0;

   if(top > 0) {
      copy_mem(x, x + word_shift, top);
   }
   clear_mem(x + top, std::min(word_shift, x_size));

   const auto carry_mask = CT::Mask<W>::expand(bit_shift);
   const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);

   W carry = 0;

   for(size_t i = 0; i != top; ++i) {
      const W w = x[top - i - 1];
      x[top - i - 1] = (w >> bit_shift) | carry;
      carry = carry_mask.if_set_return(w << carry_shift);
   }
}

template <WordType W>
inline constexpr void bigint_shl2(W y[], const W x[], size_t x_size, size_t shift) {
   const size_t word_shift = shift / WordInfo<W>::bits;
   const size_t bit_shift = shift % WordInfo<W>::bits;

   copy_mem(y + word_shift, x, x_size);

   const auto carry_mask = CT::Mask<W>::expand(bit_shift);
   const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);

   W carry = 0;
   for(size_t i = word_shift; i != x_size + word_shift + 1; ++i) {
      const W w = y[i];
      y[i] = (w << bit_shift) | carry;
      carry = carry_mask.if_set_return(w >> carry_shift);
   }
}

template <WordType W>
inline constexpr void bigint_shr2(W y[], const W x[], size_t x_size, size_t shift) {
   const size_t word_shift = shift / WordInfo<W>::bits;
   const size_t bit_shift = shift % WordInfo<W>::bits;
   const size_t new_size = x_size < word_shift ? 0 : (x_size - word_shift);

   if(new_size > 0) {
      copy_mem(y, x + word_shift, new_size);
   }

   const auto carry_mask = CT::Mask<W>::expand(bit_shift);
   const W carry_shift = carry_mask.if_set_return(WordInfo<W>::bits - bit_shift);

   W carry = 0;
   for(size_t i = new_size; i > 0; --i) {
      W w = y[i - 1];
      y[i - 1] = (w >> bit_shift) | carry;
      carry = carry_mask.if_set_return(w << carry_shift);
   }
}

/*
* Linear Multiply - returns the carry
*/
template <WordType W>
[[nodiscard]] inline constexpr auto bigint_linmul2(W x[], size_t x_size, W y) -> W {
   W carry = 0;

   for(size_t i = 0; i != x_size; ++i) {
      x[i] = word_madd2(x[i], y, &carry);
   }

   return carry;
}

template <WordType W>
inline constexpr void bigint_linmul3(W z[], const W x[], size_t x_size, W y) {
   const size_t blocks = x_size - (x_size % 8);

   W carry = 0;

   for(size_t i = 0; i != blocks; i += 8) {
      carry = word8_linmul3(z + i, x + i, y, carry);
   }

   for(size_t i = blocks; i != x_size; ++i) {
      z[i] = word_madd2(x[i], y, &carry);
   }

   z[x_size] = carry;
}

/**
* Compare x and y
* Return -1 if x < y
* Return 0 if x == y
* Return 1 if x > y
*/
template <WordType W>
inline constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size) {
   static_assert(sizeof(W) >= sizeof(uint32_t), "Size assumption");

   const W LT = static_cast<W>(-1);
   const W EQ = 0;
   const W GT = 1;

   const size_t common_elems = std::min(x_size, y_size);

   W result = EQ;  // until found otherwise

   for(size_t i = 0; i != common_elems; i++) {
      const auto is_eq = CT::Mask<W>::is_equal(x[i], y[i]);
      const auto is_lt = CT::Mask<W>::is_lt(x[i], y[i]);

      result = is_eq.select(result, is_lt.select(LT, GT));
   }

   if(x_size < y_size) {
      W mask = 0;
      for(size_t i = x_size; i != y_size; i++) {
         mask |= y[i];
      }

      // If any bits were set in high part of y, then x < y
      result = CT::Mask<W>::is_zero(mask).select(result, LT);
   } else if(y_size < x_size) {
      W mask = 0;
      for(size_t i = y_size; i != x_size; i++) {
         mask |= x[i];
      }

      // If any bits were set in high part of x, then x > y
      result = CT::Mask<W>::is_zero(mask).select(result, GT);
   }

   CT::unpoison(result);
   BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
   return static_cast<int32_t>(result);
}

/**
* Compare x and y
* Returns a Mask: |1| if x[0:x_size] < y[0:y_size] or |0| otherwise
* If lt_or_equal is true, returns |1| also for x == y
*/
template <WordType W>
inline constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal = false)
   -> CT::Mask<W> {
   const size_t common_elems = std::min(x_size, y_size);

   auto is_lt = CT::Mask<W>::expand(lt_or_equal);

   for(size_t i = 0; i != common_elems; i++) {
      const auto eq = CT::Mask<W>::is_equal(x[i], y[i]);
      const auto lt = CT::Mask<W>::is_lt(x[i], y[i]);
      is_lt = eq.select_mask(is_lt, lt);
   }

   if(x_size < y_size) {
      W mask = 0;
      for(size_t i = x_size; i != y_size; i++) {
         mask |= y[i];
      }
      // If any bits were set in high part of y, then is_lt should be forced true
      is_lt |= CT::Mask<W>::expand(mask);
   } else if(y_size < x_size) {
      W mask = 0;
      for(size_t i = y_size; i != x_size; i++) {
         mask |= x[i];
      }

      // If any bits were set in high part of x, then is_lt should be false
      is_lt &= CT::Mask<W>::is_zero(mask);
   }

   return is_lt;
}

template <WordType W>
inline constexpr auto bigint_ct_is_eq(const W x[], size_t x_size, const W y[], size_t y_size) -> CT::Mask<W> {
   const size_t common_elems = std::min(x_size, y_size);

   W diff = 0;

   for(size_t i = 0; i != common_elems; i++) {
      diff |= (x[i] ^ y[i]);
   }

   // If any bits were set in high part of x/y, then they are not equal
   if(x_size < y_size) {
      for(size_t i = x_size; i != y_size; i++) {
         diff |= y[i];
      }
   } else if(y_size < x_size) {
      for(size_t i = y_size; i != x_size; i++) {
         diff |= x[i];
      }
   }

   return CT::Mask<W>::is_zero(diff);
}

/**
* Compute ((n1<<bits) + n0) / d
*/
template <WordType W>
inline constexpr auto bigint_divop_vartime(W n1, W n0, W d) -> W {
   BOTAN_ARG_CHECK(d != 0, "Division by zero");

   if constexpr(WordInfo<W>::dword_is_native) {
      typename WordInfo<W>::dword n = n1;
      n <<= WordInfo<W>::bits;
      n |= n0;
      return static_cast<W>(n / d);
   } else {
      W high = n1 % d;
      W quotient = 0;

      for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
         const W high_top_bit = high >> (WordInfo<W>::bits - 1);

         high <<= 1;
         high |= (n0 >> (WordInfo<W>::bits - 1 - i)) & 1;
         quotient <<= 1;

         if(high_top_bit || high >= d) {
            high -= d;
            quotient |= 1;
         }
      }

      return quotient;
   }
}

/**
* Compute ((n1<<bits) + n0) % d
*/
template <WordType W>
inline constexpr auto bigint_modop_vartime(W n1, W n0, W d) -> W {
   BOTAN_ARG_CHECK(d != 0, "Division by zero");

   W z = bigint_divop_vartime(n1, n0, d);
   W carry = 0;
   z = word_madd2(z, d, &carry);
   return (n0 - z);
}

/*
* Compute an integer x such that (a*x) == -1 (mod 2^n)
*
* Throws an exception if input is even, since in that case no inverse
* exists. If input is odd, then input and 2^n are relatively prime and
* the inverse exists.
*/
template <WordType W>
inline constexpr auto monty_inverse(W a) -> W {
   BOTAN_ARG_CHECK(a % 2 == 1, "Cannot compute Montgomery inverse of an even integer");

   /*
   * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
   * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
   */

   W b = 1;
   W r = 0;

   for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
      const W bi = b % 2;
      r >>= 1;
      r += bi << (WordInfo<W>::bits - 1);

      b -= a * bi;
      b >>= 1;
   }

   // Now invert in addition space
   r = (WordInfo<W>::max - r) + 1;

   return r;
}

template <size_t S, WordType W, size_t N>
inline constexpr W shift_left(std::array<W, N>& x) {
   static_assert(S < WordInfo<W>::bits, "Shift too large");

   W carry = 0;
   for(size_t i = 0; i != N; ++i) {
      const W w = x[i];
      x[i] = (w << S) | carry;
      carry = w >> (WordInfo<W>::bits - S);
   }

   return carry;
}

template <size_t S, WordType W, size_t N>
inline constexpr W shift_right(std::array<W, N>& x) {
   static_assert(S < WordInfo<W>::bits, "Shift too large");

   W carry = 0;
   for(size_t i = 0; i != N; ++i) {
      const W w = x[N - 1 - i];
      x[N - 1 - i] = (w >> S) | carry;
      carry = w << (WordInfo<W>::bits - S);
   }

   return carry;
}

// Should be consteval but this triggers a bug in Clang 14
template <WordType W, size_t N>
constexpr auto hex_to_words(const char (&s)[N]) {
   // Char count includes null terminator which we ignore
   const constexpr size_t C = N - 1;

   // Number of nibbles that a word can hold
   const constexpr size_t NPW = (WordInfo<W>::bits / 4);

   // Round up to the next number of words that will fit the input
   const constexpr size_t S = (C + NPW - 1) / NPW;

   auto hex2int = [](char c) -> int8_t {
      if(c >= '0' && c <= '9') {
         return static_cast<int8_t>(c - '0');
      } else if(c >= 'a' && c <= 'f') {
         return static_cast<int8_t>(c - 'a' + 10);
      } else if(c >= 'A' && c <= 'F') {
         return static_cast<int8_t>(c - 'A' + 10);
      } else {
         return -1;
      }
   };

   std::array<W, S> r = {0};

   for(size_t i = 0; i != C; ++i) {
      const int8_t c = hex2int(s[i]);
      if(c >= 0) {
         shift_left<4>(r);
         r[0] += c;
      }
   }

   return r;
}

/*
* Comba Multiplication / Squaring
*/
BOTAN_FUZZER_API void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
BOTAN_FUZZER_API void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
BOTAN_FUZZER_API void bigint_comba_mul7(word z[14], const word x[7], const word y[7]);
BOTAN_FUZZER_API void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
BOTAN_FUZZER_API void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
BOTAN_FUZZER_API void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
BOTAN_FUZZER_API void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);

BOTAN_FUZZER_API void bigint_comba_sqr4(word out[8], const word in[4]);
BOTAN_FUZZER_API void bigint_comba_sqr6(word out[12], const word in[6]);
BOTAN_FUZZER_API void bigint_comba_sqr7(word out[14], const word x[7]);
BOTAN_FUZZER_API void bigint_comba_sqr8(word out[16], const word in[8]);
BOTAN_FUZZER_API void bigint_comba_sqr9(word out[18], const word in[9]);
BOTAN_FUZZER_API void bigint_comba_sqr16(word out[32], const word in[16]);
BOTAN_FUZZER_API void bigint_comba_sqr24(word out[48], const word in[24]);

/*
* Comba Fixed Length Multiplication
*/
template <size_t N, WordType W>
constexpr inline void comba_mul(W z[2 * N], const W x[N], const W y[N]) {
   if(!std::is_constant_evaluated()) {
      if constexpr(std::same_as<W, word> && N == 4) {
         return bigint_comba_mul4(z, x, y);
      }
      if constexpr(std::same_as<W, word> && N == 6) {
         return bigint_comba_mul6(z, x, y);
      }
      if constexpr(std::same_as<W, word> && N == 7) {
         return bigint_comba_mul7(z, x, y);
      }
      if constexpr(std::same_as<W, word> && N == 8) {
         return bigint_comba_mul8(z, x, y);
      }
      if constexpr(std::same_as<W, word> && N == 9) {
         return bigint_comba_mul9(z, x, y);
      }
      if constexpr(std::same_as<W, word> && N == 16) {
         return bigint_comba_mul16(z, x, y);
      }
   }

   word3<W> accum;

   for(size_t i = 0; i != 2 * N; ++i) {
      const size_t start = i + 1 < N ? 0 : i + 1 - N;
      const size_t end = std::min(N, i + 1);

      for(size_t j = start; j != end; ++j) {
         accum.mul(x[j], y[i - j]);
      }
      z[i] = accum.extract();
   }
}

template <size_t N, WordType W>
constexpr inline void comba_sqr(W z[2 * N], const W x[N]) {
   if(!std::is_constant_evaluated()) {
      if constexpr(std::same_as<W, word> && N == 4) {
         return bigint_comba_sqr4(z, x);
      }
      if constexpr(std::same_as<W, word> && N == 6) {
         return bigint_comba_sqr6(z, x);
      }
      if constexpr(std::same_as<W, word> && N == 7) {
         return bigint_comba_sqr7(z, x);
      }
      if constexpr(std::same_as<W, word> && N == 8) {
         return bigint_comba_sqr8(z, x);
      }
      if constexpr(std::same_as<W, word> && N == 9) {
         return bigint_comba_sqr9(z, x);
      }
      if constexpr(std::same_as<W, word> && N == 16) {
         return bigint_comba_sqr16(z, x);
      }
   }

   word3<W> accum;

   for(size_t i = 0; i != 2 * N; ++i) {
      const size_t start = i + 1 < N ? 0 : i + 1 - N;
      const size_t end = std::min(N, i + 1);

      for(size_t j = start; j != end; ++j) {
         accum.mul(x[j], x[i - j]);
      }
      z[i] = accum.extract();
   }
}

/*
* Montgomery reduction
*
* Sets r to the Montgomery reduction of z using parameters p / p_dash
*
* The workspace should be of size equal to the prime
*/
BOTAN_FUZZER_API void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]);
BOTAN_FUZZER_API void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]);
BOTAN_FUZZER_API void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]);
BOTAN_FUZZER_API void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]);
BOTAN_FUZZER_API void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]);
BOTAN_FUZZER_API void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]);
BOTAN_FUZZER_API void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]);

BOTAN_FUZZER_API
void bigint_monty_redc_generic(
   word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]);

/**
* Montgomery Reduction
* @param r result of exactly p_size words
* @param z integer to reduce, of size exactly 2*p_size.
* @param p modulus
* @param p_size size of p
* @param p_dash Montgomery value
* @param ws array of at least p_size words
* @param ws_size size of ws in words
*
* It is allowed to set &r[0] == &z[0] however in this case note that only the
* first p_size words of r will be written to and the high p_size words of r/z
* will still hold the original inputs, these must be cleared after use.
* See bigint_monty_redc_inplace
*/
inline void bigint_monty_redc(
   word r[], const word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
   const size_t z_size = 2 * p_size;

   BOTAN_ARG_CHECK(ws_size >= p_size, "Montgomery reduction workspace too small");

   if(p_size == 4) {
      bigint_monty_redc_4(r, z, p, p_dash, ws);
   } else if(p_size == 6) {
      bigint_monty_redc_6(r, z, p, p_dash, ws);
   } else if(p_size == 8) {
      bigint_monty_redc_8(r, z, p, p_dash, ws);
   } else if(p_size == 12) {
      bigint_monty_redc_12(r, z, p, p_dash, ws);
   } else if(p_size == 16) {
      bigint_monty_redc_16(r, z, p, p_dash, ws);
   } else if(p_size == 24) {
      bigint_monty_redc_24(r, z, p, p_dash, ws);
   } else if(p_size == 32) {
      bigint_monty_redc_32(r, z, p, p_dash, ws);
   } else {
      bigint_monty_redc_generic(r, z, z_size, p, p_size, p_dash, ws);
   }
}

inline void bigint_monty_redc_inplace(word z[], const word p[], size_t p_size, word p_dash, word ws[], size_t ws_size) {
   bigint_monty_redc(z, z, p, p_size, p_dash, ws, ws_size);
   clear_mem(z + p_size, p_size);
}

/**
* Basecase O(N^2) multiplication
*/
BOTAN_FUZZER_API
void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size);

/**
* Basecase O(N^2) squaring
*/
BOTAN_FUZZER_API
void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size);

/*
* High Level Multiplication/Squaring Interfaces
*/
void bigint_mul(word z[],
                size_t z_size,
                const word x[],
                size_t x_size,
                size_t x_sw,
                const word y[],
                size_t y_size,
                size_t y_sw,
                word workspace[],
                size_t ws_size);

void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size);

/**
* Reduce z modulo p = 2**B - C where C is small
*
* z is assumed to be at most (p-1)**2
*
* For details on the algorithm see
* - Handbook of Applied Cryptography, Algorithm 14.47
* - Guide to Elliptic Curve Cryptography, Algorithm 2.54 and Note 2.55
*
*/
template <WordType W, size_t N, W C>
constexpr std::array<W, N> redc_crandall(std::span<const W, 2 * N> z) {
   static_assert(N >= 2);

   std::array<W, N> hi = {};

   // hi = hi * c + lo

   W carry = 0;
   for(size_t i = 0; i != N; ++i) {
      hi[i] = word_madd3(z[i + N], C, z[i], &carry);
   }

   // hi += carry * C
   word carry_c[2] = {0};
   carry_c[0] = word_madd2(carry, C, &carry_c[1]);

   carry = bigint_add2(hi.data(), N, carry_c, 2);

   constexpr W P0 = WordInfo<W>::max - (C - 1);

   std::array<W, N> r = {};

   W borrow = 0;

   /*
   * For undetermined reasons, on GCC (only) removing this asm block causes
   * massive (up to 20%) performance regressions in secp256k1.
   *
   * The generated code without the asm seems quite reasonable, and timing
   * repeated calls to redc_crandall with the cycle counter show that GCC
   * computes it in about the same number of cycles with or without the asm.
   *
   * So the cause of the regression is unclear. But it is reproducible across
   * machines and GCC versions.
   */
#if defined(BOTAN_MP_USE_X86_64_ASM) && defined(__GNUC__) && !defined(__clang__)
   if constexpr(N == 4 && std::same_as<W, uint64_t>) {
      if(!std::is_constant_evaluated()) {
         asm volatile(R"(
                      movq 0(%[x]), %[borrow]
                      subq %[p0], %[borrow]
                      movq %[borrow], 0(%[r])
                      movq 16(%[x]), %[borrow]
                      sbbq $-1, %[borrow]
                      movq %[borrow], 8(%[r])
                      movq 16(%[x]), %[borrow]
                      sbbq $-1, %[borrow]
                      movq %[borrow], 16(%[r])
                      movq 24(%[x]), %[borrow]
                      sbbq $-1, %[borrow]
                      movq %[borrow], 24(%[r])
                      sbbq %[borrow],%[borrow]
                      negq %[borrow]
                      )"
                      : [borrow] "=r"(borrow)
                      : [x] "r"(hi.data()), [p0] "r"(P0), [r] "r"(r.data()), "0"(borrow)
                      : "cc", "memory");
      }

      borrow = (carry - borrow) > carry;
      CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
      return r;
   }
#endif

   r[0] = word_sub(hi[0], P0, &borrow);
   for(size_t i = 1; i != N; ++i) {
      r[i] = word_sub(hi[i], WordInfo<W>::max, &borrow);
   }

   borrow = (carry - borrow) > carry;

   CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);

   return r;
}

// Extract a WindowBits sized window out of s, depending on offset.
template <size_t WindowBits, typename W, size_t N>
constexpr size_t read_window_bits(std::span<const W, N> words, size_t offset) {
   static_assert(WindowBits >= 1 && WindowBits <= 7);

   constexpr uint8_t WindowMask = static_cast<uint8_t>(1 << WindowBits) - 1;

   constexpr size_t W_bits = sizeof(W) * 8;
   const auto bit_shift = offset % W_bits;
   const auto word_offset = words.size() - 1 - (offset / W_bits);

   const bool single_byte_window = bit_shift <= (W_bits - WindowBits) || word_offset == 0;

   const auto w0 = words[word_offset];

   if(single_byte_window) {
      return (w0 >> bit_shift) & WindowMask;
   } else {
      // Otherwise we must join two words and extract the result
      const auto w1 = words[word_offset - 1];
      const auto combined = ((w0 >> bit_shift) | (w1 << (W_bits - bit_shift)));
      return combined & WindowMask;
   }
}

}  // namespace Botan

namespace Botan {

class OID_Map final {
   public:
      void add_oid(const OID& oid, std::string_view str);

      // TODO(Botan4) remove this function when oids.h is removed
      void add_str2oid(const OID& oid, std::string_view str);

      // TODO(Botan4) remove this function when oids.h is removed
      void add_oid2str(const OID& oid, std::string_view str);

      std::string oid2str(const OID& oid);

      OID str2oid(std::string_view str);

      static OID_Map& global_registry();

   private:
      static std::optional<std::string_view> lookup_static_oid(const OID& oid);
      static std::optional<OID> lookup_static_oid_name(std::string_view name);

      static std::unordered_map<OID, std::string> load_oid2str_map();
      static std::unordered_map<std::string, OID> load_str2oid_map();

      OID_Map();

      mutex_type m_mutex;
      std::unordered_map<std::string, OID> m_str2oid;
      std::unordered_map<OID, std::string> m_oid2str;
};

}  // namespace Botan

namespace Botan {

/**
* Parse a SCAN-style algorithm name
* @param scan_name the name
* @return the name components
*/
std::vector<std::string> parse_algorithm_name(std::string_view scan_name);

/**
* Split a string
* @param str the input string
* @param delim the delimitor
* @return string split by delim
*/
BOTAN_TEST_API std::vector<std::string> split_on(std::string_view str, char delim);

/**
* Join a string
* @param strs strings to join
* @param delim the delimitor
* @return string joined by delim
*/
std::string string_join(const std::vector<std::string>& strs, char delim);

/**
* Convert a decimal string to a number
* @param str the string to convert
* @return number value of the string
*/
BOTAN_TEST_API uint32_t to_u32bit(std::string_view str);

/**
* Convert a decimal string to a number
* @param str the string to convert
* @return number value of the string
*/
uint16_t to_uint16(std::string_view str);

/**
* Convert a string representation of an IPv4 address to a number
* @param ip_str the string representation
* @return integer IPv4 address
*/
std::optional<uint32_t> BOTAN_TEST_API string_to_ipv4(std::string_view ip_str);

/**
* Convert an IPv4 address to a string
* @param ip_addr the IPv4 address to convert
* @return string representation of the IPv4 address
*/
std::string BOTAN_TEST_API ipv4_to_string(uint32_t ip_addr);

std::map<std::string, std::string> read_cfg(std::istream& is);

/**
* Accepts key value pairs deliminated by commas:
*
* "" (returns empty map)
* "K=V" (returns map {'K': 'V'})
* "K1=V1,K2=V2"
* "K1=V1,K2=V2,K3=V3"
* "K1=V1,K2=V2,K3=a_value\,with\,commas_and_\=equals"
*
* Values may be empty, keys must be non-empty and unique. Duplicate
* keys cause an exception.
*
* Within both key and value, comma and equals can be escaped with
* backslash. Backslash can also be escaped.
*/
BOTAN_TEST_API
std::map<std::string, std::string> read_kv(std::string_view kv);

std::string tolower_string(std::string_view s);

/**
* Check if the given hostname is a match for the specified wildcard
*/
BOTAN_TEST_API
bool host_wildcard_match(std::string_view wildcard, std::string_view host);

/**
* If name is a valid DNS name, return it canonicalized
*
* Otherwise throws Decoding_Error
*/
BOTAN_TEST_API std::string check_and_canonicalize_dns_name(std::string_view name);

}  // namespace Botan

namespace Botan {

class HashFunction;
class KDF;
class EME;

}  // namespace Botan

namespace Botan::PK_Ops {

// NOLINTBEGIN(*-special-member-functions)

class Encryption_with_EME : public Encryption {
   public:
      ~Encryption_with_EME() override;

      size_t max_input_bits() const override;

      std::vector<uint8_t> encrypt(std::span<const uint8_t> ptext, RandomNumberGenerator& rng) override;

   protected:
      explicit Encryption_with_EME(std::string_view eme);

   private:
      virtual size_t max_ptext_input_bits() const = 0;

      virtual std::vector<uint8_t> raw_encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) = 0;
      std::unique_ptr<EME> m_eme;
};

class Decryption_with_EME : public Decryption {
   public:
      ~Decryption_with_EME() override;

      secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) override;

   protected:
      explicit Decryption_with_EME(std::string_view eme);

   private:
      virtual secure_vector<uint8_t> raw_decrypt(std::span<const uint8_t> ctext) = 0;
      std::unique_ptr<EME> m_eme;
};

class Verification_with_Hash : public Verification {
   public:
      ~Verification_with_Hash() override;

      void update(std::span<const uint8_t> input) override;
      bool is_valid_signature(std::span<const uint8_t> sig) override;

      std::string hash_function() const final;

   protected:
      explicit Verification_with_Hash(std::string_view hash);

      explicit Verification_with_Hash(const AlgorithmIdentifier& alg_id,
                                      std::string_view pk_algo,
                                      bool allow_null_parameters = false);

      /**
      * Perform a signature check operation
      * @param msg the message
      * @param sig the signature
      * @returns if sig is a valid signature for msg
      */
      virtual bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) = 0;

   private:
      std::unique_ptr<HashFunction> m_hash;
};

class Signature_with_Hash : public Signature {
   public:
      void update(std::span<const uint8_t> input) override;

      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;

      ~Signature_with_Hash() override;

   protected:
      explicit Signature_with_Hash(std::string_view hash);

      std::string hash_function() const final;

#if defined(BOTAN_HAS_RFC6979_GENERATOR)
      std::string rfc6979_hash_function() const;
#endif

   private:
      virtual std::vector<uint8_t> raw_sign(std::span<const uint8_t> input, RandomNumberGenerator& rng) = 0;

      std::unique_ptr<HashFunction> m_hash;
};

class Key_Agreement_with_KDF : public Key_Agreement {
   public:
      secure_vector<uint8_t> agree(size_t key_len,
                                   std::span<const uint8_t> other_key,
                                   std::span<const uint8_t> salt) override;

      ~Key_Agreement_with_KDF() override;

   protected:
      explicit Key_Agreement_with_KDF(std::string_view kdf);

   private:
      virtual secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) = 0;
      std::unique_ptr<KDF> m_kdf;
};

class KEM_Encryption_with_KDF : public KEM_Encryption {
   public:
      void kem_encrypt(std::span<uint8_t> out_encapsulated_key,
                       std::span<uint8_t> out_shared_key,
                       RandomNumberGenerator& rng,
                       size_t desired_shared_key_len,
                       std::span<const uint8_t> salt) final;

      size_t shared_key_length(size_t desired_shared_key_len) const final;

      ~KEM_Encryption_with_KDF() override;

   protected:
      virtual void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
                                   std::span<uint8_t> out_raw_shared_key,
                                   RandomNumberGenerator& rng) = 0;

      virtual size_t raw_kem_shared_key_length() const = 0;

      explicit KEM_Encryption_with_KDF(std::string_view kdf);

   private:
      std::unique_ptr<KDF> m_kdf;
};

class KEM_Decryption_with_KDF : public KEM_Decryption {
   public:
      void kem_decrypt(std::span<uint8_t> out_shared_key,
                       std::span<const uint8_t> encapsulated_key,
                       size_t desired_shared_key_len,
                       std::span<const uint8_t> salt) final;

      size_t shared_key_length(size_t desired_shared_key_len) const final;

      ~KEM_Decryption_with_KDF() override;

   protected:
      virtual void raw_kem_decrypt(std::span<uint8_t> out_raw_shared_key,
                                   std::span<const uint8_t> encapsulated_key) = 0;

      virtual size_t raw_kem_shared_key_length() const = 0;

      explicit KEM_Decryption_with_KDF(std::string_view kdf);

   private:
      std::unique_ptr<KDF> m_kdf;
};

// NOLINTEND(*-special-member-functions)

}  // namespace Botan::PK_Ops

namespace Botan {

/**
* Prefetch an array
*
* This function returns a uint64_t which is accumulated from values
* read from the array. This may help confuse the compiler sufficiently
* to not elide otherwise "useless" reads. The return value will always
* be zero.
*/
uint64_t prefetch_array_raw(size_t bytes, const void* array) noexcept;

/**
* Prefetch several arrays
*
* This function returns a uint64_t which is accumulated from values
* read from the array. This may help confuse the compiler sufficiently
* to not elide otherwise "useless" reads. The return value will always
* be zero.
*/
template <std::unsigned_integral T, size_t... Ns>
T prefetch_arrays(T (&... arr)[Ns]) noexcept {
   return (static_cast<T>(prefetch_array_raw(sizeof(T) * Ns, arr)) & ...);
}

}  // namespace Botan

namespace Botan {

class BigInt;
class Barrett_Reduction;
class Montgomery_Params;
class RandomNumberGenerator;

/**
* Perform Lucas primality test
* @see FIPS 186-4 C.3.3
*
* @warning it is possible to construct composite integers which pass
* this test alone.
*
* @param n the positive integer to test
* @param mod_n a pre-created Barrett_Reduction for n
* @return true if n seems probably prime, false if n is composite
*/
bool BOTAN_TEST_API is_lucas_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n);

/**
* Perform Bailie-PSW primality test
*
* This is a combination of Miller-Rabin with base 2 and a Lucas test. No known
* composite integer passes both tests, though it is conjectured that infinitely
* many composite counterexamples exist.
*
* @param n the positive integer to test
* @param mod_n a pre-created Barrett_Reduction for n
* @return true if n seems probably prime, false if n is composite
*/
bool BOTAN_TEST_API is_bailie_psw_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n);

/**
* Return required number of Miller-Rabin tests in order to
* reach the specified probability of error.
*
* @param n_bits the bit-length of the integer being tested
* @param prob chance of false positive is bounded by 1/2**prob
* @param random is set if (and only if) the integer was randomly generated by us
*        and thus cannot have been maliciously constructed.
*/
size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random);

/**
* Perform a single Miller-Rabin test with specified base
*
* @param n the positive integer to test
* @param mod_n a pre-created Barrett_Reduction for n
* @param monty_n Montgomery parameters for n
* @param a the base to check
* @return result of primality test
*/
bool passes_miller_rabin_test(const BigInt& n,
                              const Barrett_Reduction& mod_n,
                              const Montgomery_Params& monty_n,
                              const BigInt& a);

/**
* Perform t iterations of a Miller-Rabin primality test with random bases
*
* @param n the positive integer to test
* @param mod_n a pre-created Barrett_Reduction for n
* @param rng a random number generator
* @param t number of tests to perform
*
* @return result of primality test
*/
bool BOTAN_TEST_API is_miller_rabin_probable_prime(const BigInt& n,
                                                   const Barrett_Reduction& mod_n,
                                                   RandomNumberGenerator& rng,
                                                   size_t t);

/**
* Generate DSA parameters using the FIPS 186 kosherizer
* @param rng a random number generator
* @param p_out where the prime p will be stored
* @param q_out where the prime q will be stored
* @param pbits how long p will be in bits
* @param qbits how long q will be in bits
* @return random seed used to generate this parameter set
*/
std::vector<uint8_t> generate_dsa_primes(
   RandomNumberGenerator& rng, BigInt& p_out, BigInt& q_out, size_t pbits, size_t qbits);

/**
* Generate DSA parameters using the FIPS 186 kosherizer
* @param rng a random number generator
* @param p_out where the prime p will be stored
* @param q_out where the prime q will be stored
* @param pbits how long p will be in bits
* @param qbits how long q will be in bits
* @param seed the seed used to generate the parameters
* @param offset optional offset from seed to start searching at
* @return true if seed generated a valid DSA parameter set, otherwise
          false. p_out and q_out are only valid if true was returned.
*/
bool BOTAN_TEST_API generate_dsa_primes(RandomNumberGenerator& rng,
                                        BigInt& p_out,
                                        BigInt& q_out,
                                        size_t pbits,
                                        size_t qbits,
                                        const std::vector<uint8_t>& seed,
                                        size_t offset = 0);

}  // namespace Botan

namespace Botan {

/**
* Integer rounding
*
* Returns an integer z such that n <= z <= n + align_to
* and z % align_to == 0
*
* @param n an integer
* @param align_to the alignment boundary
* @return n rounded up to a multiple of align_to
*/
constexpr inline size_t round_up(size_t n, size_t align_to) {
   // Arguably returning n in this case would also be sensible
   BOTAN_ARG_CHECK(align_to != 0, "align_to must not be 0");

   if(n % align_to > 0) {
      const size_t adj = align_to - (n % align_to);
      BOTAN_ARG_CHECK(n + adj >= n, "Integer overflow during rounding");
      n += adj;
   }
   return n;
}

}  // namespace Botan

namespace Botan {

/**
A class encapsulating a SCAN name (similar to JCE conventions)
http://www.users.zetnet.co.uk/hopwood/crypto/scan/
*/
class SCAN_Name final {
   public:
      /**
      * Create a SCAN_Name
      * @param algo_spec A SCAN-format name
      */
      explicit SCAN_Name(const char* algo_spec);

      /**
      * Create a SCAN_Name
      * @param algo_spec A SCAN-format name
      */
      explicit SCAN_Name(std::string_view algo_spec);

      /**
      * @return original input string
      */
      const std::string& to_string() const { return m_orig_algo_spec; }

      /**
      * @return algorithm name
      */
      const std::string& algo_name() const { return m_alg_name; }

      /**
      * @return number of arguments
      */
      size_t arg_count() const { return m_args.size(); }

      /**
      * @param lower is the lower bound
      * @param upper is the upper bound
      * @return if the number of arguments is between lower and upper
      */
      bool arg_count_between(size_t lower, size_t upper) const {
         return ((arg_count() >= lower) && (arg_count() <= upper));
      }

      /**
      * @param i which argument
      * @return ith argument
      */
      std::string arg(size_t i) const;

      /**
      * @param i which argument
      * @param def_value the default value
      * @return ith argument or the default value
      */
      std::string arg(size_t i, std::string_view def_value) const;

      /**
      * @param i which argument
      * @param def_value the default value
      * @return ith argument as an integer, or the default value
      */
      size_t arg_as_integer(size_t i, size_t def_value) const;

      /**
      * @param i which argument
      * @return ith argument as an integer
      */
      size_t arg_as_integer(size_t i) const;

      /**
      * @return cipher mode (if any)
      */
      std::string cipher_mode() const { return (!m_mode_info.empty()) ? m_mode_info[0] : ""; }

      /**
      * @return cipher mode padding (if any)
      */
      std::string cipher_mode_pad() const { return (m_mode_info.size() >= 2) ? m_mode_info[1] : ""; }

   private:
      std::string m_orig_algo_spec;
      std::string m_alg_name;
      std::vector<std::string> m_args;
      std::vector<std::string> m_mode_info;
};

// This is unrelated but it is convenient to stash it here
template <typename T>
std::vector<std::string> probe_providers_of(std::string_view algo_spec,
                                            const std::vector<std::string>& possible = {"base"}) {
   std::vector<std::string> providers;
   for(auto&& prov : possible) {
      auto o = T::create(algo_spec, prov);
      if(o) {
         providers.push_back(prov);  // available
      }
   }
   return providers;
}

}  // namespace Botan

namespace Botan {

/**
* SHA-384
*/
class SHA_384 final : public HashFunction {
   public:
      using digest_type = secure_vector<uint64_t>;

      static constexpr MD_Endian byte_endianness = MD_Endian::Big;
      static constexpr MD_Endian bit_endianness = MD_Endian::Big;
      static constexpr size_t block_bytes = 128;
      static constexpr size_t output_bytes = 48;
      static constexpr size_t ctr_bytes = 16;

      static void compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
      static void init(digest_type& digest);

   public:
      std::string name() const override { return "SHA-384"; }

      size_t output_length() const override { return output_bytes; }

      size_t hash_block_size() const override { return block_bytes; }

      std::unique_ptr<HashFunction> new_object() const override;

      std::unique_ptr<HashFunction> copy_state() const override;

      std::string provider() const override;

      void clear() override { m_md.clear(); }

   private:
      void add_data(std::span<const uint8_t> input) override;

      void final_result(std::span<uint8_t> output) override;

   private:
      MerkleDamgard_Hash<SHA_384> m_md;
};

/**
* SHA-512
*/
class SHA_512 final : public HashFunction {
   public:
      using digest_type = secure_vector<uint64_t>;

      static constexpr MD_Endian byte_endianness = MD_Endian::Big;
      static constexpr MD_Endian bit_endianness = MD_Endian::Big;
      static constexpr size_t block_bytes = 128;
      static constexpr size_t output_bytes = 64;
      static constexpr size_t ctr_bytes = 16;

      static void compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
      static void init(digest_type& digest);

   public:
      std::string name() const override { return "SHA-512"; }

      size_t output_length() const override { return output_bytes; }

      size_t hash_block_size() const override { return block_bytes; }

      std::unique_ptr<HashFunction> new_object() const override;

      std::unique_ptr<HashFunction> copy_state() const override;

      std::string provider() const override;

      void clear() override { m_md.clear(); }

   public:
      static void compress_digest(digest_type& digest, std::span<const uint8_t> input, size_t blocks);

#if defined(BOTAN_HAS_SHA2_64_X86_AVX2)
      static void compress_digest_x86_avx2(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
#endif

#if defined(BOTAN_HAS_SHA2_64_X86_AVX512)
      static void compress_digest_x86_avx512(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
#endif

#if defined(BOTAN_HAS_SHA2_64_X86)
      static void compress_digest_x86(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
#endif

#if defined(BOTAN_HAS_SHA2_64_ARMV8)
      static void compress_digest_armv8(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
#endif

   private:
      void add_data(std::span<const uint8_t> input) override;

      void final_result(std::span<uint8_t> output) override;

   private:
      MerkleDamgard_Hash<SHA_512> m_md;
};

/**
* SHA-512/256
*/
class SHA_512_256 final : public HashFunction {
   public:
      using digest_type = secure_vector<uint64_t>;

      static constexpr MD_Endian byte_endianness = MD_Endian::Big;
      static constexpr MD_Endian bit_endianness = MD_Endian::Big;
      static constexpr size_t block_bytes = 128;
      static constexpr size_t output_bytes = 32;
      static constexpr size_t ctr_bytes = 16;

      static void compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks);
      static void init(digest_type& digest);

   public:
      std::string name() const override { return "SHA-512-256"; }

      size_t output_length() const override { return output_bytes; }

      size_t hash_block_size() const override { return block_bytes; }

      std::unique_ptr<HashFunction> new_object() const override;

      std::unique_ptr<HashFunction> copy_state() const override;

      std::string provider() const override;

      void clear() override { m_md.clear(); }

   private:
      void add_data(std::span<const uint8_t> input) override;

      void final_result(std::span<uint8_t> output) override;

   private:
      MerkleDamgard_Hash<SHA_512_256> m_md;
};

}  // namespace Botan

namespace Botan {

/*
* SHA-512 F1 Function
*/
BOTAN_FORCE_INLINE void SHA2_64_F(uint64_t A,
                                  uint64_t B,
                                  uint64_t C,
                                  uint64_t& D,
                                  uint64_t E,
                                  uint64_t F,
                                  uint64_t G,
                                  uint64_t& H,
                                  uint64_t& M1,
                                  uint64_t M2,
                                  uint64_t M3,
                                  uint64_t M4,
                                  uint64_t magic) {
   const uint64_t E_rho = rho<14, 18, 41>(E);
   const uint64_t A_rho = rho<28, 34, 39>(A);
   const uint64_t M2_sigma = sigma<19, 61, 6>(M2);
   const uint64_t M4_sigma = sigma<1, 8, 7>(M4);
   H += magic + E_rho + choose(E, F, G) + M1;
   D += H;
   H += A_rho + majority(A, B, C);
   M1 += M2_sigma + M3 + M4_sigma;
}

/*
* SHA-512 F1 Function (No Message Expansion)
*/
BOTAN_FORCE_INLINE void SHA2_64_F(
   uint64_t A, uint64_t B, uint64_t C, uint64_t& D, uint64_t E, uint64_t F, uint64_t G, uint64_t& H, uint64_t M) {
   const uint64_t E_rho = rho<14, 18, 41>(E);
   const uint64_t A_rho = rho<28, 34, 39>(A);
   H += E_rho + choose(E, F, G) + M;
   D += H;
   H += A_rho + majority(A, B, C);
}

}  // namespace Botan

// TODO(Botan4): Move this to compiler.h (currently still a public header)

#if !defined(BOTAN_SCRUB_STACK_AFTER_RETURN)
   #if BOTAN_COMPILER_HAS_ATTRIBUTE(strub) && defined(BOTAN_USE_COMPILER_ASSISTED_STACK_SCRUBBING)
      /**
      * When a function definition is annotated with this macro, the compiler
      * generates a wrapper for the function's body to handle stack scrubbing
      * in the wrapper. In contrast to 'strub("at-calls")' this does not alter
      * the function's ABI.
      *
      * It is okay to use this annotation on C++ method definitions (in *.cpp),
      * even if the function is a public API.
      *
      * Currently this is supported on GCC 14+ only
      * See: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Common-Type-Attributes.html#index-strub-type-attribute
      */
      #define BOTAN_SCRUB_STACK_AFTER_RETURN BOTAN_COMPILER_ATTRIBUTE(strub("internal"))
   #else
      #define BOTAN_SCRUB_STACK_AFTER_RETURN
   #endif
#endif

#if defined(BOTAN_HAS_OS_UTILS)
#endif


namespace Botan {

template <typename F>
uint64_t measure_cost(std::chrono::milliseconds trial_msec, F func) {
#if defined(BOTAN_HAS_OS_UTILS)
   const uint64_t trial_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(trial_msec).count();

   uint64_t total_nsec = 0;
   uint64_t trials = 0;

   auto trial_start = OS::get_system_timestamp_ns();

   for(;;) {
      const auto start = OS::get_system_timestamp_ns();
      func();
      const auto end = OS::get_system_timestamp_ns();

      if(end >= start) {
         total_nsec += (end - start);
         trials += 1;

         if((end - trial_start) >= trial_nsec) {
            return (total_nsec / trials);
         }
      }
   }

#else
   BOTAN_UNUSED(trial_msec, func);
   throw Not_Implemented("No system clock available");
#endif
}

}  // namespace Botan
/* NOLINTBEGIN(*-macro-usage) */

#define BOTAN_FULL_VERSION_STRING "Botan 3.9.0 (unreleased, revision git:07e1cfe0a06b224bbb37ad534736924931184246)"

#define BOTAN_SHORT_VERSION_STRING "3.9.0"

#define BOTAN_VC_REVISION "git:07e1cfe0a06b224bbb37ad534736924931184246"


/* NOLINTEND(*-macro-usage) */

namespace Botan {

/**
* Estimate work factor for discrete logarithm
* @param prime_group_size size of the group in bits
* @return estimated security level for this group
*/
size_t BOTAN_TEST_API dl_work_factor(size_t prime_group_size);

/**
* Return the appropriate exponent size to use for a particular prime
* group. This is twice the size of the estimated cost of breaking the
* key using an index calculus attack; the assumption is that if an
* arbitrary discrete log on a group of size bits would take about 2^n
* effort, and thus using an exponent of size 2^(2*n) implies that all
* available attacks are about as easy (as e.g Pollard's kangaroo
* algorithm can compute the DL in sqrt(x) operations) while minimizing
* the exponent size for performance reasons.
*/
size_t BOTAN_TEST_API dl_exponent_size(size_t prime_group_size);

/**
* Estimate work factor for integer factorization
* @param n_bits size of modulus in bits
* @return estimated security level for this modulus
*/
size_t BOTAN_TEST_API if_work_factor(size_t n_bits);

/**
* Estimate work factor for EC discrete logarithm
* @param prime_group_size size of the group in bits
* @return estimated security level for this group
*/
size_t ecp_work_factor(size_t prime_group_size);

}  // namespace Botan
/*
* Algorithm Identifier
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Create an AlgorithmIdentifier
*/
AlgorithmIdentifier::AlgorithmIdentifier(const OID& oid, const std::vector<uint8_t>& param) :
      m_oid(oid), m_parameters(param) {}

/*
* Create an AlgorithmIdentifier
*/
AlgorithmIdentifier::AlgorithmIdentifier(std::string_view oid, const std::vector<uint8_t>& param) :
      AlgorithmIdentifier(OID::from_string(oid), param) {}

/*
* Create an AlgorithmIdentifier
*/
AlgorithmIdentifier::AlgorithmIdentifier(const OID& oid, Encoding_Option option) : m_oid(oid) {
   constexpr uint8_t DER_NULL[] = {0x05, 0x00};

   if(option == USE_NULL_PARAM) {
      m_parameters.assign(DER_NULL, DER_NULL + 2);
   }
}

/*
* Create an AlgorithmIdentifier
*/
AlgorithmIdentifier::AlgorithmIdentifier(std::string_view oid, Encoding_Option option) : m_oid(OID::from_string(oid)) {
   constexpr uint8_t DER_NULL[2] = {0x05, 0x00};

   if(option == USE_NULL_PARAM) {
      m_parameters.assign(DER_NULL, DER_NULL + 2);
   }
}

bool AlgorithmIdentifier::parameters_are_null() const {
   return (m_parameters.size() == 2 && (m_parameters[0] == 0x05) && (m_parameters[1] == 0x00));
}

bool operator==(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2) {
   if(a1.oid() != a2.oid()) {
      return false;
   }

   /*
   * Treat NULL and empty as equivalent
   */
   if(a1.parameters_are_null_or_empty() && a2.parameters_are_null_or_empty()) {
      return true;
   }

   return (a1.parameters() == a2.parameters());
}

bool operator!=(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2) {
   return !(a1 == a2);
}

/*
* DER encode an AlgorithmIdentifier
*/
void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const {
   codec.start_sequence().encode(oid()).raw_bytes(parameters()).end_cons();
}

/*
* Decode a BER encoded AlgorithmIdentifier
*/
void AlgorithmIdentifier::decode_from(BER_Decoder& codec) {
   codec.start_sequence().decode(m_oid).raw_bytes(m_parameters).end_cons();
}

}  // namespace Botan
/*
* ASN.1 Internals
* (C) 1999-2007,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <sstream>

namespace Botan {

std::vector<uint8_t> ASN1_Object::BER_encode() const {
   std::vector<uint8_t> output;
   DER_Encoder der(output);
   this->encode_into(der);
   return output;
}

/*
* Check a type invariant on BER data
*/
void BER_Object::assert_is_a(ASN1_Type expected_type_tag, ASN1_Class expected_class_tag, std::string_view descr) const {
   if(!this->is_a(expected_type_tag, expected_class_tag)) {
      std::stringstream msg;

      msg << "Tag mismatch when decoding " << descr << " got ";

      if(m_class_tag == ASN1_Class::NoObject && m_type_tag == ASN1_Type::NoObject) {
         msg << "EOF";
      } else {
         if(m_class_tag == ASN1_Class::Universal || m_class_tag == ASN1_Class::Constructed) {
            msg << asn1_tag_to_string(m_type_tag);
         } else {
            msg << std::to_string(static_cast<uint32_t>(m_type_tag));
         }

         msg << "/" << asn1_class_to_string(m_class_tag);
      }

      msg << " expected ";

      if(expected_class_tag == ASN1_Class::Universal || expected_class_tag == ASN1_Class::Constructed) {
         msg << asn1_tag_to_string(expected_type_tag);
      } else {
         msg << std::to_string(static_cast<uint32_t>(expected_type_tag));
      }

      msg << "/" << asn1_class_to_string(expected_class_tag);

      throw BER_Decoding_Error(msg.str());
   }
}

bool BER_Object::is_a(ASN1_Type expected_type_tag, ASN1_Class expected_class_tag) const {
   return (m_type_tag == expected_type_tag && m_class_tag == expected_class_tag);
}

bool BER_Object::is_a(int expected_type_tag, ASN1_Class expected_class_tag) const {
   return is_a(ASN1_Type(expected_type_tag), expected_class_tag);
}

void BER_Object::set_tagging(ASN1_Type type_tag, ASN1_Class class_tag) {
   m_type_tag = type_tag;
   m_class_tag = class_tag;
}

std::string asn1_class_to_string(ASN1_Class type) {
   switch(type) {
      case ASN1_Class::Universal:
         return "UNIVERSAL";
      case ASN1_Class::Constructed:
         return "CONSTRUCTED";
      case ASN1_Class::ContextSpecific:
         return "CONTEXT_SPECIFIC";
      case ASN1_Class::Application:
         return "APPLICATION";
      case ASN1_Class::Private:
         return "PRIVATE";
      case ASN1_Class::NoObject:
         return "NO_OBJECT";
      default:
         return "CLASS(" + std::to_string(static_cast<size_t>(type)) + ")";
   }
}

std::string asn1_tag_to_string(ASN1_Type type) {
   switch(type) {
      case ASN1_Type::Sequence:
         return "SEQUENCE";

      case ASN1_Type::Set:
         return "SET";

      case ASN1_Type::PrintableString:
         return "PRINTABLE STRING";

      case ASN1_Type::NumericString:
         return "NUMERIC STRING";

      case ASN1_Type::Ia5String:
         return "IA5 STRING";

      case ASN1_Type::TeletexString:
         return "T61 STRING";

      case ASN1_Type::Utf8String:
         return "UTF8 STRING";

      case ASN1_Type::VisibleString:
         return "VISIBLE STRING";

      case ASN1_Type::BmpString:
         return "BMP STRING";

      case ASN1_Type::UniversalString:
         return "UNIVERSAL STRING";

      case ASN1_Type::UtcTime:
         return "UTC TIME";

      case ASN1_Type::GeneralizedTime:
         return "GENERALIZED TIME";

      case ASN1_Type::OctetString:
         return "OCTET STRING";

      case ASN1_Type::BitString:
         return "BIT STRING";

      case ASN1_Type::Enumerated:
         return "ENUMERATED";

      case ASN1_Type::Integer:
         return "INTEGER";

      case ASN1_Type::Null:
         return "NULL";

      case ASN1_Type::ObjectId:
         return "OBJECT";

      case ASN1_Type::Boolean:
         return "BOOLEAN";

      case ASN1_Type::NoObject:
         return "NO_OBJECT";

      default:
         return "TAG(" + std::to_string(static_cast<uint32_t>(type)) + ")";
   }
}

/*
* BER Decoding Exceptions
*/
BER_Decoding_Error::BER_Decoding_Error(std::string_view err) : Decoding_Error(fmt("BER: {}", err)) {}

BER_Bad_Tag::BER_Bad_Tag(std::string_view str, uint32_t tagging) : BER_Decoding_Error(fmt("{}: {}", str, tagging)) {}

namespace ASN1 {

/*
* Put some arbitrary bytes into a SEQUENCE
*/
std::vector<uint8_t> put_in_sequence(const std::vector<uint8_t>& contents) {
   return ASN1::put_in_sequence(contents.data(), contents.size());
}

std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len) {
   std::vector<uint8_t> output;
   DER_Encoder(output).start_sequence().raw_bytes(bits, len).end_cons();
   return output;
}

/*
* Convert a BER object into a string object
*/
std::string to_string(const BER_Object& obj) {
   return bytes_to_string(obj.data());
}

/*
* Do heuristic tests for BER data
*/
bool maybe_BER(DataSource& source) {
   uint8_t first_u8 = 0;
   if(source.peek_byte(first_u8) == 0) {
      BOTAN_ASSERT_EQUAL(source.read_byte(first_u8), 0, "Expected EOF");
      throw Stream_IO_Error("ASN1::maybe_BER: Source was empty");
   }

   const auto cons_seq = static_cast<uint8_t>(ASN1_Class::Constructed) | static_cast<uint8_t>(ASN1_Type::Sequence);
   return first_u8 == cons_seq;
}

}  // namespace ASN1

}  // namespace Botan
/*
* ASN.1 OID
* (C) 1999-2007,2024 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <algorithm>
#include <span>

namespace Botan {

namespace {

void oid_valid_check(std::span<const uint32_t> oid) {
   BOTAN_ARG_CHECK(oid.size() >= 2, "OID too short to be valid");
   BOTAN_ARG_CHECK(oid[0] <= 2, "OID root out of range");
   BOTAN_ARG_CHECK(oid[1] <= 39 || oid[0] == 2, "OID second arc too large");
   // This last is a limitation of using 32 bit integers when decoding
   // not a limitation of ASN.1 object identifiers in general
   BOTAN_ARG_CHECK(oid[1] <= 0xFFFFFFAF, "OID second arc too large");
}

// returns empty on invalid
std::vector<uint32_t> parse_oid_str(std::string_view oid) {
   try {
      std::string elem;
      std::vector<uint32_t> oid_elems;

      for(char c : oid) {
         if(c == '.') {
            if(elem.empty()) {
               return std::vector<uint32_t>();
            }
            oid_elems.push_back(to_u32bit(elem));
            elem.clear();
         } else {
            elem += c;
         }
      }

      if(!elem.empty()) {
         oid_elems.push_back(to_u32bit(elem));
      }

      return oid_elems;
   } catch(Invalid_Argument&) {
      // thrown by to_u32bit
      return std::vector<uint32_t>();
   }
}

}  // namespace

//static
void OID::register_oid(const OID& oid, std::string_view name) {
   OID_Map::global_registry().add_oid(oid, name);
}

//static
std::optional<OID> OID::from_name(std::string_view name) {
   if(name.empty()) {
      throw Invalid_Argument("OID::from_name argument must be non-empty");
   }

   OID o = OID_Map::global_registry().str2oid(name);
   if(o.has_value()) {
      return std::optional(o);
   }

   return std::nullopt;
}

//static
OID OID::from_string(std::string_view str) {
   if(str.empty()) {
      throw Invalid_Argument("OID::from_string argument must be non-empty");
   }

   OID o = OID_Map::global_registry().str2oid(str);
   if(o.has_value()) {
      return o;
   }

   // Try to parse as a dotted decimal
   try {
      return OID(str);
   } catch(...) {}

   throw Lookup_Error(fmt("No OID associated with name '{}'", str));
}

OID::OID(std::initializer_list<uint32_t> init) : m_id(init) {
   oid_valid_check(m_id);
}

OID::OID(std::vector<uint32_t>&& init) : m_id(std::move(init)) {
   oid_valid_check(m_id);
}

/*
* ASN.1 OID Constructor
*/
OID::OID(std::string_view oid_str) {
   if(!oid_str.empty()) {
      m_id = parse_oid_str(oid_str);
      oid_valid_check(m_id);
   }
}

/*
* Return this OID as a string
*/
std::string OID::to_string() const {
   std::ostringstream out;

   for(size_t i = 0; i != m_id.size(); ++i) {
      // avoid locale issues with integer formatting
      out << std::to_string(m_id[i]);
      if(i != m_id.size() - 1) {
         out << ".";
      }
   }

   return out.str();
}

std::string OID::to_formatted_string() const {
   std::string s = this->human_name_or_empty();
   if(!s.empty()) {
      return s;
   }
   return this->to_string();
}

std::string OID::human_name_or_empty() const {
   return OID_Map::global_registry().oid2str(*this);
}

bool OID::registered_oid() const {
   return !human_name_or_empty().empty();
}

bool OID::matches(std::initializer_list<uint32_t> other) const {
   // TODO: once all target compilers support it, use std::ranges::equal
   return std::equal(m_id.begin(), m_id.end(), other.begin(), other.end());
}

uint64_t OID::hash_code() const {
   // If this is changed also update gen_oids.py to match
   uint64_t hash = 0x621F302327D9A49A;
   for(auto id : m_id) {
      hash *= 193;
      hash += id;
   }
   return hash;
}

/*
* Compare two OIDs
*/
bool operator<(const OID& a, const OID& b) {
   const std::vector<uint32_t>& oid1 = a.get_components();
   const std::vector<uint32_t>& oid2 = b.get_components();

   return std::lexicographical_compare(oid1.begin(), oid1.end(), oid2.begin(), oid2.end());
}

/*
* DER encode an OBJECT IDENTIFIER
*/
void OID::encode_into(DER_Encoder& der) const {
   if(m_id.size() < 2) {
      throw Invalid_Argument("OID::encode_into: OID is invalid");
   }

   auto append = [](std::vector<uint8_t>& encoding, uint32_t z) {
      if(z <= 0x7F) {
         encoding.push_back(static_cast<uint8_t>(z));
      } else {
         size_t z7 = (high_bit(z) + 7 - 1) / 7;

         for(size_t j = 0; j != z7; ++j) {
            uint8_t zp = static_cast<uint8_t>(z >> (7 * (z7 - j - 1)) & 0x7F);

            if(j != z7 - 1) {
               zp |= 0x80;
            }

            encoding.push_back(zp);
         }
      }
   };

   std::vector<uint8_t> encoding;

   // We know 40 * root can't overflow because root is between 0 and 2
   auto first = BOTAN_ASSERT_IS_SOME(checked_add(40 * m_id[0], m_id[1]));

   append(encoding, first);

   for(size_t i = 2; i != m_id.size(); ++i) {
      append(encoding, m_id[i]);
   }
   der.add_object(ASN1_Type::ObjectId, ASN1_Class::Universal, encoding);
}

/*
* Decode a BER encoded OBJECT IDENTIFIER
*/
void OID::decode_from(BER_Decoder& decoder) {
   BER_Object obj = decoder.get_next_object();
   if(obj.tagging() != (ASN1_Class::Universal | ASN1_Type::ObjectId)) {
      throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
   }

   if(obj.length() == 0) {
      throw BER_Decoding_Error("OID encoding is too short");
   }

   auto consume = [](BufferSlicer& data) -> uint32_t {
      BOTAN_ASSERT_NOMSG(!data.empty());
      uint32_t b = data.take_byte();

      if(b > 0x7F) {
         b &= 0x7F;

         // Even BER requires that the OID have minimal length, ie that
         // the first byte of a multibyte encoding cannot be zero
         // See X.690 section 8.19.2
         if(b == 0) {
            throw Decoding_Error("Leading zero byte in multibyte OID encoding");
         }

         while(true) {
            if(data.empty()) {
               throw Decoding_Error("Truncated OID value");
            }

            const uint8_t next = data.take_byte();
            const bool more = (next & 0x80) == 0x80;
            const uint8_t value = next & 0x7F;

            if((b >> (32 - 7)) != 0) {
               throw Decoding_Error("OID component overflow");
            }

            b = (b << 7) | value;

            if(!more) {
               break;
            }
         }
      }

      return b;
   };

   BufferSlicer data(obj.data());
   std::vector<uint32_t> parts;
   while(!data.empty()) {
      const uint32_t comp = consume(data);

      if(parts.empty()) {
         // divide into root and second arc

         const uint32_t root_arc = [](uint32_t b0) -> uint32_t {
            if(b0 < 40) {
               return 0;
            } else if(b0 < 80) {
               return 1;
            } else {
               return 2;
            }
         }(comp);

         parts.push_back(root_arc);
         BOTAN_ASSERT_NOMSG(comp >= 40 * root_arc);
         parts.push_back(comp - 40 * root_arc);
      } else {
         parts.push_back(comp);
      }
   }

   m_id = parts;
}

}  // namespace Botan
/*
* (C) 2014,2015,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <iomanip>

namespace Botan {

namespace {

// Printable here means fits into an ASN.1 "PRINTABLE STRING" type
bool is_printable_char(char c) {
   if(c >= 'a' && c <= 'z') {
      return true;
   }

   if(c >= 'A' && c <= 'Z') {
      return true;
   }

   if(c >= '0' && c <= '9') {
      return true;
   }

   if(c == '.' || c == ':' || c == '/' || c == '-') {
      return true;
   }

   return false;
}

bool all_printable_chars(const uint8_t bits[], size_t bits_len) {
   for(size_t i = 0; i != bits_len; ++i) {
      if(!is_printable_char(bits[i])) {
         return false;
      }
   }
   return true;
}

/*
* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
*/
bool possibly_a_general_name(const uint8_t bits[], size_t bits_len) {
   if(bits_len <= 2) {
      return false;
   }

   if(bits[0] != 0x82 && bits[0] != 0x86) {
      return false;
   }

   if(bits[1] != bits_len - 2) {
      return false;
   }

   if(!all_printable_chars(bits + 2, bits_len - 2)) {
      return false;
   }

   return true;
}

}  // namespace

std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const {
   std::ostringstream output;
   print_to_stream(output, in, len);
   return output.str();
}

void ASN1_Formatter::print_to_stream(std::ostream& output, const uint8_t in[], size_t len) const {
   BER_Decoder dec(in, len);
   decode(output, dec, 0);
}

void ASN1_Formatter::decode(std::ostream& output, BER_Decoder& decoder, size_t level) const {
   BER_Object obj = decoder.get_next_object();

   const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);

   while(obj.is_set()) {
      const ASN1_Type type_tag = obj.type();
      const ASN1_Class class_tag = obj.get_class();
      const size_t length = obj.length();

      /* hack to insert the tag+length back in front of the stuff now
         that we've gotten the type info */
      std::vector<uint8_t> bits;
      DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());

      BER_Decoder data(bits);

      if(intersects(class_tag, ASN1_Class::Constructed)) {
         BER_Decoder cons_info(obj.bits(), obj.length());

         if(recurse_deeper) {
            output << format(type_tag, class_tag, level, length, "");
            decode(output, cons_info, level + 1);  // recurse
         } else {
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
         }
      } else if(intersects(class_tag, ASN1_Class::Application) || intersects(class_tag, ASN1_Class::ContextSpecific)) {
         bool success_parsing_cs = false;

         if(m_print_context_specific) {
            try {
               if(possibly_a_general_name(bits.data(), bits.size())) {
                  output << format(type_tag, class_tag, level, level, bytes_to_string(std::span{bits}.subspan(2)));
                  success_parsing_cs = true;
               } else if(recurse_deeper) {
                  std::vector<uint8_t> inner_bits;
                  data.decode(inner_bits, type_tag);

                  BER_Decoder inner(inner_bits);
                  std::ostringstream inner_data;
                  decode(inner_data, inner, level + 1);  // recurse
                  output << inner_data.str();
                  success_parsing_cs = true;
               }
            } catch(...) {}
         }

         if(!success_parsing_cs) {
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
         }
      } else if(type_tag == ASN1_Type::ObjectId) {
         OID oid;
         data.decode(oid);

         const std::string name = oid.human_name_or_empty();
         const std::string oid_str = oid.to_string();

         if(name.empty()) {
            output << format(type_tag, class_tag, level, length, oid_str);
         } else {
            output << format(type_tag, class_tag, level, length, fmt("{} [{}]", name, oid_str));
         }
      } else if(type_tag == ASN1_Type::Integer || type_tag == ASN1_Type::Enumerated) {
         BigInt number;

         if(type_tag == ASN1_Type::Integer) {
            data.decode(number);
         } else if(type_tag == ASN1_Type::Enumerated) {
            data.decode(number, ASN1_Type::Enumerated, class_tag);
         }

         output << format(type_tag, class_tag, level, length, format_bn(number));
      } else if(type_tag == ASN1_Type::Boolean) {
         bool boolean = false;
         data.decode(boolean);
         output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
      } else if(type_tag == ASN1_Type::Null) {
         output << format(type_tag, class_tag, level, length, "");
      } else if(type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) {
         std::vector<uint8_t> decoded_bits;
         data.decode(decoded_bits, type_tag);
         bool printing_octet_string_worked = false;

         if(recurse_deeper) {
            try {
               BER_Decoder inner(decoded_bits);

               std::ostringstream inner_data;
               decode(inner_data, inner, level + 1);  // recurse

               output << format(type_tag, class_tag, level, length, "");
               output << inner_data.str();
               printing_octet_string_worked = true;
            } catch(...) {}
         }

         if(!printing_octet_string_worked) {
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, decoded_bits));
         }
      } else if(ASN1_String::is_string_type(type_tag)) {
         ASN1_String str;
         data.decode(str);
         output << format(type_tag, class_tag, level, length, str.value());
      } else if(type_tag == ASN1_Type::UtcTime || type_tag == ASN1_Type::GeneralizedTime) {
         ASN1_Time time;
         data.decode(time);
         output << format(type_tag, class_tag, level, length, time.readable_string());
      } else {
         output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag) << " type=" << static_cast<int>(type_tag)
                << "\n";
      }

      obj = decoder.get_next_object();
   }
}

namespace {

std::string format_type(ASN1_Type type_tag, ASN1_Class class_tag) {
   if(class_tag == ASN1_Class::Universal) {
      return asn1_tag_to_string(type_tag);
   }

   if(class_tag == ASN1_Class::Constructed && (type_tag == ASN1_Type::Sequence || type_tag == ASN1_Type::Set)) {
      return asn1_tag_to_string(type_tag);
   }

   std::ostringstream oss;

   if(intersects(class_tag, ASN1_Class::Constructed)) {
      oss << "cons ";
   }

   oss << "[" << std::to_string(static_cast<uint32_t>(type_tag)) << "]";

   if(intersects(class_tag, ASN1_Class::Application)) {
      oss << " appl";
   }
   if(intersects(class_tag, ASN1_Class::ContextSpecific)) {
      oss << " context";
   }

   return oss.str();
}

}  // namespace

std::string ASN1_Pretty_Printer::format(
   ASN1_Type type_tag, ASN1_Class class_tag, size_t level, size_t length, std::string_view value) const {
   bool should_skip = false;

   if(value.length() > m_print_limit) {
      should_skip = true;
   }

   if((type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) &&
      value.length() > m_print_binary_limit) {
      should_skip = true;
   }

   level += m_initial_level;

   std::ostringstream oss;

   oss << "  d=" << std::setw(2) << level << ", l=" << std::setw(4) << length << ":" << std::string(level + 1, ' ')
       << format_type(type_tag, class_tag);

   if(!value.empty() && !should_skip) {
      const size_t current_pos = static_cast<size_t>(oss.tellp());
      const size_t spaces_to_align = (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);

      oss << std::string(spaces_to_align, ' ') << value;
   }

   oss << "\n";

   return oss.str();
}

std::string ASN1_Pretty_Printer::format_bin(ASN1_Type /*type_tag*/,
                                            ASN1_Class /*class_tag*/,
                                            const std::vector<uint8_t>& vec) const {
   if(all_printable_chars(vec.data(), vec.size())) {
      return bytes_to_string(vec);
   } else {
      return hex_encode(vec);
   }
}

std::string ASN1_Pretty_Printer::format_bn(const BigInt& bn) const {
   if(bn.bits() < 16) {
      return bn.to_dec_string();
   } else {
      return bn.to_hex_string();
   }
}

}  // namespace Botan
/*
* Simple ASN.1 String Types
* (C) 1999-2007,2020 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

/*
* Choose an encoding for the string
*/
ASN1_Type choose_encoding(std::string_view str) {
   auto all_printable = CT::Mask<uint8_t>::set();

   for(char cs : str) {
      const uint8_t c = static_cast<uint8_t>(cs);

      auto is_alpha_lower = CT::Mask<uint8_t>::is_within_range(c, 'a', 'z');
      auto is_alpha_upper = CT::Mask<uint8_t>::is_within_range(c, 'A', 'Z');
      auto is_decimal = CT::Mask<uint8_t>::is_within_range(c, '0', '9');

      auto is_print_punc = CT::Mask<uint8_t>::is_any_of(c, {' ', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'});

      auto is_printable = is_alpha_lower | is_alpha_upper | is_decimal | is_print_punc;

      all_printable &= is_printable;
   }

   if(all_printable.as_bool()) {
      return ASN1_Type::PrintableString;
   } else {
      return ASN1_Type::Utf8String;
   }
}

bool is_utf8_subset_string_type(ASN1_Type tag) {
   return (tag == ASN1_Type::NumericString || tag == ASN1_Type::PrintableString || tag == ASN1_Type::VisibleString ||
           tag == ASN1_Type::Ia5String || tag == ASN1_Type::Utf8String);
}

bool is_asn1_string_type(ASN1_Type tag) {
   return (is_utf8_subset_string_type(tag) || tag == ASN1_Type::TeletexString || tag == ASN1_Type::BmpString ||
           tag == ASN1_Type::UniversalString);
}

}  // namespace

//static
bool ASN1_String::is_string_type(ASN1_Type tag) {
   return is_asn1_string_type(tag);
}

ASN1_String::ASN1_String(std::string_view str, ASN1_Type t) : m_utf8_str(str), m_tag(t) {
   if(!is_utf8_subset_string_type(m_tag)) {
      throw Invalid_Argument("ASN1_String only supports encoding to UTF-8 or a UTF-8 subset");
   }
}

ASN1_String::ASN1_String(std::string_view str) : ASN1_String(str, choose_encoding(str)) {}

/*
* DER encode an ASN1_String
*/
void ASN1_String::encode_into(DER_Encoder& encoder) const {
   if(m_data.empty()) {
      BOTAN_ASSERT_NOMSG(is_utf8_subset_string_type(tagging()));
      encoder.add_object(tagging(), ASN1_Class::Universal, m_utf8_str);
   } else {
      // If this string was decoded, reserialize using original encoding
      encoder.add_object(tagging(), ASN1_Class::Universal, m_data.data(), m_data.size());
   }
}

/*
* Decode a BER encoded ASN1_String
*/
void ASN1_String::decode_from(BER_Decoder& source) {
   BER_Object obj = source.get_next_object();

   if(!is_asn1_string_type(obj.type())) {
      auto typ = static_cast<uint32_t>(obj.type());
      throw Decoding_Error(fmt("ASN1_String: Unknown string type {}", typ));
   }

   m_tag = obj.type();
   m_data.assign(obj.bits(), obj.bits() + obj.length());

   if(m_tag == ASN1_Type::BmpString) {
      m_utf8_str = ucs2_to_utf8(m_data.data(), m_data.size());
   } else if(m_tag == ASN1_Type::UniversalString) {
      m_utf8_str = ucs4_to_utf8(m_data.data(), m_data.size());
   } else if(m_tag == ASN1_Type::TeletexString) {
      /*
      TeletexString is nominally ITU T.61 not ISO-8859-1 but it seems
      the majority of implementations actually used that charset here.
      */
      m_utf8_str = latin1_to_utf8(m_data.data(), m_data.size());
   } else {
      // All other supported string types are UTF-8 or some subset thereof
      m_utf8_str = ASN1::to_string(obj);
   }
}

}  // namespace Botan
/*
* X.509 Time Types
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

ASN1_Time ASN1_Time::from_seconds_since_epoch(uint64_t time_since_epoch) {
   return ASN1_Time(std::chrono::system_clock::time_point(std::chrono::seconds(time_since_epoch)));
}

ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time) {
   calendar_point cal(time);

   m_year = cal.year();
   m_month = cal.month();
   m_day = cal.day();
   m_hour = cal.hour();
   m_minute = cal.minutes();
   m_second = cal.seconds();

   // NOLINTNEXTLINE(*-prefer-member-initializer)
   m_tag = (m_year >= 2050) ? ASN1_Type::GeneralizedTime : ASN1_Type::UtcTime;
}

ASN1_Time::ASN1_Time(std::string_view t_spec, ASN1_Type tag) {
   set_to(t_spec, tag);
}

ASN1_Time::ASN1_Time(std::string_view t_spec) {
   if(t_spec.size() == 13) {
      set_to(t_spec, ASN1_Type::UtcTime);
   } else if(t_spec.size() == 15) {
      set_to(t_spec, ASN1_Type::GeneralizedTime);
   } else {
      throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
   }
}

void ASN1_Time::encode_into(DER_Encoder& der) const {
   BOTAN_ARG_CHECK(m_tag == ASN1_Type::UtcTime || m_tag == ASN1_Type::GeneralizedTime, "ASN1_Time: Bad encoding tag");

   der.add_object(m_tag, ASN1_Class::Universal, to_string());
}

void ASN1_Time::decode_from(BER_Decoder& source) {
   BER_Object ber_time = source.get_next_object();

   set_to(ASN1::to_string(ber_time), ber_time.type());
}

std::string ASN1_Time::to_string() const {
   if(!time_is_set()) {
      throw Invalid_State("ASN1_Time::to_string: No time set");
   }

   uint32_t full_year = m_year;

   if(m_tag == ASN1_Type::UtcTime) {
      if(m_year < 1950 || m_year >= 2050) {
         throw Encoding_Error(fmt("ASN_Time: The time {} cannot be encoded as UTCTime", readable_string()));
      }

      full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
   }

   const uint64_t year_factor = 10000000000;
   const uint64_t mon_factor = 100000000;
   const uint64_t day_factor = 1000000;
   const uint64_t hour_factor = 10000;
   const uint64_t min_factor = 100;

   const uint64_t int_repr = year_factor * full_year + mon_factor * m_month + day_factor * m_day +
                             hour_factor * m_hour + min_factor * m_minute + m_second;

   std::string repr = std::to_string(int_repr) + "Z";

   const size_t desired_size = (m_tag == ASN1_Type::UtcTime) ? 13 : 15;

   const std::string zero_padding(desired_size - repr.size(), '0');

   return zero_padding + repr;
}

std::string ASN1_Time::readable_string() const {
   if(!time_is_set()) {
      throw Invalid_State("ASN1_Time::readable_string: No time set");
   }

   // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC"
   std::stringstream output;
   output << std::setfill('0') << std::setw(4) << m_year << "/" << std::setw(2) << m_month << "/" << std::setw(2)
          << m_day << " " << std::setw(2) << m_hour << ":" << std::setw(2) << m_minute << ":" << std::setw(2)
          << m_second << " UTC";

   return output.str();
}

bool ASN1_Time::time_is_set() const {
   return (m_year != 0);
}

int32_t ASN1_Time::cmp(const ASN1_Time& other) const {
   if(!time_is_set() || !other.time_is_set()) {
      throw Invalid_State("ASN1_Time::cmp: Cannot compare empty times");
   }

   constexpr int32_t EARLIER = -1;
   constexpr int32_t LATER = 1;
   constexpr int32_t SAME_TIME = 0;

   if(m_year < other.m_year) {
      return EARLIER;
   }
   if(m_year > other.m_year) {
      return LATER;
   }
   if(m_month < other.m_month) {
      return EARLIER;
   }
   if(m_month > other.m_month) {
      return LATER;
   }
   if(m_day < other.m_day) {
      return EARLIER;
   }
   if(m_day > other.m_day) {
      return LATER;
   }
   if(m_hour < other.m_hour) {
      return EARLIER;
   }
   if(m_hour > other.m_hour) {
      return LATER;
   }
   if(m_minute < other.m_minute) {
      return EARLIER;
   }
   if(m_minute > other.m_minute) {
      return LATER;
   }
   if(m_second < other.m_second) {
      return EARLIER;
   }
   if(m_second > other.m_second) {
      return LATER;
   }

   return SAME_TIME;
}

void ASN1_Time::set_to(std::string_view t_spec, ASN1_Type spec_tag) {
   BOTAN_ARG_CHECK(spec_tag == ASN1_Type::UtcTime || spec_tag == ASN1_Type::GeneralizedTime,
                   "Invalid tag for ASN1_Time");

   if(spec_tag == ASN1_Type::GeneralizedTime) {
      BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime input string");
   } else if(spec_tag == ASN1_Type::UtcTime) {
      BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime input string");
   }

   BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support ASN1 times with timezones other than Z");

   const size_t field_len = 2;

   const size_t year_start = 0;
   const size_t year_len = (spec_tag == ASN1_Type::UtcTime) ? 2 : 4;
   const size_t month_start = year_start + year_len;
   const size_t day_start = month_start + field_len;
   const size_t hour_start = day_start + field_len;
   const size_t min_start = hour_start + field_len;
   const size_t sec_start = min_start + field_len;

   m_year = to_u32bit(t_spec.substr(year_start, year_len));
   m_month = to_u32bit(t_spec.substr(month_start, field_len));
   m_day = to_u32bit(t_spec.substr(day_start, field_len));
   m_hour = to_u32bit(t_spec.substr(hour_start, field_len));
   m_minute = to_u32bit(t_spec.substr(min_start, field_len));
   m_second = to_u32bit(t_spec.substr(sec_start, field_len));
   m_tag = spec_tag;

   if(spec_tag == ASN1_Type::UtcTime) {
      if(m_year >= 50) {
         m_year += 1900;
      } else {
         m_year += 2000;
      }
   }

   if(!passes_sanity_check()) {
      throw Invalid_Argument(fmt("ASN1_Time string '{}' does not seem to be valid", t_spec));
   }
}

/*
* Do a general sanity check on the time
*/
bool ASN1_Time::passes_sanity_check() const {
   // AppVeyor's trust store includes a cert with expiration date in 3016 ...
   if(m_year < 1950 || m_year > 3100) {
      return false;
   }
   if(m_month == 0 || m_month > 12) {
      return false;
   }

   const uint32_t days_in_month[12] = {31, 28 + 1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

   if(m_day == 0 || m_day > days_in_month[m_month - 1]) {
      return false;
   }

   if(m_month == 2 && m_day == 29) {
      if(m_year % 4 != 0) {
         return false;  // not a leap year
      }

      if(m_year % 100 == 0 && m_year % 400 != 0) {
         return false;
      }
   }

   if(m_hour >= 24 || m_minute >= 60 || m_second > 60) {
      return false;
   }

   if(m_tag == ASN1_Type::UtcTime) {
      /*
      UTCTime limits the value of components such that leap seconds
      are not covered. See "UNIVERSAL 23" in "Information technology
      Abstract Syntax Notation One (ASN.1): Specification of basic notation"

      http://www.itu.int/ITU-T/studygroups/com17/languages/
      */
      if(m_second > 59) {
         return false;
      }
   }

   return true;
}

std::chrono::system_clock::time_point ASN1_Time::to_std_timepoint() const {
   return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
}

uint64_t ASN1_Time::time_since_epoch() const {
   return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).seconds_since_epoch();
}

/*
* Compare two ASN1_Times for in various ways
*/
bool operator==(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) == 0);
}

bool operator!=(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) != 0);
}

bool operator<=(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) <= 0);
}

bool operator>=(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) >= 0);
}

bool operator<(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) < 0);
}

bool operator>(const ASN1_Time& t1, const ASN1_Time& t2) {
   return (t1.cmp(t2) > 0);
}

}  // namespace Botan
/*
* BER Decoder
* (C) 1999-2008,2015,2017,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <memory>

namespace Botan {

namespace {

/*
* This value is somewhat arbitrary. OpenSSL allows up to 128 nested
* indefinite length sequences. If you increase this, also increase the
* limit in the test in test_asn1.cpp
*/
const size_t ALLOWED_EOC_NESTINGS = 16;

/*
* BER decode an ASN.1 type tag
*/
size_t decode_tag(DataSource* ber, ASN1_Type& type_tag, ASN1_Class& class_tag) {
   auto b = ber->read_byte();

   if(!b) {
      type_tag = ASN1_Type::NoObject;
      class_tag = ASN1_Class::NoObject;
      return 0;
   }

   if((*b & 0x1F) != 0x1F) {
      type_tag = ASN1_Type(*b & 0x1F);
      class_tag = ASN1_Class(*b & 0xE0);
      return 1;
   }

   size_t tag_bytes = 1;
   class_tag = ASN1_Class(*b & 0xE0);

   size_t tag_buf = 0;
   while(true) {
      b = ber->read_byte();
      if(!b) {
         throw BER_Decoding_Error("Long-form tag truncated");
      }
      if((tag_buf >> 24) != 0) {
         throw BER_Decoding_Error("Long-form tag overflowed 32 bits");
      }
      // This is required even by BER (see X.690 section 8.1.2.4.2 sentence c)
      if(tag_bytes == 0 && b == 0x80) {
         throw BER_Decoding_Error("Long form tag with leading zero");
      }
      ++tag_bytes;
      tag_buf = (tag_buf << 7) | (*b & 0x7F);
      if((*b & 0x80) == 0) {
         break;
      }
   }
   type_tag = ASN1_Type(tag_buf);
   return tag_bytes;
}

/*
* Find the EOC marker
*/
size_t find_eoc(DataSource* src, size_t allow_indef);

/*
* BER decode an ASN.1 length field
*/
size_t decode_length(DataSource* ber, size_t& field_size, size_t allow_indef) {
   uint8_t b = 0;
   if(ber->read_byte(b) == 0) {
      throw BER_Decoding_Error("Length field not found");
   }
   field_size = 1;
   if((b & 0x80) == 0) {
      return b;
   }

   field_size += (b & 0x7F);
   if(field_size > 5) {
      throw BER_Decoding_Error("Length field is too large");
   }

   if(field_size == 1) {
      if(allow_indef == 0) {
         throw BER_Decoding_Error("Nested EOC markers too deep, rejecting to avoid stack exhaustion");
      } else {
         return find_eoc(ber, allow_indef - 1);
      }
   }

   size_t length = 0;

   for(size_t i = 0; i != field_size - 1; ++i) {
      if(get_byte<0>(length) != 0) {
         throw BER_Decoding_Error("Field length overflow");
      }
      if(ber->read_byte(b) == 0) {
         throw BER_Decoding_Error("Corrupted length field");
      }
      length = (length << 8) | b;
   }
   return length;
}

/*
* Find the EOC marker
*/
size_t find_eoc(DataSource* ber, size_t allow_indef) {
   secure_vector<uint8_t> buffer(DefaultBufferSize);
   secure_vector<uint8_t> data;

   while(true) {
      const size_t got = ber->peek(buffer.data(), buffer.size(), data.size());
      if(got == 0) {
         break;
      }

      data += std::make_pair(buffer.data(), got);
   }

   DataSource_Memory source(data);
   data.clear();

   size_t length = 0;
   while(true) {
      ASN1_Type type_tag = ASN1_Type::NoObject;
      ASN1_Class class_tag = ASN1_Class::NoObject;
      const size_t tag_size = decode_tag(&source, type_tag, class_tag);
      if(type_tag == ASN1_Type::NoObject) {
         break;
      }

      size_t length_size = 0;
      const size_t item_size = decode_length(&source, length_size, allow_indef);
      source.discard_next(item_size);

      if(auto new_len = checked_add(length, item_size, tag_size, length_size)) {
         length = new_len.value();
      } else {
         throw Decoding_Error("Integer overflow while decoding DER");
      }

      if(type_tag == ASN1_Type::Eoc && class_tag == ASN1_Class::Universal) {
         break;
      }
   }
   return length;
}

class DataSource_BERObject final : public DataSource {
   public:
      size_t read(uint8_t out[], size_t length) override {
         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
         const size_t got = std::min<size_t>(m_obj.length() - m_offset, length);
         copy_mem(out, m_obj.bits() + m_offset, got);
         m_offset += got;
         return got;
      }

      size_t peek(uint8_t out[], size_t length, size_t peek_offset) const override {
         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
         const size_t bytes_left = m_obj.length() - m_offset;

         if(peek_offset >= bytes_left) {
            return 0;
         }

         const size_t got = std::min(bytes_left - peek_offset, length);
         copy_mem(out, m_obj.bits() + m_offset + peek_offset, got);
         return got;
      }

      bool check_available(size_t n) override {
         BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
         return (n <= (m_obj.length() - m_offset));
      }

      bool end_of_data() const override { return get_bytes_read() == m_obj.length(); }

      size_t get_bytes_read() const override { return m_offset; }

      explicit DataSource_BERObject(BER_Object&& obj) : m_obj(std::move(obj)) {}

   private:
      BER_Object m_obj;
      size_t m_offset = 0;
};

}  // namespace

/*
* Check if more objects are there
*/
bool BER_Decoder::more_items() const {
   if(m_source->end_of_data() && !m_pushed.is_set()) {
      return false;
   }
   return true;
}

/*
* Verify that no bytes remain in the source
*/
BER_Decoder& BER_Decoder::verify_end() {
   return verify_end("BER_Decoder::verify_end called, but data remains");
}

/*
* Verify that no bytes remain in the source
*/
BER_Decoder& BER_Decoder::verify_end(std::string_view err) {
   if(!m_source->end_of_data() || m_pushed.is_set()) {
      throw Decoding_Error(err);
   }
   return (*this);
}

/*
* Discard all the bytes remaining in the source
*/
BER_Decoder& BER_Decoder::discard_remaining() {
   uint8_t buf = 0;
   while(m_source->read_byte(buf) != 0) {}
   return (*this);
}

const BER_Object& BER_Decoder::peek_next_object() {
   if(!m_pushed.is_set()) {
      m_pushed = get_next_object();
   }

   return m_pushed;
}

/*
* Return the BER encoding of the next object
*/
BER_Object BER_Decoder::get_next_object() {
   BER_Object next;

   if(m_pushed.is_set()) {
      std::swap(next, m_pushed);
      return next;
   }

   for(;;) {
      ASN1_Type type_tag = ASN1_Type::NoObject;
      ASN1_Class class_tag = ASN1_Class::NoObject;
      decode_tag(m_source, type_tag, class_tag);
      next.set_tagging(type_tag, class_tag);
      if(next.is_set() == false) {  // no more objects
         return next;
      }

      size_t field_size = 0;
      const size_t length = decode_length(m_source, field_size, ALLOWED_EOC_NESTINGS);
      if(!m_source->check_available(length)) {
         throw BER_Decoding_Error("Value truncated");
      }

      uint8_t* out = next.mutable_bits(length);
      if(m_source->read(out, length) != length) {
         throw BER_Decoding_Error("Value truncated");
      }

      if(next.tagging() == static_cast<uint32_t>(ASN1_Type::Eoc)) {
         continue;
      } else {
         break;
      }
   }

   return next;
}

/*
* Push a object back into the stream
*/
void BER_Decoder::push_back(const BER_Object& obj) {
   if(m_pushed.is_set()) {
      throw Invalid_State("BER_Decoder: Only one push back is allowed");
   }
   m_pushed = obj;
}

void BER_Decoder::push_back(BER_Object&& obj) {
   if(m_pushed.is_set()) {
      throw Invalid_State("BER_Decoder: Only one push back is allowed");
   }
   m_pushed = std::move(obj);
}

BER_Decoder BER_Decoder::start_cons(ASN1_Type type_tag, ASN1_Class class_tag) {
   BER_Object obj = get_next_object();
   obj.assert_is_a(type_tag, class_tag | ASN1_Class::Constructed);
   return BER_Decoder(std::move(obj), this);
}

/*
* Finish decoding a CONSTRUCTED type
*/
BER_Decoder& BER_Decoder::end_cons() {
   if(m_parent == nullptr) {
      throw Invalid_State("BER_Decoder::end_cons called with null parent");
   }
   if(!m_source->end_of_data()) {
      throw Decoding_Error("BER_Decoder::end_cons called with data left");
   }
   return (*m_parent);
}

BER_Decoder::BER_Decoder(BER_Object&& obj, BER_Decoder* parent) : m_parent(parent) {
   m_data_src = std::make_unique<DataSource_BERObject>(std::move(obj));
   m_source = m_data_src.get();
}

/*
* BER_Decoder Constructor
*/
BER_Decoder::BER_Decoder(DataSource& src) : m_source(&src) {}

/*
* BER_Decoder Constructor
 */
BER_Decoder::BER_Decoder(const uint8_t data[], size_t length) {
   m_data_src = std::make_unique<DataSource_Memory>(data, length);
   m_source = m_data_src.get();
}

/*
* BER_Decoder Constructor
*/
BER_Decoder::BER_Decoder(const secure_vector<uint8_t>& data) {
   m_data_src = std::make_unique<DataSource_Memory>(data);
   m_source = m_data_src.get();
}

/*
* BER_Decoder Constructor
*/
BER_Decoder::BER_Decoder(const std::vector<uint8_t>& data) {
   m_data_src = std::make_unique<DataSource_Memory>(data.data(), data.size());
   m_source = m_data_src.get();
}

/*
* BER_Decoder Copy Constructor
*/
BER_Decoder::BER_Decoder(const BER_Decoder& other) : m_parent(other.m_parent), m_source(other.m_source) {
   // take ownership of other's data source
   std::swap(m_data_src, other.m_data_src);
}

/*
* Request for an object to decode itself
*/
BER_Decoder& BER_Decoder::decode(ASN1_Object& obj, ASN1_Type /*unused*/, ASN1_Class /*unused*/) {
   obj.decode_from(*this);
   return (*this);
}

/*
* Decode a BER encoded NULL
*/
BER_Decoder& BER_Decoder::decode_null() {
   BER_Object obj = get_next_object();
   obj.assert_is_a(ASN1_Type::Null, ASN1_Class::Universal);
   if(obj.length() > 0) {
      throw BER_Decoding_Error("NULL object had nonzero size");
   }
   return (*this);
}

BER_Decoder& BER_Decoder::decode_octet_string_bigint(BigInt& out) {
   secure_vector<uint8_t> out_vec;
   decode(out_vec, ASN1_Type::OctetString);
   out = BigInt::from_bytes(out_vec);
   return (*this);
}

/*
* Decode a BER encoded BOOLEAN
*/
BER_Decoder& BER_Decoder::decode(bool& out, ASN1_Type type_tag, ASN1_Class class_tag) {
   BER_Object obj = get_next_object();
   obj.assert_is_a(type_tag, class_tag);

   if(obj.length() != 1) {
      throw BER_Decoding_Error("BER boolean value had invalid size");
   }

   const uint8_t val = obj.bits()[0];

   // TODO if decoding DER we should reject non-canonical booleans
   out = (val != 0) ? true : false;

   return (*this);
}

/*
* Decode a small BER encoded INTEGER
*/
BER_Decoder& BER_Decoder::decode(size_t& out, ASN1_Type type_tag, ASN1_Class class_tag) {
   BigInt integer;
   decode(integer, type_tag, class_tag);

   if(integer.is_negative()) {
      throw BER_Decoding_Error("Decoded small integer value was negative");
   }

   if(integer.bits() > 32) {
      throw BER_Decoding_Error("Decoded integer value larger than expected");
   }

   out = 0;
   for(size_t i = 0; i != 4; ++i) {
      out = (out << 8) | integer.byte_at(3 - i);
   }

   return (*this);
}

/*
* Decode a small BER encoded INTEGER
*/
uint64_t BER_Decoder::decode_constrained_integer(ASN1_Type type_tag, ASN1_Class class_tag, size_t T_bytes) {
   if(T_bytes > 8) {
      throw BER_Decoding_Error("Can't decode small integer over 8 bytes");
   }

   BigInt integer;
   decode(integer, type_tag, class_tag);

   if(integer.bits() > 8 * T_bytes) {
      throw BER_Decoding_Error("Decoded integer value larger than expected");
   }

   uint64_t out = 0;
   for(size_t i = 0; i != 8; ++i) {
      out = (out << 8) | integer.byte_at(7 - i);
   }

   return out;
}

/*
* Decode a BER encoded INTEGER
*/
BER_Decoder& BER_Decoder::decode(BigInt& out, ASN1_Type type_tag, ASN1_Class class_tag) {
   BER_Object obj = get_next_object();
   obj.assert_is_a(type_tag, class_tag);

   if(obj.length() == 0) {
      out.clear();
   } else {
      const uint8_t first = obj.bits()[0];
      const bool negative = (first & 0x80) == 0x80;

      if(negative) {
         secure_vector<uint8_t> vec(obj.bits(), obj.bits() + obj.length());
         for(size_t i = obj.length(); i > 0; --i) {
            const bool gt0 = (vec[i - 1] > 0);
            vec[i - 1] -= 1;
            if(gt0) {
               break;
            }
         }
         for(size_t i = 0; i != obj.length(); ++i) {
            vec[i] = ~vec[i];
         }
         out._assign_from_bytes(vec);
         out.flip_sign();
      } else {
         out._assign_from_bytes(obj.data());
      }
   }

   return (*this);
}

namespace {

template <typename Alloc>
void asn1_decode_binary_string(std::vector<uint8_t, Alloc>& buffer,
                               const BER_Object& obj,
                               ASN1_Type real_type,
                               ASN1_Type type_tag,
                               ASN1_Class class_tag) {
   obj.assert_is_a(type_tag, class_tag);

   if(real_type == ASN1_Type::OctetString) {
      buffer.assign(obj.bits(), obj.bits() + obj.length());
   } else {
      if(obj.length() == 0) {
         throw BER_Decoding_Error("Invalid BIT STRING");
      }
      if(obj.bits()[0] >= 8) {
         throw BER_Decoding_Error("Bad number of unused bits in BIT STRING");
      }

      buffer.resize(obj.length() - 1);

      if(obj.length() > 1) {
         copy_mem(buffer.data(), obj.bits() + 1, obj.length() - 1);
      }
   }
}

}  // namespace

/*
* BER decode a BIT STRING or OCTET STRING
*/
BER_Decoder& BER_Decoder::decode(secure_vector<uint8_t>& buffer,
                                 ASN1_Type real_type,
                                 ASN1_Type type_tag,
                                 ASN1_Class class_tag) {
   if(real_type != ASN1_Type::OctetString && real_type != ASN1_Type::BitString) {
      throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", static_cast<uint32_t>(real_type));
   }

   asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
   return (*this);
}

BER_Decoder& BER_Decoder::decode(std::vector<uint8_t>& buffer,
                                 ASN1_Type real_type,
                                 ASN1_Type type_tag,
                                 ASN1_Class class_tag) {
   if(real_type != ASN1_Type::OctetString && real_type != ASN1_Type::BitString) {
      throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", static_cast<uint32_t>(real_type));
   }

   asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
   return (*this);
}

}  // namespace Botan
/*
* DER Encoder
* (C) 1999-2007,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

/*
* DER encode an ASN.1 type tag
*/
void encode_tag(std::vector<uint8_t>& encoded_tag, ASN1_Type type_tag_e, ASN1_Class class_tag_e) {
   const uint32_t type_tag = static_cast<uint32_t>(type_tag_e);
   const uint32_t class_tag = static_cast<uint32_t>(class_tag_e);

   if((class_tag | 0xE0) != 0xE0) {
      throw Encoding_Error(fmt("DER_Encoder: Invalid class tag {}", std::to_string(class_tag)));
   }

   if(type_tag <= 30) {
      encoded_tag.push_back(static_cast<uint8_t>(type_tag | class_tag));
   } else {
      size_t blocks = high_bit(static_cast<uint32_t>(type_tag)) + 6;
      blocks = (blocks - (blocks % 7)) / 7;

      BOTAN_ASSERT_NOMSG(blocks > 0);

      encoded_tag.push_back(static_cast<uint8_t>(class_tag | 0x1F));
      for(size_t i = 0; i != blocks - 1; ++i) {
         encoded_tag.push_back(0x80 | ((type_tag >> 7 * (blocks - i - 1)) & 0x7F));
      }
      encoded_tag.push_back(type_tag & 0x7F);
   }
}

/*
* DER encode an ASN.1 length field
*/
void encode_length(std::vector<uint8_t>& encoded_length, size_t length) {
   if(length <= 127) {
      encoded_length.push_back(static_cast<uint8_t>(length));
   } else {
      const size_t bytes_needed = significant_bytes(length);

      encoded_length.push_back(static_cast<uint8_t>(0x80 | bytes_needed));

      for(size_t i = sizeof(length) - bytes_needed; i < sizeof(length); ++i) {
         encoded_length.push_back(get_byte_var(i, length));
      }
   }
}

}  // namespace

DER_Encoder::DER_Encoder(secure_vector<uint8_t>& vec) {
   m_append_output = [&vec](const uint8_t b[], size_t l) { vec.insert(vec.end(), b, b + l); };
}

DER_Encoder::DER_Encoder(std::vector<uint8_t>& vec) {
   m_append_output = [&vec](const uint8_t b[], size_t l) { vec.insert(vec.end(), b, b + l); };
}

/*
* Push the encoded SEQUENCE/SET to the encoder stream
*/
void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der) {
   const auto real_class_tag = m_class_tag | ASN1_Class::Constructed;

   if(m_type_tag == ASN1_Type::Set) {
      std::sort(m_set_contents.begin(), m_set_contents.end());
      for(const auto& set_elem : m_set_contents) {
         m_contents += set_elem;
      }
      m_set_contents.clear();
   }

   der.add_object(m_type_tag, real_class_tag, m_contents.data(), m_contents.size());
   m_contents.clear();
}

/*
* Add an encoded value to the SEQUENCE/SET
*/
void DER_Encoder::DER_Sequence::add_bytes(const uint8_t data[], size_t length) {
   if(m_type_tag == ASN1_Type::Set) {
      m_set_contents.push_back(secure_vector<uint8_t>(data, data + length));
   } else {
      m_contents += std::make_pair(data, length);
   }
}

void DER_Encoder::DER_Sequence::add_bytes(const uint8_t hdr[], size_t hdr_len, const uint8_t val[], size_t val_len) {
   if(m_type_tag == ASN1_Type::Set) {
      secure_vector<uint8_t> m;
      m.reserve(hdr_len + val_len);
      m += std::make_pair(hdr, hdr_len);
      m += std::make_pair(val, val_len);
      m_set_contents.push_back(std::move(m));
   } else {
      m_contents += std::make_pair(hdr, hdr_len);
      m_contents += std::make_pair(val, val_len);
   }
}

/*
* Return the type and class taggings
*/
uint32_t DER_Encoder::DER_Sequence::tag_of() const {
   return m_type_tag | m_class_tag;
}

/*
* DER_Sequence Constructor
*/
DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Type type_tag, ASN1_Class class_tag) :
      m_type_tag(type_tag), m_class_tag(class_tag) {}

/*
* Return the encoded contents
*/
secure_vector<uint8_t> DER_Encoder::get_contents() {
   if(!m_subsequences.empty()) {
      throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
   }

   if(m_append_output) {
      throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
   }

   secure_vector<uint8_t> output;
   std::swap(output, m_default_outbuf);
   return output;
}

std::vector<uint8_t> DER_Encoder::get_contents_unlocked() {
   if(!m_subsequences.empty()) {
      throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
   }

   if(m_append_output) {
      throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
   }

   std::vector<uint8_t> output(m_default_outbuf.begin(), m_default_outbuf.end());
   m_default_outbuf.clear();
   return output;
}

/*
* Start a new ASN.1 SEQUENCE/SET/EXPLICIT
*/
DER_Encoder& DER_Encoder::start_cons(ASN1_Type type_tag, ASN1_Class class_tag) {
   m_subsequences.push_back(DER_Sequence(type_tag, class_tag));
   return (*this);
}

/*
* Finish the current ASN.1 SEQUENCE/SET/EXPLICIT
*/
DER_Encoder& DER_Encoder::end_cons() {
   if(m_subsequences.empty()) {
      throw Invalid_State("DER_Encoder::end_cons: No such sequence");
   }

   DER_Sequence last_seq = std::move(m_subsequences[m_subsequences.size() - 1]);
   m_subsequences.pop_back();
   last_seq.push_contents(*this);

   return (*this);
}

/*
* Start a new ASN.1 EXPLICIT encoding
*/
DER_Encoder& DER_Encoder::start_explicit(uint16_t type_no) {
   ASN1_Type type_tag = static_cast<ASN1_Type>(type_no);

   // This would confuse DER_Sequence
   if(type_tag == ASN1_Type::Set) {
      throw Internal_Error("DER_Encoder.start_explicit(SET) not supported");
   }

   return start_cons(type_tag, ASN1_Class::ContextSpecific);
}

/*
* Finish the current ASN.1 EXPLICIT encoding
*/
DER_Encoder& DER_Encoder::end_explicit() {
   return end_cons();
}

/*
* Write raw bytes into the stream
*/
DER_Encoder& DER_Encoder::raw_bytes(const uint8_t bytes[], size_t length) {
   if(!m_subsequences.empty()) {
      m_subsequences[m_subsequences.size() - 1].add_bytes(bytes, length);
   } else if(m_append_output) {
      m_append_output(bytes, length);
   } else {
      m_default_outbuf += std::make_pair(bytes, length);
   }

   return (*this);
}

/*
* Write the encoding of the byte(s)
*/
DER_Encoder& DER_Encoder::add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length) {
   std::vector<uint8_t> hdr;
   encode_tag(hdr, type_tag, class_tag);
   encode_length(hdr, length);

   if(!m_subsequences.empty()) {
      m_subsequences[m_subsequences.size() - 1].add_bytes(hdr.data(), hdr.size(), rep, length);
   } else if(m_append_output) {
      m_append_output(hdr.data(), hdr.size());
      m_append_output(rep, length);
   } else {
      m_default_outbuf += hdr;
      m_default_outbuf += std::make_pair(rep, length);
   }

   return (*this);
}

/*
* Encode a NULL object
*/
DER_Encoder& DER_Encoder::encode_null() {
   return add_object(ASN1_Type::Null, ASN1_Class::Universal, nullptr, 0);
}

/*
* DER encode a BOOLEAN
*/
DER_Encoder& DER_Encoder::encode(bool is_true) {
   return encode(is_true, ASN1_Type::Boolean, ASN1_Class::Universal);
}

/*
* DER encode a small INTEGER
*/
DER_Encoder& DER_Encoder::encode(size_t n) {
   return encode(BigInt::from_u64(n), ASN1_Type::Integer, ASN1_Class::Universal);
}

/*
* DER encode a small INTEGER
*/
DER_Encoder& DER_Encoder::encode(const BigInt& n) {
   return encode(n, ASN1_Type::Integer, ASN1_Class::Universal);
}

/*
* Encode this object
*/
DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length, ASN1_Type real_type) {
   return encode(bytes, length, real_type, real_type, ASN1_Class::Universal);
}

/*
* DER encode a BOOLEAN
*/
DER_Encoder& DER_Encoder::encode(bool is_true, ASN1_Type type_tag, ASN1_Class class_tag) {
   uint8_t val = is_true ? 0xFF : 0x00;
   return add_object(type_tag, class_tag, &val, 1);
}

/*
* DER encode a small INTEGER
*/
DER_Encoder& DER_Encoder::encode(size_t n, ASN1_Type type_tag, ASN1_Class class_tag) {
   return encode(BigInt::from_u64(n), type_tag, class_tag);
}

/*
* DER encode an INTEGER
*/
DER_Encoder& DER_Encoder::encode(const BigInt& n, ASN1_Type type_tag, ASN1_Class class_tag) {
   if(n == 0) {
      return add_object(type_tag, class_tag, 0);
   }

   const size_t extra_zero = (n.bits() % 8 == 0) ? 1 : 0;

   auto contents = n.serialize(n.bytes() + extra_zero);
   if(n < 0) {
      for(unsigned char& content : contents) {
         content = ~content;
      }
      for(size_t i = contents.size(); i > 0; --i) {
         contents[i - 1] += 1;
         if(contents[i - 1] != 0) {
            break;
         }
      }
   }

   return add_object(type_tag, class_tag, contents);
}

/*
* DER encode an OCTET STRING or BIT STRING
*/
DER_Encoder& DER_Encoder::encode(
   const uint8_t bytes[], size_t length, ASN1_Type real_type, ASN1_Type type_tag, ASN1_Class class_tag) {
   if(real_type != ASN1_Type::OctetString && real_type != ASN1_Type::BitString) {
      throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string");
   }

   if(real_type == ASN1_Type::BitString) {
      secure_vector<uint8_t> encoded;
      encoded.push_back(0);
      encoded += std::make_pair(bytes, length);
      return add_object(type_tag, class_tag, encoded);
   } else {
      return add_object(type_tag, class_tag, bytes, length);
   }
}

DER_Encoder& DER_Encoder::encode(const ASN1_Object& obj) {
   obj.encode_into(*this);
   return (*this);
}

/*
* Write the encoding of the byte(s)
*/
DER_Encoder& DER_Encoder::add_object(ASN1_Type type_tag, ASN1_Class class_tag, std::string_view rep_str) {
   return add_object(type_tag, class_tag, as_span_of_bytes(rep_str));
}

/*
* Write the encoding of the byte
*/
DER_Encoder& DER_Encoder::add_object(ASN1_Type type_tag, ASN1_Class class_tag, uint8_t rep) {
   return add_object(type_tag, class_tag, std::span<const uint8_t>{&rep, 1});
}

}  // namespace Botan
/*
* (C) 2023 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


namespace Botan {

OID_Map::OID_Map() {
   m_str2oid = OID_Map::load_str2oid_map();
   m_oid2str = OID_Map::load_oid2str_map();
}

OID_Map& OID_Map::global_registry() {
   static OID_Map g_map;
   return g_map;
}

void OID_Map::add_oid(const OID& oid, std::string_view str) {
   if(auto name = lookup_static_oid(oid)) {
      if(*name != str) {
         throw Invalid_State("Cannot register two different names to a single OID");
      } else {
         return;
      }
   }

   lock_guard_type<mutex_type> lock(m_mutex);

   auto o2s = m_oid2str.find(oid);

   if(o2s == m_oid2str.end()) {
      m_oid2str.insert(std::make_pair(oid, str));
   } else if(o2s->second != str) {
      throw Invalid_State("Cannot register two different names to a single OID");
   }

   auto s2o = m_str2oid.find(std::string(str));

   if(s2o == m_str2oid.end()) {
      m_str2oid.insert(std::make_pair(str, oid));
   }
}

void OID_Map::add_str2oid(const OID& oid, std::string_view str) {
   if(lookup_static_oid_name(str).has_value()) {
      return;
   }

   lock_guard_type<mutex_type> lock(m_mutex);
   if(!m_str2oid.contains(std::string(str))) {
      m_str2oid.insert(std::make_pair(str, oid));
   }
}

void OID_Map::add_oid2str(const OID& oid, std::string_view str) {
   if(lookup_static_oid(oid).has_value()) {
      return;
   }

   lock_guard_type<mutex_type> lock(m_mutex);
   if(!m_oid2str.contains(oid)) {
      m_oid2str.insert(std::make_pair(oid, str));
   }
}

std::string OID_Map::oid2str(const OID& oid) {
   if(auto name = lookup_static_oid(oid)) {
      return std::string(*name);
   }

   lock_guard_type<mutex_type> lock(m_mutex);

   auto i = m_oid2str.find(oid);
   if(i != m_oid2str.end()) {
      return i->second;
   }

   return "";
}

OID OID_Map::str2oid(std::string_view str) {
   if(auto oid = lookup_static_oid_name(str)) {
      return std::move(*oid);
   }

   lock_guard_type<mutex_type> lock(m_mutex);
   auto i = m_str2oid.find(std::string(str));
   if(i != m_str2oid.end()) {
      return i->second;
   }

   return OID();
}

}  // namespace Botan
/*
* OID Registry
* (C) 1999-2008,2013 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

void OIDS::add_oid2str(const OID& oid, std::string_view name) {
   OID_Map::global_registry().add_oid2str(oid, name);
}

void OIDS::add_str2oid(const OID& oid, std::string_view name) {
   OID_Map::global_registry().add_str2oid(oid, name);
}

}  // namespace Botan
/*
* (C) 2017 Daniel Neus
*     2023 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

//static
PSS_Params PSS_Params::from_padding_name(std::string_view padding_name) {
   SCAN_Name scanner(padding_name);

   if((scanner.algo_name() != "PSS" && scanner.algo_name() != "PSS_Raw") || scanner.arg_count() != 3) {
      throw Invalid_Argument(fmt("PSS_Params::from_padding_name unexpected param '{}'", padding_name));
   }

   const std::string hash_fn = scanner.arg(0);
   BOTAN_ASSERT_NOMSG(scanner.arg(1) == "MGF1");
   const size_t salt_len = scanner.arg_as_integer(2);
   return PSS_Params(hash_fn, salt_len);
}

PSS_Params::PSS_Params(std::string_view hash_fn, size_t salt_len) :
      m_hash(hash_fn, AlgorithmIdentifier::USE_NULL_PARAM),
      m_mgf("MGF1", m_hash.BER_encode()),
      m_mgf_hash(m_hash),
      m_salt_len(salt_len),
      m_trailer_field(1) {}

PSS_Params::PSS_Params(std::span<const uint8_t> der) : m_salt_len(0), m_trailer_field(1) {
   BER_Decoder decoder(der);
   this->decode_from(decoder);
}

std::vector<uint8_t> PSS_Params::serialize() const {
   std::vector<uint8_t> output;
   DER_Encoder(output).encode(*this);
   return output;
}

void PSS_Params::encode_into(DER_Encoder& to) const {
   to.start_sequence()
      .start_context_specific(0)
      .encode(m_hash)
      .end_cons()
      .start_context_specific(1)
      .encode(m_mgf)
      .end_cons()
      .start_context_specific(2)
      .encode(m_salt_len)
      .end_cons()
      .end_cons();
}

void PSS_Params::decode_from(BER_Decoder& from) {
   const AlgorithmIdentifier default_hash("SHA-1", AlgorithmIdentifier::USE_NULL_PARAM);
   const AlgorithmIdentifier default_mgf("MGF1", default_hash.BER_encode());
   const size_t default_salt_len = 20;
   const size_t default_trailer = 1;

   from.start_sequence()
      .decode_optional(m_hash, ASN1_Type(0), ASN1_Class::ExplicitContextSpecific, default_hash)
      .decode_optional(m_mgf, ASN1_Type(1), ASN1_Class::ExplicitContextSpecific, default_mgf)
      .decode_optional(m_salt_len, ASN1_Type(2), ASN1_Class::ExplicitContextSpecific, default_salt_len)
      .decode_optional(m_trailer_field, ASN1_Type(3), ASN1_Class::ExplicitContextSpecific, default_trailer)
      .end_cons();

   BER_Decoder(m_mgf.parameters()).decode(m_mgf_hash);
}

}  // namespace Botan
/*
* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2025-06-04
*
* All manual edits to this file will be lost. Edit the script then regenerate this source file.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <initializer_list>
#include <unordered_map>

namespace Botan {

namespace {

// The hash can collide so we must verify the actual value matches before returning
std::optional<std::string_view> if_match(const OID& oid, std::initializer_list<uint32_t> val, std::string_view name) {
   if(oid.matches(val)) {
      return name;
   } else {
      return {};
   }
}

std::optional<OID> if_match(std::string_view req, std::string_view actual, std::initializer_list<uint32_t> oid) {
   if(req == actual) {
      return OID(oid);
   } else {
      return {};
   }
}

uint32_t hash_oid_name(std::string_view s) {
   uint64_t hash = 0x8188B31879A4879A;

   for(char c : s) {
      hash *= 251;
      hash += c;
   }

   return static_cast<uint32_t>(hash % 805289);
}

}  // namespace

//static
std::optional<std::string_view> OID_Map::lookup_static_oid(const OID& oid) {
   const uint32_t hc = static_cast<uint32_t>(oid.hash_code() % 858701);

   switch(hc) {
      case 0x01506:
         return if_match(oid, {1, 2, 840, 10045, 4, 3, 1}, "ECDSA/SHA-224");
      case 0x01507:
         return if_match(oid, {1, 2, 840, 10045, 4, 3, 2}, "ECDSA/SHA-256");
      case 0x01508:
         return if_match(oid, {1, 2, 840, 10045, 4, 3, 3}, "ECDSA/SHA-384");
      case 0x01509:
         return if_match(oid, {1, 2, 840, 10045, 4, 3, 4}, "ECDSA/SHA-512");
      case 0x04C1E:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 3029, 1, 2, 1}, "ElGamal");
      case 0x04E61:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 3029, 1, 5, 1}, "OpenPGP.Curve25519");
      case 0x0779B:
         return if_match(oid, {1, 2, 840, 113549, 2, 5}, "MD5");
      case 0x0779D:
         return if_match(oid, {1, 2, 840, 113549, 2, 7}, "HMAC(SHA-1)");
      case 0x0779E:
         return if_match(oid, {1, 2, 840, 113549, 2, 8}, "HMAC(SHA-224)");
      case 0x0779F:
         return if_match(oid, {1, 2, 840, 113549, 2, 9}, "HMAC(SHA-256)");
      case 0x077A0:
         return if_match(oid, {1, 2, 840, 113549, 2, 10}, "HMAC(SHA-384)");
      case 0x077A1:
         return if_match(oid, {1, 2, 840, 113549, 2, 11}, "HMAC(SHA-512)");
      case 0x077A3:
         return if_match(oid, {1, 2, 840, 113549, 2, 13}, "HMAC(SHA-512-256)");
      case 0x0785E:
         return if_match(oid, {1, 2, 840, 113549, 3, 7}, "TripleDES/CBC");
      case 0x0C904:
         return if_match(oid, {1, 0, 14888, 3, 0, 5}, "ECKCDSA");
      case 0x11547:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1}, "SphincsPlus-shake-128s-r3.1");
      case 0x11548:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2}, "SphincsPlus-shake-128f-r3.1");
      case 0x11549:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3}, "SphincsPlus-shake-192s-r3.1");
      case 0x1154A:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4}, "SphincsPlus-shake-192f-r3.1");
      case 0x1154B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5}, "SphincsPlus-shake-256s-r3.1");
      case 0x1154C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6}, "SphincsPlus-shake-256f-r3.1");
      case 0x11608:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1}, "SphincsPlus-sha2-128s-r3.1");
      case 0x11609:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2}, "SphincsPlus-sha2-128f-r3.1");
      case 0x1160A:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3}, "SphincsPlus-sha2-192s-r3.1");
      case 0x1160B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4}, "SphincsPlus-sha2-192f-r3.1");
      case 0x1160C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5}, "SphincsPlus-sha2-256s-r3.1");
      case 0x1160D:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6}, "SphincsPlus-sha2-256f-r3.1");
      case 0x116C9:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1}, "SphincsPlus-haraka-128s-r3.1");
      case 0x116CA:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2}, "SphincsPlus-haraka-128f-r3.1");
      case 0x116CB:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3}, "SphincsPlus-haraka-192s-r3.1");
      case 0x116CC:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4}, "SphincsPlus-haraka-192f-r3.1");
      case 0x116CD:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5}, "SphincsPlus-haraka-256s-r3.1");
      case 0x116CE:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6}, "SphincsPlus-haraka-256f-r3.1");
      case 0x1533B:
         return if_match(oid, {1, 2, 156, 10197, 1, 104, 2}, "SM4/CBC");
      case 0x15341:
         return if_match(oid, {1, 2, 156, 10197, 1, 104, 8}, "SM4/GCM");
      case 0x1539D:
         return if_match(oid, {1, 2, 156, 10197, 1, 104, 100}, "SM4/OCB");
      case 0x187D7:
         return if_match(oid, {1, 3, 14, 3, 2, 7}, "DES/CBC");
      case 0x187EA:
         return if_match(oid, {1, 3, 14, 3, 2, 26}, "SHA-1");
      case 0x19933:
         return if_match(oid, {1, 3, 132, 0, 8}, "secp160r1");
      case 0x19934:
         return if_match(oid, {1, 3, 132, 0, 9}, "secp160k1");
      case 0x19935:
         return if_match(oid, {1, 3, 132, 0, 10}, "secp256k1");
      case 0x19949:
         return if_match(oid, {1, 3, 132, 0, 30}, "secp160r2");
      case 0x1994A:
         return if_match(oid, {1, 3, 132, 0, 31}, "secp192k1");
      case 0x1994B:
         return if_match(oid, {1, 3, 132, 0, 32}, "secp224k1");
      case 0x1994C:
         return if_match(oid, {1, 3, 132, 0, 33}, "secp224r1");
      case 0x1994D:
         return if_match(oid, {1, 3, 132, 0, 34}, "secp384r1");
      case 0x1994E:
         return if_match(oid, {1, 3, 132, 0, 35}, "secp521r1");
      case 0x199F8:
         return if_match(oid, {1, 3, 132, 1, 12}, "ECDH");
      case 0x1E7BF:
         return if_match(oid, {1, 2, 156, 10197, 1, 301, 1}, "SM2");
      case 0x1E7C0:
         return if_match(oid, {1, 2, 156, 10197, 1, 301, 2}, "SM2_Kex");
      case 0x1E7C1:
         return if_match(oid, {1, 2, 156, 10197, 1, 301, 3}, "SM2_Enc");
      case 0x21960:
         return if_match(oid, {1, 3, 36, 3, 3, 1, 2}, "RSA/PKCS1v15(RIPEMD-160)");
      case 0x2198A:
         return if_match(oid, {1, 2, 840, 113533, 7, 66, 10}, "CAST-128/CBC");
      case 0x2198F:
         return if_match(oid, {1, 2, 840, 113533, 7, 66, 15}, "KeyWrap.CAST-128");
      case 0x227C0:
         return if_match(oid, {1, 3, 101, 110}, "X25519");
      case 0x227C1:
         return if_match(oid, {1, 3, 101, 111}, "X448");
      case 0x227C2:
         return if_match(oid, {1, 3, 101, 112}, "Ed25519");
      case 0x227C3:
         return if_match(oid, {1, 3, 101, 113}, "Ed448");
      case 0x27565:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1, 1}, "PKIX.OCSP.BasicResponse");
      case 0x27569:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1, 5}, "PKIX.OCSP.NoCheck");
      case 0x29F7C:
         return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 3}, "ECKCDSA/SHA-1");
      case 0x29F7D:
         return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 4}, "ECKCDSA/SHA-224");
      case 0x29F7E:
         return if_match(oid, {1, 2, 410, 200004, 1, 100, 4, 5}, "ECKCDSA/SHA-256");
      case 0x2AC3B:
         return if_match(oid, {2, 5, 29, 32, 0}, "X509v3.AnyPolicy");
      case 0x2B5C9:
         return if_match(oid, {1, 2, 840, 10045, 2, 1}, "ECDSA");
      case 0x2B74B:
         return if_match(oid, {1, 2, 840, 10045, 4, 1}, "ECDSA/SHA-1");
      case 0x3474A:
         return if_match(oid, {1, 2, 840, 10046, 2, 1}, "DH");
      case 0x38D6D:
         return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 1, 1}, "gost_256A");
      case 0x38D6E:
         return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 1, 2}, "gost_256B");
      case 0x38E2E:
         return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 2, 1}, "gost_512A");
      case 0x38E2F:
         return if_match(oid, {1, 2, 643, 7, 1, 2, 1, 2, 2}, "gost_512B");
      case 0x38F2C:
         return if_match(oid, {1, 2, 643, 2, 2, 3}, "GOST-34.10/GOST-R-34.11-94");
      case 0x38F3C:
         return if_match(oid, {1, 2, 643, 2, 2, 19}, "GOST-34.10");
      case 0x3D7B8:
         return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 6}, "Camellia-128/GCM");
      case 0x3D7CC:
         return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 26}, "Camellia-192/GCM");
      case 0x3D7E0:
         return if_match(oid, {0, 3, 4401, 5, 3, 1, 9, 46}, "Camellia-256/GCM");
      case 0x3F20F:
         return if_match(oid, {1, 3, 36, 3, 2, 1}, "RIPEMD-160");
      case 0x4266E:
         return if_match(oid, {0, 4, 0, 127, 0, 15, 1, 1, 13, 0}, "XMSS");
      case 0x478C4:
         return if_match(oid, {1, 2, 410, 200004, 1, 4}, "SEED/CBC");
      case 0x47D98:
         return if_match(oid, {1, 2, 156, 10197, 1, 301}, "sm2p256v1");
      case 0x47DFC:
         return if_match(oid, {1, 2, 156, 10197, 1, 401}, "SM3");
      case 0x47E60:
         return if_match(oid, {1, 2, 156, 10197, 1, 501}, "SM2_Sig/SM3");
      case 0x47E63:
         return if_match(oid, {1, 2, 156, 10197, 1, 504}, "RSA/PKCS1v15(SM3)");
      case 0x52B13:
         return if_match(oid, {1, 2, 643, 3, 131, 1, 1}, "GOST.INN");
      case 0x635AE:
         return if_match(oid, {1, 2, 250, 1, 223, 101, 256, 1}, "frp256v1");
      case 0x6EB86:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 6, 1}, "GOST-34.10-2012-256/SHA-256");
      case 0x6EC47:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 1}, "Kyber-512-r3");
      case 0x6EC48:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 2}, "Kyber-768-r3");
      case 0x6EC49:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 7, 3}, "Kyber-1024-r3");
      case 0x6EDC9:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 1}, "Dilithium-4x4-r3");
      case 0x6EDCA:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 2}, "Dilithium-6x5-r3");
      case 0x6EDCB:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 9, 3}, "Dilithium-8x7-r3");
      case 0x6EE8A:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 1}, "Dilithium-4x4-AES-r3");
      case 0x6EE8B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 2}, "Dilithium-6x5-AES-r3");
      case 0x6EE8C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 10, 3}, "Dilithium-8x7-AES-r3");
      case 0x6EF4B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 1}, "Kyber-512-90s-r3");
      case 0x6EF4C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 2}, "Kyber-768-90s-r3");
      case 0x6EF4D:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 11, 3}, "Kyber-1024-90s-r3");
      case 0x6F18E:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 1}, "FrodoKEM-640-SHAKE");
      case 0x6F18F:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 2}, "FrodoKEM-976-SHAKE");
      case 0x6F190:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 14, 3}, "FrodoKEM-1344-SHAKE");
      case 0x6F24F:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 1}, "FrodoKEM-640-AES");
      case 0x6F250:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 2}, "FrodoKEM-976-AES");
      case 0x6F251:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 15, 3}, "FrodoKEM-1344-AES");
      case 0x6F310:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 1}, "eFrodoKEM-640-SHAKE");
      case 0x6F311:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 2}, "eFrodoKEM-976-SHAKE");
      case 0x6F312:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 16, 3}, "eFrodoKEM-1344-SHAKE");
      case 0x6F3D1:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 1}, "eFrodoKEM-640-AES");
      case 0x6F3D2:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 2}, "eFrodoKEM-976-AES");
      case 0x6F3D3:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 17, 3}, "eFrodoKEM-1344-AES");
      case 0x6F492:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 1}, "ClassicMcEliece_6688128pc");
      case 0x6F493:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 2}, "ClassicMcEliece_6688128pcf");
      case 0x6F494:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 3}, "ClassicMcEliece_6960119pc");
      case 0x6F495:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 4}, "ClassicMcEliece_6960119pcf");
      case 0x6F496:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 5}, "ClassicMcEliece_8192128pc");
      case 0x6F497:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 18, 6}, "ClassicMcEliece_8192128pcf");
      case 0x6F79D:
         return if_match(oid, {2, 16, 840, 1, 113730, 1, 13}, "Certificate Comment");
      case 0x701A0:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 2, 1}, "ECGDSA");
      case 0x70322:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 1}, "ECGDSA/RIPEMD-160");
      case 0x70323:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 2}, "ECGDSA/SHA-1");
      case 0x70324:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 3}, "ECGDSA/SHA-224");
      case 0x70325:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 4}, "ECGDSA/SHA-256");
      case 0x70326:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 5}, "ECGDSA/SHA-384");
      case 0x70327:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 5, 4, 6}, "ECGDSA/SHA-512");
      case 0x72B21:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 1, 1}, "GOST-34.10-2012-256");
      case 0x72B22:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 1, 2}, "GOST-34.10-2012-512");
      case 0x72BE3:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 2, 2}, "Streebog-256");
      case 0x72BE4:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 2, 3}, "Streebog-512");
      case 0x72CA4:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 3, 2}, "GOST-34.10-2012-256/Streebog-256");
      case 0x72CA5:
         return if_match(oid, {1, 2, 643, 7, 1, 1, 3, 3}, "GOST-34.10-2012-512/Streebog-512");
      case 0x7E10F:
         return if_match(oid, {2, 5, 4, 3}, "X520.CommonName");
      case 0x7E110:
         return if_match(oid, {2, 5, 4, 4}, "X520.Surname");
      case 0x7E111:
         return if_match(oid, {2, 5, 4, 5}, "X520.SerialNumber");
      case 0x7E112:
         return if_match(oid, {2, 5, 4, 6}, "X520.Country");
      case 0x7E113:
         return if_match(oid, {2, 5, 4, 7}, "X520.Locality");
      case 0x7E114:
         return if_match(oid, {2, 5, 4, 8}, "X520.State");
      case 0x7E115:
         return if_match(oid, {2, 5, 4, 9}, "X520.StreetAddress");
      case 0x7E116:
         return if_match(oid, {2, 5, 4, 10}, "X520.Organization");
      case 0x7E117:
         return if_match(oid, {2, 5, 4, 11}, "X520.OrganizationalUnit");
      case 0x7E118:
         return if_match(oid, {2, 5, 4, 12}, "X520.Title");
      case 0x7E136:
         return if_match(oid, {2, 5, 4, 42}, "X520.GivenName");
      case 0x7E137:
         return if_match(oid, {2, 5, 4, 43}, "X520.Initials");
      case 0x7E138:
         return if_match(oid, {2, 5, 4, 44}, "X520.GenerationalQualifier");
      case 0x7E13A:
         return if_match(oid, {2, 5, 4, 46}, "X520.DNQualifier");
      case 0x7E14D:
         return if_match(oid, {2, 5, 4, 65}, "X520.Pseudonym");
      case 0x7F3F3:
         return if_match(oid, {2, 5, 29, 14}, "X509v3.SubjectKeyIdentifier");
      case 0x7F3F4:
         return if_match(oid, {2, 5, 29, 15}, "X509v3.KeyUsage");
      case 0x7F3F5:
         return if_match(oid, {2, 5, 29, 16}, "X509v3.PrivateKeyUsagePeriod");
      case 0x7F3F6:
         return if_match(oid, {2, 5, 29, 17}, "X509v3.SubjectAlternativeName");
      case 0x7F3F7:
         return if_match(oid, {2, 5, 29, 18}, "X509v3.IssuerAlternativeName");
      case 0x7F3F8:
         return if_match(oid, {2, 5, 29, 19}, "X509v3.BasicConstraints");
      case 0x7F3F9:
         return if_match(oid, {2, 5, 29, 20}, "X509v3.CRLNumber");
      case 0x7F3FA:
         return if_match(oid, {2, 5, 29, 21}, "X509v3.ReasonCode");
      case 0x7F3FC:
         return if_match(oid, {2, 5, 29, 23}, "X509v3.HoldInstructionCode");
      case 0x7F3FD:
         return if_match(oid, {2, 5, 29, 24}, "X509v3.InvalidityDate");
      case 0x7F401:
         return if_match(oid, {2, 5, 29, 28}, "X509v3.CRLIssuingDistributionPoint");
      case 0x7F403:
         return if_match(oid, {2, 5, 29, 30}, "X509v3.NameConstraints");
      case 0x7F404:
         return if_match(oid, {2, 5, 29, 31}, "X509v3.CRLDistributionPoints");
      case 0x7F405:
         return if_match(oid, {2, 5, 29, 32}, "X509v3.CertificatePolicies");
      case 0x7F408:
         return if_match(oid, {2, 5, 29, 35}, "X509v3.AuthorityKeyIdentifier");
      case 0x7F409:
         return if_match(oid, {2, 5, 29, 36}, "X509v3.PolicyConstraints");
      case 0x7F40A:
         return if_match(oid, {2, 5, 29, 37}, "X509v3.ExtendedKeyUsage");
      case 0x80B84:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 1}, "AES-128/OCB");
      case 0x80B85:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 2}, "AES-192/OCB");
      case 0x80B86:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 3}, "AES-256/OCB");
      case 0x80B87:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 4}, "Serpent/OCB");
      case 0x80B88:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 5}, "Twofish/OCB");
      case 0x80B89:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 6}, "Camellia-128/OCB");
      case 0x80B8A:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 7}, "Camellia-192/OCB");
      case 0x80B8B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2, 8}, "Camellia-256/OCB");
      case 0x80D06:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 1}, "AES-128/SIV");
      case 0x80D07:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 2}, "AES-192/SIV");
      case 0x80D08:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 3}, "AES-256/SIV");
      case 0x80D09:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 4}, "Serpent/SIV");
      case 0x80D0A:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 5}, "Twofish/SIV");
      case 0x80D0B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 6}, "Camellia-128/SIV");
      case 0x80D0C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 7}, "Camellia-192/SIV");
      case 0x80D0D:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 8}, "Camellia-256/SIV");
      case 0x80D0E:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 4, 9}, "SM4/SIV");
      case 0x84C6A:
         return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 2}, "Camellia-128/CBC");
      case 0x84C6B:
         return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 3}, "Camellia-192/CBC");
      case 0x84C6C:
         return if_match(oid, {1, 2, 392, 200011, 61, 1, 1, 1, 4}, "Camellia-256/CBC");
      case 0x88CD3:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 6}, "KeyWrap.TripleDES");
      case 0x88CD5:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 8}, "Compression.Zlib");
      case 0x88CDE:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 17}, "HSS-LMS");
      case 0x88CDF:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 16, 3, 18}, "ChaCha20Poly1305");
      case 0x92296:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 2}, "AES-128/CBC");
      case 0x92299:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 5}, "KeyWrap.AES-128");
      case 0x9229A:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 6}, "AES-128/GCM");
      case 0x9229B:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 7}, "AES-128/CCM");
      case 0x922AA:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 22}, "AES-192/CBC");
      case 0x922AD:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 25}, "KeyWrap.AES-192");
      case 0x922AE:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 26}, "AES-192/GCM");
      case 0x922AF:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 27}, "AES-192/CCM");
      case 0x922BE:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 42}, "AES-256/CBC");
      case 0x922C1:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 45}, "KeyWrap.AES-256");
      case 0x922C2:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 46}, "AES-256/GCM");
      case 0x922C3:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 1, 47}, "AES-256/CCM");
      case 0x92356:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 1}, "SHA-256");
      case 0x92357:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 2}, "SHA-384");
      case 0x92358:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 3}, "SHA-512");
      case 0x92359:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 4}, "SHA-224");
      case 0x9235B:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 6}, "SHA-512-256");
      case 0x9235C:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 7}, "SHA-3(224)");
      case 0x9235D:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 8}, "SHA-3(256)");
      case 0x9235E:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 9}, "SHA-3(384)");
      case 0x9235F:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 10}, "SHA-3(512)");
      case 0x92360:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 11}, "SHAKE-128");
      case 0x92361:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 2, 12}, "SHAKE-256");
      case 0x92417:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 1}, "DSA/SHA-224");
      case 0x92418:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 2}, "DSA/SHA-256");
      case 0x92419:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 3}, "DSA/SHA-384");
      case 0x9241A:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 4}, "DSA/SHA-512");
      case 0x9241B:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 5}, "DSA/SHA-3(224)");
      case 0x9241C:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 6}, "DSA/SHA-3(256)");
      case 0x9241D:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 7}, "DSA/SHA-3(384)");
      case 0x9241E:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 8}, "DSA/SHA-3(512)");
      case 0x9241F:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 9}, "ECDSA/SHA-3(224)");
      case 0x92420:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 10}, "ECDSA/SHA-3(256)");
      case 0x92421:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 11}, "ECDSA/SHA-3(384)");
      case 0x92422:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 12}, "ECDSA/SHA-3(512)");
      case 0x92423:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 13}, "RSA/PKCS1v15(SHA-3(224))");
      case 0x92424:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 14}, "RSA/PKCS1v15(SHA-3(256))");
      case 0x92425:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 15}, "RSA/PKCS1v15(SHA-3(384))");
      case 0x92426:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 16}, "RSA/PKCS1v15(SHA-3(512))");
      case 0x92427:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 17}, "ML-DSA-4x4");
      case 0x92428:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 18}, "ML-DSA-6x5");
      case 0x92429:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 19}, "ML-DSA-8x7");
      case 0x9242A:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 20}, "SLH-DSA-SHA2-128s");
      case 0x9242B:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 21}, "SLH-DSA-SHA2-128f");
      case 0x9242C:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 22}, "SLH-DSA-SHA2-192s");
      case 0x9242D:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 23}, "SLH-DSA-SHA2-192f");
      case 0x9242E:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 24}, "SLH-DSA-SHA2-256s");
      case 0x9242F:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 25}, "SLH-DSA-SHA2-256f");
      case 0x92430:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 26}, "SLH-DSA-SHAKE-128s");
      case 0x92431:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 27}, "SLH-DSA-SHAKE-128f");
      case 0x92432:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 28}, "SLH-DSA-SHAKE-192s");
      case 0x92433:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 29}, "SLH-DSA-SHAKE-192f");
      case 0x92434:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 30}, "SLH-DSA-SHAKE-256s");
      case 0x92435:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 3, 31}, "SLH-DSA-SHAKE-256f");
      case 0x924D8:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 1}, "ML-KEM-512");
      case 0x924D9:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 2}, "ML-KEM-768");
      case 0x924DA:
         return if_match(oid, {2, 16, 840, 1, 101, 3, 4, 4, 3}, "ML-KEM-1024");
      case 0x9479F:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 1}, "PKIX.AuthorityInformationAccess");
      case 0x947A5:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 7}, "PKIX.IpAddrBlocks");
      case 0x947A6:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 8}, "PKIX.AutonomousSysIds");
      case 0x947B8:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 1, 26}, "PKIX.TNAuthList");
      case 0x94921:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 1}, "PKIX.ServerAuth");
      case 0x94922:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 2}, "PKIX.ClientAuth");
      case 0x94923:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 3}, "PKIX.CodeSigning");
      case 0x94924:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 4}, "PKIX.EmailProtection");
      case 0x94925:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 5}, "PKIX.IPsecEndSystem");
      case 0x94926:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 6}, "PKIX.IPsecTunnel");
      case 0x94927:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 7}, "PKIX.IPsecUser");
      case 0x94928:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 8}, "PKIX.TimeStamping");
      case 0x94929:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 3, 9}, "PKIX.OCSPSigning");
      case 0x94CEA:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 8, 5}, "PKIX.XMPPAddr");
      case 0x954DB:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 311, 20, 2, 2}, "Microsoft SmartcardLogon");
      case 0x954DC:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, "Microsoft UPN");
      case 0x96B0E:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 1}, "PKIX.OCSP");
      case 0x96B0F:
         return if_match(oid, {1, 3, 6, 1, 5, 5, 7, 48, 2}, "PKIX.CertificateAuthorityIssuers");
      case 0x9A008:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 1}, "brainpool160r1");
      case 0x9A00A:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 3}, "brainpool192r1");
      case 0x9A00C:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 5}, "brainpool224r1");
      case 0x9A00E:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 7}, "brainpool256r1");
      case 0x9A010:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 9}, "brainpool320r1");
      case 0x9A012:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 11}, "brainpool384r1");
      case 0x9A014:
         return if_match(oid, {1, 3, 36, 3, 3, 2, 8, 1, 1, 13}, "brainpool512r1");
      case 0xA0D61:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 3}, "McEliece");
      case 0xA0D63:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 5}, "XMSS-draft6");
      case 0xA0D66:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 8}, "XMSS-draft12");
      case 0xA0D6B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 1, 13}, "HSS-LMS-Private-Key");
      case 0xA0EE1:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 1}, "Serpent/CBC");
      case 0xA0EE2:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 2}, "Threefish-512/CBC");
      case 0xA0EE3:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 3}, "Twofish/CBC");
      case 0xA0F45:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 101}, "Serpent/GCM");
      case 0xA0F46:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 3, 102}, "Twofish/GCM");
      case 0xA0FA2:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 1}, "numsp256d1");
      case 0xA0FA3:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 2}, "numsp384d1");
      case 0xA0FA4:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 25258, 4, 3}, "numsp512d1");
      case 0xA244B:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 1}, "ClassicMcEliece_348864");
      case 0xA244C:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 2}, "ClassicMcEliece_348864f");
      case 0xA244D:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 3}, "ClassicMcEliece_460896");
      case 0xA244E:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 4}, "ClassicMcEliece_460896f");
      case 0xA244F:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 5}, "ClassicMcEliece_6688128");
      case 0xA2450:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 6}, "ClassicMcEliece_6688128f");
      case 0xA2451:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 7}, "ClassicMcEliece_6960119");
      case 0xA2452:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 8}, "ClassicMcEliece_6960119f");
      case 0xA2453:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 9}, "ClassicMcEliece_8192128");
      case 0xA2454:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 22554, 5, 1, 10}, "ClassicMcEliece_8192128f");
      case 0xAF989:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 1}, "RSA");
      case 0xAF98A:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 2}, "RSA/PKCS1v15(MD2)");
      case 0xAF98C:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 4}, "RSA/PKCS1v15(MD5)");
      case 0xAF98D:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 5}, "RSA/PKCS1v15(SHA-1)");
      case 0xAF98F:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 7}, "RSA/OAEP");
      case 0xAF990:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 8}, "MGF1");
      case 0xAF992:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 10}, "RSA/PSS");
      case 0xAF993:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 11}, "RSA/PKCS1v15(SHA-256)");
      case 0xAF994:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 12}, "RSA/PKCS1v15(SHA-384)");
      case 0xAF995:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 13}, "RSA/PKCS1v15(SHA-512)");
      case 0xAF996:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 14}, "RSA/PKCS1v15(SHA-224)");
      case 0xAF998:
         return if_match(oid, {1, 2, 840, 113549, 1, 1, 16}, "RSA/PKCS1v15(SHA-512-256)");
      case 0xAFC98:
         return if_match(oid, {1, 2, 840, 113549, 1, 5, 12}, "PKCS5.PBKDF2");
      case 0xAFC99:
         return if_match(oid, {1, 2, 840, 113549, 1, 5, 13}, "PBE-PKCS5v20");
      case 0xAFF91:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 1}, "PKCS9.EmailAddress");
      case 0xAFF92:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 2}, "PKCS9.UnstructuredName");
      case 0xAFF93:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 3}, "PKCS9.ContentType");
      case 0xAFF94:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 4}, "PKCS9.MessageDigest");
      case 0xAFF97:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 7}, "PKCS9.ChallengePassword");
      case 0xAFF9E:
         return if_match(oid, {1, 2, 840, 113549, 1, 9, 14}, "PKCS9.ExtensionRequest");
      case 0xC0226:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 11591, 4, 11}, "Scrypt");
      case 0xC0A67:
         return if_match(oid, {1, 3, 6, 1, 4, 1, 11591, 15, 1}, "OpenPGP.Ed25519");
      case 0xC4CE5:
         return if_match(oid, {1, 2, 643, 100, 1}, "GOST.OGRN");
      case 0xC4D53:
         return if_match(oid, {1, 2, 643, 100, 111}, "GOST.SubjectSigningTool");
      case 0xC4D54:
         return if_match(oid, {1, 2, 643, 100, 112}, "GOST.IssuerSigningTool");
      case 0xC9C50:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 1}, "secp192r1");
      case 0xC9C51:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 2}, "x962_p192v2");
      case 0xC9C52:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 3}, "x962_p192v3");
      case 0xC9C53:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 4}, "x962_p239v1");
      case 0xC9C54:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 5}, "x962_p239v2");
      case 0xC9C55:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 6}, "x962_p239v3");
      case 0xC9C56:
         return if_match(oid, {1, 2, 840, 10045, 3, 1, 7}, "secp256r1");
      case 0xCFA13:
         return if_match(oid, {1, 2, 840, 10040, 4, 1}, "DSA");
      case 0xCFA15:
         return if_match(oid, {1, 2, 840, 10040, 4, 3}, "DSA/SHA-1");
      default:
         return {};
   }
}

//static
std::optional<OID> OID_Map::lookup_static_oid_name(std::string_view req) {
   const uint32_t hc = hash_oid_name(req);

   switch(hc) {
      case 0x00545:
         return if_match(req, "Twofish/GCM", {1, 3, 6, 1, 4, 1, 25258, 3, 102});
      case 0x00CF3:
         return if_match(req, "SphincsPlus-sha2-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 4});
      case 0x015FE:
         return if_match(req, "FrodoKEM-640-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 1});
      case 0x01F9E:
         return if_match(req, "MD5", {1, 2, 840, 113549, 2, 5});
      case 0x02293:
         return if_match(req, "SphincsPlus-shake-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 4});
      case 0x02B93:
         return if_match(req, "Microsoft SmartcardLogon", {1, 3, 6, 1, 4, 1, 311, 20, 2, 2});
      case 0x041D5:
         return if_match(req, "secp160k1", {1, 3, 132, 0, 9});
      case 0x044B3:
         return if_match(req, "Camellia-256/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 8});
      case 0x048B2:
         return if_match(req, "secp160r1", {1, 3, 132, 0, 8});
      case 0x048B3:
         return if_match(req, "secp160r2", {1, 3, 132, 0, 30});
      case 0x05CDA:
         return if_match(req, "X520.Country", {2, 5, 4, 6});
      case 0x07783:
         return if_match(req, "PKIX.ServerAuth", {1, 3, 6, 1, 5, 5, 7, 3, 1});
      case 0x086C7:
         return if_match(req, "numsp384d1", {1, 3, 6, 1, 4, 1, 25258, 4, 2});
      case 0x08A92:
         return if_match(req, "RSA/PKCS1v15(SHA-1)", {1, 2, 840, 113549, 1, 1, 5});
      case 0x09EA0:
         return if_match(req, "DES/CBC", {1, 3, 14, 3, 2, 7});
      case 0x0B2D6:
         return if_match(req, "ECDSA/SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 3, 12});
      case 0x0BA72:
         return if_match(req, "SphincsPlus-sha2-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 1});
      case 0x0BE23:
         return if_match(req, "ECGDSA", {1, 3, 36, 3, 3, 2, 5, 2, 1});
      case 0x0D012:
         return if_match(req, "SphincsPlus-shake-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 1});
      case 0x0DCE9:
         return if_match(req, "ClassicMcEliece_8192128f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 10});
      case 0x0E52A:
         return if_match(req, "numsp512d1", {1, 3, 6, 1, 4, 1, 25258, 4, 3});
      case 0x0F9CC:
         return if_match(req, "PKCS9.UnstructuredName", {1, 2, 840, 113549, 1, 9, 2});
      case 0x0FF45:
         return if_match(req, "Camellia-256/GCM", {0, 3, 4401, 5, 3, 1, 9, 46});
      case 0x1033D:
         return if_match(req, "DSA/SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 3, 7});
      case 0x1139D:
         return if_match(req, "secp192k1", {1, 3, 132, 0, 31});
      case 0x113D6:
         return if_match(req, "X520.DNQualifier", {2, 5, 4, 46});
      case 0x11A7A:
         return if_match(req, "secp192r1", {1, 2, 840, 10045, 3, 1, 1});
      case 0x12096:
         return if_match(req, "SM2_Kex", {1, 2, 156, 10197, 1, 301, 2});
      case 0x13FC1:
         return if_match(req, "X520.GenerationalQualifier", {2, 5, 4, 44});
      case 0x1445B:
         return if_match(req, "PKCS5.PBKDF2", {1, 2, 840, 113549, 1, 5, 12});
      case 0x1495D:
         return if_match(req, "eFrodoKEM-1344-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 3});
      case 0x14E30:
         return if_match(req, "ClassicMcEliece_460896", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 3});
      case 0x14FB1:
         return if_match(req, "XMSS-draft12", {1, 3, 6, 1, 4, 1, 25258, 1, 8});
      case 0x156E3:
         return if_match(req, "Compression.Zlib", {1, 2, 840, 113549, 1, 9, 16, 3, 8});
      case 0x1579E:
         return if_match(req, "Streebog-512", {1, 2, 643, 7, 1, 1, 2, 3});
      case 0x175EF:
         return if_match(req, "Kyber-1024-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 3});
      case 0x17709:
         return if_match(req, "X520.GivenName", {2, 5, 4, 42});
      case 0x17AD9:
         return if_match(req, "RSA/PKCS1v15(SM3)", {1, 2, 156, 10197, 1, 504});
      case 0x17CE2:
         return if_match(req, "SLH-DSA-SHA2-256f", {2, 16, 840, 1, 101, 3, 4, 3, 25});
      case 0x17CEF:
         return if_match(req, "SLH-DSA-SHA2-256s", {2, 16, 840, 1, 101, 3, 4, 3, 24});
      case 0x18618:
         return if_match(req, "FrodoKEM-976-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 2});
      case 0x19480:
         return if_match(req, "eFrodoKEM-1344-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 3});
      case 0x1958A:
         return if_match(req, "X509v3.InvalidityDate", {2, 5, 29, 24});
      case 0x19851:
         return if_match(req, "DSA/SHA-1", {1, 2, 840, 10040, 4, 3});
      case 0x1B2E7:
         return if_match(req, "KeyWrap.AES-128", {2, 16, 840, 1, 101, 3, 4, 1, 5});
      case 0x1B9BE:
         return if_match(req, "KeyWrap.AES-192", {2, 16, 840, 1, 101, 3, 4, 1, 25});
      case 0x1D439:
         return if_match(req, "SphincsPlus-haraka-192f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 4});
      case 0x2065B:
         return if_match(req, "KeyWrap.CAST-128", {1, 2, 840, 113533, 7, 66, 15});
      case 0x216A0:
         return if_match(req, "ML-KEM-512", {2, 16, 840, 1, 101, 3, 4, 4, 1});
      case 0x2216B:
         return if_match(req, "GOST-34.10-2012-512", {1, 2, 643, 7, 1, 1, 1, 2});
      case 0x22C2C:
         return if_match(req, "ElGamal", {1, 3, 6, 1, 4, 1, 3029, 1, 2, 1});
      case 0x2559A:
         return if_match(req, "X520.Initials", {2, 5, 4, 43});
      case 0x271AC:
         return if_match(req, "PKIX.AutonomousSysIds", {1, 3, 6, 1, 5, 5, 7, 1, 8});
      case 0x281B8:
         return if_match(req, "SphincsPlus-haraka-128s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 1});
      case 0x29999:
         return if_match(req, "DSA/SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 3, 6});
      case 0x2A83D:
         return if_match(req, "SHA-224", {2, 16, 840, 1, 101, 3, 4, 2, 4});
      case 0x2AB30:
         return if_match(req, "SHA-256", {2, 16, 840, 1, 101, 3, 4, 2, 1});
      case 0x2ABEF:
         return if_match(req, "KeyWrap.AES-256", {2, 16, 840, 1, 101, 3, 4, 1, 45});
      case 0x2BAEF:
         return if_match(req, "SM2_Sig/SM3", {1, 2, 156, 10197, 1, 501});
      case 0x2C39A:
         return if_match(req, "ECGDSA/RIPEMD-160", {1, 3, 36, 3, 3, 2, 5, 4, 1});
      case 0x2C54F:
         return if_match(req, "ECDSA/SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 3, 9});
      case 0x2EEA6:
         return if_match(req, "RSA/PKCS1v15(RIPEMD-160)", {1, 3, 36, 3, 3, 1, 2});
      case 0x2EFBA:
         return if_match(req, "Kyber-512-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 1});
      case 0x3133E:
         return if_match(req, "SLH-DSA-SHA2-128f", {2, 16, 840, 1, 101, 3, 4, 3, 21});
      case 0x3134B:
         return if_match(req, "SLH-DSA-SHA2-128s", {2, 16, 840, 1, 101, 3, 4, 3, 20});
      case 0x3160D:
         return if_match(req, "RSA/PKCS1v15(SHA-3(224))", {2, 16, 840, 1, 101, 3, 4, 3, 13});
      case 0x319E0:
         return if_match(req, "GOST-34.10-2012-256/Streebog-256", {1, 2, 643, 7, 1, 1, 3, 2});
      case 0x31B3D:
         return if_match(req, "HMAC(SHA-512)", {1, 2, 840, 113549, 2, 11});
      case 0x31C6D:
         return if_match(req, "secp384r1", {1, 3, 132, 0, 34});
      case 0x32899:
         return if_match(req, "TripleDES/CBC", {1, 2, 840, 113549, 3, 7});
      case 0x3615D:
         return if_match(req, "FrodoKEM-976-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 2});
      case 0x361B8:
         return if_match(req, "Ed25519", {1, 3, 101, 112});
      case 0x3649D:
         return if_match(req, "SHAKE-128", {2, 16, 840, 1, 101, 3, 4, 2, 11});
      case 0x36693:
         return if_match(req, "ClassicMcEliece_348864", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 1});
      case 0x373C7:
         return if_match(req, "ML-DSA-4x4", {2, 16, 840, 1, 101, 3, 4, 3, 17});
      case 0x3750B:
         return if_match(req, "ClassicMcEliece_8192128", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 9});
      case 0x39890:
         return if_match(req, "Ed448", {1, 3, 101, 113});
      case 0x3A438:
         return if_match(req, "SHA-384", {2, 16, 840, 1, 101, 3, 4, 2, 2});
      case 0x3A963:
         return if_match(req, "DH", {1, 2, 840, 10046, 2, 1});
      case 0x3AC83:
         return if_match(req, "MGF1", {1, 2, 840, 113549, 1, 1, 8});
      case 0x3ACBA:
         return if_match(req, "X509v3.IssuerAlternativeName", {2, 5, 29, 18});
      case 0x3B273:
         return if_match(req, "KeyWrap.TripleDES", {1, 2, 840, 113549, 1, 9, 16, 3, 6});
      case 0x3B91E:
         return if_match(req, "X509v3.PrivateKeyUsagePeriod", {2, 5, 29, 16});
      case 0x3BC8A:
         return if_match(req, "SLH-DSA-SHAKE-192f", {2, 16, 840, 1, 101, 3, 4, 3, 29});
      case 0x3BC97:
         return if_match(req, "SLH-DSA-SHAKE-192s", {2, 16, 840, 1, 101, 3, 4, 3, 28});
      case 0x3D127:
         return if_match(req, "DSA", {1, 2, 840, 10040, 4, 1});
      case 0x3E249:
         return if_match(req, "HSS-LMS", {1, 2, 840, 113549, 1, 9, 16, 3, 17});
      case 0x3E7D5:
         return if_match(req, "RSA/PKCS1v15(SHA-3(256))", {2, 16, 840, 1, 101, 3, 4, 3, 14});
      case 0x3F748:
         return if_match(req, "GOST.OGRN", {1, 2, 643, 100, 1});
      case 0x3F99F:
         return if_match(req, "X509v3.BasicConstraints", {2, 5, 29, 19});
      case 0x40726:
         return if_match(req, "SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 2, 10});
      case 0x407BF:
         return if_match(req, "ML-KEM-768", {2, 16, 840, 1, 101, 3, 4, 4, 2});
      case 0x41334:
         return if_match(req, "ECDSA/SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 3, 11});
      case 0x42DF3:
         return if_match(req, "X509v3.CRLDistributionPoints", {2, 5, 29, 31});
      case 0x437FB:
         return if_match(req, "brainpool160r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 1});
      case 0x441F5:
         return if_match(req, "gost_256A", {1, 2, 643, 7, 1, 2, 1, 1, 1});
      case 0x441F6:
         return if_match(req, "gost_256B", {1, 2, 643, 7, 1, 2, 1, 1, 2});
      case 0x44221:
         return if_match(req, "GOST-34.10-2012-512/Streebog-512", {1, 2, 643, 7, 1, 1, 3, 3});
      case 0x44322:
         return if_match(req, "ClassicMcEliece_6960119pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 3});
      case 0x44973:
         return if_match(req, "Kyber-512-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 1});
      case 0x45C27:
         return if_match(req, "RSA/PKCS1v15(SHA-512-256)", {1, 2, 840, 113549, 1, 1, 16});
      case 0x45C85:
         return if_match(req, "X509v3.ReasonCode", {2, 5, 29, 21});
      case 0x45DA5:
         return if_match(req, "SHAKE-256", {2, 16, 840, 1, 101, 3, 4, 2, 12});
      case 0x4663C:
         return if_match(req, "X509v3.PolicyConstraints", {2, 5, 29, 36});
      case 0x480F7:
         return if_match(req, "Serpent/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 4});
      case 0x48627:
         return if_match(req, "Dilithium-4x4-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 1});
      case 0x48861:
         return if_match(req, "ChaCha20Poly1305", {1, 2, 840, 113549, 1, 9, 16, 3, 18});
      case 0x4A292:
         return if_match(req, "frp256v1", {1, 2, 250, 1, 223, 101, 256, 1});
      case 0x4A9EE:
         return if_match(req, "ClassicMcEliece_6960119f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 8});
      case 0x4BF87:
         return if_match(req, "PKIX.TNAuthList", {1, 3, 6, 1, 5, 5, 7, 1, 26});
      case 0x4C088:
         return if_match(req, "eFrodoKEM-976-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 2});
      case 0x4C513:
         return if_match(req, "DSA/SHA-224", {2, 16, 840, 1, 101, 3, 4, 3, 1});
      case 0x4C806:
         return if_match(req, "DSA/SHA-256", {2, 16, 840, 1, 101, 3, 4, 3, 2});
      case 0x4D740:
         return if_match(req, "X509v3.AnyPolicy", {2, 5, 29, 32, 0});
      case 0x4DE49:
         return if_match(req, "RSA/PKCS1v15(SHA-512)", {1, 2, 840, 113549, 1, 1, 13});
      case 0x4ED5D:
         return if_match(req, "CAST-128/CBC", {1, 2, 840, 113533, 7, 66, 10});
      case 0x4FCDC:
         return if_match(req, "RSA", {1, 2, 840, 113549, 1, 1, 1});
      case 0x501CB:
         return if_match(req, "ECDSA/SHA-224", {1, 2, 840, 10045, 4, 3, 1});
      case 0x50395:
         return if_match(req, "GOST-34.10/GOST-R-34.11-94", {1, 2, 643, 2, 2, 3});
      case 0x504BE:
         return if_match(req, "ECDSA/SHA-256", {1, 2, 840, 10045, 4, 3, 2});
      case 0x509C3:
         return if_match(req, "brainpool192r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 3});
      case 0x509F9:
         return if_match(req, "PKCS9.ContentType", {1, 2, 840, 113549, 1, 9, 3});
      case 0x50B26:
         return if_match(req, "FrodoKEM-640-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 1});
      case 0x50D78:
         return if_match(req, "x962_p192v2", {1, 2, 840, 10045, 3, 1, 2});
      case 0x50D79:
         return if_match(req, "x962_p192v3", {1, 2, 840, 10045, 3, 1, 3});
      case 0x51DC6:
         return if_match(req, "AES-128/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 1});
      case 0x52DB6:
         return if_match(req, "HMAC(SHA-224)", {1, 2, 840, 113549, 2, 8});
      case 0x53E11:
         return if_match(req, "FrodoKEM-1344-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 14, 3});
      case 0x54012:
         return if_match(req, "PKIX.TimeStamping", {1, 3, 6, 1, 5, 5, 7, 3, 8});
      case 0x5407A:
         return if_match(req, "Serpent/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 1});
      case 0x5576D:
         return if_match(req, "SphincsPlus-sha2-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 2});
      case 0x55EF6:
         return if_match(req, "AES-192/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 2});
      case 0x55FFA:
         return if_match(req, "ML-DSA-6x5", {2, 16, 840, 1, 101, 3, 4, 3, 18});
      case 0x56826:
         return if_match(req, "brainpool320r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 9});
      case 0x56D0D:
         return if_match(req, "SphincsPlus-shake-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 2});
      case 0x57077:
         return if_match(req, "XMSS-draft6", {1, 3, 6, 1, 4, 1, 25258, 1, 5});
      case 0x5818B:
         return if_match(req, "ECGDSA/SHA-224", {1, 3, 36, 3, 3, 2, 5, 4, 3});
      case 0x5847E:
         return if_match(req, "ECGDSA/SHA-256", {1, 3, 36, 3, 3, 2, 5, 4, 4});
      case 0x5898B:
         return if_match(req, "SHA-512", {2, 16, 840, 1, 101, 3, 4, 2, 3});
      case 0x58991:
         return if_match(req, "PKIX.OCSP.NoCheck", {1, 3, 6, 1, 5, 5, 7, 48, 1, 5});
      case 0x59717:
         return if_match(req, "X509v3.SubjectKeyIdentifier", {2, 5, 29, 14});
      case 0x5A570:
         return if_match(req, "X520.CommonName", {2, 5, 4, 3});
      case 0x5A990:
         return if_match(req, "ECDSA/SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 3, 10});
      case 0x5AB0E:
         return if_match(req, "SphincsPlus-sha2-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 5});
      case 0x5AC4A:
         return if_match(req, "X520.Surname", {2, 5, 4, 4});
      case 0x5AF2C:
         return if_match(req, "ClassicMcEliece_8192128pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 5});
      case 0x5BC39:
         return if_match(req, "X509v3.KeyUsage", {2, 5, 29, 15});
      case 0x5BDDB:
         return if_match(req, "numsp256d1", {1, 3, 6, 1, 4, 1, 25258, 4, 1});
      case 0x5C0AE:
         return if_match(req, "SphincsPlus-shake-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 5});
      case 0x5C10E:
         return if_match(req, "DSA/SHA-384", {2, 16, 840, 1, 101, 3, 4, 3, 3});
      case 0x5D1CF:
         return if_match(req, "X520.SerialNumber", {2, 5, 4, 5});
      case 0x5D375:
         return if_match(req, "SM4/OCB", {1, 2, 156, 10197, 1, 104, 100});
      case 0x5DD49:
         return if_match(req, "AES-128/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 2});
      case 0x5DE4E:
         return if_match(req, "AES-128/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 7});
      case 0x5DF23:
         return if_match(req, "HMAC(SHA-512-256)", {1, 2, 840, 113549, 2, 13});
      case 0x5ED04:
         return if_match(req, "SM2", {1, 2, 156, 10197, 1, 301, 1});
      case 0x5ED05:
         return if_match(req, "SM3", {1, 2, 156, 10197, 1, 401});
      case 0x5FDC6:
         return if_match(req, "ECDSA/SHA-384", {1, 2, 840, 10045, 4, 3, 3});
      case 0x6199F:
         return if_match(req, "SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 2, 7});
      case 0x61E79:
         return if_match(req, "AES-192/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 22});
      case 0x61F7E:
         return if_match(req, "AES-192/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 27});
      case 0x64947:
         return if_match(req, "OpenPGP.Ed25519", {1, 3, 6, 1, 4, 1, 11591, 15, 1});
      case 0x652E7:
         return if_match(req, "sm2p256v1", {1, 2, 156, 10197, 1, 301});
      case 0x6697B:
         return if_match(req, "FrodoKEM-1344-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 15, 3});
      case 0x67B2C:
         return if_match(req, "X520.State", {2, 5, 4, 8});
      case 0x67B9B:
         return if_match(req, "HMAC(SHA-384)", {1, 2, 840, 113549, 2, 10});
      case 0x67D86:
         return if_match(req, "ECGDSA/SHA-384", {1, 3, 36, 3, 3, 2, 5, 4, 5});
      case 0x68A0B:
         return if_match(req, "Camellia-128/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 6});
      case 0x68E33:
         return if_match(req, "PKCS9.ExtensionRequest", {1, 2, 840, 113549, 1, 9, 14});
      case 0x69126:
         return if_match(req, "X509v3.SubjectAlternativeName", {2, 5, 29, 17});
      case 0x692F8:
         return if_match(req, "SM4/CBC", {1, 2, 156, 10197, 1, 104, 2});
      case 0x695E1:
         return if_match(req, "Dilithium-4x4-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 1});
      case 0x696DC:
         return if_match(req, "PKIX.IpAddrBlocks", {1, 3, 6, 1, 5, 5, 7, 1, 7});
      case 0x6A7CA:
         return if_match(req, "ECDSA", {1, 2, 840, 10045, 2, 1});
      case 0x6BD26:
         return if_match(req, "GOST.INN", {1, 2, 643, 3, 131, 1, 1});
      case 0x6CB3B:
         return if_match(req, "Camellia-192/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 7});
      case 0x6E602:
         return if_match(req, "Dilithium-8x7-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 3});
      case 0x6F0C2:
         return if_match(req, "RSA/PKCS1v15(SHA-224)", {1, 2, 840, 113549, 1, 1, 14});
      case 0x6FB26:
         return if_match(req, "PKIX.AuthorityInformationAccess", {1, 3, 6, 1, 5, 5, 7, 1, 1});
      case 0x70BB6:
         return if_match(req, "brainpool384r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 11});
      case 0x71EB3:
         return if_match(req, "SphincsPlus-haraka-128f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 2});
      case 0x7382C:
         return if_match(req, "ML-KEM-1024", {2, 16, 840, 1, 101, 3, 4, 4, 3});
      case 0x743BD:
         return if_match(req, "AES-256/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 3});
      case 0x7498E:
         return if_match(req, "Camellia-128/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 2});
      case 0x74C2E:
         return if_match(req, "ML-DSA-8x7", {2, 16, 840, 1, 101, 3, 4, 3, 19});
      case 0x7505F:
         return if_match(req, "PKIX.XMPPAddr", {1, 3, 6, 1, 5, 5, 7, 8, 5});
      case 0x7517A:
         return if_match(req, "RSA/PKCS1v15(MD2)", {1, 2, 840, 113549, 1, 1, 2});
      case 0x7546B:
         return if_match(req, "RSA/PKCS1v15(MD5)", {1, 2, 840, 113549, 1, 1, 4});
      case 0x75921:
         return if_match(req, "ClassicMcEliece_348864f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 2});
      case 0x76784:
         return if_match(req, "SHA-3(384)", {2, 16, 840, 1, 101, 3, 4, 2, 9});
      case 0x76A19:
         return if_match(req, "brainpool512r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 13});
      case 0x77254:
         return if_match(req, "SphincsPlus-haraka-256s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 5});
      case 0x77ADC:
         return if_match(req, "secp224k1", {1, 3, 132, 0, 32});
      case 0x781B9:
         return if_match(req, "secp224r1", {1, 3, 132, 0, 33});
      case 0x78ABE:
         return if_match(req, "Camellia-192/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 3});
      case 0x792F2:
         return if_match(req, "ClassicMcEliece_6688128pc", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 1});
      case 0x7A661:
         return if_match(req, "DSA/SHA-512", {2, 16, 840, 1, 101, 3, 4, 3, 4});
      case 0x7A977:
         return if_match(req, "X509v3.ExtendedKeyUsage", {2, 5, 29, 37});
      case 0x7AE67:
         return if_match(req, "SM2_Enc", {1, 2, 156, 10197, 1, 301, 3});
      case 0x7B602:
         return if_match(req, "Twofish/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 5});
      case 0x7B9A1:
         return if_match(req, "SphincsPlus-sha2-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 3});
      case 0x7BB0A:
         return if_match(req, "SLH-DSA-SHAKE-256f", {2, 16, 840, 1, 101, 3, 4, 3, 31});
      case 0x7BB17:
         return if_match(req, "SLH-DSA-SHAKE-256s", {2, 16, 840, 1, 101, 3, 4, 3, 30});
      case 0x7BCF3:
         return if_match(req, "PKIX.EmailProtection", {1, 3, 6, 1, 5, 5, 7, 3, 4});
      case 0x7CC2C:
         return if_match(req, "SHA-512-256", {2, 16, 840, 1, 101, 3, 4, 2, 6});
      case 0x7CF41:
         return if_match(req, "SphincsPlus-shake-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 3});
      case 0x7DB91:
         return if_match(req, "GOST-34.10", {1, 2, 643, 2, 2, 19});
      case 0x7E319:
         return if_match(req, "ECDSA/SHA-512", {1, 2, 840, 10045, 4, 3, 4});
      case 0x7E874:
         return if_match(req, "ClassicMcEliece_6688128f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 6});
      case 0x7EAAF:
         return if_match(req, "eFrodoKEM-640-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 1});
      case 0x7F51F:
         return if_match(req, "PKIX.IPsecTunnel", {1, 3, 6, 1, 5, 5, 7, 3, 6});
      case 0x80272:
         return if_match(req, "X520.Organization", {2, 5, 4, 10});
      case 0x80340:
         return if_match(req, "AES-256/CBC", {2, 16, 840, 1, 101, 3, 4, 1, 42});
      case 0x80445:
         return if_match(req, "AES-256/CCM", {2, 16, 840, 1, 101, 3, 4, 1, 47});
      case 0x811F7:
         return if_match(req, "HMAC(SHA-256)", {1, 2, 840, 113549, 2, 9});
      case 0x82B47:
         return if_match(req, "Threefish-512/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 2});
      case 0x83EA7:
         return if_match(req, "RSA/PKCS1v15(SHA-384)", {1, 2, 840, 113549, 1, 1, 12});
      case 0x84596:
         return if_match(req, "eFrodoKEM-640-AES", {1, 3, 6, 1, 4, 1, 25258, 1, 17, 1});
      case 0x8469F:
         return if_match(req, "ClassicMcEliece_6960119pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 4});
      case 0x84CA4:
         return if_match(req, "secp256k1", {1, 3, 132, 0, 10});
      case 0x85381:
         return if_match(req, "secp256r1", {1, 2, 840, 10045, 3, 1, 7});
      case 0x854FC:
         return if_match(req, "PKIX.IPsecUser", {1, 3, 6, 1, 5, 5, 7, 3, 7});
      case 0x85F51:
         return if_match(req, "Serpent/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 4});
      case 0x862D9:
         return if_match(req, "ECGDSA/SHA-512", {1, 3, 36, 3, 3, 2, 5, 4, 6});
      case 0x87585:
         return if_match(req, "Twofish/CBC", {1, 3, 6, 1, 4, 1, 25258, 3, 3});
      case 0x877D1:
         return if_match(req, "PKCS9.EmailAddress", {1, 2, 840, 113549, 1, 9, 1});
      case 0x87D27:
         return if_match(req, "PKIX.CertificateAuthorityIssuers", {1, 3, 6, 1, 5, 5, 7, 48, 2});
      case 0x87E42:
         return if_match(req, "X509v3.AuthorityKeyIdentifier", {2, 5, 29, 35});
      case 0x889B1:
         return if_match(req, "ECDSA/SHA-1", {1, 2, 840, 10045, 4, 1});
      case 0x89658:
         return if_match(req, "PBE-PKCS5v20", {1, 2, 840, 113549, 1, 5, 13});
      case 0x8976D:
         return if_match(req, "PKCS9.MessageDigest", {1, 2, 840, 113549, 1, 9, 4});
      case 0x8B002:
         return if_match(req, "Camellia-256/OCB", {1, 3, 6, 1, 4, 1, 25258, 3, 2, 8});
      case 0x8B935:
         return if_match(req, "ClassicMcEliece_6688128", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 5});
      case 0x8CE3D:
         return if_match(req, "PKCS9.ChallengePassword", {1, 2, 840, 113549, 1, 9, 7});
      case 0x8D45C:
         return if_match(req, "ECKCDSA", {1, 0, 14888, 3, 0, 5});
      case 0x8E0C1:
         return if_match(req, "X509v3.CertificatePolicies", {2, 5, 29, 32});
      case 0x8E39A:
         return if_match(req, "HSS-LMS-Private-Key", {1, 3, 6, 1, 4, 1, 25258, 1, 13});
      case 0x8EC51:
         return if_match(req, "Kyber-768-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 2});
      case 0x8F94A:
         return if_match(req, "Dilithium-6x5-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 9, 2});
      case 0x8FC20:
         return if_match(req, "AES-128/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 1});
      case 0x8FDE0:
         return if_match(req, "SHA-3(256)", {2, 16, 840, 1, 101, 3, 4, 2, 8});
      case 0x919E3:
         return if_match(req, "Serpent/GCM", {1, 3, 6, 1, 4, 1, 25258, 3, 101});
      case 0x91C1A:
         return if_match(req, "X25519", {1, 3, 101, 110});
      case 0x91DC4:
         return if_match(req, "McEliece", {1, 3, 6, 1, 4, 1, 25258, 1, 3});
      case 0x93467:
         return if_match(req, "Dilithium-6x5-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 2});
      case 0x93D50:
         return if_match(req, "AES-192/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 2});
      case 0x95166:
         return if_match(req, "SLH-DSA-SHAKE-128f", {2, 16, 840, 1, 101, 3, 4, 3, 27});
      case 0x95173:
         return if_match(req, "SLH-DSA-SHAKE-128s", {2, 16, 840, 1, 101, 3, 4, 3, 26});
      case 0x952D6:
         return if_match(req, "PKIX.OCSP", {1, 3, 6, 1, 5, 5, 7, 48, 1});
      case 0x959B9:
         return if_match(req, "PKIX.IPsecEndSystem", {1, 3, 6, 1, 5, 5, 7, 3, 5});
      case 0x96F85:
         return if_match(req, "Camellia-256/CBC", {1, 2, 392, 200011, 61, 1, 1, 1, 4});
      case 0x97D5E:
         return if_match(req, "HMAC(SHA-1)", {1, 2, 840, 113549, 2, 7});
      case 0x9805C:
         return if_match(req, "SEED/CBC", {1, 2, 410, 200004, 1, 4});
      case 0x980E7:
         return if_match(req, "SphincsPlus-haraka-192s-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 3});
      case 0x980F5:
         return if_match(req, "GOST.SubjectSigningTool", {1, 2, 643, 100, 111});
      case 0x98B03:
         return if_match(req, "XMSS", {0, 4, 0, 127, 0, 15, 1, 1, 13, 0});
      case 0x9A6B2:
         return if_match(req, "ECKCDSA/SHA-1", {1, 2, 410, 200004, 1, 100, 4, 3});
      case 0x9B1CF:
         return if_match(req, "SM4/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 9});
      case 0x9B6B2:
         return if_match(req, "AES-128/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 6});
      case 0x9B6BB:
         return if_match(req, "X520.OrganizationalUnit", {2, 5, 4, 11});
      case 0x9B851:
         return if_match(req, "OpenPGP.Curve25519", {1, 3, 6, 1, 4, 1, 3029, 1, 5, 1});
      case 0x9C80B:
         return if_match(req, "SLH-DSA-SHA2-192f", {2, 16, 840, 1, 101, 3, 4, 3, 23});
      case 0x9C818:
         return if_match(req, "SLH-DSA-SHA2-192s", {2, 16, 840, 1, 101, 3, 4, 3, 22});
      case 0x9CD2B:
         return if_match(req, "Scrypt", {1, 3, 6, 1, 4, 1, 11591, 4, 11});
      case 0x9CDE1:
         return if_match(req, "GOST-34.10-2012-256/SHA-256", {1, 3, 6, 1, 4, 1, 25258, 1, 6, 1});
      case 0x9CF73:
         return if_match(req, "ClassicMcEliece_460896f", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 4});
      case 0x9D354:
         return if_match(req, "RIPEMD-160", {1, 3, 36, 3, 2, 1});
      case 0x9D503:
         return if_match(req, "RSA/PKCS1v15(SHA-256)", {1, 2, 840, 113549, 1, 1, 11});
      case 0x9EC88:
         return if_match(req, "DSA/SHA-3(512)", {2, 16, 840, 1, 101, 3, 4, 3, 8});
      case 0x9EF36:
         return if_match(req, "ClassicMcEliece_6960119", {1, 3, 6, 1, 4, 1, 22554, 5, 1, 7});
      case 0x9F764:
         return if_match(req, "X448", {1, 3, 101, 111});
      case 0x9F7E2:
         return if_match(req, "AES-192/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 26});
      case 0x9F9C5:
         return if_match(req, "ClassicMcEliece_6688128pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 2});
      case 0xA2B5B:
         return if_match(req, "X509v3.CRLNumber", {2, 5, 29, 20});
      case 0xA3005:
         return if_match(req, "X520.Title", {2, 5, 4, 12});
      case 0xA323F:
         return if_match(req, "X509v3.NameConstraints", {2, 5, 29, 30});
      case 0xA3C55:
         return if_match(req, "X520.Pseudonym", {2, 5, 4, 65});
      case 0xA4809:
         return if_match(req, "SphincsPlus-sha2-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 2, 6});
      case 0xA57AF:
         return if_match(req, "secp521r1", {1, 3, 132, 0, 35});
      case 0xA5DA9:
         return if_match(req, "SphincsPlus-shake-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 1, 6});
      case 0xA6865:
         return if_match(req, "Camellia-128/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 6});
      case 0xA6C61:
         return if_match(req, "SM4/GCM", {1, 2, 156, 10197, 1, 104, 8});
      case 0xA9061:
         return if_match(req, "Kyber-768-90s-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 11, 2});
      case 0xAA995:
         return if_match(req, "Camellia-192/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 7});
      case 0xAAE2B:
         return if_match(req, "Dilithium-8x7-AES-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 10, 3});
      case 0xABCED:
         return if_match(req, "GOST.IssuerSigningTool", {1, 2, 643, 100, 112});
      case 0xABD24:
         return if_match(req, "RSA/OAEP", {1, 2, 840, 113549, 1, 1, 7});
      case 0xAC2EC:
         return if_match(req, "Streebog-256", {1, 2, 643, 7, 1, 1, 2, 2});
      case 0xAC3DD:
         return if_match(req, "Certificate Comment", {2, 16, 840, 1, 113730, 1, 13});
      case 0xAE6FE:
         return if_match(req, "PKIX.ClientAuth", {1, 3, 6, 1, 5, 5, 7, 3, 2});
      case 0xAE8D3:
         return if_match(req, "ClassicMcEliece_8192128pcf", {1, 3, 6, 1, 4, 1, 25258, 1, 18, 6});
      case 0xAF476:
         return if_match(req, "ECDH", {1, 3, 132, 1, 12});
      case 0xAFA6A:
         return if_match(req, "RSA/PKCS1v15(SHA-3(384))", {2, 16, 840, 1, 101, 3, 4, 3, 15});
      case 0xB2217:
         return if_match(req, "AES-256/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 3});
      case 0xB22F7:
         return if_match(req, "Camellia-128/GCM", {0, 3, 4401, 5, 3, 1, 9, 6});
      case 0xB23DE:
         return if_match(req, "X520.Locality", {2, 5, 4, 7});
      case 0xB2FBD:
         return if_match(req, "ECKCDSA/SHA-224", {1, 2, 410, 200004, 1, 100, 4, 4});
      case 0xB32B0:
         return if_match(req, "ECKCDSA/SHA-256", {1, 2, 410, 200004, 1, 100, 4, 5});
      case 0xB360E:
         return if_match(req, "eFrodoKEM-976-SHAKE", {1, 3, 6, 1, 4, 1, 25258, 1, 16, 2});
      case 0xB4368:
         return if_match(req, "ECGDSA/SHA-1", {1, 3, 36, 3, 3, 2, 5, 4, 2});
      case 0xB58CD:
         return if_match(req, "RSA/PKCS1v15(SHA-3(512))", {2, 16, 840, 1, 101, 3, 4, 3, 16});
      case 0xB6427:
         return if_match(req, "Camellia-192/GCM", {0, 3, 4401, 5, 3, 1, 9, 26});
      case 0xB7102:
         return if_match(req, "brainpool224r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 5});
      case 0xB710D:
         return if_match(req, "X509v3.CRLIssuingDistributionPoint", {2, 5, 29, 28});
      case 0xB72D4:
         return if_match(req, "Microsoft UPN", {1, 3, 6, 1, 4, 1, 311, 20, 2, 3});
      case 0xB73A5:
         return if_match(req, "RSA/PSS", {1, 2, 840, 113549, 1, 1, 10});
      case 0xB84B3:
         return if_match(req, "PKIX.CodeSigning", {1, 3, 6, 1, 5, 5, 7, 3, 3});
      case 0xB8CB9:
         return if_match(req, "GOST-34.10-2012-256", {1, 2, 643, 7, 1, 1, 1, 1});
      case 0xB945C:
         return if_match(req, "Twofish/SIV", {1, 3, 6, 1, 4, 1, 25258, 3, 4, 5});
      case 0xB94E4:
         return if_match(req, "gost_512A", {1, 2, 643, 7, 1, 2, 1, 2, 1});
      case 0xB94E5:
         return if_match(req, "gost_512B", {1, 2, 643, 7, 1, 2, 1, 2, 2});
      case 0xBA1D8:
         return if_match(req, "X520.StreetAddress", {2, 5, 4, 9});
      case 0xBCC82:
         return if_match(req, "x962_p239v1", {1, 2, 840, 10045, 3, 1, 4});
      case 0xBCC83:
         return if_match(req, "x962_p239v2", {1, 2, 840, 10045, 3, 1, 5});
      case 0xBCC84:
         return if_match(req, "x962_p239v3", {1, 2, 840, 10045, 3, 1, 6});
      case 0xBD92B:
         return if_match(req, "X509v3.HoldInstructionCode", {2, 5, 29, 23});
      case 0xBDCA9:
         return if_match(req, "AES-256/GCM", {2, 16, 840, 1, 101, 3, 4, 1, 46});
      case 0xBE48D:
         return if_match(req, "PKIX.OCSP.BasicResponse", {1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
      case 0xBF71E:
         return if_match(req, "Kyber-1024-r3", {1, 3, 6, 1, 4, 1, 25258, 1, 7, 3});
      case 0xBFF01:
         return if_match(req, "DSA/SHA-3(224)", {2, 16, 840, 1, 101, 3, 4, 3, 5});
      case 0xC0F4F:
         return if_match(req, "SphincsPlus-haraka-256f-r3.1", {1, 3, 6, 1, 4, 1, 25258, 1, 12, 3, 6});
      case 0xC1875:
         return if_match(req, "SHA-1", {1, 3, 14, 3, 2, 26});
      case 0xC28D1:
         return if_match(req, "PKIX.OCSPSigning", {1, 3, 6, 1, 5, 5, 7, 3, 9});
      case 0xC42CA:
         return if_match(req, "brainpool256r1", {1, 3, 36, 3, 3, 2, 8, 1, 1, 7});
      default:
         return {};
   }
}

std::unordered_map<OID, std::string> OID_Map::load_oid2str_map() {
   return {
      {OID{2, 5, 8, 1, 1}, "RSA"},
      {OID{1, 3, 6, 1, 4, 1, 8301, 3, 1, 2, 9, 0, 38}, "secp521r1"},
      {OID{1, 2, 643, 2, 2, 35, 1}, "gost_256A"},
      {OID{1, 2, 643, 2, 2, 36, 0}, "gost_256A"},
   };
}

std::unordered_map<std::string, OID> OID_Map::load_str2oid_map() {
   return {
      {"Curve25519", OID{1, 3, 101, 110}},
      {"SM2_Sig", OID{1, 2, 156, 10197, 1, 301, 1}},
      {"RSA/EMSA3(MD2)", OID{1, 2, 840, 113549, 1, 1, 2}},
      {"RSA/EMSA3(MD5)", OID{1, 2, 840, 113549, 1, 1, 4}},
      {"RSA/EMSA3(SHA-1)", OID{1, 2, 840, 113549, 1, 1, 5}},
      {"RSA/EMSA3(SHA-256)", OID{1, 2, 840, 113549, 1, 1, 11}},
      {"RSA/EMSA3(SHA-384)", OID{1, 2, 840, 113549, 1, 1, 12}},
      {"RSA/EMSA3(SHA-512)", OID{1, 2, 840, 113549, 1, 1, 13}},
      {"RSA/EMSA3(SHA-224)", OID{1, 2, 840, 113549, 1, 1, 14}},
      {"RSA/EMSA3(SHA-512-256)", OID{1, 2, 840, 113549, 1, 1, 16}},
      {"RSA/EMSA3(SHA-3(224))", OID{2, 16, 840, 1, 101, 3, 4, 3, 13}},
      {"RSA/EMSA3(SHA-3(256))", OID{2, 16, 840, 1, 101, 3, 4, 3, 14}},
      {"RSA/EMSA3(SHA-3(384))", OID{2, 16, 840, 1, 101, 3, 4, 3, 15}},
      {"RSA/EMSA3(SHA-3(512))", OID{2, 16, 840, 1, 101, 3, 4, 3, 16}},
      {"RSA/EMSA3(SM3)", OID{1, 2, 156, 10197, 1, 504}},
      {"RSA/EMSA3(RIPEMD-160)", OID{1, 3, 36, 3, 3, 1, 2}},
      {"RSA/EMSA4", OID{1, 2, 840, 113549, 1, 1, 10}},
      {"PBES2", OID{1, 2, 840, 113549, 1, 5, 13}},
   };
}

}  // namespace Botan
/*
* (C) 2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

void Buffered_Computation::update(std::string_view str) {
   add_data(as_span_of_bytes(str));
}

void Buffered_Computation::update_be(uint16_t val) {
   uint8_t inb[sizeof(val)];
   store_be(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::update_be(uint32_t val) {
   uint8_t inb[sizeof(val)];
   store_be(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::update_be(uint64_t val) {
   uint8_t inb[sizeof(val)];
   store_be(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::update_le(uint16_t val) {
   uint8_t inb[sizeof(val)];
   store_le(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::update_le(uint32_t val) {
   uint8_t inb[sizeof(val)];
   store_le(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::update_le(uint64_t val) {
   uint8_t inb[sizeof(val)];
   store_le(val, inb);
   add_data({inb, sizeof(inb)});
}

void Buffered_Computation::final(std::span<uint8_t> out) {
   BOTAN_ARG_CHECK(out.size() >= output_length(), "provided output buffer has insufficient capacity");
   final_result(out);
}

}  // namespace Botan
/*
* (C) 2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

void SymmetricAlgorithm::set_key(const OctetString& key) {
   set_key(std::span{key.begin(), key.length()});
}

void SymmetricAlgorithm::throw_key_not_set_error() const {
   throw Key_Not_Set(name());
}

void SymmetricAlgorithm::set_key(std::span<const uint8_t> key) {
   if(!valid_keylength(key.size())) {
      throw Invalid_Key_Length(name(), key.size());
   }
   key_schedule(key);
}

}  // namespace Botan
/*
* OctetString
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Create an OctetString from RNG output
*/
OctetString::OctetString(RandomNumberGenerator& rng, size_t len) {
   rng.random_vec(m_data, len);
}

/*
* Create an OctetString from a hex string
*/
OctetString::OctetString(std::string_view hex_string) {
   if(!hex_string.empty()) {
      m_data.resize(1 + hex_string.length() / 2);
      m_data.resize(hex_decode(m_data.data(), hex_string));
   }
}

/*
* Create an OctetString from a byte string
*/
OctetString::OctetString(const uint8_t in[], size_t n) {
   m_data.assign(in, in + n);
}

namespace {

uint8_t odd_parity_of(uint8_t x) {
   uint8_t f = x | 0x01;
   f ^= (f >> 4);
   f ^= (f >> 2);
   f ^= (f >> 1);

   return (x & 0xFE) ^ (f & 0x01);
}

}  // namespace

/*
* Set the parity of each key byte to odd
*/
void OctetString::set_odd_parity() {
   for(auto& b : m_data) {
      b = odd_parity_of(b);
   }
}

/*
* Hex encode an OctetString
*/
std::string OctetString::to_string() const {
   return hex_encode(m_data.data(), m_data.size());
}

/*
* XOR Operation for OctetStrings
*/
OctetString& OctetString::operator^=(const OctetString& k) {
   if(&k == this) {
      zeroise(m_data);
      return (*this);
   }
   xor_buf(m_data.data(), k.begin(), std::min(length(), k.length()));
   return (*this);
}

/*
* Equality Operation for OctetStrings
*/
bool operator==(const OctetString& s1, const OctetString& s2) {
   return (s1.bits_of() == s2.bits_of());
}

/*
* Unequality Operation for OctetStrings
*/
bool operator!=(const OctetString& s1, const OctetString& s2) {
   return !(s1 == s2);
}

/*
* Append Operation for OctetStrings
*/
OctetString operator+(const OctetString& k1, const OctetString& k2) {
   secure_vector<uint8_t> out;
   out += k1.bits_of();
   out += k2.bits_of();
   return OctetString(out);
}

/*
* XOR Operation for OctetStrings
*/
OctetString operator^(const OctetString& k1, const OctetString& k2) {
   secure_vector<uint8_t> out(std::max(k1.length(), k2.length()));

   copy_mem(out.data(), k1.begin(), k1.length());
   xor_buf(out.data(), k2.begin(), k2.length());
   return OctetString(out);
}

}  // namespace Botan
/*
* Base64 Encoding and Decoding
* (C) 2010,2015,2020 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

class Base64 final {
   public:
      static std::string name() noexcept { return "base64"; }

      static constexpr size_t encoding_bytes_in() noexcept { return m_encoding_bytes_in; }

      static constexpr size_t encoding_bytes_out() noexcept { return m_encoding_bytes_out; }

      static constexpr size_t decoding_bytes_in() noexcept { return m_encoding_bytes_out; }

      static constexpr size_t decoding_bytes_out() noexcept { return m_encoding_bytes_in; }

      static constexpr size_t bits_consumed() noexcept { return m_encoding_bits; }

      static constexpr size_t remaining_bits_before_padding() noexcept { return m_remaining_bits_before_padding; }

      static constexpr size_t encode_max_output(size_t input_length) {
         return (round_up(input_length, m_encoding_bytes_in) / m_encoding_bytes_in) * m_encoding_bytes_out;
      }

      static constexpr size_t decode_max_output(size_t input_length) {
         return (round_up(input_length, m_encoding_bytes_out) * m_encoding_bytes_in) / m_encoding_bytes_out;
      }

      static void encode(char out[4], const uint8_t in[3]) noexcept;

      static uint8_t lookup_binary_value(char input) noexcept;

      static bool check_bad_char(uint8_t bin, char input, bool ignore_ws);

      static void decode(uint8_t* out_ptr, const uint8_t decode_buf[4]) {
         out_ptr[0] = (decode_buf[0] << 2) | (decode_buf[1] >> 4);
         out_ptr[1] = (decode_buf[1] << 4) | (decode_buf[2] >> 2);
         out_ptr[2] = (decode_buf[2] << 6) | decode_buf[3];
      }

      static size_t bytes_to_remove(size_t final_truncate) { return final_truncate; }

   private:
      static constexpr size_t m_encoding_bits = 6;
      static constexpr size_t m_remaining_bits_before_padding = 8;

      static constexpr size_t m_encoding_bytes_in = 3;
      static constexpr size_t m_encoding_bytes_out = 4;
};

uint32_t lookup_base64_chars(uint32_t x32) {
   /*
   * The basic insight of this approach is that our goal is computing
   * f(x) = y where x is in [0,63) and y is the correct base64 encoding.
   *
   * Instead of doing this directly, we compute
   * offset(x) such that f(x) = x + offset(x)
   *
   * This is described in
   * http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html#improved-version
   *
   * Here we do a SWAR (simd within a register) implementation of Wojciech's lookup_version2_swar
   */

   uint32_t r = x32 + 0x41414141;

   r += (~swar_lt<uint32_t>(x32, 0x1A1A1A1A)) & 0x06060606;
   r -= (~swar_lt<uint32_t>(x32, 0x34343434)) & 0x4B4B4B4B;
   r -= (~swar_lt<uint32_t>(x32, 0x3E3E3E3E)) & 0x0F0F0F0F;
   r += (~swar_lt<uint32_t>(x32, 0x3F3F3F3F)) & 0x03030303;

   return r;
}

//static
void Base64::encode(char out[4], const uint8_t in[3]) noexcept {
   const uint32_t b0 = (in[0] & 0xFC) >> 2;
   const uint32_t b1 = ((in[0] & 0x03) << 4) | (in[1] >> 4);
   const uint32_t b2 = ((in[1] & 0x0F) << 2) | (in[2] >> 6);
   const uint32_t b3 = in[2] & 0x3F;

   const uint32_t z = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;

   const uint32_t b64 = lookup_base64_chars(z);

   out[0] = static_cast<char>(get_byte<0>(b64));
   out[1] = static_cast<char>(get_byte<1>(b64));
   out[2] = static_cast<char>(get_byte<2>(b64));
   out[3] = static_cast<char>(get_byte<3>(b64));
}

//static
uint8_t Base64::lookup_binary_value(char input) noexcept {
   auto has_zero_byte = [](uint64_t v) { return ((v - 0x0101010101010101) & ~(v) & 0x8080808080808080); };

   // Assumes each byte is either 0x00 or 0x80
   auto index_of_first_set_byte = [](uint64_t v) {
      return ((((v - 1) & 0x0101010101010101) * 0x0101010101010101) >> 56) - 1;
   };

   constexpr uint64_t lo = 0x0101010101010101;

   const uint8_t x = static_cast<uint8_t>(input);

   const uint64_t x8 = x * lo;

   // Defines the valid ASCII ranges of base64, except the special chars (below)
   constexpr uint64_t val_l = make_uint64(0, 0, 0, 0, 0, 'A', 'a', '0');
   constexpr uint64_t val_u = make_uint64(0, 0, 0, 0, 0, 26, 26, 10);

   // If x is in one of the ranges return a mask. Otherwise we xor in at the
   // high word which will be our invalid marker
   auto v_mask = swar_in_range<uint64_t>(x8, val_l, val_u) ^ 0x80000000;

   // This is the offset added to x to get the value
   const uint64_t val_v = 0xbfb904 ^ (0xFF000000 - (x << 24));

   uint8_t z = x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));

   // Valid base64 special characters, and some whitespace chars
   constexpr uint64_t specials_i = make_uint64(0, '+', '/', '=', ' ', '\n', '\t', '\r');

   const uint64_t specials_v = 0x3e3f8180808080 ^ (static_cast<uint64_t>(z) << 56);

   const uint64_t smask = has_zero_byte(x8 ^ specials_i) ^ 0x8000000000000000;

   return static_cast<uint8_t>(specials_v >> (8 * index_of_first_set_byte(smask)));
}

//static
bool Base64::check_bad_char(uint8_t bin, char input, bool ignore_ws) {
   if(bin <= 0x3F) {
      return true;
   } else if(!(bin == 0x81 || (bin == 0x80 && ignore_ws))) {
      throw Invalid_Argument(fmt("base64_decode: invalid character '{}'", format_char_for_display(input)));
   }
   return false;
}

}  // namespace

size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t& input_consumed, bool final_inputs) {
   return base_encode(Base64(), out, in, input_length, input_consumed, final_inputs);
}

std::string base64_encode(const uint8_t input[], size_t input_length) {
   return base_encode_to_string(Base64(), input, input_length);
}

size_t base64_decode(
   uint8_t out[], const char in[], size_t input_length, size_t& input_consumed, bool final_inputs, bool ignore_ws) {
   return base_decode(Base64(), out, in, input_length, input_consumed, final_inputs, ignore_ws);
}

size_t base64_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
   return base_decode_full(Base64(), output, input, input_length, ignore_ws);
}

size_t base64_decode(uint8_t output[], std::string_view input, bool ignore_ws) {
   return base64_decode(output, input.data(), input.length(), ignore_ws);
}

size_t base64_decode(std::span<uint8_t> output, std::string_view input, bool ignore_ws) {
   if(output.size() < base64_decode_max_output(input.size())) {
      throw Invalid_Argument("base64_decode: output buffer is too short");
   }
   return base64_decode(output.data(), input.data(), input.length(), ignore_ws);
}

secure_vector<uint8_t> base64_decode(const char input[], size_t input_length, bool ignore_ws) {
   return base_decode_to_vec<secure_vector<uint8_t>>(Base64(), input, input_length, ignore_ws);
}

secure_vector<uint8_t> base64_decode(std::string_view input, bool ignore_ws) {
   return base64_decode(input.data(), input.size(), ignore_ws);
}

size_t base64_encode_max_output(size_t input_length) {
   return Base64::encode_max_output(input_length);
}

size_t base64_decode_max_output(size_t input_length) {
   return Base64::decode_max_output(input_length);
}

}  // namespace Botan
/*
* BigInt Encoding/Decoding
* (C) 1999-2010,2012,2019,2021 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

consteval word decimal_conversion_radix() {
   if constexpr(sizeof(word) == 8) {
      return 10000000000000000000U;
   } else {
      return 1000000000U;
   }
}

consteval size_t decimal_conversion_radix_digits() {
   if constexpr(sizeof(word) == 8) {
      return 19;
   } else {
      return 9;
   }
}

}  // namespace

std::string BigInt::to_dec_string() const {
   // Use the largest power of 10 that fits in a word
   constexpr word conversion_radix = decimal_conversion_radix();
   constexpr size_t radix_digits = decimal_conversion_radix_digits();

   // (over-)estimate of the number of digits needed; log2(10) ~ 3.3219
   const size_t digit_estimate = static_cast<size_t>(1 + (static_cast<double>(this->bits()) / 3.32));

   // (over-)estimate of db such that conversion_radix^db > *this
   const size_t digit_blocks = (digit_estimate + radix_digits - 1) / radix_digits;

   BigInt value = *this;
   value.set_sign(Positive);

   // Extract groups of digits into words
   std::vector<word> digit_groups(digit_blocks);

   for(size_t i = 0; i != digit_blocks; ++i) {
      word remainder = 0;
      ct_divide_word(value, conversion_radix, value, remainder);
      digit_groups[i] = remainder;
   }

   BOTAN_ASSERT_NOMSG(value.is_zero());

   // Extract digits from the groups
   std::vector<uint8_t> digits(digit_blocks * radix_digits);

   for(size_t i = 0; i != digit_blocks; ++i) {
      word remainder = digit_groups[i];
      for(size_t j = 0; j != radix_digits; ++j) {
         // Compiler should convert div/mod by 10 into mul by magic constant
         const word digit = remainder % 10;
         remainder /= 10;
         digits[radix_digits * i + j] = static_cast<uint8_t>(digit);
      }
   }

   // remove leading zeros
   while(!digits.empty() && digits.back() == 0) {
      digits.pop_back();
   }

   BOTAN_ASSERT_NOMSG(digit_estimate >= digits.size());

   // Reverse the digits to big-endian and format to text
   std::string s;
   s.reserve(1 + digits.size());

   if(is_negative()) {
      s += "-";
   }

   // Reverse and convert to textual digits
   // TODO(Botan4) use std::ranges::reverse_view here once available (need newer Clang)
   // NOLINTNEXTLINE(modernize-loop-convert)
   for(auto i = digits.rbegin(); i != digits.rend(); ++i) {
      s.push_back(*i + '0');  // assumes ASCII
   }

   if(s.empty()) {
      s += "0";
   }

   return s;
}

std::string BigInt::to_hex_string() const {
   const size_t this_bytes = this->bytes();
   std::vector<uint8_t> bits(std::max<size_t>(1, this_bytes));

   if(this_bytes > 0) {
      this->serialize_to(bits);
   }

   std::string hrep;
   if(is_negative()) {
      hrep += "-";
   }
   hrep += "0x";
   hrep += hex_encode(bits);
   return hrep;
}

/*
* Encode two BigInt, with leading 0s if needed, and concatenate
*/
secure_vector<uint8_t> BigInt::encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes) {
   if(n1.is_negative() || n2.is_negative()) {
      throw Encoding_Error("encode_fixed_length_int_pair: values must be positive");
   }
   if(n1.bytes() > bytes || n2.bytes() > bytes) {
      throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
   }
   secure_vector<uint8_t> output(2 * bytes);
   BufferStuffer stuffer(output);
   n1.serialize_to(stuffer.next(bytes));
   n2.serialize_to(stuffer.next(bytes));
   return output;
}

BigInt BigInt::decode(std::span<const uint8_t> buf, Base base) {
   if(base == Binary) {
      return BigInt::from_bytes(buf);
   }
   return BigInt::decode(buf.data(), buf.size(), base);
}

/*
* Decode a BigInt
*/
BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base) {
   if(base == Binary) {
      return BigInt::from_bytes(std::span{buf, length});
   } else if(base == Hexadecimal) {
      BigInt r;
      secure_vector<uint8_t> binary;

      if(length % 2 == 1) {
         // Handle lack of leading 0
         const char buf0_with_leading_0[2] = {'0', static_cast<char>(buf[0])};

         binary = hex_decode_locked(buf0_with_leading_0, 2);

         if(length > 1) {
            binary += hex_decode_locked(cast_uint8_ptr_to_char(&buf[1]), length - 1, false);
         }
      } else {
         binary = hex_decode_locked(cast_uint8_ptr_to_char(buf), length, false);
      }

      r.assign_from_bytes(binary);
      return r;
   } else if(base == Decimal) {
      BigInt r;
      // This could be made faster using the same trick as to_dec_string
      for(size_t i = 0; i != length; ++i) {
         const char c = buf[i];

         if(c < '0' || c > '9') {
            throw Invalid_Argument("BigInt::decode: invalid decimal char");
         }

         const uint8_t x = c - '0';
         BOTAN_ASSERT_NOMSG(x < 10);

         r *= 10;
         r += x;
      }
      return r;
   } else {
      throw Invalid_Argument("Unknown BigInt decoding method");
   }
}

}  // namespace Botan
/*
* BigInt Input/Output
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <istream>
#include <ostream>

namespace Botan {

/*
* Write the BigInt into a stream
*/
std::ostream& operator<<(std::ostream& stream, const BigInt& n) {
   const auto stream_flags = stream.flags();
   if((stream_flags & std::ios::oct) != 0) {
      throw Invalid_Argument("Octal output of BigInt not supported");
   }

   const size_t base = (stream_flags & std::ios::hex) != 0 ? 16 : 10;

   if(base == 10) {
      stream << n.to_dec_string();
   } else {
      stream << n.to_hex_string();
   }

   if(!stream.good()) {
      throw Stream_IO_Error("BigInt output operator has failed");
   }
   return stream;
}

/*
* Read the BigInt from a stream
*/
std::istream& operator>>(std::istream& stream, BigInt& n) {
   std::string str;
   std::getline(stream, str);
   if(stream.bad() || (stream.fail() && !stream.eof())) {
      throw Stream_IO_Error("BigInt input operator has failed");
   }
   n = BigInt(str);
   return stream;
}

}  // namespace Botan
/*
* (C) 1999-2007,2018 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

BigInt& BigInt::add(const word y[], size_t y_words, Sign y_sign) {
   const size_t x_sw = sig_words();

   grow_to(std::max(x_sw, y_words) + 1);

   if(sign() == y_sign) {
      word carry = bigint_add2(mutable_data(), size() - 1, y, y_words);
      mutable_data()[size() - 1] += carry;
   } else {
      const int32_t relative_size = bigint_cmp(_data(), x_sw, y, y_words);

      if(relative_size >= 0) {
         // *this >= y
         bigint_sub2(mutable_data(), x_sw, y, y_words);
      } else {
         // *this < y: compute *this = y - *this
         bigint_sub2_rev(mutable_data(), y, y_words);
      }

      if(relative_size < 0) {
         set_sign(y_sign);
      } else if(relative_size == 0) {
         set_sign(Positive);
      }
   }

   return (*this);
}

BigInt& BigInt::mod_add(const BigInt& s, const BigInt& mod, secure_vector<word>& ws) {
   if(this->is_negative() || s.is_negative() || mod.is_negative()) {
      throw Invalid_Argument("BigInt::mod_add expects all arguments are positive");
   }

   BOTAN_DEBUG_ASSERT(*this < mod);
   BOTAN_DEBUG_ASSERT(s < mod);

   /*
   t + s or t + s - p == t - (p - s)

   So first compute ws = p - s

   Then compute t + s and t - ws

   If t - ws does not borrow, then that is the correct valued
   */

   const size_t mod_sw = mod.sig_words();
   BOTAN_ARG_CHECK(mod_sw > 0, "BigInt::mod_add modulus must be positive");

   this->grow_to(mod_sw);
   s.grow_to(mod_sw);

   // First mod_sw for p - s, 2*mod_sw for bigint_addsub workspace
   if(ws.size() < 3 * mod_sw) {
      ws.resize(3 * mod_sw);
   }

   // NOLINTBEGIN(readability-container-data-pointer)

   word borrow = bigint_sub3(&ws[0], mod._data(), mod_sw, s._data(), mod_sw);
   BOTAN_DEBUG_ASSERT(borrow == 0);
   BOTAN_UNUSED(borrow);

   // Compute t - ws
   borrow = bigint_sub3(&ws[mod_sw], this->_data(), mod_sw, &ws[0], mod_sw);

   // Compute t + s
   bigint_add3(&ws[mod_sw * 2], this->_data(), mod_sw, s._data(), mod_sw);

   CT::conditional_copy_mem(borrow, &ws[0], &ws[mod_sw * 2], &ws[mod_sw], mod_sw);
   set_words(&ws[0], mod_sw);

   // NOLINTEND(readability-container-data-pointer)

   return (*this);
}

BigInt& BigInt::mod_sub(const BigInt& s, const BigInt& mod, secure_vector<word>& ws) {
   if(this->is_negative() || s.is_negative() || mod.is_negative()) {
      throw Invalid_Argument("BigInt::mod_sub expects all arguments are positive");
   }

   // We are assuming in this function that *this and s are no more than mod_sw words long
   BOTAN_DEBUG_ASSERT(*this < mod);
   BOTAN_DEBUG_ASSERT(s < mod);

   const size_t mod_sw = mod.sig_words();

   this->grow_to(mod_sw);
   s.grow_to(mod_sw);

   if(ws.size() < mod_sw) {
      ws.resize(mod_sw);
   }

   const word borrow = bigint_sub3(ws.data(), mutable_data(), mod_sw, s._data(), mod_sw);

   // Conditionally add back the modulus
   bigint_cnd_add(borrow, ws.data(), mod._data(), mod_sw);

   copy_mem(mutable_data(), ws.data(), mod_sw);

   return (*this);
}

BigInt& BigInt::mod_mul(uint8_t y, const BigInt& mod, secure_vector<word>& ws) {
   BOTAN_ARG_CHECK(this->is_negative() == false, "*this must be positive");
   BOTAN_ARG_CHECK(y < 16, "y too large");

   BOTAN_DEBUG_ASSERT(*this < mod);

   *this *= static_cast<word>(y);
   this->reduce_below(mod, ws);
   return (*this);
}

BigInt& BigInt::rev_sub(const word y[], size_t y_sw, secure_vector<word>& ws) {
   BOTAN_UNUSED(ws);
   BigInt y_bn;
   y_bn.m_data.set_words(y, y_sw);
   *this = y_bn - *this;
   return (*this);
}

/*
* Multiplication Operator
*/
BigInt& BigInt::operator*=(const BigInt& y) {
   secure_vector<word> ws;
   return this->mul(y, ws);
}

BigInt& BigInt::mul(const BigInt& y, secure_vector<word>& ws) {
   const size_t x_sw = sig_words();
   const size_t y_sw = y.sig_words();
   set_sign((sign() == y.sign()) ? Positive : Negative);

   if(x_sw == 0 || y_sw == 0) {
      clear();
      set_sign(Positive);
   } else if(x_sw == 1 && y_sw > 0) {
      grow_to(y_sw + 1);
      bigint_linmul3(mutable_data(), y._data(), y_sw, word_at(0));
   } else {
      const size_t new_size = x_sw + y_sw + 1;
      if(ws.size() < new_size) {
         ws.resize(new_size);
      }
      secure_vector<word> z_reg(new_size);

      bigint_mul(z_reg.data(), z_reg.size(), _data(), size(), x_sw, y._data(), y.size(), y_sw, ws.data(), ws.size());

      this->swap_reg(z_reg);
   }

   return (*this);
}

BigInt& BigInt::square(secure_vector<word>& ws) {
   const size_t sw = sig_words();

   secure_vector<word> z(2 * sw);
   ws.resize(z.size());

   bigint_sqr(z.data(), z.size(), _data(), size(), sw, ws.data(), ws.size());

   swap_reg(z);
   set_sign(BigInt::Positive);

   return (*this);
}

BigInt& BigInt::operator*=(word y) {
   if(y == 0) {
      clear();
      set_sign(Positive);
   }

   const word carry = bigint_linmul2(mutable_data(), size(), y);
   set_word_at(size(), carry);

   return (*this);
}

/*
* Division Operator
*/
BigInt& BigInt::operator/=(const BigInt& y) {
   if(y.sig_words() == 1 && is_power_of_2(y.word_at(0))) {
      (*this) >>= (y.bits() - 1);
   } else {
      (*this) = (*this) / y;
   }
   return (*this);
}

/*
* Modulo Operator
*/
BigInt& BigInt::operator%=(const BigInt& mod) {
   return (*this = (*this) % mod);
}

/*
* Modulo Operator
*/
word BigInt::operator%=(word mod) {
   if(mod == 0) {
      throw Invalid_Argument("BigInt::operator%= divide by zero");
   }

   word remainder = 0;

   if(is_power_of_2(mod)) {
      remainder = (word_at(0) & (mod - 1));
   } else {
      const size_t sw = sig_words();
      for(size_t i = sw; i > 0; --i) {
         remainder = bigint_modop_vartime(remainder, word_at(i - 1), mod);
      }
   }

   if(remainder != 0 && sign() == BigInt::Negative) {
      remainder = mod - remainder;
   }

   m_data.set_to_zero();
   m_data.set_word_at(0, remainder);
   set_sign(BigInt::Positive);
   return remainder;
}

/*
* Left Shift Operator
*/
BigInt& BigInt::operator<<=(size_t shift) {
   const size_t sw = sig_words();
   const size_t new_size = sw + (shift + WordInfo<word>::bits - 1) / WordInfo<word>::bits;

   m_data.grow_to(new_size);

   bigint_shl1(m_data.mutable_data(), new_size, sw, shift);

   return (*this);
}

/*
* Right Shift Operator
*/
BigInt& BigInt::operator>>=(size_t shift) {
   bigint_shr1(m_data.mutable_data(), m_data.size(), shift);

   if(is_negative() && is_zero()) {
      set_sign(Positive);
   }

   return (*this);
}

}  // namespace Botan
/*
* BigInt Binary Operators
* (C) 1999-2007,2018 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

//static
BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_size, BigInt::Sign y_sign) {
   const size_t x_sw = x.sig_words();

   BigInt z = BigInt::with_capacity(std::max(x_sw, y_size) + 1);

   if(x.sign() == y_sign) {
      word carry = bigint_add3(z.mutable_data(), x._data(), x_sw, y, y_size);
      z.mutable_data()[std::max(x_sw, y_size)] += carry;
      z.set_sign(x.sign());
   } else {
      const int32_t relative_size = bigint_cmp(x.data(), x_sw, y, y_size);

      if(relative_size < 0) {
         // x < y so z = abs(y - x)
         // NOLINTNEXTLINE(*-suspicious-call-argument) intentionally swapping x and y here
         bigint_sub3(z.mutable_data(), y, y_size, x.data(), x_sw);
         z.set_sign(y_sign);
      } else if(relative_size == 0) {
         // Positive zero (nothing to do in this case)
      } else {
         /*
         * We know at this point that x >= y so if y_size is larger than
         * x_sw, we are guaranteed they are just leading zeros which can
         * be ignored
         */
         y_size = std::min(x_sw, y_size);
         bigint_sub3(z.mutable_data(), x.data(), x_sw, y, y_size);
         z.set_sign(x.sign());
      }
   }

   return z;
}

/*
* Multiplication Operator
*/
BigInt operator*(const BigInt& x, const BigInt& y) {
   const size_t x_sw = x.sig_words();
   const size_t y_sw = y.sig_words();

   BigInt z = BigInt::with_capacity(x.size() + y.size());

   if(x_sw == 1 && y_sw > 0) {
      bigint_linmul3(z.mutable_data(), y._data(), y_sw, x.word_at(0));
   } else if(y_sw == 1 && x_sw > 0) {
      bigint_linmul3(z.mutable_data(), x._data(), x_sw, y.word_at(0));
   } else if(x_sw > 0 && y_sw > 0) {
      secure_vector<word> workspace(z.size());

      bigint_mul(z.mutable_data(),
                 z.size(),
                 x._data(),
                 x.size(),
                 x_sw,
                 y._data(),
                 y.size(),
                 y_sw,
                 workspace.data(),
                 workspace.size());
   }

   z.cond_flip_sign(x_sw > 0 && y_sw > 0 && x.sign() != y.sign());

   return z;
}

/*
* Multiplication Operator
*/
BigInt operator*(const BigInt& x, word y) {
   const size_t x_sw = x.sig_words();

   BigInt z = BigInt::with_capacity(x_sw + 1);

   if(x_sw > 0 && y > 0) {
      bigint_linmul3(z.mutable_data(), x._data(), x_sw, y);
      z.set_sign(x.sign());
   }

   return z;
}

/*
* Division Operator
*/
BigInt operator/(const BigInt& x, const BigInt& y) {
   if(y.sig_words() == 1) {
      return x / y.word_at(0);
   }

   BigInt q;
   BigInt r;
   vartime_divide(x, y, q, r);
   return q;
}

/*
* Division Operator
*/
BigInt operator/(const BigInt& x, word y) {
   if(y == 0) {
      throw Invalid_Argument("BigInt::operator/ divide by zero");
   }

   BigInt q;
   word r = 0;
   ct_divide_word(x, y, q, r);
   return q;
}

/*
* Modulo Operator
*/
BigInt operator%(const BigInt& n, const BigInt& mod) {
   if(mod.is_zero()) {
      throw Invalid_Argument("BigInt::operator% divide by zero");
   }
   if(mod.is_negative()) {
      throw Invalid_Argument("BigInt::operator% modulus must be > 0");
   }
   if(n.is_positive() && mod.is_positive() && n < mod) {
      return n;
   }

   if(mod.sig_words() == 1) {
      return BigInt::from_word(n % mod.word_at(0));
   }

   BigInt q;
   BigInt r;
   vartime_divide(n, mod, q, r);
   return r;
}

/*
* Modulo Operator
*/
word operator%(const BigInt& n, word mod) {
   if(mod == 0) {
      throw Invalid_Argument("BigInt::operator% divide by zero");
   }

   if(mod == 1) {
      return 0;
   }

   word remainder = 0;

   if(is_power_of_2(mod)) {
      remainder = (n.word_at(0) & (mod - 1));
   } else {
      const size_t sw = n.sig_words();
      for(size_t i = sw; i > 0; --i) {
         remainder = bigint_modop_vartime(remainder, n.word_at(i - 1), mod);
      }
   }

   if(remainder != 0 && n.sign() == BigInt::Negative) {
      return mod - remainder;
   }
   return remainder;
}

/*
* Left Shift Operator
*/
BigInt operator<<(const BigInt& x, size_t shift) {
   const size_t x_sw = x.sig_words();

   const size_t new_size = x_sw + (shift + WordInfo<word>::bits - 1) / WordInfo<word>::bits;
   BigInt y = BigInt::with_capacity(new_size);
   bigint_shl2(y.mutable_data(), x._data(), x_sw, shift);
   y.set_sign(x.sign());
   return y;
}

/*
* Right Shift Operator
*/
BigInt operator>>(const BigInt& x, size_t shift) {
   const size_t shift_words = shift / WordInfo<word>::bits;
   const size_t x_sw = x.sig_words();

   if(shift_words >= x_sw) {
      return BigInt::zero();
   }

   BigInt y = BigInt::with_capacity(x_sw - shift_words);
   bigint_shr2(y.mutable_data(), x._data(), x_sw, shift);

   if(x.is_negative() && y.is_zero()) {
      y.set_sign(BigInt::Positive);
   } else {
      y.set_sign(x.sign());
   }

   return y;
}

}  // namespace Botan
/*
* BigInt Random Generation
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Randomize this number
*/
void BigInt::randomize(RandomNumberGenerator& rng, size_t bitsize, bool set_high_bit) {
   set_sign(Positive);

   if(bitsize == 0) {
      clear();
   } else {
      secure_vector<uint8_t> array = rng.random_vec(round_up(bitsize, 8) / 8);

      // Always cut unwanted bits
      if(bitsize % 8 > 0) {
         array[0] &= 0xFF >> (8 - (bitsize % 8));
      }

      // Set the highest bit if wanted
      if(set_high_bit) {
         array[0] |= 0x80 >> ((bitsize % 8) > 0 ? (8 - bitsize % 8) : 0);
      }

      assign_from_bytes(array);
   }
}

/*
* Generate a random integer within given range
*/
BigInt BigInt::random_integer(RandomNumberGenerator& rng, const BigInt& min, const BigInt& max) {
   if(min.is_negative() || max.is_negative() || max <= min) {
      throw Invalid_Argument("BigInt::random_integer invalid range");
   }

   /*
   If min is > 1 then we generate a random number `r` in [0,max-min)
   and return min + r.

   This same logic could also be reasonbly chosen for min == 1, but
   that breaks certain tests which expect stability of this function
   when generating within [1,n)
   */
   if(min > 1) {
      const BigInt diff = max - min;
      // This call is recursive, but will not recurse further
      return min + BigInt::random_integer(rng, BigInt::zero(), diff);
   }

   BOTAN_DEBUG_ASSERT(min <= 1);

   const size_t bits = max.bits();

   for(;;) {
      BigInt r;
      r.randomize(rng, bits, false);
      if(r >= min && r < max) {
         return r;
      }
   }
}

}  // namespace Botan
/*
* BigInt Base
* (C) 1999-2011,2012,2014,2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

BigInt::BigInt(uint64_t n) {
   if constexpr(sizeof(word) == 8) {
      m_data.set_word_at(0, static_cast<word>(n));
   } else {
      m_data.set_word_at(1, static_cast<word>(n >> 32));
      m_data.set_word_at(0, static_cast<word>(n));
   }
}

//static
BigInt BigInt::from_u64(uint64_t n) {
   return BigInt(n);
}

//static
BigInt BigInt::from_word(word n) {
   BigInt bn;
   bn.set_word_at(0, n);
   return bn;
}

//static
BigInt BigInt::from_s32(int32_t n) {
   if(n >= 0) {
      return BigInt::from_u64(static_cast<uint64_t>(n));
   } else {
      return -BigInt::from_u64(static_cast<uint64_t>(-n));
   }
}

//static
BigInt BigInt::with_capacity(size_t size) {
   BigInt bn;
   bn.grow_to(size);
   return bn;
}

/*
* Construct a BigInt from a string
*/
BigInt::BigInt(std::string_view str) {
   Base base = Decimal;
   size_t markers = 0;
   bool negative = false;

   if(!str.empty() && str[0] == '-') {
      markers += 1;
      negative = true;
   }

   if(str.length() > markers + 2 && str[markers] == '0' && str[markers + 1] == 'x') {
      markers += 2;
      base = Hexadecimal;
   }

   *this = decode(as_span_of_bytes(str).subspan(markers), base);

   if(negative) {
      set_sign(Negative);
   } else {
      set_sign(Positive);
   }
}

BigInt BigInt::from_string(std::string_view str) {
   return BigInt(str);
}

BigInt BigInt::from_bytes(std::span<const uint8_t> input) {
   BigInt r;
   r.assign_from_bytes(input);
   return r;
}

/*
* Construct a BigInt from an encoded BigInt
*/
BigInt::BigInt(const uint8_t input[], size_t length, Base base) {
   *this = decode(input, length, base);
}

//static
BigInt BigInt::from_bytes_with_max_bits(const uint8_t input[], size_t length, size_t max_bits) {
   const size_t input_bits = 8 * length;

   auto bn = BigInt::from_bytes(std::span{input, length});

   if(input_bits > max_bits) {
      const size_t bits_to_shift = input_bits - max_bits;

      bn >>= bits_to_shift;
   }

   return bn;
}

/*
* Construct a BigInt from an encoded BigInt
*/
BigInt::BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit) {
   randomize(rng, bits, set_high_bit);
}

uint8_t BigInt::byte_at(size_t n) const {
   return get_byte_var(sizeof(word) - (n % sizeof(word)) - 1, word_at(n / sizeof(word)));
}

int32_t BigInt::cmp_word(word other) const {
   if(is_negative()) {
      return -1;  // other is positive ...
   }

   const size_t sw = this->sig_words();
   if(sw > 1) {
      return 1;  // must be larger since other is just one word ...
   }

   return bigint_cmp(this->_data(), sw, &other, 1);
}

/*
* Comparison Function
*/
int32_t BigInt::cmp(const BigInt& other, bool check_signs) const {
   if(check_signs) {
      if(other.is_positive() && this->is_negative()) {
         return -1;
      }

      if(other.is_negative() && this->is_positive()) {
         return 1;
      }

      if(other.is_negative() && this->is_negative()) {
         return (-bigint_cmp(this->_data(), this->size(), other._data(), other.size()));
      }
   }

   return bigint_cmp(this->_data(), this->size(), other._data(), other.size());
}

bool BigInt::is_equal(const BigInt& other) const {
   if(this->sign() != other.sign()) {
      return false;
   }

   return bigint_ct_is_eq(this->_data(), this->sig_words(), other._data(), other.sig_words()).as_bool();
}

bool BigInt::is_less_than(const BigInt& other) const {
   if(this->is_negative() && other.is_positive()) {
      return true;
   }

   if(this->is_positive() && other.is_negative()) {
      return false;
   }

   if(other.is_negative() && this->is_negative()) {
      return bigint_ct_is_lt(other._data(), other.sig_words(), this->_data(), this->sig_words()).as_bool();
   }

   return bigint_ct_is_lt(this->_data(), this->sig_words(), other._data(), other.sig_words()).as_bool();
}

void BigInt::encode_words(word out[], size_t size) const {
   const size_t words = sig_words();

   if(words > size) {
      throw Encoding_Error("BigInt::encode_words value too large to encode");
   }

   clear_mem(out, size);
   copy_mem(out, _data(), words);
}

void BigInt::Data::set_to_zero() {
   m_reg.resize(m_reg.capacity());
   clear_mem(m_reg.data(), m_reg.size());
   m_sig_words = 0;
}

void BigInt::Data::mask_bits(size_t n) {
   if(n == 0) {
      return set_to_zero();
   }

   const size_t top_word = n / WordInfo<word>::bits;

   if(top_word < size()) {
      const word mask = (static_cast<word>(1) << (n % WordInfo<word>::bits)) - 1;
      const size_t len = size() - (top_word + 1);
      if(len > 0) {
         clear_mem(&m_reg[top_word + 1], len);
      }
      m_reg[top_word] &= mask;
      invalidate_sig_words();
   }
}

size_t BigInt::Data::calc_sig_words() const {
   const size_t sz = m_reg.size();
   size_t sig = sz;

   word sub = 1;

   for(size_t i = 0; i != sz; ++i) {
      const word w = m_reg[sz - i - 1];
      sub &= ct_is_zero(w);
      sig -= sub;
   }

   /*
   * This depends on the data so is poisoned, but unpoison it here as
   * later conditionals are made on the size.
   */
   CT::unpoison(sig);

   return sig;
}

/*
* Return bits {offset...offset+length}
*/
uint32_t BigInt::get_substring(size_t offset, size_t length) const {
   if(length == 0 || length > 32) {
      throw Invalid_Argument("BigInt::get_substring invalid substring length");
   }

   const uint32_t mask = 0xFFFFFFFF >> (32 - length);

   const size_t word_offset = offset / WordInfo<word>::bits;
   const size_t wshift = (offset % WordInfo<word>::bits);

   /*
   * The substring is contained within one or at most two words. The
   * offset and length are not secret, so we can perform conditional
   * operations on those values.
   */
   const word w0 = word_at(word_offset);

   if(wshift == 0 || (offset + length) / WordInfo<word>::bits == word_offset) {
      return static_cast<uint32_t>(w0 >> wshift) & mask;
   } else {
      const word w1 = word_at(word_offset + 1);
      return static_cast<uint32_t>((w0 >> wshift) | (w1 << (WordInfo<word>::bits - wshift))) & mask;
   }
}

/*
* Convert this number to a uint32_t, if possible
*/
uint32_t BigInt::to_u32bit() const {
   if(is_negative()) {
      throw Encoding_Error("BigInt::to_u32bit: Number is negative");
   }
   if(bits() > 32) {
      throw Encoding_Error("BigInt::to_u32bit: Number is too big to convert");
   }

   uint32_t out = 0;
   for(size_t i = 0; i != 4; ++i) {
      out = (out << 8) | byte_at(3 - i);
   }
   return out;
}

/*
* Clear bit number n
*/
void BigInt::clear_bit(size_t n) {
   const size_t which = n / WordInfo<word>::bits;

   if(which < size()) {
      const word mask = ~(static_cast<word>(1) << (n % WordInfo<word>::bits));
      m_data.set_word_at(which, word_at(which) & mask);
   }
}

size_t BigInt::bytes() const {
   return round_up(bits(), 8) / 8;
}

size_t BigInt::top_bits_free() const {
   const size_t words = sig_words();

   const word top_word = word_at(words - 1);
   const size_t bits_used = high_bit(CT::value_barrier(top_word));
   CT::unpoison(bits_used);
   return WordInfo<word>::bits - bits_used;
}

size_t BigInt::bits() const {
   const size_t words = sig_words();

   if(words == 0) {
      return 0;
   }

   const size_t full_words = (words - 1) * WordInfo<word>::bits;
   const size_t top_bits = WordInfo<word>::bits - top_bits_free();

   return full_words + top_bits;
}

/*
* Return the negation of this number
*/
BigInt BigInt::operator-() const {
   BigInt x = (*this);
   x.flip_sign();
   return x;
}

size_t BigInt::reduce_below(const BigInt& p, secure_vector<word>& ws) {
   if(p.is_negative() || this->is_negative()) {
      throw Invalid_Argument("BigInt::reduce_below both values must be positive");
   }

   const size_t p_words = p.sig_words();

   if(size() < p_words + 1) {
      grow_to(p_words + 1);
   }

   if(ws.size() < p_words + 1) {
      ws.resize(p_words + 1);
   }

   clear_mem(ws.data(), ws.size());

   size_t reductions = 0;

   for(;;) {
      word borrow = bigint_sub3(ws.data(), _data(), p_words + 1, p._data(), p_words);
      if(borrow > 0) {
         break;
      }

      ++reductions;
      swap_reg(ws);
   }

   return reductions;
}

void BigInt::ct_reduce_below(const BigInt& mod, secure_vector<word>& ws, size_t bound) {
   if(mod.is_negative() || this->is_negative()) {
      throw Invalid_Argument("BigInt::ct_reduce_below both values must be positive");
   }

   const size_t mod_words = mod.sig_words();

   grow_to(mod_words);

   const size_t sz = size();

   ws.resize(sz);

   clear_mem(ws.data(), sz);

   for(size_t i = 0; i != bound; ++i) {
      word borrow = bigint_sub3(ws.data(), _data(), sz, mod._data(), mod_words);

      CT::Mask<word>::is_zero(borrow).select_n(mutable_data(), ws.data(), _data(), sz);
   }
}

/*
* Return the absolute value of this number
*/
BigInt BigInt::abs() const {
   BigInt x = (*this);
   x.set_sign(Positive);
   return x;
}

/*
* Encode this number into bytes
*/
void BigInt::serialize_to(std::span<uint8_t> output) const {
   BOTAN_ARG_CHECK(this->bytes() <= output.size(), "Insufficient output space");

   this->binary_encode(output.data(), output.size());
}

/*
* Encode this number into bytes
*/
void BigInt::binary_encode(uint8_t output[], size_t len) const {
   const size_t full_words = len / sizeof(word);
   const size_t extra_bytes = len % sizeof(word);

   for(size_t i = 0; i != full_words; ++i) {
      const word w = word_at(i);
      store_be(w, output + (len - (i + 1) * sizeof(word)));
   }

   if(extra_bytes > 0) {
      const word w = word_at(full_words);

      for(size_t i = 0; i != extra_bytes; ++i) {
         output[extra_bytes - i - 1] = get_byte_var(sizeof(word) - i - 1, w);
      }
   }
}

/*
* Set this number to the value in buf
*/
void BigInt::assign_from_bytes(std::span<const uint8_t> bytes) {
   clear();

   const size_t length = bytes.size();
   const size_t full_words = length / sizeof(word);
   const size_t extra_bytes = length % sizeof(word);

   secure_vector<word> reg((round_up(full_words + (extra_bytes > 0 ? 1 : 0), 8)));

   for(size_t i = 0; i != full_words; ++i) {
      reg[i] = load_be<word>(bytes.last<sizeof(word)>());
      bytes = bytes.first(bytes.size() - sizeof(word));
   }

   if(!bytes.empty()) {
      BOTAN_ASSERT_NOMSG(extra_bytes == bytes.size());
      std::array<uint8_t, sizeof(word)> last_partial_word = {0};
      copy_mem(std::span{last_partial_word}.last(extra_bytes), bytes);
      reg[full_words] = load_be<word>(last_partial_word);
   }

   m_data.swap(reg);
}

void BigInt::ct_cond_add(bool predicate, const BigInt& value) {
   if(this->is_negative() || value.is_negative()) {
      throw Invalid_Argument("BigInt::ct_cond_add requires both values to be positive");
   }
   const size_t v_words = value.sig_words();

   this->grow_to(1 + v_words);

   const auto mask = CT::Mask<word>::expand(static_cast<word>(predicate)).value();

   word carry = 0;

   word* x = this->mutable_data();
   const word* y = value._data();

   for(size_t i = 0; i != v_words; ++i) {
      x[i] = word_add(x[i], y[i] & mask, &carry);
   }

   for(size_t i = v_words; i != size(); ++i) {
      x[i] = word_add(x[i], static_cast<word>(0), &carry);
   }
}

void BigInt::ct_shift_left(size_t shift) {
   auto shl_bit = [](const BigInt& a, BigInt& result) {
      BOTAN_DEBUG_ASSERT(a.size() + 1 == result.size());
      bigint_shl2(result.mutable_data(), a._data(), a.size(), 1);
      // shl2 may have shifted a bit into the next word, which must be dropped
      clear_mem(result.mutable_data() + result.size() - 1, 1);
   };

   auto shl_word = [](const BigInt& a, BigInt& result) {
      // the most significant word is not copied, aka. shifted out
      bigint_shl2(result.mutable_data(), a._data(), a.size() - 1 /* ignore msw */, WordInfo<word>::bits);
      // we left-shifted by a full word, the least significant word must be zero'ed
      clear_mem(result.mutable_data(), 1);
   };

   BOTAN_ASSERT_NOMSG(size() > 0);

   constexpr size_t bits_in_word = sizeof(word) * 8;
   const size_t word_shift = shift >> ceil_log2(bits_in_word);             // shift / bits_in_word
   const size_t bit_shift = shift & ((1 << ceil_log2(bits_in_word)) - 1);  // shift % bits_in_word
   const size_t iterations = std::max(size(), bits_in_word) - 1;           // uint64_t i; i << 64 is undefined behaviour

   // In every iteration, shift one bit and one word to the left and use the
   // shift results only when they are within the shift range.
   BigInt tmp;
   tmp.resize(size() + 1 /* to hold the shifted-out word */);
   for(size_t i = 0; i < iterations; ++i) {
      shl_bit(*this, tmp);
      ct_cond_assign(i < bit_shift, tmp);
      shl_word(*this, tmp);
      ct_cond_assign(i < word_shift, tmp);
   }
}

void BigInt::ct_cond_swap(bool predicate, BigInt& other) {
   const size_t max_words = std::max(size(), other.size());
   grow_to(max_words);
   other.grow_to(max_words);

   bigint_cnd_swap(static_cast<word>(predicate), this->mutable_data(), other.mutable_data(), max_words);
}

void BigInt::cond_flip_sign(bool predicate) {
   // This code is assuming Negative == 0, Positive == 1

   const auto mask = CT::Mask<uint8_t>::expand_bool(predicate);

   const uint8_t current_sign = static_cast<uint8_t>(sign());

   const uint8_t new_sign = mask.select(current_sign ^ 1, current_sign);

   set_sign(static_cast<Sign>(new_sign));
}

void BigInt::ct_cond_assign(bool predicate, const BigInt& other) {
   const size_t t_words = size();
   const size_t o_words = other.size();

   if(o_words < t_words) {
      grow_to(o_words);
   }

   const size_t r_words = std::max(t_words, o_words);

   const auto mask = CT::Mask<word>::expand_bool(predicate);

   for(size_t i = 0; i != r_words; ++i) {
      const word o_word = other.word_at(i);
      const word t_word = this->word_at(i);
      this->set_word_at(i, mask.select(o_word, t_word));
   }

   const auto same_sign = CT::Mask<word>::is_equal(sign(), other.sign()).as_choice();
   cond_flip_sign((mask.as_choice() && !same_sign).as_bool());
}

void BigInt::_const_time_poison() const {
   CT::poison(m_data.const_data(), m_data.size());
}

void BigInt::_const_time_unpoison() const {
   CT::unpoison(m_data.const_data(), m_data.size());
}

}  // namespace Botan
/*
* Division Algorithms
* (C) 1999-2007,2012,2018,2021 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

/*
* Handle signed operands, if necessary
*/
void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r) {
   q.cond_flip_sign(x.sign() != y.sign());

   if(x.is_negative() && r.is_nonzero()) {
      q -= 1;
      r = y.abs() - r;
   }
}

inline bool division_check_vartime(word q, word y2, word y1, word x3, word x2, word x1) {
   /*
   Compute (y3,y2,y1) = (y2,y1) * q
   and return true if (y3,y2,y1) > (x3,x2,x1)
   */

   word y3 = 0;
   y1 = word_madd2(q, y1, &y3);
   y2 = word_madd2(q, y2, &y3);

   if(x3 != y3) {
      return (y3 > x3);
   }
   if(x2 != y2) {
      return (y2 > x2);
   }
   return (y1 > x1);
}

}  // namespace

void ct_divide(const BigInt& x, const BigInt& y, BigInt& q_out, BigInt& r_out) {
   if(y.is_zero()) {
      throw Invalid_Argument("ct_divide: cannot divide by zero");
   }

   const size_t x_words = x.sig_words();
   const size_t y_words = y.sig_words();

   const size_t x_bits = x.bits();

   BigInt q = BigInt::with_capacity(x_words);
   BigInt r = BigInt::with_capacity(y_words);
   BigInt t = BigInt::with_capacity(y_words);  // a temporary

   for(size_t i = 0; i != x_bits; ++i) {
      const size_t b = x_bits - 1 - i;
      const bool x_b = x.get_bit(b);

      r <<= 1;
      r.conditionally_set_bit(0, x_b);

      const bool r_gte_y = bigint_sub3(t.mutable_data(), r._data(), r.size(), y._data(), y_words) == 0;

      q.conditionally_set_bit(b, r_gte_y);
      r.ct_cond_swap(r_gte_y, t);
   }

   sign_fixup(x, y, q, r);
   r_out = r;
   q_out = q;
}

BigInt ct_divide_pow2k(size_t k, const BigInt& y) {
   BOTAN_ARG_CHECK(!y.is_zero(), "Cannot divide by zero");
   BOTAN_ARG_CHECK(!y.is_negative(), "Negative divisor not supported");
   BOTAN_ARG_CHECK(k > 1, "Invalid k");

   const size_t x_bits = k + 1;
   const size_t y_bits = y.bits();

   if(x_bits < y_bits) {
      return BigInt::zero();
   }

   BOTAN_ASSERT_NOMSG(y_bits >= 1);
   const size_t x_words = (x_bits + WordInfo<word>::bits - 1) / WordInfo<word>::bits;
   const size_t y_words = y.sig_words();

   BigInt q = BigInt::with_capacity(x_words);
   BigInt r = BigInt::with_capacity(y_words + 1);
   BigInt t = BigInt::with_capacity(y_words + 1);  // a temporary

   r.set_bit(y_bits - 1);
   for(size_t i = y_bits - 1; i != x_bits; ++i) {
      const size_t b = x_bits - 1 - i;

      if(i >= y_bits) {
         bigint_shl1(r.mutable_data(), r.size(), r.size(), 1);
      }

      const bool r_gte_y = bigint_sub3(t.mutable_data(), r._data(), r.size(), y._data(), y_words) == 0;

      q.conditionally_set_bit(b, r_gte_y);

      bigint_cnd_swap(static_cast<word>(r_gte_y), r.mutable_data(), t.mutable_data(), y_words + 1);
   }

   // No need for sign fixup

   return q;
}

void ct_divide_word(const BigInt& x, word y, BigInt& q_out, word& r_out) {
   if(y == 0) {
      throw Invalid_Argument("ct_divide_word: cannot divide by zero");
   }

   const size_t x_words = x.sig_words();
   const size_t x_bits = x.bits();

   BigInt q = BigInt::with_capacity(x_words);
   word r = 0;

   for(size_t i = 0; i != x_bits; ++i) {
      const size_t b = x_bits - 1 - i;
      const bool x_b = x.get_bit(b);

      const auto r_carry = CT::Mask<word>::expand_top_bit(r);

      r <<= 1;
      r += static_cast<word>(x_b);

      const auto r_gte_y = CT::Mask<word>::is_gte(r, y) | r_carry;
      q.conditionally_set_bit(b, r_gte_y.as_bool());
      r = r_gte_y.select(r - y, r);
   }

   if(x.is_negative()) {
      q.flip_sign();
      if(r != 0) {
         --q;
         r = y - r;
      }
   }

   r_out = r;
   q_out = q;
}

BigInt ct_divide_word(const BigInt& x, word y) {
   BigInt q;
   word r = 0;
   ct_divide_word(x, y, q, r);
   BOTAN_UNUSED(r);
   return q;
}

word ct_mod_word(const BigInt& x, word y) {
   BOTAN_ARG_CHECK(x.is_positive(), "The argument x must be positive");
   BOTAN_ARG_CHECK(y != 0, "Cannot divide by zero");

   const size_t x_bits = x.bits();

   word r = 0;

   for(size_t i = 0; i != x_bits; ++i) {
      const size_t b = x_bits - 1 - i;
      const bool x_b = x.get_bit(b);

      const auto r_carry = CT::Mask<word>::expand_top_bit(r);

      r <<= 1;
      r += static_cast<word>(x_b);

      const auto r_gte_y = CT::Mask<word>::is_gte(r, y) | r_carry;
      r = r_gte_y.select(r - y, r);
   }

   return r;
}

BigInt ct_modulo(const BigInt& x, const BigInt& y) {
   if(y.is_negative() || y.is_zero()) {
      throw Invalid_Argument("ct_modulo requires y > 0");
   }

   const size_t y_words = y.sig_words();

   const size_t x_bits = x.bits();

   BigInt r = BigInt::with_capacity(y_words);
   BigInt t = BigInt::with_capacity(y_words);

   for(size_t i = 0; i != x_bits; ++i) {
      const size_t b = x_bits - 1 - i;
      const bool x_b = x.get_bit(b);

      r <<= 1;
      r.conditionally_set_bit(0, x_b);

      const bool r_gte_y = bigint_sub3(t.mutable_data(), r._data(), r.size(), y._data(), y_words) == 0;

      r.ct_cond_swap(r_gte_y, t);
   }

   if(x.is_negative()) {
      if(r.is_nonzero()) {
         r = y - r;
      }
   }

   return r;
}

/*
* Solve x = q * y + r
*
* See Handbook of Applied Cryptography section 14.2.5
*/
void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& r_out) {
   if(y_arg.is_zero()) {
      throw Invalid_Argument("vartime_divide: cannot divide by zero");
   }

   const size_t y_words = y_arg.sig_words();

   BOTAN_ASSERT_NOMSG(y_words > 0);

   BigInt y = y_arg;

   BigInt r = x;
   BigInt q = BigInt::zero();
   secure_vector<word> ws;

   r.set_sign(BigInt::Positive);
   y.set_sign(BigInt::Positive);

   // Calculate shifts needed to normalize y with high bit set
   const size_t shifts = y.top_bits_free();

   if(shifts > 0) {
      y <<= shifts;
      r <<= shifts;
   }

   // we know y has not changed size, since we only shifted up to set high bit
   const size_t t = y_words - 1;
   const size_t n = std::max(y_words, r.sig_words()) - 1;  // r may have changed size however

   BOTAN_ASSERT_NOMSG(n >= t);

   q.grow_to(n - t + 1);

   word* q_words = q.mutable_data();

   BigInt shifted_y = y << (WordInfo<word>::bits * (n - t));

   // Set q_{n-t} to number of times r > shifted_y
   q_words[n - t] = r.reduce_below(shifted_y, ws);

   const word y_t0 = y.word_at(t);
   const word y_t1 = y.word_at(t - 1);
   BOTAN_DEBUG_ASSERT((y_t0 >> (WordInfo<word>::bits - 1)) == 1);

   for(size_t j = n; j != t; --j) {
      const word x_j0 = r.word_at(j);
      const word x_j1 = r.word_at(j - 1);
      const word x_j2 = r.word_at(j - 2);

      word qjt = (x_j0 == y_t0) ? WordInfo<word>::max : bigint_divop_vartime(x_j0, x_j1, y_t0);

      // Per HAC 14.23, this operation is required at most twice
      for(size_t k = 0; k != 2; ++k) {
         if(division_check_vartime(qjt, y_t0, y_t1, x_j0, x_j1, x_j2)) {
            qjt--;
         } else {
            break;
         }
      }

      shifted_y >>= WordInfo<word>::bits;
      // Now shifted_y == y << (WordInfo<word>::bits * (j-t-1))

      // TODO this sequence could be better
      r -= qjt * shifted_y;
      if(r.is_negative()) {
         qjt--;
         r += shifted_y;
         BOTAN_ASSERT_NOMSG(r.is_positive());
      }

      q_words[j - t - 1] = qjt;
   }

   if(shifts > 0) {
      r >>= shifts;
   }

   sign_fixup(x, y_arg, q, r);

   r_out = r;
   q_out = q;
}

}  // namespace Botan
/*
* Ed25519
* (C) 2017 Ribose Inc
*
* Based on the public domain code from SUPERCOP ref10 by
* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32]) {
   uint8_t az[64];

   auto sha512 = HashFunction::create_or_throw("SHA-512");
   sha512->update(seed, 32);
   sha512->final(az);
   az[0] &= 248;
   az[31] &= 63;
   az[31] |= 64;

   ed25519_basepoint_mul(std::span<uint8_t, 32>{pk, 32}, az);

   copy_mem(sk, seed, 32);
   copy_mem(sk + 32, pk, 32);
}

void ed25519_sign(uint8_t sig[64],
                  const uint8_t m[],
                  size_t mlen,
                  const uint8_t sk[64],
                  const uint8_t domain_sep[],
                  size_t domain_sep_len) {
   uint8_t az[64];
   uint8_t nonce[64];
   uint8_t hram[64];

   auto sha512 = HashFunction::create_or_throw("SHA-512");

   sha512->update(sk, 32);
   sha512->final(az);
   az[0] &= 248;
   az[31] &= 63;
   az[31] |= 64;

   sha512->update(domain_sep, domain_sep_len);
   sha512->update(az + 32, 32);
   sha512->update(m, mlen);
   sha512->final(nonce);

   sc_reduce(nonce);
   ed25519_basepoint_mul(std::span<uint8_t, 32>{sig, 32}, nonce);

   sha512->update(domain_sep, domain_sep_len);
   sha512->update(sig, 32);
   sha512->update(sk + 32, 32);
   sha512->update(m, mlen);
   sha512->final(hram);

   sc_reduce(hram);
   sc_muladd(sig + 32, hram, az, nonce);
}

bool ed25519_verify(const uint8_t* m,
                    size_t mlen,
                    const uint8_t sig[64],
                    const uint8_t* pk,
                    const uint8_t domain_sep[],
                    size_t domain_sep_len) {
   if((sig[63] & 0xE0) != 0x00) {
      return false;
   }

   const uint64_t CURVE25519_ORDER[4] = {
      0x1000000000000000,
      0x0000000000000000,
      0x14def9dea2f79cd6,
      0x5812631a5cf5d3ed,
   };

   const uint64_t s[4] = {load_le<uint64_t>(sig + 32, 3),
                          load_le<uint64_t>(sig + 32, 2),
                          load_le<uint64_t>(sig + 32, 1),
                          load_le<uint64_t>(sig + 32, 0)};

   // RFC 8032 adds the requirement that we verify that s < order in
   // the signature; this did not exist in the original Ed25519 spec.
   for(size_t i = 0; i != 4; ++i) {
      if(s[i] > CURVE25519_ORDER[i]) {
         return false;
      }
      if(s[i] < CURVE25519_ORDER[i]) {
         break;
      }
      if(i == 3) {  // here s == order
         return false;
      }
   }

   uint8_t h[64];
   auto sha512 = HashFunction::create_or_throw("SHA-512");

   sha512->update(domain_sep, domain_sep_len);
   sha512->update(sig, 32);
   sha512->update(pk, 32);
   sha512->update(m, mlen);
   sha512->final(h);
   sc_reduce(h);

   return signature_check(std::span<const uint8_t, 32>{pk, 32}, h, sig, sig + 32);
}

}  // namespace Botan
/*
* Ed25519 field element
* (C) 2017 Ribose Inc
*
* Based on the public domain code from SUPERCOP ref10 by
* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

//static
Ed25519_FieldElement Ed25519_FieldElement::invert() const {
   auto t0 = this->sqr();
   auto t1 = t0.sqr_iter(2);
   t1 = *this * t1;
   t0 = t0 * t1;
   auto t2 = t0.sqr();
   t1 = t1 * t2;
   t2 = t1.sqr_iter(5);
   t1 = t2 * t1;
   t2 = t1.sqr_iter(10);
   t2 = t2 * t1;
   auto t3 = t2.sqr_iter(20);
   t2 = t3 * t2;
   t2 = t2.sqr_iter(10);
   t1 = t2 * t1;
   t2 = t1.sqr_iter(50);
   t2 = t2 * t1;
   t3 = t2.sqr_iter(100);
   t2 = t3 * t2;
   t2 = t2.sqr_iter(50);
   t1 = t2 * t1;
   t1 = t1.sqr_iter(5);

   t0 = t1 * t0;
   return t0;
}

Ed25519_FieldElement Ed25519_FieldElement::pow_22523() const {
   auto t0 = this->sqr();
   auto t1 = t0.sqr_iter(2);
   t1 = (*this) * t1;
   t0 = t0 * t1;
   t0 = t0.sqr();
   t0 = t1 * t0;
   t1 = t0.sqr_iter(5);
   t0 = t1 * t0;
   t1 = t0.sqr_iter(10);
   t1 = t1 * t0;
   auto t2 = t1.sqr_iter(20);
   t1 = t2 * t1;
   t1 = t1.sqr_iter(10);
   t0 = t1 * t0;
   t1 = t0.sqr_iter(50);
   t1 = t1 * t0;
   t2 = t1.sqr_iter(100);
   t1 = t2 * t1;
   t1 = t1.sqr_iter(50);
   t0 = t1 * t0;
   t0 = t0.sqr_iter(2);

   t0 = t0 * (*this);
   return t0;
}

/*
h = f * g
Can overlap h with f or g.

Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.

Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/

/*
Notes on implementation strategy:

Using schoolbook multiplication.
Karatsuba would save a little in some cost models.

Most multiplications by 2 and 19 are 32-bit precomputations;
cheaper than 64-bit postcomputations.

There is one remaining multiplication by 19 in the carry chain;
one *19 precomputation can be merged into this,
but the resulting data flow is considerably less clean.

There are 12 carries below.
10 of them are 2-way parallelizable and vectorizable.
Can get away with 11 carries, but then data flow is much deeper.

With tighter constraints on inputs can squeeze carries into int32.
*/

//static
Ed25519_FieldElement Ed25519_FieldElement::mul(const Ed25519_FieldElement& f, const Ed25519_FieldElement& g) {
   const int32_t f0 = f.m_fe[0];
   const int32_t f1 = f.m_fe[1];
   const int32_t f2 = f.m_fe[2];
   const int32_t f3 = f.m_fe[3];
   const int32_t f4 = f.m_fe[4];
   const int32_t f5 = f.m_fe[5];
   const int32_t f6 = f.m_fe[6];
   const int32_t f7 = f.m_fe[7];
   const int32_t f8 = f.m_fe[8];
   const int32_t f9 = f.m_fe[9];

   const int32_t g0 = g.m_fe[0];
   const int32_t g1 = g.m_fe[1];
   const int32_t g2 = g.m_fe[2];
   const int32_t g3 = g.m_fe[3];
   const int32_t g4 = g.m_fe[4];
   const int32_t g5 = g.m_fe[5];
   const int32_t g6 = g.m_fe[6];
   const int32_t g7 = g.m_fe[7];
   const int32_t g8 = g.m_fe[8];
   const int32_t g9 = g.m_fe[9];

   const int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */
   const int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
   const int32_t g3_19 = 19 * g3;
   const int32_t g4_19 = 19 * g4;
   const int32_t g5_19 = 19 * g5;
   const int32_t g6_19 = 19 * g6;
   const int32_t g7_19 = 19 * g7;
   const int32_t g8_19 = 19 * g8;
   const int32_t g9_19 = 19 * g9;
   const int32_t f1_2 = 2 * f1;
   const int32_t f3_2 = 2 * f3;
   const int32_t f5_2 = 2 * f5;
   const int32_t f7_2 = 2 * f7;
   const int32_t f9_2 = 2 * f9;

   const int64_t f0g0 = f0 * static_cast<int64_t>(g0);
   const int64_t f0g1 = f0 * static_cast<int64_t>(g1);
   const int64_t f0g2 = f0 * static_cast<int64_t>(g2);
   const int64_t f0g3 = f0 * static_cast<int64_t>(g3);
   const int64_t f0g4 = f0 * static_cast<int64_t>(g4);
   const int64_t f0g5 = f0 * static_cast<int64_t>(g5);
   const int64_t f0g6 = f0 * static_cast<int64_t>(g6);
   const int64_t f0g7 = f0 * static_cast<int64_t>(g7);
   const int64_t f0g8 = f0 * static_cast<int64_t>(g8);
   const int64_t f0g9 = f0 * static_cast<int64_t>(g9);
   const int64_t f1g0 = f1 * static_cast<int64_t>(g0);
   const int64_t f1g1_2 = f1_2 * static_cast<int64_t>(g1);
   const int64_t f1g2 = f1 * static_cast<int64_t>(g2);
   const int64_t f1g3_2 = f1_2 * static_cast<int64_t>(g3);
   const int64_t f1g4 = f1 * static_cast<int64_t>(g4);
   const int64_t f1g5_2 = f1_2 * static_cast<int64_t>(g5);
   const int64_t f1g6 = f1 * static_cast<int64_t>(g6);
   const int64_t f1g7_2 = f1_2 * static_cast<int64_t>(g7);
   const int64_t f1g8 = f1 * static_cast<int64_t>(g8);
   const int64_t f1g9_38 = f1_2 * static_cast<int64_t>(g9_19);
   const int64_t f2g0 = f2 * static_cast<int64_t>(g0);
   const int64_t f2g1 = f2 * static_cast<int64_t>(g1);
   const int64_t f2g2 = f2 * static_cast<int64_t>(g2);
   const int64_t f2g3 = f2 * static_cast<int64_t>(g3);
   const int64_t f2g4 = f2 * static_cast<int64_t>(g4);
   const int64_t f2g5 = f2 * static_cast<int64_t>(g5);
   const int64_t f2g6 = f2 * static_cast<int64_t>(g6);
   const int64_t f2g7 = f2 * static_cast<int64_t>(g7);
   const int64_t f2g8_19 = f2 * static_cast<int64_t>(g8_19);
   const int64_t f2g9_19 = f2 * static_cast<int64_t>(g9_19);
   const int64_t f3g0 = f3 * static_cast<int64_t>(g0);
   const int64_t f3g1_2 = f3_2 * static_cast<int64_t>(g1);
   const int64_t f3g2 = f3 * static_cast<int64_t>(g2);
   const int64_t f3g3_2 = f3_2 * static_cast<int64_t>(g3);
   const int64_t f3g4 = f3 * static_cast<int64_t>(g4);
   const int64_t f3g5_2 = f3_2 * static_cast<int64_t>(g5);
   const int64_t f3g6 = f3 * static_cast<int64_t>(g6);
   const int64_t f3g7_38 = f3_2 * static_cast<int64_t>(g7_19);
   const int64_t f3g8_19 = f3 * static_cast<int64_t>(g8_19);
   const int64_t f3g9_38 = f3_2 * static_cast<int64_t>(g9_19);
   const int64_t f4g0 = f4 * static_cast<int64_t>(g0);
   const int64_t f4g1 = f4 * static_cast<int64_t>(g1);
   const int64_t f4g2 = f4 * static_cast<int64_t>(g2);
   const int64_t f4g3 = f4 * static_cast<int64_t>(g3);
   const int64_t f4g4 = f4 * static_cast<int64_t>(g4);
   const int64_t f4g5 = f4 * static_cast<int64_t>(g5);
   const int64_t f4g6_19 = f4 * static_cast<int64_t>(g6_19);
   const int64_t f4g7_19 = f4 * static_cast<int64_t>(g7_19);
   const int64_t f4g8_19 = f4 * static_cast<int64_t>(g8_19);
   const int64_t f4g9_19 = f4 * static_cast<int64_t>(g9_19);
   const int64_t f5g0 = f5 * static_cast<int64_t>(g0);
   const int64_t f5g1_2 = f5_2 * static_cast<int64_t>(g1);
   const int64_t f5g2 = f5 * static_cast<int64_t>(g2);
   const int64_t f5g3_2 = f5_2 * static_cast<int64_t>(g3);
   const int64_t f5g4 = f5 * static_cast<int64_t>(g4);
   const int64_t f5g5_38 = f5_2 * static_cast<int64_t>(g5_19);
   const int64_t f5g6_19 = f5 * static_cast<int64_t>(g6_19);
   const int64_t f5g7_38 = f5_2 * static_cast<int64_t>(g7_19);
   const int64_t f5g8_19 = f5 * static_cast<int64_t>(g8_19);
   const int64_t f5g9_38 = f5_2 * static_cast<int64_t>(g9_19);
   const int64_t f6g0 = f6 * static_cast<int64_t>(g0);
   const int64_t f6g1 = f6 * static_cast<int64_t>(g1);
   const int64_t f6g2 = f6 * static_cast<int64_t>(g2);
   const int64_t f6g3 = f6 * static_cast<int64_t>(g3);
   const int64_t f6g4_19 = f6 * static_cast<int64_t>(g4_19);
   const int64_t f6g5_19 = f6 * static_cast<int64_t>(g5_19);
   const int64_t f6g6_19 = f6 * static_cast<int64_t>(g6_19);
   const int64_t f6g7_19 = f6 * static_cast<int64_t>(g7_19);
   const int64_t f6g8_19 = f6 * static_cast<int64_t>(g8_19);
   const int64_t f6g9_19 = f6 * static_cast<int64_t>(g9_19);
   const int64_t f7g0 = f7 * static_cast<int64_t>(g0);
   const int64_t f7g1_2 = f7_2 * static_cast<int64_t>(g1);
   const int64_t f7g2 = f7 * static_cast<int64_t>(g2);
   const int64_t f7g3_38 = f7_2 * static_cast<int64_t>(g3_19);
   const int64_t f7g4_19 = f7 * static_cast<int64_t>(g4_19);
   const int64_t f7g5_38 = f7_2 * static_cast<int64_t>(g5_19);
   const int64_t f7g6_19 = f7 * static_cast<int64_t>(g6_19);
   const int64_t f7g7_38 = f7_2 * static_cast<int64_t>(g7_19);
   const int64_t f7g8_19 = f7 * static_cast<int64_t>(g8_19);
   const int64_t f7g9_38 = f7_2 * static_cast<int64_t>(g9_19);
   const int64_t f8g0 = f8 * static_cast<int64_t>(g0);
   const int64_t f8g1 = f8 * static_cast<int64_t>(g1);
   const int64_t f8g2_19 = f8 * static_cast<int64_t>(g2_19);
   const int64_t f8g3_19 = f8 * static_cast<int64_t>(g3_19);
   const int64_t f8g4_19 = f8 * static_cast<int64_t>(g4_19);
   const int64_t f8g5_19 = f8 * static_cast<int64_t>(g5_19);
   const int64_t f8g6_19 = f8 * static_cast<int64_t>(g6_19);
   const int64_t f8g7_19 = f8 * static_cast<int64_t>(g7_19);
   const int64_t f8g8_19 = f8 * static_cast<int64_t>(g8_19);
   const int64_t f8g9_19 = f8 * static_cast<int64_t>(g9_19);
   const int64_t f9g0 = f9 * static_cast<int64_t>(g0);
   const int64_t f9g1_38 = f9_2 * static_cast<int64_t>(g1_19);
   const int64_t f9g2_19 = f9 * static_cast<int64_t>(g2_19);
   const int64_t f9g3_38 = f9_2 * static_cast<int64_t>(g3_19);
   const int64_t f9g4_19 = f9 * static_cast<int64_t>(g4_19);
   const int64_t f9g5_38 = f9_2 * static_cast<int64_t>(g5_19);
   const int64_t f9g6_19 = f9 * static_cast<int64_t>(g6_19);
   const int64_t f9g7_38 = f9_2 * static_cast<int64_t>(g7_19);
   const int64_t f9g8_19 = f9 * static_cast<int64_t>(g8_19);
   const int64_t f9g9_38 = f9_2 * static_cast<int64_t>(g9_19);

   int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38;
   int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19;
   int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38;
   int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19;
   int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38;
   int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19;
   int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38;
   int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19;
   int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38;
   int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0;

   /*
   |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
   i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
   |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
   i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
   */
   carry<26>(h0, h1);
   carry<26>(h4, h5);

   /* |h0| <= 2^25 */
   /* |h4| <= 2^25 */
   /* |h1| <= 1.71*2^59 */
   /* |h5| <= 1.71*2^59 */

   carry<25>(h1, h2);
   carry<25>(h5, h6);

   /* |h1| <= 2^24; from now on fits into int32 */
   /* |h5| <= 2^24; from now on fits into int32 */
   /* |h2| <= 1.41*2^60 */
   /* |h6| <= 1.41*2^60 */

   carry<26>(h2, h3);
   carry<26>(h6, h7);
   /* |h2| <= 2^25; from now on fits into int32 unchanged */
   /* |h6| <= 2^25; from now on fits into int32 unchanged */
   /* |h3| <= 1.71*2^59 */
   /* |h7| <= 1.71*2^59 */

   carry<25>(h3, h4);
   carry<25>(h7, h8);
   /* |h3| <= 2^24; from now on fits into int32 unchanged */
   /* |h7| <= 2^24; from now on fits into int32 unchanged */
   /* |h4| <= 1.72*2^34 */
   /* |h8| <= 1.41*2^60 */

   carry<26>(h4, h5);
   carry<26>(h8, h9);
   /* |h4| <= 2^25; from now on fits into int32 unchanged */
   /* |h8| <= 2^25; from now on fits into int32 unchanged */
   /* |h5| <= 1.01*2^24 */
   /* |h9| <= 1.71*2^59 */

   carry<25, 19>(h9, h0);

   /* |h9| <= 2^24; from now on fits into int32 unchanged */
   /* |h0| <= 1.1*2^39 */

   carry<26>(h0, h1);
   /* |h0| <= 2^25; from now on fits into int32 unchanged */
   /* |h1| <= 1.01*2^24 */

   return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9);
}

/*
h = f * f
Can overlap h with f.

Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.

Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/

/*
See fe_mul.c for discussion of implementation strategy.
*/

//static
Ed25519_FieldElement Ed25519_FieldElement::sqr_iter(size_t iter) const {
   int32_t f0 = m_fe[0];
   int32_t f1 = m_fe[1];
   int32_t f2 = m_fe[2];
   int32_t f3 = m_fe[3];
   int32_t f4 = m_fe[4];
   int32_t f5 = m_fe[5];
   int32_t f6 = m_fe[6];
   int32_t f7 = m_fe[7];
   int32_t f8 = m_fe[8];
   int32_t f9 = m_fe[9];

   for(size_t i = 0; i != iter; ++i) {
      const int32_t f0_2 = 2 * f0;
      const int32_t f1_2 = 2 * f1;
      const int32_t f2_2 = 2 * f2;
      const int32_t f3_2 = 2 * f3;
      const int32_t f4_2 = 2 * f4;
      const int32_t f5_2 = 2 * f5;
      const int32_t f6_2 = 2 * f6;
      const int32_t f7_2 = 2 * f7;
      const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
      const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
      const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
      const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
      const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */

      const int64_t f0f0 = f0 * static_cast<int64_t>(f0);
      const int64_t f0f1_2 = f0_2 * static_cast<int64_t>(f1);
      const int64_t f0f2_2 = f0_2 * static_cast<int64_t>(f2);
      const int64_t f0f3_2 = f0_2 * static_cast<int64_t>(f3);
      const int64_t f0f4_2 = f0_2 * static_cast<int64_t>(f4);
      const int64_t f0f5_2 = f0_2 * static_cast<int64_t>(f5);
      const int64_t f0f6_2 = f0_2 * static_cast<int64_t>(f6);
      const int64_t f0f7_2 = f0_2 * static_cast<int64_t>(f7);
      const int64_t f0f8_2 = f0_2 * static_cast<int64_t>(f8);
      const int64_t f0f9_2 = f0_2 * static_cast<int64_t>(f9);
      const int64_t f1f1_2 = f1_2 * static_cast<int64_t>(f1);
      const int64_t f1f2_2 = f1_2 * static_cast<int64_t>(f2);
      const int64_t f1f3_4 = f1_2 * static_cast<int64_t>(f3_2);
      const int64_t f1f4_2 = f1_2 * static_cast<int64_t>(f4);
      const int64_t f1f5_4 = f1_2 * static_cast<int64_t>(f5_2);
      const int64_t f1f6_2 = f1_2 * static_cast<int64_t>(f6);
      const int64_t f1f7_4 = f1_2 * static_cast<int64_t>(f7_2);
      const int64_t f1f8_2 = f1_2 * static_cast<int64_t>(f8);
      const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
      const int64_t f2f2 = f2 * static_cast<int64_t>(f2);
      const int64_t f2f3_2 = f2_2 * static_cast<int64_t>(f3);
      const int64_t f2f4_2 = f2_2 * static_cast<int64_t>(f4);
      const int64_t f2f5_2 = f2_2 * static_cast<int64_t>(f5);
      const int64_t f2f6_2 = f2_2 * static_cast<int64_t>(f6);
      const int64_t f2f7_2 = f2_2 * static_cast<int64_t>(f7);
      const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
      const int64_t f2f9_38 = f2 * static_cast<int64_t>(f9_38);
      const int64_t f3f3_2 = f3_2 * static_cast<int64_t>(f3);
      const int64_t f3f4_2 = f3_2 * static_cast<int64_t>(f4);
      const int64_t f3f5_4 = f3_2 * static_cast<int64_t>(f5_2);
      const int64_t f3f6_2 = f3_2 * static_cast<int64_t>(f6);
      const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
      const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
      const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
      const int64_t f4f4 = f4 * static_cast<int64_t>(f4);
      const int64_t f4f5_2 = f4_2 * static_cast<int64_t>(f5);
      const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
      const int64_t f4f7_38 = f4 * static_cast<int64_t>(f7_38);
      const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
      const int64_t f4f9_38 = f4 * static_cast<int64_t>(f9_38);
      const int64_t f5f5_38 = f5 * static_cast<int64_t>(f5_38);
      const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
      const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
      const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
      const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
      const int64_t f6f6_19 = f6 * static_cast<int64_t>(f6_19);
      const int64_t f6f7_38 = f6 * static_cast<int64_t>(f7_38);
      const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
      const int64_t f6f9_38 = f6 * static_cast<int64_t>(f9_38);
      const int64_t f7f7_38 = f7 * static_cast<int64_t>(f7_38);
      const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
      const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
      const int64_t f8f8_19 = f8 * static_cast<int64_t>(f8_19);
      const int64_t f8f9_38 = f8 * static_cast<int64_t>(f9_38);
      const int64_t f9f9_38 = f9 * static_cast<int64_t>(f9_38);

      int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
      int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
      int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
      int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
      int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
      int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
      int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
      int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
      int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
      int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;

      carry<26>(h0, h1);
      carry<26>(h4, h5);
      carry<25>(h1, h2);
      carry<25>(h5, h6);
      carry<26>(h2, h3);
      carry<26>(h6, h7);

      carry<25>(h3, h4);
      carry<25>(h7, h8);

      carry<26>(h4, h5);
      carry<26>(h8, h9);
      carry<25, 19>(h9, h0);
      carry<26>(h0, h1);

      f0 = static_cast<int32_t>(h0);
      f1 = static_cast<int32_t>(h1);
      f2 = static_cast<int32_t>(h2);
      f3 = static_cast<int32_t>(h3);
      f4 = static_cast<int32_t>(h4);
      f5 = static_cast<int32_t>(h5);
      f6 = static_cast<int32_t>(h6);
      f7 = static_cast<int32_t>(h7);
      f8 = static_cast<int32_t>(h8);
      f9 = static_cast<int32_t>(h9);
   }

   return Ed25519_FieldElement(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
}

/*
h = 2 * f * f
Can overlap h with f.

Preconditions:
|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.

Postconditions:
|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*/

/*
See fe_mul.c for discussion of implementation strategy.
*/

//static
Ed25519_FieldElement Ed25519_FieldElement::sqr2() const {
   const int32_t f0 = m_fe[0];
   const int32_t f1 = m_fe[1];
   const int32_t f2 = m_fe[2];
   const int32_t f3 = m_fe[3];
   const int32_t f4 = m_fe[4];
   const int32_t f5 = m_fe[5];
   const int32_t f6 = m_fe[6];
   const int32_t f7 = m_fe[7];
   const int32_t f8 = m_fe[8];
   const int32_t f9 = m_fe[9];

   const int32_t f0_2 = 2 * f0;
   const int32_t f1_2 = 2 * f1;
   const int32_t f2_2 = 2 * f2;
   const int32_t f3_2 = 2 * f3;
   const int32_t f4_2 = 2 * f4;
   const int32_t f5_2 = 2 * f5;
   const int32_t f6_2 = 2 * f6;
   const int32_t f7_2 = 2 * f7;
   const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
   const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
   const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
   const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
   const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
   const int64_t f0f0 = f0 * static_cast<int64_t>(f0);
   const int64_t f0f1_2 = f0_2 * static_cast<int64_t>(f1);
   const int64_t f0f2_2 = f0_2 * static_cast<int64_t>(f2);
   const int64_t f0f3_2 = f0_2 * static_cast<int64_t>(f3);
   const int64_t f0f4_2 = f0_2 * static_cast<int64_t>(f4);
   const int64_t f0f5_2 = f0_2 * static_cast<int64_t>(f5);
   const int64_t f0f6_2 = f0_2 * static_cast<int64_t>(f6);
   const int64_t f0f7_2 = f0_2 * static_cast<int64_t>(f7);
   const int64_t f0f8_2 = f0_2 * static_cast<int64_t>(f8);
   const int64_t f0f9_2 = f0_2 * static_cast<int64_t>(f9);
   const int64_t f1f1_2 = f1_2 * static_cast<int64_t>(f1);
   const int64_t f1f2_2 = f1_2 * static_cast<int64_t>(f2);
   const int64_t f1f3_4 = f1_2 * static_cast<int64_t>(f3_2);
   const int64_t f1f4_2 = f1_2 * static_cast<int64_t>(f4);
   const int64_t f1f5_4 = f1_2 * static_cast<int64_t>(f5_2);
   const int64_t f1f6_2 = f1_2 * static_cast<int64_t>(f6);
   const int64_t f1f7_4 = f1_2 * static_cast<int64_t>(f7_2);
   const int64_t f1f8_2 = f1_2 * static_cast<int64_t>(f8);
   const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
   const int64_t f2f2 = f2 * static_cast<int64_t>(f2);
   const int64_t f2f3_2 = f2_2 * static_cast<int64_t>(f3);
   const int64_t f2f4_2 = f2_2 * static_cast<int64_t>(f4);
   const int64_t f2f5_2 = f2_2 * static_cast<int64_t>(f5);
   const int64_t f2f6_2 = f2_2 * static_cast<int64_t>(f6);
   const int64_t f2f7_2 = f2_2 * static_cast<int64_t>(f7);
   const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
   const int64_t f2f9_38 = f2 * static_cast<int64_t>(f9_38);
   const int64_t f3f3_2 = f3_2 * static_cast<int64_t>(f3);
   const int64_t f3f4_2 = f3_2 * static_cast<int64_t>(f4);
   const int64_t f3f5_4 = f3_2 * static_cast<int64_t>(f5_2);
   const int64_t f3f6_2 = f3_2 * static_cast<int64_t>(f6);
   const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
   const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
   const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
   const int64_t f4f4 = f4 * static_cast<int64_t>(f4);
   const int64_t f4f5_2 = f4_2 * static_cast<int64_t>(f5);
   const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
   const int64_t f4f7_38 = f4 * static_cast<int64_t>(f7_38);
   const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
   const int64_t f4f9_38 = f4 * static_cast<int64_t>(f9_38);
   const int64_t f5f5_38 = f5 * static_cast<int64_t>(f5_38);
   const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
   const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
   const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
   const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
   const int64_t f6f6_19 = f6 * static_cast<int64_t>(f6_19);
   const int64_t f6f7_38 = f6 * static_cast<int64_t>(f7_38);
   const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
   const int64_t f6f9_38 = f6 * static_cast<int64_t>(f9_38);
   const int64_t f7f7_38 = f7 * static_cast<int64_t>(f7_38);
   const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
   const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
   const int64_t f8f8_19 = f8 * static_cast<int64_t>(f8_19);
   const int64_t f8f9_38 = f8 * static_cast<int64_t>(f9_38);
   const int64_t f9f9_38 = f9 * static_cast<int64_t>(f9_38);

   int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
   int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
   int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
   int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
   int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
   int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
   int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
   int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
   int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
   int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;

   h0 += h0;
   h1 += h1;
   h2 += h2;
   h3 += h3;
   h4 += h4;
   h5 += h5;
   h6 += h6;
   h7 += h7;
   h8 += h8;
   h9 += h9;

   carry<26>(h0, h1);
   carry<26>(h4, h5);

   carry<25>(h1, h2);
   carry<25>(h5, h6);

   carry<26>(h2, h3);
   carry<26>(h6, h7);

   carry<25>(h3, h4);
   carry<25>(h7, h8);
   carry<26>(h4, h5);
   carry<26>(h8, h9);
   carry<25, 19>(h9, h0);
   carry<26>(h0, h1);

   return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9);
}

/*
Ignores top bit of h.
*/
Ed25519_FieldElement Ed25519_FieldElement::deserialize(const uint8_t s[32]) {
   int64_t h0 = load_4(s);
   int64_t h1 = load_3(s + 4) << 6;
   int64_t h2 = load_3(s + 7) << 5;
   int64_t h3 = load_3(s + 10) << 3;
   int64_t h4 = load_3(s + 13) << 2;
   int64_t h5 = load_4(s + 16);
   int64_t h6 = load_3(s + 20) << 7;
   int64_t h7 = load_3(s + 23) << 5;
   int64_t h8 = load_3(s + 26) << 4;
   int64_t h9 = (load_3(s + 29) & 0x7fffff) << 2;

   carry<25, 19>(h9, h0);
   carry<25>(h1, h2);
   carry<25>(h3, h4);
   carry<25>(h5, h6);
   carry<25>(h7, h8);

   carry<26>(h0, h1);
   carry<26>(h2, h3);
   carry<26>(h4, h5);
   carry<26>(h6, h7);
   carry<26>(h8, h9);

   return Ed25519_FieldElement(h0, h1, h2, h3, h4, h5, h6, h7, h8, h9);
}

/*
Preconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.

Write p=2^255-19; q=floor(h/p).
Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).

Proof:
Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.

Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
Then 0<y<1.

Write r=h-pq.
Have 0<=r<=p-1=2^255-20.
Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.

Write x=r+19(2^-255)r+y.
Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.

Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
*/

void Ed25519_FieldElement::serialize_to(std::span<uint8_t, 32> s) const {
   const int32_t X25 = (1 << 25);

   int32_t h0 = m_fe[0];
   int32_t h1 = m_fe[1];
   int32_t h2 = m_fe[2];
   int32_t h3 = m_fe[3];
   int32_t h4 = m_fe[4];
   int32_t h5 = m_fe[5];
   int32_t h6 = m_fe[6];
   int32_t h7 = m_fe[7];
   int32_t h8 = m_fe[8];
   int32_t h9 = m_fe[9];

   int32_t q = (19 * h9 + ((static_cast<int32_t>(1) << 24))) >> 25;
   q = (h0 + q) >> 26;
   q = (h1 + q) >> 25;
   q = (h2 + q) >> 26;
   q = (h3 + q) >> 25;
   q = (h4 + q) >> 26;
   q = (h5 + q) >> 25;
   q = (h6 + q) >> 26;
   q = (h7 + q) >> 25;
   q = (h8 + q) >> 26;
   q = (h9 + q) >> 25;

   /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
   h0 += 19 * q;
   /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */

   carry0<26>(h0, h1);
   carry0<25>(h1, h2);
   carry0<26>(h2, h3);
   carry0<25>(h3, h4);
   carry0<26>(h4, h5);
   carry0<25>(h5, h6);
   carry0<26>(h6, h7);
   carry0<25>(h7, h8);
   carry0<26>(h8, h9);

   int32_t carry9 = h9 >> 25;
   h9 -= carry9 * X25;
   /* h10 = carry9 */

   /*
   Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
   Have h0+...+2^230 h9 between 0 and 2^255-1;
   evidently 2^255 h10-2^255 q = 0.
   Goal: Output h0+...+2^230 h9.
   */

   s[0] = static_cast<uint8_t>(h0 >> 0);
   s[1] = static_cast<uint8_t>(h0 >> 8);
   s[2] = static_cast<uint8_t>(h0 >> 16);
   s[3] = static_cast<uint8_t>((h0 >> 24) | (h1 << 2));
   s[4] = static_cast<uint8_t>(h1 >> 6);
   s[5] = static_cast<uint8_t>(h1 >> 14);
   s[6] = static_cast<uint8_t>((h1 >> 22) | (h2 << 3));
   s[7] = static_cast<uint8_t>(h2 >> 5);
   s[8] = static_cast<uint8_t>(h2 >> 13);
   s[9] = static_cast<uint8_t>((h2 >> 21) | (h3 << 5));
   s[10] = static_cast<uint8_t>(h3 >> 3);
   s[11] = static_cast<uint8_t>(h3 >> 11);
   s[12] = static_cast<uint8_t>((h3 >> 19) | (h4 << 6));
   s[13] = static_cast<uint8_t>(h4 >> 2);
   s[14] = static_cast<uint8_t>(h4 >> 10);
   s[15] = static_cast<uint8_t>(h4 >> 18);
   s[16] = static_cast<uint8_t>(h5 >> 0);
   s[17] = static_cast<uint8_t>(h5 >> 8);
   s[18] = static_cast<uint8_t>(h5 >> 16);
   s[19] = static_cast<uint8_t>((h5 >> 24) | (h6 << 1));
   s[20] = static_cast<uint8_t>(h6 >> 7);
   s[21] = static_cast<uint8_t>(h6 >> 15);
   s[22] = static_cast<uint8_t>((h6 >> 23) | (h7 << 3));
   s[23] = static_cast<uint8_t>(h7 >> 5);
   s[24] = static_cast<uint8_t>(h7 >> 13);
   s[25] = static_cast<uint8_t>((h7 >> 21) | (h8 << 4));
   s[26] = static_cast<uint8_t>(h8 >> 4);
   s[27] = static_cast<uint8_t>(h8 >> 12);
   s[28] = static_cast<uint8_t>((h8 >> 20) | (h9 << 6));
   s[29] = static_cast<uint8_t>(h9 >> 2);
   s[30] = static_cast<uint8_t>(h9 >> 10);
   s[31] = static_cast<uint8_t>(h9 >> 18);
}

}  // namespace Botan
/*
* Ed25519
* (C) 2017 Ribose Inc
*     2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

AlgorithmIdentifier Ed25519_PublicKey::algorithm_identifier() const {
   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
   if(m_public.size() != 32) {
      return false;
   }

   /*
   This function was derived from public domain code in Tor's blinding.c
   */

   const uint8_t identity_element[32] = {1};
   if(CT::is_equal(m_public.data(), identity_element, 32).as_bool()) {
      return false;
   }

   // The order of the Ed25519 group encoded
   const uint8_t modm_m[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7,
                               0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};

   const unsigned char zero[32] = {0};

   unsigned char pkcopy[32];

   copy_mem(pkcopy, m_public.data(), 32);
   pkcopy[31] ^= (1 << 7);  // flip sign

   return signature_check(pkcopy, modm_m, identity_element, zero);
}

Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len) {
   if(pub_len != 32) {
      throw Decoding_Error("Invalid length for Ed25519 key");
   }
   m_public.assign(pub_key, pub_key + pub_len);
}

Ed25519_PublicKey::Ed25519_PublicKey(const AlgorithmIdentifier& /*unused*/, std::span<const uint8_t> key_bits) {
   m_public.assign(key_bits.begin(), key_bits.end());

   if(m_public.size() != 32) {
      throw Decoding_Error("Invalid size for Ed25519 public key");
   }
}

std::vector<uint8_t> Ed25519_PublicKey::raw_public_key_bits() const {
   return m_public;
}

std::vector<uint8_t> Ed25519_PublicKey::public_key_bits() const {
   return raw_public_key_bits();
}

std::unique_ptr<Private_Key> Ed25519_PublicKey::generate_another(RandomNumberGenerator& rng) const {
   return std::make_unique<Ed25519_PrivateKey>(rng);
}

Ed25519_PrivateKey::Ed25519_PrivateKey(std::span<const uint8_t> secret_key) {
   if(secret_key.size() == 64) {
      m_private.assign(secret_key.begin(), secret_key.end());
      m_public.assign(m_private.begin() + 32, m_private.end());
   } else if(secret_key.size() == 32) {
      m_public.resize(32);
      m_private.resize(64);
      ed25519_gen_keypair(m_public.data(), m_private.data(), secret_key.data());
   } else {
      throw Decoding_Error("Invalid size for Ed25519 private key");
   }
}

//static
Ed25519_PrivateKey Ed25519_PrivateKey::from_seed(std::span<const uint8_t> seed) {
   BOTAN_ARG_CHECK(seed.size() == 32, "Ed25519 seed must be exactly 32 bytes long");
   return Ed25519_PrivateKey(seed);
}

//static
Ed25519_PrivateKey Ed25519_PrivateKey::from_bytes(std::span<const uint8_t> bytes) {
   BOTAN_ARG_CHECK(bytes.size() == 64, "Ed25519 private key must be exactly 64 bytes long");
   return Ed25519_PrivateKey(bytes);
}

Ed25519_PrivateKey::Ed25519_PrivateKey(RandomNumberGenerator& rng) {
   const secure_vector<uint8_t> seed = rng.random_vec(32);
   m_public.resize(32);
   m_private.resize(64);
   ed25519_gen_keypair(m_public.data(), m_private.data(), seed.data());
}

Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span<const uint8_t> key_bits) {
   secure_vector<uint8_t> bits;
   BER_Decoder(key_bits).decode(bits, ASN1_Type::OctetString).discard_remaining();

   if(bits.size() != 32) {
      throw Decoding_Error("Invalid size for Ed25519 private key");
   }
   m_public.resize(32);
   m_private.resize(64);
   ed25519_gen_keypair(m_public.data(), m_private.data(), bits.data());
}

std::unique_ptr<Public_Key> Ed25519_PrivateKey::public_key() const {
   return std::make_unique<Ed25519_PublicKey>(get_public_key());
}

secure_vector<uint8_t> Ed25519_PrivateKey::private_key_bits() const {
   secure_vector<uint8_t> bits(m_private.data(), &m_private[32]);
   return DER_Encoder().encode(bits, ASN1_Type::OctetString).get_contents();
}

bool Ed25519_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
   return true;  // ???
}

namespace {

/**
* Ed25519 verifying operation
*/
class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification {
   public:
      explicit Ed25519_Pure_Verify_Operation(const Ed25519_PublicKey& key) : m_key(key.get_public_key()) {}

      void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }

      bool is_valid_signature(std::span<const uint8_t> sig) override {
         if(sig.size() != 64) {
            return false;
         }

         BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig.data(), m_key.data(), nullptr, 0);
         m_msg.clear();
         return ok;
      }

      std::string hash_function() const override { return "SHA-512"; }

   private:
      std::vector<uint8_t> m_msg;
      std::vector<uint8_t> m_key;
};

/**
* Ed25519 verifying operation with pre-hash
*/
class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification {
   public:
      Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, std::string_view hash, bool rfc8032) :
            m_key(key.get_public_key()) {
         m_hash = HashFunction::create_or_throw(hash);

         if(rfc8032) {
            m_domain_sep = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
                            0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
                            0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
         }
      }

      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }

      bool is_valid_signature(std::span<const uint8_t> sig) override {
         if(sig.size() != 64) {
            return false;
         }
         std::vector<uint8_t> msg_hash(m_hash->output_length());
         m_hash->final(msg_hash.data());

         BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
         return ed25519_verify(
            msg_hash.data(), msg_hash.size(), sig.data(), m_key.data(), m_domain_sep.data(), m_domain_sep.size());
      }

      std::string hash_function() const override { return m_hash->name(); }

   private:
      std::unique_ptr<HashFunction> m_hash;
      std::vector<uint8_t> m_key;
      std::vector<uint8_t> m_domain_sep;
};

/**
* Ed25519 signing operation ('pure' - signs message directly)
*/
class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature {
   public:
      explicit Ed25519_Pure_Sign_Operation(const Ed25519_PrivateKey& key) : m_key(key.raw_private_key_bits()) {}

      void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }

      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
         std::vector<uint8_t> sig(64);
         ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.data(), nullptr, 0);
         m_msg.clear();
         return sig;
      }

      size_t signature_length() const override { return 64; }

      AlgorithmIdentifier algorithm_identifier() const override;

      std::string hash_function() const override { return "SHA-512"; }

   private:
      std::vector<uint8_t> m_msg;
      secure_vector<uint8_t> m_key;
};

AlgorithmIdentifier Ed25519_Pure_Sign_Operation::algorithm_identifier() const {
   return AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

/**
* Ed25519 signing operation with pre-hash
*/
class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature {
   public:
      Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, std::string_view hash, bool rfc8032) :
            m_key(key.raw_private_key_bits()) {
         m_hash = HashFunction::create_or_throw(hash);

         if(rfc8032) {
            m_domain_sep = std::vector<uint8_t>{0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
                                                0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
                                                0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
         }
      }

      size_t signature_length() const override { return 64; }

      void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }

      std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
         std::vector<uint8_t> sig(64);
         std::vector<uint8_t> msg_hash(m_hash->output_length());
         m_hash->final(msg_hash.data());
         ed25519_sign(
            sig.data(), msg_hash.data(), msg_hash.size(), m_key.data(), m_domain_sep.data(), m_domain_sep.size());
         return sig;
      }

      std::string hash_function() const override { return m_hash->name(); }

   private:
      std::unique_ptr<HashFunction> m_hash;
      secure_vector<uint8_t> m_key;
      std::vector<uint8_t> m_domain_sep;
};

}  // namespace

std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_verification_op(std::string_view params,
                                                                                std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      if(params.empty() || params == "Identity" || params == "Pure") {
         return std::make_unique<Ed25519_Pure_Verify_Operation>(*this);
      } else if(params == "Ed25519ph") {
         return std::make_unique<Ed25519_Hashed_Verify_Operation>(*this, "SHA-512", true);
      } else {
         return std::make_unique<Ed25519_Hashed_Verify_Operation>(*this, params, false);
      }
   }
   throw Provider_Not_Found(algo_name(), provider);
}

std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id,
                                                                                     std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      if(alg_id != this->algorithm_identifier()) {
         throw Decoding_Error("Unexpected AlgorithmIdentifier for Ed25519 X509 signature");
      }

      return std::make_unique<Ed25519_Pure_Verify_Operation>(*this);
   }
   throw Provider_Not_Found(algo_name(), provider);
}

std::unique_ptr<PK_Ops::Signature> Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
                                                                           std::string_view params,
                                                                           std::string_view provider) const {
   if(provider == "base" || provider.empty()) {
      if(params.empty() || params == "Identity" || params == "Pure") {
         return std::make_unique<Ed25519_Pure_Sign_Operation>(*this);
      } else if(params == "Ed25519ph") {
         return std::make_unique<Ed25519_Hashed_Sign_Operation>(*this, "SHA-512", true);
      } else {
         return std::make_unique<Ed25519_Hashed_Sign_Operation>(*this, params, false);
      }
   }
   throw Provider_Not_Found(algo_name(), provider);
}

}  // namespace Botan
/*
* Ed25519 group operations
* (C) 2017 Ribose Inc
*     2025 Jack Lloyd
*
* Based on the public domain code from SUPERCOP ref10 by
* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <array>

namespace Botan {

namespace {

/**
Here the group is the set of pairs (x,y) of field elements (see ed5519_fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2 where d = -121665/121666.

Several different point representations are used in this implementation
*/

/**
* Ed25519_Point_Completed
*
* ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
*/
class Ed25519_Point_Completed {
   public:
      Ed25519_FieldElement X;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Y;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Z;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement T;  // NOLINT(misc-non-private-member-variables-in-classes)
};

/**
* Ed25519_Point_Projective
*
* (X:Y:Z) satisfying x=X/Z, y=Y/Z
*/
class Ed25519_Point_Projective {
   public:
      Ed25519_FieldElement X;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Y;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Z;  // NOLINT(misc-non-private-member-variables-in-classes)

      /*
      * Point conversion
      */
      static Ed25519_Point_Projective from(const Ed25519_Point_Completed& p) {
         Ed25519_Point_Projective r;
         r.X = p.X * p.T;
         r.Y = p.Y * p.Z;
         r.Z = p.Z * p.T;
         return r;
      }

      static constexpr Ed25519_Point_Projective identity() {
         Ed25519_Point_Projective h;
         h.X = Ed25519_FieldElement::zero();
         h.Y = Ed25519_FieldElement::one();
         h.Z = Ed25519_FieldElement::one();
         return h;
      }

      void serialize_to(std::span<uint8_t, 32> s) const {
         auto recip = this->Z.invert();
         auto x = this->X * recip;
         auto y = this->Y * recip;
         y.serialize_to(s);
         s[31] ^= x.is_negative() ? 0x80 : 0x00;
      }

      Ed25519_Point_Completed dbl() const;
};

Ed25519_Point_Completed Ed25519_Point_Projective::dbl() const {
   Ed25519_Point_Completed r;
   r.X = X.sqr();        // XX=X1^2
   r.Z = Y.sqr();        // YY=Y1^2
   r.T = Z.sqr2();       // B=2*Z1^2
   r.Y = X + Y;          // A=X1+Y1
   auto t0 = r.Y.sqr();  // AA=A^2
   r.Y = r.Z + r.X;      // Y3=YY+XX
   r.Z = r.Z - r.X;      // Z3=YY-XX
   r.X = t0 - r.Y;       // X3=AA-Y3
   r.T = r.T - r.Z;      // T3=B-Z3
   return r;
}

/**
* Ed25519_Point_Extended
*
* (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
*/
class Ed25519_Point_Extended {
   public:
      Ed25519_FieldElement X;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Y;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Z;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement T;  // NOLINT(misc-non-private-member-variables-in-classes)

      static constexpr Ed25519_Point_Extended identity() {
         Ed25519_Point_Extended h;
         h.X = Ed25519_FieldElement::zero();
         h.Y = Ed25519_FieldElement::one();
         h.Z = Ed25519_FieldElement::one();
         h.T = Ed25519_FieldElement::zero();
         return h;
      }

      Ed25519_Point_Completed dbl() const {
         Ed25519_Point_Projective q;
         q.X = X;
         q.Y = Y;
         q.Z = Z;
         return q.dbl();
      }

      /**
      * Point conversion
      */
      static Ed25519_Point_Extended from(const Ed25519_Point_Completed& p) {
         Ed25519_Point_Extended r;
         r.X = p.X * p.T;
         r.Y = p.Y * p.Z;
         r.Z = p.Z * p.T;
         r.T = p.X * p.Y;
         return r;
      }

      void serialize_to(std::span<uint8_t, 32> out) const {
         auto recip = this->Z.invert();
         auto x = this->X * recip;
         auto y = this->Y * recip;
         y.serialize_to(out);
         out[31] ^= x.is_negative() ? 0x80 : 0x00;
      }
};

/**
* Ed25519 Point in "Niels" coordinates
*
* y + x, y - x, 2d * x * y
*
* where d is the Edwards curve constant.
*/
class Ed25519_Point_Niels {
   public:
      Ed25519_FieldElement yplusx;   // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement yminusx;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement xy2d;     // NOLINT(misc-non-private-member-variables-in-classes)

      static constexpr Ed25519_Point_Niels identity() {
         Ed25519_Point_Niels h;
         h.yplusx = Ed25519_FieldElement::one();
         h.yminusx = Ed25519_FieldElement::one();
         h.xy2d = Ed25519_FieldElement::zero();
         return h;
      }
};

class Ed25519_Point_Cached {
   public:
      Ed25519_FieldElement YplusX;   // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement YminusX;  // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement Z;        // NOLINT(misc-non-private-member-variables-in-classes)
      Ed25519_FieldElement T2d;      // NOLINT(misc-non-private-member-variables-in-classes)

      /**
      * Point conversion
      */
      static Ed25519_Point_Cached from(const Ed25519_Point_Extended& p) {
         static constexpr Ed25519_FieldElement d2 = {
            -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199};
         Ed25519_Point_Cached r;
         r.YplusX = p.Y + p.X;
         r.YminusX = p.Y - p.X;
         r.Z = p.Z;
         r.T2d = p.T * d2;
         return r;
      }

      /**
      * Point conversion
      */
      static Ed25519_Point_Cached from(const Ed25519_Point_Completed& p) {
         return Ed25519_Point_Cached::from(Ed25519_Point_Extended::from(p));
      }
};

/*
* Point addition
*/
inline Ed25519_Point_Completed operator+(const Ed25519_Point_Extended& p, const Ed25519_Point_Cached& q) {
   Ed25519_Point_Completed r;
   r.X = p.Y + p.X;        // YpX1 = Y1+X1
   r.Y = p.Y - p.X;        // YmX1 = Y1-X1
   r.Z = r.X * q.YplusX;   // A = YpX1*YpX2
   r.Y = r.Y * q.YminusX;  // B = YmX1*YmX2
   r.T = q.T2d * p.T;      // C = T2d2*T1
   r.X = p.Z * q.Z;        // ZZ = Z1*Z2
   auto t0 = r.X + r.X;    // D = 2*ZZ
   r.X = r.Z - r.Y;        // X3 = A-B
   r.Y = r.Z + r.Y;        // Y3 = A+B
   r.Z = t0 + r.T;         // Z3 = D+C
   r.T = t0 - r.T;         // T3 = D-C
   return r;
}

/*
* Point addition
*/
inline Ed25519_Point_Completed operator+(const Ed25519_Point_Extended& p, const Ed25519_Point_Niels& q) {
   Ed25519_Point_Completed r;
   r.X = p.Y + p.X;        // YpX1 = Y1+X1
   r.Y = p.Y - p.X;        // YmX1 = Y1-X1
   r.Z = r.X * q.yplusx;   // A = YpX1*ypx2
   r.Y = r.Y * q.yminusx;  // B = YmX1*ymx2
   r.T = q.xy2d * p.T;     // C = xy2d2*T1
   auto t0 = p.Z + p.Z;    // D = 2*Z1
   r.X = r.Z - r.Y;        // X3 = A-B
   r.Y = r.Z + r.Y;        // Y3 = A+B
   r.Z = t0 + r.T;         // Z3 = D+C
   r.T = t0 - r.T;         // T3 = D-C
   return r;
}

/*
* Point subtraction
*/
inline Ed25519_Point_Completed operator-(const Ed25519_Point_Extended& p, const Ed25519_Point_Niels& q) {
   Ed25519_Point_Completed r;
   r.X = p.Y + p.X;        // YpX1 = Y1+X1
   r.Y = p.Y - p.X;        // YmX1 = Y1-X1
   r.Z = r.X * q.yminusx;  // A = YpX1*ymx2
   r.Y = r.Y * q.yplusx;   // B = YmX1*ypx2
   r.T = q.xy2d * p.T;     // C = xy2d2*T1
   auto t0 = p.Z + p.Z;    // D = 2*Z1
   r.X = r.Z - r.Y;        // X3 = A-B
   r.Y = r.Z + r.Y;        // Y3 = A+B
   r.Z = t0 - r.T;         // Z3 = D-C
   r.T = t0 + r.T;         // T3 = D+C
   return r;
}

/*
* Point subtraction
*/
inline Ed25519_Point_Completed operator-(const Ed25519_Point_Extended& p, const Ed25519_Point_Cached& q) {
   Ed25519_Point_Completed r;
   r.X = p.Y + p.X;        // YpX1 = Y1+X1
   r.Y = p.Y - p.X;        // YmX1 = Y1-X1
   r.Z = r.X * q.YminusX;  // A = YpX1*YmX2
   r.Y = r.Y * q.YplusX;   // B = YmX1*YpX2
   r.T = q.T2d * p.T;      // C = T2d2*T1
   r.X = p.Z * q.Z;        // ZZ = Z1*Z2
   auto t0 = r.X + r.X;    // D = 2*ZZ
   r.X = r.Z - r.Y;        // X3 = A-B
   r.Y = r.Z + r.Y;        // Y3 = A+B
   r.Z = t0 - r.T;         // Z3 = D-C
   r.T = t0 + r.T;         // T3 = D+C
   return r;
}

std::array<int8_t, 256> slide(const uint8_t* a) {
   std::array<int8_t, 256> r{};
   for(size_t i = 0; i < 256; ++i) {
      r[i] = 1 & (a[i >> 3] >> (i & 7));
   }

   for(size_t i = 0; i < 256; ++i) {
      if(r[i] != 0) {
         for(size_t b = 1; b <= 6 && i + b < 256; ++b) {
            if(r[i + b] != 0) {
               if(r[i] + (r[i + b] << b) <= 15) {
                  r[i] += r[i + b] << b;
                  r[i + b] = 0;
               } else if(r[i] - (r[i + b] << b) >= -15) {
                  r[i] -= r[i + b] << b;
                  for(size_t k = i + b; k < 256; ++k) {
                     if(r[k] == 0) {
                        r[k] = 1;
                        break;
                     }
                     r[k] = 0;
                  }
               } else {
                  break;
               }
            }
         }
      }
   }

   return r;
}

std::optional<Ed25519_Point_Extended> frombytes_negate_vartime(std::span<const uint8_t, 32> s) {
   static constexpr Ed25519_FieldElement d = {
      -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116};
   static constexpr Ed25519_FieldElement sqrtm1 = {
      -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482};

   auto h = Ed25519_Point_Extended::identity();
   h.Y = Ed25519_FieldElement::deserialize(s.data());
   h.Z = Ed25519_FieldElement::one();
   auto u = h.Y.sqr();
   auto v = u * d;
   u = u - h.Z; /* u = y^2-1 */
   v = v + h.Z; /* v = dy^2+1 */

   auto v3 = v.sqr() * v;
   h.X = v3.sqr();
   h.X = h.X * v;
   h.X = h.X * u; /* x = uv^7 */

   h.X = h.X.pow_22523();
   h.X = h.X * v3;
   h.X = h.X * u; /* x = uv^3(uv^7)^((q-5)/8) */

   auto vxx = h.X.sqr();
   vxx = vxx * v;
   auto check = vxx - u; /* vx^2-u */
   if(!check.is_zero()) {
      check = vxx + u; /* vx^2+u */
      if(!check.is_zero()) {
         return {};
      }
      h.X = h.X * sqrtm1;
   }

   if(h.X.is_negative() == bool(s[31] >> 7)) {
      h.X = -h.X;
   }

   h.T = h.X * h.Y;
   return h;
}

/*
r = a * A + b * B
where a = a[0]+256*a[1]+...+256^31 a[31].
and b = b[0]+256*b[1]+...+256^31 b[31].
B is the Ed25519 base point (x,4/5) with x positive.
*/

void ge_double_scalarmult_vartime(std::span<uint8_t, 32> out,
                                  const uint8_t* a,
                                  const Ed25519_Point_Extended& A,
                                  const uint8_t* b) {
   static constexpr Ed25519_Point_Niels Bi[8] = {
      {
         {25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
         {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
         {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
      },
      {
         {15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
         {16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
         {30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
      },
      {
         {10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
         {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
         {19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
      },
      {
         {5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
         {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
         {28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
      },
      {
         {-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
         {-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
         {4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784},
      },
      {
         {-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
         {25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
         {23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877},
      },
      {
         {-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
         {-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
         {-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300},
      },
      {
         {-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
         {-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
         {-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683},
      },
   };

   auto aslide = slide(a);
   auto bslide = slide(b);

   Ed25519_Point_Cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
   Ai[0] = Ed25519_Point_Cached::from(A);
   const auto A2 = Ed25519_Point_Extended::from(A.dbl());

   for(size_t i = 1; i != 8; ++i) {
      Ai[i] = Ed25519_Point_Cached::from(A2 + Ai[i - 1]);
   }

   auto r = Ed25519_Point_Projective::identity();

   int i = [&]() -> int {
      int w = 255;
      while(w >= 0) {
         if(aslide[w] != 0 || bslide[w] != 0) {
            return w;
         } else {
            w--;
         }
      }
      return 0;
   }();

   for(; i >= 0; --i) {
      auto t = r.dbl();

      if(aslide[i] > 0) {
         t = Ed25519_Point_Extended::from(t) + Ai[aslide[i] >> 1];
      } else if(aslide[i] < 0) {
         t = Ed25519_Point_Extended::from(t) - Ai[(-aslide[i]) >> 1];
      }

      if(bslide[i] > 0) {
         t = Ed25519_Point_Extended::from(t) + Bi[bslide[i] >> 1];
      } else if(bslide[i] < 0) {
         t = Ed25519_Point_Extended::from(t) - Bi[(-bslide[i]) >> 1];
      }

      r = Ed25519_Point_Projective::from(t);
   }

   r.serialize_to(std::span<uint8_t, 32>{out});
}

inline uint32_t equal32(uint8_t b, uint8_t c) {
   return CT::Mask<uint32_t>::is_equal(b, c).value();
}

inline uint8_t negative(int8_t b) {
   return static_cast<uint8_t>(b) >> 7;
}

Ed25519_Point_Niels select(const Ed25519_Point_Niels base[8], int8_t b) {
   const uint8_t bnegative = negative(b);
   const uint8_t babs = b - ((-static_cast<int>(bnegative) & b) * 2);
   const uint32_t neg_mask = equal32(bnegative, 1);

   const uint32_t mask1 = equal32(babs, 1);
   const uint32_t mask2 = equal32(babs, 2);
   const uint32_t mask3 = equal32(babs, 3);
   const uint32_t mask4 = equal32(babs, 4);
   const uint32_t mask5 = equal32(babs, 5);
   const uint32_t mask6 = equal32(babs, 6);
   const uint32_t mask7 = equal32(babs, 7);
   const uint32_t mask8 = equal32(babs, 8);

   auto t = Ed25519_Point_Niels::identity();

   for(size_t i = 0; i != 10; ++i) {
      t.yplusx[i] = t.yplusx[i] ^ ((t.yplusx[i] ^ base[0].yplusx[i]) & mask1) ^
                    ((t.yplusx[i] ^ base[1].yplusx[i]) & mask2) ^ ((t.yplusx[i] ^ base[2].yplusx[i]) & mask3) ^
                    ((t.yplusx[i] ^ base[3].yplusx[i]) & mask4) ^ ((t.yplusx[i] ^ base[4].yplusx[i]) & mask5) ^
                    ((t.yplusx[i] ^ base[5].yplusx[i]) & mask6) ^ ((t.yplusx[i] ^ base[6].yplusx[i]) & mask7) ^
                    ((t.yplusx[i] ^ base[7].yplusx[i]) & mask8);

      t.yminusx[i] = t.yminusx[i] ^ ((t.yminusx[i] ^ base[0].yminusx[i]) & mask1) ^
                     ((t.yminusx[i] ^ base[1].yminusx[i]) & mask2) ^ ((t.yminusx[i] ^ base[2].yminusx[i]) & mask3) ^
                     ((t.yminusx[i] ^ base[3].yminusx[i]) & mask4) ^ ((t.yminusx[i] ^ base[4].yminusx[i]) & mask5) ^
                     ((t.yminusx[i] ^ base[5].yminusx[i]) & mask6) ^ ((t.yminusx[i] ^ base[6].yminusx[i]) & mask7) ^
                     ((t.yminusx[i] ^ base[7].yminusx[i]) & mask8);

      t.xy2d[i] = t.xy2d[i] ^ ((t.xy2d[i] ^ base[0].xy2d[i]) & mask1) ^ ((t.xy2d[i] ^ base[1].xy2d[i]) & mask2) ^
                  ((t.xy2d[i] ^ base[2].xy2d[i]) & mask3) ^ ((t.xy2d[i] ^ base[3].xy2d[i]) & mask4) ^
                  ((t.xy2d[i] ^ base[4].xy2d[i]) & mask5) ^ ((t.xy2d[i] ^ base[5].xy2d[i]) & mask6) ^
                  ((t.xy2d[i] ^ base[6].xy2d[i]) & mask7) ^ ((t.xy2d[i] ^ base[7].xy2d[i]) & mask8);
   }

   auto minus_xy2d = -t.xy2d;

   // If negative have to swap yminusx and yplusx
   for(size_t i = 0; i != 10; ++i) {
      int32_t t_yplusx = t.yplusx[i] ^ ((t.yplusx[i] ^ t.yminusx[i]) & neg_mask);
      int32_t t_yminusx = t.yminusx[i] ^ ((t.yminusx[i] ^ t.yplusx[i]) & neg_mask);

      t.yplusx[i] = t_yplusx;
      t.yminusx[i] = t_yminusx;
      t.xy2d[i] = t.xy2d[i] ^ ((t.xy2d[i] ^ minus_xy2d[i]) & neg_mask);
   }

   return t;
}

/* base[i][j] = (j+1)*256^i*B */
constexpr Ed25519_Point_Niels B_precomp[32][8] = {
   {
      {
         {25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
         {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
         {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
      },
      {
         {-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
         {-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
         {26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697},
      },
      {
         {15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
         {16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
         {30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
      },
      {
         {-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
         {23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
         {7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325},
      },
      {
         {10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
         {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
         {19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
      },
      {
         {-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
         {-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
         {-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652},
      },
      {
         {5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
         {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
         {28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
      },
      {
         {14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
         {-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
         {27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425},
      },
   },
   {
      {
         {-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
         {27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
         {17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660},
      },
      {
         {-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
         {29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
         {5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950},
      },
      {
         {-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
         {12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
         {25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628},
      },
      {
         {-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
         {-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
         {-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653},
      },
      {
         {2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
         {13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
         {21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372},
      },
      {
         {-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
         {-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
         {-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953},
      },
      {
         {24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
         {-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
         {-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876},
      },
      {
         {2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
         {33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
         {1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038},
      },
   },
   {
      {
         {6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
         {4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
         {-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664},
      },
      {
         {1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
         {-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
         {-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222},
      },
      {
         {-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
         {20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
         {9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092},
      },
      {
         {-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
         {19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
         {8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553},
      },
      {
         {-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
         {11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
         {-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033},
      },
      {
         {-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
         {-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
         {1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890},
      },
      {
         {32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
         {-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
         {-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891},
      },
      {
         {31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
         {11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
         {-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606},
      },
   },
   {
      {
         {7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
         {-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
         {-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341},
      },
      {
         {-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
         {14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
         {-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655},
      },
      {
         {15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
         {12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
         {-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099},
      },
      {
         {26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
         {14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
         {236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930},
      },
      {
         {1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
         {5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
         {20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066},
      },
      {
         {24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
         {-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
         {-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383},
      },
      {
         {-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
         {-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
         {23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948},
      },
      {
         {9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
         {-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
         {27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629},
      },
   },
   {
      {
         {-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
         {-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
         {24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919},
      },
      {
         {11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
         {8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
         {-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771},
      },
      {
         {-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
         {10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
         {10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409},
      },
      {
         {-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
         {-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
         {28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420},
      },
      {
         {-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
         {-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
         {-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384},
      },
      {
         {-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
         {-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
         {29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708},
      },
      {
         {-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
         {-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
         {-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387},
      },
      {
         {-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
         {23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
         {-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665},
      },
   },
   {
      {
         {11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
         {-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
         {14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628},
      },
      {
         {-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
         {-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
         {-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822},
      },
      {
         {-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
         {19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
         {-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508},
      },
      {
         {-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
         {-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
         {-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158},
      },
      {
         {-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
         {-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
         {-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664},
      },
      {
         {27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
         {-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
         {-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070},
      },
      {
         {3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
         {-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
         {-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083},
      },
      {
         {31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
         {-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
         {29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396},
      },
   },
   {
      {
         {-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
         {20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
         {-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294},
      },
      {
         {-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
         {22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
         {16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195},
      },
      {
         {9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
         {24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
         {-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762},
      },
      {
         {-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
         {-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
         {-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605},
      },
      {
         {-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
         {-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
         {-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482},
      },
      {
         {-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
         {-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
         {-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170},
      },
      {
         {5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
         {10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
         {-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392},
      },
      {
         {8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
         {6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
         {28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958},
      },
   },
   {
      {
         {24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
         {26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
         {-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692},
      },
      {
         {11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
         {-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
         {-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001},
      },
      {
         {-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
         {-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
         {-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762},
      },
      {
         {15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
         {-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
         {4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035},
      },
      {
         {7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
         {-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
         {-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812},
      },
      {
         {33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
         {-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
         {-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158},
      },
      {
         {8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
         {26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
         {19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857},
      },
      {
         {801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
         {19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
         {19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483},
      },
   },
   {
      {
         {-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
         {32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
         {22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751},
      },
      {
         {-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
         {-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
         {1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230},
      },
      {
         {16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
         {-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
         {-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968},
      },
      {
         {-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
         {14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
         {5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304},
      },
      {
         {30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
         {-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
         {20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566},
      },
      {
         {-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
         {-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
         {-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943},
      },
      {
         {17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
         {23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
         {15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971},
      },
      {
         {-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
         {-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
         {-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883},
      },
   },
   {
      {
         {5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
         {-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
         {3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849},
      },
      {
         {29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
         {-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
         {-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335},
      },
      {
         {-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
         {-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
         {-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735},
      },
      {
         {-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
         {29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
         {-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336},
      },
      {
         {-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
         {31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
         {-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388},
      },
      {
         {-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
         {-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
         {-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462},
      },
      {
         {18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
         {9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
         {-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797},
      },
      {
         {21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
         {27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
         {19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688},
      },
   },
   {
      {
         {12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
         {2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
         {-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707},
      },
      {
         {7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
         {915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
         {32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044},
      },
      {
         {32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
         {-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
         {21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197},
      },
      {
         {8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
         {31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
         {19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878},
      },
      {
         {-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
         {32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
         {2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730},
      },
      {
         {-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
         {-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
         {-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715},
      },
      {
         {-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
         {-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
         {-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865},
      },
      {
         {15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
         {20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
         {32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348},
      },
   },
   {
      {
         {9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
         {-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
         {5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086},
      },
      {
         {-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
         {-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
         {-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233},
      },
      {
         {-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
         {-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
         {580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435},
      },
      {
         {23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
         {13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
         {2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563},
      },
      {
         {14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
         {-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
         {4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030},
      },
      {
         {10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
         {27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
         {6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844},
      },
      {
         {14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
         {16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
         {22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760},
      },
      {
         {-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
         {-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
         {-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112},
      },
   },
   {
      {
         {-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
         {3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
         {10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289},
      },
      {
         {15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
         {24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
         {13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226},
      },
      {
         {16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
         {29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
         {-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811},
      },
      {
         {27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
         {20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
         {-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121},
      },
      {
         {9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
         {12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
         {13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400},
      },
      {
         {4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
         {-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
         {17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045},
      },
      {
         {-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
         {-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
         {-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079},
      },
      {
         {21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
         {-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
         {-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836},
      },
   },
   {
      {
         {12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
         {18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
         {-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065},
      },
      {
         {30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
         {10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
         {-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101},
      },
      {
         {32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
         {29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
         {10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966},
      },
      {
         {25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
         {-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
         {-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689},
      },
      {
         {12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
         {-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
         {3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982},
      },
      {
         {-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
         {-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
         {-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265},
      },
      {
         {29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
         {-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
         {-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798},
      },
      {
         {-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
         {-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
         {-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927},
      },
   },
   {
      {
         {-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
         {-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
         {22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862},
      },
      {
         {-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
         {15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
         {15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020},
      },
      {
         {-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
         {11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
         {14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795},
      },
      {
         {15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
         {-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
         {29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531},
      },
      {
         {-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
         {-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
         {10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431},
      },
      {
         {10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
         {-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
         {10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320},
      },
      {
         {-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
         {14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
         {30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878},
      },
      {
         {12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
         {-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
         {-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585},
      },
   },
   {
      {
         {-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
         {10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
         {17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661},
      },
      {
         {7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
         {26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
         {-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525},
      },
      {
         {-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
         {9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
         {-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988},
      },
      {
         {5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
         {32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
         {14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697},
      },
      {
         {20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
         {-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
         {-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656},
      },
      {
         {11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
         {27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
         {10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203},
      },
      {
         {20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
         {-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
         {-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084},
      },
      {
         {-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
         {20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
         {27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667},
      },
   },
   {
      {
         {11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
         {-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
         {-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255},
      },
      {
         {6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
         {1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
         {-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333},
      },
      {
         {17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
         {31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
         {29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409},
      },
      {
         {12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
         {6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
         {-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363},
      },
      {
         {28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
         {-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
         {-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940},
      },
      {
         {13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
         {-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
         {-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290},
      },
      {
         {24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
         {-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
         {-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236},
      },
      {
         {-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
         {11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
         {-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347},
      },
   },
   {
      {
         {-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
         {-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
         {-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906},
      },
      {
         {3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
         {-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
         {-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926},
      },
      {
         {-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
         {8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
         {-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286},
      },
      {
         {2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
         {27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
         {21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896},
      },
      {
         {30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
         {26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
         {-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437},
      },
      {
         {-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
         {-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
         {-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193},
      },
      {
         {-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
         {-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
         {21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961},
      },
      {
         {8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
         {29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
         {-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362},
      },
   },
   {
      {
         {-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
         {2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
         {-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063},
      },
      {
         {-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
         {-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
         {-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369},
      },
      {
         {9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
         {18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
         {2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906},
      },
      {
         {4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
         {-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
         {7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962},
      },
      {
         {-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
         {10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
         {696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404},
      },
      {
         {-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
         {17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
         {26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804},
      },
      {
         {-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
         {-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
         {-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849},
      },
      {
         {32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
         {-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
         {-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319},
      },
   },
   {
      {
         {-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
         {-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
         {-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084},
      },
      {
         {-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
         {-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
         {20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920},
      },
      {
         {12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
         {-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
         {-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397},
      },
      {
         {-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
         {6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
         {30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165},
      },
      {
         {32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
         {17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
         {-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861},
      },
      {
         {14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
         {-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
         {18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870},
      },
      {
         {10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
         {33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
         {-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168},
      },
      {
         {30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
         {-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
         {18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598},
      },
   },
   {
      {
         {5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
         {5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
         {-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479},
      },
      {
         {-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
         {8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
         {17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719},
      },
      {
         {16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
         {-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
         {-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132},
      },
      {
         {14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
         {8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
         {15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670},
      },
      {
         {-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
         {-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
         {31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317},
      },
      {
         {-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
         {842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
         {-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078},
      },
      {
         {-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
         {-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
         {-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654},
      },
      {
         {-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
         {-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
         {12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579},
      },
   },
   {
      {
         {14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
         {10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
         {-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743},
      },
      {
         {-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
         {21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
         {-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155},
      },
      {
         {6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
         {-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
         {-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943},
      },
      {
         {-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
         {30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
         {9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406},
      },
      {
         {22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
         {-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
         {-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693},
      },
      {
         {1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
         {-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
         {-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410},
      },
      {
         {-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
         {-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
         {22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710},
      },
      {
         {29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
         {-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
         {-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895},
      },
   },
   {
      {
         {22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
         {-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
         {6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880},
      },
      {
         {-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
         {22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
         {-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899},
      },
      {
         {21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
         {9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
         {7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080},
      },
      {
         {-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
         {-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
         {-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133},
      },
      {
         {-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
         {-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
         {-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197},
      },
      {
         {2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
         {31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
         {4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755},
      },
      {
         {-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
         {-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
         {26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171},
      },
      {
         {15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
         {16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
         {28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270},
      },
   },
   {
      {
         {-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
         {-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
         {-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562},
      },
      {
         {30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
         {18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
         {19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269},
      },
      {
         {-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
         {14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
         {19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461},
      },
      {
         {30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
         {-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
         {-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218},
      },
      {
         {-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
         {18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
         {-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037},
      },
      {
         {31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
         {-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
         {-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841},
      },
      {
         {21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
         {-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
         {-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040},
      },
      {
         {3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
         {24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
         {-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814},
      },
   },
   {
      {
         {793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
         {5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
         {-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646},
      },
      {
         {10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
         {10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
         {27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453},
      },
      {
         {-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
         {4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
         {-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921},
      },
      {
         {-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
         {-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
         {-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072},
      },
      {
         {-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
         {13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
         {28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943},
      },
      {
         {-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
         {24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
         {17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494},
      },
      {
         {-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
         {-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
         {28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915},
      },
      {
         {16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
         {10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
         {-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224},
      },
   },
   {
      {
         {-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
         {15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
         {-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101},
      },
      {
         {23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
         {21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
         {-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930},
      },
      {
         {-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
         {-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
         {-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404},
      },
      {
         {-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
         {-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
         {-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525},
      },
      {
         {-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
         {-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
         {15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865},
      },
      {
         {-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
         {4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
         {-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138},
      },
      {
         {-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
         {-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
         {2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941},
      },
      {
         {-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
         {18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
         {-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819},
      },
   },
   {
      {
         {-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
         {-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
         {21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075},
      },
      {
         {16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
         {-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
         {-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817},
      },
      {
         {-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
         {-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
         {4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278},
      },
      {
         {31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
         {19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
         {24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480},
      },
      {
         {17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
         {510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
         {18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525},
      },
      {
         {13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
         {9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
         {12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892},
      },
      {
         {15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
         {11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
         {20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140},
      },
      {
         {-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
         {-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
         {24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127},
      },
   },
   {
      {
         {-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
         {-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
         {-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160},
      },
      {
         {31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
         {22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
         {-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478},
      },
      {
         {27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
         {26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
         {-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248},
      },
      {
         {-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
         {15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
         {8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184},
      },
      {
         {-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
         {-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
         {21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948},
      },
      {
         {11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
         {-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
         {-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416},
      },
      {
         {-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
         {4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
         {366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659},
      },
      {
         {-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
         {18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
         {476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329},
      },
   },
   {
      {
         {20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
         {-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
         {24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948},
      },
      {
         {-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
         {-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
         {-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517},
      },
      {
         {-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
         {-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
         {-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589},
      },
      {
         {-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
         {-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
         {-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930},
      },
      {
         {-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
         {25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
         {-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876},
      },
      {
         {22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
         {-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
         {-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112},
      },
      {
         {26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
         {17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
         {-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221},
      },
      {
         {30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
         {13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
         {-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310},
      },
   },
   {
      {
         {19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
         {31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
         {-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008},
      },
      {
         {-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
         {-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
         {31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066},
      },
      {
         {-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
         {-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
         {33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053},
      },
      {
         {31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
         {-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
         {22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095},
      },
      {
         {-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
         {23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
         {-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424},
      },
      {
         {2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
         {3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
         {-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079},
      },
      {
         {-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
         {21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
         {18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576},
      },
      {
         {30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
         {9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
         {-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099},
      },
   },
   {
      {
         {-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
         {-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
         {-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242},
      },
      {
         {-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
         {-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
         {18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373},
      },
      {
         {26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
         {-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
         {-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657},
      },
      {
         {-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
         {25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
         {31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014},
      },
      {
         {24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
         {-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
         {-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718},
      },
      {
         {30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
         {2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
         {33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444},
      },
      {
         {-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
         {23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
         {1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799},
      },
      {
         {-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
         {13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
         {-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032},
      },
   },
   {
      {
         {9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
         {-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
         {29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062},
      },
      {
         {-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
         {-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
         {-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240},
      },
      {
         {-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
         {-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
         {16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103},
      },
      {
         {-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
         {-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
         {31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016},
      },
      {
         {-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
         {15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
         {-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141},
      },
      {
         {16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
         {11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
         {-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376},
      },
      {
         {-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
         {-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
         {-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577},
      },
      {
         {-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
         {29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
         {-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476},
      },
   },
};

}  // namespace

/*
h = a * B
where a = a[0]+256*a[1]+...+256^31 a[31]
B is the Ed25519 base point (x,4/5) with x positive.

Preconditions:
  a[31] <= 127
*/
void ed25519_basepoint_mul(std::span<uint8_t, 32> out, const uint8_t a[32]) {
   std::array<int8_t, 64> e{};

   CT::poison(a, 32);

   // each e[i] is between 0 and 15 except e[63] which is between 0 and 7
   for(size_t i = 0; i != 32; ++i) {
      e[2 * i + 0] = (a[i] >> 0) & 0x0F;
      e[2 * i + 1] = (a[i] >> 4) & 0x0F;
   }

   int8_t carry = 0;
   for(size_t i = 0; i < 63; ++i) {
      e[i] += carry;
      carry = e[i] + 8;
      carry >>= 4;
      e[i] -= carry << 4;
   }
   e[63] += carry;
   /* each e[i] is between -8 and 8 */

   auto h = Ed25519_Point_Extended::identity();
   for(size_t i = 1; i < 64; i += 2) {
      h = Ed25519_Point_Extended::from(h + select(B_precomp[i / 2], e[i]));
   }

   auto s = Ed25519_Point_Projective::from(h.dbl());
   s = Ed25519_Point_Projective::from(s.dbl());
   s = Ed25519_Point_Projective::from(s.dbl());
   h = Ed25519_Point_Extended::from(s.dbl());

   for(size_t i = 0; i != 64; i += 2) {
      h = Ed25519_Point_Extended::from(h + select(B_precomp[i / 2], e[i]));
   }

   h.serialize_to(out);

   CT::unpoison(a, 32);
   CT::unpoison(out);
}

bool signature_check(std::span<const uint8_t, 32> pk, const uint8_t h[32], const uint8_t r[32], const uint8_t s[32]) {
   if(auto A = frombytes_negate_vartime(pk)) {
      std::array<uint8_t, 32> rcheck{};
      ge_double_scalarmult_vartime(rcheck, h, *A, s);
      return CT::is_equal(rcheck.data(), r, 32).as_bool();
   }
   return false;
}

}  // namespace Botan
/*
* Ed25519
* (C) 2017 Ribose Inc
*
* Based on the public domain code from SUPERCOP ref10 by
* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


namespace Botan {

/*
Input:
  a[0]+256*a[1]+...+256^31*a[31] = a
  b[0]+256*b[1]+...+256^31*b[31] = b
  c[0]+256*c[1]+...+256^31*c[31] = c

Output:
  s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
  where l = 2^252 + 27742317777372353535851937790883648493.
*/

void sc_muladd(uint8_t* s, const uint8_t* a, const uint8_t* b, const uint8_t* c) {
   const int32_t MASK = 0x1fffff;

   const int64_t a0 = MASK & load_3(a);
   const int64_t a1 = MASK & (load_4(a + 2) >> 5);
   const int64_t a2 = MASK & (load_3(a + 5) >> 2);
   const int64_t a3 = MASK & (load_4(a + 7) >> 7);
   const int64_t a4 = MASK & (load_4(a + 10) >> 4);
   const int64_t a5 = MASK & (load_3(a + 13) >> 1);
   const int64_t a6 = MASK & (load_4(a + 15) >> 6);
   const int64_t a7 = MASK & (load_3(a + 18) >> 3);
   const int64_t a8 = MASK & load_3(a + 21);
   const int64_t a9 = MASK & (load_4(a + 23) >> 5);
   const int64_t a10 = MASK & (load_3(a + 26) >> 2);
   const int64_t a11 = (load_4(a + 28) >> 7);
   const int64_t b0 = MASK & load_3(b);
   const int64_t b1 = MASK & (load_4(b + 2) >> 5);
   const int64_t b2 = MASK & (load_3(b + 5) >> 2);
   const int64_t b3 = MASK & (load_4(b + 7) >> 7);
   const int64_t b4 = MASK & (load_4(b + 10) >> 4);
   const int64_t b5 = MASK & (load_3(b + 13) >> 1);
   const int64_t b6 = MASK & (load_4(b + 15) >> 6);
   const int64_t b7 = MASK & (load_3(b + 18) >> 3);
   const int64_t b8 = MASK & load_3(b + 21);
   const int64_t b9 = MASK & (load_4(b + 23) >> 5);
   const int64_t b10 = MASK & (load_3(b + 26) >> 2);
   const int64_t b11 = (load_4(b + 28) >> 7);
   const int64_t c0 = MASK & load_3(c);
   const int64_t c1 = MASK & (load_4(c + 2) >> 5);
   const int64_t c2 = MASK & (load_3(c + 5) >> 2);
   const int64_t c3 = MASK & (load_4(c + 7) >> 7);
   const int64_t c4 = MASK & (load_4(c + 10) >> 4);
   const int64_t c5 = MASK & (load_3(c + 13) >> 1);
   const int64_t c6 = MASK & (load_4(c + 15) >> 6);
   const int64_t c7 = MASK & (load_3(c + 18) >> 3);
   const int64_t c8 = MASK & load_3(c + 21);
   const int64_t c9 = MASK & (load_4(c + 23) >> 5);
   const int64_t c10 = MASK & (load_3(c + 26) >> 2);
   const int64_t c11 = (load_4(c + 28) >> 7);

   int64_t s0 = c0 + a0 * b0;
   int64_t s1 = c1 + a0 * b1 + a1 * b0;
   int64_t s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
   int64_t s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
   int64_t s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
   int64_t s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
   int64_t s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
   int64_t s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;
   int64_t s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0;
   int64_t s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
   int64_t s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 +
                 a9 * b1 + a10 * b0;
   int64_t s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 +
                 a9 * b2 + a10 * b1 + a11 * b0;
   int64_t s12 =
      a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
   int64_t s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
   int64_t s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3;
   int64_t s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;
   int64_t s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
   int64_t s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
   int64_t s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
   int64_t s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
   int64_t s20 = a9 * b11 + a10 * b10 + a11 * b9;
   int64_t s21 = a10 * b11 + a11 * b10;
   int64_t s22 = a11 * b11;
   int64_t s23 = 0;

   carry<21>(s0, s1);
   carry<21>(s2, s3);
   carry<21>(s4, s5);
   carry<21>(s6, s7);
   carry<21>(s8, s9);
   carry<21>(s10, s11);
   carry<21>(s12, s13);
   carry<21>(s14, s15);
   carry<21>(s16, s17);
   carry<21>(s18, s19);
   carry<21>(s20, s21);
   carry<21>(s22, s23);

   carry<21>(s1, s2);
   carry<21>(s3, s4);
   carry<21>(s5, s6);
   carry<21>(s7, s8);
   carry<21>(s9, s10);
   carry<21>(s11, s12);
   carry<21>(s13, s14);
   carry<21>(s15, s16);
   carry<21>(s17, s18);
   carry<21>(s19, s20);
   carry<21>(s21, s22);

   redc_mul(s11, s12, s13, s14, s15, s16, s23);
   redc_mul(s10, s11, s12, s13, s14, s15, s22);
   redc_mul(s9, s10, s11, s12, s13, s14, s21);
   redc_mul(s8, s9, s10, s11, s12, s13, s20);
   redc_mul(s7, s8, s9, s10, s11, s12, s19);
   redc_mul(s6, s7, s8, s9, s10, s11, s18);

   carry<21>(s6, s7);
   carry<21>(s8, s9);
   carry<21>(s10, s11);
   carry<21>(s12, s13);
   carry<21>(s14, s15);
   carry<21>(s16, s17);

   carry<21>(s7, s8);
   carry<21>(s9, s10);
   carry<21>(s11, s12);
   carry<21>(s13, s14);
   carry<21>(s15, s16);

   redc_mul(s5, s6, s7, s8, s9, s10, s17);
   redc_mul(s4, s5, s6, s7, s8, s9, s16);
   redc_mul(s3, s4, s5, s6, s7, s8, s15);
   redc_mul(s2, s3, s4, s5, s6, s7, s14);
   redc_mul(s1, s2, s3, s4, s5, s6, s13);
   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry<21>(s0, s1);
   carry<21>(s2, s3);
   carry<21>(s4, s5);
   carry<21>(s6, s7);
   carry<21>(s8, s9);
   carry<21>(s10, s11);

   carry<21>(s1, s2);
   carry<21>(s3, s4);
   carry<21>(s5, s6);
   carry<21>(s7, s8);
   carry<21>(s9, s10);
   carry<21>(s11, s12);

   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry0<21>(s0, s1);
   carry0<21>(s1, s2);
   carry0<21>(s2, s3);
   carry0<21>(s3, s4);
   carry0<21>(s4, s5);
   carry0<21>(s5, s6);
   carry0<21>(s6, s7);
   carry0<21>(s7, s8);
   carry0<21>(s8, s9);
   carry0<21>(s9, s10);
   carry0<21>(s10, s11);
   carry0<21>(s11, s12);

   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry0<21>(s0, s1);
   carry0<21>(s1, s2);
   carry0<21>(s2, s3);
   carry0<21>(s3, s4);
   carry0<21>(s4, s5);
   carry0<21>(s5, s6);
   carry0<21>(s6, s7);
   carry0<21>(s7, s8);
   carry0<21>(s8, s9);
   carry0<21>(s9, s10);
   carry0<21>(s10, s11);

   s[0] = static_cast<uint8_t>(s0 >> 0);
   s[1] = static_cast<uint8_t>(s0 >> 8);
   s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
   s[3] = static_cast<uint8_t>(s1 >> 3);
   s[4] = static_cast<uint8_t>(s1 >> 11);
   s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
   s[6] = static_cast<uint8_t>(s2 >> 6);
   s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
   s[8] = static_cast<uint8_t>(s3 >> 1);
   s[9] = static_cast<uint8_t>(s3 >> 9);
   s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
   s[11] = static_cast<uint8_t>(s4 >> 4);
   s[12] = static_cast<uint8_t>(s4 >> 12);
   s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
   s[14] = static_cast<uint8_t>(s5 >> 7);
   s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
   s[16] = static_cast<uint8_t>(s6 >> 2);
   s[17] = static_cast<uint8_t>(s6 >> 10);
   s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
   s[19] = static_cast<uint8_t>(s7 >> 5);
   s[20] = static_cast<uint8_t>(s7 >> 13);
   s[21] = static_cast<uint8_t>(s8 >> 0);
   s[22] = static_cast<uint8_t>(s8 >> 8);
   s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
   s[24] = static_cast<uint8_t>(s9 >> 3);
   s[25] = static_cast<uint8_t>(s9 >> 11);
   s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
   s[27] = static_cast<uint8_t>(s10 >> 6);
   s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
   s[29] = static_cast<uint8_t>(s11 >> 1);
   s[30] = static_cast<uint8_t>(s11 >> 9);
   s[31] = static_cast<uint8_t>(s11 >> 17);
}

}  // namespace Botan
/*
* Ed25519
* (C) 2017 Ribose Inc
*
* Based on the public domain code from SUPERCOP ref10 by
* Peter Schwabe, Daniel J. Bernstein, Niels Duif, Tanja Lange, Bo-Yin Yang
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


namespace Botan {

/*
Input:
  s[0]+256*s[1]+...+256^63*s[63] = s

Output:
  s[0]+256*s[1]+...+256^31*s[31] = s mod l
  where l = 2^252 + 27742317777372353535851937790883648493.
  Overwrites s in place.
*/

void sc_reduce(uint8_t* s) {
   const uint32_t MASK = 0x1fffff;

   int64_t s0 = MASK & load_3(s);
   int64_t s1 = MASK & (load_4(s + 2) >> 5);
   int64_t s2 = MASK & (load_3(s + 5) >> 2);
   int64_t s3 = MASK & (load_4(s + 7) >> 7);
   int64_t s4 = MASK & (load_4(s + 10) >> 4);
   int64_t s5 = MASK & (load_3(s + 13) >> 1);
   int64_t s6 = MASK & (load_4(s + 15) >> 6);
   int64_t s7 = MASK & (load_3(s + 18) >> 3);
   int64_t s8 = MASK & load_3(s + 21);
   int64_t s9 = MASK & (load_4(s + 23) >> 5);
   int64_t s10 = MASK & (load_3(s + 26) >> 2);
   int64_t s11 = MASK & (load_4(s + 28) >> 7);
   int64_t s12 = MASK & (load_4(s + 31) >> 4);
   int64_t s13 = MASK & (load_3(s + 34) >> 1);
   int64_t s14 = MASK & (load_4(s + 36) >> 6);
   int64_t s15 = MASK & (load_3(s + 39) >> 3);
   int64_t s16 = MASK & load_3(s + 42);
   int64_t s17 = MASK & (load_4(s + 44) >> 5);
   int64_t s18 = MASK & (load_3(s + 47) >> 2);
   int64_t s19 = MASK & (load_4(s + 49) >> 7);
   int64_t s20 = MASK & (load_4(s + 52) >> 4);
   int64_t s21 = MASK & (load_3(s + 55) >> 1);
   int64_t s22 = MASK & (load_4(s + 57) >> 6);
   int64_t s23 = (load_4(s + 60) >> 3);

   redc_mul(s11, s12, s13, s14, s15, s16, s23);
   redc_mul(s10, s11, s12, s13, s14, s15, s22);
   redc_mul(s9, s10, s11, s12, s13, s14, s21);
   redc_mul(s8, s9, s10, s11, s12, s13, s20);
   redc_mul(s7, s8, s9, s10, s11, s12, s19);
   redc_mul(s6, s7, s8, s9, s10, s11, s18);

   carry<21>(s6, s7);
   carry<21>(s8, s9);
   carry<21>(s10, s11);
   carry<21>(s12, s13);
   carry<21>(s14, s15);
   carry<21>(s16, s17);

   carry<21>(s7, s8);
   carry<21>(s9, s10);
   carry<21>(s11, s12);
   carry<21>(s13, s14);
   carry<21>(s15, s16);

   redc_mul(s5, s6, s7, s8, s9, s10, s17);
   redc_mul(s4, s5, s6, s7, s8, s9, s16);
   redc_mul(s3, s4, s5, s6, s7, s8, s15);
   redc_mul(s2, s3, s4, s5, s6, s7, s14);
   redc_mul(s1, s2, s3, s4, s5, s6, s13);
   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry<21>(s0, s1);
   carry<21>(s2, s3);
   carry<21>(s4, s5);
   carry<21>(s6, s7);
   carry<21>(s8, s9);
   carry<21>(s10, s11);

   carry<21>(s1, s2);
   carry<21>(s3, s4);
   carry<21>(s5, s6);
   carry<21>(s7, s8);
   carry<21>(s9, s10);
   carry<21>(s11, s12);

   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry0<21>(s0, s1);
   carry0<21>(s1, s2);
   carry0<21>(s2, s3);
   carry0<21>(s3, s4);
   carry0<21>(s4, s5);
   carry0<21>(s5, s6);
   carry0<21>(s6, s7);
   carry0<21>(s7, s8);
   carry0<21>(s8, s9);
   carry0<21>(s9, s10);
   carry0<21>(s10, s11);
   carry0<21>(s11, s12);

   redc_mul(s0, s1, s2, s3, s4, s5, s12);

   carry0<21>(s0, s1);
   carry0<21>(s1, s2);
   carry0<21>(s2, s3);
   carry0<21>(s3, s4);
   carry0<21>(s4, s5);
   carry0<21>(s5, s6);
   carry0<21>(s6, s7);
   carry0<21>(s7, s8);
   carry0<21>(s8, s9);
   carry0<21>(s9, s10);
   carry0<21>(s10, s11);
   carry0<21>(s11, s12);

   s[0] = static_cast<uint8_t>(s0 >> 0);
   s[1] = static_cast<uint8_t>(s0 >> 8);
   s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
   s[3] = static_cast<uint8_t>(s1 >> 3);
   s[4] = static_cast<uint8_t>(s1 >> 11);
   s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
   s[6] = static_cast<uint8_t>(s2 >> 6);
   s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
   s[8] = static_cast<uint8_t>(s3 >> 1);
   s[9] = static_cast<uint8_t>(s3 >> 9);
   s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
   s[11] = static_cast<uint8_t>(s4 >> 4);
   s[12] = static_cast<uint8_t>(s4 >> 12);
   s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
   s[14] = static_cast<uint8_t>(s5 >> 7);
   s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
   s[16] = static_cast<uint8_t>(s6 >> 2);
   s[17] = static_cast<uint8_t>(s6 >> 10);
   s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
   s[19] = static_cast<uint8_t>(s7 >> 5);
   s[20] = static_cast<uint8_t>(s7 >> 13);
   s[21] = static_cast<uint8_t>(s8 >> 0);
   s[22] = static_cast<uint8_t>(s8 >> 8);
   s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
   s[24] = static_cast<uint8_t>(s9 >> 3);
   s[25] = static_cast<uint8_t>(s9 >> 11);
   s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
   s[27] = static_cast<uint8_t>(s10 >> 6);
   s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
   s[29] = static_cast<uint8_t>(s11 >> 1);
   s[30] = static_cast<uint8_t>(s11 >> 9);
   s[31] = static_cast<uint8_t>(s11 >> 17);
}

}  // namespace Botan
/*
* Hash Functions
* (C) 2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_ADLER32)
#endif

#if defined(BOTAN_HAS_CRC24)
#endif

#if defined(BOTAN_HAS_CRC32)
#endif

#if defined(BOTAN_HAS_GOST_34_11)
#endif

#if defined(BOTAN_HAS_KECCAK)
#endif

#if defined(BOTAN_HAS_MD4)
#endif

#if defined(BOTAN_HAS_MD5)
#endif

#if defined(BOTAN_HAS_RIPEMD_160)
#endif

#if defined(BOTAN_HAS_SHA1)
#endif

#if defined(BOTAN_HAS_SHA2_32)
#endif

#if defined(BOTAN_HAS_SHA2_64)
#endif

#if defined(BOTAN_HAS_SHA3)
#endif

#if defined(BOTAN_HAS_SHAKE)
#endif

#if defined(BOTAN_HAS_SKEIN_512)
#endif

#if defined(BOTAN_HAS_STREEBOG)
#endif

#if defined(BOTAN_HAS_SM3)
#endif

#if defined(BOTAN_HAS_WHIRLPOOL)
#endif

#if defined(BOTAN_HAS_PARALLEL_HASH)
#endif

#if defined(BOTAN_HAS_TRUNCATED_HASH)
#endif

#if defined(BOTAN_HAS_COMB4P)
#endif

#if defined(BOTAN_HAS_BLAKE2B)
#endif

#if defined(BOTAN_HAS_BLAKE2S)
#endif

#if defined(BOTAN_HAS_COMMONCRYPTO)
#endif

namespace Botan {

std::unique_ptr<HashFunction> HashFunction::create(std::string_view algo_spec, std::string_view provider) {
#if defined(BOTAN_HAS_COMMONCRYPTO)
   if(provider.empty() || provider == "commoncrypto") {
      if(auto hash = make_commoncrypto_hash(algo_spec))
         return hash;

      if(!provider.empty())
         return nullptr;
   }
#endif

   if(provider.empty() == false && provider != "base") {
      return nullptr;  // unknown provider
   }

#if defined(BOTAN_HAS_SHA1)
   if(algo_spec == "SHA-1") {
      return std::make_unique<SHA_1>();
   }
#endif

#if defined(BOTAN_HAS_SHA2_32)
   if(algo_spec == "SHA-224") {
      return std::make_unique<SHA_224>();
   }

   if(algo_spec == "SHA-256") {
      return std::make_unique<SHA_256>();
   }
#endif

#if defined(BOTAN_HAS_SHA2_64)
   if(algo_spec == "SHA-384") {
      return std::make_unique<SHA_384>();
   }

   if(algo_spec == "SHA-512") {
      return std::make_unique<SHA_512>();
   }

   if(algo_spec == "SHA-512-256") {
      return std::make_unique<SHA_512_256>();
   }
#endif

#if defined(BOTAN_HAS_RIPEMD_160)
   if(algo_spec == "RIPEMD-160") {
      return std::make_unique<RIPEMD_160>();
   }
#endif

#if defined(BOTAN_HAS_WHIRLPOOL)
   if(algo_spec == "Whirlpool") {
      return std::make_unique<Whirlpool>();
   }
#endif

#if defined(BOTAN_HAS_MD5)
   if(algo_spec == "MD5") {
      return std::make_unique<MD5>();
   }
#endif

#if defined(BOTAN_HAS_MD4)
   if(algo_spec == "MD4") {
      return std::make_unique<MD4>();
   }
#endif

#if defined(BOTAN_HAS_GOST_34_11)
   if(algo_spec == "GOST-R-34.11-94" || algo_spec == "GOST-34.11") {
      return std::make_unique<GOST_34_11>();
   }
#endif

#if defined(BOTAN_HAS_ADLER32)
   if(algo_spec == "Adler32") {
      return std::make_unique<Adler32>();
   }
#endif

#if defined(BOTAN_HAS_CRC24)
   if(algo_spec == "CRC24") {
      return std::make_unique<CRC24>();
   }
#endif

#if defined(BOTAN_HAS_CRC32)
   if(algo_spec == "CRC32") {
      return std::make_unique<CRC32>();
   }
#endif

#if defined(BOTAN_HAS_STREEBOG)
   if(algo_spec == "Streebog-256") {
      return std::make_unique<Streebog>(256);
   }
   if(algo_spec == "Streebog-512") {
      return std::make_unique<Streebog>(512);
   }
#endif

#if defined(BOTAN_HAS_SM3)
   if(algo_spec == "SM3") {
      return std::make_unique<SM3>();
   }
#endif

   const SCAN_Name req(algo_spec);

#if defined(BOTAN_HAS_SKEIN_512)
   if(req.algo_name() == "Skein-512") {
      return std::make_unique<Skein_512>(req.arg_as_integer(0, 512), req.arg(1, ""));
   }
#endif

#if defined(BOTAN_HAS_BLAKE2B)
   if(req.algo_name() == "Blake2b" || req.algo_name() == "BLAKE2b") {
      return std::make_unique<BLAKE2b>(req.arg_as_integer(0, 512));
   }
#endif

#if defined(BOTAN_HAS_BLAKE2S)
   if(req.algo_name() == "Blake2s" || req.algo_name() == "BLAKE2s") {
      return std::make_unique<BLAKE2s>(req.arg_as_integer(0, 256));
   }
#endif

#if defined(BOTAN_HAS_KECCAK)
   if(req.algo_name() == "Keccak-1600") {
      return std::make_unique<Keccak_1600>(req.arg_as_integer(0, 512));
   }
#endif

#if defined(BOTAN_HAS_SHA3)
   if(req.algo_name() == "SHA-3") {
      return std::make_unique<SHA_3>(req.arg_as_integer(0, 512));
   }
#endif

#if defined(BOTAN_HAS_SHAKE)
   if(req.algo_name() == "SHAKE-128" && req.arg_count() == 1) {
      return std::make_unique<SHAKE_128>(req.arg_as_integer(0));
   }
   if(req.algo_name() == "SHAKE-256" && req.arg_count() == 1) {
      return std::make_unique<SHAKE_256>(req.arg_as_integer(0));
   }
#endif

#if defined(BOTAN_HAS_PARALLEL_HASH)
   if(req.algo_name() == "Parallel") {
      std::vector<std::unique_ptr<HashFunction>> hashes;

      for(size_t i = 0; i != req.arg_count(); ++i) {
         auto h = HashFunction::create(req.arg(i));
         if(!h) {
            return nullptr;
         }
         hashes.push_back(std::move(h));
      }

      return std::make_unique<Parallel>(hashes);
   }
#endif

#if defined(BOTAN_HAS_TRUNCATED_HASH)
   if(req.algo_name() == "Truncated" && req.arg_count() == 2) {
      auto hash = HashFunction::create(req.arg(0));
      if(!hash) {
         return nullptr;
      }

      return std::make_unique<Truncated_Hash>(std::move(hash), req.arg_as_integer(1));
   }
#endif

#if defined(BOTAN_HAS_COMB4P)
   if(req.algo_name() == "Comb4P" && req.arg_count() == 2) {
      auto h1 = HashFunction::create(req.arg(0));
      auto h2 = HashFunction::create(req.arg(1));

      if(h1 && h2) {
         return std::make_unique<Comb4P>(std::move(h1), std::move(h2));
      }
   }
#endif

   return nullptr;
}

//static
std::unique_ptr<HashFunction> HashFunction::create_or_throw(std::string_view algo, std::string_view provider) {
   if(auto hash = HashFunction::create(algo, provider)) {
      return hash;
   }
   throw Lookup_Error("Hash", algo, provider);
}

std::vector<std::string> HashFunction::providers(std::string_view algo_spec) {
   return probe_providers_of<HashFunction>(algo_spec, {"base", "commoncrypto"});
}

}  // namespace Botan
/*
* Hex Encoding and Decoding
* (C) 2010,2020 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

uint16_t hex_encode_2nibble(uint8_t n8, bool uppercase) {
   // Offset for upper or lower case 'a' resp
   const uint16_t a_mask = uppercase ? 0x0707 : 0x2727;

   const uint16_t n = (static_cast<uint16_t>(n8 & 0xF0) << 4) | (n8 & 0x0F);
   // n >= 10? If so add offset
   const uint16_t diff = swar_lt<uint16_t>(0x0909, n) & a_mask;
   // Can't overflow between bytes, so don't need explicit SWAR addition:
   return n + 0x3030 + diff;
}

}  // namespace

void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase) {
   for(size_t i = 0; i != input_length; ++i) {
      const uint16_t h = hex_encode_2nibble(input[i], uppercase);
      output[2 * i] = get_byte<0>(h);
      output[2 * i + 1] = get_byte<1>(h);
   }
}

std::string hex_encode(const uint8_t input[], size_t input_length, bool uppercase) {
   std::string output(2 * input_length, 0);

   if(input_length > 0) {
      hex_encode(&output.front(), input, input_length, uppercase);
   }

   return output;
}

namespace {

uint8_t hex_char_to_bin(char input) {
   // Starts of valid value ranges (v_lo) and their lengths (v_range)
   constexpr uint64_t v_lo = make_uint64(0, '0', 'a', 'A', ' ', '\n', '\t', '\r');
   constexpr uint64_t v_range = make_uint64(0, 10, 6, 6, 1, 1, 1, 1);

   const uint8_t x = static_cast<uint8_t>(input);
   const uint64_t x8 = x * 0x0101010101010101;

   const uint64_t v_mask = swar_in_range<uint64_t>(x8, v_lo, v_range) ^ 0x8000000000000000;

   // This is the offset added to x to get the value we need
   const uint64_t val_v = 0xd0a9c960767773 ^ static_cast<uint64_t>(0xFF - x) << 56;

   return x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));
}

}  // namespace

size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t& input_consumed, bool ignore_ws) {
   uint8_t* out_ptr = output;
   bool top_nibble = true;

   clear_mem(output, input_length / 2);

   for(size_t i = 0; i != input_length; ++i) {
      const uint8_t bin = hex_char_to_bin(input[i]);

      if(bin >= 0x10) {
         if(bin == 0x80 && ignore_ws) {
            continue;
         }

         throw Invalid_Argument(fmt("hex_decode: invalid character '{}'", format_char_for_display(input[i])));
      }

      if(top_nibble) {
         *out_ptr |= bin << 4;
      } else {
         *out_ptr |= bin;
      }

      top_nibble = !top_nibble;
      if(top_nibble) {
         ++out_ptr;
      }
   }

   input_consumed = input_length;
   size_t written = (out_ptr - output);

   /*
   * We only got half of a uint8_t at the end; zap the half-written
   * output and mark it as unread
   */
   if(!top_nibble) {
      *out_ptr = 0;
      input_consumed -= 1;
   }

   return written;
}

size_t hex_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
   size_t consumed = 0;
   size_t written = hex_decode(output, input, input_length, consumed, ignore_ws);

   if(consumed != input_length) {
      throw Invalid_Argument("hex_decode: input did not have full bytes");
   }

   return written;
}

size_t hex_decode(uint8_t output[], std::string_view input, bool ignore_ws) {
   return hex_decode(output, input.data(), input.length(), ignore_ws);
}

size_t hex_decode(std::span<uint8_t> output, std::string_view input, bool ignore_ws) {
   return hex_decode(output.data(), input.data(), input.length(), ignore_ws);
}

secure_vector<uint8_t> hex_decode_locked(const char input[], size_t input_length, bool ignore_ws) {
   secure_vector<uint8_t> bin(1 + input_length / 2);

   size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);

   bin.resize(written);
   return bin;
}

secure_vector<uint8_t> hex_decode_locked(std::string_view input, bool ignore_ws) {
   return hex_decode_locked(input.data(), input.size(), ignore_ws);
}

std::vector<uint8_t> hex_decode(const char input[], size_t input_length, bool ignore_ws) {
   std::vector<uint8_t> bin(1 + input_length / 2);

   size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);

   bin.resize(written);
   return bin;
}

std::vector<uint8_t> hex_decode(std::string_view input, bool ignore_ws) {
   return hex_decode(input.data(), input.size(), ignore_ws);
}

}  // namespace Botan
/*
* HMAC
* (C) 1999-2007,2014,2020 Jack Lloyd
*     2007 Yves Jerschow
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Update a HMAC Calculation
*/
void HMAC::add_data(std::span<const uint8_t> input) {
   assert_key_material_set();
   m_hash->update(input);
}

/*
* Finalize a HMAC Calculation
*/
void HMAC::final_result(std::span<uint8_t> mac) {
   assert_key_material_set();
   m_hash->final(mac);
   m_hash->update(m_okey);
   m_hash->update(mac.first(m_hash_output_length));
   m_hash->final(mac);
   m_hash->update(m_ikey);
}

Key_Length_Specification HMAC::key_spec() const {
   // Support very long lengths for things like PBKDF2 and the TLS PRF
   return Key_Length_Specification(0, 4096);
}

size_t HMAC::output_length() const {
   return m_hash_output_length;
}

bool HMAC::has_keying_material() const {
   return !m_okey.empty();
}

/*
* HMAC Key Schedule
*/
void HMAC::key_schedule(std::span<const uint8_t> key) {
   const uint8_t ipad = 0x36;
   const uint8_t opad = 0x5C;

   m_hash->clear();

   m_ikey.resize(m_hash_block_size);
   m_okey.resize(m_hash_block_size);

   clear_mem(m_ikey.data(), m_ikey.size());
   clear_mem(m_okey.data(), m_okey.size());

   /*
   * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it
   * reveals the length of the passphrase. Make some attempt to hide this to
   * side channels. Clearly if the secret is longer than the block size then the
   * branch to hash first reveals that. In addition, counting the number of
   * compression functions executed reveals the size at the granularity of the
   * hash function's block size.
   *
   * The greater concern is for smaller keys; being able to detect when a
   * passphrase is say 4 bytes may assist choosing weaker targets. Even though
   * the loop bounds are constant, we can only actually read key[0..length] so
   * it doesn't seem possible to make this computation truly constant time.
   *
   * We don't mind leaking if the length is exactly zero since that's
   * trivial to simply check.
   */

   if(key.size() > m_hash_block_size) {
      m_hash->update(key);
      m_hash->final(m_ikey.data());
   } else if(key.size() >= 20) {
      // For long keys we just leak the length either it is a cryptovariable
      // or a long enough password that just the length is not a useful signal
      copy_mem(std::span{m_ikey}.first(key.size()), key);
   } else if(!key.empty()) {
      for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i) {
         /*
         access key[i % length] but avoiding division due to variable
         time computation on some processors.
         */
         auto needs_reduction = CT::Mask<size_t>::is_lte(key.size(), i_mod_length);
         i_mod_length = needs_reduction.select(0, i_mod_length);
         const uint8_t kb = key[i_mod_length];

         auto in_range = CT::Mask<size_t>::is_lt(i, key.size());
         m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb));
         i_mod_length += 1;
      }
   }

   for(size_t i = 0; i != m_hash_block_size; ++i) {
      m_ikey[i] ^= ipad;
      m_okey[i] = m_ikey[i] ^ ipad ^ opad;
   }

   m_hash->update(m_ikey);
}

/*
* Clear memory of sensitive data
*/
void HMAC::clear() {
   m_hash->clear();
   zap(m_ikey);
   zap(m_okey);
}

/*
* Return the name of this type
*/
std::string HMAC::name() const {
   return fmt("HMAC({})", m_hash->name());
}

/*
* Return a new_object of this object
*/
std::unique_ptr<MessageAuthenticationCode> HMAC::new_object() const {
   return std::make_unique<HMAC>(m_hash->new_object());
}

/*
* HMAC Constructor
*/
HMAC::HMAC(std::unique_ptr<HashFunction> hash) :
      m_hash(std::move(hash)),
      m_hash_output_length(m_hash->output_length()),
      m_hash_block_size(m_hash->hash_block_size()) {
   BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length, "HMAC is not compatible with this hash function");
}

}  // namespace Botan
/*
* KDF Retrieval
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_HKDF)
#endif

#if defined(BOTAN_HAS_KDF1)
#endif

#if defined(BOTAN_HAS_KDF2)
#endif

#if defined(BOTAN_HAS_KDF1_18033)
#endif

#if defined(BOTAN_HAS_TLS_V12_PRF)
#endif

#if defined(BOTAN_HAS_X942_PRF)
#endif

#if defined(BOTAN_HAS_SP800_108)
#endif

#if defined(BOTAN_HAS_SP800_56A)
#endif

#if defined(BOTAN_HAS_SP800_56C)
#endif

namespace Botan {

namespace {

template <typename KDF_Type, typename... ParamTs>
std::unique_ptr<KDF> kdf_create_mac_or_hash(std::string_view nm, ParamTs&&... params) {
   if(auto mac = MessageAuthenticationCode::create(fmt("HMAC({})", nm))) {
      return std::make_unique<KDF_Type>(std::move(mac), std::forward<ParamTs>(params)...);
   }

   if(auto mac = MessageAuthenticationCode::create(nm)) {
      return std::make_unique<KDF_Type>(std::move(mac), std::forward<ParamTs>(params)...);
   }

   return nullptr;
}

}  // namespace

std::unique_ptr<KDF> KDF::create(std::string_view algo_spec, std::string_view provider) {
   const SCAN_Name req(algo_spec);

#if defined(BOTAN_HAS_HKDF)
   if(req.algo_name() == "HKDF" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<HKDF>(req.arg(0));
      }
   }

   if(req.algo_name() == "HKDF-Extract" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<HKDF_Extract>(req.arg(0));
      }
   }

   if(req.algo_name() == "HKDF-Expand" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<HKDF_Expand>(req.arg(0));
      }
   }
#endif

#if defined(BOTAN_HAS_KDF2)
   if(req.algo_name() == "KDF2" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<KDF2>(std::move(hash));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_KDF1_18033)
   if(req.algo_name() == "KDF1-18033" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<KDF1_18033>(std::move(hash));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_KDF1)
   if(req.algo_name() == "KDF1" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<KDF1>(std::move(hash));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_TLS_V12_PRF)
   if(req.algo_name() == "TLS-12-PRF" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<TLS_12_PRF>(req.arg(0));
      }
   }
#endif

#if defined(BOTAN_HAS_X942_PRF)
   if(req.algo_name() == "X9.42-PRF" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         return std::make_unique<X942_PRF>(req.arg(0));
      }
   }
#endif

#if defined(BOTAN_HAS_SP800_108)
   if(req.algo_name() == "SP800-108-Counter" && req.arg_count_between(1, 3)) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<SP800_108_Counter>(
            req.arg(0), req.arg_as_integer(1, 32), req.arg_as_integer(2, 32));
      }
   }

   if(req.algo_name() == "SP800-108-Feedback" && req.arg_count_between(1, 3)) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<SP800_108_Feedback>(
            req.arg(0), req.arg_as_integer(1, 32), req.arg_as_integer(2, 32));
      }
   }

   if(req.algo_name() == "SP800-108-Pipeline" && req.arg_count_between(1, 3)) {
      if(provider.empty() || provider == "base") {
         return kdf_create_mac_or_hash<SP800_108_Pipeline>(
            req.arg(0), req.arg_as_integer(1, 32), req.arg_as_integer(2, 32));
      }
   }
#endif

#if defined(BOTAN_HAS_SP800_56A)
   if(req.algo_name() == "SP800-56A" && req.arg_count() == 1) {
      if(auto hash = HashFunction::create(req.arg(0))) {
         return std::make_unique<SP800_56C_One_Step_Hash>(std::move(hash));
      }
      if(req.arg(0) == "KMAC-128") {
         return std::make_unique<SP800_56C_One_Step_KMAC128>();
      }
      if(req.arg(0) == "KMAC-256") {
         return std::make_unique<SP800_56C_One_Step_KMAC256>();
      }
      if(auto mac = MessageAuthenticationCode::create(req.arg(0))) {
         return std::make_unique<SP800_56C_One_Step_HMAC>(std::move(mac));
      }
   }
#endif

#if defined(BOTAN_HAS_SP800_56C)
   if(req.algo_name() == "SP800-56C" && req.arg_count() == 1) {
      std::unique_ptr<KDF> exp(kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0), 32, 32));
      if(exp) {
         if(auto mac = MessageAuthenticationCode::create(req.arg(0))) {
            return std::make_unique<SP800_56C_Two_Step>(std::move(mac), std::move(exp));
         }

         if(auto mac = MessageAuthenticationCode::create(fmt("HMAC({})", req.arg(0)))) {
            return std::make_unique<SP800_56C_Two_Step>(std::move(mac), std::move(exp));
         }
      }
   }
#endif

   BOTAN_UNUSED(req);
   BOTAN_UNUSED(provider);

   return nullptr;
}

//static
std::unique_ptr<KDF> KDF::create_or_throw(std::string_view algo, std::string_view provider) {
   if(auto kdf = KDF::create(algo, provider)) {
      return kdf;
   }
   throw Lookup_Error("KDF", algo, provider);
}

std::vector<std::string> KDF::providers(std::string_view algo_spec) {
   return probe_providers_of<KDF>(algo_spec);
}

//static
std::span<const uint8_t> KDF::_as_span(std::string_view s) {
   return as_span_of_bytes(s);
}

}  // namespace Botan
/*
* Keccak
* (C) 2010,2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

std::unique_ptr<HashFunction> Keccak_1600::copy_state() const {
   return std::make_unique<Keccak_1600>(*this);
}

Keccak_1600::Keccak_1600(size_t output_bits) : m_keccak(2 * output_bits, 0, 0), m_output_length(output_bits / 8) {
   // We only support the parameters for the SHA-3 proposal

   if(output_bits != 224 && output_bits != 256 && output_bits != 384 && output_bits != 512) {
      throw Invalid_Argument(fmt("Keccak_1600: Invalid output length {}", output_bits));
   }
}

std::string Keccak_1600::name() const {
   return fmt("Keccak-1600({})", m_output_length * 8);
}

std::unique_ptr<HashFunction> Keccak_1600::new_object() const {
   return std::make_unique<Keccak_1600>(m_output_length * 8);
}

void Keccak_1600::clear() {
   m_keccak.clear();
}

std::string Keccak_1600::provider() const {
   return m_keccak.provider();
}

void Keccak_1600::add_data(std::span<const uint8_t> input) {
   m_keccak.absorb(input);
}

void Keccak_1600::final_result(std::span<uint8_t> output) {
   m_keccak.finish();
   m_keccak.squeeze(output);
   clear();
}

}  // namespace Botan
/*
 * Helper functions to implement Keccak-derived functions from NIST SP.800-185
 * (C) 2023 Jack Lloyd
 * (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */



#include <limits>

namespace Botan {

namespace {

size_t int_encoding_size(uint64_t x) {
   BOTAN_ASSERT_NOMSG(x < std::numeric_limits<uint64_t>::max());
   return ceil_tobytes(std::max(uint8_t(1), ceil_log2(x + 1)));
}

uint8_t encode(std::span<uint8_t> out, uint64_t x) {
   const auto bytes_needed = int_encoding_size(x);
   BOTAN_ASSERT_NOMSG(sizeof(x) >= bytes_needed);
   BOTAN_ASSERT_NOMSG(out.size() >= bytes_needed);

   const size_t leading_zeros = sizeof(x) - bytes_needed;

   std::array<uint8_t, sizeof(x)> bigendian_x{};
   store_be(x, bigendian_x.data());

   std::copy(bigendian_x.begin() + leading_zeros, bigendian_x.end(), out.begin());

   return static_cast<uint8_t>(bytes_needed);
}

}  // namespace

std::span<const uint8_t> keccak_int_left_encode(std::span<uint8_t> out, size_t x) {
   BOTAN_ASSERT_NOMSG(!out.empty());
   out[0] = encode(out.last(out.size() - 1), x);
   return out.first(out[0] + 1 /* the length tag */);
}

std::span<const uint8_t> keccak_int_right_encode(std::span<uint8_t> out, size_t x) {
   const auto bytes_needed = encode(out, x);
   BOTAN_ASSERT_NOMSG(out.size() >= bytes_needed + size_t(1));
   out[bytes_needed] = bytes_needed;
   return out.first(bytes_needed + 1 /* the length tag */);
}

size_t keccak_int_encoding_size(size_t x) {
   return int_encoding_size(x) + 1 /* the length tag */;
}

}  // namespace Botan
/*
* Keccak Permutation
* (C) 2010,2016 Jack Lloyd
* (C) 2023 Falko Strenzke
* (C) 2023 René Meusel - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_CPUID)
#endif

namespace Botan {

Keccak_Permutation::Keccak_Permutation(size_t capacity, uint64_t custom_padding, uint8_t custom_padding_bit_len) :
      m_capacity(capacity),
      m_byterate((1600 - capacity) / 8),
      m_custom_padding(custom_padding),
      m_custom_padding_bit_len(custom_padding_bit_len),
      m_S(25),  // 1600 bit
      m_S_inpos(0),
      m_S_outpos(0) {
   BOTAN_ARG_CHECK(capacity % 64 == 0, "capacity must be a multiple of 64");
}

std::string Keccak_Permutation::provider() const {
#if defined(BOTAN_HAS_KECCAK_PERM_BMI2)
   if(auto feat = CPUID::check(CPUID::Feature::BMI)) {
      return *feat;
   }
#endif

   return "base";
}

void Keccak_Permutation::clear() {
   zeroise(m_S);
   m_S_inpos = 0;
   m_S_outpos = 0;
}

void Keccak_Permutation::absorb(std::span<const uint8_t> input) {
   BufferSlicer input_slicer(input);

   // Block-wise incorporation of the input data into the sponge state until
   // all input bytes are processed
   while(!input_slicer.empty()) {
      const size_t to_take_this_round = std::min(input_slicer.remaining(), m_byterate - m_S_inpos);
      BufferSlicer input_this_round(input_slicer.take(to_take_this_round));

      // If necessary, try to get aligned with the sponge state's 64-bit integer array
      for(; !input_this_round.empty() && m_S_inpos % 8 > 0; ++m_S_inpos) {
         m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(input_this_round.take_byte()) << (8 * (m_S_inpos % 8));
      }

      // Process as many aligned 64-bit integer values as possible
      for(; input_this_round.remaining() >= 8; m_S_inpos += 8) {
         m_S[m_S_inpos / 8] ^= load_le<uint64_t>(input_this_round.take(8).data(), 0);
      }

      // Read remaining output data, causing misalignment, if necessary
      for(; !input_this_round.empty(); ++m_S_inpos) {
         m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(input_this_round.take_byte()) << (8 * (m_S_inpos % 8));
      }

      // We reached the end of a sponge state block... permute() and start over
      if(m_S_inpos == m_byterate) {
         permute();
         m_S_inpos = 0;
      }
   }
}

void Keccak_Permutation::squeeze(std::span<uint8_t> output) {
   BufferStuffer output_stuffer(output);

   // Block-wise readout of the sponge state until enough bytes
   // were filled into the output buffer
   while(!output_stuffer.full()) {
      const size_t bytes_in_this_round = std::min(output_stuffer.remaining_capacity(), m_byterate - m_S_outpos);
      BufferStuffer output_this_round(output_stuffer.next(bytes_in_this_round));

      // If necessary, try to get aligned with the sponge state's 64-bit integer array
      for(; !output_this_round.full() && m_S_outpos % 8 != 0; ++m_S_outpos) {
         output_this_round.next_byte() = static_cast<uint8_t>(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8)));
      }

      // Read out as many aligned 64-bit integer values as possible
      for(; output_this_round.remaining_capacity() >= 8; m_S_outpos += 8) {
         store_le(m_S[m_S_outpos / 8], output_this_round.next(8).data());
      }

      // Read remaining output data, causing misalignment, if necessary
      for(; !output_this_round.full(); ++m_S_outpos) {
         output_this_round.next_byte() = static_cast<uint8_t>(m_S[m_S_outpos / 8] >> (8 * (m_S_outpos % 8)));
      }

      // We reached the end of a sponge state block... permute() and start over
      if(m_S_outpos == m_byterate) {
         permute();
         m_S_outpos = 0;
      }
   }
}

void Keccak_Permutation::finish() {
   // append the first bit of the final padding after the custom padding
   uint8_t init_pad = static_cast<uint8_t>(m_custom_padding | uint64_t(1) << m_custom_padding_bit_len);
   m_S[m_S_inpos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (m_S_inpos % 8));

   // final bit of the padding of the last block
   m_S[(m_byterate / 8) - 1] ^= static_cast<uint64_t>(0x80) << 56;

   permute();
}

void Keccak_Permutation::permute() {
#if defined(BOTAN_HAS_KECCAK_PERM_BMI2)
   if(CPUID::has(CPUID::Feature::BMI)) {
      return permute_bmi2();
   }
#endif

   static const uint64_t RC[24] = {0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
                                   0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
                                   0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
                                   0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
                                   0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
                                   0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008};

   uint64_t T[25];

   for(size_t i = 0; i != 24; i += 2) {
      Keccak_Permutation_round(T, m_S.data(), RC[i + 0]);
      Keccak_Permutation_round(m_S.data(), T, RC[i + 1]);
   }
}

}  // namespace Botan
/*
* Message Authentication Code base class
* (C) 1999-2008 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_CMAC)
#endif

#if defined(BOTAN_HAS_GMAC)
#endif

#if defined(BOTAN_HAS_HMAC)
#endif

#if defined(BOTAN_HAS_POLY1305)
#endif

#if defined(BOTAN_HAS_SIPHASH)
#endif

#if defined(BOTAN_HAS_ANSI_X919_MAC)
#endif

#if defined(BOTAN_HAS_BLAKE2BMAC)
#endif

#if defined(BOTAN_HAS_KMAC)
#endif

namespace Botan {

std::unique_ptr<MessageAuthenticationCode> MessageAuthenticationCode::create(std::string_view algo_spec,
                                                                             std::string_view provider) {
   const SCAN_Name req(algo_spec);

#if defined(BOTAN_HAS_BLAKE2BMAC)
   if(req.algo_name() == "Blake2b" || req.algo_name() == "BLAKE2b") {
      return std::make_unique<BLAKE2bMAC>(req.arg_as_integer(0, 512));
   }
#endif

#if defined(BOTAN_HAS_GMAC)
   if(req.algo_name() == "GMAC" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto bc = BlockCipher::create(req.arg(0))) {
            return std::make_unique<GMAC>(std::move(bc));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_HMAC)
   if(req.algo_name() == "HMAC" && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<HMAC>(std::move(hash));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_POLY1305)
   if(req.algo_name() == "Poly1305" && req.arg_count() == 0) {
      if(provider.empty() || provider == "base") {
         return std::make_unique<Poly1305>();
      }
   }
#endif

#if defined(BOTAN_HAS_SIPHASH)
   if(req.algo_name() == "SipHash") {
      if(provider.empty() || provider == "base") {
         return std::make_unique<SipHash>(req.arg_as_integer(0, 2), req.arg_as_integer(1, 4));
      }
   }
#endif

#if defined(BOTAN_HAS_CMAC)
   if((req.algo_name() == "CMAC" || req.algo_name() == "OMAC") && req.arg_count() == 1) {
      if(provider.empty() || provider == "base") {
         if(auto bc = BlockCipher::create(req.arg(0))) {
            return std::make_unique<CMAC>(std::move(bc));
         }
      }
   }
#endif

#if defined(BOTAN_HAS_ANSI_X919_MAC)
   if(req.algo_name() == "X9.19-MAC") {
      if(provider.empty() || provider == "base") {
         return std::make_unique<ANSI_X919_MAC>();
      }
   }
#endif

#if defined(BOTAN_HAS_KMAC)
   if(req.algo_name() == "KMAC-128") {
      if(provider.empty() || provider == "base") {
         if(req.arg_count() != 1) {
            throw Invalid_Argument(
               "invalid algorithm specification for KMAC-128: need exactly one argument for output bit length");
         }
         return std::make_unique<KMAC128>(req.arg_as_integer(0));
      }
   }

   if(req.algo_name() == "KMAC-256") {
      if(provider.empty() || provider == "base") {
         if(req.arg_count() != 1) {
            throw Invalid_Argument(
               "invalid algorithm specification for KMAC-256: need exactly one argument for output bit length");
         }
         return std::make_unique<KMAC256>(req.arg_as_integer(0));
      }
   }
#endif

   BOTAN_UNUSED(req);
   BOTAN_UNUSED(provider);

   return nullptr;
}

std::vector<std::string> MessageAuthenticationCode::providers(std::string_view algo_spec) {
   return probe_providers_of<MessageAuthenticationCode>(algo_spec);
}

//static
std::unique_ptr<MessageAuthenticationCode> MessageAuthenticationCode::create_or_throw(std::string_view algo,
                                                                                      std::string_view provider) {
   if(auto mac = MessageAuthenticationCode::create(algo, provider)) {
      return mac;
   }
   throw Lookup_Error("MAC", algo, provider);
}

void MessageAuthenticationCode::start_msg(std::span<const uint8_t> nonce) {
   BOTAN_UNUSED(nonce);
   if(!nonce.empty()) {
      throw Invalid_IV_Length(name(), nonce.size());
   }
}

/*
* Default (deterministic) MAC verification operation
*/
bool MessageAuthenticationCode::verify_mac_result(std::span<const uint8_t> mac) {
   secure_vector<uint8_t> our_mac = final();

   if(our_mac.size() != mac.size()) {
      return false;
   }

   return CT::is_equal(our_mac.data(), mac.data(), mac.size()).as_bool();
}

}  // namespace Botan
/*
* Comba Multiplication and Squaring
*
* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_comba.py on 2024-06-27
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


namespace Botan {

/*
* Comba 4x4 Squaring
*/
void bigint_comba_sqr4(word z[8], const word x[4]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   z[7] = accum.extract();
}

/*
* Comba 4x4 Multiplication
*/
void bigint_comba_mul4(word z[8], const word x[4], const word y[4]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   z[4] = accum.extract();
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   z[5] = accum.extract();
   accum.mul(x[3], y[3]);
   z[6] = accum.extract();
   z[7] = accum.extract();
}

/*
* Comba 6x6 Squaring
*/
void bigint_comba_sqr6(word z[12], const word x[6]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   z[11] = accum.extract();
}

/*
* Comba 6x6 Multiplication
*/
void bigint_comba_mul6(word z[12], const word x[6], const word y[6]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   z[6] = accum.extract();
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   z[7] = accum.extract();
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   z[8] = accum.extract();
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   z[9] = accum.extract();
   accum.mul(x[5], y[5]);
   z[10] = accum.extract();
   z[11] = accum.extract();
}

/*
* Comba 7x7 Squaring
*/
void bigint_comba_sqr7(word z[14], const word x[7]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[0], x[6]);
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[1], x[6]);
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[2], x[6]);
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[3], x[6]);
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul_x2(x[4], x[6]);
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   accum.mul_x2(x[5], x[6]);
   z[11] = accum.extract();
   accum.mul(x[6], x[6]);
   z[12] = accum.extract();
   z[13] = accum.extract();
}

/*
* Comba 7x7 Multiplication
*/
void bigint_comba_mul7(word z[14], const word x[7], const word y[7]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[0], y[6]);
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   accum.mul(x[6], y[0]);
   z[6] = accum.extract();
   accum.mul(x[1], y[6]);
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   accum.mul(x[6], y[1]);
   z[7] = accum.extract();
   accum.mul(x[2], y[6]);
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   accum.mul(x[6], y[2]);
   z[8] = accum.extract();
   accum.mul(x[3], y[6]);
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   accum.mul(x[6], y[3]);
   z[9] = accum.extract();
   accum.mul(x[4], y[6]);
   accum.mul(x[5], y[5]);
   accum.mul(x[6], y[4]);
   z[10] = accum.extract();
   accum.mul(x[5], y[6]);
   accum.mul(x[6], y[5]);
   z[11] = accum.extract();
   accum.mul(x[6], y[6]);
   z[12] = accum.extract();
   z[13] = accum.extract();
}

/*
* Comba 8x8 Squaring
*/
void bigint_comba_sqr8(word z[16], const word x[8]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[0], x[6]);
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[0], x[7]);
   accum.mul_x2(x[1], x[6]);
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[1], x[7]);
   accum.mul_x2(x[2], x[6]);
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[2], x[7]);
   accum.mul_x2(x[3], x[6]);
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul_x2(x[3], x[7]);
   accum.mul_x2(x[4], x[6]);
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   accum.mul_x2(x[4], x[7]);
   accum.mul_x2(x[5], x[6]);
   z[11] = accum.extract();
   accum.mul_x2(x[5], x[7]);
   accum.mul(x[6], x[6]);
   z[12] = accum.extract();
   accum.mul_x2(x[6], x[7]);
   z[13] = accum.extract();
   accum.mul(x[7], x[7]);
   z[14] = accum.extract();
   z[15] = accum.extract();
}

/*
* Comba 8x8 Multiplication
*/
void bigint_comba_mul8(word z[16], const word x[8], const word y[8]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[0], y[6]);
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   accum.mul(x[6], y[0]);
   z[6] = accum.extract();
   accum.mul(x[0], y[7]);
   accum.mul(x[1], y[6]);
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   accum.mul(x[6], y[1]);
   accum.mul(x[7], y[0]);
   z[7] = accum.extract();
   accum.mul(x[1], y[7]);
   accum.mul(x[2], y[6]);
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   accum.mul(x[6], y[2]);
   accum.mul(x[7], y[1]);
   z[8] = accum.extract();
   accum.mul(x[2], y[7]);
   accum.mul(x[3], y[6]);
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   accum.mul(x[6], y[3]);
   accum.mul(x[7], y[2]);
   z[9] = accum.extract();
   accum.mul(x[3], y[7]);
   accum.mul(x[4], y[6]);
   accum.mul(x[5], y[5]);
   accum.mul(x[6], y[4]);
   accum.mul(x[7], y[3]);
   z[10] = accum.extract();
   accum.mul(x[4], y[7]);
   accum.mul(x[5], y[6]);
   accum.mul(x[6], y[5]);
   accum.mul(x[7], y[4]);
   z[11] = accum.extract();
   accum.mul(x[5], y[7]);
   accum.mul(x[6], y[6]);
   accum.mul(x[7], y[5]);
   z[12] = accum.extract();
   accum.mul(x[6], y[7]);
   accum.mul(x[7], y[6]);
   z[13] = accum.extract();
   accum.mul(x[7], y[7]);
   z[14] = accum.extract();
   z[15] = accum.extract();
}

/*
* Comba 9x9 Squaring
*/
void bigint_comba_sqr9(word z[18], const word x[9]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[0], x[6]);
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[0], x[7]);
   accum.mul_x2(x[1], x[6]);
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[0], x[8]);
   accum.mul_x2(x[1], x[7]);
   accum.mul_x2(x[2], x[6]);
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[1], x[8]);
   accum.mul_x2(x[2], x[7]);
   accum.mul_x2(x[3], x[6]);
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul_x2(x[2], x[8]);
   accum.mul_x2(x[3], x[7]);
   accum.mul_x2(x[4], x[6]);
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   accum.mul_x2(x[3], x[8]);
   accum.mul_x2(x[4], x[7]);
   accum.mul_x2(x[5], x[6]);
   z[11] = accum.extract();
   accum.mul_x2(x[4], x[8]);
   accum.mul_x2(x[5], x[7]);
   accum.mul(x[6], x[6]);
   z[12] = accum.extract();
   accum.mul_x2(x[5], x[8]);
   accum.mul_x2(x[6], x[7]);
   z[13] = accum.extract();
   accum.mul_x2(x[6], x[8]);
   accum.mul(x[7], x[7]);
   z[14] = accum.extract();
   accum.mul_x2(x[7], x[8]);
   z[15] = accum.extract();
   accum.mul(x[8], x[8]);
   z[16] = accum.extract();
   z[17] = accum.extract();
}

/*
* Comba 9x9 Multiplication
*/
void bigint_comba_mul9(word z[18], const word x[9], const word y[9]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[0], y[6]);
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   accum.mul(x[6], y[0]);
   z[6] = accum.extract();
   accum.mul(x[0], y[7]);
   accum.mul(x[1], y[6]);
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   accum.mul(x[6], y[1]);
   accum.mul(x[7], y[0]);
   z[7] = accum.extract();
   accum.mul(x[0], y[8]);
   accum.mul(x[1], y[7]);
   accum.mul(x[2], y[6]);
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   accum.mul(x[6], y[2]);
   accum.mul(x[7], y[1]);
   accum.mul(x[8], y[0]);
   z[8] = accum.extract();
   accum.mul(x[1], y[8]);
   accum.mul(x[2], y[7]);
   accum.mul(x[3], y[6]);
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   accum.mul(x[6], y[3]);
   accum.mul(x[7], y[2]);
   accum.mul(x[8], y[1]);
   z[9] = accum.extract();
   accum.mul(x[2], y[8]);
   accum.mul(x[3], y[7]);
   accum.mul(x[4], y[6]);
   accum.mul(x[5], y[5]);
   accum.mul(x[6], y[4]);
   accum.mul(x[7], y[3]);
   accum.mul(x[8], y[2]);
   z[10] = accum.extract();
   accum.mul(x[3], y[8]);
   accum.mul(x[4], y[7]);
   accum.mul(x[5], y[6]);
   accum.mul(x[6], y[5]);
   accum.mul(x[7], y[4]);
   accum.mul(x[8], y[3]);
   z[11] = accum.extract();
   accum.mul(x[4], y[8]);
   accum.mul(x[5], y[7]);
   accum.mul(x[6], y[6]);
   accum.mul(x[7], y[5]);
   accum.mul(x[8], y[4]);
   z[12] = accum.extract();
   accum.mul(x[5], y[8]);
   accum.mul(x[6], y[7]);
   accum.mul(x[7], y[6]);
   accum.mul(x[8], y[5]);
   z[13] = accum.extract();
   accum.mul(x[6], y[8]);
   accum.mul(x[7], y[7]);
   accum.mul(x[8], y[6]);
   z[14] = accum.extract();
   accum.mul(x[7], y[8]);
   accum.mul(x[8], y[7]);
   z[15] = accum.extract();
   accum.mul(x[8], y[8]);
   z[16] = accum.extract();
   z[17] = accum.extract();
}

/*
* Comba 16x16 Squaring
*/
void bigint_comba_sqr16(word z[32], const word x[16]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[0], x[6]);
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[0], x[7]);
   accum.mul_x2(x[1], x[6]);
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[0], x[8]);
   accum.mul_x2(x[1], x[7]);
   accum.mul_x2(x[2], x[6]);
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[0], x[9]);
   accum.mul_x2(x[1], x[8]);
   accum.mul_x2(x[2], x[7]);
   accum.mul_x2(x[3], x[6]);
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul_x2(x[0], x[10]);
   accum.mul_x2(x[1], x[9]);
   accum.mul_x2(x[2], x[8]);
   accum.mul_x2(x[3], x[7]);
   accum.mul_x2(x[4], x[6]);
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   accum.mul_x2(x[0], x[11]);
   accum.mul_x2(x[1], x[10]);
   accum.mul_x2(x[2], x[9]);
   accum.mul_x2(x[3], x[8]);
   accum.mul_x2(x[4], x[7]);
   accum.mul_x2(x[5], x[6]);
   z[11] = accum.extract();
   accum.mul_x2(x[0], x[12]);
   accum.mul_x2(x[1], x[11]);
   accum.mul_x2(x[2], x[10]);
   accum.mul_x2(x[3], x[9]);
   accum.mul_x2(x[4], x[8]);
   accum.mul_x2(x[5], x[7]);
   accum.mul(x[6], x[6]);
   z[12] = accum.extract();
   accum.mul_x2(x[0], x[13]);
   accum.mul_x2(x[1], x[12]);
   accum.mul_x2(x[2], x[11]);
   accum.mul_x2(x[3], x[10]);
   accum.mul_x2(x[4], x[9]);
   accum.mul_x2(x[5], x[8]);
   accum.mul_x2(x[6], x[7]);
   z[13] = accum.extract();
   accum.mul_x2(x[0], x[14]);
   accum.mul_x2(x[1], x[13]);
   accum.mul_x2(x[2], x[12]);
   accum.mul_x2(x[3], x[11]);
   accum.mul_x2(x[4], x[10]);
   accum.mul_x2(x[5], x[9]);
   accum.mul_x2(x[6], x[8]);
   accum.mul(x[7], x[7]);
   z[14] = accum.extract();
   accum.mul_x2(x[0], x[15]);
   accum.mul_x2(x[1], x[14]);
   accum.mul_x2(x[2], x[13]);
   accum.mul_x2(x[3], x[12]);
   accum.mul_x2(x[4], x[11]);
   accum.mul_x2(x[5], x[10]);
   accum.mul_x2(x[6], x[9]);
   accum.mul_x2(x[7], x[8]);
   z[15] = accum.extract();
   accum.mul_x2(x[1], x[15]);
   accum.mul_x2(x[2], x[14]);
   accum.mul_x2(x[3], x[13]);
   accum.mul_x2(x[4], x[12]);
   accum.mul_x2(x[5], x[11]);
   accum.mul_x2(x[6], x[10]);
   accum.mul_x2(x[7], x[9]);
   accum.mul(x[8], x[8]);
   z[16] = accum.extract();
   accum.mul_x2(x[2], x[15]);
   accum.mul_x2(x[3], x[14]);
   accum.mul_x2(x[4], x[13]);
   accum.mul_x2(x[5], x[12]);
   accum.mul_x2(x[6], x[11]);
   accum.mul_x2(x[7], x[10]);
   accum.mul_x2(x[8], x[9]);
   z[17] = accum.extract();
   accum.mul_x2(x[3], x[15]);
   accum.mul_x2(x[4], x[14]);
   accum.mul_x2(x[5], x[13]);
   accum.mul_x2(x[6], x[12]);
   accum.mul_x2(x[7], x[11]);
   accum.mul_x2(x[8], x[10]);
   accum.mul(x[9], x[9]);
   z[18] = accum.extract();
   accum.mul_x2(x[4], x[15]);
   accum.mul_x2(x[5], x[14]);
   accum.mul_x2(x[6], x[13]);
   accum.mul_x2(x[7], x[12]);
   accum.mul_x2(x[8], x[11]);
   accum.mul_x2(x[9], x[10]);
   z[19] = accum.extract();
   accum.mul_x2(x[5], x[15]);
   accum.mul_x2(x[6], x[14]);
   accum.mul_x2(x[7], x[13]);
   accum.mul_x2(x[8], x[12]);
   accum.mul_x2(x[9], x[11]);
   accum.mul(x[10], x[10]);
   z[20] = accum.extract();
   accum.mul_x2(x[6], x[15]);
   accum.mul_x2(x[7], x[14]);
   accum.mul_x2(x[8], x[13]);
   accum.mul_x2(x[9], x[12]);
   accum.mul_x2(x[10], x[11]);
   z[21] = accum.extract();
   accum.mul_x2(x[7], x[15]);
   accum.mul_x2(x[8], x[14]);
   accum.mul_x2(x[9], x[13]);
   accum.mul_x2(x[10], x[12]);
   accum.mul(x[11], x[11]);
   z[22] = accum.extract();
   accum.mul_x2(x[8], x[15]);
   accum.mul_x2(x[9], x[14]);
   accum.mul_x2(x[10], x[13]);
   accum.mul_x2(x[11], x[12]);
   z[23] = accum.extract();
   accum.mul_x2(x[9], x[15]);
   accum.mul_x2(x[10], x[14]);
   accum.mul_x2(x[11], x[13]);
   accum.mul(x[12], x[12]);
   z[24] = accum.extract();
   accum.mul_x2(x[10], x[15]);
   accum.mul_x2(x[11], x[14]);
   accum.mul_x2(x[12], x[13]);
   z[25] = accum.extract();
   accum.mul_x2(x[11], x[15]);
   accum.mul_x2(x[12], x[14]);
   accum.mul(x[13], x[13]);
   z[26] = accum.extract();
   accum.mul_x2(x[12], x[15]);
   accum.mul_x2(x[13], x[14]);
   z[27] = accum.extract();
   accum.mul_x2(x[13], x[15]);
   accum.mul(x[14], x[14]);
   z[28] = accum.extract();
   accum.mul_x2(x[14], x[15]);
   z[29] = accum.extract();
   accum.mul(x[15], x[15]);
   z[30] = accum.extract();
   z[31] = accum.extract();
}

/*
* Comba 16x16 Multiplication
*/
void bigint_comba_mul16(word z[32], const word x[16], const word y[16]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[0], y[6]);
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   accum.mul(x[6], y[0]);
   z[6] = accum.extract();
   accum.mul(x[0], y[7]);
   accum.mul(x[1], y[6]);
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   accum.mul(x[6], y[1]);
   accum.mul(x[7], y[0]);
   z[7] = accum.extract();
   accum.mul(x[0], y[8]);
   accum.mul(x[1], y[7]);
   accum.mul(x[2], y[6]);
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   accum.mul(x[6], y[2]);
   accum.mul(x[7], y[1]);
   accum.mul(x[8], y[0]);
   z[8] = accum.extract();
   accum.mul(x[0], y[9]);
   accum.mul(x[1], y[8]);
   accum.mul(x[2], y[7]);
   accum.mul(x[3], y[6]);
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   accum.mul(x[6], y[3]);
   accum.mul(x[7], y[2]);
   accum.mul(x[8], y[1]);
   accum.mul(x[9], y[0]);
   z[9] = accum.extract();
   accum.mul(x[0], y[10]);
   accum.mul(x[1], y[9]);
   accum.mul(x[2], y[8]);
   accum.mul(x[3], y[7]);
   accum.mul(x[4], y[6]);
   accum.mul(x[5], y[5]);
   accum.mul(x[6], y[4]);
   accum.mul(x[7], y[3]);
   accum.mul(x[8], y[2]);
   accum.mul(x[9], y[1]);
   accum.mul(x[10], y[0]);
   z[10] = accum.extract();
   accum.mul(x[0], y[11]);
   accum.mul(x[1], y[10]);
   accum.mul(x[2], y[9]);
   accum.mul(x[3], y[8]);
   accum.mul(x[4], y[7]);
   accum.mul(x[5], y[6]);
   accum.mul(x[6], y[5]);
   accum.mul(x[7], y[4]);
   accum.mul(x[8], y[3]);
   accum.mul(x[9], y[2]);
   accum.mul(x[10], y[1]);
   accum.mul(x[11], y[0]);
   z[11] = accum.extract();
   accum.mul(x[0], y[12]);
   accum.mul(x[1], y[11]);
   accum.mul(x[2], y[10]);
   accum.mul(x[3], y[9]);
   accum.mul(x[4], y[8]);
   accum.mul(x[5], y[7]);
   accum.mul(x[6], y[6]);
   accum.mul(x[7], y[5]);
   accum.mul(x[8], y[4]);
   accum.mul(x[9], y[3]);
   accum.mul(x[10], y[2]);
   accum.mul(x[11], y[1]);
   accum.mul(x[12], y[0]);
   z[12] = accum.extract();
   accum.mul(x[0], y[13]);
   accum.mul(x[1], y[12]);
   accum.mul(x[2], y[11]);
   accum.mul(x[3], y[10]);
   accum.mul(x[4], y[9]);
   accum.mul(x[5], y[8]);
   accum.mul(x[6], y[7]);
   accum.mul(x[7], y[6]);
   accum.mul(x[8], y[5]);
   accum.mul(x[9], y[4]);
   accum.mul(x[10], y[3]);
   accum.mul(x[11], y[2]);
   accum.mul(x[12], y[1]);
   accum.mul(x[13], y[0]);
   z[13] = accum.extract();
   accum.mul(x[0], y[14]);
   accum.mul(x[1], y[13]);
   accum.mul(x[2], y[12]);
   accum.mul(x[3], y[11]);
   accum.mul(x[4], y[10]);
   accum.mul(x[5], y[9]);
   accum.mul(x[6], y[8]);
   accum.mul(x[7], y[7]);
   accum.mul(x[8], y[6]);
   accum.mul(x[9], y[5]);
   accum.mul(x[10], y[4]);
   accum.mul(x[11], y[3]);
   accum.mul(x[12], y[2]);
   accum.mul(x[13], y[1]);
   accum.mul(x[14], y[0]);
   z[14] = accum.extract();
   accum.mul(x[0], y[15]);
   accum.mul(x[1], y[14]);
   accum.mul(x[2], y[13]);
   accum.mul(x[3], y[12]);
   accum.mul(x[4], y[11]);
   accum.mul(x[5], y[10]);
   accum.mul(x[6], y[9]);
   accum.mul(x[7], y[8]);
   accum.mul(x[8], y[7]);
   accum.mul(x[9], y[6]);
   accum.mul(x[10], y[5]);
   accum.mul(x[11], y[4]);
   accum.mul(x[12], y[3]);
   accum.mul(x[13], y[2]);
   accum.mul(x[14], y[1]);
   accum.mul(x[15], y[0]);
   z[15] = accum.extract();
   accum.mul(x[1], y[15]);
   accum.mul(x[2], y[14]);
   accum.mul(x[3], y[13]);
   accum.mul(x[4], y[12]);
   accum.mul(x[5], y[11]);
   accum.mul(x[6], y[10]);
   accum.mul(x[7], y[9]);
   accum.mul(x[8], y[8]);
   accum.mul(x[9], y[7]);
   accum.mul(x[10], y[6]);
   accum.mul(x[11], y[5]);
   accum.mul(x[12], y[4]);
   accum.mul(x[13], y[3]);
   accum.mul(x[14], y[2]);
   accum.mul(x[15], y[1]);
   z[16] = accum.extract();
   accum.mul(x[2], y[15]);
   accum.mul(x[3], y[14]);
   accum.mul(x[4], y[13]);
   accum.mul(x[5], y[12]);
   accum.mul(x[6], y[11]);
   accum.mul(x[7], y[10]);
   accum.mul(x[8], y[9]);
   accum.mul(x[9], y[8]);
   accum.mul(x[10], y[7]);
   accum.mul(x[11], y[6]);
   accum.mul(x[12], y[5]);
   accum.mul(x[13], y[4]);
   accum.mul(x[14], y[3]);
   accum.mul(x[15], y[2]);
   z[17] = accum.extract();
   accum.mul(x[3], y[15]);
   accum.mul(x[4], y[14]);
   accum.mul(x[5], y[13]);
   accum.mul(x[6], y[12]);
   accum.mul(x[7], y[11]);
   accum.mul(x[8], y[10]);
   accum.mul(x[9], y[9]);
   accum.mul(x[10], y[8]);
   accum.mul(x[11], y[7]);
   accum.mul(x[12], y[6]);
   accum.mul(x[13], y[5]);
   accum.mul(x[14], y[4]);
   accum.mul(x[15], y[3]);
   z[18] = accum.extract();
   accum.mul(x[4], y[15]);
   accum.mul(x[5], y[14]);
   accum.mul(x[6], y[13]);
   accum.mul(x[7], y[12]);
   accum.mul(x[8], y[11]);
   accum.mul(x[9], y[10]);
   accum.mul(x[10], y[9]);
   accum.mul(x[11], y[8]);
   accum.mul(x[12], y[7]);
   accum.mul(x[13], y[6]);
   accum.mul(x[14], y[5]);
   accum.mul(x[15], y[4]);
   z[19] = accum.extract();
   accum.mul(x[5], y[15]);
   accum.mul(x[6], y[14]);
   accum.mul(x[7], y[13]);
   accum.mul(x[8], y[12]);
   accum.mul(x[9], y[11]);
   accum.mul(x[10], y[10]);
   accum.mul(x[11], y[9]);
   accum.mul(x[12], y[8]);
   accum.mul(x[13], y[7]);
   accum.mul(x[14], y[6]);
   accum.mul(x[15], y[5]);
   z[20] = accum.extract();
   accum.mul(x[6], y[15]);
   accum.mul(x[7], y[14]);
   accum.mul(x[8], y[13]);
   accum.mul(x[9], y[12]);
   accum.mul(x[10], y[11]);
   accum.mul(x[11], y[10]);
   accum.mul(x[12], y[9]);
   accum.mul(x[13], y[8]);
   accum.mul(x[14], y[7]);
   accum.mul(x[15], y[6]);
   z[21] = accum.extract();
   accum.mul(x[7], y[15]);
   accum.mul(x[8], y[14]);
   accum.mul(x[9], y[13]);
   accum.mul(x[10], y[12]);
   accum.mul(x[11], y[11]);
   accum.mul(x[12], y[10]);
   accum.mul(x[13], y[9]);
   accum.mul(x[14], y[8]);
   accum.mul(x[15], y[7]);
   z[22] = accum.extract();
   accum.mul(x[8], y[15]);
   accum.mul(x[9], y[14]);
   accum.mul(x[10], y[13]);
   accum.mul(x[11], y[12]);
   accum.mul(x[12], y[11]);
   accum.mul(x[13], y[10]);
   accum.mul(x[14], y[9]);
   accum.mul(x[15], y[8]);
   z[23] = accum.extract();
   accum.mul(x[9], y[15]);
   accum.mul(x[10], y[14]);
   accum.mul(x[11], y[13]);
   accum.mul(x[12], y[12]);
   accum.mul(x[13], y[11]);
   accum.mul(x[14], y[10]);
   accum.mul(x[15], y[9]);
   z[24] = accum.extract();
   accum.mul(x[10], y[15]);
   accum.mul(x[11], y[14]);
   accum.mul(x[12], y[13]);
   accum.mul(x[13], y[12]);
   accum.mul(x[14], y[11]);
   accum.mul(x[15], y[10]);
   z[25] = accum.extract();
   accum.mul(x[11], y[15]);
   accum.mul(x[12], y[14]);
   accum.mul(x[13], y[13]);
   accum.mul(x[14], y[12]);
   accum.mul(x[15], y[11]);
   z[26] = accum.extract();
   accum.mul(x[12], y[15]);
   accum.mul(x[13], y[14]);
   accum.mul(x[14], y[13]);
   accum.mul(x[15], y[12]);
   z[27] = accum.extract();
   accum.mul(x[13], y[15]);
   accum.mul(x[14], y[14]);
   accum.mul(x[15], y[13]);
   z[28] = accum.extract();
   accum.mul(x[14], y[15]);
   accum.mul(x[15], y[14]);
   z[29] = accum.extract();
   accum.mul(x[15], y[15]);
   z[30] = accum.extract();
   z[31] = accum.extract();
}

/*
* Comba 24x24 Squaring
*/
void bigint_comba_sqr24(word z[48], const word x[24]) {
   word3<word> accum;

   accum.mul(x[0], x[0]);
   z[0] = accum.extract();
   accum.mul_x2(x[0], x[1]);
   z[1] = accum.extract();
   accum.mul_x2(x[0], x[2]);
   accum.mul(x[1], x[1]);
   z[2] = accum.extract();
   accum.mul_x2(x[0], x[3]);
   accum.mul_x2(x[1], x[2]);
   z[3] = accum.extract();
   accum.mul_x2(x[0], x[4]);
   accum.mul_x2(x[1], x[3]);
   accum.mul(x[2], x[2]);
   z[4] = accum.extract();
   accum.mul_x2(x[0], x[5]);
   accum.mul_x2(x[1], x[4]);
   accum.mul_x2(x[2], x[3]);
   z[5] = accum.extract();
   accum.mul_x2(x[0], x[6]);
   accum.mul_x2(x[1], x[5]);
   accum.mul_x2(x[2], x[4]);
   accum.mul(x[3], x[3]);
   z[6] = accum.extract();
   accum.mul_x2(x[0], x[7]);
   accum.mul_x2(x[1], x[6]);
   accum.mul_x2(x[2], x[5]);
   accum.mul_x2(x[3], x[4]);
   z[7] = accum.extract();
   accum.mul_x2(x[0], x[8]);
   accum.mul_x2(x[1], x[7]);
   accum.mul_x2(x[2], x[6]);
   accum.mul_x2(x[3], x[5]);
   accum.mul(x[4], x[4]);
   z[8] = accum.extract();
   accum.mul_x2(x[0], x[9]);
   accum.mul_x2(x[1], x[8]);
   accum.mul_x2(x[2], x[7]);
   accum.mul_x2(x[3], x[6]);
   accum.mul_x2(x[4], x[5]);
   z[9] = accum.extract();
   accum.mul_x2(x[0], x[10]);
   accum.mul_x2(x[1], x[9]);
   accum.mul_x2(x[2], x[8]);
   accum.mul_x2(x[3], x[7]);
   accum.mul_x2(x[4], x[6]);
   accum.mul(x[5], x[5]);
   z[10] = accum.extract();
   accum.mul_x2(x[0], x[11]);
   accum.mul_x2(x[1], x[10]);
   accum.mul_x2(x[2], x[9]);
   accum.mul_x2(x[3], x[8]);
   accum.mul_x2(x[4], x[7]);
   accum.mul_x2(x[5], x[6]);
   z[11] = accum.extract();
   accum.mul_x2(x[0], x[12]);
   accum.mul_x2(x[1], x[11]);
   accum.mul_x2(x[2], x[10]);
   accum.mul_x2(x[3], x[9]);
   accum.mul_x2(x[4], x[8]);
   accum.mul_x2(x[5], x[7]);
   accum.mul(x[6], x[6]);
   z[12] = accum.extract();
   accum.mul_x2(x[0], x[13]);
   accum.mul_x2(x[1], x[12]);
   accum.mul_x2(x[2], x[11]);
   accum.mul_x2(x[3], x[10]);
   accum.mul_x2(x[4], x[9]);
   accum.mul_x2(x[5], x[8]);
   accum.mul_x2(x[6], x[7]);
   z[13] = accum.extract();
   accum.mul_x2(x[0], x[14]);
   accum.mul_x2(x[1], x[13]);
   accum.mul_x2(x[2], x[12]);
   accum.mul_x2(x[3], x[11]);
   accum.mul_x2(x[4], x[10]);
   accum.mul_x2(x[5], x[9]);
   accum.mul_x2(x[6], x[8]);
   accum.mul(x[7], x[7]);
   z[14] = accum.extract();
   accum.mul_x2(x[0], x[15]);
   accum.mul_x2(x[1], x[14]);
   accum.mul_x2(x[2], x[13]);
   accum.mul_x2(x[3], x[12]);
   accum.mul_x2(x[4], x[11]);
   accum.mul_x2(x[5], x[10]);
   accum.mul_x2(x[6], x[9]);
   accum.mul_x2(x[7], x[8]);
   z[15] = accum.extract();
   accum.mul_x2(x[0], x[16]);
   accum.mul_x2(x[1], x[15]);
   accum.mul_x2(x[2], x[14]);
   accum.mul_x2(x[3], x[13]);
   accum.mul_x2(x[4], x[12]);
   accum.mul_x2(x[5], x[11]);
   accum.mul_x2(x[6], x[10]);
   accum.mul_x2(x[7], x[9]);
   accum.mul(x[8], x[8]);
   z[16] = accum.extract();
   accum.mul_x2(x[0], x[17]);
   accum.mul_x2(x[1], x[16]);
   accum.mul_x2(x[2], x[15]);
   accum.mul_x2(x[3], x[14]);
   accum.mul_x2(x[4], x[13]);
   accum.mul_x2(x[5], x[12]);
   accum.mul_x2(x[6], x[11]);
   accum.mul_x2(x[7], x[10]);
   accum.mul_x2(x[8], x[9]);
   z[17] = accum.extract();
   accum.mul_x2(x[0], x[18]);
   accum.mul_x2(x[1], x[17]);
   accum.mul_x2(x[2], x[16]);
   accum.mul_x2(x[3], x[15]);
   accum.mul_x2(x[4], x[14]);
   accum.mul_x2(x[5], x[13]);
   accum.mul_x2(x[6], x[12]);
   accum.mul_x2(x[7], x[11]);
   accum.mul_x2(x[8], x[10]);
   accum.mul(x[9], x[9]);
   z[18] = accum.extract();
   accum.mul_x2(x[0], x[19]);
   accum.mul_x2(x[1], x[18]);
   accum.mul_x2(x[2], x[17]);
   accum.mul_x2(x[3], x[16]);
   accum.mul_x2(x[4], x[15]);
   accum.mul_x2(x[5], x[14]);
   accum.mul_x2(x[6], x[13]);
   accum.mul_x2(x[7], x[12]);
   accum.mul_x2(x[8], x[11]);
   accum.mul_x2(x[9], x[10]);
   z[19] = accum.extract();
   accum.mul_x2(x[0], x[20]);
   accum.mul_x2(x[1], x[19]);
   accum.mul_x2(x[2], x[18]);
   accum.mul_x2(x[3], x[17]);
   accum.mul_x2(x[4], x[16]);
   accum.mul_x2(x[5], x[15]);
   accum.mul_x2(x[6], x[14]);
   accum.mul_x2(x[7], x[13]);
   accum.mul_x2(x[8], x[12]);
   accum.mul_x2(x[9], x[11]);
   accum.mul(x[10], x[10]);
   z[20] = accum.extract();
   accum.mul_x2(x[0], x[21]);
   accum.mul_x2(x[1], x[20]);
   accum.mul_x2(x[2], x[19]);
   accum.mul_x2(x[3], x[18]);
   accum.mul_x2(x[4], x[17]);
   accum.mul_x2(x[5], x[16]);
   accum.mul_x2(x[6], x[15]);
   accum.mul_x2(x[7], x[14]);
   accum.mul_x2(x[8], x[13]);
   accum.mul_x2(x[9], x[12]);
   accum.mul_x2(x[10], x[11]);
   z[21] = accum.extract();
   accum.mul_x2(x[0], x[22]);
   accum.mul_x2(x[1], x[21]);
   accum.mul_x2(x[2], x[20]);
   accum.mul_x2(x[3], x[19]);
   accum.mul_x2(x[4], x[18]);
   accum.mul_x2(x[5], x[17]);
   accum.mul_x2(x[6], x[16]);
   accum.mul_x2(x[7], x[15]);
   accum.mul_x2(x[8], x[14]);
   accum.mul_x2(x[9], x[13]);
   accum.mul_x2(x[10], x[12]);
   accum.mul(x[11], x[11]);
   z[22] = accum.extract();
   accum.mul_x2(x[0], x[23]);
   accum.mul_x2(x[1], x[22]);
   accum.mul_x2(x[2], x[21]);
   accum.mul_x2(x[3], x[20]);
   accum.mul_x2(x[4], x[19]);
   accum.mul_x2(x[5], x[18]);
   accum.mul_x2(x[6], x[17]);
   accum.mul_x2(x[7], x[16]);
   accum.mul_x2(x[8], x[15]);
   accum.mul_x2(x[9], x[14]);
   accum.mul_x2(x[10], x[13]);
   accum.mul_x2(x[11], x[12]);
   z[23] = accum.extract();
   accum.mul_x2(x[1], x[23]);
   accum.mul_x2(x[2], x[22]);
   accum.mul_x2(x[3], x[21]);
   accum.mul_x2(x[4], x[20]);
   accum.mul_x2(x[5], x[19]);
   accum.mul_x2(x[6], x[18]);
   accum.mul_x2(x[7], x[17]);
   accum.mul_x2(x[8], x[16]);
   accum.mul_x2(x[9], x[15]);
   accum.mul_x2(x[10], x[14]);
   accum.mul_x2(x[11], x[13]);
   accum.mul(x[12], x[12]);
   z[24] = accum.extract();
   accum.mul_x2(x[2], x[23]);
   accum.mul_x2(x[3], x[22]);
   accum.mul_x2(x[4], x[21]);
   accum.mul_x2(x[5], x[20]);
   accum.mul_x2(x[6], x[19]);
   accum.mul_x2(x[7], x[18]);
   accum.mul_x2(x[8], x[17]);
   accum.mul_x2(x[9], x[16]);
   accum.mul_x2(x[10], x[15]);
   accum.mul_x2(x[11], x[14]);
   accum.mul_x2(x[12], x[13]);
   z[25] = accum.extract();
   accum.mul_x2(x[3], x[23]);
   accum.mul_x2(x[4], x[22]);
   accum.mul_x2(x[5], x[21]);
   accum.mul_x2(x[6], x[20]);
   accum.mul_x2(x[7], x[19]);
   accum.mul_x2(x[8], x[18]);
   accum.mul_x2(x[9], x[17]);
   accum.mul_x2(x[10], x[16]);
   accum.mul_x2(x[11], x[15]);
   accum.mul_x2(x[12], x[14]);
   accum.mul(x[13], x[13]);
   z[26] = accum.extract();
   accum.mul_x2(x[4], x[23]);
   accum.mul_x2(x[5], x[22]);
   accum.mul_x2(x[6], x[21]);
   accum.mul_x2(x[7], x[20]);
   accum.mul_x2(x[8], x[19]);
   accum.mul_x2(x[9], x[18]);
   accum.mul_x2(x[10], x[17]);
   accum.mul_x2(x[11], x[16]);
   accum.mul_x2(x[12], x[15]);
   accum.mul_x2(x[13], x[14]);
   z[27] = accum.extract();
   accum.mul_x2(x[5], x[23]);
   accum.mul_x2(x[6], x[22]);
   accum.mul_x2(x[7], x[21]);
   accum.mul_x2(x[8], x[20]);
   accum.mul_x2(x[9], x[19]);
   accum.mul_x2(x[10], x[18]);
   accum.mul_x2(x[11], x[17]);
   accum.mul_x2(x[12], x[16]);
   accum.mul_x2(x[13], x[15]);
   accum.mul(x[14], x[14]);
   z[28] = accum.extract();
   accum.mul_x2(x[6], x[23]);
   accum.mul_x2(x[7], x[22]);
   accum.mul_x2(x[8], x[21]);
   accum.mul_x2(x[9], x[20]);
   accum.mul_x2(x[10], x[19]);
   accum.mul_x2(x[11], x[18]);
   accum.mul_x2(x[12], x[17]);
   accum.mul_x2(x[13], x[16]);
   accum.mul_x2(x[14], x[15]);
   z[29] = accum.extract();
   accum.mul_x2(x[7], x[23]);
   accum.mul_x2(x[8], x[22]);
   accum.mul_x2(x[9], x[21]);
   accum.mul_x2(x[10], x[20]);
   accum.mul_x2(x[11], x[19]);
   accum.mul_x2(x[12], x[18]);
   accum.mul_x2(x[13], x[17]);
   accum.mul_x2(x[14], x[16]);
   accum.mul(x[15], x[15]);
   z[30] = accum.extract();
   accum.mul_x2(x[8], x[23]);
   accum.mul_x2(x[9], x[22]);
   accum.mul_x2(x[10], x[21]);
   accum.mul_x2(x[11], x[20]);
   accum.mul_x2(x[12], x[19]);
   accum.mul_x2(x[13], x[18]);
   accum.mul_x2(x[14], x[17]);
   accum.mul_x2(x[15], x[16]);
   z[31] = accum.extract();
   accum.mul_x2(x[9], x[23]);
   accum.mul_x2(x[10], x[22]);
   accum.mul_x2(x[11], x[21]);
   accum.mul_x2(x[12], x[20]);
   accum.mul_x2(x[13], x[19]);
   accum.mul_x2(x[14], x[18]);
   accum.mul_x2(x[15], x[17]);
   accum.mul(x[16], x[16]);
   z[32] = accum.extract();
   accum.mul_x2(x[10], x[23]);
   accum.mul_x2(x[11], x[22]);
   accum.mul_x2(x[12], x[21]);
   accum.mul_x2(x[13], x[20]);
   accum.mul_x2(x[14], x[19]);
   accum.mul_x2(x[15], x[18]);
   accum.mul_x2(x[16], x[17]);
   z[33] = accum.extract();
   accum.mul_x2(x[11], x[23]);
   accum.mul_x2(x[12], x[22]);
   accum.mul_x2(x[13], x[21]);
   accum.mul_x2(x[14], x[20]);
   accum.mul_x2(x[15], x[19]);
   accum.mul_x2(x[16], x[18]);
   accum.mul(x[17], x[17]);
   z[34] = accum.extract();
   accum.mul_x2(x[12], x[23]);
   accum.mul_x2(x[13], x[22]);
   accum.mul_x2(x[14], x[21]);
   accum.mul_x2(x[15], x[20]);
   accum.mul_x2(x[16], x[19]);
   accum.mul_x2(x[17], x[18]);
   z[35] = accum.extract();
   accum.mul_x2(x[13], x[23]);
   accum.mul_x2(x[14], x[22]);
   accum.mul_x2(x[15], x[21]);
   accum.mul_x2(x[16], x[20]);
   accum.mul_x2(x[17], x[19]);
   accum.mul(x[18], x[18]);
   z[36] = accum.extract();
   accum.mul_x2(x[14], x[23]);
   accum.mul_x2(x[15], x[22]);
   accum.mul_x2(x[16], x[21]);
   accum.mul_x2(x[17], x[20]);
   accum.mul_x2(x[18], x[19]);
   z[37] = accum.extract();
   accum.mul_x2(x[15], x[23]);
   accum.mul_x2(x[16], x[22]);
   accum.mul_x2(x[17], x[21]);
   accum.mul_x2(x[18], x[20]);
   accum.mul(x[19], x[19]);
   z[38] = accum.extract();
   accum.mul_x2(x[16], x[23]);
   accum.mul_x2(x[17], x[22]);
   accum.mul_x2(x[18], x[21]);
   accum.mul_x2(x[19], x[20]);
   z[39] = accum.extract();
   accum.mul_x2(x[17], x[23]);
   accum.mul_x2(x[18], x[22]);
   accum.mul_x2(x[19], x[21]);
   accum.mul(x[20], x[20]);
   z[40] = accum.extract();
   accum.mul_x2(x[18], x[23]);
   accum.mul_x2(x[19], x[22]);
   accum.mul_x2(x[20], x[21]);
   z[41] = accum.extract();
   accum.mul_x2(x[19], x[23]);
   accum.mul_x2(x[20], x[22]);
   accum.mul(x[21], x[21]);
   z[42] = accum.extract();
   accum.mul_x2(x[20], x[23]);
   accum.mul_x2(x[21], x[22]);
   z[43] = accum.extract();
   accum.mul_x2(x[21], x[23]);
   accum.mul(x[22], x[22]);
   z[44] = accum.extract();
   accum.mul_x2(x[22], x[23]);
   z[45] = accum.extract();
   accum.mul(x[23], x[23]);
   z[46] = accum.extract();
   z[47] = accum.extract();
}

/*
* Comba 24x24 Multiplication
*/
void bigint_comba_mul24(word z[48], const word x[24], const word y[24]) {
   word3<word> accum;

   accum.mul(x[0], y[0]);
   z[0] = accum.extract();
   accum.mul(x[0], y[1]);
   accum.mul(x[1], y[0]);
   z[1] = accum.extract();
   accum.mul(x[0], y[2]);
   accum.mul(x[1], y[1]);
   accum.mul(x[2], y[0]);
   z[2] = accum.extract();
   accum.mul(x[0], y[3]);
   accum.mul(x[1], y[2]);
   accum.mul(x[2], y[1]);
   accum.mul(x[3], y[0]);
   z[3] = accum.extract();
   accum.mul(x[0], y[4]);
   accum.mul(x[1], y[3]);
   accum.mul(x[2], y[2]);
   accum.mul(x[3], y[1]);
   accum.mul(x[4], y[0]);
   z[4] = accum.extract();
   accum.mul(x[0], y[5]);
   accum.mul(x[1], y[4]);
   accum.mul(x[2], y[3]);
   accum.mul(x[3], y[2]);
   accum.mul(x[4], y[1]);
   accum.mul(x[5], y[0]);
   z[5] = accum.extract();
   accum.mul(x[0], y[6]);
   accum.mul(x[1], y[5]);
   accum.mul(x[2], y[4]);
   accum.mul(x[3], y[3]);
   accum.mul(x[4], y[2]);
   accum.mul(x[5], y[1]);
   accum.mul(x[6], y[0]);
   z[6] = accum.extract();
   accum.mul(x[0], y[7]);
   accum.mul(x[1], y[6]);
   accum.mul(x[2], y[5]);
   accum.mul(x[3], y[4]);
   accum.mul(x[4], y[3]);
   accum.mul(x[5], y[2]);
   accum.mul(x[6], y[1]);
   accum.mul(x[7], y[0]);
   z[7] = accum.extract();
   accum.mul(x[0], y[8]);
   accum.mul(x[1], y[7]);
   accum.mul(x[2], y[6]);
   accum.mul(x[3], y[5]);
   accum.mul(x[4], y[4]);
   accum.mul(x[5], y[3]);
   accum.mul(x[6], y[2]);
   accum.mul(x[7], y[1]);
   accum.mul(x[8], y[0]);
   z[8] = accum.extract();
   accum.mul(x[0], y[9]);
   accum.mul(x[1], y[8]);
   accum.mul(x[2], y[7]);
   accum.mul(x[3], y[6]);
   accum.mul(x[4], y[5]);
   accum.mul(x[5], y[4]);
   accum.mul(x[6], y[3]);
   accum.mul(x[7], y[2]);
   accum.mul(x[8], y[1]);
   accum.mul(x[9], y[0]);
   z[9] = accum.extract();
   accum.mul(x[0], y[10]);
   accum.mul(x[1], y[9]);
   accum.mul(x[2], y[8]);
   accum.mul(x[3], y[7]);
   accum.mul(x[4], y[6]);
   accum.mul(x[5], y[5]);
   accum.mul(x[6], y[4]);
   accum.mul(x[7], y[3]);
   accum.mul(x[8], y[2]);
   accum.mul(x[9], y[1]);
   accum.mul(x[10], y[0]);
   z[10] = accum.extract();
   accum.mul(x[0], y[11]);
   accum.mul(x[1], y[10]);
   accum.mul(x[2], y[9]);
   accum.mul(x[3], y[8]);
   accum.mul(x[4], y[7]);
   accum.mul(x[5], y[6]);
   accum.mul(x[6], y[5]);
   accum.mul(x[7], y[4]);
   accum.mul(x[8], y[3]);
   accum.mul(x[9], y[2]);
   accum.mul(x[10], y[1]);
   accum.mul(x[11], y[0]);
   z[11] = accum.extract();
   accum.mul(x[0], y[12]);
   accum.mul(x[1], y[11]);
   accum.mul(x[2], y[10]);
   accum.mul(x[3], y[9]);
   accum.mul(x[4], y[8]);
   accum.mul(x[5], y[7]);
   accum.mul(x[6], y[6]);
   accum.mul(x[7], y[5]);
   accum.mul(x[8], y[4]);
   accum.mul(x[9], y[3]);
   accum.mul(x[10], y[2]);
   accum.mul(x[11], y[1]);
   accum.mul(x[12], y[0]);
   z[12] = accum.extract();
   accum.mul(x[0], y[13]);
   accum.mul(x[1], y[12]);
   accum.mul(x[2], y[11]);
   accum.mul(x[3], y[10]);
   accum.mul(x[4], y[9]);
   accum.mul(x[5], y[8]);
   accum.mul(x[6], y[7]);
   accum.mul(x[7], y[6]);
   accum.mul(x[8], y[5]);
   accum.mul(x[9], y[4]);
   accum.mul(x[10], y[3]);
   accum.mul(x[11], y[2]);
   accum.mul(x[12], y[1]);
   accum.mul(x[13], y[0]);
   z[13] = accum.extract();
   accum.mul(x[0], y[14]);
   accum.mul(x[1], y[13]);
   accum.mul(x[2], y[12]);
   accum.mul(x[3], y[11]);
   accum.mul(x[4], y[10]);
   accum.mul(x[5], y[9]);
   accum.mul(x[6], y[8]);
   accum.mul(x[7], y[7]);
   accum.mul(x[8], y[6]);
   accum.mul(x[9], y[5]);
   accum.mul(x[10], y[4]);
   accum.mul(x[11], y[3]);
   accum.mul(x[12], y[2]);
   accum.mul(x[13], y[1]);
   accum.mul(x[14], y[0]);
   z[14] = accum.extract();
   accum.mul(x[0], y[15]);
   accum.mul(x[1], y[14]);
   accum.mul(x[2], y[13]);
   accum.mul(x[3], y[12]);
   accum.mul(x[4], y[11]);
   accum.mul(x[5], y[10]);
   accum.mul(x[6], y[9]);
   accum.mul(x[7], y[8]);
   accum.mul(x[8], y[7]);
   accum.mul(x[9], y[6]);
   accum.mul(x[10], y[5]);
   accum.mul(x[11], y[4]);
   accum.mul(x[12], y[3]);
   accum.mul(x[13], y[2]);
   accum.mul(x[14], y[1]);
   accum.mul(x[15], y[0]);
   z[15] = accum.extract();
   accum.mul(x[0], y[16]);
   accum.mul(x[1], y[15]);
   accum.mul(x[2], y[14]);
   accum.mul(x[3], y[13]);
   accum.mul(x[4], y[12]);
   accum.mul(x[5], y[11]);
   accum.mul(x[6], y[10]);
   accum.mul(x[7], y[9]);
   accum.mul(x[8], y[8]);
   accum.mul(x[9], y[7]);
   accum.mul(x[10], y[6]);
   accum.mul(x[11], y[5]);
   accum.mul(x[12], y[4]);
   accum.mul(x[13], y[3]);
   accum.mul(x[14], y[2]);
   accum.mul(x[15], y[1]);
   accum.mul(x[16], y[0]);
   z[16] = accum.extract();
   accum.mul(x[0], y[17]);
   accum.mul(x[1], y[16]);
   accum.mul(x[2], y[15]);
   accum.mul(x[3], y[14]);
   accum.mul(x[4], y[13]);
   accum.mul(x[5], y[12]);
   accum.mul(x[6], y[11]);
   accum.mul(x[7], y[10]);
   accum.mul(x[8], y[9]);
   accum.mul(x[9], y[8]);
   accum.mul(x[10], y[7]);
   accum.mul(x[11], y[6]);
   accum.mul(x[12], y[5]);
   accum.mul(x[13], y[4]);
   accum.mul(x[14], y[3]);
   accum.mul(x[15], y[2]);
   accum.mul(x[16], y[1]);
   accum.mul(x[17], y[0]);
   z[17] = accum.extract();
   accum.mul(x[0], y[18]);
   accum.mul(x[1], y[17]);
   accum.mul(x[2], y[16]);
   accum.mul(x[3], y[15]);
   accum.mul(x[4], y[14]);
   accum.mul(x[5], y[13]);
   accum.mul(x[6], y[12]);
   accum.mul(x[7], y[11]);
   accum.mul(x[8], y[10]);
   accum.mul(x[9], y[9]);
   accum.mul(x[10], y[8]);
   accum.mul(x[11], y[7]);
   accum.mul(x[12], y[6]);
   accum.mul(x[13], y[5]);
   accum.mul(x[14], y[4]);
   accum.mul(x[15], y[3]);
   accum.mul(x[16], y[2]);
   accum.mul(x[17], y[1]);
   accum.mul(x[18], y[0]);
   z[18] = accum.extract();
   accum.mul(x[0], y[19]);
   accum.mul(x[1], y[18]);
   accum.mul(x[2], y[17]);
   accum.mul(x[3], y[16]);
   accum.mul(x[4], y[15]);
   accum.mul(x[5], y[14]);
   accum.mul(x[6], y[13]);
   accum.mul(x[7], y[12]);
   accum.mul(x[8], y[11]);
   accum.mul(x[9], y[10]);
   accum.mul(x[10], y[9]);
   accum.mul(x[11], y[8]);
   accum.mul(x[12], y[7]);
   accum.mul(x[13], y[6]);
   accum.mul(x[14], y[5]);
   accum.mul(x[15], y[4]);
   accum.mul(x[16], y[3]);
   accum.mul(x[17], y[2]);
   accum.mul(x[18], y[1]);
   accum.mul(x[19], y[0]);
   z[19] = accum.extract();
   accum.mul(x[0], y[20]);
   accum.mul(x[1], y[19]);
   accum.mul(x[2], y[18]);
   accum.mul(x[3], y[17]);
   accum.mul(x[4], y[16]);
   accum.mul(x[5], y[15]);
   accum.mul(x[6], y[14]);
   accum.mul(x[7], y[13]);
   accum.mul(x[8], y[12]);
   accum.mul(x[9], y[11]);
   accum.mul(x[10], y[10]);
   accum.mul(x[11], y[9]);
   accum.mul(x[12], y[8]);
   accum.mul(x[13], y[7]);
   accum.mul(x[14], y[6]);
   accum.mul(x[15], y[5]);
   accum.mul(x[16], y[4]);
   accum.mul(x[17], y[3]);
   accum.mul(x[18], y[2]);
   accum.mul(x[19], y[1]);
   accum.mul(x[20], y[0]);
   z[20] = accum.extract();
   accum.mul(x[0], y[21]);
   accum.mul(x[1], y[20]);
   accum.mul(x[2], y[19]);
   accum.mul(x[3], y[18]);
   accum.mul(x[4], y[17]);
   accum.mul(x[5], y[16]);
   accum.mul(x[6], y[15]);
   accum.mul(x[7], y[14]);
   accum.mul(x[8], y[13]);
   accum.mul(x[9], y[12]);
   accum.mul(x[10], y[11]);
   accum.mul(x[11], y[10]);
   accum.mul(x[12], y[9]);
   accum.mul(x[13], y[8]);
   accum.mul(x[14], y[7]);
   accum.mul(x[15], y[6]);
   accum.mul(x[16], y[5]);
   accum.mul(x[17], y[4]);
   accum.mul(x[18], y[3]);
   accum.mul(x[19], y[2]);
   accum.mul(x[20], y[1]);
   accum.mul(x[21], y[0]);
   z[21] = accum.extract();
   accum.mul(x[0], y[22]);
   accum.mul(x[1], y[21]);
   accum.mul(x[2], y[20]);
   accum.mul(x[3], y[19]);
   accum.mul(x[4], y[18]);
   accum.mul(x[5], y[17]);
   accum.mul(x[6], y[16]);
   accum.mul(x[7], y[15]);
   accum.mul(x[8], y[14]);
   accum.mul(x[9], y[13]);
   accum.mul(x[10], y[12]);
   accum.mul(x[11], y[11]);
   accum.mul(x[12], y[10]);
   accum.mul(x[13], y[9]);
   accum.mul(x[14], y[8]);
   accum.mul(x[15], y[7]);
   accum.mul(x[16], y[6]);
   accum.mul(x[17], y[5]);
   accum.mul(x[18], y[4]);
   accum.mul(x[19], y[3]);
   accum.mul(x[20], y[2]);
   accum.mul(x[21], y[1]);
   accum.mul(x[22], y[0]);
   z[22] = accum.extract();
   accum.mul(x[0], y[23]);
   accum.mul(x[1], y[22]);
   accum.mul(x[2], y[21]);
   accum.mul(x[3], y[20]);
   accum.mul(x[4], y[19]);
   accum.mul(x[5], y[18]);
   accum.mul(x[6], y[17]);
   accum.mul(x[7], y[16]);
   accum.mul(x[8], y[15]);
   accum.mul(x[9], y[14]);
   accum.mul(x[10], y[13]);
   accum.mul(x[11], y[12]);
   accum.mul(x[12], y[11]);
   accum.mul(x[13], y[10]);
   accum.mul(x[14], y[9]);
   accum.mul(x[15], y[8]);
   accum.mul(x[16], y[7]);
   accum.mul(x[17], y[6]);
   accum.mul(x[18], y[5]);
   accum.mul(x[19], y[4]);
   accum.mul(x[20], y[3]);
   accum.mul(x[21], y[2]);
   accum.mul(x[22], y[1]);
   accum.mul(x[23], y[0]);
   z[23] = accum.extract();
   accum.mul(x[1], y[23]);
   accum.mul(x[2], y[22]);
   accum.mul(x[3], y[21]);
   accum.mul(x[4], y[20]);
   accum.mul(x[5], y[19]);
   accum.mul(x[6], y[18]);
   accum.mul(x[7], y[17]);
   accum.mul(x[8], y[16]);
   accum.mul(x[9], y[15]);
   accum.mul(x[10], y[14]);
   accum.mul(x[11], y[13]);
   accum.mul(x[12], y[12]);
   accum.mul(x[13], y[11]);
   accum.mul(x[14], y[10]);
   accum.mul(x[15], y[9]);
   accum.mul(x[16], y[8]);
   accum.mul(x[17], y[7]);
   accum.mul(x[18], y[6]);
   accum.mul(x[19], y[5]);
   accum.mul(x[20], y[4]);
   accum.mul(x[21], y[3]);
   accum.mul(x[22], y[2]);
   accum.mul(x[23], y[1]);
   z[24] = accum.extract();
   accum.mul(x[2], y[23]);
   accum.mul(x[3], y[22]);
   accum.mul(x[4], y[21]);
   accum.mul(x[5], y[20]);
   accum.mul(x[6], y[19]);
   accum.mul(x[7], y[18]);
   accum.mul(x[8], y[17]);
   accum.mul(x[9], y[16]);
   accum.mul(x[10], y[15]);
   accum.mul(x[11], y[14]);
   accum.mul(x[12], y[13]);
   accum.mul(x[13], y[12]);
   accum.mul(x[14], y[11]);
   accum.mul(x[15], y[10]);
   accum.mul(x[16], y[9]);
   accum.mul(x[17], y[8]);
   accum.mul(x[18], y[7]);
   accum.mul(x[19], y[6]);
   accum.mul(x[20], y[5]);
   accum.mul(x[21], y[4]);
   accum.mul(x[22], y[3]);
   accum.mul(x[23], y[2]);
   z[25] = accum.extract();
   accum.mul(x[3], y[23]);
   accum.mul(x[4], y[22]);
   accum.mul(x[5], y[21]);
   accum.mul(x[6], y[20]);
   accum.mul(x[7], y[19]);
   accum.mul(x[8], y[18]);
   accum.mul(x[9], y[17]);
   accum.mul(x[10], y[16]);
   accum.mul(x[11], y[15]);
   accum.mul(x[12], y[14]);
   accum.mul(x[13], y[13]);
   accum.mul(x[14], y[12]);
   accum.mul(x[15], y[11]);
   accum.mul(x[16], y[10]);
   accum.mul(x[17], y[9]);
   accum.mul(x[18], y[8]);
   accum.mul(x[19], y[7]);
   accum.mul(x[20], y[6]);
   accum.mul(x[21], y[5]);
   accum.mul(x[22], y[4]);
   accum.mul(x[23], y[3]);
   z[26] = accum.extract();
   accum.mul(x[4], y[23]);
   accum.mul(x[5], y[22]);
   accum.mul(x[6], y[21]);
   accum.mul(x[7], y[20]);
   accum.mul(x[8], y[19]);
   accum.mul(x[9], y[18]);
   accum.mul(x[10], y[17]);
   accum.mul(x[11], y[16]);
   accum.mul(x[12], y[15]);
   accum.mul(x[13], y[14]);
   accum.mul(x[14], y[13]);
   accum.mul(x[15], y[12]);
   accum.mul(x[16], y[11]);
   accum.mul(x[17], y[10]);
   accum.mul(x[18], y[9]);
   accum.mul(x[19], y[8]);
   accum.mul(x[20], y[7]);
   accum.mul(x[21], y[6]);
   accum.mul(x[22], y[5]);
   accum.mul(x[23], y[4]);
   z[27] = accum.extract();
   accum.mul(x[5], y[23]);
   accum.mul(x[6], y[22]);
   accum.mul(x[7], y[21]);
   accum.mul(x[8], y[20]);
   accum.mul(x[9], y[19]);
   accum.mul(x[10], y[18]);
   accum.mul(x[11], y[17]);
   accum.mul(x[12], y[16]);
   accum.mul(x[13], y[15]);
   accum.mul(x[14], y[14]);
   accum.mul(x[15], y[13]);
   accum.mul(x[16], y[12]);
   accum.mul(x[17], y[11]);
   accum.mul(x[18], y[10]);
   accum.mul(x[19], y[9]);
   accum.mul(x[20], y[8]);
   accum.mul(x[21], y[7]);
   accum.mul(x[22], y[6]);
   accum.mul(x[23], y[5]);
   z[28] = accum.extract();
   accum.mul(x[6], y[23]);
   accum.mul(x[7], y[22]);
   accum.mul(x[8], y[21]);
   accum.mul(x[9], y[20]);
   accum.mul(x[10], y[19]);
   accum.mul(x[11], y[18]);
   accum.mul(x[12], y[17]);
   accum.mul(x[13], y[16]);
   accum.mul(x[14], y[15]);
   accum.mul(x[15], y[14]);
   accum.mul(x[16], y[13]);
   accum.mul(x[17], y[12]);
   accum.mul(x[18], y[11]);
   accum.mul(x[19], y[10]);
   accum.mul(x[20], y[9]);
   accum.mul(x[21], y[8]);
   accum.mul(x[22], y[7]);
   accum.mul(x[23], y[6]);
   z[29] = accum.extract();
   accum.mul(x[7], y[23]);
   accum.mul(x[8], y[22]);
   accum.mul(x[9], y[21]);
   accum.mul(x[10], y[20]);
   accum.mul(x[11], y[19]);
   accum.mul(x[12], y[18]);
   accum.mul(x[13], y[17]);
   accum.mul(x[14], y[16]);
   accum.mul(x[15], y[15]);
   accum.mul(x[16], y[14]);
   accum.mul(x[17], y[13]);
   accum.mul(x[18], y[12]);
   accum.mul(x[19], y[11]);
   accum.mul(x[20], y[10]);
   accum.mul(x[21], y[9]);
   accum.mul(x[22], y[8]);
   accum.mul(x[23], y[7]);
   z[30] = accum.extract();
   accum.mul(x[8], y[23]);
   accum.mul(x[9], y[22]);
   accum.mul(x[10], y[21]);
   accum.mul(x[11], y[20]);
   accum.mul(x[12], y[19]);
   accum.mul(x[13], y[18]);
   accum.mul(x[14], y[17]);
   accum.mul(x[15], y[16]);
   accum.mul(x[16], y[15]);
   accum.mul(x[17], y[14]);
   accum.mul(x[18], y[13]);
   accum.mul(x[19], y[12]);
   accum.mul(x[20], y[11]);
   accum.mul(x[21], y[10]);
   accum.mul(x[22], y[9]);
   accum.mul(x[23], y[8]);
   z[31] = accum.extract();
   accum.mul(x[9], y[23]);
   accum.mul(x[10], y[22]);
   accum.mul(x[11], y[21]);
   accum.mul(x[12], y[20]);
   accum.mul(x[13], y[19]);
   accum.mul(x[14], y[18]);
   accum.mul(x[15], y[17]);
   accum.mul(x[16], y[16]);
   accum.mul(x[17], y[15]);
   accum.mul(x[18], y[14]);
   accum.mul(x[19], y[13]);
   accum.mul(x[20], y[12]);
   accum.mul(x[21], y[11]);
   accum.mul(x[22], y[10]);
   accum.mul(x[23], y[9]);
   z[32] = accum.extract();
   accum.mul(x[10], y[23]);
   accum.mul(x[11], y[22]);
   accum.mul(x[12], y[21]);
   accum.mul(x[13], y[20]);
   accum.mul(x[14], y[19]);
   accum.mul(x[15], y[18]);
   accum.mul(x[16], y[17]);
   accum.mul(x[17], y[16]);
   accum.mul(x[18], y[15]);
   accum.mul(x[19], y[14]);
   accum.mul(x[20], y[13]);
   accum.mul(x[21], y[12]);
   accum.mul(x[22], y[11]);
   accum.mul(x[23], y[10]);
   z[33] = accum.extract();
   accum.mul(x[11], y[23]);
   accum.mul(x[12], y[22]);
   accum.mul(x[13], y[21]);
   accum.mul(x[14], y[20]);
   accum.mul(x[15], y[19]);
   accum.mul(x[16], y[18]);
   accum.mul(x[17], y[17]);
   accum.mul(x[18], y[16]);
   accum.mul(x[19], y[15]);
   accum.mul(x[20], y[14]);
   accum.mul(x[21], y[13]);
   accum.mul(x[22], y[12]);
   accum.mul(x[23], y[11]);
   z[34] = accum.extract();
   accum.mul(x[12], y[23]);
   accum.mul(x[13], y[22]);
   accum.mul(x[14], y[21]);
   accum.mul(x[15], y[20]);
   accum.mul(x[16], y[19]);
   accum.mul(x[17], y[18]);
   accum.mul(x[18], y[17]);
   accum.mul(x[19], y[16]);
   accum.mul(x[20], y[15]);
   accum.mul(x[21], y[14]);
   accum.mul(x[22], y[13]);
   accum.mul(x[23], y[12]);
   z[35] = accum.extract();
   accum.mul(x[13], y[23]);
   accum.mul(x[14], y[22]);
   accum.mul(x[15], y[21]);
   accum.mul(x[16], y[20]);
   accum.mul(x[17], y[19]);
   accum.mul(x[18], y[18]);
   accum.mul(x[19], y[17]);
   accum.mul(x[20], y[16]);
   accum.mul(x[21], y[15]);
   accum.mul(x[22], y[14]);
   accum.mul(x[23], y[13]);
   z[36] = accum.extract();
   accum.mul(x[14], y[23]);
   accum.mul(x[15], y[22]);
   accum.mul(x[16], y[21]);
   accum.mul(x[17], y[20]);
   accum.mul(x[18], y[19]);
   accum.mul(x[19], y[18]);
   accum.mul(x[20], y[17]);
   accum.mul(x[21], y[16]);
   accum.mul(x[22], y[15]);
   accum.mul(x[23], y[14]);
   z[37] = accum.extract();
   accum.mul(x[15], y[23]);
   accum.mul(x[16], y[22]);
   accum.mul(x[17], y[21]);
   accum.mul(x[18], y[20]);
   accum.mul(x[19], y[19]);
   accum.mul(x[20], y[18]);
   accum.mul(x[21], y[17]);
   accum.mul(x[22], y[16]);
   accum.mul(x[23], y[15]);
   z[38] = accum.extract();
   accum.mul(x[16], y[23]);
   accum.mul(x[17], y[22]);
   accum.mul(x[18], y[21]);
   accum.mul(x[19], y[20]);
   accum.mul(x[20], y[19]);
   accum.mul(x[21], y[18]);
   accum.mul(x[22], y[17]);
   accum.mul(x[23], y[16]);
   z[39] = accum.extract();
   accum.mul(x[17], y[23]);
   accum.mul(x[18], y[22]);
   accum.mul(x[19], y[21]);
   accum.mul(x[20], y[20]);
   accum.mul(x[21], y[19]);
   accum.mul(x[22], y[18]);
   accum.mul(x[23], y[17]);
   z[40] = accum.extract();
   accum.mul(x[18], y[23]);
   accum.mul(x[19], y[22]);
   accum.mul(x[20], y[21]);
   accum.mul(x[21], y[20]);
   accum.mul(x[22], y[19]);
   accum.mul(x[23], y[18]);
   z[41] = accum.extract();
   accum.mul(x[19], y[23]);
   accum.mul(x[20], y[22]);
   accum.mul(x[21], y[21]);
   accum.mul(x[22], y[20]);
   accum.mul(x[23], y[19]);
   z[42] = accum.extract();
   accum.mul(x[20], y[23]);
   accum.mul(x[21], y[22]);
   accum.mul(x[22], y[21]);
   accum.mul(x[23], y[20]);
   z[43] = accum.extract();
   accum.mul(x[21], y[23]);
   accum.mul(x[22], y[22]);
   accum.mul(x[23], y[21]);
   z[44] = accum.extract();
   accum.mul(x[22], y[23]);
   accum.mul(x[23], y[22]);
   z[45] = accum.extract();
   accum.mul(x[23], y[23]);
   z[46] = accum.extract();
   z[47] = accum.extract();
}

}  // namespace Botan
/*
* Multiplication and Squaring
* (C) 1999-2010,2018 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Simple O(N^2) Multiplication
*/
void basecase_mul(word z[], size_t z_size, const word x[], size_t x_size, const word y[], size_t y_size) {
   if(z_size < x_size + y_size) {
      throw Invalid_Argument("basecase_mul z_size too small");
   }

   const size_t x_size_8 = x_size - (x_size % 8);

   clear_mem(z, z_size);

   for(size_t i = 0; i != y_size; ++i) {
      const word y_i = y[i];

      word carry = 0;

      for(size_t j = 0; j != x_size_8; j += 8) {
         carry = word8_madd3(z + i + j, x + j, y_i, carry);
      }

      for(size_t j = x_size_8; j != x_size; ++j) {
         z[i + j] = word_madd3(x[j], y_i, z[i + j], &carry);
      }

      z[x_size + i] = carry;
   }
}

void basecase_sqr(word z[], size_t z_size, const word x[], size_t x_size) {
   if(z_size < 2 * x_size) {
      throw Invalid_Argument("basecase_sqr z_size too small");
   }

   const size_t x_size_8 = x_size - (x_size % 8);

   clear_mem(z, z_size);

   for(size_t i = 0; i != x_size; ++i) {
      const word x_i = x[i];

      word carry = 0;

      for(size_t j = 0; j != x_size_8; j += 8) {
         carry = word8_madd3(z + i + j, x + j, x_i, carry);
      }

      for(size_t j = x_size_8; j != x_size; ++j) {
         z[i + j] = word_madd3(x[j], x_i, z[i + j], &carry);
      }

      z[x_size + i] = carry;
   }
}

namespace {

const size_t KARATSUBA_MULTIPLY_THRESHOLD = 32;
const size_t KARATSUBA_SQUARE_THRESHOLD = 32;

/*
* Karatsuba Multiplication Operation
*/
void karatsuba_mul(word z[], const word x[], const word y[], size_t N, word workspace[]) {
   if(N < KARATSUBA_MULTIPLY_THRESHOLD || N % 2 != 0) {
      switch(N) {
         case 6:
            return bigint_comba_mul6(z, x, y);
         case 8:
            return bigint_comba_mul8(z, x, y);
         case 9:
            return bigint_comba_mul9(z, x, y);
         case 16:
            return bigint_comba_mul16(z, x, y);
         case 24:
            return bigint_comba_mul24(z, x, y);
         default:
            return basecase_mul(z, 2 * N, x, N, y, N);
      }
   }

   const size_t N2 = N / 2;

   const word* x0 = x;
   const word* x1 = x + N2;
   const word* y0 = y;
   const word* y1 = y + N2;
   word* z0 = z;
   word* z1 = z + N;

   word* ws0 = workspace;
   word* ws1 = workspace + N;

   clear_mem(workspace, 2 * N);

   /*
   * If either of cmp0 or cmp1 is zero then z0 or z1 resp is zero here,
   * resulting in a no-op - z0*z1 will be equal to zero so we don't need to do
   * anything, clear_mem above already set the correct result.
   *
   * However we ignore the result of the comparisons and always perform the
   * subtractions and recursively multiply to avoid the timing channel.
   */

   // First compute (X_lo - X_hi)*(Y_hi - Y_lo)
   const auto cmp0 = bigint_sub_abs(z0, x0, x1, N2, workspace);
   const auto cmp1 = bigint_sub_abs(z1, y1, y0, N2, workspace);
   const auto neg_mask = ~(cmp0 ^ cmp1);

   karatsuba_mul(ws0, z0, z1, N2, ws1);

   // Compute X_lo * Y_lo
   karatsuba_mul(z0, x0, y0, N2, ws1);

   // Compute X_hi * Y_hi
   karatsuba_mul(z1, x1, y1, N2, ws1);

   const word ws_carry = bigint_add3(ws1, z0, N, z1, N);
   word z_carry = bigint_add2(z + N2, N, ws1, N);

   z_carry += bigint_add2(z + N + N2, N2, &ws_carry, 1);
   bigint_add2(z + N + N2, N2, &z_carry, 1);

   clear_mem(workspace + N, N2);

   bigint_cnd_add(neg_mask.value(), z + N2, workspace, 2 * N - N2);
   bigint_cnd_sub((~neg_mask).value(), z + N2, workspace, 2 * N - N2);
}

/*
* Karatsuba Squaring Operation
*/
void karatsuba_sqr(word z[], const word x[], size_t N, word workspace[]) {
   if(N < KARATSUBA_SQUARE_THRESHOLD || N % 2 != 0) {
      switch(N) {
         case 6:
            return bigint_comba_sqr6(z, x);
         case 8:
            return bigint_comba_sqr8(z, x);
         case 9:
            return bigint_comba_sqr9(z, x);
         case 16:
            return bigint_comba_sqr16(z, x);
         case 24:
            return bigint_comba_sqr24(z, x);
         default:
            return basecase_sqr(z, 2 * N, x, N);
      }
   }

   const size_t N2 = N / 2;

   const word* x0 = x;
   const word* x1 = x + N2;
   word* z0 = z;
   word* z1 = z + N;

   word* ws0 = workspace;
   word* ws1 = workspace + N;

   clear_mem(workspace, 2 * N);

   // See comment in karatsuba_mul
   bigint_sub_abs(z0, x0, x1, N2, workspace);
   karatsuba_sqr(ws0, z0, N2, ws1);

   karatsuba_sqr(z0, x0, N2, ws1);
   karatsuba_sqr(z1, x1, N2, ws1);

   const word ws_carry = bigint_add3(ws1, z0, N, z1, N);
   word z_carry = bigint_add2(z + N2, N, ws1, N);

   z_carry += bigint_add2(z + N + N2, N2, &ws_carry, 1);
   bigint_add2(z + N + N2, N2, &z_carry, 1);

   /*
   * This is only actually required if cmp (result of bigint_sub_abs) is != 0,
   * however if cmp==0 then ws0[0:N] == 0 and avoiding the jump hides a
   * timing channel.
   */
   bigint_sub2(z + N2, 2 * N - N2, ws0, N);
}

/*
* Pick a good size for the Karatsuba multiply
*/
size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw, size_t y_size, size_t y_sw) {
   if(x_sw > x_size || x_sw > y_size || y_sw > x_size || y_sw > y_size) {
      return 0;
   }

   if(((x_size == x_sw) && (x_size % 2 != 0)) || ((y_size == y_sw) && (y_size % 2 != 0))) {
      return 0;
   }

   const size_t start = (x_sw > y_sw) ? x_sw : y_sw;
   const size_t end = (x_size < y_size) ? x_size : y_size;

   if(start == end) {
      if(start % 2 != 0) {
         return 0;
      }
      return start;
   }

   for(size_t j = start; j <= end; ++j) {
      if(j % 2 != 0) {
         continue;
      }

      if(2 * j > z_size) {
         return 0;
      }

      if(x_sw <= j && j <= x_size && y_sw <= j && j <= y_size) {
         if(j % 4 == 2 && (j + 2) <= x_size && (j + 2) <= y_size && 2 * (j + 2) <= z_size) {
            return j + 2;
         }
         return j;
      }
   }

   return 0;
}

/*
* Pick a good size for the Karatsuba squaring
*/
size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw) {
   if(x_sw == x_size) {
      if(x_sw % 2 != 0) {
         return 0;
      }
      return x_sw;
   }

   for(size_t j = x_sw; j <= x_size; ++j) {
      if(j % 2 != 0) {
         continue;
      }

      if(2 * j > z_size) {
         return 0;
      }

      if(j % 4 == 2 && (j + 2) <= x_size && 2 * (j + 2) <= z_size) {
         return j + 2;
      }
      return j;
   }

   return 0;
}

template <size_t SZ>
inline bool sized_for_comba_mul(size_t x_sw, size_t x_size, size_t y_sw, size_t y_size, size_t z_size) {
   return (x_sw <= SZ && x_size >= SZ && y_sw <= SZ && y_size >= SZ && z_size >= 2 * SZ);
}

template <size_t SZ>
inline bool sized_for_comba_sqr(size_t x_sw, size_t x_size, size_t z_size) {
   return (x_sw <= SZ && x_size >= SZ && z_size >= 2 * SZ);
}

}  // namespace

void bigint_mul(word z[],
                size_t z_size,
                const word x[],
                size_t x_size,
                size_t x_sw,
                const word y[],
                size_t y_size,
                size_t y_sw,
                word workspace[],
                size_t ws_size) {
   clear_mem(z, z_size);

   if(x_sw == 1) {
      bigint_linmul3(z, y, y_sw, x[0]);
   } else if(y_sw == 1) {
      bigint_linmul3(z, x, x_sw, y[0]);
   } else if(sized_for_comba_mul<4>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul4(z, x, y);
   } else if(sized_for_comba_mul<6>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul6(z, x, y);
   } else if(sized_for_comba_mul<8>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul8(z, x, y);
   } else if(sized_for_comba_mul<9>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul9(z, x, y);
   } else if(sized_for_comba_mul<16>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul16(z, x, y);
   } else if(sized_for_comba_mul<24>(x_sw, x_size, y_sw, y_size, z_size)) {
      bigint_comba_mul24(z, x, y);
   } else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || y_sw < KARATSUBA_MULTIPLY_THRESHOLD || workspace == nullptr) {
      basecase_mul(z, z_size, x, x_sw, y, y_sw);
   } else {
      const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw);

      if(N > 0 && z_size >= 2 * N && ws_size >= 2 * N) {
         karatsuba_mul(z, x, y, N, workspace);
      } else {
         basecase_mul(z, z_size, x, x_sw, y, y_sw);
      }
   }
}

/*
* Squaring Algorithm Dispatcher
*/
void bigint_sqr(word z[], size_t z_size, const word x[], size_t x_size, size_t x_sw, word workspace[], size_t ws_size) {
   clear_mem(z, z_size);

   BOTAN_ASSERT(z_size / 2 >= x_sw, "Output size is sufficient");

   if(x_sw == 1) {
      bigint_linmul3(z, x, x_sw, x[0]);
   } else if(sized_for_comba_sqr<4>(x_sw, x_size, z_size)) {
      bigint_comba_sqr4(z, x);
   } else if(sized_for_comba_sqr<6>(x_sw, x_size, z_size)) {
      bigint_comba_sqr6(z, x);
   } else if(sized_for_comba_sqr<8>(x_sw, x_size, z_size)) {
      bigint_comba_sqr8(z, x);
   } else if(sized_for_comba_sqr<9>(x_sw, x_size, z_size)) {
      bigint_comba_sqr9(z, x);
   } else if(sized_for_comba_sqr<16>(x_sw, x_size, z_size)) {
      bigint_comba_sqr16(z, x);
   } else if(sized_for_comba_sqr<24>(x_sw, x_size, z_size)) {
      bigint_comba_sqr24(z, x);
   } else if(x_size < KARATSUBA_SQUARE_THRESHOLD || workspace == nullptr) {
      basecase_sqr(z, z_size, x, x_sw);
   } else {
      const size_t N = karatsuba_size(z_size, x_size, x_sw);

      if(N > 0 && z_size >= 2 * N && ws_size >= 2 * N) {
         karatsuba_sqr(z, x, N, workspace);
      } else {
         basecase_sqr(z, z_size, x, x_sw);
      }
   }
}

}  // namespace Botan
/*
* Montgomery Reduction
* (C) 1999-2011 Jack Lloyd
*     2006 Luca Piccarreta
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Montgomery reduction - product scanning form
*
* Algorithm 5 from "Energy-Efficient Software Implementation of Long
* Integer Modular Arithmetic"
* (https://www.iacr.org/archive/ches2005/006.pdf)
*
* See also
*
* https://eprint.iacr.org/2013/882.pdf
* https://www.microsoft.com/en-us/research/wp-content/uploads/1996/01/j37acmon.pdf
*/
void bigint_monty_redc_generic(
   word r[], const word z[], size_t z_size, const word p[], size_t p_size, word p_dash, word ws[]) {
   BOTAN_ARG_CHECK(z_size >= 2 * p_size && p_size > 0, "Invalid sizes for bigint_monty_redc_generic");

   word3<word> accum;

   accum.add(z[0]);

   ws[0] = accum.monty_step(p[0], p_dash);

   for(size_t i = 1; i != p_size; ++i) {
      for(size_t j = 0; j < i; ++j) {
         accum.mul(ws[j], p[i - j]);
      }

      accum.add(z[i]);
      ws[i] = accum.monty_step(p[0], p_dash);
   }

   for(size_t i = 0; i != p_size - 1; ++i) {
      for(size_t j = i + 1; j != p_size; ++j) {
         accum.mul(ws[j], p[p_size + i - j]);
      }

      accum.add(z[p_size + i]);
      ws[i] = accum.extract();
   }

   accum.add(z[2 * p_size - 1]);

   ws[p_size - 1] = accum.extract();
   // w1 is the final part, which is not stored in the workspace
   const word w1 = accum.extract();

   /*
   * The result might need to be reduced mod p. To avoid a timing
   * channel, always perform the subtraction. If in the compution
   * of x - p a borrow is required then x was already < p.
   *
   * x starts at ws[0] and is p_size bytes long plus a possible high
   * digit left over in w1.
   *
   * x - p starts at z[0] and is also p_size bytes long
   *
   * If borrow was set after the subtraction, then x was already less
   * than p and the subtraction was not needed. In that case overwrite
   * z[0:p_size] with the original x in ws[0:p_size].
   *
   * We only copy out p_size in the final step because we know
   * the Montgomery result is < P
   */

   bigint_monty_maybe_sub(p_size, r, w1, ws, p);
}

}  // namespace Botan
/*
* This file was automatically generated by ./src/scripts/dev_tools/gen_mp_monty.py on 2025-02-01
* All manual changes will be lost. Edit the script instead.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

void bigint_monty_redc_4(word r[4], const word z[8], const word p[4], word p_dash, word ws[4]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.add(z[5]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[3]);
   accum.add(z[6]);
   ws[2] = accum.extract();
   accum.add(z[7]);
   ws[3] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<4>(r, w1, ws, p);
}

void bigint_monty_redc_6(word r[6], const word z[12], const word p[6], word p_dash, word ws[6]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.add(z[7]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.add(z[8]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.add(z[9]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[5]);
   accum.add(z[10]);
   ws[4] = accum.extract();
   accum.add(z[11]);
   ws[5] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<6>(r, w1, ws, p);
}

void bigint_monty_redc_8(word r[8], const word z[16], const word p[8], word p_dash, word ws[8]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[6]);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[6] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[7]);
   accum.mul(ws[1], p[6]);
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.mul(ws[6], p[1]);
   accum.add(z[7]);
   ws[7] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[7]);
   accum.mul(ws[2], p[6]);
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.mul(ws[6], p[2]);
   accum.mul(ws[7], p[1]);
   accum.add(z[8]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[7]);
   accum.mul(ws[3], p[6]);
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.mul(ws[6], p[3]);
   accum.mul(ws[7], p[2]);
   accum.add(z[9]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[7]);
   accum.mul(ws[4], p[6]);
   accum.mul(ws[5], p[5]);
   accum.mul(ws[6], p[4]);
   accum.mul(ws[7], p[3]);
   accum.add(z[10]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[7]);
   accum.mul(ws[5], p[6]);
   accum.mul(ws[6], p[5]);
   accum.mul(ws[7], p[4]);
   accum.add(z[11]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[7]);
   accum.mul(ws[6], p[6]);
   accum.mul(ws[7], p[5]);
   accum.add(z[12]);
   ws[4] = accum.extract();
   accum.mul(ws[6], p[7]);
   accum.mul(ws[7], p[6]);
   accum.add(z[13]);
   ws[5] = accum.extract();
   accum.mul(ws[7], p[7]);
   accum.add(z[14]);
   ws[6] = accum.extract();
   accum.add(z[15]);
   ws[7] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<8>(r, w1, ws, p);
}

void bigint_monty_redc_12(word r[12], const word z[24], const word p[12], word p_dash, word ws[12]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[6]);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[6] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[7]);
   accum.mul(ws[1], p[6]);
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.mul(ws[6], p[1]);
   accum.add(z[7]);
   ws[7] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[8]);
   accum.mul(ws[1], p[7]);
   accum.mul(ws[2], p[6]);
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.mul(ws[6], p[2]);
   accum.mul(ws[7], p[1]);
   accum.add(z[8]);
   ws[8] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[9]);
   accum.mul(ws[1], p[8]);
   accum.mul(ws[2], p[7]);
   accum.mul(ws[3], p[6]);
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.mul(ws[6], p[3]);
   accum.mul(ws[7], p[2]);
   accum.mul(ws[8], p[1]);
   accum.add(z[9]);
   ws[9] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[10]);
   accum.mul(ws[1], p[9]);
   accum.mul(ws[2], p[8]);
   accum.mul(ws[3], p[7]);
   accum.mul(ws[4], p[6]);
   accum.mul(ws[5], p[5]);
   accum.mul(ws[6], p[4]);
   accum.mul(ws[7], p[3]);
   accum.mul(ws[8], p[2]);
   accum.mul(ws[9], p[1]);
   accum.add(z[10]);
   ws[10] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[11]);
   accum.mul(ws[1], p[10]);
   accum.mul(ws[2], p[9]);
   accum.mul(ws[3], p[8]);
   accum.mul(ws[4], p[7]);
   accum.mul(ws[5], p[6]);
   accum.mul(ws[6], p[5]);
   accum.mul(ws[7], p[4]);
   accum.mul(ws[8], p[3]);
   accum.mul(ws[9], p[2]);
   accum.mul(ws[10], p[1]);
   accum.add(z[11]);
   ws[11] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[11]);
   accum.mul(ws[2], p[10]);
   accum.mul(ws[3], p[9]);
   accum.mul(ws[4], p[8]);
   accum.mul(ws[5], p[7]);
   accum.mul(ws[6], p[6]);
   accum.mul(ws[7], p[5]);
   accum.mul(ws[8], p[4]);
   accum.mul(ws[9], p[3]);
   accum.mul(ws[10], p[2]);
   accum.mul(ws[11], p[1]);
   accum.add(z[12]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[11]);
   accum.mul(ws[3], p[10]);
   accum.mul(ws[4], p[9]);
   accum.mul(ws[5], p[8]);
   accum.mul(ws[6], p[7]);
   accum.mul(ws[7], p[6]);
   accum.mul(ws[8], p[5]);
   accum.mul(ws[9], p[4]);
   accum.mul(ws[10], p[3]);
   accum.mul(ws[11], p[2]);
   accum.add(z[13]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[11]);
   accum.mul(ws[4], p[10]);
   accum.mul(ws[5], p[9]);
   accum.mul(ws[6], p[8]);
   accum.mul(ws[7], p[7]);
   accum.mul(ws[8], p[6]);
   accum.mul(ws[9], p[5]);
   accum.mul(ws[10], p[4]);
   accum.mul(ws[11], p[3]);
   accum.add(z[14]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[11]);
   accum.mul(ws[5], p[10]);
   accum.mul(ws[6], p[9]);
   accum.mul(ws[7], p[8]);
   accum.mul(ws[8], p[7]);
   accum.mul(ws[9], p[6]);
   accum.mul(ws[10], p[5]);
   accum.mul(ws[11], p[4]);
   accum.add(z[15]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[11]);
   accum.mul(ws[6], p[10]);
   accum.mul(ws[7], p[9]);
   accum.mul(ws[8], p[8]);
   accum.mul(ws[9], p[7]);
   accum.mul(ws[10], p[6]);
   accum.mul(ws[11], p[5]);
   accum.add(z[16]);
   ws[4] = accum.extract();
   accum.mul(ws[6], p[11]);
   accum.mul(ws[7], p[10]);
   accum.mul(ws[8], p[9]);
   accum.mul(ws[9], p[8]);
   accum.mul(ws[10], p[7]);
   accum.mul(ws[11], p[6]);
   accum.add(z[17]);
   ws[5] = accum.extract();
   accum.mul(ws[7], p[11]);
   accum.mul(ws[8], p[10]);
   accum.mul(ws[9], p[9]);
   accum.mul(ws[10], p[8]);
   accum.mul(ws[11], p[7]);
   accum.add(z[18]);
   ws[6] = accum.extract();
   accum.mul(ws[8], p[11]);
   accum.mul(ws[9], p[10]);
   accum.mul(ws[10], p[9]);
   accum.mul(ws[11], p[8]);
   accum.add(z[19]);
   ws[7] = accum.extract();
   accum.mul(ws[9], p[11]);
   accum.mul(ws[10], p[10]);
   accum.mul(ws[11], p[9]);
   accum.add(z[20]);
   ws[8] = accum.extract();
   accum.mul(ws[10], p[11]);
   accum.mul(ws[11], p[10]);
   accum.add(z[21]);
   ws[9] = accum.extract();
   accum.mul(ws[11], p[11]);
   accum.add(z[22]);
   ws[10] = accum.extract();
   accum.add(z[23]);
   ws[11] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<12>(r, w1, ws, p);
}

void bigint_monty_redc_16(word r[16], const word z[32], const word p[16], word p_dash, word ws[16]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[6]);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[6] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[7]);
   accum.mul(ws[1], p[6]);
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.mul(ws[6], p[1]);
   accum.add(z[7]);
   ws[7] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[8]);
   accum.mul(ws[1], p[7]);
   accum.mul(ws[2], p[6]);
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.mul(ws[6], p[2]);
   accum.mul(ws[7], p[1]);
   accum.add(z[8]);
   ws[8] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[9]);
   accum.mul(ws[1], p[8]);
   accum.mul(ws[2], p[7]);
   accum.mul(ws[3], p[6]);
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.mul(ws[6], p[3]);
   accum.mul(ws[7], p[2]);
   accum.mul(ws[8], p[1]);
   accum.add(z[9]);
   ws[9] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[10]);
   accum.mul(ws[1], p[9]);
   accum.mul(ws[2], p[8]);
   accum.mul(ws[3], p[7]);
   accum.mul(ws[4], p[6]);
   accum.mul(ws[5], p[5]);
   accum.mul(ws[6], p[4]);
   accum.mul(ws[7], p[3]);
   accum.mul(ws[8], p[2]);
   accum.mul(ws[9], p[1]);
   accum.add(z[10]);
   ws[10] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[11]);
   accum.mul(ws[1], p[10]);
   accum.mul(ws[2], p[9]);
   accum.mul(ws[3], p[8]);
   accum.mul(ws[4], p[7]);
   accum.mul(ws[5], p[6]);
   accum.mul(ws[6], p[5]);
   accum.mul(ws[7], p[4]);
   accum.mul(ws[8], p[3]);
   accum.mul(ws[9], p[2]);
   accum.mul(ws[10], p[1]);
   accum.add(z[11]);
   ws[11] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[12]);
   accum.mul(ws[1], p[11]);
   accum.mul(ws[2], p[10]);
   accum.mul(ws[3], p[9]);
   accum.mul(ws[4], p[8]);
   accum.mul(ws[5], p[7]);
   accum.mul(ws[6], p[6]);
   accum.mul(ws[7], p[5]);
   accum.mul(ws[8], p[4]);
   accum.mul(ws[9], p[3]);
   accum.mul(ws[10], p[2]);
   accum.mul(ws[11], p[1]);
   accum.add(z[12]);
   ws[12] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[13]);
   accum.mul(ws[1], p[12]);
   accum.mul(ws[2], p[11]);
   accum.mul(ws[3], p[10]);
   accum.mul(ws[4], p[9]);
   accum.mul(ws[5], p[8]);
   accum.mul(ws[6], p[7]);
   accum.mul(ws[7], p[6]);
   accum.mul(ws[8], p[5]);
   accum.mul(ws[9], p[4]);
   accum.mul(ws[10], p[3]);
   accum.mul(ws[11], p[2]);
   accum.mul(ws[12], p[1]);
   accum.add(z[13]);
   ws[13] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[14]);
   accum.mul(ws[1], p[13]);
   accum.mul(ws[2], p[12]);
   accum.mul(ws[3], p[11]);
   accum.mul(ws[4], p[10]);
   accum.mul(ws[5], p[9]);
   accum.mul(ws[6], p[8]);
   accum.mul(ws[7], p[7]);
   accum.mul(ws[8], p[6]);
   accum.mul(ws[9], p[5]);
   accum.mul(ws[10], p[4]);
   accum.mul(ws[11], p[3]);
   accum.mul(ws[12], p[2]);
   accum.mul(ws[13], p[1]);
   accum.add(z[14]);
   ws[14] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[15]);
   accum.mul(ws[1], p[14]);
   accum.mul(ws[2], p[13]);
   accum.mul(ws[3], p[12]);
   accum.mul(ws[4], p[11]);
   accum.mul(ws[5], p[10]);
   accum.mul(ws[6], p[9]);
   accum.mul(ws[7], p[8]);
   accum.mul(ws[8], p[7]);
   accum.mul(ws[9], p[6]);
   accum.mul(ws[10], p[5]);
   accum.mul(ws[11], p[4]);
   accum.mul(ws[12], p[3]);
   accum.mul(ws[13], p[2]);
   accum.mul(ws[14], p[1]);
   accum.add(z[15]);
   ws[15] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[15]);
   accum.mul(ws[2], p[14]);
   accum.mul(ws[3], p[13]);
   accum.mul(ws[4], p[12]);
   accum.mul(ws[5], p[11]);
   accum.mul(ws[6], p[10]);
   accum.mul(ws[7], p[9]);
   accum.mul(ws[8], p[8]);
   accum.mul(ws[9], p[7]);
   accum.mul(ws[10], p[6]);
   accum.mul(ws[11], p[5]);
   accum.mul(ws[12], p[4]);
   accum.mul(ws[13], p[3]);
   accum.mul(ws[14], p[2]);
   accum.mul(ws[15], p[1]);
   accum.add(z[16]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[15]);
   accum.mul(ws[3], p[14]);
   accum.mul(ws[4], p[13]);
   accum.mul(ws[5], p[12]);
   accum.mul(ws[6], p[11]);
   accum.mul(ws[7], p[10]);
   accum.mul(ws[8], p[9]);
   accum.mul(ws[9], p[8]);
   accum.mul(ws[10], p[7]);
   accum.mul(ws[11], p[6]);
   accum.mul(ws[12], p[5]);
   accum.mul(ws[13], p[4]);
   accum.mul(ws[14], p[3]);
   accum.mul(ws[15], p[2]);
   accum.add(z[17]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[15]);
   accum.mul(ws[4], p[14]);
   accum.mul(ws[5], p[13]);
   accum.mul(ws[6], p[12]);
   accum.mul(ws[7], p[11]);
   accum.mul(ws[8], p[10]);
   accum.mul(ws[9], p[9]);
   accum.mul(ws[10], p[8]);
   accum.mul(ws[11], p[7]);
   accum.mul(ws[12], p[6]);
   accum.mul(ws[13], p[5]);
   accum.mul(ws[14], p[4]);
   accum.mul(ws[15], p[3]);
   accum.add(z[18]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[15]);
   accum.mul(ws[5], p[14]);
   accum.mul(ws[6], p[13]);
   accum.mul(ws[7], p[12]);
   accum.mul(ws[8], p[11]);
   accum.mul(ws[9], p[10]);
   accum.mul(ws[10], p[9]);
   accum.mul(ws[11], p[8]);
   accum.mul(ws[12], p[7]);
   accum.mul(ws[13], p[6]);
   accum.mul(ws[14], p[5]);
   accum.mul(ws[15], p[4]);
   accum.add(z[19]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[15]);
   accum.mul(ws[6], p[14]);
   accum.mul(ws[7], p[13]);
   accum.mul(ws[8], p[12]);
   accum.mul(ws[9], p[11]);
   accum.mul(ws[10], p[10]);
   accum.mul(ws[11], p[9]);
   accum.mul(ws[12], p[8]);
   accum.mul(ws[13], p[7]);
   accum.mul(ws[14], p[6]);
   accum.mul(ws[15], p[5]);
   accum.add(z[20]);
   ws[4] = accum.extract();
   accum.mul(ws[6], p[15]);
   accum.mul(ws[7], p[14]);
   accum.mul(ws[8], p[13]);
   accum.mul(ws[9], p[12]);
   accum.mul(ws[10], p[11]);
   accum.mul(ws[11], p[10]);
   accum.mul(ws[12], p[9]);
   accum.mul(ws[13], p[8]);
   accum.mul(ws[14], p[7]);
   accum.mul(ws[15], p[6]);
   accum.add(z[21]);
   ws[5] = accum.extract();
   accum.mul(ws[7], p[15]);
   accum.mul(ws[8], p[14]);
   accum.mul(ws[9], p[13]);
   accum.mul(ws[10], p[12]);
   accum.mul(ws[11], p[11]);
   accum.mul(ws[12], p[10]);
   accum.mul(ws[13], p[9]);
   accum.mul(ws[14], p[8]);
   accum.mul(ws[15], p[7]);
   accum.add(z[22]);
   ws[6] = accum.extract();
   accum.mul(ws[8], p[15]);
   accum.mul(ws[9], p[14]);
   accum.mul(ws[10], p[13]);
   accum.mul(ws[11], p[12]);
   accum.mul(ws[12], p[11]);
   accum.mul(ws[13], p[10]);
   accum.mul(ws[14], p[9]);
   accum.mul(ws[15], p[8]);
   accum.add(z[23]);
   ws[7] = accum.extract();
   accum.mul(ws[9], p[15]);
   accum.mul(ws[10], p[14]);
   accum.mul(ws[11], p[13]);
   accum.mul(ws[12], p[12]);
   accum.mul(ws[13], p[11]);
   accum.mul(ws[14], p[10]);
   accum.mul(ws[15], p[9]);
   accum.add(z[24]);
   ws[8] = accum.extract();
   accum.mul(ws[10], p[15]);
   accum.mul(ws[11], p[14]);
   accum.mul(ws[12], p[13]);
   accum.mul(ws[13], p[12]);
   accum.mul(ws[14], p[11]);
   accum.mul(ws[15], p[10]);
   accum.add(z[25]);
   ws[9] = accum.extract();
   accum.mul(ws[11], p[15]);
   accum.mul(ws[12], p[14]);
   accum.mul(ws[13], p[13]);
   accum.mul(ws[14], p[12]);
   accum.mul(ws[15], p[11]);
   accum.add(z[26]);
   ws[10] = accum.extract();
   accum.mul(ws[12], p[15]);
   accum.mul(ws[13], p[14]);
   accum.mul(ws[14], p[13]);
   accum.mul(ws[15], p[12]);
   accum.add(z[27]);
   ws[11] = accum.extract();
   accum.mul(ws[13], p[15]);
   accum.mul(ws[14], p[14]);
   accum.mul(ws[15], p[13]);
   accum.add(z[28]);
   ws[12] = accum.extract();
   accum.mul(ws[14], p[15]);
   accum.mul(ws[15], p[14]);
   accum.add(z[29]);
   ws[13] = accum.extract();
   accum.mul(ws[15], p[15]);
   accum.add(z[30]);
   ws[14] = accum.extract();
   accum.add(z[31]);
   ws[15] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<16>(r, w1, ws, p);
}

void bigint_monty_redc_24(word r[24], const word z[48], const word p[24], word p_dash, word ws[24]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[6]);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[6] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[7]);
   accum.mul(ws[1], p[6]);
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.mul(ws[6], p[1]);
   accum.add(z[7]);
   ws[7] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[8]);
   accum.mul(ws[1], p[7]);
   accum.mul(ws[2], p[6]);
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.mul(ws[6], p[2]);
   accum.mul(ws[7], p[1]);
   accum.add(z[8]);
   ws[8] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[9]);
   accum.mul(ws[1], p[8]);
   accum.mul(ws[2], p[7]);
   accum.mul(ws[3], p[6]);
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.mul(ws[6], p[3]);
   accum.mul(ws[7], p[2]);
   accum.mul(ws[8], p[1]);
   accum.add(z[9]);
   ws[9] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[10]);
   accum.mul(ws[1], p[9]);
   accum.mul(ws[2], p[8]);
   accum.mul(ws[3], p[7]);
   accum.mul(ws[4], p[6]);
   accum.mul(ws[5], p[5]);
   accum.mul(ws[6], p[4]);
   accum.mul(ws[7], p[3]);
   accum.mul(ws[8], p[2]);
   accum.mul(ws[9], p[1]);
   accum.add(z[10]);
   ws[10] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[11]);
   accum.mul(ws[1], p[10]);
   accum.mul(ws[2], p[9]);
   accum.mul(ws[3], p[8]);
   accum.mul(ws[4], p[7]);
   accum.mul(ws[5], p[6]);
   accum.mul(ws[6], p[5]);
   accum.mul(ws[7], p[4]);
   accum.mul(ws[8], p[3]);
   accum.mul(ws[9], p[2]);
   accum.mul(ws[10], p[1]);
   accum.add(z[11]);
   ws[11] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[12]);
   accum.mul(ws[1], p[11]);
   accum.mul(ws[2], p[10]);
   accum.mul(ws[3], p[9]);
   accum.mul(ws[4], p[8]);
   accum.mul(ws[5], p[7]);
   accum.mul(ws[6], p[6]);
   accum.mul(ws[7], p[5]);
   accum.mul(ws[8], p[4]);
   accum.mul(ws[9], p[3]);
   accum.mul(ws[10], p[2]);
   accum.mul(ws[11], p[1]);
   accum.add(z[12]);
   ws[12] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[13]);
   accum.mul(ws[1], p[12]);
   accum.mul(ws[2], p[11]);
   accum.mul(ws[3], p[10]);
   accum.mul(ws[4], p[9]);
   accum.mul(ws[5], p[8]);
   accum.mul(ws[6], p[7]);
   accum.mul(ws[7], p[6]);
   accum.mul(ws[8], p[5]);
   accum.mul(ws[9], p[4]);
   accum.mul(ws[10], p[3]);
   accum.mul(ws[11], p[2]);
   accum.mul(ws[12], p[1]);
   accum.add(z[13]);
   ws[13] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[14]);
   accum.mul(ws[1], p[13]);
   accum.mul(ws[2], p[12]);
   accum.mul(ws[3], p[11]);
   accum.mul(ws[4], p[10]);
   accum.mul(ws[5], p[9]);
   accum.mul(ws[6], p[8]);
   accum.mul(ws[7], p[7]);
   accum.mul(ws[8], p[6]);
   accum.mul(ws[9], p[5]);
   accum.mul(ws[10], p[4]);
   accum.mul(ws[11], p[3]);
   accum.mul(ws[12], p[2]);
   accum.mul(ws[13], p[1]);
   accum.add(z[14]);
   ws[14] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[15]);
   accum.mul(ws[1], p[14]);
   accum.mul(ws[2], p[13]);
   accum.mul(ws[3], p[12]);
   accum.mul(ws[4], p[11]);
   accum.mul(ws[5], p[10]);
   accum.mul(ws[6], p[9]);
   accum.mul(ws[7], p[8]);
   accum.mul(ws[8], p[7]);
   accum.mul(ws[9], p[6]);
   accum.mul(ws[10], p[5]);
   accum.mul(ws[11], p[4]);
   accum.mul(ws[12], p[3]);
   accum.mul(ws[13], p[2]);
   accum.mul(ws[14], p[1]);
   accum.add(z[15]);
   ws[15] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[16]);
   accum.mul(ws[1], p[15]);
   accum.mul(ws[2], p[14]);
   accum.mul(ws[3], p[13]);
   accum.mul(ws[4], p[12]);
   accum.mul(ws[5], p[11]);
   accum.mul(ws[6], p[10]);
   accum.mul(ws[7], p[9]);
   accum.mul(ws[8], p[8]);
   accum.mul(ws[9], p[7]);
   accum.mul(ws[10], p[6]);
   accum.mul(ws[11], p[5]);
   accum.mul(ws[12], p[4]);
   accum.mul(ws[13], p[3]);
   accum.mul(ws[14], p[2]);
   accum.mul(ws[15], p[1]);
   accum.add(z[16]);
   ws[16] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[17]);
   accum.mul(ws[1], p[16]);
   accum.mul(ws[2], p[15]);
   accum.mul(ws[3], p[14]);
   accum.mul(ws[4], p[13]);
   accum.mul(ws[5], p[12]);
   accum.mul(ws[6], p[11]);
   accum.mul(ws[7], p[10]);
   accum.mul(ws[8], p[9]);
   accum.mul(ws[9], p[8]);
   accum.mul(ws[10], p[7]);
   accum.mul(ws[11], p[6]);
   accum.mul(ws[12], p[5]);
   accum.mul(ws[13], p[4]);
   accum.mul(ws[14], p[3]);
   accum.mul(ws[15], p[2]);
   accum.mul(ws[16], p[1]);
   accum.add(z[17]);
   ws[17] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[18]);
   accum.mul(ws[1], p[17]);
   accum.mul(ws[2], p[16]);
   accum.mul(ws[3], p[15]);
   accum.mul(ws[4], p[14]);
   accum.mul(ws[5], p[13]);
   accum.mul(ws[6], p[12]);
   accum.mul(ws[7], p[11]);
   accum.mul(ws[8], p[10]);
   accum.mul(ws[9], p[9]);
   accum.mul(ws[10], p[8]);
   accum.mul(ws[11], p[7]);
   accum.mul(ws[12], p[6]);
   accum.mul(ws[13], p[5]);
   accum.mul(ws[14], p[4]);
   accum.mul(ws[15], p[3]);
   accum.mul(ws[16], p[2]);
   accum.mul(ws[17], p[1]);
   accum.add(z[18]);
   ws[18] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[19]);
   accum.mul(ws[1], p[18]);
   accum.mul(ws[2], p[17]);
   accum.mul(ws[3], p[16]);
   accum.mul(ws[4], p[15]);
   accum.mul(ws[5], p[14]);
   accum.mul(ws[6], p[13]);
   accum.mul(ws[7], p[12]);
   accum.mul(ws[8], p[11]);
   accum.mul(ws[9], p[10]);
   accum.mul(ws[10], p[9]);
   accum.mul(ws[11], p[8]);
   accum.mul(ws[12], p[7]);
   accum.mul(ws[13], p[6]);
   accum.mul(ws[14], p[5]);
   accum.mul(ws[15], p[4]);
   accum.mul(ws[16], p[3]);
   accum.mul(ws[17], p[2]);
   accum.mul(ws[18], p[1]);
   accum.add(z[19]);
   ws[19] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[20]);
   accum.mul(ws[1], p[19]);
   accum.mul(ws[2], p[18]);
   accum.mul(ws[3], p[17]);
   accum.mul(ws[4], p[16]);
   accum.mul(ws[5], p[15]);
   accum.mul(ws[6], p[14]);
   accum.mul(ws[7], p[13]);
   accum.mul(ws[8], p[12]);
   accum.mul(ws[9], p[11]);
   accum.mul(ws[10], p[10]);
   accum.mul(ws[11], p[9]);
   accum.mul(ws[12], p[8]);
   accum.mul(ws[13], p[7]);
   accum.mul(ws[14], p[6]);
   accum.mul(ws[15], p[5]);
   accum.mul(ws[16], p[4]);
   accum.mul(ws[17], p[3]);
   accum.mul(ws[18], p[2]);
   accum.mul(ws[19], p[1]);
   accum.add(z[20]);
   ws[20] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[21]);
   accum.mul(ws[1], p[20]);
   accum.mul(ws[2], p[19]);
   accum.mul(ws[3], p[18]);
   accum.mul(ws[4], p[17]);
   accum.mul(ws[5], p[16]);
   accum.mul(ws[6], p[15]);
   accum.mul(ws[7], p[14]);
   accum.mul(ws[8], p[13]);
   accum.mul(ws[9], p[12]);
   accum.mul(ws[10], p[11]);
   accum.mul(ws[11], p[10]);
   accum.mul(ws[12], p[9]);
   accum.mul(ws[13], p[8]);
   accum.mul(ws[14], p[7]);
   accum.mul(ws[15], p[6]);
   accum.mul(ws[16], p[5]);
   accum.mul(ws[17], p[4]);
   accum.mul(ws[18], p[3]);
   accum.mul(ws[19], p[2]);
   accum.mul(ws[20], p[1]);
   accum.add(z[21]);
   ws[21] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[22]);
   accum.mul(ws[1], p[21]);
   accum.mul(ws[2], p[20]);
   accum.mul(ws[3], p[19]);
   accum.mul(ws[4], p[18]);
   accum.mul(ws[5], p[17]);
   accum.mul(ws[6], p[16]);
   accum.mul(ws[7], p[15]);
   accum.mul(ws[8], p[14]);
   accum.mul(ws[9], p[13]);
   accum.mul(ws[10], p[12]);
   accum.mul(ws[11], p[11]);
   accum.mul(ws[12], p[10]);
   accum.mul(ws[13], p[9]);
   accum.mul(ws[14], p[8]);
   accum.mul(ws[15], p[7]);
   accum.mul(ws[16], p[6]);
   accum.mul(ws[17], p[5]);
   accum.mul(ws[18], p[4]);
   accum.mul(ws[19], p[3]);
   accum.mul(ws[20], p[2]);
   accum.mul(ws[21], p[1]);
   accum.add(z[22]);
   ws[22] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[23]);
   accum.mul(ws[1], p[22]);
   accum.mul(ws[2], p[21]);
   accum.mul(ws[3], p[20]);
   accum.mul(ws[4], p[19]);
   accum.mul(ws[5], p[18]);
   accum.mul(ws[6], p[17]);
   accum.mul(ws[7], p[16]);
   accum.mul(ws[8], p[15]);
   accum.mul(ws[9], p[14]);
   accum.mul(ws[10], p[13]);
   accum.mul(ws[11], p[12]);
   accum.mul(ws[12], p[11]);
   accum.mul(ws[13], p[10]);
   accum.mul(ws[14], p[9]);
   accum.mul(ws[15], p[8]);
   accum.mul(ws[16], p[7]);
   accum.mul(ws[17], p[6]);
   accum.mul(ws[18], p[5]);
   accum.mul(ws[19], p[4]);
   accum.mul(ws[20], p[3]);
   accum.mul(ws[21], p[2]);
   accum.mul(ws[22], p[1]);
   accum.add(z[23]);
   ws[23] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[23]);
   accum.mul(ws[2], p[22]);
   accum.mul(ws[3], p[21]);
   accum.mul(ws[4], p[20]);
   accum.mul(ws[5], p[19]);
   accum.mul(ws[6], p[18]);
   accum.mul(ws[7], p[17]);
   accum.mul(ws[8], p[16]);
   accum.mul(ws[9], p[15]);
   accum.mul(ws[10], p[14]);
   accum.mul(ws[11], p[13]);
   accum.mul(ws[12], p[12]);
   accum.mul(ws[13], p[11]);
   accum.mul(ws[14], p[10]);
   accum.mul(ws[15], p[9]);
   accum.mul(ws[16], p[8]);
   accum.mul(ws[17], p[7]);
   accum.mul(ws[18], p[6]);
   accum.mul(ws[19], p[5]);
   accum.mul(ws[20], p[4]);
   accum.mul(ws[21], p[3]);
   accum.mul(ws[22], p[2]);
   accum.mul(ws[23], p[1]);
   accum.add(z[24]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[23]);
   accum.mul(ws[3], p[22]);
   accum.mul(ws[4], p[21]);
   accum.mul(ws[5], p[20]);
   accum.mul(ws[6], p[19]);
   accum.mul(ws[7], p[18]);
   accum.mul(ws[8], p[17]);
   accum.mul(ws[9], p[16]);
   accum.mul(ws[10], p[15]);
   accum.mul(ws[11], p[14]);
   accum.mul(ws[12], p[13]);
   accum.mul(ws[13], p[12]);
   accum.mul(ws[14], p[11]);
   accum.mul(ws[15], p[10]);
   accum.mul(ws[16], p[9]);
   accum.mul(ws[17], p[8]);
   accum.mul(ws[18], p[7]);
   accum.mul(ws[19], p[6]);
   accum.mul(ws[20], p[5]);
   accum.mul(ws[21], p[4]);
   accum.mul(ws[22], p[3]);
   accum.mul(ws[23], p[2]);
   accum.add(z[25]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[23]);
   accum.mul(ws[4], p[22]);
   accum.mul(ws[5], p[21]);
   accum.mul(ws[6], p[20]);
   accum.mul(ws[7], p[19]);
   accum.mul(ws[8], p[18]);
   accum.mul(ws[9], p[17]);
   accum.mul(ws[10], p[16]);
   accum.mul(ws[11], p[15]);
   accum.mul(ws[12], p[14]);
   accum.mul(ws[13], p[13]);
   accum.mul(ws[14], p[12]);
   accum.mul(ws[15], p[11]);
   accum.mul(ws[16], p[10]);
   accum.mul(ws[17], p[9]);
   accum.mul(ws[18], p[8]);
   accum.mul(ws[19], p[7]);
   accum.mul(ws[20], p[6]);
   accum.mul(ws[21], p[5]);
   accum.mul(ws[22], p[4]);
   accum.mul(ws[23], p[3]);
   accum.add(z[26]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[23]);
   accum.mul(ws[5], p[22]);
   accum.mul(ws[6], p[21]);
   accum.mul(ws[7], p[20]);
   accum.mul(ws[8], p[19]);
   accum.mul(ws[9], p[18]);
   accum.mul(ws[10], p[17]);
   accum.mul(ws[11], p[16]);
   accum.mul(ws[12], p[15]);
   accum.mul(ws[13], p[14]);
   accum.mul(ws[14], p[13]);
   accum.mul(ws[15], p[12]);
   accum.mul(ws[16], p[11]);
   accum.mul(ws[17], p[10]);
   accum.mul(ws[18], p[9]);
   accum.mul(ws[19], p[8]);
   accum.mul(ws[20], p[7]);
   accum.mul(ws[21], p[6]);
   accum.mul(ws[22], p[5]);
   accum.mul(ws[23], p[4]);
   accum.add(z[27]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[23]);
   accum.mul(ws[6], p[22]);
   accum.mul(ws[7], p[21]);
   accum.mul(ws[8], p[20]);
   accum.mul(ws[9], p[19]);
   accum.mul(ws[10], p[18]);
   accum.mul(ws[11], p[17]);
   accum.mul(ws[12], p[16]);
   accum.mul(ws[13], p[15]);
   accum.mul(ws[14], p[14]);
   accum.mul(ws[15], p[13]);
   accum.mul(ws[16], p[12]);
   accum.mul(ws[17], p[11]);
   accum.mul(ws[18], p[10]);
   accum.mul(ws[19], p[9]);
   accum.mul(ws[20], p[8]);
   accum.mul(ws[21], p[7]);
   accum.mul(ws[22], p[6]);
   accum.mul(ws[23], p[5]);
   accum.add(z[28]);
   ws[4] = accum.extract();
   accum.mul(ws[6], p[23]);
   accum.mul(ws[7], p[22]);
   accum.mul(ws[8], p[21]);
   accum.mul(ws[9], p[20]);
   accum.mul(ws[10], p[19]);
   accum.mul(ws[11], p[18]);
   accum.mul(ws[12], p[17]);
   accum.mul(ws[13], p[16]);
   accum.mul(ws[14], p[15]);
   accum.mul(ws[15], p[14]);
   accum.mul(ws[16], p[13]);
   accum.mul(ws[17], p[12]);
   accum.mul(ws[18], p[11]);
   accum.mul(ws[19], p[10]);
   accum.mul(ws[20], p[9]);
   accum.mul(ws[21], p[8]);
   accum.mul(ws[22], p[7]);
   accum.mul(ws[23], p[6]);
   accum.add(z[29]);
   ws[5] = accum.extract();
   accum.mul(ws[7], p[23]);
   accum.mul(ws[8], p[22]);
   accum.mul(ws[9], p[21]);
   accum.mul(ws[10], p[20]);
   accum.mul(ws[11], p[19]);
   accum.mul(ws[12], p[18]);
   accum.mul(ws[13], p[17]);
   accum.mul(ws[14], p[16]);
   accum.mul(ws[15], p[15]);
   accum.mul(ws[16], p[14]);
   accum.mul(ws[17], p[13]);
   accum.mul(ws[18], p[12]);
   accum.mul(ws[19], p[11]);
   accum.mul(ws[20], p[10]);
   accum.mul(ws[21], p[9]);
   accum.mul(ws[22], p[8]);
   accum.mul(ws[23], p[7]);
   accum.add(z[30]);
   ws[6] = accum.extract();
   accum.mul(ws[8], p[23]);
   accum.mul(ws[9], p[22]);
   accum.mul(ws[10], p[21]);
   accum.mul(ws[11], p[20]);
   accum.mul(ws[12], p[19]);
   accum.mul(ws[13], p[18]);
   accum.mul(ws[14], p[17]);
   accum.mul(ws[15], p[16]);
   accum.mul(ws[16], p[15]);
   accum.mul(ws[17], p[14]);
   accum.mul(ws[18], p[13]);
   accum.mul(ws[19], p[12]);
   accum.mul(ws[20], p[11]);
   accum.mul(ws[21], p[10]);
   accum.mul(ws[22], p[9]);
   accum.mul(ws[23], p[8]);
   accum.add(z[31]);
   ws[7] = accum.extract();
   accum.mul(ws[9], p[23]);
   accum.mul(ws[10], p[22]);
   accum.mul(ws[11], p[21]);
   accum.mul(ws[12], p[20]);
   accum.mul(ws[13], p[19]);
   accum.mul(ws[14], p[18]);
   accum.mul(ws[15], p[17]);
   accum.mul(ws[16], p[16]);
   accum.mul(ws[17], p[15]);
   accum.mul(ws[18], p[14]);
   accum.mul(ws[19], p[13]);
   accum.mul(ws[20], p[12]);
   accum.mul(ws[21], p[11]);
   accum.mul(ws[22], p[10]);
   accum.mul(ws[23], p[9]);
   accum.add(z[32]);
   ws[8] = accum.extract();
   accum.mul(ws[10], p[23]);
   accum.mul(ws[11], p[22]);
   accum.mul(ws[12], p[21]);
   accum.mul(ws[13], p[20]);
   accum.mul(ws[14], p[19]);
   accum.mul(ws[15], p[18]);
   accum.mul(ws[16], p[17]);
   accum.mul(ws[17], p[16]);
   accum.mul(ws[18], p[15]);
   accum.mul(ws[19], p[14]);
   accum.mul(ws[20], p[13]);
   accum.mul(ws[21], p[12]);
   accum.mul(ws[22], p[11]);
   accum.mul(ws[23], p[10]);
   accum.add(z[33]);
   ws[9] = accum.extract();
   accum.mul(ws[11], p[23]);
   accum.mul(ws[12], p[22]);
   accum.mul(ws[13], p[21]);
   accum.mul(ws[14], p[20]);
   accum.mul(ws[15], p[19]);
   accum.mul(ws[16], p[18]);
   accum.mul(ws[17], p[17]);
   accum.mul(ws[18], p[16]);
   accum.mul(ws[19], p[15]);
   accum.mul(ws[20], p[14]);
   accum.mul(ws[21], p[13]);
   accum.mul(ws[22], p[12]);
   accum.mul(ws[23], p[11]);
   accum.add(z[34]);
   ws[10] = accum.extract();
   accum.mul(ws[12], p[23]);
   accum.mul(ws[13], p[22]);
   accum.mul(ws[14], p[21]);
   accum.mul(ws[15], p[20]);
   accum.mul(ws[16], p[19]);
   accum.mul(ws[17], p[18]);
   accum.mul(ws[18], p[17]);
   accum.mul(ws[19], p[16]);
   accum.mul(ws[20], p[15]);
   accum.mul(ws[21], p[14]);
   accum.mul(ws[22], p[13]);
   accum.mul(ws[23], p[12]);
   accum.add(z[35]);
   ws[11] = accum.extract();
   accum.mul(ws[13], p[23]);
   accum.mul(ws[14], p[22]);
   accum.mul(ws[15], p[21]);
   accum.mul(ws[16], p[20]);
   accum.mul(ws[17], p[19]);
   accum.mul(ws[18], p[18]);
   accum.mul(ws[19], p[17]);
   accum.mul(ws[20], p[16]);
   accum.mul(ws[21], p[15]);
   accum.mul(ws[22], p[14]);
   accum.mul(ws[23], p[13]);
   accum.add(z[36]);
   ws[12] = accum.extract();
   accum.mul(ws[14], p[23]);
   accum.mul(ws[15], p[22]);
   accum.mul(ws[16], p[21]);
   accum.mul(ws[17], p[20]);
   accum.mul(ws[18], p[19]);
   accum.mul(ws[19], p[18]);
   accum.mul(ws[20], p[17]);
   accum.mul(ws[21], p[16]);
   accum.mul(ws[22], p[15]);
   accum.mul(ws[23], p[14]);
   accum.add(z[37]);
   ws[13] = accum.extract();
   accum.mul(ws[15], p[23]);
   accum.mul(ws[16], p[22]);
   accum.mul(ws[17], p[21]);
   accum.mul(ws[18], p[20]);
   accum.mul(ws[19], p[19]);
   accum.mul(ws[20], p[18]);
   accum.mul(ws[21], p[17]);
   accum.mul(ws[22], p[16]);
   accum.mul(ws[23], p[15]);
   accum.add(z[38]);
   ws[14] = accum.extract();
   accum.mul(ws[16], p[23]);
   accum.mul(ws[17], p[22]);
   accum.mul(ws[18], p[21]);
   accum.mul(ws[19], p[20]);
   accum.mul(ws[20], p[19]);
   accum.mul(ws[21], p[18]);
   accum.mul(ws[22], p[17]);
   accum.mul(ws[23], p[16]);
   accum.add(z[39]);
   ws[15] = accum.extract();
   accum.mul(ws[17], p[23]);
   accum.mul(ws[18], p[22]);
   accum.mul(ws[19], p[21]);
   accum.mul(ws[20], p[20]);
   accum.mul(ws[21], p[19]);
   accum.mul(ws[22], p[18]);
   accum.mul(ws[23], p[17]);
   accum.add(z[40]);
   ws[16] = accum.extract();
   accum.mul(ws[18], p[23]);
   accum.mul(ws[19], p[22]);
   accum.mul(ws[20], p[21]);
   accum.mul(ws[21], p[20]);
   accum.mul(ws[22], p[19]);
   accum.mul(ws[23], p[18]);
   accum.add(z[41]);
   ws[17] = accum.extract();
   accum.mul(ws[19], p[23]);
   accum.mul(ws[20], p[22]);
   accum.mul(ws[21], p[21]);
   accum.mul(ws[22], p[20]);
   accum.mul(ws[23], p[19]);
   accum.add(z[42]);
   ws[18] = accum.extract();
   accum.mul(ws[20], p[23]);
   accum.mul(ws[21], p[22]);
   accum.mul(ws[22], p[21]);
   accum.mul(ws[23], p[20]);
   accum.add(z[43]);
   ws[19] = accum.extract();
   accum.mul(ws[21], p[23]);
   accum.mul(ws[22], p[22]);
   accum.mul(ws[23], p[21]);
   accum.add(z[44]);
   ws[20] = accum.extract();
   accum.mul(ws[22], p[23]);
   accum.mul(ws[23], p[22]);
   accum.add(z[45]);
   ws[21] = accum.extract();
   accum.mul(ws[23], p[23]);
   accum.add(z[46]);
   ws[22] = accum.extract();
   accum.add(z[47]);
   ws[23] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<24>(r, w1, ws, p);
}

void bigint_monty_redc_32(word r[32], const word z[64], const word p[32], word p_dash, word ws[32]) {
   word3<word> accum;
   accum.add(z[0]);
   ws[0] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[1]);
   accum.add(z[1]);
   ws[1] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[2]);
   accum.mul(ws[1], p[1]);
   accum.add(z[2]);
   ws[2] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[3]);
   accum.mul(ws[1], p[2]);
   accum.mul(ws[2], p[1]);
   accum.add(z[3]);
   ws[3] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[4]);
   accum.mul(ws[1], p[3]);
   accum.mul(ws[2], p[2]);
   accum.mul(ws[3], p[1]);
   accum.add(z[4]);
   ws[4] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[5]);
   accum.mul(ws[1], p[4]);
   accum.mul(ws[2], p[3]);
   accum.mul(ws[3], p[2]);
   accum.mul(ws[4], p[1]);
   accum.add(z[5]);
   ws[5] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[6]);
   accum.mul(ws[1], p[5]);
   accum.mul(ws[2], p[4]);
   accum.mul(ws[3], p[3]);
   accum.mul(ws[4], p[2]);
   accum.mul(ws[5], p[1]);
   accum.add(z[6]);
   ws[6] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[7]);
   accum.mul(ws[1], p[6]);
   accum.mul(ws[2], p[5]);
   accum.mul(ws[3], p[4]);
   accum.mul(ws[4], p[3]);
   accum.mul(ws[5], p[2]);
   accum.mul(ws[6], p[1]);
   accum.add(z[7]);
   ws[7] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[8]);
   accum.mul(ws[1], p[7]);
   accum.mul(ws[2], p[6]);
   accum.mul(ws[3], p[5]);
   accum.mul(ws[4], p[4]);
   accum.mul(ws[5], p[3]);
   accum.mul(ws[6], p[2]);
   accum.mul(ws[7], p[1]);
   accum.add(z[8]);
   ws[8] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[9]);
   accum.mul(ws[1], p[8]);
   accum.mul(ws[2], p[7]);
   accum.mul(ws[3], p[6]);
   accum.mul(ws[4], p[5]);
   accum.mul(ws[5], p[4]);
   accum.mul(ws[6], p[3]);
   accum.mul(ws[7], p[2]);
   accum.mul(ws[8], p[1]);
   accum.add(z[9]);
   ws[9] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[10]);
   accum.mul(ws[1], p[9]);
   accum.mul(ws[2], p[8]);
   accum.mul(ws[3], p[7]);
   accum.mul(ws[4], p[6]);
   accum.mul(ws[5], p[5]);
   accum.mul(ws[6], p[4]);
   accum.mul(ws[7], p[3]);
   accum.mul(ws[8], p[2]);
   accum.mul(ws[9], p[1]);
   accum.add(z[10]);
   ws[10] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[11]);
   accum.mul(ws[1], p[10]);
   accum.mul(ws[2], p[9]);
   accum.mul(ws[3], p[8]);
   accum.mul(ws[4], p[7]);
   accum.mul(ws[5], p[6]);
   accum.mul(ws[6], p[5]);
   accum.mul(ws[7], p[4]);
   accum.mul(ws[8], p[3]);
   accum.mul(ws[9], p[2]);
   accum.mul(ws[10], p[1]);
   accum.add(z[11]);
   ws[11] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[12]);
   accum.mul(ws[1], p[11]);
   accum.mul(ws[2], p[10]);
   accum.mul(ws[3], p[9]);
   accum.mul(ws[4], p[8]);
   accum.mul(ws[5], p[7]);
   accum.mul(ws[6], p[6]);
   accum.mul(ws[7], p[5]);
   accum.mul(ws[8], p[4]);
   accum.mul(ws[9], p[3]);
   accum.mul(ws[10], p[2]);
   accum.mul(ws[11], p[1]);
   accum.add(z[12]);
   ws[12] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[13]);
   accum.mul(ws[1], p[12]);
   accum.mul(ws[2], p[11]);
   accum.mul(ws[3], p[10]);
   accum.mul(ws[4], p[9]);
   accum.mul(ws[5], p[8]);
   accum.mul(ws[6], p[7]);
   accum.mul(ws[7], p[6]);
   accum.mul(ws[8], p[5]);
   accum.mul(ws[9], p[4]);
   accum.mul(ws[10], p[3]);
   accum.mul(ws[11], p[2]);
   accum.mul(ws[12], p[1]);
   accum.add(z[13]);
   ws[13] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[14]);
   accum.mul(ws[1], p[13]);
   accum.mul(ws[2], p[12]);
   accum.mul(ws[3], p[11]);
   accum.mul(ws[4], p[10]);
   accum.mul(ws[5], p[9]);
   accum.mul(ws[6], p[8]);
   accum.mul(ws[7], p[7]);
   accum.mul(ws[8], p[6]);
   accum.mul(ws[9], p[5]);
   accum.mul(ws[10], p[4]);
   accum.mul(ws[11], p[3]);
   accum.mul(ws[12], p[2]);
   accum.mul(ws[13], p[1]);
   accum.add(z[14]);
   ws[14] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[15]);
   accum.mul(ws[1], p[14]);
   accum.mul(ws[2], p[13]);
   accum.mul(ws[3], p[12]);
   accum.mul(ws[4], p[11]);
   accum.mul(ws[5], p[10]);
   accum.mul(ws[6], p[9]);
   accum.mul(ws[7], p[8]);
   accum.mul(ws[8], p[7]);
   accum.mul(ws[9], p[6]);
   accum.mul(ws[10], p[5]);
   accum.mul(ws[11], p[4]);
   accum.mul(ws[12], p[3]);
   accum.mul(ws[13], p[2]);
   accum.mul(ws[14], p[1]);
   accum.add(z[15]);
   ws[15] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[16]);
   accum.mul(ws[1], p[15]);
   accum.mul(ws[2], p[14]);
   accum.mul(ws[3], p[13]);
   accum.mul(ws[4], p[12]);
   accum.mul(ws[5], p[11]);
   accum.mul(ws[6], p[10]);
   accum.mul(ws[7], p[9]);
   accum.mul(ws[8], p[8]);
   accum.mul(ws[9], p[7]);
   accum.mul(ws[10], p[6]);
   accum.mul(ws[11], p[5]);
   accum.mul(ws[12], p[4]);
   accum.mul(ws[13], p[3]);
   accum.mul(ws[14], p[2]);
   accum.mul(ws[15], p[1]);
   accum.add(z[16]);
   ws[16] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[17]);
   accum.mul(ws[1], p[16]);
   accum.mul(ws[2], p[15]);
   accum.mul(ws[3], p[14]);
   accum.mul(ws[4], p[13]);
   accum.mul(ws[5], p[12]);
   accum.mul(ws[6], p[11]);
   accum.mul(ws[7], p[10]);
   accum.mul(ws[8], p[9]);
   accum.mul(ws[9], p[8]);
   accum.mul(ws[10], p[7]);
   accum.mul(ws[11], p[6]);
   accum.mul(ws[12], p[5]);
   accum.mul(ws[13], p[4]);
   accum.mul(ws[14], p[3]);
   accum.mul(ws[15], p[2]);
   accum.mul(ws[16], p[1]);
   accum.add(z[17]);
   ws[17] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[18]);
   accum.mul(ws[1], p[17]);
   accum.mul(ws[2], p[16]);
   accum.mul(ws[3], p[15]);
   accum.mul(ws[4], p[14]);
   accum.mul(ws[5], p[13]);
   accum.mul(ws[6], p[12]);
   accum.mul(ws[7], p[11]);
   accum.mul(ws[8], p[10]);
   accum.mul(ws[9], p[9]);
   accum.mul(ws[10], p[8]);
   accum.mul(ws[11], p[7]);
   accum.mul(ws[12], p[6]);
   accum.mul(ws[13], p[5]);
   accum.mul(ws[14], p[4]);
   accum.mul(ws[15], p[3]);
   accum.mul(ws[16], p[2]);
   accum.mul(ws[17], p[1]);
   accum.add(z[18]);
   ws[18] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[19]);
   accum.mul(ws[1], p[18]);
   accum.mul(ws[2], p[17]);
   accum.mul(ws[3], p[16]);
   accum.mul(ws[4], p[15]);
   accum.mul(ws[5], p[14]);
   accum.mul(ws[6], p[13]);
   accum.mul(ws[7], p[12]);
   accum.mul(ws[8], p[11]);
   accum.mul(ws[9], p[10]);
   accum.mul(ws[10], p[9]);
   accum.mul(ws[11], p[8]);
   accum.mul(ws[12], p[7]);
   accum.mul(ws[13], p[6]);
   accum.mul(ws[14], p[5]);
   accum.mul(ws[15], p[4]);
   accum.mul(ws[16], p[3]);
   accum.mul(ws[17], p[2]);
   accum.mul(ws[18], p[1]);
   accum.add(z[19]);
   ws[19] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[20]);
   accum.mul(ws[1], p[19]);
   accum.mul(ws[2], p[18]);
   accum.mul(ws[3], p[17]);
   accum.mul(ws[4], p[16]);
   accum.mul(ws[5], p[15]);
   accum.mul(ws[6], p[14]);
   accum.mul(ws[7], p[13]);
   accum.mul(ws[8], p[12]);
   accum.mul(ws[9], p[11]);
   accum.mul(ws[10], p[10]);
   accum.mul(ws[11], p[9]);
   accum.mul(ws[12], p[8]);
   accum.mul(ws[13], p[7]);
   accum.mul(ws[14], p[6]);
   accum.mul(ws[15], p[5]);
   accum.mul(ws[16], p[4]);
   accum.mul(ws[17], p[3]);
   accum.mul(ws[18], p[2]);
   accum.mul(ws[19], p[1]);
   accum.add(z[20]);
   ws[20] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[21]);
   accum.mul(ws[1], p[20]);
   accum.mul(ws[2], p[19]);
   accum.mul(ws[3], p[18]);
   accum.mul(ws[4], p[17]);
   accum.mul(ws[5], p[16]);
   accum.mul(ws[6], p[15]);
   accum.mul(ws[7], p[14]);
   accum.mul(ws[8], p[13]);
   accum.mul(ws[9], p[12]);
   accum.mul(ws[10], p[11]);
   accum.mul(ws[11], p[10]);
   accum.mul(ws[12], p[9]);
   accum.mul(ws[13], p[8]);
   accum.mul(ws[14], p[7]);
   accum.mul(ws[15], p[6]);
   accum.mul(ws[16], p[5]);
   accum.mul(ws[17], p[4]);
   accum.mul(ws[18], p[3]);
   accum.mul(ws[19], p[2]);
   accum.mul(ws[20], p[1]);
   accum.add(z[21]);
   ws[21] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[22]);
   accum.mul(ws[1], p[21]);
   accum.mul(ws[2], p[20]);
   accum.mul(ws[3], p[19]);
   accum.mul(ws[4], p[18]);
   accum.mul(ws[5], p[17]);
   accum.mul(ws[6], p[16]);
   accum.mul(ws[7], p[15]);
   accum.mul(ws[8], p[14]);
   accum.mul(ws[9], p[13]);
   accum.mul(ws[10], p[12]);
   accum.mul(ws[11], p[11]);
   accum.mul(ws[12], p[10]);
   accum.mul(ws[13], p[9]);
   accum.mul(ws[14], p[8]);
   accum.mul(ws[15], p[7]);
   accum.mul(ws[16], p[6]);
   accum.mul(ws[17], p[5]);
   accum.mul(ws[18], p[4]);
   accum.mul(ws[19], p[3]);
   accum.mul(ws[20], p[2]);
   accum.mul(ws[21], p[1]);
   accum.add(z[22]);
   ws[22] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[23]);
   accum.mul(ws[1], p[22]);
   accum.mul(ws[2], p[21]);
   accum.mul(ws[3], p[20]);
   accum.mul(ws[4], p[19]);
   accum.mul(ws[5], p[18]);
   accum.mul(ws[6], p[17]);
   accum.mul(ws[7], p[16]);
   accum.mul(ws[8], p[15]);
   accum.mul(ws[9], p[14]);
   accum.mul(ws[10], p[13]);
   accum.mul(ws[11], p[12]);
   accum.mul(ws[12], p[11]);
   accum.mul(ws[13], p[10]);
   accum.mul(ws[14], p[9]);
   accum.mul(ws[15], p[8]);
   accum.mul(ws[16], p[7]);
   accum.mul(ws[17], p[6]);
   accum.mul(ws[18], p[5]);
   accum.mul(ws[19], p[4]);
   accum.mul(ws[20], p[3]);
   accum.mul(ws[21], p[2]);
   accum.mul(ws[22], p[1]);
   accum.add(z[23]);
   ws[23] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[24]);
   accum.mul(ws[1], p[23]);
   accum.mul(ws[2], p[22]);
   accum.mul(ws[3], p[21]);
   accum.mul(ws[4], p[20]);
   accum.mul(ws[5], p[19]);
   accum.mul(ws[6], p[18]);
   accum.mul(ws[7], p[17]);
   accum.mul(ws[8], p[16]);
   accum.mul(ws[9], p[15]);
   accum.mul(ws[10], p[14]);
   accum.mul(ws[11], p[13]);
   accum.mul(ws[12], p[12]);
   accum.mul(ws[13], p[11]);
   accum.mul(ws[14], p[10]);
   accum.mul(ws[15], p[9]);
   accum.mul(ws[16], p[8]);
   accum.mul(ws[17], p[7]);
   accum.mul(ws[18], p[6]);
   accum.mul(ws[19], p[5]);
   accum.mul(ws[20], p[4]);
   accum.mul(ws[21], p[3]);
   accum.mul(ws[22], p[2]);
   accum.mul(ws[23], p[1]);
   accum.add(z[24]);
   ws[24] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[25]);
   accum.mul(ws[1], p[24]);
   accum.mul(ws[2], p[23]);
   accum.mul(ws[3], p[22]);
   accum.mul(ws[4], p[21]);
   accum.mul(ws[5], p[20]);
   accum.mul(ws[6], p[19]);
   accum.mul(ws[7], p[18]);
   accum.mul(ws[8], p[17]);
   accum.mul(ws[9], p[16]);
   accum.mul(ws[10], p[15]);
   accum.mul(ws[11], p[14]);
   accum.mul(ws[12], p[13]);
   accum.mul(ws[13], p[12]);
   accum.mul(ws[14], p[11]);
   accum.mul(ws[15], p[10]);
   accum.mul(ws[16], p[9]);
   accum.mul(ws[17], p[8]);
   accum.mul(ws[18], p[7]);
   accum.mul(ws[19], p[6]);
   accum.mul(ws[20], p[5]);
   accum.mul(ws[21], p[4]);
   accum.mul(ws[22], p[3]);
   accum.mul(ws[23], p[2]);
   accum.mul(ws[24], p[1]);
   accum.add(z[25]);
   ws[25] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[26]);
   accum.mul(ws[1], p[25]);
   accum.mul(ws[2], p[24]);
   accum.mul(ws[3], p[23]);
   accum.mul(ws[4], p[22]);
   accum.mul(ws[5], p[21]);
   accum.mul(ws[6], p[20]);
   accum.mul(ws[7], p[19]);
   accum.mul(ws[8], p[18]);
   accum.mul(ws[9], p[17]);
   accum.mul(ws[10], p[16]);
   accum.mul(ws[11], p[15]);
   accum.mul(ws[12], p[14]);
   accum.mul(ws[13], p[13]);
   accum.mul(ws[14], p[12]);
   accum.mul(ws[15], p[11]);
   accum.mul(ws[16], p[10]);
   accum.mul(ws[17], p[9]);
   accum.mul(ws[18], p[8]);
   accum.mul(ws[19], p[7]);
   accum.mul(ws[20], p[6]);
   accum.mul(ws[21], p[5]);
   accum.mul(ws[22], p[4]);
   accum.mul(ws[23], p[3]);
   accum.mul(ws[24], p[2]);
   accum.mul(ws[25], p[1]);
   accum.add(z[26]);
   ws[26] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[27]);
   accum.mul(ws[1], p[26]);
   accum.mul(ws[2], p[25]);
   accum.mul(ws[3], p[24]);
   accum.mul(ws[4], p[23]);
   accum.mul(ws[5], p[22]);
   accum.mul(ws[6], p[21]);
   accum.mul(ws[7], p[20]);
   accum.mul(ws[8], p[19]);
   accum.mul(ws[9], p[18]);
   accum.mul(ws[10], p[17]);
   accum.mul(ws[11], p[16]);
   accum.mul(ws[12], p[15]);
   accum.mul(ws[13], p[14]);
   accum.mul(ws[14], p[13]);
   accum.mul(ws[15], p[12]);
   accum.mul(ws[16], p[11]);
   accum.mul(ws[17], p[10]);
   accum.mul(ws[18], p[9]);
   accum.mul(ws[19], p[8]);
   accum.mul(ws[20], p[7]);
   accum.mul(ws[21], p[6]);
   accum.mul(ws[22], p[5]);
   accum.mul(ws[23], p[4]);
   accum.mul(ws[24], p[3]);
   accum.mul(ws[25], p[2]);
   accum.mul(ws[26], p[1]);
   accum.add(z[27]);
   ws[27] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[28]);
   accum.mul(ws[1], p[27]);
   accum.mul(ws[2], p[26]);
   accum.mul(ws[3], p[25]);
   accum.mul(ws[4], p[24]);
   accum.mul(ws[5], p[23]);
   accum.mul(ws[6], p[22]);
   accum.mul(ws[7], p[21]);
   accum.mul(ws[8], p[20]);
   accum.mul(ws[9], p[19]);
   accum.mul(ws[10], p[18]);
   accum.mul(ws[11], p[17]);
   accum.mul(ws[12], p[16]);
   accum.mul(ws[13], p[15]);
   accum.mul(ws[14], p[14]);
   accum.mul(ws[15], p[13]);
   accum.mul(ws[16], p[12]);
   accum.mul(ws[17], p[11]);
   accum.mul(ws[18], p[10]);
   accum.mul(ws[19], p[9]);
   accum.mul(ws[20], p[8]);
   accum.mul(ws[21], p[7]);
   accum.mul(ws[22], p[6]);
   accum.mul(ws[23], p[5]);
   accum.mul(ws[24], p[4]);
   accum.mul(ws[25], p[3]);
   accum.mul(ws[26], p[2]);
   accum.mul(ws[27], p[1]);
   accum.add(z[28]);
   ws[28] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[29]);
   accum.mul(ws[1], p[28]);
   accum.mul(ws[2], p[27]);
   accum.mul(ws[3], p[26]);
   accum.mul(ws[4], p[25]);
   accum.mul(ws[5], p[24]);
   accum.mul(ws[6], p[23]);
   accum.mul(ws[7], p[22]);
   accum.mul(ws[8], p[21]);
   accum.mul(ws[9], p[20]);
   accum.mul(ws[10], p[19]);
   accum.mul(ws[11], p[18]);
   accum.mul(ws[12], p[17]);
   accum.mul(ws[13], p[16]);
   accum.mul(ws[14], p[15]);
   accum.mul(ws[15], p[14]);
   accum.mul(ws[16], p[13]);
   accum.mul(ws[17], p[12]);
   accum.mul(ws[18], p[11]);
   accum.mul(ws[19], p[10]);
   accum.mul(ws[20], p[9]);
   accum.mul(ws[21], p[8]);
   accum.mul(ws[22], p[7]);
   accum.mul(ws[23], p[6]);
   accum.mul(ws[24], p[5]);
   accum.mul(ws[25], p[4]);
   accum.mul(ws[26], p[3]);
   accum.mul(ws[27], p[2]);
   accum.mul(ws[28], p[1]);
   accum.add(z[29]);
   ws[29] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[30]);
   accum.mul(ws[1], p[29]);
   accum.mul(ws[2], p[28]);
   accum.mul(ws[3], p[27]);
   accum.mul(ws[4], p[26]);
   accum.mul(ws[5], p[25]);
   accum.mul(ws[6], p[24]);
   accum.mul(ws[7], p[23]);
   accum.mul(ws[8], p[22]);
   accum.mul(ws[9], p[21]);
   accum.mul(ws[10], p[20]);
   accum.mul(ws[11], p[19]);
   accum.mul(ws[12], p[18]);
   accum.mul(ws[13], p[17]);
   accum.mul(ws[14], p[16]);
   accum.mul(ws[15], p[15]);
   accum.mul(ws[16], p[14]);
   accum.mul(ws[17], p[13]);
   accum.mul(ws[18], p[12]);
   accum.mul(ws[19], p[11]);
   accum.mul(ws[20], p[10]);
   accum.mul(ws[21], p[9]);
   accum.mul(ws[22], p[8]);
   accum.mul(ws[23], p[7]);
   accum.mul(ws[24], p[6]);
   accum.mul(ws[25], p[5]);
   accum.mul(ws[26], p[4]);
   accum.mul(ws[27], p[3]);
   accum.mul(ws[28], p[2]);
   accum.mul(ws[29], p[1]);
   accum.add(z[30]);
   ws[30] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[0], p[31]);
   accum.mul(ws[1], p[30]);
   accum.mul(ws[2], p[29]);
   accum.mul(ws[3], p[28]);
   accum.mul(ws[4], p[27]);
   accum.mul(ws[5], p[26]);
   accum.mul(ws[6], p[25]);
   accum.mul(ws[7], p[24]);
   accum.mul(ws[8], p[23]);
   accum.mul(ws[9], p[22]);
   accum.mul(ws[10], p[21]);
   accum.mul(ws[11], p[20]);
   accum.mul(ws[12], p[19]);
   accum.mul(ws[13], p[18]);
   accum.mul(ws[14], p[17]);
   accum.mul(ws[15], p[16]);
   accum.mul(ws[16], p[15]);
   accum.mul(ws[17], p[14]);
   accum.mul(ws[18], p[13]);
   accum.mul(ws[19], p[12]);
   accum.mul(ws[20], p[11]);
   accum.mul(ws[21], p[10]);
   accum.mul(ws[22], p[9]);
   accum.mul(ws[23], p[8]);
   accum.mul(ws[24], p[7]);
   accum.mul(ws[25], p[6]);
   accum.mul(ws[26], p[5]);
   accum.mul(ws[27], p[4]);
   accum.mul(ws[28], p[3]);
   accum.mul(ws[29], p[2]);
   accum.mul(ws[30], p[1]);
   accum.add(z[31]);
   ws[31] = accum.monty_step(p[0], p_dash);
   accum.mul(ws[1], p[31]);
   accum.mul(ws[2], p[30]);
   accum.mul(ws[3], p[29]);
   accum.mul(ws[4], p[28]);
   accum.mul(ws[5], p[27]);
   accum.mul(ws[6], p[26]);
   accum.mul(ws[7], p[25]);
   accum.mul(ws[8], p[24]);
   accum.mul(ws[9], p[23]);
   accum.mul(ws[10], p[22]);
   accum.mul(ws[11], p[21]);
   accum.mul(ws[12], p[20]);
   accum.mul(ws[13], p[19]);
   accum.mul(ws[14], p[18]);
   accum.mul(ws[15], p[17]);
   accum.mul(ws[16], p[16]);
   accum.mul(ws[17], p[15]);
   accum.mul(ws[18], p[14]);
   accum.mul(ws[19], p[13]);
   accum.mul(ws[20], p[12]);
   accum.mul(ws[21], p[11]);
   accum.mul(ws[22], p[10]);
   accum.mul(ws[23], p[9]);
   accum.mul(ws[24], p[8]);
   accum.mul(ws[25], p[7]);
   accum.mul(ws[26], p[6]);
   accum.mul(ws[27], p[5]);
   accum.mul(ws[28], p[4]);
   accum.mul(ws[29], p[3]);
   accum.mul(ws[30], p[2]);
   accum.mul(ws[31], p[1]);
   accum.add(z[32]);
   ws[0] = accum.extract();
   accum.mul(ws[2], p[31]);
   accum.mul(ws[3], p[30]);
   accum.mul(ws[4], p[29]);
   accum.mul(ws[5], p[28]);
   accum.mul(ws[6], p[27]);
   accum.mul(ws[7], p[26]);
   accum.mul(ws[8], p[25]);
   accum.mul(ws[9], p[24]);
   accum.mul(ws[10], p[23]);
   accum.mul(ws[11], p[22]);
   accum.mul(ws[12], p[21]);
   accum.mul(ws[13], p[20]);
   accum.mul(ws[14], p[19]);
   accum.mul(ws[15], p[18]);
   accum.mul(ws[16], p[17]);
   accum.mul(ws[17], p[16]);
   accum.mul(ws[18], p[15]);
   accum.mul(ws[19], p[14]);
   accum.mul(ws[20], p[13]);
   accum.mul(ws[21], p[12]);
   accum.mul(ws[22], p[11]);
   accum.mul(ws[23], p[10]);
   accum.mul(ws[24], p[9]);
   accum.mul(ws[25], p[8]);
   accum.mul(ws[26], p[7]);
   accum.mul(ws[27], p[6]);
   accum.mul(ws[28], p[5]);
   accum.mul(ws[29], p[4]);
   accum.mul(ws[30], p[3]);
   accum.mul(ws[31], p[2]);
   accum.add(z[33]);
   ws[1] = accum.extract();
   accum.mul(ws[3], p[31]);
   accum.mul(ws[4], p[30]);
   accum.mul(ws[5], p[29]);
   accum.mul(ws[6], p[28]);
   accum.mul(ws[7], p[27]);
   accum.mul(ws[8], p[26]);
   accum.mul(ws[9], p[25]);
   accum.mul(ws[10], p[24]);
   accum.mul(ws[11], p[23]);
   accum.mul(ws[12], p[22]);
   accum.mul(ws[13], p[21]);
   accum.mul(ws[14], p[20]);
   accum.mul(ws[15], p[19]);
   accum.mul(ws[16], p[18]);
   accum.mul(ws[17], p[17]);
   accum.mul(ws[18], p[16]);
   accum.mul(ws[19], p[15]);
   accum.mul(ws[20], p[14]);
   accum.mul(ws[21], p[13]);
   accum.mul(ws[22], p[12]);
   accum.mul(ws[23], p[11]);
   accum.mul(ws[24], p[10]);
   accum.mul(ws[25], p[9]);
   accum.mul(ws[26], p[8]);
   accum.mul(ws[27], p[7]);
   accum.mul(ws[28], p[6]);
   accum.mul(ws[29], p[5]);
   accum.mul(ws[30], p[4]);
   accum.mul(ws[31], p[3]);
   accum.add(z[34]);
   ws[2] = accum.extract();
   accum.mul(ws[4], p[31]);
   accum.mul(ws[5], p[30]);
   accum.mul(ws[6], p[29]);
   accum.mul(ws[7], p[28]);
   accum.mul(ws[8], p[27]);
   accum.mul(ws[9], p[26]);
   accum.mul(ws[10], p[25]);
   accum.mul(ws[11], p[24]);
   accum.mul(ws[12], p[23]);
   accum.mul(ws[13], p[22]);
   accum.mul(ws[14], p[21]);
   accum.mul(ws[15], p[20]);
   accum.mul(ws[16], p[19]);
   accum.mul(ws[17], p[18]);
   accum.mul(ws[18], p[17]);
   accum.mul(ws[19], p[16]);
   accum.mul(ws[20], p[15]);
   accum.mul(ws[21], p[14]);
   accum.mul(ws[22], p[13]);
   accum.mul(ws[23], p[12]);
   accum.mul(ws[24], p[11]);
   accum.mul(ws[25], p[10]);
   accum.mul(ws[26], p[9]);
   accum.mul(ws[27], p[8]);
   accum.mul(ws[28], p[7]);
   accum.mul(ws[29], p[6]);
   accum.mul(ws[30], p[5]);
   accum.mul(ws[31], p[4]);
   accum.add(z[35]);
   ws[3] = accum.extract();
   accum.mul(ws[5], p[31]);
   accum.mul(ws[6], p[30]);
   accum.mul(ws[7], p[29]);
   accum.mul(ws[8], p[28]);
   accum.mul(ws[9], p[27]);
   accum.mul(ws[10], p[26]);
   accum.mul(ws[11], p[25]);
   accum.mul(ws[12], p[24]);
   accum.mul(ws[13], p[23]);
   accum.mul(ws[14], p[22]);
   accum.mul(ws[15], p[21]);
   accum.mul(ws[16], p[20]);
   accum.mul(ws[17], p[19]);
   accum.mul(ws[18], p[18]);
   accum.mul(ws[19], p[17]);
   accum.mul(ws[20], p[16]);
   accum.mul(ws[21], p[15]);
   accum.mul(ws[22], p[14]);
   accum.mul(ws[23], p[13]);
   accum.mul(ws[24], p[12]);
   accum.mul(ws[25], p[11]);
   accum.mul(ws[26], p[10]);
   accum.mul(ws[27], p[9]);
   accum.mul(ws[28], p[8]);
   accum.mul(ws[29], p[7]);
   accum.mul(ws[30], p[6]);
   accum.mul(ws[31], p[5]);
   accum.add(z[36]);
   ws[4] = accum.extract();
   accum.mul(ws[6], p[31]);
   accum.mul(ws[7], p[30]);
   accum.mul(ws[8], p[29]);
   accum.mul(ws[9], p[28]);
   accum.mul(ws[10], p[27]);
   accum.mul(ws[11], p[26]);
   accum.mul(ws[12], p[25]);
   accum.mul(ws[13], p[24]);
   accum.mul(ws[14], p[23]);
   accum.mul(ws[15], p[22]);
   accum.mul(ws[16], p[21]);
   accum.mul(ws[17], p[20]);
   accum.mul(ws[18], p[19]);
   accum.mul(ws[19], p[18]);
   accum.mul(ws[20], p[17]);
   accum.mul(ws[21], p[16]);
   accum.mul(ws[22], p[15]);
   accum.mul(ws[23], p[14]);
   accum.mul(ws[24], p[13]);
   accum.mul(ws[25], p[12]);
   accum.mul(ws[26], p[11]);
   accum.mul(ws[27], p[10]);
   accum.mul(ws[28], p[9]);
   accum.mul(ws[29], p[8]);
   accum.mul(ws[30], p[7]);
   accum.mul(ws[31], p[6]);
   accum.add(z[37]);
   ws[5] = accum.extract();
   accum.mul(ws[7], p[31]);
   accum.mul(ws[8], p[30]);
   accum.mul(ws[9], p[29]);
   accum.mul(ws[10], p[28]);
   accum.mul(ws[11], p[27]);
   accum.mul(ws[12], p[26]);
   accum.mul(ws[13], p[25]);
   accum.mul(ws[14], p[24]);
   accum.mul(ws[15], p[23]);
   accum.mul(ws[16], p[22]);
   accum.mul(ws[17], p[21]);
   accum.mul(ws[18], p[20]);
   accum.mul(ws[19], p[19]);
   accum.mul(ws[20], p[18]);
   accum.mul(ws[21], p[17]);
   accum.mul(ws[22], p[16]);
   accum.mul(ws[23], p[15]);
   accum.mul(ws[24], p[14]);
   accum.mul(ws[25], p[13]);
   accum.mul(ws[26], p[12]);
   accum.mul(ws[27], p[11]);
   accum.mul(ws[28], p[10]);
   accum.mul(ws[29], p[9]);
   accum.mul(ws[30], p[8]);
   accum.mul(ws[31], p[7]);
   accum.add(z[38]);
   ws[6] = accum.extract();
   accum.mul(ws[8], p[31]);
   accum.mul(ws[9], p[30]);
   accum.mul(ws[10], p[29]);
   accum.mul(ws[11], p[28]);
   accum.mul(ws[12], p[27]);
   accum.mul(ws[13], p[26]);
   accum.mul(ws[14], p[25]);
   accum.mul(ws[15], p[24]);
   accum.mul(ws[16], p[23]);
   accum.mul(ws[17], p[22]);
   accum.mul(ws[18], p[21]);
   accum.mul(ws[19], p[20]);
   accum.mul(ws[20], p[19]);
   accum.mul(ws[21], p[18]);
   accum.mul(ws[22], p[17]);
   accum.mul(ws[23], p[16]);
   accum.mul(ws[24], p[15]);
   accum.mul(ws[25], p[14]);
   accum.mul(ws[26], p[13]);
   accum.mul(ws[27], p[12]);
   accum.mul(ws[28], p[11]);
   accum.mul(ws[29], p[10]);
   accum.mul(ws[30], p[9]);
   accum.mul(ws[31], p[8]);
   accum.add(z[39]);
   ws[7] = accum.extract();
   accum.mul(ws[9], p[31]);
   accum.mul(ws[10], p[30]);
   accum.mul(ws[11], p[29]);
   accum.mul(ws[12], p[28]);
   accum.mul(ws[13], p[27]);
   accum.mul(ws[14], p[26]);
   accum.mul(ws[15], p[25]);
   accum.mul(ws[16], p[24]);
   accum.mul(ws[17], p[23]);
   accum.mul(ws[18], p[22]);
   accum.mul(ws[19], p[21]);
   accum.mul(ws[20], p[20]);
   accum.mul(ws[21], p[19]);
   accum.mul(ws[22], p[18]);
   accum.mul(ws[23], p[17]);
   accum.mul(ws[24], p[16]);
   accum.mul(ws[25], p[15]);
   accum.mul(ws[26], p[14]);
   accum.mul(ws[27], p[13]);
   accum.mul(ws[28], p[12]);
   accum.mul(ws[29], p[11]);
   accum.mul(ws[30], p[10]);
   accum.mul(ws[31], p[9]);
   accum.add(z[40]);
   ws[8] = accum.extract();
   accum.mul(ws[10], p[31]);
   accum.mul(ws[11], p[30]);
   accum.mul(ws[12], p[29]);
   accum.mul(ws[13], p[28]);
   accum.mul(ws[14], p[27]);
   accum.mul(ws[15], p[26]);
   accum.mul(ws[16], p[25]);
   accum.mul(ws[17], p[24]);
   accum.mul(ws[18], p[23]);
   accum.mul(ws[19], p[22]);
   accum.mul(ws[20], p[21]);
   accum.mul(ws[21], p[20]);
   accum.mul(ws[22], p[19]);
   accum.mul(ws[23], p[18]);
   accum.mul(ws[24], p[17]);
   accum.mul(ws[25], p[16]);
   accum.mul(ws[26], p[15]);
   accum.mul(ws[27], p[14]);
   accum.mul(ws[28], p[13]);
   accum.mul(ws[29], p[12]);
   accum.mul(ws[30], p[11]);
   accum.mul(ws[31], p[10]);
   accum.add(z[41]);
   ws[9] = accum.extract();
   accum.mul(ws[11], p[31]);
   accum.mul(ws[12], p[30]);
   accum.mul(ws[13], p[29]);
   accum.mul(ws[14], p[28]);
   accum.mul(ws[15], p[27]);
   accum.mul(ws[16], p[26]);
   accum.mul(ws[17], p[25]);
   accum.mul(ws[18], p[24]);
   accum.mul(ws[19], p[23]);
   accum.mul(ws[20], p[22]);
   accum.mul(ws[21], p[21]);
   accum.mul(ws[22], p[20]);
   accum.mul(ws[23], p[19]);
   accum.mul(ws[24], p[18]);
   accum.mul(ws[25], p[17]);
   accum.mul(ws[26], p[16]);
   accum.mul(ws[27], p[15]);
   accum.mul(ws[28], p[14]);
   accum.mul(ws[29], p[13]);
   accum.mul(ws[30], p[12]);
   accum.mul(ws[31], p[11]);
   accum.add(z[42]);
   ws[10] = accum.extract();
   accum.mul(ws[12], p[31]);
   accum.mul(ws[13], p[30]);
   accum.mul(ws[14], p[29]);
   accum.mul(ws[15], p[28]);
   accum.mul(ws[16], p[27]);
   accum.mul(ws[17], p[26]);
   accum.mul(ws[18], p[25]);
   accum.mul(ws[19], p[24]);
   accum.mul(ws[20], p[23]);
   accum.mul(ws[21], p[22]);
   accum.mul(ws[22], p[21]);
   accum.mul(ws[23], p[20]);
   accum.mul(ws[24], p[19]);
   accum.mul(ws[25], p[18]);
   accum.mul(ws[26], p[17]);
   accum.mul(ws[27], p[16]);
   accum.mul(ws[28], p[15]);
   accum.mul(ws[29], p[14]);
   accum.mul(ws[30], p[13]);
   accum.mul(ws[31], p[12]);
   accum.add(z[43]);
   ws[11] = accum.extract();
   accum.mul(ws[13], p[31]);
   accum.mul(ws[14], p[30]);
   accum.mul(ws[15], p[29]);
   accum.mul(ws[16], p[28]);
   accum.mul(ws[17], p[27]);
   accum.mul(ws[18], p[26]);
   accum.mul(ws[19], p[25]);
   accum.mul(ws[20], p[24]);
   accum.mul(ws[21], p[23]);
   accum.mul(ws[22], p[22]);
   accum.mul(ws[23], p[21]);
   accum.mul(ws[24], p[20]);
   accum.mul(ws[25], p[19]);
   accum.mul(ws[26], p[18]);
   accum.mul(ws[27], p[17]);
   accum.mul(ws[28], p[16]);
   accum.mul(ws[29], p[15]);
   accum.mul(ws[30], p[14]);
   accum.mul(ws[31], p[13]);
   accum.add(z[44]);
   ws[12] = accum.extract();
   accum.mul(ws[14], p[31]);
   accum.mul(ws[15], p[30]);
   accum.mul(ws[16], p[29]);
   accum.mul(ws[17], p[28]);
   accum.mul(ws[18], p[27]);
   accum.mul(ws[19], p[26]);
   accum.mul(ws[20], p[25]);
   accum.mul(ws[21], p[24]);
   accum.mul(ws[22], p[23]);
   accum.mul(ws[23], p[22]);
   accum.mul(ws[24], p[21]);
   accum.mul(ws[25], p[20]);
   accum.mul(ws[26], p[19]);
   accum.mul(ws[27], p[18]);
   accum.mul(ws[28], p[17]);
   accum.mul(ws[29], p[16]);
   accum.mul(ws[30], p[15]);
   accum.mul(ws[31], p[14]);
   accum.add(z[45]);
   ws[13] = accum.extract();
   accum.mul(ws[15], p[31]);
   accum.mul(ws[16], p[30]);
   accum.mul(ws[17], p[29]);
   accum.mul(ws[18], p[28]);
   accum.mul(ws[19], p[27]);
   accum.mul(ws[20], p[26]);
   accum.mul(ws[21], p[25]);
   accum.mul(ws[22], p[24]);
   accum.mul(ws[23], p[23]);
   accum.mul(ws[24], p[22]);
   accum.mul(ws[25], p[21]);
   accum.mul(ws[26], p[20]);
   accum.mul(ws[27], p[19]);
   accum.mul(ws[28], p[18]);
   accum.mul(ws[29], p[17]);
   accum.mul(ws[30], p[16]);
   accum.mul(ws[31], p[15]);
   accum.add(z[46]);
   ws[14] = accum.extract();
   accum.mul(ws[16], p[31]);
   accum.mul(ws[17], p[30]);
   accum.mul(ws[18], p[29]);
   accum.mul(ws[19], p[28]);
   accum.mul(ws[20], p[27]);
   accum.mul(ws[21], p[26]);
   accum.mul(ws[22], p[25]);
   accum.mul(ws[23], p[24]);
   accum.mul(ws[24], p[23]);
   accum.mul(ws[25], p[22]);
   accum.mul(ws[26], p[21]);
   accum.mul(ws[27], p[20]);
   accum.mul(ws[28], p[19]);
   accum.mul(ws[29], p[18]);
   accum.mul(ws[30], p[17]);
   accum.mul(ws[31], p[16]);
   accum.add(z[47]);
   ws[15] = accum.extract();
   accum.mul(ws[17], p[31]);
   accum.mul(ws[18], p[30]);
   accum.mul(ws[19], p[29]);
   accum.mul(ws[20], p[28]);
   accum.mul(ws[21], p[27]);
   accum.mul(ws[22], p[26]);
   accum.mul(ws[23], p[25]);
   accum.mul(ws[24], p[24]);
   accum.mul(ws[25], p[23]);
   accum.mul(ws[26], p[22]);
   accum.mul(ws[27], p[21]);
   accum.mul(ws[28], p[20]);
   accum.mul(ws[29], p[19]);
   accum.mul(ws[30], p[18]);
   accum.mul(ws[31], p[17]);
   accum.add(z[48]);
   ws[16] = accum.extract();
   accum.mul(ws[18], p[31]);
   accum.mul(ws[19], p[30]);
   accum.mul(ws[20], p[29]);
   accum.mul(ws[21], p[28]);
   accum.mul(ws[22], p[27]);
   accum.mul(ws[23], p[26]);
   accum.mul(ws[24], p[25]);
   accum.mul(ws[25], p[24]);
   accum.mul(ws[26], p[23]);
   accum.mul(ws[27], p[22]);
   accum.mul(ws[28], p[21]);
   accum.mul(ws[29], p[20]);
   accum.mul(ws[30], p[19]);
   accum.mul(ws[31], p[18]);
   accum.add(z[49]);
   ws[17] = accum.extract();
   accum.mul(ws[19], p[31]);
   accum.mul(ws[20], p[30]);
   accum.mul(ws[21], p[29]);
   accum.mul(ws[22], p[28]);
   accum.mul(ws[23], p[27]);
   accum.mul(ws[24], p[26]);
   accum.mul(ws[25], p[25]);
   accum.mul(ws[26], p[24]);
   accum.mul(ws[27], p[23]);
   accum.mul(ws[28], p[22]);
   accum.mul(ws[29], p[21]);
   accum.mul(ws[30], p[20]);
   accum.mul(ws[31], p[19]);
   accum.add(z[50]);
   ws[18] = accum.extract();
   accum.mul(ws[20], p[31]);
   accum.mul(ws[21], p[30]);
   accum.mul(ws[22], p[29]);
   accum.mul(ws[23], p[28]);
   accum.mul(ws[24], p[27]);
   accum.mul(ws[25], p[26]);
   accum.mul(ws[26], p[25]);
   accum.mul(ws[27], p[24]);
   accum.mul(ws[28], p[23]);
   accum.mul(ws[29], p[22]);
   accum.mul(ws[30], p[21]);
   accum.mul(ws[31], p[20]);
   accum.add(z[51]);
   ws[19] = accum.extract();
   accum.mul(ws[21], p[31]);
   accum.mul(ws[22], p[30]);
   accum.mul(ws[23], p[29]);
   accum.mul(ws[24], p[28]);
   accum.mul(ws[25], p[27]);
   accum.mul(ws[26], p[26]);
   accum.mul(ws[27], p[25]);
   accum.mul(ws[28], p[24]);
   accum.mul(ws[29], p[23]);
   accum.mul(ws[30], p[22]);
   accum.mul(ws[31], p[21]);
   accum.add(z[52]);
   ws[20] = accum.extract();
   accum.mul(ws[22], p[31]);
   accum.mul(ws[23], p[30]);
   accum.mul(ws[24], p[29]);
   accum.mul(ws[25], p[28]);
   accum.mul(ws[26], p[27]);
   accum.mul(ws[27], p[26]);
   accum.mul(ws[28], p[25]);
   accum.mul(ws[29], p[24]);
   accum.mul(ws[30], p[23]);
   accum.mul(ws[31], p[22]);
   accum.add(z[53]);
   ws[21] = accum.extract();
   accum.mul(ws[23], p[31]);
   accum.mul(ws[24], p[30]);
   accum.mul(ws[25], p[29]);
   accum.mul(ws[26], p[28]);
   accum.mul(ws[27], p[27]);
   accum.mul(ws[28], p[26]);
   accum.mul(ws[29], p[25]);
   accum.mul(ws[30], p[24]);
   accum.mul(ws[31], p[23]);
   accum.add(z[54]);
   ws[22] = accum.extract();
   accum.mul(ws[24], p[31]);
   accum.mul(ws[25], p[30]);
   accum.mul(ws[26], p[29]);
   accum.mul(ws[27], p[28]);
   accum.mul(ws[28], p[27]);
   accum.mul(ws[29], p[26]);
   accum.mul(ws[30], p[25]);
   accum.mul(ws[31], p[24]);
   accum.add(z[55]);
   ws[23] = accum.extract();
   accum.mul(ws[25], p[31]);
   accum.mul(ws[26], p[30]);
   accum.mul(ws[27], p[29]);
   accum.mul(ws[28], p[28]);
   accum.mul(ws[29], p[27]);
   accum.mul(ws[30], p[26]);
   accum.mul(ws[31], p[25]);
   accum.add(z[56]);
   ws[24] = accum.extract();
   accum.mul(ws[26], p[31]);
   accum.mul(ws[27], p[30]);
   accum.mul(ws[28], p[29]);
   accum.mul(ws[29], p[28]);
   accum.mul(ws[30], p[27]);
   accum.mul(ws[31], p[26]);
   accum.add(z[57]);
   ws[25] = accum.extract();
   accum.mul(ws[27], p[31]);
   accum.mul(ws[28], p[30]);
   accum.mul(ws[29], p[29]);
   accum.mul(ws[30], p[28]);
   accum.mul(ws[31], p[27]);
   accum.add(z[58]);
   ws[26] = accum.extract();
   accum.mul(ws[28], p[31]);
   accum.mul(ws[29], p[30]);
   accum.mul(ws[30], p[29]);
   accum.mul(ws[31], p[28]);
   accum.add(z[59]);
   ws[27] = accum.extract();
   accum.mul(ws[29], p[31]);
   accum.mul(ws[30], p[30]);
   accum.mul(ws[31], p[29]);
   accum.add(z[60]);
   ws[28] = accum.extract();
   accum.mul(ws[30], p[31]);
   accum.mul(ws[31], p[30]);
   accum.add(z[61]);
   ws[29] = accum.extract();
   accum.mul(ws[31], p[31]);
   accum.add(z[62]);
   ws[30] = accum.extract();
   accum.add(z[63]);
   ws[31] = accum.extract();
   word w1 = accum.extract();
   bigint_monty_maybe_sub<32>(r, w1, ws, p);
}

}  // namespace Botan
/*
* (C) 2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

Barrett_Reduction::Barrett_Reduction(const BigInt& m, BigInt mu, size_t mw) :
      m_modulus(m), m_mu(std::move(mu)), m_mod_words(mw), m_modulus_bits(m.bits()) {
   // Give some extra space for Karatsuba
   m_modulus.grow_to(m_mod_words + 8);
   m_mu.grow_to(m_mod_words + 8);
}

Barrett_Reduction Barrett_Reduction::for_secret_modulus(const BigInt& mod) {
   BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero");
   BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative");

   size_t mod_words = mod.sig_words();

   // Compute mu = floor(2^{2k} / m)
   const size_t mu_bits = 2 * WordInfo<word>::bits * mod_words;
   return Barrett_Reduction(mod, ct_divide_pow2k(mu_bits, mod), mod_words);
}

Barrett_Reduction Barrett_Reduction::for_public_modulus(const BigInt& mod) {
   BOTAN_ARG_CHECK(!mod.is_zero(), "Modulus cannot be zero");
   BOTAN_ARG_CHECK(!mod.is_negative(), "Modulus cannot be negative");

   size_t mod_words = mod.sig_words();

   // Compute mu = floor(2^{2k} / m)
   const size_t mu_bits = 2 * WordInfo<word>::bits * mod_words;
   return Barrett_Reduction(mod, BigInt::power_of_2(mu_bits) / mod, mod_words);
}

namespace {

/*
* Barrett Reduction
*
* This function assumes that the significant size of x_words (ie the number of
* words with a value other than zero) is at most 2 * mod_words. In any case, any
* larger value cannot be reduced using Barrett reduction; callers should have
* already checked for this.
*/
BigInt barrett_reduce(
   size_t mod_words, const BigInt& modulus, const BigInt& mu, std::span<const word> x_words, secure_vector<word>& ws) {
   BOTAN_ASSERT_NOMSG(modulus.sig_words() == mod_words);

   // Caller must expand input to be at least this size
   BOTAN_ASSERT_NOMSG(x_words.size() >= 2 * mod_words);

   // Normally mod_words + 1 but can be + 2 if the modulus is a power of 2
   const size_t mu_words = mu.sig_words();
   BOTAN_ASSERT_NOMSG(mu_words <= mod_words + 2);

   if(ws.size() < 2 * (mod_words + 2)) {
      ws.resize(2 * (mod_words + 2));
   }

   CT::poison(x_words);

   /*
   * Following the notation of Handbook of Applied Cryptography
   * Algorithm 14.42 "Barrett modular reduction", page 604
   * <https://cacr.uwaterloo.ca/hac/about/chap14.pdf>
   *
   * Using `mu` for μ in the code
   */

   // Compute q1 = floor(x / 2^(k - 1)) which is equivalent to ignoring the low (k-1) words

   // 2 * mod_words + 1 is sufficient, extra is to enable Karatsuba
   secure_vector<word> r(2 * mu_words + 2);

   copy_mem(r.data(), x_words.data() + (mod_words - 1), mod_words + 1);

   // Now compute q2 = q1 * μ

   // We allocate more size than required since this allows Karatsuba more often;
   // just `mu_words + (mod_words + 1)` is sufficient
   const size_t q2_size = 2 * mu_words + 2;

   secure_vector<word> q2(q2_size);

   bigint_mul(
      q2.data(), q2.size(), r.data(), r.size(), mod_words + 1, mu._data(), mu.size(), mu_words, ws.data(), ws.size());

   // Compute r2 = (floor(q2 / b^(k+1)) * m) mod 2^(k+1)
   // The division/floor is again effected by just ignoring the low k + 1 words
   bigint_mul(r.data(),
              r.size(),
              &q2[mod_words + 1],  // ignoring the low mod_words + 1 words of the first product
              q2.size() - (mod_words + 1),
              mod_words + 1,
              modulus._data(),
              modulus.size(),
              mod_words,
              ws.data(),
              ws.size());

   // Clear the high words of the product, equivalent to computing mod 2^(k+1)
   // TODO add masked mul to avoid computing high bits at all
   clear_mem(std::span{r}.subspan(mod_words + 1));

   // Compute r = r1 - r2

   // The return value of bigint_sub_abs isn't quite right for what we need here so first compare
   const int32_t relative_size = bigint_cmp(r.data(), mod_words + 1, x_words.data(), mod_words + 1);

   bigint_sub_abs(r.data(), r.data(), x_words.data(), mod_words + 1, ws.data());

   /*
   If r is negative then we have to set r to r + 2^(k+1)

   However for r negative computing this sum is equivalent to computing 2^(k+1) - r
   */
   word borrow = 0;
   for(size_t i = 0; i != mod_words + 1; ++i) {
      ws[i] = word_sub(static_cast<word>(0), r[i], &borrow);
   }
   ws[mod_words + 1] = word_sub(static_cast<word>(1), r[mod_words + 1], &borrow);

   // If relative_size > 0 then assign r to 2^(k+1) - r
   CT::Mask<word>::is_equal(static_cast<word>(relative_size), 1).select_n(r.data(), ws.data(), r.data(), mod_words + 2);

   /*
   * Per HAC Note 14.44 (ii) "step 4 is repeated at most twice since 0 ≤ r < 3m"
   */
   const size_t bound = 2;

   BOTAN_ASSERT_NOMSG(r.size() >= mod_words + 1);
   for(size_t i = 0; i != bound; ++i) {
      borrow = bigint_sub3(ws.data(), r.data(), mod_words + 1, modulus._data(), mod_words);
      CT::Mask<word>::is_zero(borrow).select_n(r.data(), ws.data(), r.data(), mod_words + 1);
   }

   CT::unpoison(q2);
   CT::unpoison(r);
   CT::unpoison(ws);
   CT::unpoison(x_words);

   return BigInt::_from_words(r);
}

CT::Choice acceptable_barrett_input(const BigInt& x, const BigInt& modulus) {
   auto x_is_positive = CT::Choice::from_int(static_cast<uint32_t>(x.is_positive()));
   auto x_lt_mod = bigint_ct_is_lt(x._data(), x.size(), modulus._data(), modulus.sig_words()).as_choice();
   return x_is_positive && x_lt_mod;
}

}  // namespace

BigInt Barrett_Reduction::multiply(const BigInt& x, const BigInt& y) const {
   BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett multiply");
   BOTAN_ARG_CHECK(acceptable_barrett_input(y, m_modulus).as_bool(), "Invalid y param for Barrett multiply");

   secure_vector<word> ws(2 * (m_mod_words + 2));
   secure_vector<word> xy(2 * m_mod_words);

   bigint_mul(xy.data(),
              xy.size(),
              x._data(),
              x.size(),
              std::min(x.size(), m_mod_words),
              y._data(),
              y.size(),
              std::min(y.size(), m_mod_words),
              ws.data(),
              ws.size());

   return barrett_reduce(m_mod_words, m_modulus, m_mu, xy, ws);
}

BigInt Barrett_Reduction::square(const BigInt& x) const {
   BOTAN_ARG_CHECK(acceptable_barrett_input(x, m_modulus).as_bool(), "Invalid x param for Barrett square");

   secure_vector<word> ws(2 * (m_mod_words + 2));
   secure_vector<word> x2(2 * m_mod_words);

   bigint_sqr(x2.data(), x2.size(), x._data(), x.size(), std::min(x.size(), m_mod_words), ws.data(), ws.size());

   return barrett_reduce(m_mod_words, m_modulus, m_mu, x2, ws);
}

BigInt Barrett_Reduction::reduce(const BigInt& x) const {
   BOTAN_ARG_CHECK(x.is_positive(), "Argument must be positive");

   const size_t x_sw = x.sig_words();
   BOTAN_ARG_CHECK(x_sw <= 2 * m_mod_words, "Argument is too large for Barrett reduction");

   x.grow_to(2 * m_mod_words);

   secure_vector<word> ws;
   return barrett_reduce(m_mod_words, m_modulus, m_mu, x._as_span(), ws);
}

}  // namespace Botan
/*
* DSA Parameter Generation
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

/*
* Check if this size is allowed by FIPS 186-3
*/
bool fips186_3_valid_size(size_t pbits, size_t qbits) {
   if(qbits == 160) {
      return (pbits == 1024);
   }

   if(qbits == 224) {
      return (pbits == 2048);
   }

   if(qbits == 256) {
      return (pbits == 2048 || pbits == 3072);
   }

   return false;
}

// qbits assumed to be a valid size for FIPS param gen
std::string hash_function_for(size_t qbits) {
   if(qbits == 160) {
      return "SHA-1";
   }

   return "SHA-" + std::to_string(qbits);
}

}  // namespace

/*
* Attempt DSA prime generation with given seed
*/
bool generate_dsa_primes(RandomNumberGenerator& rng,
                         BigInt& p,
                         BigInt& q,
                         size_t pbits,
                         size_t qbits,
                         const std::vector<uint8_t>& seed_c,
                         size_t offset) {
   if(!fips186_3_valid_size(pbits, qbits)) {
      throw Invalid_Argument(fmt("FIPS 186-3 does not allow DSA domain parameters of {}/{} bits long", pbits, qbits));
   }

   if(seed_c.size() * 8 < qbits) {
      throw Invalid_Argument(
         fmt("Generating a DSA parameter set with a {} bit long q requires a seed at least as many bits long", qbits));
   }

   const std::string hash_name = hash_function_for(qbits);
   auto hash = HashFunction::create_or_throw(hash_name);

   const size_t HASH_SIZE = hash->output_length();

   class Seed final {
      public:
         explicit Seed(const std::vector<uint8_t>& s) : m_seed(s) {}

         const std::vector<uint8_t>& value() const { return m_seed; }

         Seed& operator++() {
            for(size_t j = m_seed.size(); j > 0; --j) {
               m_seed[j - 1] += 1;
               if(m_seed[j - 1] != 0) {
                  break;
               }
            }
            return (*this);
         }

      private:
         std::vector<uint8_t> m_seed;
   };

   Seed seed(seed_c);

   q._assign_from_bytes(hash->process(seed.value()));
   q.set_bit(qbits - 1);
   q.set_bit(0);

   if(!is_prime(q, rng, 128, true)) {
      return false;
   }

   const size_t n = (pbits - 1) / (HASH_SIZE * 8);
   const size_t b = (pbits - 1) % (HASH_SIZE * 8);

   BigInt X;
   std::vector<uint8_t> V(HASH_SIZE * (n + 1));

   const BigInt q2 = 2 * q;

   for(size_t j = 0; j != 4 * pbits; ++j) {
      for(size_t k = 0; k <= n; ++k) {
         ++seed;
         hash->update(seed.value());
         hash->final(&V[HASH_SIZE * (n - k)]);
      }

      if(j >= offset) {
         X._assign_from_bytes(std::span{V}.subspan(HASH_SIZE - 1 - b / 8));
         X.set_bit(pbits - 1);

         // Variable time division is OK here since DSA primes are public anyway
         p = X - ((X % q2) - 1);

         if(p.bits() == pbits && is_prime(p, rng, 128, true)) {
            return true;
         }
      }
   }
   return false;
}

/*
* Generate DSA Primes
*/
std::vector<uint8_t> generate_dsa_primes(RandomNumberGenerator& rng, BigInt& p, BigInt& q, size_t pbits, size_t qbits) {
   while(true) {
      std::vector<uint8_t> seed(qbits / 8);
      rng.randomize(seed.data(), seed.size());

      if(generate_dsa_primes(rng, p, q, pbits, qbits, seed)) {
         return seed;
      }
   }
}

}  // namespace Botan
/*
* Prime Generation
* (C) 1999-2007,2018,2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

class Prime_Sieve final {
   public:
      Prime_Sieve(const BigInt& init_value, size_t sieve_size, word step, bool check_2p1) :
            m_sieve(std::min(sieve_size, PRIME_TABLE_SIZE)), m_step(step), m_check_2p1(check_2p1) {
         for(size_t i = 0; i != m_sieve.size(); ++i) {
            m_sieve[i] = init_value % PRIMES[i];
         }
      }

      size_t sieve_size() const { return m_sieve.size(); }

      bool check_2p1() const { return m_check_2p1; }

      bool next() {
         auto passes = CT::Mask<word>::set();
         for(size_t i = 0; i != m_sieve.size(); ++i) {
            m_sieve[i] = (m_sieve[i] + m_step) % PRIMES[i];

            // If m_sieve[i] == 0 then val % p == 0 -> not prime
            passes &= CT::Mask<word>::expand(m_sieve[i]);

            if(this->check_2p1()) {
               /*
               If v % p == (p-1)/2 then 2*v+1 == 0 (mod p)

               So if potentially generating a safe prime, we want to
               avoid this value because 2*v+1 will certainly not be prime.

               See "Safe Prime Generation with a Combined Sieve" M. Wiener
               https://eprint.iacr.org/2003/186.pdf
               */
               passes &= ~CT::Mask<word>::is_equal(m_sieve[i], (PRIMES[i] - 1) / 2);
            }
         }

         return passes.as_bool();
      }

   private:
      std::vector<word> m_sieve;
      const word m_step;
      const bool m_check_2p1;
};

#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)

bool no_small_multiples(const BigInt& v, const Prime_Sieve& sieve) {
   const size_t sieve_size = sieve.sieve_size();
   const bool check_2p1 = sieve.check_2p1();

   if(v.is_even())
      return false;

   const BigInt v_x2_p1 = 2 * v + 1;

   for(size_t i = 0; i != sieve_size; ++i) {
      if((v % PRIMES[i]) == 0)
         return false;

      if(check_2p1) {
         if(v_x2_p1 % PRIMES[i] == 0)
            return false;
      }
   }

   return true;
}

#endif

}  // namespace

/*
* Generate a random prime
*/
BigInt random_prime(
   RandomNumberGenerator& rng, size_t bits, const BigInt& coprime, size_t equiv, size_t modulo, size_t prob) {
   if(bits <= 1) {
      throw Invalid_Argument("random_prime: Can't make a prime of " + std::to_string(bits) + " bits");
   }
   if(coprime.is_negative() || (!coprime.is_zero() && coprime.is_even()) || coprime.bits() >= bits) {
      throw Invalid_Argument("random_prime: invalid coprime");
   }
   if(modulo == 0 || modulo >= 100000) {
      throw Invalid_Argument("random_prime: Invalid modulo value");
   }

   equiv %= modulo;

   if(equiv == 0) {
      throw Invalid_Argument("random_prime Invalid value for equiv/modulo");
   }

   // Handle small values:

   if(bits <= 16) {
      if(equiv != 1 || modulo != 2 || coprime != 0) {
         throw Not_Implemented("random_prime equiv/modulo/coprime options not usable for small primes");
      }

      if(bits == 2) {
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 2 : 3));
      } else if(bits == 3) {
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 5 : 7));
      } else if(bits == 4) {
         return BigInt::from_word(((rng.next_byte() % 2) == 0 ? 11 : 13));
      } else {
         for(;;) {
            // This is slightly biased, but for small primes it does not seem to matter
            uint8_t b[4] = {0};
            rng.randomize(b, 4);
            const size_t idx = load_le<uint32_t>(b, 0) % PRIME_TABLE_SIZE;
            const uint16_t small_prime = PRIMES[idx];

            if(high_bit(small_prime) == bits) {
               return BigInt::from_word(small_prime);
            }
         }
      }
   }

   const size_t MAX_ATTEMPTS = 32 * 1024;

   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);

   while(true) {
      BigInt p(rng, bits);

      // Force lowest and two top bits on
      p.set_bit(bits - 1);
      p.set_bit(bits - 2);
      p.set_bit(0);

      // Force p to be equal to equiv mod modulo
      p += (modulo - (p % modulo)) + equiv;

      Prime_Sieve sieve(p, bits, modulo, true);

      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt) {
         p += modulo;

         if(!sieve.next()) {
            continue;
         }

         // here p can be even if modulo is odd, continue on in that case
         if(p.is_even()) {
            continue;
         }

         BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve));

         auto mod_p = Barrett_Reduction::for_secret_modulus(p);

         if(coprime > 1) {
            /*
            First do a single M-R iteration to quickly elimate most non-primes,
            before doing the coprimality check which is expensive
            */
            if(!is_miller_rabin_probable_prime(p, mod_p, rng, 1)) {
               continue;
            }

            /*
            * Check if p - 1 and coprime are relatively prime, using gcd.
            * The gcd computation is const-time
            */
            if(gcd(p - 1, coprime) > 1) {
               continue;
            }
         }

         if(p.bits() > bits) {
            break;
         }

         if(!is_miller_rabin_probable_prime(p, mod_p, rng, mr_trials)) {
            continue;
         }

         if(prob > 32 && !is_lucas_probable_prime(p, mod_p)) {
            continue;
         }

         return p;
      }
   }
}

BigInt generate_rsa_prime(RandomNumberGenerator& keygen_rng,
                          RandomNumberGenerator& prime_test_rng,
                          size_t bits,
                          const BigInt& coprime,
                          size_t prob) {
   if(bits < 512) {
      throw Invalid_Argument("generate_rsa_prime bits too small");
   }

   /*
   * The restriction on coprime <= 64 bits is arbitrary but generally speaking
   * very large RSA public exponents are a bad idea both for performance and due
   * to attacks on small d.
   */
   if(coprime <= 1 || coprime.is_even() || coprime.bits() > 64) {
      throw Invalid_Argument("generate_rsa_prime coprime must be small odd positive integer");
   }

   const size_t MAX_ATTEMPTS = 32 * 1024;

   const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);

   while(true) {
      BigInt p(keygen_rng, bits);

      /*
      Force high two bits so multiplication always results in expected n bit integer

      Force the two low bits, and step by 4, so the generated prime is always == 3 (mod 4).
      This way when we perform the inversion modulo phi(n) it is always of the form 2*o
      with o odd, which allows a fastpath and avoids leaking any information about the
      structure of the prime.
      */
      p.set_bit(bits - 1);
      p.set_bit(bits - 2);
      p.set_bit(1);
      p.set_bit(0);

      const word step = 4;

      Prime_Sieve sieve(p, bits, step, false);

      for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt) {
         p += step;

         if(!sieve.next()) {
            continue;
         }

         BOTAN_DEBUG_ASSERT(no_small_multiples(p, sieve));

         auto mod_p = Barrett_Reduction::for_secret_modulus(p);

         /*
         * Do a single primality test first before checking coprimality, since
         * currently a single Miller-Rabin test is faster than computing gcd,
         * and this eliminates almost all wasted gcd computations.
         */
         if(!is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, 1)) {
            continue;
         }

         /*
         * Check if p - 1 and coprime are relatively prime.
         */
         if(gcd(p - 1, coprime) > 1) {
            continue;
         }

         if(p.bits() > bits) {
            break;
         }

         if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, mr_trials)) {
            return p;
         }
      }
   }
}

/*
* Generate a random safe prime
*/
BigInt random_safe_prime(RandomNumberGenerator& rng, size_t bits) {
   if(bits <= 64) {
      throw Invalid_Argument("random_safe_prime: Can't make a prime of " + std::to_string(bits) + " bits");
   }

   const size_t error_bound = 128;

   BigInt q;
   BigInt p;
   for(;;) {
      /*
      Generate q == 2 (mod 3), since otherwise [in the case of q == 1 (mod 3)],
      2*q+1 == 3 (mod 3) and so certainly not prime.
      */
      q = random_prime(rng, bits - 1, BigInt::zero(), 2, 3, error_bound);
      p = (q << 1) + 1;

      if(is_prime(p, rng, error_bound, true)) {
         return p;
      }
   }
}

}  // namespace Botan
/*
* (C) 1999-2011,2016,2018,2019,2020,2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

BigInt inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod) {
   // Caller should assure these preconditions:
   BOTAN_ASSERT_NOMSG(n.is_positive());
   BOTAN_ASSERT_NOMSG(mod.is_positive());
   BOTAN_ASSERT_NOMSG(n < mod);
   BOTAN_ASSERT_NOMSG(mod >= 3 && mod.is_odd());

   /*
   This uses a modular inversion algorithm designed by Niels Möller
   and implemented in Nettle. The same algorithm was later also
   adapted to GMP in mpn_sec_invert.

   It can be easily implemented in a way that does not depend on
   secret branches or memory lookups, providing resistance against
   some forms of side channel attack.

   There is also a description of the algorithm in Appendix 5 of "Fast
   Software Polynomial Multiplication on ARM Processors using the NEON Engine"
   by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
   Dahab in LNCS 8182
      https://inria.hal.science/hal-01506572/document

   Thanks to Niels for creating the algorithm, explaining some things
   about it, and the reference to the paper.
   */

   const size_t mod_words = mod.sig_words();
   BOTAN_ASSERT(mod_words > 0, "Not empty");

   secure_vector<word> tmp_mem(5 * mod_words);

   word* v_w = &tmp_mem[0];  // NOLINT(readability-container-data-pointer)
   word* u_w = &tmp_mem[1 * mod_words];
   word* b_w = &tmp_mem[2 * mod_words];
   word* a_w = &tmp_mem[3 * mod_words];
   word* mp1o2 = &tmp_mem[4 * mod_words];

   CT::poison(tmp_mem.data(), tmp_mem.size());

   copy_mem(a_w, n._data(), std::min(n.size(), mod_words));
   copy_mem(b_w, mod._data(), std::min(mod.size(), mod_words));
   u_w[0] = 1;
   // v_w = 0

   // compute (mod + 1) / 2 which [because mod is odd] is equal to
   // (mod / 2) + 1
   copy_mem(mp1o2, mod._data(), std::min(mod.size(), mod_words));
   bigint_shr1(mp1o2, mod_words, 1);
   word carry = bigint_add2(mp1o2, mod_words, u_w, 1);
   BOTAN_ASSERT_NOMSG(carry == 0);

   // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
   const size_t execs = 2 * mod.bits();

   for(size_t i = 0; i != execs; ++i) {
      const word odd_a = a_w[0] & 1;

      //if(odd_a) a -= b
      word underflow = bigint_cnd_sub(odd_a, a_w, b_w, mod_words);

      //if(underflow) { b -= a; a = abs(a); swap(u, v); }
      bigint_cnd_add(underflow, b_w, a_w, mod_words);
      bigint_cnd_abs(underflow, a_w, mod_words);
      bigint_cnd_swap(underflow, u_w, v_w, mod_words);

      // a >>= 1
      bigint_shr1(a_w, mod_words, 1);

      //if(odd_a) u -= v;
      word borrow = bigint_cnd_sub(odd_a, u_w, v_w, mod_words);

      // if(borrow) u += p
      bigint_cnd_add(borrow, u_w, mod._data(), mod_words);

      const word odd_u = u_w[0] & 1;

      // u >>= 1
      bigint_shr1(u_w, mod_words, 1);

      //if(odd_u) u += mp1o2;
      bigint_cnd_add(odd_u, u_w, mp1o2, mod_words);
   }

   const auto a_is_0 = CT::all_zeros(a_w, mod_words);

   auto b_is_1 = CT::Mask<word>::is_equal(b_w[0], 1);
   for(size_t i = 1; i != mod_words; ++i) {
      b_is_1 &= CT::Mask<word>::is_zero(b_w[i]);
   }

   BOTAN_ASSERT(a_is_0.as_bool(), "A is zero");

   // if b != 1 then gcd(n,mod) > 1 and inverse does not exist
   // in which case zero out the result to indicate this
   (~b_is_1).if_set_zero_out(v_w, mod_words);

   /*
   * We've placed the result in the lowest words of the temp buffer.
   * So just clear out the other values and then give that buffer to a
   * BigInt.
   */
   clear_mem(&tmp_mem[mod_words], 4 * mod_words);

   CT::unpoison(tmp_mem.data(), tmp_mem.size());

   BigInt r;
   r.swap_reg(tmp_mem);
   return r;
}

BigInt inverse_mod_pow2(const BigInt& a1, size_t k) {
   /*
   * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
   * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
   */

   if(a1.is_even() || k == 0) {
      return BigInt::zero();
   }
   if(k == 1) {
      return BigInt::one();
   }

   BigInt a = a1;
   a.mask_bits(k);

   BigInt b = BigInt::one();
   BigInt X = BigInt::zero();
   BigInt newb;

   const size_t a_words = a.sig_words();

   X.grow_to(round_up(k, WordInfo<word>::bits) / WordInfo<word>::bits);
   b.grow_to(a_words);

   /*
   Hide the exact value of k. k is anyway known to word length
   granularity because of the length of a, so no point in doing more
   than this.
   */
   const size_t iter = round_up(k, WordInfo<word>::bits);

   for(size_t i = 0; i != iter; ++i) {
      const bool b0 = b.get_bit(0);
      X.conditionally_set_bit(i, b0);
      newb = b - a;
      b.ct_cond_assign(b0, newb);
      b >>= 1;
   }

   X.mask_bits(k);

   CT::unpoison(X);
   return X;
}

}  // namespace

std::optional<BigInt> inverse_mod_general(const BigInt& x, const BigInt& mod) {
   BOTAN_ARG_CHECK(x > 0, "x must be greater than zero");
   BOTAN_ARG_CHECK(mod > 0, "mod must be greater than zero");
   BOTAN_ARG_CHECK(x < mod, "x must be less than m");

   // Easy case where gcd > 1 so no inverse exists
   if(x.is_even() && mod.is_even()) {
      return std::nullopt;
   }

   if(mod.is_odd()) {
      BigInt z = inverse_mod_odd_modulus(x, mod);
      if(z.is_zero()) {
         return std::nullopt;
      } else {
         return z;
      }
   }

   // If x is even and mod is even we already returned 0
   // If x is even and mod is odd we jumped directly to odd-modulus algo
   BOTAN_ASSERT_NOMSG(x.is_odd());

   const size_t mod_lz = low_zero_bits(mod);
   BOTAN_ASSERT_NOMSG(mod_lz > 0);
   const size_t mod_bits = mod.bits();
   BOTAN_ASSERT_NOMSG(mod_bits > mod_lz);

   if(mod_lz == mod_bits - 1) {
      // In this case we are performing an inversion modulo 2^k
      auto z = inverse_mod_pow2(x, mod_lz);
      if(z.is_zero()) {
         return std::nullopt;
      } else {
         return z;
      }
   }

   if(mod_lz == 1) {
      /*
      Inversion modulo 2*o is an easier special case of CRT

      This is exactly the main CRT flow below but taking advantage of
      the fact that any odd number ^-1 modulo 2 is 1. As a result both
      inv_2k and c can be taken to be 1, m2k is 2, and h is always
      either 0 or 1, and its value depends only on the low bit of inv_o.

      This is worth special casing because we generate RSA primes such
      that phi(n) is of this form. However this only works for keys
      that we generated in this way; pre-existing keys will typically
      fall back to the general algorithm below.
      */

      const BigInt o = mod >> 1;
      const BigInt inv_o = inverse_mod_odd_modulus(ct_modulo(x, o), o);

      // No modular inverse in this case:
      if(inv_o == 0) {
         return std::nullopt;
      }

      BigInt h = inv_o;
      h.ct_cond_add(!inv_o.get_bit(0), o);
      return h;
   }

   /*
   * In this case we are performing an inversion modulo 2^k*o for
   * some k >= 2 and some odd (not necessarily prime) integer.
   * Compute the inversions modulo 2^k and modulo o, then combine them
   * using CRT, which is possible because 2^k and o are relatively prime.
   */

   const BigInt o = mod >> mod_lz;
   const BigInt inv_o = inverse_mod_odd_modulus(ct_modulo(x, o), o);
   const BigInt inv_2k = inverse_mod_pow2(x, mod_lz);

   // No modular inverse in this case:
   if(inv_o == 0 || inv_2k == 0) {
      return std::nullopt;
   }

   const BigInt m2k = BigInt::power_of_2(mod_lz);
   // Compute the CRT parameter
   const BigInt c = inverse_mod_pow2(o, mod_lz);

   // This should never happen; o is odd so gcd is 1 and inverse mod 2^k exists
   BOTAN_ASSERT_NOMSG(!c.is_zero());

   // Compute h = c*(inv_2k-inv_o) mod 2^k
   BigInt h = c * (inv_2k - inv_o);
   const bool h_neg = h.is_negative();
   h.set_sign(BigInt::Positive);
   h.mask_bits(mod_lz);
   const bool h_nonzero = h.is_nonzero();
   h.ct_cond_assign(h_nonzero && h_neg, m2k - h);

   // Return result inv_o + h * o
   h *= o;
   h += inv_o;
   return h;
}

BigInt inverse_mod_secret_prime(const BigInt& x, const BigInt& p) {
   BOTAN_ARG_CHECK(x.is_positive() && p.is_positive(), "Parameters must be positive");
   BOTAN_ARG_CHECK(x < p, "x must be less than p");
   BOTAN_ARG_CHECK(p.is_odd() && p > 1, "Primes are odd integers greater than 1");

   // TODO possibly use FLT, or the algorithm presented for this case in
   // Handbook of Elliptic and Hyperelliptic Curve Cryptography

   return inverse_mod_odd_modulus(x, p);
}

BigInt inverse_mod_public_prime(const BigInt& x, const BigInt& p) {
   return inverse_mod_secret_prime(x, p);
}

BigInt inverse_mod_rsa_public_modulus(const BigInt& x, const BigInt& n) {
   BOTAN_ARG_CHECK(n.is_positive() && n.is_odd(), "RSA public modulus must be odd and positive");
   BOTAN_ARG_CHECK(x.is_positive() && x < n, "Input must be positive and less than RSA modulus");
   BigInt z = inverse_mod_odd_modulus(x, n);
   BOTAN_ASSERT(!z.is_zero(), "Accidentally factored the public modulus");  // whoops
   return z;
}

namespace {

uint64_t barrett_mod_65537(uint64_t x) {
   constexpr uint64_t mod = 65537;
   constexpr size_t s = 32;
   constexpr uint64_t c = (static_cast<uint64_t>(1) << s) / mod;

   uint64_t q = (x * c) >> s;
   uint64_t r = x - q * mod;

   auto r_gt_mod = CT::Mask<uint64_t>::is_gte(r, mod);
   return r - r_gt_mod.if_set_return(mod);
}

word inverse_mod_65537(word x) {
   // Need 64-bit here as accum*accum exceeds 32-bit if accum=0x10000
   uint64_t accum = 1;
   // Basic square and multiply, with all bits of exponent set
   for(size_t i = 0; i != 16; ++i) {
      accum = barrett_mod_65537(accum * accum);
      accum = barrett_mod_65537(accum * x);
   }
   return static_cast<word>(accum);
}

}  // namespace

BigInt compute_rsa_secret_exponent(const BigInt& e, const BigInt& phi_n, const BigInt& p, const BigInt& q) {
   /*
   * Both p - 1 and q - 1 are chosen to be relatively prime to e. Thus
   * phi(n), the least common multiple of p - 1 and q - 1, is also
   * relatively prime to e.
   */
   BOTAN_DEBUG_ASSERT(gcd(e, phi_n) == 1);

   if(e == 65537) {
      /*
      Arazi's algorithm for inversion of prime x modulo a non-prime

      "GCD-Free Algorithms for Computing Modular Inverses"
      Marc Joye and Pascal Paillier, CHES 2003 (LNCS 2779)
      https://marcjoye.github.io/papers/JP03gcdfree.pdf

      This could be extended to cover other cases such as e=3 or e=17 but
      these days 65537 is the standard RSA public exponent
      */

      constexpr word e_w = 65537;

      const word phi_mod_e = ct_mod_word(phi_n, e_w);
      const word inv_phi_mod_e = inverse_mod_65537(phi_mod_e);
      BOTAN_DEBUG_ASSERT((inv_phi_mod_e * phi_mod_e) % e_w == 1);
      const word neg_inv_phi_mod_e = (e_w - inv_phi_mod_e);
      return ct_divide_word((phi_n * neg_inv_phi_mod_e) + 1, e_w);
   } else {
      // TODO possibly do something else taking advantage of the special structure here

      BOTAN_UNUSED(p, q);
      if(auto d = inverse_mod_general(e, phi_n)) {
         return *d;
      } else {
         throw Internal_Error("Failed to compute RSA secret exponent");
      }
   }
}

BigInt inverse_mod(const BigInt& n, const BigInt& mod) {
   BOTAN_ARG_CHECK(!mod.is_zero(), "modulus cannot be zero");
   BOTAN_ARG_CHECK(!mod.is_negative(), "modulus cannot be negative");
   BOTAN_ARG_CHECK(!n.is_negative(), "value cannot be negative");

   if(n.is_zero() || (n.is_even() && mod.is_even())) {
      return BigInt::zero();
   }

   if(n >= mod) {
      return inverse_mod(ct_modulo(n, mod), mod);
   }

   return inverse_mod_general(n, mod).value_or(BigInt::zero());
}

}  // namespace Botan
/*
* (C) 2018,2024,2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

// If the modulus is at most this many words, then use the stack instead
// of a heap variable for some temporary values
constexpr size_t MontgomeryUseStackLimit = 32;

}  // namespace

Montgomery_Params::Data::Data(const BigInt& p, const Barrett_Reduction& mod_p) {
   if(p.is_even() || p < 3) {
      throw Invalid_Argument("Montgomery_Params invalid modulus");
   }

   m_p = p;
   m_p_words = m_p.sig_words();
   m_p_dash = monty_inverse(m_p.word_at(0));

   const BigInt r = BigInt::power_of_2(m_p_words * WordInfo<word>::bits);

   m_r1 = mod_p.reduce(r);
   m_r2 = mod_p.square(m_r1);
   m_r3 = mod_p.multiply(m_r1, m_r2);

   // Barrett should be at least zero prefixing up to modulus size
   BOTAN_ASSERT_NOMSG(m_r1.size() >= m_p_words);
   BOTAN_ASSERT_NOMSG(m_r2.size() >= m_p_words);
   BOTAN_ASSERT_NOMSG(m_r3.size() >= m_p_words);
}

Montgomery_Params::Montgomery_Params(const BigInt& p, const Barrett_Reduction& mod_p) :
      m_data(std::make_shared<Data>(p, mod_p)) {}

Montgomery_Params::Montgomery_Params(const BigInt& p) :
      Montgomery_Params(p, Barrett_Reduction::for_secret_modulus(p)) {}

bool Montgomery_Params::operator==(const Montgomery_Params& other) const {
   if(this->m_data == other.m_data) {
      return true;
   }

   return (this->m_data->p() == other.m_data->p());
}

BigInt Montgomery_Params::redc(const BigInt& x, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();

   if(ws.size() < p_size) {
      ws.resize(p_size);
   }

   BigInt z = x;
   z.grow_to(2 * p_size);

   bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());

   return z;
}

BigInt Montgomery_Params::mul(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();
   BigInt z = BigInt::with_capacity(2 * p_size);
   this->mul(z, x, y, ws);
   return z;
}

void Montgomery_Params::mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }

   BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);
   BOTAN_DEBUG_ASSERT(y.sig_words() <= p_size);

   if(z.size() < 2 * p_size) {
      z.grow_to(2 * p_size);
   }

   bigint_mul(z.mutable_data(),
              z.size(),
              x._data(),
              x.size(),
              std::min(p_size, x.size()),
              y._data(),
              y.size(),
              std::min(p_size, y.size()),
              ws.data(),
              ws.size());

   bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
}

void Montgomery_Params::mul(BigInt& z, const BigInt& x, std::span<const word> y, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }
   if(z.size() < 2 * p_size) {
      z.grow_to(2 * p_size);
   }

   BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);

   bigint_mul(z.mutable_data(),
              z.size(),
              x._data(),
              x.size(),
              std::min(p_size, x.size()),
              y.data(),
              y.size(),
              std::min(p_size, y.size()),
              ws.data(),
              ws.size());

   bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
}

void Montgomery_Params::mul_by(BigInt& x, const BigInt& y, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();

   if(ws.size() < 4 * p_size) {
      ws.resize(4 * p_size);
   }

   word* z_data = ws.data();
   word* ws_data = &ws[2 * p_size];

   BOTAN_DEBUG_ASSERT(x.sig_words() <= p_size);

   bigint_mul(z_data,
              2 * p_size,
              x._data(),
              x.size(),
              std::min(p_size, x.size()),
              y._data(),
              y.size(),
              std::min(p_size, y.size()),
              ws_data,
              2 * p_size);

   bigint_monty_redc_inplace(z_data, this->p()._data(), p_size, this->p_dash(), ws_data, 2 * p_size);

   if(x.size() < 2 * p_size) {
      x.grow_to(2 * p_size);
   }
   copy_mem(x.mutable_data(), z_data, 2 * p_size);
}

BigInt Montgomery_Params::sqr(const BigInt& x, secure_vector<word>& ws) const {
   BOTAN_DEBUG_ASSERT(x.sig_words() <= this->p_words());
   return this->sqr(std::span{x._data(), x.size()}, ws);
}

BigInt Montgomery_Params::sqr(std::span<const word> x, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();
   BigInt z = BigInt::with_capacity(2 * p_size);
   this->sqr(z, x, ws);
   return z;
}

void Montgomery_Params::sqr(BigInt& z, const BigInt& x, secure_vector<word>& ws) const {
   this->sqr(z, std::span{x._data(), x.size()}, ws);
}

void Montgomery_Params::sqr(BigInt& z, std::span<const word> x, secure_vector<word>& ws) const {
   const size_t p_size = this->p_words();

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }

   if(z.size() < 2 * p_size) {
      z.grow_to(2 * p_size);
   }

   bigint_sqr(z.mutable_data(), z.size(), x.data(), x.size(), std::min(p_size, x.size()), ws.data(), ws.size());

   bigint_monty_redc_inplace(z.mutable_data(), this->p()._data(), p_size, this->p_dash(), ws.data(), ws.size());
}

Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, secure_vector<word> words) :
      m_params(params), m_v(std::move(words)) {
   BOTAN_ASSERT_NOMSG(m_v.size() == m_params.p_words());
}

Montgomery_Int Montgomery_Int::one(const Montgomery_Params& params) {
   return Montgomery_Int(params, params.R1(), false);
}

Montgomery_Int Montgomery_Int::from_wide_int(const Montgomery_Params& params, const BigInt& x) {
   secure_vector<word> ws;
   auto redc_x = params.mul(params.redc(x, ws), params.R3(), ws);
   return Montgomery_Int(params, redc_x, false);
}

Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, const BigInt& v, bool redc_needed) :
      m_params(params), m_v(m_params.p_words()) {
   BOTAN_ASSERT_NOMSG(v < m_params.p());

   const size_t p_size = m_params.p_words();

   auto v_span = v._as_span();

   if(v_span.size() > p_size) {
      // Safe to truncate the span since we already checked v < p
      v_span = v_span.first(p_size);
   }

   BOTAN_ASSERT_NOMSG(m_v.size() >= v_span.size());

   copy_mem(std::span{m_v}.first(v_span.size()), v_span);

   if(redc_needed) {
      secure_vector<word> ws;
      this->mul_by(m_params.R2()._as_span().first(p_size), ws);
   }
}

Montgomery_Int::Montgomery_Int(const Montgomery_Params& params, std::span<const word> words) :
      m_params(params), m_v(words.begin(), words.end()) {
   BOTAN_ARG_CHECK(m_v.size() == m_params.p_words(), "Invalid input span");
}

std::vector<uint8_t> Montgomery_Int::serialize() const {
   return value().serialize();
}

BigInt Montgomery_Int::value() const {
   secure_vector<word> ws(m_params.p_words());

   secure_vector<word> z = m_v;
   z.resize(2 * m_params.p_words());  // zero extend

   bigint_monty_redc_inplace(
      z.data(), m_params.p()._data(), m_params.p_words(), m_params.p_dash(), ws.data(), ws.size());

   return BigInt::_from_words(z);
}

Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const {
   BOTAN_STATE_CHECK(other.m_params == m_params);

   const size_t p_size = m_params.p_words();
   BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);

   secure_vector<word> z(2 * p_size);

   word* r = std::span{z}.first(p_size).data();
   word* t = std::span{z}.last(p_size).data();

   // t = this + other
   const word carry = bigint_add3(t, m_v.data(), p_size, other.m_v.data(), p_size);

   // Conditionally subtract r = t - p
   bigint_monty_maybe_sub(p_size, r, carry, t, m_params.p()._data());

   z.resize(p_size);  // truncate leaving only r
   return Montgomery_Int(m_params, std::move(z));
}

Montgomery_Int Montgomery_Int::operator-(const Montgomery_Int& other) const {
   BOTAN_STATE_CHECK(other.m_params == m_params);

   const size_t p_size = m_params.p_words();
   BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);

   secure_vector<word> t(p_size);
   const word borrow = bigint_sub3(t.data(), m_v.data(), p_size, other.m_v.data(), p_size);

   bigint_cnd_add(borrow, t.data(), m_params.p()._data(), p_size);

   return Montgomery_Int(m_params, std::move(t));
}

Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other, secure_vector<word>& ws) const {
   BOTAN_STATE_CHECK(other.m_params == m_params);

   const size_t p_size = m_params.p_words();
   BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.m_v.size() == p_size);

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }

   secure_vector<word> z(2 * p_size);

   bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.m_v.data(), p_size, p_size, ws.data(), ws.size());

   bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());
   z.resize(p_size);  // truncate off high zero words

   return Montgomery_Int(m_params, std::move(z));
}

Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other, secure_vector<word>& ws) {
   BOTAN_STATE_CHECK(other.m_params == m_params);
   return this->mul_by(std::span{other.m_v}, ws);
}

Montgomery_Int& Montgomery_Int::mul_by(std::span<const word> other, secure_vector<word>& ws) {
   const size_t p_size = m_params.p_words();
   BOTAN_ASSERT_NOMSG(m_v.size() == p_size && other.size() == p_size);

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }

   auto do_mul_by = [&](std::span<word> z) {
      bigint_mul(z.data(), z.size(), m_v.data(), p_size, p_size, other.data(), p_size, p_size, ws.data(), ws.size());

      bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());

      copy_mem(m_v, z.first(p_size));
   };

   if(p_size <= MontgomeryUseStackLimit) {
      std::array<word, 2 * MontgomeryUseStackLimit> z{};
      do_mul_by(z);
   } else {
      secure_vector<word> z(2 * p_size);
      do_mul_by(z);
   }

   return (*this);
}

Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector<word>& ws, size_t n) {
   const size_t p_size = m_params.p_words();
   BOTAN_ASSERT_NOMSG(m_v.size() == p_size);

   if(ws.size() < 2 * p_size) {
      ws.resize(2 * p_size);
   }

   auto do_sqr_n = [&](std::span<word> z) {
      for(size_t i = 0; i != n; ++i) {
         bigint_sqr(z.data(), 2 * p_size, m_v.data(), p_size, p_size, ws.data(), ws.size());

         bigint_monty_redc_inplace(z.data(), m_params.p()._data(), p_size, m_params.p_dash(), ws.data(), ws.size());

         copy_mem(m_v, std::span{z}.first(p_size));
      }
   };

   if(p_size <= MontgomeryUseStackLimit) {
      std::array<word, 2 * MontgomeryUseStackLimit> z{};
      do_sqr_n(z);
   } else {
      secure_vector<word> z(2 * p_size);
      do_sqr_n(z);
   }

   return (*this);
}

Montgomery_Int Montgomery_Int::square(secure_vector<word>& ws) const {
   auto z = (*this);
   z.square_this_n_times(ws, 1);
   return z;
}

}  // namespace Botan
/*
* Montgomery Exponentiation
* (C) 1999-2010,2012,2018,2025 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

class Montgomery_Exponentation_State final {
   public:
      Montgomery_Exponentation_State(const Montgomery_Int& g, size_t window_bits, bool const_time);

      Montgomery_Int exponentiation(const BigInt& k, size_t max_k_bits) const;

      Montgomery_Int exponentiation_vartime(const BigInt& k) const;

   private:
      Montgomery_Params m_params;
      std::vector<Montgomery_Int> m_g;
      size_t m_window_bits;
};

Montgomery_Exponentation_State::Montgomery_Exponentation_State(const Montgomery_Int& g,
                                                               size_t window_bits,
                                                               bool const_time) :
      m_params(g._params()), m_window_bits(window_bits == 0 ? 4 : window_bits) {
   if(m_window_bits < 1 || m_window_bits > 12) {  // really even 8 is too large ...
      throw Invalid_Argument("Invalid window bits for Montgomery exponentiation");
   }

   const size_t window_size = (static_cast<size_t>(1) << m_window_bits);

   m_g.reserve(window_size);

   m_g.push_back(Montgomery_Int::one(m_params));

   m_g.push_back(g);

   secure_vector<word> ws(2 * m_params.p_words());

   for(size_t i = 2; i != window_size; ++i) {
      m_g.push_back(m_g[1].mul(m_g[i - 1], ws));
   }

   if(const_time) {
      CT::poison_range(m_g);
   }
}

namespace {

void const_time_lookup(secure_vector<word>& output, const std::vector<Montgomery_Int>& g, size_t nibble) {
   BOTAN_ASSERT_NOMSG(g.size() % 2 == 0);  // actually a power of 2

   const size_t words = output.size();

   clear_mem(output.data(), output.size());

   for(size_t i = 0; i != g.size(); i += 2) {
      const secure_vector<word>& vec_0 = g[i].repr();
      const secure_vector<word>& vec_1 = g[i + 1].repr();

      BOTAN_ASSERT_NOMSG(vec_0.size() >= words && vec_1.size() >= words);

      const auto mask_0 = CT::Mask<word>::is_equal(nibble, i);
      const auto mask_1 = CT::Mask<word>::is_equal(nibble, i + 1);

      for(size_t w = 0; w != words; ++w) {
         output[w] |= mask_0.if_set_return(vec_0[w]);
         output[w] |= mask_1.if_set_return(vec_1[w]);
      }
   }
}

}  // namespace

Montgomery_Int Montgomery_Exponentation_State::exponentiation(const BigInt& scalar, size_t max_k_bits) const {
   BOTAN_DEBUG_ASSERT(scalar.bits() <= max_k_bits);
   // TODO add a const-time implementation of above assert and use it in release builds

   const size_t exp_nibbles = (max_k_bits + m_window_bits - 1) / m_window_bits;

   if(exp_nibbles == 0) {
      return Montgomery_Int::one(m_params);
   }

   secure_vector<word> e_bits(m_params.p_words());
   secure_vector<word> ws(2 * m_params.p_words());

   const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits * (exp_nibbles - 1), m_window_bits));
   Montgomery_Int x(m_params, std::span{e_bits});

   for(size_t i = exp_nibbles - 1; i > 0; --i) {
      x.square_this_n_times(ws, m_window_bits);
      const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits * (i - 1), m_window_bits));
      x.mul_by(e_bits, ws);
   }

   CT::unpoison(x);
   return x;
}

Montgomery_Int Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const {
   const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits;

   secure_vector<word> ws(2 * m_params.p_words());

   if(exp_nibbles == 0) {
      return Montgomery_Int::one(m_params);
   }

   Montgomery_Int x = m_g[scalar.get_substring(m_window_bits * (exp_nibbles - 1), m_window_bits)];

   for(size_t i = exp_nibbles - 1; i > 0; --i) {
      x.square_this_n_times(ws, m_window_bits);

      const uint32_t nibble = scalar.get_substring(m_window_bits * (i - 1), m_window_bits);
      if(nibble > 0) {
         x.mul_by(m_g[nibble], ws);
      }
   }

   CT::unpoison(x);
   return x;
}

std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(const Montgomery_Int& g,
                                                                       size_t window_bits,
                                                                       bool const_time) {
   return std::make_shared<const Montgomery_Exponentation_State>(g, window_bits, const_time);
}

std::shared_ptr<const Montgomery_Exponentation_State> monty_precompute(const Montgomery_Params& params,
                                                                       const BigInt& g,
                                                                       size_t window_bits,
                                                                       bool const_time) {
   BOTAN_ARG_CHECK(g < params.p(), "Montgomery base too big");
   Montgomery_Int monty_g(params, g);
   return monty_precompute(monty_g, window_bits, const_time);
}

Montgomery_Int monty_execute(const Montgomery_Exponentation_State& precomputed_state,
                             const BigInt& k,
                             size_t max_k_bits) {
   return precomputed_state.exponentiation(k, max_k_bits);
}

Montgomery_Int monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state, const BigInt& k) {
   return precomputed_state.exponentiation_vartime(k);
}

Montgomery_Int monty_multi_exp(
   const Montgomery_Params& params_p, const BigInt& x_bn, const BigInt& z1, const BigInt& y_bn, const BigInt& z2) {
   if(z1.is_negative() || z2.is_negative()) {
      throw Invalid_Argument("multi_exponentiate exponents must be positive");
   }

   const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);

   secure_vector<word> ws(2 * params_p.p_words());

   const Montgomery_Int one = Montgomery_Int::one(params_p);

   const Montgomery_Int x1(params_p, x_bn);
   const Montgomery_Int x2 = x1.square(ws);
   const Montgomery_Int x3 = x2.mul(x1, ws);

   const Montgomery_Int y1(params_p, y_bn);
   const Montgomery_Int y2 = y1.square(ws);
   const Montgomery_Int y3 = y2.mul(y1, ws);

   const Montgomery_Int y1x1 = y1.mul(x1, ws);
   const Montgomery_Int y1x2 = y1.mul(x2, ws);
   const Montgomery_Int y1x3 = y1.mul(x3, ws);

   const Montgomery_Int y2x1 = y2.mul(x1, ws);
   const Montgomery_Int y2x2 = y2.mul(x2, ws);
   const Montgomery_Int y2x3 = y2.mul(x3, ws);

   const Montgomery_Int y3x1 = y3.mul(x1, ws);
   const Montgomery_Int y3x2 = y3.mul(x2, ws);
   const Montgomery_Int y3x3 = y3.mul(x3, ws);

   const Montgomery_Int* M[16] = {&one,
                                  &x1,  // 0001
                                  &x2,  // 0010
                                  &x3,  // 0011
                                  &y1,  // 0100
                                  &y1x1,
                                  &y1x2,
                                  &y1x3,
                                  &y2,  // 1000
                                  &y2x1,
                                  &y2x2,
                                  &y2x3,
                                  &y3,  // 1100
                                  &y3x1,
                                  &y3x2,
                                  &y3x3};

   Montgomery_Int H = one;

   for(size_t i = 0; i != z_bits; i += 2) {
      if(i > 0) {
         H.square_this_n_times(ws, 2);
      }

      const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
      const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);

      const uint32_t z12 = (4 * z2_b) + z1_b;

      if(z12 > 0) {
         H.mul_by(*M[z12], ws);
      }
   }

   return H;
}

}  // namespace Botan
/*
* Number Theory Functions
* (C) 1999-2011,2016,2018,2019 Jack Lloyd
* (C) 2007,2008 Falko Strenzke, FlexSecure GmbH
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

/*
* Tonelli-Shanks algorithm
*/
BigInt sqrt_modulo_prime(const BigInt& a, const BigInt& p) {
   BOTAN_ARG_CHECK(p > 1, "invalid prime");
   BOTAN_ARG_CHECK(a < p, "value to solve for must be less than p");
   BOTAN_ARG_CHECK(a >= 0, "value to solve for must not be negative");

   // some very easy cases
   if(p == 2 || a <= 1) {
      return a;
   }

   BOTAN_ARG_CHECK(p.is_odd(), "invalid prime");

   if(jacobi(a, p) != 1) {  // not a quadratic residue
      return BigInt::from_s32(-1);
   }

   auto mod_p = Barrett_Reduction::for_public_modulus(p);
   const Montgomery_Params monty_p(p, mod_p);

   // If p == 3 (mod 4) there is a simple solution
   if(p % 4 == 3) {
      return monty_exp_vartime(monty_p, a, ((p + 1) >> 2)).value();
   }

   // Otherwise we have to use Shanks-Tonelli
   size_t s = low_zero_bits(p - 1);
   BigInt q = p >> s;

   q -= 1;
   q >>= 1;

   BigInt r = monty_exp_vartime(monty_p, a, q).value();
   BigInt n = mod_p.multiply(a, mod_p.square(r));
   r = mod_p.multiply(r, a);

   if(n == 1) {
      return r;
   }

   // find random quadratic nonresidue z
   word z = 2;
   for(;;) {
      if(jacobi(BigInt::from_word(z), p) == -1) {  // found one
         break;
      }

      z += 1;  // try next z

      /*
      * The expected number of tests to find a non-residue modulo a
      * prime is 2. If we have not found one after 256 then almost
      * certainly we have been given a non-prime p.
      */
      if(z >= 256) {
         return BigInt::from_s32(-1);
      }
   }

   BigInt c = monty_exp_vartime(monty_p, BigInt::from_word(z), (q << 1) + 1).value();

   while(n > 1) {
      q = n;

      size_t i = 0;
      while(q != 1) {
         q = mod_p.square(q);
         ++i;

         if(i >= s) {
            return BigInt::from_s32(-1);
         }
      }

      BOTAN_ASSERT_NOMSG(s >= (i + 1));  // No underflow!
      c = monty_exp_vartime(monty_p, c, BigInt::power_of_2(s - i - 1)).value();
      r = mod_p.multiply(r, c);
      c = mod_p.square(c);
      n = mod_p.multiply(n, c);

      // s decreases as the algorithm proceeds
      BOTAN_ASSERT_NOMSG(s >= i);
      s = i;
   }

   return r;
}

/*
* Calculate the Jacobi symbol
*/
int32_t jacobi(const BigInt& a, const BigInt& n) {
   if(n.is_even() || n < 2) {
      throw Invalid_Argument("jacobi: second argument must be odd and > 1");
   }

   BigInt x = a % n;
   BigInt y = n;
   int32_t J = 1;

   while(y > 1) {
      x %= y;
      if(x > y / 2) {
         x = y - x;
         if(y % 4 == 3) {
            J = -J;
         }
      }
      if(x.is_zero()) {
         return 0;
      }

      size_t shifts = low_zero_bits(x);
      x >>= shifts;
      if(shifts % 2 == 1) {
         word y_mod_8 = y % 8;
         if(y_mod_8 == 3 || y_mod_8 == 5) {
            J = -J;
         }
      }

      if(x % 4 == 3 && y % 4 == 3) {
         J = -J;
      }
      std::swap(x, y);
   }
   return J;
}

/*
* Square a BigInt
*/
BigInt square(const BigInt& x) {
   BigInt z = x;
   secure_vector<word> ws;
   z.square(ws);
   return z;
}

/*
* Return the number of 0 bits at the end of n
*/
size_t low_zero_bits(const BigInt& n) {
   size_t low_zero = 0;

   auto seen_nonempty_word = CT::Mask<word>::cleared();

   for(size_t i = 0; i != n.size(); ++i) {
      const word x = n.word_at(i);

      // ctz(0) will return sizeof(word)
      const size_t tz_x = ctz(x);

      // if x > 0 we want to count tz_x in total but not any
      // further words, so set the mask after the addition
      low_zero += seen_nonempty_word.if_not_set_return(tz_x);

      seen_nonempty_word |= CT::Mask<word>::expand(x);
   }

   // if we saw no words with x > 0 then n == 0 and the value we have
   // computed is meaningless. Instead return BigInt::zero() in that case.
   return static_cast<size_t>(seen_nonempty_word.if_set_return(low_zero));
}

/*
* Calculate the GCD in constant time
*/
BigInt gcd(const BigInt& a, const BigInt& b) {
   if(a.is_zero()) {
      return abs(b);
   }
   if(b.is_zero()) {
      return abs(a);
   }

   const size_t sz = std::max(a.sig_words(), b.sig_words());
   auto u = BigInt::with_capacity(sz);
   auto v = BigInt::with_capacity(sz);
   u += a;
   v += b;

   CT::poison_all(u, v);

   u.set_sign(BigInt::Positive);
   v.set_sign(BigInt::Positive);

   // In the worst case we have two fully populated big ints. After right
   // shifting so many times, we'll have reached the result for sure.
   const size_t loop_cnt = u.bits() + v.bits();

   // This temporary is big enough to hold all intermediate results of the
   // algorithm. No reallocation will happen during the loop.
   // Note however, that `ct_cond_assign()` will invalidate the 'sig_words'
   // cache, which _does not_ shrink the capacity of the underlying buffer.
   auto tmp = BigInt::with_capacity(sz);
   secure_vector<word> ws(sz * 2);
   size_t factors_of_two = 0;
   for(size_t i = 0; i != loop_cnt; ++i) {
      auto both_odd = CT::Mask<word>::expand_bool(u.is_odd()) & CT::Mask<word>::expand_bool(v.is_odd());

      // Subtract the smaller from the larger if both are odd
      auto u_gt_v = CT::Mask<word>::expand_bool(bigint_cmp(u._data(), u.size(), v._data(), v.size()) > 0);
      bigint_sub_abs(tmp.mutable_data(), u._data(), v._data(), sz, ws.data());
      u.ct_cond_assign((u_gt_v & both_odd).as_bool(), tmp);
      v.ct_cond_assign((~u_gt_v & both_odd).as_bool(), tmp);

      const auto u_is_even = CT::Mask<word>::expand_bool(u.is_even());
      const auto v_is_even = CT::Mask<word>::expand_bool(v.is_even());
      BOTAN_DEBUG_ASSERT((u_is_even | v_is_even).as_bool());

      // When both are even, we're going to eliminate a factor of 2.
      // We have to reapply this factor to the final result.
      factors_of_two += (u_is_even & v_is_even).if_set_return(1);

      // remove one factor of 2, if u is even
      bigint_shr2(tmp.mutable_data(), u._data(), sz, 1);
      u.ct_cond_assign(u_is_even.as_bool(), tmp);

      // remove one factor of 2, if v is even
      bigint_shr2(tmp.mutable_data(), v._data(), sz, 1);
      v.ct_cond_assign(v_is_even.as_bool(), tmp);
   }

   // The GCD (without factors of two) is either in u or v, the other one is
   // zero. The non-zero variable _must_ be odd, because all factors of two were
   // removed in the loop iterations above.
   BOTAN_DEBUG_ASSERT(u.is_zero() || v.is_zero());
   BOTAN_DEBUG_ASSERT(u.is_odd() || v.is_odd());

   // make sure that the GCD (without factors of two) is in u
   u.ct_cond_assign(u.is_even() /* .is_zero() would not be constant time */, v);

   // re-apply the factors of two
   u.ct_shift_left(factors_of_two);

   CT::unpoison_all(u, v);

   return u;
}

/*
* Calculate the LCM
*/
BigInt lcm(const BigInt& a, const BigInt& b) {
   if(a == b) {
      return a;
   }

   auto ab = a * b;
   ab.set_sign(BigInt::Positive);  // ignore the signs of a & b
   const auto g = gcd(a, b);
   return ct_divide(ab, g);
}

/*
* Modular Exponentiation
*/
BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod) {
   if(mod.is_negative() || mod == 1) {
      return BigInt::zero();
   }

   if(base.is_zero() || mod.is_zero()) {
      if(exp.is_zero()) {
         return BigInt::one();
      }
      return BigInt::zero();
   }

   auto reduce_mod = Barrett_Reduction::for_secret_modulus(mod);

   const size_t exp_bits = exp.bits();

   if(mod.is_odd()) {
      const Montgomery_Params monty_params(mod, reduce_mod);
      return monty_exp(monty_params, ct_modulo(base, mod), exp, exp_bits).value();
   }

   /*
   Support for even modulus is just a convenience and not considered
   cryptographically important, so this implementation is slow ...
   */
   BigInt accum = BigInt::one();
   BigInt g = ct_modulo(base, mod);
   BigInt t;

   for(size_t i = 0; i != exp_bits; ++i) {
      t = reduce_mod.multiply(g, accum);
      g = reduce_mod.square(g);
      accum.ct_cond_assign(exp.get_bit(i), t);
   }
   return accum;
}

BigInt is_perfect_square(const BigInt& C) {
   if(C < 1) {
      throw Invalid_Argument("is_perfect_square requires C >= 1");
   }
   if(C == 1) {
      return BigInt::one();
   }

   const size_t n = C.bits();
   const size_t m = (n + 1) / 2;
   const BigInt B = C + BigInt::power_of_2(m);

   BigInt X = BigInt::power_of_2(m) - 1;
   BigInt X2 = (X * X);

   for(;;) {
      X = (X2 + C) / (2 * X);
      X2 = (X * X);

      if(X2 < B) {
         break;
      }
   }

   if(X2 == C) {
      return X;
   } else {
      return BigInt::zero();
   }
}

/*
* Test for primality using Miller-Rabin
*/
bool is_prime(const BigInt& n, RandomNumberGenerator& rng, size_t prob, bool is_random) {
   if(n == 2) {
      return true;
   }
   if(n <= 1 || n.is_even()) {
      return false;
   }

   const size_t n_bits = n.bits();

   // Fast path testing for small numbers (<= 65521)
   if(n_bits <= 16) {
      const uint16_t num = static_cast<uint16_t>(n.word_at(0));

      return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
   }

   auto mod_n = Barrett_Reduction::for_secret_modulus(n);

   if(rng.is_seeded()) {
      const size_t t = miller_rabin_test_iterations(n_bits, prob, is_random);

      if(!is_miller_rabin_probable_prime(n, mod_n, rng, t)) {
         return false;
      }

      if(is_random) {
         return true;
      } else {
         return is_lucas_probable_prime(n, mod_n);
      }
   } else {
      return is_bailie_psw_probable_prime(n, mod_n);
   }
}

}  // namespace Botan
/*
* (C) 2016,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

bool is_lucas_probable_prime(const BigInt& C, const Barrett_Reduction& mod_C) {
   BOTAN_ARG_CHECK(C.is_positive(), "Argument should be a positive integer");

   if(C == 2 || C == 3 || C == 5 || C == 7 || C == 11 || C == 13) {
      return true;
   }

   if(C <= 1 || C.is_even()) {
      return false;
   }

   BigInt D = BigInt::from_word(5);

   for(;;) {
      int32_t j = jacobi(D, C);
      if(j == 0) {
         return false;
      }

      if(j == -1) {
         break;
      }

      // Check 5, -7, 9, -11, 13, -15, 17, ...
      if(D.is_negative()) {
         D.flip_sign();
         D += 2;
      } else {
         D += 2;
         D.flip_sign();
      }

      if(D == 17 && is_perfect_square(C).is_nonzero()) {
         return false;
      }
   }

   if(D.is_negative()) {
      D += C;
   }

   const BigInt K = C + 1;
   const size_t K_bits = K.bits() - 1;

   BigInt U = BigInt::one();
   BigInt V = BigInt::one();

   BigInt Ut;
   BigInt Vt;
   BigInt U2;
   BigInt V2;

   for(size_t i = 0; i != K_bits; ++i) {
      const bool k_bit = K.get_bit(K_bits - 1 - i);

      Ut = mod_C.multiply(U, V);

      Vt = mod_C.reduce(mod_C.square(V) + mod_C.multiply(D, mod_C.square(U)));
      Vt.ct_cond_add(Vt.is_odd(), C);
      Vt >>= 1;
      Vt = mod_C.reduce(Vt);

      U = Ut;
      V = Vt;

      U2 = mod_C.reduce(Ut + Vt);
      U2.ct_cond_add(U2.is_odd(), C);
      U2 >>= 1;

      V2 = mod_C.reduce(Vt + mod_C.multiply(Ut, D));
      V2.ct_cond_add(V2.is_odd(), C);
      V2 >>= 1;

      U.ct_cond_assign(k_bit, U2);
      V.ct_cond_assign(k_bit, V2);
   }

   return (U == 0);
}

bool is_bailie_psw_probable_prime(const BigInt& n, const Barrett_Reduction& mod_n) {
   if(n == 2) {
      return true;
   } else if(n <= 1 || n.is_even()) {
      return false;
   }

   Montgomery_Params monty_n(n, mod_n);
   const auto base = BigInt::from_word(2);
   return passes_miller_rabin_test(n, mod_n, monty_n, base) && is_lucas_probable_prime(n, mod_n);
}

bool passes_miller_rabin_test(const BigInt& n,
                              const Barrett_Reduction& mod_n,
                              const Montgomery_Params& monty_n,
                              const BigInt& a) {
   if(n < 3 || n.is_even()) {
      return false;
   }

   BOTAN_ASSERT_NOMSG(n > 1);

   const BigInt n_minus_1 = n - 1;
   const size_t s = low_zero_bits(n_minus_1);
   const BigInt nm1_s = n_minus_1 >> s;
   const size_t n_bits = n.bits();

   const size_t powm_window = 4;

   auto powm_a_n = monty_precompute(monty_n, a, powm_window);

   BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits).value();

   if(y == 1 || y == n_minus_1) {
      return true;
   }

   for(size_t i = 1; i != s; ++i) {
      y = mod_n.square(y);

      if(y == 1) {  // found a non-trivial square root
         return false;
      }

      /*
      -1 is the trivial square root of unity, so ``a`` is not a
      witness for this number - give up
      */
      if(y == n_minus_1) {
         return true;
      }
   }

   return false;
}

bool is_miller_rabin_probable_prime(const BigInt& n,
                                    const Barrett_Reduction& mod_n,
                                    RandomNumberGenerator& rng,
                                    size_t test_iterations) {
   if(n < 3 || n.is_even()) {
      return false;
   }

   Montgomery_Params monty_n(n, mod_n);

   for(size_t i = 0; i != test_iterations; ++i) {
      const BigInt a = BigInt::random_integer(rng, BigInt::from_word(2), n);

      if(!passes_miller_rabin_test(n, mod_n, monty_n, a)) {
         return false;
      }
   }

   // Failed to find a counterexample
   return true;
}

size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random) {
   const size_t base = (prob + 2) / 2;  // worst case 4^-t error rate

   /*
   * If the candidate prime was maliciously constructed, we can't rely
   * on arguments based on p being random.
   */
   if(!random) {
      return base;
   }

   /*
   * For randomly chosen numbers we can use the estimates from
   * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
   *
   * These values are derived from the inequality for p(k,t) given on
   * the second page.
   */
   if(prob <= 128) {
      if(n_bits >= 1536) {
         return 4;  // < 2^-133
      }
      if(n_bits >= 1024) {
         return 6;  // < 2^-133
      }
      if(n_bits >= 512) {
         return 12;  // < 2^-129
      }
      if(n_bits >= 256) {
         return 29;  // < 2^-128
      }
   }

   /*
   If the user desires a smaller error probability than we have
   precomputed error estimates for, just fall back to using the worst
   case error rate.
   */
   return base;
}

}  // namespace Botan
/*
* Small Primes Table
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


namespace Botan {

const uint16_t PRIMES[PRIME_TABLE_SIZE + 1] = {
   3,     5,     7,     11,    13,    17,    19,    23,    29,    31,    37,    41,    43,    47,    53,    59,
   61,    67,    71,    73,    79,    83,    89,    97,    101,   103,   107,   109,   113,   127,   131,   137,
   139,   149,   151,   157,   163,   167,   173,   179,   181,   191,   193,   197,   199,   211,   223,   227,
   229,   233,   239,   241,   251,   257,   263,   269,   271,   277,   281,   283,   293,   307,   311,   313,
   317,   331,   337,   347,   349,   353,   359,   367,   373,   379,   383,   389,   397,   401,   409,   419,
   421,   431,   433,   439,   443,   449,   457,   461,   463,   467,   479,   487,   491,   499,   503,   509,
   521,   523,   541,   547,   557,   563,   569,   571,   577,   587,   593,   599,   601,   607,   613,   617,
   619,   631,   641,   643,   647,   653,   659,   661,   673,   677,   683,   691,   701,   709,   719,   727,
   733,   739,   743,   751,   757,   761,   769,   773,   787,   797,   809,   811,   821,   823,   827,   829,
   839,   853,   857,   859,   863,   877,   881,   883,   887,   907,   911,   919,   929,   937,   941,   947,
   953,   967,   971,   977,   983,   991,   997,   1009,  1013,  1019,  1021,  1031,  1033,  1039,  1049,  1051,
   1061,  1063,  1069,  1087,  1091,  1093,  1097,  1103,  1109,  1117,  1123,  1129,  1151,  1153,  1163,  1171,
   1181,  1187,  1193,  1201,  1213,  1217,  1223,  1229,  1231,  1237,  1249,  1259,  1277,  1279,  1283,  1289,
   1291,  1297,  1301,  1303,  1307,  1319,  1321,  1327,  1361,  1367,  1373,  1381,  1399,  1409,  1423,  1427,
   1429,  1433,  1439,  1447,  1451,  1453,  1459,  1471,  1481,  1483,  1487,  1489,  1493,  1499,  1511,  1523,
   1531,  1543,  1549,  1553,  1559,  1567,  1571,  1579,  1583,  1597,  1601,  1607,  1609,  1613,  1619,  1621,
   1627,  1637,  1657,  1663,  1667,  1669,  1693,  1697,  1699,  1709,  1721,  1723,  1733,  1741,  1747,  1753,
   1759,  1777,  1783,  1787,  1789,  1801,  1811,  1823,  1831,  1847,  1861,  1867,  1871,  1873,  1877,  1879,
   1889,  1901,  1907,  1913,  1931,  1933,  1949,  1951,  1973,  1979,  1987,  1993,  1997,  1999,  2003,  2011,
   2017,  2027,  2029,  2039,  2053,  2063,  2069,  2081,  2083,  2087,  2089,  2099,  2111,  2113,  2129,  2131,
   2137,  2141,  2143,  2153,  2161,  2179,  2203,  2207,  2213,  2221,  2237,  2239,  2243,  2251,  2267,  2269,
   2273,  2281,  2287,  2293,  2297,  2309,  2311,  2333,  2339,  2341,  2347,  2351,  2357,  2371,  2377,  2381,
   2383,  2389,  2393,  2399,  2411,  2417,  2423,  2437,  2441,  2447,  2459,  2467,  2473,  2477,  2503,  2521,
   2531,  2539,  2543,  2549,  2551,  2557,  2579,  2591,  2593,  2609,  2617,  2621,  2633,  2647,  2657,  2659,
   2663,  2671,  2677,  2683,  2687,  2689,  2693,  2699,  2707,  2711,  2713,  2719,  2729,  2731,  2741,  2749,
   2753,  2767,  2777,  2789,  2791,  2797,  2801,  2803,  2819,  2833,  2837,  2843,  2851,  2857,  2861,  2879,
   2887,  2897,  2903,  2909,  2917,  2927,  2939,  2953,  2957,  2963,  2969,  2971,  2999,  3001,  3011,  3019,
   3023,  3037,  3041,  3049,  3061,  3067,  3079,  3083,  3089,  3109,  3119,  3121,  3137,  3163,  3167,  3169,
   3181,  3187,  3191,  3203,  3209,  3217,  3221,  3229,  3251,  3253,  3257,  3259,  3271,  3299,  3301,  3307,
   3313,  3319,  3323,  3329,  3331,  3343,  3347,  3359,  3361,  3371,  3373,  3389,  3391,  3407,  3413,  3433,
   3449,  3457,  3461,  3463,  3467,  3469,  3491,  3499,  3511,  3517,  3527,  3529,  3533,  3539,  3541,  3547,
   3557,  3559,  3571,  3581,  3583,  3593,  3607,  3613,  3617,  3623,  3631,  3637,  3643,  3659,  3671,  3673,
   3677,  3691,  3697,  3701,  3709,  3719,  3727,  3733,  3739,  3761,  3767,  3769,  3779,  3793,  3797,  3803,
   3821,  3823,  3833,  3847,  3851,  3853,  3863,  3877,  3881,  3889,  3907,  3911,  3917,  3919,  3923,  3929,
   3931,  3943,  3947,  3967,  3989,  4001,  4003,  4007,  4013,  4019,  4021,  4027,  4049,  4051,  4057,  4073,
   4079,  4091,  4093,  4099,  4111,  4127,  4129,  4133,  4139,  4153,  4157,  4159,  4177,  4201,  4211,  4217,
   4219,  4229,  4231,  4241,  4243,  4253,  4259,  4261,  4271,  4273,  4283,  4289,  4297,  4327,  4337,  4339,
   4349,  4357,  4363,  4373,  4391,  4397,  4409,  4421,  4423,  4441,  4447,  4451,  4457,  4463,  4481,  4483,
   4493,  4507,  4513,  4517,  4519,  4523,  4547,  4549,  4561,  4567,  4583,  4591,  4597,  4603,  4621,  4637,
   4639,  4643,  4649,  4651,  4657,  4663,  4673,  4679,  4691,  4703,  4721,  4723,  4729,  4733,  4751,  4759,
   4783,  4787,  4789,  4793,  4799,  4801,  4813,  4817,  4831,  4861,  4871,  4877,  4889,  4903,  4909,  4919,
   4931,  4933,  4937,  4943,  4951,  4957,  4967,  4969,  4973,  4987,  4993,  4999,  5003,  5009,  5011,  5021,
   5023,  5039,  5051,  5059,  5077,  5081,  5087,  5099,  5101,  5107,  5113,  5119,  5147,  5153,  5167,  5171,
   5179,  5189,  5197,  5209,  5227,  5231,  5233,  5237,  5261,  5273,  5279,  5281,  5297,  5303,  5309,  5323,
   5333,  5347,  5351,  5381,  5387,  5393,  5399,  5407,  5413,  5417,  5419,  5431,  5437,  5441,  5443,  5449,
   5471,  5477,  5479,  5483,  5501,  5503,  5507,  5519,  5521,  5527,  5531,  5557,  5563,  5569,  5573,  5581,
   5591,  5623,  5639,  5641,  5647,  5651,  5653,  5657,  5659,  5669,  5683,  5689,  5693,  5701,  5711,  5717,
   5737,  5741,  5743,  5749,  5779,  5783,  5791,  5801,  5807,  5813,  5821,  5827,  5839,  5843,  5849,  5851,
   5857,  5861,  5867,  5869,  5879,  5881,  5897,  5903,  5923,  5927,  5939,  5953,  5981,  5987,  6007,  6011,
   6029,  6037,  6043,  6047,  6053,  6067,  6073,  6079,  6089,  6091,  6101,  6113,  6121,  6131,  6133,  6143,
   6151,  6163,  6173,  6197,  6199,  6203,  6211,  6217,  6221,  6229,  6247,  6257,  6263,  6269,  6271,  6277,
   6287,  6299,  6301,  6311,  6317,  6323,  6329,  6337,  6343,  6353,  6359,  6361,  6367,  6373,  6379,  6389,
   6397,  6421,  6427,  6449,  6451,  6469,  6473,  6481,  6491,  6521,  6529,  6547,  6551,  6553,  6563,  6569,
   6571,  6577,  6581,  6599,  6607,  6619,  6637,  6653,  6659,  6661,  6673,  6679,  6689,  6691,  6701,  6703,
   6709,  6719,  6733,  6737,  6761,  6763,  6779,  6781,  6791,  6793,  6803,  6823,  6827,  6829,  6833,  6841,
   6857,  6863,  6869,  6871,  6883,  6899,  6907,  6911,  6917,  6947,  6949,  6959,  6961,  6967,  6971,  6977,
   6983,  6991,  6997,  7001,  7013,  7019,  7027,  7039,  7043,  7057,  7069,  7079,  7103,  7109,  7121,  7127,
   7129,  7151,  7159,  7177,  7187,  7193,  7207,  7211,  7213,  7219,  7229,  7237,  7243,  7247,  7253,  7283,
   7297,  7307,  7309,  7321,  7331,  7333,  7349,  7351,  7369,  7393,  7411,  7417,  7433,  7451,  7457,  7459,
   7477,  7481,  7487,  7489,  7499,  7507,  7517,  7523,  7529,  7537,  7541,  7547,  7549,  7559,  7561,  7573,
   7577,  7583,  7589,  7591,  7603,  7607,  7621,  7639,  7643,  7649,  7669,  7673,  7681,  7687,  7691,  7699,
   7703,  7717,  7723,  7727,  7741,  7753,  7757,  7759,  7789,  7793,  7817,  7823,  7829,  7841,  7853,  7867,
   7873,  7877,  7879,  7883,  7901,  7907,  7919,  7927,  7933,  7937,  7949,  7951,  7963,  7993,  8009,  8011,
   8017,  8039,  8053,  8059,  8069,  8081,  8087,  8089,  8093,  8101,  8111,  8117,  8123,  8147,  8161,  8167,
   8171,  8179,  8191,  8209,  8219,  8221,  8231,  8233,  8237,  8243,  8263,  8269,  8273,  8287,  8291,  8293,
   8297,  8311,  8317,  8329,  8353,  8363,  8369,  8377,  8387,  8389,  8419,  8423,  8429,  8431,  8443,  8447,
   8461,  8467,  8501,  8513,  8521,  8527,  8537,  8539,  8543,  8563,  8573,  8581,  8597,  8599,  8609,  8623,
   8627,  8629,  8641,  8647,  8663,  8669,  8677,  8681,  8689,  8693,  8699,  8707,  8713,  8719,  8731,  8737,
   8741,  8747,  8753,  8761,  8779,  8783,  8803,  8807,  8819,  8821,  8831,  8837,  8839,  8849,  8861,  8863,
   8867,  8887,  8893,  8923,  8929,  8933,  8941,  8951,  8963,  8969,  8971,  8999,  9001,  9007,  9011,  9013,
   9029,  9041,  9043,  9049,  9059,  9067,  9091,  9103,  9109,  9127,  9133,  9137,  9151,  9157,  9161,  9173,
   9181,  9187,  9199,  9203,  9209,  9221,  9227,  9239,  9241,  9257,  9277,  9281,  9283,  9293,  9311,  9319,
   9323,  9337,  9341,  9343,  9349,  9371,  9377,  9391,  9397,  9403,  9413,  9419,  9421,  9431,  9433,  9437,
   9439,  9461,  9463,  9467,  9473,  9479,  9491,  9497,  9511,  9521,  9533,  9539,  9547,  9551,  9587,  9601,
   9613,  9619,  9623,  9629,  9631,  9643,  9649,  9661,  9677,  9679,  9689,  9697,  9719,  9721,  9733,  9739,
   9743,  9749,  9767,  9769,  9781,  9787,  9791,  9803,  9811,  9817,  9829,  9833,  9839,  9851,  9857,  9859,
   9871,  9883,  9887,  9901,  9907,  9923,  9929,  9931,  9941,  9949,  9967,  9973,  10007, 10009, 10037, 10039,
   10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169,
   10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313,
   10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 10463,
   10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631,
   10639, 10651, 10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781,
   10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939,
   10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 11093,
   11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257,
   11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411,
   11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 11579,
   11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743,
   11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887, 11897,
   11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037,
   12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163,
   12197, 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329,
   12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479,
   12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601,
   12611, 12613, 12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743,
   12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911,
   12917, 12919, 12923, 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037,
   13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183,
   13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339,
   13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513,
   13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687,
   13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 13807,
   13829, 13831, 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963,
   13967, 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149,
   14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327,
   14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479,
   14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629,
   14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759,
   14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891,
   14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077,
   15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 15227,
   15233, 15241, 15259, 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349,
   15359, 15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493,
   15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647,
   15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787,
   15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923,
   15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091,
   16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253,
   16267, 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 16433,
   16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 16607,
   16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759,
   16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931,
   16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077,
   17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239,
   17257, 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393,
   17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 17539,
   17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707,
   17713, 17729, 17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881,
   17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, 17977, 17981, 17987, 17989, 18013,
   18041, 18043, 18047, 18049, 18059, 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, 18149,
   18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, 18251, 18253, 18257, 18269, 18287, 18289, 18301,
   18307, 18311, 18313, 18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, 18443,
   18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617,
   18637, 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, 18757, 18773, 18787, 18793, 18797,
   18803, 18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009, 19013,
   19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207,
   19211, 19213, 19219, 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, 19333, 19373, 19379,
   19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471,
   19477, 19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, 19577, 19583, 19597, 19603, 19609,
   19661, 19681, 19687, 19697, 19699, 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, 19801,
   19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961, 19963,
   19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071, 20089, 20101, 20107,
   20113, 20117, 20123, 20129, 20143, 20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249,
   20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, 20359, 20369, 20389, 20393, 20399,
   20407, 20411, 20431, 20441, 20443, 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563,
   20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719, 20731, 20743, 20747, 20749,
   20753, 20759, 20771, 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897, 20899, 20903, 20921,
   20929, 20939, 20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061,
   21067, 21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, 21179, 21187, 21191, 21193, 21211,
   21221, 21227, 21247, 21269, 21277, 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, 21391,
   21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491, 21493, 21499, 21503, 21517, 21521, 21523, 21529,
   21557, 21559, 21563, 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, 21649, 21661, 21673,
   21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839,
   21841, 21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003,
   22013, 22027, 22031, 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, 22129,
   22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, 22247, 22259, 22271, 22273, 22277, 22279, 22283,
   22291, 22303, 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, 22447, 22453, 22469,
   22481, 22483, 22501, 22511, 22531, 22541, 22543, 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639,
   22643, 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, 22739, 22741, 22751, 22769, 22777,
   22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961,
   22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, 23059, 23063, 23071,
   23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203, 23209, 23227, 23251,
   23269, 23279, 23291, 23293, 23297, 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431,
   23447, 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, 23563, 23567, 23581, 23593, 23599,
   23603, 23609, 23623, 23627, 23629, 23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, 23747,
   23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833, 23857, 23869, 23873, 23879, 23887,
   23893, 23899, 23909, 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, 24019, 24023, 24029,
   24043, 24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151,
   24169, 24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371,
   24373, 24379, 24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517, 24527,
   24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, 24671, 24677, 24683, 24691, 24697, 24709, 24733,
   24749, 24763, 24767, 24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889, 24907, 24917,
   24919, 24923, 24943, 24953, 24967, 24971, 24977, 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087,
   25097, 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, 25189, 25219, 25229, 25237, 25243,
   25247, 25253, 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, 25409,
   25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, 25577, 25579, 25583,
   25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693, 25703, 25717, 25733,
   25741, 25747, 25759, 25763, 25771, 25793, 25799, 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903,
   25913, 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003, 26017, 26021, 26029, 26041,
   26053, 26083, 26099, 26107, 26111, 26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, 26209,
   26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339, 26347, 26357, 26371,
   26387, 26393, 26399, 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539,
   26557, 26561, 26573, 26591, 26597, 26627, 26633, 26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701,
   26711, 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, 26813, 26821, 26833, 26839, 26849,
   26861, 26863, 26879, 26881, 26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, 26993,
   27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, 27091, 27103, 27107, 27109, 27127, 27143, 27179,
   27191, 27197, 27211, 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367,
   27397, 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541,
   27551, 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, 27697, 27701, 27733, 27737, 27739,
   27743, 27749, 27751, 27763, 27767, 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, 27847,
   27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019,
   28027, 28031, 28051, 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151, 28163, 28181, 28183,
   28201, 28211, 28219, 28229, 28277, 28279, 28283, 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393,
   28403, 28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, 28537, 28541, 28547,
   28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, 28657,
   28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793, 28807,
   28813, 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, 28949, 28961, 28979,
   29009, 29017, 29021, 29023, 29027, 29033, 29059, 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153,
   29167, 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251, 29269, 29287, 29297, 29303, 29311,
   29327, 29333, 29339, 29347, 29363, 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453,
   29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, 29581, 29587, 29599, 29611, 29629, 29633, 29641,
   29663, 29669, 29671, 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851,
   29863, 29867, 29873, 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047,
   30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137, 30139, 30161, 30169, 30181, 30187,
   30197, 30203, 30211, 30223, 30241, 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, 30347,
   30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, 30491, 30493, 30497, 30509, 30517, 30529, 30539,
   30553, 30557, 30559, 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, 30697, 30703, 30707,
   30713, 30727, 30757, 30763, 30773, 30781, 30803, 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869,
   30871, 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051,
   31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31183, 31189,
   31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327,
   31333, 31337, 31357, 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, 31513, 31517, 31531,
   31541, 31543, 31547, 31567, 31573, 31583, 31601, 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699,
   31721, 31723, 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847, 31849, 31859, 31873, 31883,
   31891, 31907, 31957, 31963, 31973, 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069,
   32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, 32191, 32203, 32213, 32233,
   32237, 32251, 32257, 32261, 32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, 32359, 32363, 32369,
   32371, 32377, 32381, 32401, 32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507,
   32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, 32611, 32621, 32633, 32647, 32653,
   32687, 32693, 32707, 32713, 32717, 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833,
   32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957, 32969, 32971, 32983, 32987, 32993,
   32999, 33013, 33023, 33029, 33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, 33151,
   33161, 33179, 33181, 33191, 33199, 33203, 33211, 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331,
   33343, 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, 33457, 33461, 33469, 33479, 33487,
   33493, 33503, 33521, 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, 33617,
   33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769,
   33773, 33791, 33797, 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911, 33923, 33931,
   33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147,
   34157, 34159, 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297, 34301,
   34303, 34313, 34319, 34327, 34337, 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, 34469,
   34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, 34607,
   34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, 34747, 34757, 34759,
   34763, 34781, 34807, 34819, 34841, 34843, 34847, 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949,
   34961, 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089, 35099, 35107, 35111, 35117,
   35129, 35141, 35149, 35153, 35159, 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311,
   35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437, 35447, 35449, 35461,
   35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, 35597, 35603, 35617,
   35671, 35677, 35729, 35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851,
   35863, 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, 35977, 35983, 35993, 35999, 36007,
   36011, 36013, 36017, 36037, 36061, 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, 36187,
   36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293, 36299, 36307, 36313, 36319, 36341, 36343,
   36353, 36373, 36383, 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523, 36527, 36529,
   36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683,
   36691, 36697, 36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821,
   36833, 36847, 36857, 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, 36973,
   36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, 37061, 37087, 37097, 37117, 37123, 37139, 37159,
   37171, 37181, 37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, 37313, 37321, 37337,
   37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501,
   37507, 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591, 37607, 37619,
   37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, 37831,
   37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, 37991, 37993, 37997,
   38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, 38183, 38189, 38197, 38201,
   38219, 38231, 38237, 38239, 38261, 38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351,
   38371, 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, 38557, 38561, 38567, 38569, 38593,
   38603, 38609, 38611, 38629, 38639, 38651, 38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, 38723,
   38729, 38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, 38851, 38861, 38867, 38873, 38891,
   38903, 38917, 38921, 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, 39043, 39047, 39079,
   39089, 39097, 39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217,
   39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371,
   39373, 39383, 39397, 39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, 39551,
   39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, 39671, 39679, 39703, 39709, 39719, 39727, 39733,
   39749, 39761, 39769, 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869, 39877,
   39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063,
   40087, 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231,
   40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, 40459,
   40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609,
   40627, 40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, 40801, 40813, 40819,
   40823, 40829, 40841, 40847, 40849, 40853, 40867, 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961,
   40973, 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117, 41131, 41141, 41143,
   41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263,
   41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443, 41453, 41467,
   41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, 41609, 41611, 41617,
   41621, 41627, 41641, 41647, 41651, 41659, 41669, 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771, 41777,
   41801, 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, 41903, 41911, 41927, 41941, 41947,
   41953, 41957, 41959, 41969, 41981, 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, 42083,
   42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, 42193, 42197, 42209, 42221, 42223, 42227, 42239,
   42257, 42281, 42283, 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403,
   42407, 42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487, 42491, 42499, 42509, 42533,
   42557, 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, 42683, 42689, 42697, 42701, 42703,
   42709, 42719, 42727, 42737, 42743, 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, 42853,
   42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019,
   43037, 43049, 43051, 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, 43201, 43207, 43223,
   43237, 43261, 43271, 43283, 43291, 43313, 43319, 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441,
   43451, 43457, 43481, 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, 43607, 43609, 43613,
   43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, 43787,
   43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987,
   43991, 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, 44111, 44119, 44123,
   44129, 44131, 44159, 44171, 44179, 44189, 44201, 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273,
   44279, 44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, 44453, 44483, 44491, 44497, 44501,
   44507, 44519, 44531, 44533, 44537, 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647,
   44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, 44753, 44771, 44773, 44777, 44789, 44797, 44809,
   44819, 44839, 44843, 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971,
   44983, 44987, 45007, 45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179,
   45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, 45293, 45307, 45317, 45319, 45329, 45337, 45341,
   45343, 45361, 45377, 45389, 45403, 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, 45541,
   45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659, 45667, 45673, 45677, 45691, 45697, 45707,
   45737, 45751, 45757, 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, 45863, 45869, 45887,
   45893, 45943, 45949, 45953, 45959, 45971, 45979, 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093,
   46099, 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271,
   46273, 46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, 46381, 46399, 46411, 46439, 46441, 46447, 46451,
   46457, 46471, 46477, 46489, 46499, 46507, 46511, 46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619,
   46633, 46639, 46643, 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, 46751, 46757, 46769,
   46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957,
   46993, 46997, 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, 47123, 47129, 47137, 47143, 47147,
   47149, 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, 47309, 47317, 47339,
   47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, 47491, 47497, 47501, 47507,
   47513, 47521, 47527, 47533, 47543, 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, 47639, 47653, 47657,
   47659, 47681, 47699, 47701, 47711, 47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809,
   47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, 47947, 47951, 47963, 47969, 47977,
   47981, 48017, 48023, 48029, 48049, 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187,
   48193, 48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337, 48341, 48353, 48371, 48383,
   48397, 48407, 48409, 48413, 48437, 48449, 48463, 48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, 48533,
   48539, 48541, 48563, 48571, 48589, 48593, 48611, 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731,
   48733, 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, 48821, 48823, 48847, 48857, 48859,
   48869, 48871, 48883, 48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033, 49037,
   49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123, 49139, 49157, 49169, 49171, 49177, 49193, 49199,
   49201, 49207, 49211, 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367, 49369,
   49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, 49463, 49477, 49481, 49499, 49523, 49529, 49531,
   49537, 49547, 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711,
   49727, 49739, 49741, 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, 49871,
   49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, 50047,
   50051, 50053, 50069, 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131, 50147, 50153, 50159, 50177,
   50207, 50221, 50227, 50231, 50261, 50263, 50273, 50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363,
   50377, 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, 50527, 50539, 50543, 50549,
   50551, 50581, 50587, 50591, 50593, 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767,
   50773, 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, 50923, 50929, 50951,
   50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071, 51109, 51131, 51133, 51137,
   51151, 51157, 51169, 51193, 51197, 51199, 51203, 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307,
   51329, 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, 51427, 51431, 51437, 51439, 51449,
   51461, 51473, 51479, 51481, 51487, 51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, 51599,
   51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, 51691, 51713, 51719, 51721, 51749, 51767, 51769,
   51787, 51797, 51803, 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, 51907, 51913, 51929,
   51941, 51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103, 52121,
   52127, 52147, 52153, 52163, 52177, 52181, 52183, 52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289,
   52291, 52301, 52313, 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, 52511,
   52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, 52583, 52609, 52627, 52631, 52639, 52667, 52673,
   52691, 52697, 52709, 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, 52817, 52837, 52859,
   52861, 52879, 52883, 52889, 52901, 52903, 52919, 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003,
   53017, 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161, 53171,
   53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, 53353,
   53359, 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, 53527, 53549, 53551,
   53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, 53657, 53681, 53693, 53699,
   53717, 53719, 53731, 53759, 53773, 53777, 53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887,
   53891, 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001, 54011, 54013, 54037, 54049,
   54059, 54083, 54091, 54101, 54121, 54133, 54139, 54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, 54277,
   54287, 54293, 54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, 54401, 54403, 54409, 54413, 54419,
   54421, 54437, 54443, 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, 54547, 54559, 54563,
   54577, 54581, 54583, 54601, 54617, 54623, 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, 54727,
   54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877, 54881, 54907, 54917, 54919, 54941,
   54949, 54959, 54973, 54979, 54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103, 55109,
   55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, 55219, 55229, 55243, 55249, 55259, 55291, 55313,
   55331, 55333, 55337, 55339, 55343, 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501,
   55511, 55529, 55541, 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667,
   55673, 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, 55799, 55807, 55813, 55817, 55819,
   55823, 55829, 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, 55967,
   55987, 55997, 56003, 56009, 56039, 56041, 56053, 56081, 56087, 56093, 56099, 56101, 56113, 56123, 56131, 56149,
   56167, 56171, 56179, 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299, 56311, 56333, 56359,
   56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501,
   56503, 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, 56633, 56659, 56663,
   56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809, 56813,
   56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951, 56957,
   56963, 56983, 56989, 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, 57107, 57119, 57131,
   57139, 57143, 57149, 57163, 57173, 57179, 57191, 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271,
   57283, 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, 57397, 57413, 57427, 57457, 57467,
   57487, 57493, 57503, 57527, 57529, 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, 57667,
   57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, 57751, 57773, 57781, 57787, 57791, 57793, 57803,
   57809, 57829, 57839, 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991,
   58013, 58027, 58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129, 58147, 58151, 58153,
   58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, 58243, 58271, 58309, 58313, 58321,
   58337, 58363, 58367, 58369, 58379, 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, 58477,
   58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, 58603, 58613, 58631, 58657, 58661, 58679, 58687,
   58693, 58699, 58711, 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, 58897, 58901, 58907,
   58909, 58913, 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051,
   59053, 59063, 59069, 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, 59167, 59183, 59197,
   59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, 59369,
   59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509,
   59513, 59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, 59663, 59669, 59671,
   59693, 59699, 59707, 59723, 59729, 59743, 59747, 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879,
   59887, 59921, 59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041, 60077, 60083, 60089,
   60091, 60101, 60103, 60107, 60127, 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257,
   60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, 60373, 60383, 60397, 60413, 60427, 60443, 60449,
   60457, 60493, 60497, 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, 60631, 60637, 60647,
   60649, 60659, 60661, 60679, 60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793,
   60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917, 60919, 60923, 60937, 60943, 60953, 60961,
   61001, 61007, 61027, 61031, 61043, 61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211,
   61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343, 61357, 61363, 61379, 61381, 61403,
   61409, 61417, 61441, 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, 61543, 61547, 61553, 61559,
   61561, 61583, 61603, 61609, 61613, 61627, 61631, 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703,
   61717, 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933,
   61949, 61961, 61967, 61979, 61981, 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053, 62057, 62071, 62081,
   62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171, 62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273,
   62297, 62299, 62303, 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, 62467, 62473, 62477,
   62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, 62639,
   62653, 62659, 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827, 62851,
   62861, 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, 62987, 62989, 63029,
   63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, 63131, 63149, 63179, 63197, 63199, 63211, 63241,
   63247, 63277, 63281, 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, 63377, 63389, 63391,
   63397, 63409, 63419, 63421, 63439, 63443, 63463, 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541,
   63559, 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, 63649, 63659, 63667, 63671, 63689,
   63691, 63697, 63703, 63709, 63719, 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823,
   63839, 63841, 63853, 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007, 64013, 64019, 64033,
   64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151, 64153, 64157, 64171, 64187, 64189, 64217, 64223, 64231,
   64237, 64271, 64279, 64283, 64301, 64303, 64319, 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451,
   64453, 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, 64609, 64613, 64621, 64627, 64633,
   64661, 64663, 64667, 64679, 64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849, 64853,
   64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, 64951, 64969, 64997, 65003, 65011, 65027, 65029,
   65033, 65053, 65063, 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173,
   65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, 65293, 65309, 65323, 65327, 65353, 65357, 65371,
   65381, 65393, 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, 65519, 65521, 0};

}
/*
* Modular Reducer
* (C) 1999-2011,2018,2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

Modular_Reducer::Modular_Reducer(const BigInt& mod) : m_mod_words(mod.sig_words()) {
   if(mod < 0) {
      throw Invalid_Argument("Modular_Reducer: modulus must be positive");
   }

   m_modulus = mod;
}

BigInt Modular_Reducer::reduce(const BigInt& x) const {
   return ct_modulo(x, m_modulus);
}

}  // namespace Botan
/*
* PEM Encoding/Decoding
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan::PEM_Code {

namespace {

std::string linewrap(size_t width, std::string_view in) {
   std::string out;
   for(size_t i = 0; i != in.size(); ++i) {
      if(i > 0 && i % width == 0) {
         out.push_back('\n');
      }
      out.push_back(in[i]);
   }
   if(!out.empty() && out[out.size() - 1] != '\n') {
      out.push_back('\n');
   }

   return out;
}

}  // namespace

/*
* PEM encode BER/DER-encoded objects
*/
std::string encode(const uint8_t der[], size_t length, std::string_view label, size_t width) {
   const std::string PEM_HEADER = fmt("-----BEGIN {}-----\n", label);
   const std::string PEM_TRAILER = fmt("-----END {}-----\n", label);

   return (PEM_HEADER + linewrap(width, base64_encode(der, length)) + PEM_TRAILER);
}

/*
* Decode PEM down to raw BER/DER
*/
secure_vector<uint8_t> decode_check_label(DataSource& source, std::string_view label_want) {
   std::string label_got;
   secure_vector<uint8_t> ber = decode(source, label_got);
   if(label_got != label_want) {
      throw Decoding_Error(fmt("PEM: Label mismatch, wanted '{}' got '{}'", label_want, label_got));
   }

   return ber;
}

/*
* Decode PEM down to raw BER/DER
*/
secure_vector<uint8_t> decode(DataSource& source, std::string& label) {
   const size_t RANDOM_CHAR_LIMIT = 8;

   label.clear();

   const std::string PEM_HEADER1 = "-----BEGIN ";
   const std::string PEM_HEADER2 = "-----";
   size_t position = 0;

   while(position != PEM_HEADER1.length()) {
      auto b = source.read_byte();

      if(!b) {
         throw Decoding_Error("PEM: No PEM header found");
      }
      if(static_cast<char>(*b) == PEM_HEADER1[position]) {
         ++position;
      } else if(position >= RANDOM_CHAR_LIMIT) {
         throw Decoding_Error("PEM: Malformed PEM header");
      } else {
         position = 0;
      }
   }
   position = 0;
   while(position != PEM_HEADER2.length()) {
      auto b = source.read_byte();

      if(!b) {
         throw Decoding_Error("PEM: No PEM header found");
      }
      if(static_cast<char>(*b) == PEM_HEADER2[position]) {
         ++position;
      } else if(position > 0) {
         throw Decoding_Error("PEM: Malformed PEM header");
      }

      if(position == 0) {
         label += static_cast<char>(*b);
      }
   }

   std::vector<char> b64;

   const std::string PEM_TRAILER = fmt("-----END {}-----", label);
   position = 0;
   while(position != PEM_TRAILER.length()) {
      auto b = source.read_byte();

      if(!b) {
         throw Decoding_Error("PEM: No PEM trailer found");
      }
      if(static_cast<char>(*b) == PEM_TRAILER[position]) {
         ++position;
      } else if(position > 0) {
         throw Decoding_Error("PEM: Malformed PEM trailer");
      }

      if(position == 0) {
         b64.push_back(*b);
      }
   }

   return base64_decode(b64.data(), b64.size());
}

secure_vector<uint8_t> decode_check_label(std::string_view pem, std::string_view label_want) {
   DataSource_Memory src(pem);
   return decode_check_label(src, label_want);
}

secure_vector<uint8_t> decode(std::string_view pem, std::string& label) {
   DataSource_Memory src(pem);
   return decode(src, label);
}

/*
* Search for a PEM signature
*/
bool matches(DataSource& source, std::string_view extra, size_t search_range) {
   const std::string PEM_HEADER = fmt("-----BEGIN {}", extra);

   secure_vector<uint8_t> search_buf(search_range);
   const size_t got = source.peek(search_buf.data(), search_buf.size(), 0);

   if(got < PEM_HEADER.length()) {
      return false;
   }

   size_t index = 0;

   for(size_t j = 0; j != got; ++j) {
      if(static_cast<char>(search_buf[j]) == PEM_HEADER[index]) {
         ++index;
      } else {
         index = 0;
      }

      if(index == PEM_HEADER.size()) {
         return true;
      }
   }

   return false;
}

}  // namespace Botan::PEM_Code
/*
* EME Base Class
* (C) 1999-2008 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_EME_OAEP)
#endif

#if defined(BOTAN_HAS_EME_PKCS1)
#endif

#if defined(BOTAN_HAS_EME_RAW)
#endif

namespace Botan {

std::unique_ptr<EME> EME::create(std::string_view algo_spec) {
#if defined(BOTAN_HAS_EME_RAW)
   if(algo_spec == "Raw") {
      return std::make_unique<EME_Raw>();
   }
#endif

#if defined(BOTAN_HAS_EME_PKCS1)
   // TODO(Botan4) Remove all but "PKCS1v15"
   if(algo_spec == "PKCS1v15" || algo_spec == "EME-PKCS1-v1_5") {
      return std::make_unique<EME_PKCS1v15>();
   }
#endif

#if defined(BOTAN_HAS_EME_OAEP)
   SCAN_Name req(algo_spec);

   // TODO(Botan4) Remove all but "OAEP"
   if(req.algo_name() == "OAEP" || req.algo_name() == "EME-OAEP" || req.algo_name() == "EME1") {
      if(req.arg_count() == 1 || ((req.arg_count() == 2 || req.arg_count() == 3) && req.arg(1) == "MGF1")) {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<OAEP>(std::move(hash), req.arg(2, ""));
         }
      } else if(req.arg_count() == 2 || req.arg_count() == 3) {
         auto mgf_params = parse_algorithm_name(req.arg(1));

         if(mgf_params.size() == 2 && mgf_params[0] == "MGF1") {
            auto hash = HashFunction::create(req.arg(0));
            auto mgf1_hash = HashFunction::create(mgf_params[1]);

            if(hash && mgf1_hash) {
               return std::make_unique<OAEP>(std::move(hash), std::move(mgf1_hash), req.arg(2, ""));
            }
         }
      }
   }
#endif

   throw Algorithm_Not_Found(algo_spec);
}

EME::~EME() = default;

}  // namespace Botan
/*
* PK Key
* (C) 1999-2010,2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_RSA)
#endif

#if defined(BOTAN_HAS_CLASSICMCELIECE)
#endif

#if defined(BOTAN_HAS_DSA)
#endif

#if defined(BOTAN_HAS_DL_GROUP)
#endif

#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
#endif

#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
#endif

#if defined(BOTAN_HAS_ECDSA)
#endif

#if defined(BOTAN_HAS_ECGDSA)
#endif

#if defined(BOTAN_HAS_ECKCDSA)
#endif

#if defined(BOTAN_HAS_ED25519)
#endif

#if defined(BOTAN_HAS_ED448)
#endif

#if defined(BOTAN_HAS_GOST_34_10_2001)
#endif

#if defined(BOTAN_HAS_ELGAMAL)
#endif

#if defined(BOTAN_HAS_ECDH)
#endif

#if defined(BOTAN_HAS_X25519)
#endif

#if defined(BOTAN_HAS_X448)
#endif

#if defined(BOTAN_HAS_MCELIECE)
#endif

#if defined(BOTAN_HAS_FRODOKEM)
#endif

#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
#endif

#if defined(BOTAN_HAS_ML_KEM)
#endif

#if defined(BOTAN_HAS_HSS_LMS)
#endif

#if defined(BOTAN_HAS_XMSS_RFC8391)
#endif

#if defined(BOTAN_HAS_SM2)
#endif

#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
#endif

#if defined(BOTAN_HAS_ML_DSA)
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
#endif

namespace Botan {

std::unique_ptr<Public_Key> load_public_key(const AlgorithmIdentifier& alg_id,
                                            [[maybe_unused]] std::span<const uint8_t> key_bits) {
   const std::string oid_str = alg_id.oid().to_formatted_string();
   const std::vector<std::string> alg_info = split_on(oid_str, '/');
   std::string_view alg_name = alg_info[0];

#if defined(BOTAN_HAS_RSA)
   if(alg_name == "RSA") {
      return std::make_unique<RSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_X25519)
   if(alg_name == "X25519" || alg_name == "Curve25519") {
      return std::make_unique<X25519_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_X448)
   if(alg_name == "X448") {
      return std::make_unique<X448_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_MCELIECE)
   if(alg_name == "McEliece") {
      return std::make_unique<McEliece_PublicKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_FRODOKEM)
   if(alg_name == "FrodoKEM" || alg_name.starts_with("FrodoKEM-") || alg_name.starts_with("eFrodoKEM-")) {
      return std::make_unique<FrodoKEM_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
   if(alg_name == "Kyber" || alg_name.starts_with("Kyber-")) {
      return std::make_unique<Kyber_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ML_KEM)
   if(alg_name.starts_with("ML-KEM-")) {
      return std::make_unique<ML_KEM_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECDSA)
   if(alg_name == "ECDSA") {
      return std::make_unique<ECDSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECDH)
   if(alg_name == "ECDH") {
      return std::make_unique<ECDH_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
   if(alg_name == "DH") {
      return std::make_unique<DH_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_DSA)
   if(alg_name == "DSA") {
      return std::make_unique<DSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ELGAMAL)
   if(alg_name == "ElGamal") {
      return std::make_unique<ElGamal_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECGDSA)
   if(alg_name == "ECGDSA") {
      return std::make_unique<ECGDSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECKCDSA)
   if(alg_name == "ECKCDSA") {
      return std::make_unique<ECKCDSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ED25519)
   if(alg_name == "Ed25519") {
      return std::make_unique<Ed25519_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ED448)
   if(alg_name == "Ed448") {
      return std::make_unique<Ed448_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_GOST_34_10_2001)
   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512") {
      return std::make_unique<GOST_3410_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_SM2)
   if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc") {
      return std::make_unique<SM2_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_XMSS_RFC8391)
   if(alg_name == "XMSS") {
      return std::make_unique<XMSS_PublicKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
   if(alg_name == "Dilithium" || alg_name.starts_with("Dilithium-")) {
      return std::make_unique<Dilithium_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ML_DSA)
   if(alg_name.starts_with("ML-DSA-")) {
      return std::make_unique<ML_DSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_HSS_LMS)
   if(alg_name == "HSS-LMS") {
      return std::make_unique<HSS_LMS_PublicKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
   if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) {
      return std::make_unique<SphincsPlus_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
   if(alg_name.starts_with("SLH-DSA-") || alg_name.starts_with("Hash-SLH-DSA-")) {
      return std::make_unique<SLH_DSA_PublicKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_CLASSICMCELIECE)
   if(alg_name.starts_with("ClassicMcEliece")) {
      return std::make_unique<Classic_McEliece_PublicKey>(alg_id, key_bits);
   }
#endif

   throw Decoding_Error(fmt("Unknown or unavailable public key algorithm '{}'", alg_name));
}

std::unique_ptr<Private_Key> load_private_key(const AlgorithmIdentifier& alg_id,
                                              [[maybe_unused]] std::span<const uint8_t> key_bits) {
   const std::string oid_str = alg_id.oid().to_formatted_string();
   const std::vector<std::string> alg_info = split_on(oid_str, '/');
   std::string_view alg_name = alg_info[0];

#if defined(BOTAN_HAS_RSA)
   if(alg_name == "RSA") {
      return std::make_unique<RSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_X25519)
   if(alg_name == "X25519" || alg_name == "Curve25519") {
      return std::make_unique<X25519_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_X448)
   if(alg_name == "X448") {
      return std::make_unique<X448_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECDSA)
   if(alg_name == "ECDSA") {
      return std::make_unique<ECDSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECDH)
   if(alg_name == "ECDH") {
      return std::make_unique<ECDH_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
   if(alg_name == "DH") {
      return std::make_unique<DH_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_DSA)
   if(alg_name == "DSA") {
      return std::make_unique<DSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_FRODOKEM)
   if(alg_name == "FrodoKEM" || alg_name.starts_with("FrodoKEM-") || alg_name.starts_with("eFrodoKEM-")) {
      return std::make_unique<FrodoKEM_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
   if(alg_name == "Kyber" || alg_name.starts_with("Kyber-")) {
      return std::make_unique<Kyber_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ML_KEM)
   if(alg_name.starts_with("ML-KEM-")) {
      return std::make_unique<ML_KEM_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_MCELIECE)
   if(alg_name == "McEliece") {
      return std::make_unique<McEliece_PrivateKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECGDSA)
   if(alg_name == "ECGDSA") {
      return std::make_unique<ECGDSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ECKCDSA)
   if(alg_name == "ECKCDSA") {
      return std::make_unique<ECKCDSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ED25519)
   if(alg_name == "Ed25519") {
      return std::make_unique<Ed25519_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ED448)
   if(alg_name == "Ed448") {
      return std::make_unique<Ed448_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_GOST_34_10_2001)
   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512") {
      return std::make_unique<GOST_3410_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_SM2)
   if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc") {
      return std::make_unique<SM2_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ELGAMAL)
   if(alg_name == "ElGamal") {
      return std::make_unique<ElGamal_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_XMSS_RFC8391)
   if(alg_name == "XMSS") {
      return std::make_unique<XMSS_PrivateKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
   if(alg_name == "Dilithium" || alg_name.starts_with("Dilithium-")) {
      return std::make_unique<Dilithium_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_ML_DSA)
   if(alg_name.starts_with("ML-DSA-")) {
      return std::make_unique<ML_DSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_HSS_LMS)
   if(alg_name == "HSS-LMS-Private-Key") {
      return std::make_unique<HSS_LMS_PrivateKey>(key_bits);
   }
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
   if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) {
      return std::make_unique<SphincsPlus_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
   if(alg_name.starts_with("SLH-DSA-") || alg_name.starts_with("Hash-SLH-DSA-")) {
      return std::make_unique<SLH_DSA_PrivateKey>(alg_id, key_bits);
   }
#endif

#if defined(BOTAN_HAS_CLASSICMCELIECE)
   if(alg_name.starts_with("ClassicMcEliece")) {
      return std::make_unique<Classic_McEliece_PrivateKey>(alg_id, key_bits);
   }
#endif

   throw Decoding_Error(fmt("Unknown or unavailable public key algorithm '{}'", alg_name));
}

std::unique_ptr<Private_Key> create_ec_private_key(std::string_view alg_name,
                                                   const EC_Group& ec_group,
                                                   RandomNumberGenerator& rng) {
   // Potentially unused if all EC algorithms are disabled
   BOTAN_UNUSED(alg_name, ec_group, rng);

#if defined(BOTAN_HAS_ECDSA)
   if(alg_name == "ECDSA") {
      return std::make_unique<ECDSA_PrivateKey>(rng, ec_group);
   }
#endif

#if defined(BOTAN_HAS_ECDH)
   if(alg_name == "ECDH") {
      return std::make_unique<ECDH_PrivateKey>(rng, ec_group);
   }
#endif

#if defined(BOTAN_HAS_ECKCDSA)
   if(alg_name == "ECKCDSA") {
      return std::make_unique<ECKCDSA_PrivateKey>(rng, ec_group);
   }
#endif

#if defined(BOTAN_HAS_GOST_34_10_2001)
   if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512") {
      return std::make_unique<GOST_3410_PrivateKey>(rng, ec_group);
   }
#endif

#if defined(BOTAN_HAS_SM2)
   if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc") {
      return std::make_unique<SM2_PrivateKey>(rng, ec_group);
   }
#endif

#if defined(BOTAN_HAS_ECGDSA)
   if(alg_name == "ECGDSA") {
      return std::make_unique<ECGDSA_PrivateKey>(rng, ec_group);
   }
#endif

   return nullptr;
}

std::unique_ptr<Private_Key> create_private_key(std::string_view alg_name,
                                                RandomNumberGenerator& rng,
                                                std::string_view params,
                                                std::string_view provider) {
   /*
   * Default paramaters are chosen for work factor > 2**128 where possible
   */

#if defined(BOTAN_HAS_X25519)
   if(alg_name == "X25519" || alg_name == "Curve25519") {
      return std::make_unique<X25519_PrivateKey>(rng);
   }
#endif

#if defined(BOTAN_HAS_X448)
   if(alg_name == "X448") {
      return std::make_unique<X448_PrivateKey>(rng);
   }
#endif

#if defined(BOTAN_HAS_RSA)
   if(alg_name == "RSA") {
      const size_t modulus_bits = params.empty() ? 3072 : to_u32bit(params);
      return std::make_unique<RSA_PrivateKey>(rng, modulus_bits);
   }
#endif

#if defined(BOTAN_HAS_MCELIECE)
   if(alg_name == "McEliece") {
      const auto [n, t] = [&]() -> std::pair<size_t, size_t> {
         if(params.empty()) {
            return {2960, 57};
         }

         const auto mce_params = split_on(params, ',');

         if(mce_params.size() != 2) {
            throw Invalid_Argument(fmt("create_private_key: invalid McEliece parameters '{}'", params));
         }

         const size_t mce_n = to_u32bit(mce_params[0]);
         const size_t mce_t = to_u32bit(mce_params[1]);
         return {mce_n, mce_t};
      }();

      return std::make_unique<McEliece_PrivateKey>(rng, n, t);
   }
#endif
#if defined(BOTAN_HAS_CLASSICMCELIECE)
   if(alg_name == "ClassicMcEliece") {
      auto cmce_params_set = params.empty() ? Classic_McEliece_Parameter_Set::ClassicMcEliece_6960119f
                                            : Classic_McEliece_Parameter_Set::from_string(params);
      return std::make_unique<Classic_McEliece_PrivateKey>(rng, cmce_params_set);
   }
#endif

#if defined(BOTAN_HAS_FRODOKEM)
   if(alg_name == "FrodoKEM") {
      const auto mode = params.empty() ? FrodoKEMMode::FrodoKEM976_SHAKE : FrodoKEMMode(params);
      return std::make_unique<FrodoKEM_PrivateKey>(rng, mode);
   }
#endif

#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
   if(alg_name == "Kyber") {
      const auto mode = [&]() -> KyberMode {
         if(params.empty()) {
            return KyberMode::Kyber1024_R3;
         }
         return KyberMode(params);
      }();

      return std::make_unique<Kyber_PrivateKey>(rng, mode);
   }
#endif

#if defined(BOTAN_HAS_ML_KEM)
   if(alg_name == "ML-KEM") {
      const auto mode = [&]() -> ML_KEM_Mode {
         if(params.empty()) {
            return ML_KEM_Mode::ML_KEM_768;
         }
         return ML_KEM_Mode(params);
      }();

      return std::make_unique<ML_KEM_PrivateKey>(rng, mode);
   }
#endif

#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)
   if(alg_name == "Dilithium" || alg_name.starts_with("Dilithium-")) {
      const auto mode = [&]() -> DilithiumMode {
         if(params.empty()) {
            return DilithiumMode::Dilithium6x5;
         }
         return DilithiumMode(params);
      }();

      return std::make_unique<Dilithium_PrivateKey>(rng, mode);
   }
#endif

#if defined(BOTAN_HAS_ML_DSA)
   if(alg_name == "ML-DSA") {
      const auto mode = [&]() -> ML_DSA_Mode {
         if(params.empty()) {
            return ML_DSA_Mode::ML_DSA_6x5;
         }
         return ML_DSA_Mode(params);
      }();

      return std::make_unique<ML_DSA_PrivateKey>(rng, mode);
   }
#endif

#if defined(BOTAN_HAS_HSS_LMS)
   if(alg_name == "HSS-LMS") {
      const auto hss_params = [&]() -> std::string {
         if(params.empty()) {
            return "SHA-256,HW(10,1)";
         } else {
            return std::string(params);
         }
      }();
      return std::make_unique<HSS_LMS_PrivateKey>(rng, hss_params);
   }
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
   if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus") {
      auto sphincs_params = Sphincs_Parameters::create(params);

      return std::make_unique<SphincsPlus_PrivateKey>(rng, sphincs_params);
   }
#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
   if(alg_name == "SLH-DSA") {
      auto slh_dsa_params = SLH_DSA_Parameters::create(params);

      return std::make_unique<SLH_DSA_PrivateKey>(rng, slh_dsa_params);
   }
#endif

#if defined(BOTAN_HAS_XMSS_RFC8391)
   if(alg_name == "XMSS") {
      const auto xmss_oid = [&]() -> XMSS_Parameters::xmss_algorithm_t {
         if(params.empty()) {
            return XMSS_Parameters::XMSS_SHA2_10_512;
         }
         return XMSS_Parameters(params).oid();
      }();

      return std::make_unique<XMSS_PrivateKey>(xmss_oid, rng);
   }
#endif

#if defined(BOTAN_HAS_ED25519)
   if(alg_name == "Ed25519") {
      return std::make_unique<Ed25519_PrivateKey>(rng);
   }
#endif

#if defined(BOTAN_HAS_ED448)
   if(alg_name == "Ed448") {
      return std::make_unique<Ed448_PrivateKey>(rng);
   }
#endif

   // ECC crypto
#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)

   if(alg_name == "ECDSA" || alg_name == "ECDH" || alg_name == "ECKCDSA" || alg_name == "ECGDSA" || alg_name == "SM2" ||
      alg_name == "SM2_Sig" || alg_name == "SM2_Enc" || alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" ||
      alg_name == "GOST-34.10-2012-512") {
      const std::string group_id = [&]() -> std::string {
         if(!params.empty()) {
            return std::string(params);
         }
         if(alg_name == "SM2" || alg_name == "SM2_Enc" || alg_name == "SM2_Sig") {
            return "sm2p256v1";
         }
         if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256") {
            return "gost_256A";
         }
         if(alg_name == "GOST-34.10-2012-512") {
            return "gost_512A";
         }
         if(alg_name == "ECGDSA") {
            return "brainpool256r1";
         }
         return "secp256r1";
      }();

      if(EC_Group::supports_named_group(group_id)) {
         auto ec_group = EC_Group::from_name(group_id);
         return create_ec_private_key(alg_name, ec_group, rng);
      } else {
         return {};
      }
   }
#endif

   // DL crypto
#if defined(BOTAN_HAS_DL_GROUP)
   if(alg_name == "DH" || alg_name == "DSA" || alg_name == "ElGamal") {
      const std::string group_id = [&]() -> std::string {
         if(!params.empty()) {
            return std::string(params);
         }
         if(alg_name == "DSA") {
            return "dsa/botan/2048";
         }
         return "modp/ietf/2048";
      }();

      auto modp_group = DL_Group::from_name(group_id);

   #if defined(BOTAN_HAS_DIFFIE_HELLMAN)
      if(alg_name == "DH") {
         return std::make_unique<DH_PrivateKey>(rng, modp_group);
      }
   #endif

   #if defined(BOTAN_HAS_DSA)
      if(alg_name == "DSA") {
         return std::make_unique<DSA_PrivateKey>(rng, modp_group);
      }
   #endif

   #if defined(BOTAN_HAS_ELGAMAL)
      if(alg_name == "ElGamal") {
         return std::make_unique<ElGamal_PrivateKey>(rng, modp_group);
      }
   #endif
   }
#endif

   BOTAN_UNUSED(alg_name, rng, params, provider);

   return std::unique_ptr<Private_Key>();
}

std::vector<std::string> probe_provider_private_key(std::string_view alg_name,
                                                    const std::vector<std::string>& possible) {
   std::vector<std::string> providers;

   for(auto&& prov : possible) {
      if(prov == "base") {
         providers.push_back(prov);
      }
   }

   BOTAN_UNUSED(alg_name);

   return providers;
}
}  // namespace Botan
/*
* PK Key Types
* (C) 1999-2007 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

const BigInt& Asymmetric_Key::get_int_field(std::string_view field) const {
   throw Unknown_PK_Field_Name(algo_name(), field);
}

OID Asymmetric_Key::object_identifier() const {
   try {
      return OID::from_string(algo_name());
   } catch(Lookup_Error&) {
      throw Lookup_Error(fmt("Public key algorithm {} has no defined OIDs", algo_name()));
   }
}

Signature_Format Asymmetric_Key::_default_x509_signature_format() const {
   if(_signature_element_size_for_DER_encoding()) {
      return Signature_Format::DerSequence;
   } else {
      return Signature_Format::Standard;
   }
}

std::string create_hex_fingerprint(const uint8_t bits[], size_t bits_len, std::string_view hash_name) {
   auto hash_fn = HashFunction::create_or_throw(hash_name);
   const std::string hex_hash = hex_encode(hash_fn->process(bits, bits_len));

   std::string fprint;

   for(size_t i = 0; i != hex_hash.size(); i += 2) {
      if(i != 0) {
         fprint.push_back(':');
      }

      fprint.push_back(hex_hash[i]);
      fprint.push_back(hex_hash[i + 1]);
   }

   return fprint;
}

std::vector<uint8_t> Public_Key::subject_public_key() const {
   std::vector<uint8_t> output;

   DER_Encoder(output)
      .start_sequence()
      .encode(algorithm_identifier())
      .encode(public_key_bits(), ASN1_Type::BitString)
      .end_cons();

   return output;
}

secure_vector<uint8_t> Private_Key::private_key_info() const {
   const size_t PKCS8_VERSION = 0;

   return DER_Encoder()
      .start_sequence()
      .encode(PKCS8_VERSION)
      .encode(pkcs8_algorithm_identifier())
      .encode(private_key_bits(), ASN1_Type::OctetString)
      .end_cons()
      .get_contents();
}

secure_vector<uint8_t> Private_Key::raw_private_key_bits() const {
   throw Not_Implemented(algo_name() + " does not implement raw_private_key_bits");
}

/*
* Hash of the X.509 subjectPublicKey encoding
*/
std::string Public_Key::fingerprint_public(std::string_view hash_algo) const {
   return create_hex_fingerprint(subject_public_key(), hash_algo);
}

/*
* Hash of the PKCS #8 encoding for this key object
*/
std::string Private_Key::fingerprint_private(std::string_view hash_algo) const {
   return create_hex_fingerprint(private_key_bits(), hash_algo);
}

std::unique_ptr<PK_Ops::Encryption> Public_Key::create_encryption_op(RandomNumberGenerator& /*rng*/,
                                                                     std::string_view /*params*/,
                                                                     std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support encryption", algo_name()));
}

std::unique_ptr<PK_Ops::KEM_Encryption> Public_Key::create_kem_encryption_op(std::string_view /*params*/,
                                                                             std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support KEM encryption", algo_name()));
}

std::unique_ptr<PK_Ops::Verification> Public_Key::create_verification_op(std::string_view /*params*/,
                                                                         std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support verification", algo_name()));
}

std::unique_ptr<PK_Ops::Verification> Public_Key::create_x509_verification_op(const AlgorithmIdentifier& /*params*/,
                                                                              std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support X.509 verification", algo_name()));
}

std::unique_ptr<PK_Ops::Decryption> Private_Key::create_decryption_op(RandomNumberGenerator& /*rng*/,
                                                                      std::string_view /*params*/,
                                                                      std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support decryption", algo_name()));
}

std::unique_ptr<PK_Ops::KEM_Decryption> Private_Key::create_kem_decryption_op(RandomNumberGenerator& /*rng*/,
                                                                              std::string_view /*params*/,
                                                                              std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support KEM decryption", algo_name()));
}

std::unique_ptr<PK_Ops::Signature> Private_Key::create_signature_op(RandomNumberGenerator& /*rng*/,
                                                                    std::string_view /*params*/,
                                                                    std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support signatures", algo_name()));
}

std::unique_ptr<PK_Ops::Key_Agreement> Private_Key::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
                                                                            std::string_view /*params*/,
                                                                            std::string_view /*provider*/) const {
   throw Lookup_Error(fmt("{} does not support key agreement", algo_name()));
}

}  // namespace Botan
/*
* PK Operation Types
* (C) 2010,2015,2023 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_RAW_HASH_FN)
#endif

namespace Botan {

AlgorithmIdentifier PK_Ops::Signature::algorithm_identifier() const {
   throw Not_Implemented("This signature scheme does not have an algorithm identifier available");
}

PK_Ops::Encryption_with_EME::Encryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}

PK_Ops::Encryption_with_EME::~Encryption_with_EME() = default;

size_t PK_Ops::Encryption_with_EME::max_input_bits() const {
   return 8 * m_eme->maximum_input_size(max_ptext_input_bits());
}

std::vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
   const size_t max_input_bits = max_ptext_input_bits();
   const size_t max_input_bytes = (max_input_bits + 7) / 8;
   BOTAN_ARG_CHECK(msg.size() <= max_input_bytes, "Plaintext too large");

   secure_vector<uint8_t> eme_output(max_input_bits);
   const size_t written = m_eme->pad(eme_output, msg, max_input_bits, rng);
   return raw_encrypt(std::span{eme_output}.first(written), rng);
}

PK_Ops::Decryption_with_EME::Decryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}

PK_Ops::Decryption_with_EME::~Decryption_with_EME() = default;

secure_vector<uint8_t> PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) {
   const secure_vector<uint8_t> raw = raw_decrypt(ctext);

   secure_vector<uint8_t> ptext(raw.size());
   auto len = m_eme->unpad(ptext, raw);

   valid_mask = CT::Mask<uint8_t>::from_choice(len.has_value()).if_set_return(0xFF);

   /*
   This is potentially not const time, depending on how std::vector is
   implemented. But since we are always reducing length, it should
   just amount to setting the member var holding the length. Resizing
   downwards is guaranteed to not change the capacity, and since we
   set ctext to the maximum possible size (equal to the raw input) we
   know that this is always, if anything, resizing smaller than the
   capacity, so no reallocation occurs.
   */

   ptext.resize(len.value_or(0));
   return ptext;
}

PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
   if(kdf != "Raw") {
      m_kdf = KDF::create_or_throw(kdf);
   }
}

PK_Ops::Key_Agreement_with_KDF::~Key_Agreement_with_KDF() = default;

secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
                                                             std::span<const uint8_t> other_key,
                                                             std::span<const uint8_t> salt) {
   if(!salt.empty() && m_kdf == nullptr) {
      throw Invalid_Argument("PK_Key_Agreement::derive_key requires a KDF to use a salt");
   }

   secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size());
   if(m_kdf) {
      return m_kdf->derive_key(key_len, z, salt.data(), salt.size());
   }
   return z;
}

namespace {

std::unique_ptr<HashFunction> create_signature_hash(std::string_view padding) {
   if(auto hash = HashFunction::create(padding)) {
      return hash;
   }

   SCAN_Name req(padding);

   if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
      if(auto hash = HashFunction::create(req.arg(0))) {
         return hash;
      }
   }

#if defined(BOTAN_HAS_RAW_HASH_FN)
   if(req.algo_name() == "Raw") {
      if(req.arg_count() == 0) {
         return std::make_unique<RawHashFunction>("Raw", 0);
      }

      if(req.arg_count() == 1) {
         if(auto hash = HashFunction::create(req.arg(0))) {
            return std::make_unique<RawHashFunction>(std::move(hash));
         }
      }
   }
#endif

   throw Algorithm_Not_Found(padding);
}

}  // namespace

PK_Ops::Signature_with_Hash::Signature_with_Hash(std::string_view hash) :
      Signature(), m_hash(create_signature_hash(hash)) {}

PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default;

#if defined(BOTAN_HAS_RFC6979_GENERATOR)
std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
   std::string hash = m_hash->name();
   if(hash != "Raw") {
      return hash;
   }
   return "SHA-512";
}
#endif

std::string PK_Ops::Signature_with_Hash::hash_function() const {
   return m_hash->name();
}

void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
   m_hash->update(msg);
}

std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
   return raw_sign(msg, rng);
}

PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) :
      Verification(), m_hash(create_signature_hash(padding)) {}

PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default;

std::string PK_Ops::Verification_with_Hash::hash_function() const {
   return m_hash->name();
}

PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id,
                                                       std::string_view pk_algo,
                                                       bool allow_null_parameters) {
   const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');

   if(oid_info.size() != 2 || oid_info[0] != pk_algo) {
      throw Decoding_Error(
         fmt("Unexpected AlgorithmIdentifier OID {} in association with {} key", alg_id.oid(), pk_algo));
   }

   if(!alg_id.parameters_are_empty()) {
      if(alg_id.parameters_are_null()) {
         if(!allow_null_parameters) {
            throw Decoding_Error(fmt("Unexpected NULL AlgorithmIdentifier parameters for {}", pk_algo));
         }
      } else {
         throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier parameters for {}", pk_algo));
      }
   }

   m_hash = HashFunction::create_or_throw(oid_info[1]);
}

void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
   m_hash->update(msg);
}

bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
   return verify(msg, sig);
}

size_t PK_Ops::KEM_Encryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
   if(m_kdf) {
      return desired_shared_key_len;
   } else {
      return this->raw_kem_shared_key_length();
   }
}

void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(std::span<uint8_t> out_encapsulated_key,
                                                  std::span<uint8_t> out_shared_key,
                                                  RandomNumberGenerator& rng,
                                                  size_t desired_shared_key_len,
                                                  std::span<const uint8_t> salt) {
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Encryptor::encrypt requires a KDF to use a salt");
   BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length());

   if(m_kdf) {
      BOTAN_ASSERT_EQUAL(
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");

      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
      this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
   } else {
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
      this->raw_kem_encrypt(out_encapsulated_key, out_shared_key, rng);
   }
}

PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(std::string_view kdf) {
   if(kdf != "Raw") {
      m_kdf = KDF::create_or_throw(kdf);
   }
}

PK_Ops::KEM_Encryption_with_KDF::~KEM_Encryption_with_KDF() = default;

size_t PK_Ops::KEM_Decryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
   if(m_kdf) {
      return desired_shared_key_len;
   } else {
      return this->raw_kem_shared_key_length();
   }
}

void PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(std::span<uint8_t> out_shared_key,
                                                  std::span<const uint8_t> encapsulated_key,
                                                  size_t desired_shared_key_len,
                                                  std::span<const uint8_t> salt) {
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Decryptor::decrypt requires a KDF to use a salt");

   if(m_kdf) {
      BOTAN_ASSERT_EQUAL(
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");

      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
      this->raw_kem_decrypt(raw_shared, encapsulated_key);
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
   } else {
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
      this->raw_kem_decrypt(out_shared_key, encapsulated_key);
   }
}

PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(std::string_view kdf) {
   if(kdf != "Raw") {
      m_kdf = KDF::create_or_throw(kdf);
   }
}

PK_Ops::KEM_Decryption_with_KDF::~KEM_Decryption_with_KDF() = default;

}  // namespace Botan
/*
* PKCS #8
* (C) 1999-2010,2014,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_PKCS5_PBES2)
#endif

namespace Botan::PKCS8 {

namespace {

/*
* Get info from an EncryptedPrivateKeyInfo
*/
secure_vector<uint8_t> PKCS8_extract(DataSource& source, AlgorithmIdentifier& pbe_alg_id) {
   secure_vector<uint8_t> key_data;

   BER_Decoder(source).start_sequence().decode(pbe_alg_id).decode(key_data, ASN1_Type::OctetString).verify_end();

   return key_data;
}

/*
* PEM decode and/or decrypt a private key
*/
secure_vector<uint8_t> PKCS8_decode(DataSource& source,
                                    const std::function<std::string()>& get_passphrase,
                                    AlgorithmIdentifier& pk_alg_id,
                                    bool is_encrypted) {
   AlgorithmIdentifier pbe_alg_id;
   secure_vector<uint8_t> key_data;
   secure_vector<uint8_t> key;

   try {
      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
         if(is_encrypted) {
            key_data = PKCS8_extract(source, pbe_alg_id);
         } else {
            // todo read more efficiently
            while(auto b = source.read_byte()) {
               key_data.push_back(*b);
            }
         }
      } else {
         std::string label;
         key_data = PEM_Code::decode(source, label);

         // todo remove autodetect for pem as well?
         if(label == "PRIVATE KEY") {
            is_encrypted = false;
         } else if(label == "ENCRYPTED PRIVATE KEY") {
            DataSource_Memory key_source(key_data);
            key_data = PKCS8_extract(key_source, pbe_alg_id);
         } else {
            throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
         }
      }

      if(key_data.empty()) {
         throw PKCS8_Exception("No key data found");
      }
   } catch(Decoding_Error& e) {
      throw Decoding_Error("PKCS #8 private key decoding", e);
   }

   try {
      if(is_encrypted) {
         if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
            throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
         }

#if defined(BOTAN_HAS_PKCS5_PBES2)
         key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
#else
         BOTAN_UNUSED(get_passphrase);
         throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
#endif
      } else {
         key = key_data;
      }

      BER_Decoder(key)
         .start_sequence()
         .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
         .decode(pk_alg_id)
         .decode(key, ASN1_Type::OctetString)
         .discard_remaining()
         .end_cons();
   } catch(std::exception& e) {
      throw Decoding_Error("PKCS #8 private key decoding", e);
   }
   return key;
}

}  // namespace

/*
* PEM encode a PKCS #8 private key, unencrypted
*/
std::string PEM_encode(const Private_Key& key) {
   return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
}

#if defined(BOTAN_HAS_PKCS5_PBES2)

namespace {

std::pair<std::string, std::string> choose_pbe_params(std::string_view pbe_algo, std::string_view key_algo) {
   if(pbe_algo.empty()) {
      /*
      * For algorithms where we are using a non-RFC format anyway, default to
      * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
      * compatible.
      */
      const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");

      if(nonstandard_pk) {
   #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
         return std::make_pair("AES-256/SIV", "SHA-512");
   #elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
         return std::make_pair("AES-256/GCM", "SHA-512");
   #endif
      }

      // Default is something compatible with everyone else
      return std::make_pair("AES-256/CBC", "SHA-256");
   }

   SCAN_Name request(pbe_algo);

   if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
      throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
   }

   return std::make_pair(request.arg(0), request.arg(1));
}

}  // namespace

#endif

/*
* BER encode a PKCS #8 private key, encrypted
*/
std::vector<uint8_t> BER_encode(const Private_Key& key,
                                RandomNumberGenerator& rng,
                                std::string_view pass,
                                std::chrono::milliseconds msec,
                                std::string_view pbe_algo) {
#if defined(BOTAN_HAS_PKCS5_PBES2)
   const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());

   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
      pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr, pbe_params.first, pbe_params.second, rng);

   std::vector<uint8_t> output;
   DER_Encoder der(output);
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();

   return output;
#else
   BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
   throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
#endif
}

/*
* PEM encode a PKCS #8 private key, encrypted
*/
std::string PEM_encode(const Private_Key& key,
                       RandomNumberGenerator& rng,
                       std::string_view pass,
                       std::chrono::milliseconds msec,
                       std::string_view pbe_algo) {
   if(pass.empty()) {
      return PEM_encode(key);
   }

   return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
}

/*
* BER encode a PKCS #8 private key, encrypted
*/
std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
                                                     RandomNumberGenerator& rng,
                                                     std::string_view pass,
                                                     size_t pbkdf_iterations,
                                                     std::string_view cipher,
                                                     std::string_view pbkdf_hash) {
#if defined(BOTAN_HAS_PKCS5_PBES2)
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
      pbes2_encrypt_iter(key.private_key_info(),
                         pass,
                         pbkdf_iterations,
                         cipher.empty() ? "AES-256/CBC" : cipher,
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
                         rng);

   std::vector<uint8_t> output;
   DER_Encoder der(output);
   der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();

   return output;

#else
   BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
   throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
#endif
}

/*
* PEM encode a PKCS #8 private key, encrypted
*/
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
                                            RandomNumberGenerator& rng,
                                            std::string_view pass,
                                            size_t pbkdf_iterations,
                                            std::string_view cipher,
                                            std::string_view pbkdf_hash) {
   return PEM_Code::encode(PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
                           "ENCRYPTED PRIVATE KEY");
}

/*
* BER encode a PKCS #8 private key, encrypted
*/
std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
                                                     RandomNumberGenerator& rng,
                                                     std::string_view pass,
                                                     std::chrono::milliseconds pbkdf_msec,
                                                     size_t* pbkdf_iterations,
                                                     std::string_view cipher,
                                                     std::string_view pbkdf_hash) {
#if defined(BOTAN_HAS_PKCS5_PBES2)
   const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
      pbes2_encrypt_msec(key.private_key_info(),
                         pass,
                         pbkdf_msec,
                         pbkdf_iterations,
                         cipher.empty() ? "AES-256/CBC" : cipher,
                         pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
                         rng);

   std::vector<uint8_t> output;
   DER_Encoder(output)
      .start_sequence()
      .encode(pbe_info.first)
      .encode(pbe_info.second, ASN1_Type::OctetString)
      .end_cons();

   return output;
#else
   BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
   throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
#endif
}

/*
* PEM encode a PKCS #8 private key, encrypted
*/
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
                                            RandomNumberGenerator& rng,
                                            std::string_view pass,
                                            std::chrono::milliseconds pbkdf_msec,
                                            size_t* pbkdf_iterations,
                                            std::string_view cipher,
                                            std::string_view pbkdf_hash) {
   return PEM_Code::encode(
      PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
      "ENCRYPTED PRIVATE KEY");
}

namespace {

/*
* Extract a private key (encrypted/unencrypted) and return it
*/
std::unique_ptr<Private_Key> load_key(DataSource& source,
                                      const std::function<std::string()>& get_pass,
                                      bool is_encrypted) {
   AlgorithmIdentifier alg_id;
   secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);

   const std::string alg_name = alg_id.oid().human_name_or_empty();
   if(alg_name.empty()) {
      throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
   }

   return load_private_key(alg_id, pkcs8_key);
}

}  // namespace

/*
* Extract an encrypted private key and return it
*/
std::unique_ptr<Private_Key> load_key(DataSource& source, const std::function<std::string()>& get_pass) {
   return load_key(source, get_pass, true);
}

std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source,
                                      const std::function<std::string()>& get_passphrase) {
   Botan::DataSource_Memory ds(source);
   return load_key(ds, get_passphrase);
}

std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source, std::string_view pass) {
   Botan::DataSource_Memory ds(source);
   return load_key(ds, pass);
}

std::unique_ptr<Private_Key> load_key(std::span<const uint8_t> source) {
   Botan::DataSource_Memory ds(source);
   return load_key(ds);
}

/*
* Extract an encrypted private key and return it
*/
std::unique_ptr<Private_Key> load_key(DataSource& source, std::string_view pass) {
   return load_key(
      source, [pass]() { return std::string(pass); }, true);
}

/*
* Extract an unencrypted private key and return it
*/
std::unique_ptr<Private_Key> load_key(DataSource& source) {
   auto fail_fn = []() -> std::string {
      throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
   };

   return load_key(source, fail_fn, false);
}

}  // namespace Botan::PKCS8
/*
* (C) 1999-2010,2015,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

secure_vector<uint8_t> PK_Decryptor::decrypt(const uint8_t in[], size_t length) const {
   uint8_t valid_mask = 0;

   secure_vector<uint8_t> decoded = do_decrypt(valid_mask, in, length);

   if(valid_mask == 0) {
      throw Decoding_Error("Invalid public key ciphertext, cannot decrypt");
   }

   return decoded;
}

secure_vector<uint8_t> PK_Decryptor::decrypt_or_random(const uint8_t in[],
                                                       size_t length,
                                                       size_t expected_pt_len,
                                                       RandomNumberGenerator& rng,
                                                       const uint8_t required_content_bytes[],
                                                       const uint8_t required_content_offsets[],
                                                       size_t required_contents_length) const {
   const secure_vector<uint8_t> fake_pms = [&]() {
      auto pms = rng.random_vec(expected_pt_len);

      for(size_t i = 0; i != required_contents_length; ++i) {
         const uint8_t exp = required_content_bytes[i];

         /*
         If an offset repeats we don't detect this and just return a PMS that satisfies
         the last requested index. If the requested (idx,value) tuple is the same, that's
         fine and just redundant. If they disagree, decryption will always fail, since the
         same byte cannot possibly have two distinct values.
         */
         const uint8_t off = required_content_offsets[i];
         BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext");
         pms[off] = exp;
      }

      return pms;
   }();

   uint8_t decrypt_valid = 0;
   secure_vector<uint8_t> decoded = do_decrypt(decrypt_valid, in, length);

   auto valid_mask = CT::Mask<uint8_t>::is_equal(decrypt_valid, 0xFF);
   valid_mask &= CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(decoded.size(), expected_pt_len));

   decoded.resize(expected_pt_len);

   for(size_t i = 0; i != required_contents_length; ++i) {
      const uint8_t exp = required_content_bytes[i];

      // We know off is in range because we already checked it when creating the fake premaster above
      const uint8_t off = required_content_offsets[i];

      auto eq = CT::Mask<uint8_t>::is_equal(decoded[off], exp);

      valid_mask &= eq;
   }

   // If valid_mask is false, assign fake pre master instead
   valid_mask.select_n(decoded.data(), decoded.data(), fake_pms.data(), expected_pt_len);

   return decoded;
}

secure_vector<uint8_t> PK_Decryptor::decrypt_or_random(const uint8_t in[],
                                                       size_t length,
                                                       size_t expected_pt_len,
                                                       RandomNumberGenerator& rng) const {
   return decrypt_or_random(in, length, expected_pt_len, rng, nullptr, nullptr, 0);
}

PK_Encryptor_EME::PK_Encryptor_EME(const Public_Key& key,
                                   RandomNumberGenerator& rng,
                                   std::string_view padding,
                                   std::string_view provider) {
   m_op = key.create_encryption_op(rng, padding, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support encryption", key.algo_name()));
   }
}

PK_Encryptor_EME::~PK_Encryptor_EME() = default;

PK_Encryptor_EME::PK_Encryptor_EME(PK_Encryptor_EME&&) noexcept = default;
PK_Encryptor_EME& PK_Encryptor_EME::operator=(PK_Encryptor_EME&&) noexcept = default;

size_t PK_Encryptor_EME::ciphertext_length(size_t ptext_len) const {
   return m_op->ciphertext_length(ptext_len);
}

std::vector<uint8_t> PK_Encryptor_EME::enc(const uint8_t ptext[], size_t len, RandomNumberGenerator& rng) const {
   return m_op->encrypt(std::span{ptext, len}, rng);
}

size_t PK_Encryptor_EME::maximum_input_size() const {
   return m_op->max_input_bits() / 8;
}

PK_Decryptor_EME::PK_Decryptor_EME(const Private_Key& key,
                                   RandomNumberGenerator& rng,
                                   std::string_view padding,
                                   std::string_view provider) {
   m_op = key.create_decryption_op(rng, padding, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support decryption", key.algo_name()));
   }
}

PK_Decryptor_EME::~PK_Decryptor_EME() = default;

PK_Decryptor_EME::PK_Decryptor_EME(PK_Decryptor_EME&&) noexcept = default;
PK_Decryptor_EME& PK_Decryptor_EME::operator=(PK_Decryptor_EME&&) noexcept = default;

size_t PK_Decryptor_EME::plaintext_length(size_t ctext_len) const {
   return m_op->plaintext_length(ctext_len);
}

secure_vector<uint8_t> PK_Decryptor_EME::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const {
   return m_op->decrypt(valid_mask, {in, in_len});
}

PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key, std::string_view param, std::string_view provider) {
   m_op = key.create_kem_encryption_op(param, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support KEM encryption", key.algo_name()));
   }
}

PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key,
                                   RandomNumberGenerator& rng,
                                   std::string_view kem_param,
                                   std::string_view provider) :
      PK_KEM_Encryptor(key, kem_param, provider) {
   BOTAN_UNUSED(rng);
}

PK_KEM_Encryptor::~PK_KEM_Encryptor() = default;

PK_KEM_Encryptor::PK_KEM_Encryptor(PK_KEM_Encryptor&&) noexcept = default;
PK_KEM_Encryptor& PK_KEM_Encryptor::operator=(PK_KEM_Encryptor&&) noexcept = default;

size_t PK_KEM_Encryptor::shared_key_length(size_t desired_shared_key_len) const {
   return m_op->shared_key_length(desired_shared_key_len);
}

size_t PK_KEM_Encryptor::encapsulated_key_length() const {
   return m_op->encapsulated_key_length();
}

void PK_KEM_Encryptor::encrypt(std::span<uint8_t> out_encapsulated_key,
                               std::span<uint8_t> out_shared_key,
                               RandomNumberGenerator& rng,
                               size_t desired_shared_key_len,
                               std::span<const uint8_t> salt) {
   BOTAN_ARG_CHECK(out_encapsulated_key.size() == encapsulated_key_length(), "not enough space for encapsulated key");
   BOTAN_ARG_CHECK(out_shared_key.size() == shared_key_length(desired_shared_key_len),
                   "not enough space for shared key");
   m_op->kem_encrypt(out_encapsulated_key, out_shared_key, rng, desired_shared_key_len, salt);
}

size_t PK_KEM_Decryptor::shared_key_length(size_t desired_shared_key_len) const {
   return m_op->shared_key_length(desired_shared_key_len);
}

size_t PK_KEM_Decryptor::encapsulated_key_length() const {
   return m_op->encapsulated_key_length();
}

PK_KEM_Decryptor::PK_KEM_Decryptor(const Private_Key& key,
                                   RandomNumberGenerator& rng,
                                   std::string_view param,
                                   std::string_view provider) {
   m_op = key.create_kem_decryption_op(rng, param, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support KEM decryption", key.algo_name()));
   }
}

PK_KEM_Decryptor::~PK_KEM_Decryptor() = default;

PK_KEM_Decryptor::PK_KEM_Decryptor(PK_KEM_Decryptor&&) noexcept = default;
PK_KEM_Decryptor& PK_KEM_Decryptor::operator=(PK_KEM_Decryptor&&) noexcept = default;

void PK_KEM_Decryptor::decrypt(std::span<uint8_t> out_shared_key,
                               std::span<const uint8_t> encap_key,
                               size_t desired_shared_key_len,
                               std::span<const uint8_t> salt) {
   BOTAN_ARG_CHECK(out_shared_key.size() == shared_key_length(desired_shared_key_len),
                   "inconsistent size of shared key output buffer");
   m_op->kem_decrypt(out_shared_key, encap_key, desired_shared_key_len, salt);
}

PK_Key_Agreement::PK_Key_Agreement(const Private_Key& key,
                                   RandomNumberGenerator& rng,
                                   std::string_view kdf,
                                   std::string_view provider) {
   m_op = key.create_key_agreement_op(rng, kdf, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support key agreement", key.algo_name()));
   }
}

PK_Key_Agreement::~PK_Key_Agreement() = default;

PK_Key_Agreement::PK_Key_Agreement(PK_Key_Agreement&&) noexcept = default;
PK_Key_Agreement& PK_Key_Agreement::operator=(PK_Key_Agreement&&) noexcept = default;

size_t PK_Key_Agreement::agreed_value_size() const {
   return m_op->agreed_value_size();
}

SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
                                          const uint8_t peer_key[],
                                          size_t peer_key_len,
                                          std::string_view salt) const {
   return this->derive_key(key_len, {peer_key, peer_key_len}, as_span_of_bytes(salt));
}

SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
                                          const std::span<const uint8_t> peer_key,
                                          std::string_view salt) const {
   return this->derive_key(key_len, peer_key, as_span_of_bytes(salt));
}

SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
                                          std::span<const uint8_t> peer_key,
                                          std::span<const uint8_t> salt) const {
   return SymmetricKey(m_op->agree(key_len, peer_key, salt));
}

PK_Signer::PK_Signer(const Private_Key& key,
                     RandomNumberGenerator& rng,
                     std::string_view padding,
                     Signature_Format format,
                     std::string_view provider) :
      m_sig_format(format), m_sig_element_size(key._signature_element_size_for_DER_encoding()) {
   if(m_sig_format == Signature_Format::DerSequence) {
      BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures");
   }

   m_op = key.create_signature_op(rng, padding, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support signature generation", key.algo_name()));
   }
}

AlgorithmIdentifier PK_Signer::algorithm_identifier() const {
   return m_op->algorithm_identifier();
}

std::string PK_Signer::hash_function() const {
   return m_op->hash_function();
}

PK_Signer::~PK_Signer() = default;

PK_Signer::PK_Signer(PK_Signer&&) noexcept = default;
PK_Signer& PK_Signer::operator=(PK_Signer&&) noexcept = default;

void PK_Signer::update(std::string_view in) {
   this->update(as_span_of_bytes(in));
}

void PK_Signer::update(const uint8_t in[], size_t length) {
   m_op->update({in, length});
}

namespace {

std::vector<uint8_t> der_encode_signature(std::span<const uint8_t> sig, size_t parts, size_t part_size) {
   if(sig.size() % parts != 0 || sig.size() != parts * part_size) {
      throw Encoding_Error("Unexpected size for DER signature");
   }

   BufferSlicer bs_sig(sig);
   std::vector<BigInt> sig_parts;
   sig_parts.reserve(parts);
   for(size_t i = 0; i != parts; ++i) {
      sig_parts.emplace_back(BigInt::from_bytes(bs_sig.take(part_size)));
   }

   std::vector<uint8_t> output;
   DER_Encoder(output).start_sequence().encode_list(sig_parts).end_cons();
   return output;
}

}  // namespace

size_t PK_Signer::signature_length() const {
   if(m_sig_format == Signature_Format::Standard) {
      return m_op->signature_length();
   } else if(m_sig_format == Signature_Format::DerSequence) {
      size_t sig_len = m_op->signature_length();

      size_t der_overhead = [sig_len]() {
         /*
         This was computed by DER encoding of some maximal value signatures
         (since DER is variable length)

         The first two cases covers all EC schemes since groups are at most 521
         bits.

         The other cases are only for finite field DSA which practically is only
         used up to 3072 bit groups but the calculation is correct up to a
         262096 (!) bit group so allow it. There are some intermediate sizes but
         this function is allowed to (and indeed must) return an over-estimate
         rather than an exact value since the actual length will change based on
         the computed signature.
         */

         if(sig_len <= 120) {
            // EC signatures <= 480 bits
            return 8;
         } else if(sig_len <= 248) {
            // EC signatures > 480 bits (or very small DSA groups...)
            return 9;
         } else {
            // Everything else. This is an over-estimate for groups under
            // 2040 bits but exact otherwise

            // This requires 15 bytes DER overhead and should never happen
            BOTAN_ASSERT_NOMSG(sig_len < 65524);
            return 14;
         }
      }();

      return sig_len + der_overhead;
   } else {
      throw Internal_Error("PK_Signer: Invalid signature format enum");
   }
}

std::vector<uint8_t> PK_Signer::signature(RandomNumberGenerator& rng) {
   std::vector<uint8_t> sig = m_op->sign(rng);

   if(m_sig_format == Signature_Format::Standard) {
      return sig;
   } else if(m_sig_format == Signature_Format::DerSequence) {
      BOTAN_ASSERT_NOMSG(m_sig_element_size.has_value());
      return der_encode_signature(sig, 2, m_sig_element_size.value());
   } else {
      throw Internal_Error("PK_Signer: Invalid signature format enum");
   }
}

PK_Verifier::PK_Verifier(const Public_Key& key,
                         std::string_view padding,
                         Signature_Format format,
                         std::string_view provider) {
   m_op = key.create_verification_op(padding, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support signature verification", key.algo_name()));
   }

   m_sig_format = format;
   m_sig_element_size = key._signature_element_size_for_DER_encoding();

   if(m_sig_format == Signature_Format::DerSequence) {
      BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures");
   }
}

PK_Verifier::PK_Verifier(const Public_Key& key,
                         const AlgorithmIdentifier& signature_algorithm,
                         std::string_view provider) {
   m_op = key.create_x509_verification_op(signature_algorithm, provider);
   if(!m_op) {
      throw Invalid_Argument(fmt("Key type {} does not support X.509 signature verification", key.algo_name()));
   }

   m_sig_format = key._default_x509_signature_format();
   m_sig_element_size = key._signature_element_size_for_DER_encoding();
}

PK_Verifier::~PK_Verifier() = default;

PK_Verifier::PK_Verifier(PK_Verifier&&) noexcept = default;
PK_Verifier& PK_Verifier::operator=(PK_Verifier&&) noexcept = default;

std::string PK_Verifier::hash_function() const {
   return m_op->hash_function();
}

void PK_Verifier::set_input_format(Signature_Format format) {
   if(format == Signature_Format::DerSequence) {
      BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures");
   }
   m_sig_format = format;
}

bool PK_Verifier::verify_message(const uint8_t msg[], size_t msg_length, const uint8_t sig[], size_t sig_length) {
   update(msg, msg_length);
   return check_signature(sig, sig_length);
}

void PK_Verifier::update(std::string_view in) {
   this->update(as_span_of_bytes(in));
}

void PK_Verifier::update(const uint8_t in[], size_t length) {
   m_op->update({in, length});
}

namespace {

std::vector<uint8_t> decode_der_signature(const uint8_t sig[], size_t length, size_t sig_parts, size_t sig_part_size) {
   std::vector<uint8_t> real_sig;
   BER_Decoder decoder(sig, length);
   BER_Decoder ber_sig = decoder.start_sequence();

   BOTAN_ASSERT_NOMSG(sig_parts != 0 && sig_part_size != 0);

   size_t count = 0;

   while(ber_sig.more_items()) {
      BigInt sig_part;
      ber_sig.decode(sig_part);
      real_sig += sig_part.serialize(sig_part_size);
      ++count;
   }

   if(count != sig_parts) {
      throw Decoding_Error("PK_Verifier: signature size invalid");
   }

   const std::vector<uint8_t> reencoded = der_encode_signature(real_sig, sig_parts, sig_part_size);

   if(reencoded.size() != length || CT::is_equal(reencoded.data(), sig, reencoded.size()).as_bool() == false) {
      throw Decoding_Error("PK_Verifier: signature is not the canonical DER encoding");
   }
   return real_sig;
}

}  // namespace

bool PK_Verifier::check_signature(const uint8_t sig[], size_t length) {
   try {
      if(m_sig_format == Signature_Format::Standard) {
         return m_op->is_valid_signature({sig, length});
      } else if(m_sig_format == Signature_Format::DerSequence) {
         bool decoding_success = false;
         std::vector<uint8_t> real_sig;

         BOTAN_ASSERT_NOMSG(m_sig_element_size.has_value());

         try {
            real_sig = decode_der_signature(sig, length, 2, m_sig_element_size.value());
            decoding_success = true;
         } catch(Decoding_Error&) {}

         bool accept = m_op->is_valid_signature(real_sig);

         return accept && decoding_success;
      } else {
         throw Internal_Error("PK_Verifier: Invalid signature format enum");
      }
   } catch(Invalid_Argument&) {
      return false;
   } catch(Decoding_Error&) {
      return false;
   } catch(Encoding_Error&) {
      return false;
   }
}

}  // namespace Botan
/*
* Public Key Work Factor Functions
* (C) 1999-2007,2012 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <cmath>
#include <numbers>

namespace Botan {

size_t ecp_work_factor(size_t bits) {
   return bits / 2;
}

namespace {

size_t nfs_workfactor(size_t bits, double log2_k) {
   // approximates natural logarithm of an integer of given bitsize
   const double log_p = static_cast<double>(bits) / std::numbers::log2e;

   const double log_log_p = std::log(log_p);

   // RFC 3766: k * e^((1.92 + o(1)) * cubrt(ln(n) * (ln(ln(n)))^2))
   const double est = 1.92 * std::pow(log_p * log_log_p * log_log_p, 1.0 / 3.0);

   // return log2 of the workfactor
   return static_cast<size_t>(log2_k + std::numbers::log2e * est);
}

}  // namespace

size_t if_work_factor(size_t bits) {
   if(bits < 512) {
      return 0;
   }

   // RFC 3766 estimates k at .02 and o(1) to be effectively zero for sizes of interest

   const double log2_k = -5.6438;  // log2(.02)
   return nfs_workfactor(bits, log2_k);
}

size_t dl_work_factor(size_t bits) {
   // Lacking better estimates...
   return if_work_factor(bits);
}

size_t dl_exponent_size(size_t bits) {
   if(bits == 0) {
      return 0;
   }
   if(bits <= 256) {
      return bits - 1;
   }
   if(bits <= 1024) {
      return 192;
   }
   if(bits <= 1536) {
      return 224;
   }
   if(bits <= 2048) {
      return 256;
   }
   if(bits <= 4096) {
      return 384;
   }
   return 512;
}

}  // namespace Botan
/*
* X.509 Public Key
* (C) 1999-2010 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan::X509 {

/*
* PEM encode a X.509 public key
*/
std::string PEM_encode(const Public_Key& key) {
   return PEM_Code::encode(key.subject_public_key(), "PUBLIC KEY");
}

/*
* Extract a public key and return it
*/
std::unique_ptr<Public_Key> load_key(DataSource& source) {
   try {
      AlgorithmIdentifier alg_id;
      std::vector<uint8_t> key_bits;

      if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
         BER_Decoder(source).start_sequence().decode(alg_id).decode(key_bits, ASN1_Type::BitString).end_cons();
      } else {
         DataSource_Memory ber(PEM_Code::decode_check_label(source, "PUBLIC KEY"));

         BER_Decoder(ber).start_sequence().decode(alg_id).decode(key_bits, ASN1_Type::BitString).end_cons();
      }

      if(key_bits.empty()) {
         throw Decoding_Error("X.509 public key decoding");
      }

      return load_public_key(alg_id, key_bits);
   } catch(Decoding_Error& e) {
      throw Decoding_Error("X.509 public key decoding", e);
   }
}

}  // namespace Botan::X509
/*
* (C) 2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_ENTROPY_SOURCE)
#endif

#if defined(BOTAN_HAS_SYSTEM_RNG)
#endif

#if defined(BOTAN_HAS_OS_UTILS)
#endif


namespace Botan {

void RandomNumberGenerator::randomize_with_ts_input(std::span<uint8_t> output) {
   if(this->accepts_input()) {
      std::array<uint8_t, 32> additional_input = {0};

#if defined(BOTAN_HAS_OS_UTILS)
      store_le(std::span{additional_input}.subspan<0, 8>(), OS::get_high_resolution_clock());
      store_le(std::span{additional_input}.subspan<8, 4>(), OS::get_process_id());
      constexpr size_t offset = 12;
#else
      constexpr size_t offset = 0;
#endif

#if defined(BOTAN_HAS_SYSTEM_RNG)
      system_rng().randomize(std::span{additional_input}.subspan<offset>());
#else
      BOTAN_UNUSED(offset);
#endif

      this->fill_bytes_with_input(output, additional_input);
   } else {
      this->fill_bytes_with_input(output, {});
   }
}

size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs, size_t poll_bits, std::chrono::milliseconds poll_timeout) {
   if(this->accepts_input()) {
#if defined(BOTAN_HAS_ENTROPY_SOURCE)
      return srcs.poll(*this, poll_bits, poll_timeout);
#else
      BOTAN_UNUSED(srcs, poll_bits, poll_timeout);
#endif
   }

   return 0;
}

void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) {
   if(this->accepts_input()) {
      this->add_entropy(rng.random_vec(poll_bits / 8));
   }
}

void Null_RNG::fill_bytes_with_input(std::span<uint8_t> output, std::span<const uint8_t> /* ignored */) {
   // throw if caller tries to obtain random bytes
   if(!output.empty()) {
      throw PRNG_Unseeded("Null_RNG called");
   }
}

}  // namespace Botan
/*
* SHA-{384,512}
* (C) 1999-2011,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_HAS_CPUID)
#endif

namespace Botan {

namespace {

std::string sha512_provider() {
#if defined(BOTAN_HAS_SHA2_64_X86)
   if(auto feat = CPUID::check(CPUID::Feature::SHA512)) {
      return *feat;
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_ARMV8)
   if(auto feat = CPUID::check(CPUID::Feature::SHA2_512)) {
      return *feat;
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_X86_AVX512)
   if(auto feat = CPUID::check(CPUID::Feature::AVX512, CPUID::Feature::BMI)) {
      return *feat;
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_X86_AVX2)
   if(auto feat = CPUID::check(CPUID::Feature::AVX2, CPUID::Feature::BMI)) {
      return *feat;
   }
#endif

   return "base";
}

}  // namespace

/*
* SHA-{384,512} Compression Function
*/
//static
void SHA_512::compress_digest(digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
#if defined(BOTAN_HAS_SHA2_64_X86)
   if(CPUID::has(CPUID::Feature::SHA512)) {
      return compress_digest_x86(digest, input, blocks);
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_ARMV8)
   if(CPUID::has(CPUID::Feature::SHA2_512)) {
      return compress_digest_armv8(digest, input, blocks);
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_X86_AVX512)
   if(CPUID::has(CPUID::Feature::AVX512, CPUID::Feature::BMI)) {
      return compress_digest_x86_avx512(digest, input, blocks);
   }
#endif

#if defined(BOTAN_HAS_SHA2_64_X86_AVX2)
   if(CPUID::has(CPUID::Feature::AVX2, CPUID::Feature::BMI)) {
      return compress_digest_x86_avx2(digest, input, blocks);
   }
#endif

   uint64_t A = digest[0];
   uint64_t B = digest[1];
   uint64_t C = digest[2];
   uint64_t D = digest[3];
   uint64_t E = digest[4];
   uint64_t F = digest[5];
   uint64_t G = digest[6];
   uint64_t H = digest[7];

   std::array<uint64_t, 16> W{};

   BufferSlicer in(input);

   for(size_t i = 0; i != blocks; ++i) {
      load_be(W, in.take<block_bytes>());

      // clang-format off

      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x428A2F98D728AE22);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x7137449123EF65CD);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0xB5C0FBCFEC4D3B2F);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0xE9B5DBA58189DBBC);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x3956C25BF348B538);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x59F111F1B605D019);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x923F82A4AF194F9B);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0xAB1C5ED5DA6D8118);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xD807AA98A3030242);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x12835B0145706FBE);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x243185BE4EE4B28C);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x550C7DC3D5FFB4E2);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x72BE5D74F27B896F);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0x80DEB1FE3B1696B1);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x9BDC06A725C71235);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC19BF174CF692694);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0xE49B69C19EF14AD2);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0xEFBE4786384F25E3);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x0FC19DC68B8CD5B5);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x240CA1CC77AC9C65);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x2DE92C6F592B0275);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4A7484AA6EA6E483);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5CB0A9DCBD41FBD4);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x76F988DA831153B5);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x983E5152EE66DFAB);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA831C66D2DB43210);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xB00327C898FB213F);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xBF597FC7BEEF0EE4);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xC6E00BF33DA88FC2);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD5A79147930AA725);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x06CA6351E003826F);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x142929670A0E6E70);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x27B70A8546D22FFC);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x2E1B21385C26C926);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x4D2C6DFC5AC42AED);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x53380D139D95B3DF);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x650A73548BAF63DE);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x766A0ABB3C77B2A8);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x81C2C92E47EDAEE6);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x92722C851482353B);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0xA2BFE8A14CF10364);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0xA81A664BBC423001);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0xC24B8B70D0F89791);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0xC76C51A30654BE30);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0xD192E819D6EF5218);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xD69906245565A910);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xF40E35855771202A);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x106AA07032BBD1B8);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0x19A4C116B8D2D0C8);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0x1E376C085141AB53);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0x2748774CDF8EEB99);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0x34B0BCB5E19B48A8);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x391C0CB3C5C95A63);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x4ED8AA4AE3418ACB);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x5B9CCA4F7763E373);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x682E6FF3D6B2B8A3);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x748F82EE5DEFB2FC);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x78A5636F43172F60);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x84C87814A1F0AB72);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x8CC702081A6439EC);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x90BEFFFA23631E28);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0xA4506CEBDE82BDE9);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0xBEF9A3F7B2C67915);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0xC67178F2E372532B);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 0], W[14], W[ 9], W[ 1], 0xCA273ECEEA26619C);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 1], W[15], W[10], W[ 2], 0xD186B8C721C0C207);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[ 2], W[ 0], W[11], W[ 3], 0xEADA7DD6CDE0EB1E);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[ 3], W[ 1], W[12], W[ 4], 0xF57D4F7FEE6ED178);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[ 4], W[ 2], W[13], W[ 5], 0x06F067AA72176FBA);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[ 5], W[ 3], W[14], W[ 6], 0x0A637DC5A2C898A6);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[ 6], W[ 4], W[15], W[ 7], 0x113F9804BEF90DAE);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[ 7], W[ 5], W[ 0], W[ 8], 0x1B710B35131C471B);
      SHA2_64_F(A, B, C, D, E, F, G, H, W[ 8], W[ 6], W[ 1], W[ 9], 0x28DB77F523047D84);
      SHA2_64_F(H, A, B, C, D, E, F, G, W[ 9], W[ 7], W[ 2], W[10], 0x32CAAB7B40C72493);
      SHA2_64_F(G, H, A, B, C, D, E, F, W[10], W[ 8], W[ 3], W[11], 0x3C9EBE0A15C9BEBC);
      SHA2_64_F(F, G, H, A, B, C, D, E, W[11], W[ 9], W[ 4], W[12], 0x431D67C49C100D4C);
      SHA2_64_F(E, F, G, H, A, B, C, D, W[12], W[10], W[ 5], W[13], 0x4CC5D4BECB3E42B6);
      SHA2_64_F(D, E, F, G, H, A, B, C, W[13], W[11], W[ 6], W[14], 0x597F299CFC657E2A);
      SHA2_64_F(C, D, E, F, G, H, A, B, W[14], W[12], W[ 7], W[15], 0x5FCB6FAB3AD6FAEC);
      SHA2_64_F(B, C, D, E, F, G, H, A, W[15], W[13], W[ 8], W[ 0], 0x6C44198C4A475817);

      // clang-format on

      A = (digest[0] += A);
      B = (digest[1] += B);
      C = (digest[2] += C);
      D = (digest[3] += D);
      E = (digest[4] += E);
      F = (digest[5] += F);
      G = (digest[6] += G);
      H = (digest[7] += H);
   }
}

std::string SHA_512_256::provider() const {
   return sha512_provider();
}

std::string SHA_384::provider() const {
   return sha512_provider();
}

std::string SHA_512::provider() const {
   return sha512_provider();
}

void SHA_512_256::compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
   SHA_512::compress_digest(digest, input, blocks);
}

void SHA_384::compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
   SHA_512::compress_digest(digest, input, blocks);
}

void SHA_512::compress_n(digest_type& digest, std::span<const uint8_t> input, size_t blocks) {
   SHA_512::compress_digest(digest, input, blocks);
}

void SHA_512_256::init(digest_type& digest) {
   digest.assign({0x22312194FC2BF72C,
                  0x9F555FA3C84C64C2,
                  0x2393B86B6F53B151,
                  0x963877195940EABD,
                  0x96283EE2A88EFFE3,
                  0xBE5E1E2553863992,
                  0x2B0199FC2C85B8AA,
                  0x0EB72DDC81C52CA2});
}

void SHA_384::init(digest_type& digest) {
   digest.assign({0xCBBB9D5DC1059ED8,
                  0x629A292A367CD507,
                  0x9159015A3070DD17,
                  0x152FECD8F70E5939,
                  0x67332667FFC00B31,
                  0x8EB44A8768581511,
                  0xDB0C2E0D64F98FA7,
                  0x47B5481DBEFA4FA4});
}

void SHA_512::init(digest_type& digest) {
   digest.assign({0x6A09E667F3BCC908,
                  0xBB67AE8584CAA73B,
                  0x3C6EF372FE94F82B,
                  0xA54FF53A5F1D36F1,
                  0x510E527FADE682D1,
                  0x9B05688C2B3E6C1F,
                  0x1F83D9ABFB41BD6B,
                  0x5BE0CD19137E2179});
}

std::unique_ptr<HashFunction> SHA_384::new_object() const {
   return std::make_unique<SHA_384>();
}

std::unique_ptr<HashFunction> SHA_512::new_object() const {
   return std::make_unique<SHA_512>();
}

std::unique_ptr<HashFunction> SHA_512_256::new_object() const {
   return std::make_unique<SHA_512_256>();
}

std::unique_ptr<HashFunction> SHA_384::copy_state() const {
   return std::make_unique<SHA_384>(*this);
}

std::unique_ptr<HashFunction> SHA_512::copy_state() const {
   return std::make_unique<SHA_512>(*this);
}

std::unique_ptr<HashFunction> SHA_512_256::copy_state() const {
   return std::make_unique<SHA_512_256>(*this);
}

void SHA_384::add_data(std::span<const uint8_t> input) {
   m_md.update(input);
}

void SHA_512::add_data(std::span<const uint8_t> input) {
   m_md.update(input);
}

void SHA_512_256::add_data(std::span<const uint8_t> input) {
   m_md.update(input);
}

void SHA_384::final_result(std::span<uint8_t> output) {
   m_md.final(output);
}

void SHA_512::final_result(std::span<uint8_t> output) {
   m_md.final(output);
}

void SHA_512_256::final_result(std::span<uint8_t> output) {
   m_md.final(output);
}

}  // namespace Botan
/*
* (C) 2017,2023 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <cstdlib>
#include <new>

#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
#endif

namespace Botan {

BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size) {
   if(elems == 0 || elem_size == 0) {
      return nullptr;
   }

   // Some calloc implementations do not check for overflow (?!?)
   if(!checked_mul(elems, elem_size).has_value()) {
      throw std::bad_alloc();
   }

#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
   if(void* p = mlock_allocator::instance().allocate(elems, elem_size)) {
      return p;
   }
#endif

#if defined(BOTAN_TARGET_OS_HAS_ALLOC_CONCEAL)
   void* ptr = ::calloc_conceal(elems, elem_size);
#else
   void* ptr = std::calloc(elems, elem_size);  // NOLINT(*-no-malloc,*-owning-memory)
#endif
   if(ptr == nullptr) {
      [[unlikely]] throw std::bad_alloc();
   }
   return ptr;
}

void deallocate_memory(void* p, size_t elems, size_t elem_size) {
   if(p == nullptr) {
      [[unlikely]] return;
   }

   secure_scrub_memory(p, elems * elem_size);

#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
   if(mlock_allocator::instance().deallocate(p, elems, elem_size)) {
      return;
   }
#endif

   std::free(p);  // NOLINT(*-no-malloc,*-owning-memory)
}

void initialize_allocator() {
#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
   mlock_allocator::instance();
#endif
}

}  // namespace Botan
/*
* Runtime assertion checking
* (C) 2010,2012,2018 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_TERMINATE_ON_ASSERTS)
   #include <iostream>
#endif

namespace Botan {

void throw_invalid_argument(const char* message, const char* func, const char* file) {
   throw Invalid_Argument(fmt("{} in {}:{}", message, func, file));
}

void throw_invalid_state(const char* expr, const char* func, const char* file) {
   throw Invalid_State(fmt("Invalid state: expr {} was false in {}:{}", expr, func, file));
}

void assertion_failure(const char* expr_str, const char* assertion_made, const char* func, const char* file, int line) {
   std::ostringstream format;

   format << "False assertion ";

   if(assertion_made != nullptr && assertion_made[0] != 0) {
      format << "'" << assertion_made << "' (expression " << expr_str << ") ";
   } else {
      format << expr_str << " ";
   }

   if(func != nullptr) {
      format << "in " << func << " ";
   }

   format << "@" << file << ":" << line;

#if defined(BOTAN_TERMINATE_ON_ASSERTS)
   std::cerr << format.str() << '\n';
   std::abort();
#else
   throw Internal_Error(format.str());
#endif
}

void assert_unreachable(const char* file, int line) {
   const std::string msg = fmt("Codepath that was marked unreachable was reached @{}:{}", file, line);

#if defined(BOTAN_TERMINATE_ON_ASSERTS)
   std::cerr << msg << '\n';
   std::abort();
#else
   throw Internal_Error(msg);
#endif
}

}  // namespace Botan
/*
* Calendar Functions
* (C) 1999-2010,2017 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <ctime>

namespace Botan {

namespace {

// TODO replace this with https://howardhinnant.github.io/date_algorithms.html#civil_from_days
std::tm do_gmtime(std::time_t time_val) {
   std::tm tm{};

#if defined(BOTAN_TARGET_OS_HAS_WIN32)
   ::gmtime_s(&tm, &time_val);  // Windows
#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
   if(::gmtime_r(&time_val, &tm) == nullptr) {
      throw Encoding_Error("do_gmtime could not convert");
   }
#else
   std::tm* tm_p = std::gmtime(&time_val);
   if(tm_p == nullptr) {
      throw Encoding_Error("do_gmtime could not convert");
   }
   tm = *tm_p;
#endif

   return tm;
}

/*
Portable replacement for timegm, _mkgmtime, etc

Algorithm due to Howard Hinnant

See https://howardhinnant.github.io/date_algorithms.html#days_from_civil
for details and explaination. The code is slightly simplified by our assumption
that the date is at least 1970, which is sufficient for our purposes.
*/
uint64_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day) {
   BOTAN_ARG_CHECK(year >= 1970, "Years before 1970 not supported");

   if(month <= 2) {
      year -= 1;
   }
   const uint32_t era = year / 400;
   const uint32_t yoe = year - era * 400;                                          // [0, 399]
   const uint32_t doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1;  // [0, 365]
   const uint32_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;                     // [0, 146096]
   return era * 146097 + doe - 719468;
}

}  // namespace

uint64_t calendar_point::seconds_since_epoch() const {
   return (days_since_epoch(year(), month(), day()) * 86400) + (hour() * 60 * 60) + (minutes() * 60) + seconds();
}

std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const {
   const uint64_t seconds_64 = this->seconds_since_epoch();
   const time_t seconds_time_t = static_cast<time_t>(seconds_64);

   if(seconds_64 - seconds_time_t != 0) {
      throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow");
   }

   return std::chrono::system_clock::from_time_t(seconds_time_t);
}

std::string calendar_point::to_string() const {
   // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss>
   std::stringstream output;
   output << std::setfill('0') << std::setw(4) << year() << "-" << std::setw(2) << month() << "-" << std::setw(2)
          << day() << "T" << std::setw(2) << hour() << ":" << std::setw(2) << minutes() << ":" << std::setw(2)
          << seconds();
   return output.str();
}

calendar_point::calendar_point(const std::chrono::system_clock::time_point& time_point) {
   std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point));

   m_year = tm.tm_year + 1900;
   m_month = tm.tm_mon + 1;
   m_day = tm.tm_mday;
   m_hour = tm.tm_hour;
   m_minutes = tm.tm_min;
   m_seconds = tm.tm_sec;
}

}  // namespace Botan
/*
* Character Set Handling
* (C) 1999-2007,2021 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

void append_utf8_for(std::string& s, uint32_t c) {
   if(c >= 0xD800 && c < 0xE000) {
      throw Decoding_Error("Invalid Unicode character");
   }

   if(c <= 0x7F) {
      const uint8_t b0 = static_cast<uint8_t>(c);
      s.push_back(static_cast<char>(b0));
   } else if(c <= 0x7FF) {
      const uint8_t b0 = 0xC0 | static_cast<uint8_t>(c >> 6);
      const uint8_t b1 = 0x80 | static_cast<uint8_t>(c & 0x3F);
      s.push_back(static_cast<char>(b0));
      s.push_back(static_cast<char>(b1));
   } else if(c <= 0xFFFF) {
      const uint8_t b0 = 0xE0 | static_cast<uint8_t>(c >> 12);
      const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
      const uint8_t b2 = 0x80 | static_cast<uint8_t>(c & 0x3F);
      s.push_back(static_cast<char>(b0));
      s.push_back(static_cast<char>(b1));
      s.push_back(static_cast<char>(b2));
   } else if(c <= 0x10FFFF) {
      const uint8_t b0 = 0xF0 | static_cast<uint8_t>(c >> 18);
      const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 12) & 0x3F);
      const uint8_t b2 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
      const uint8_t b3 = 0x80 | static_cast<uint8_t>(c & 0x3F);
      s.push_back(static_cast<char>(b0));
      s.push_back(static_cast<char>(b1));
      s.push_back(static_cast<char>(b2));
      s.push_back(static_cast<char>(b3));
   } else {
      throw Decoding_Error("Invalid Unicode character");
   }
}

}  // namespace

std::string ucs2_to_utf8(const uint8_t ucs2[], size_t len) {
   if(len % 2 != 0) {
      throw Decoding_Error("Invalid length for UCS-2 string");
   }

   const size_t chars = len / 2;

   std::string s;
   for(size_t i = 0; i != chars; ++i) {
      const uint32_t c = load_be<uint16_t>(ucs2, i);
      append_utf8_for(s, c);
   }

   return s;
}

std::string ucs4_to_utf8(const uint8_t ucs4[], size_t len) {
   if(len % 4 != 0) {
      throw Decoding_Error("Invalid length for UCS-4 string");
   }

   const size_t chars = len / 4;

   std::string s;
   for(size_t i = 0; i != chars; ++i) {
      const uint32_t c = load_be<uint32_t>(ucs4, i);
      append_utf8_for(s, c);
   }

   return s;
}

/*
* Convert from ISO 8859-1 to UTF-8
*/
std::string latin1_to_utf8(const uint8_t chars[], size_t len) {
   std::string s;
   for(size_t i = 0; i != len; ++i) {
      const uint32_t c = static_cast<uint8_t>(chars[i]);
      append_utf8_for(s, c);
   }
   return s;
}

std::string format_char_for_display(char c) {
   std::ostringstream oss;

   oss << "'";

   if(c == '\t') {
      oss << "\\t";
   } else if(c == '\n') {
      oss << "\\n";
   } else if(c == '\r') {
      oss << "\\r";
   } else if(static_cast<unsigned char>(c) >= 128) {
      unsigned char z = static_cast<unsigned char>(c);
      oss << "\\x" << std::hex << std::uppercase << static_cast<int>(z);
   } else {
      oss << c;
   }

   oss << "'";

   return oss.str();
}

}  // namespace Botan
/*
* (C) 2018,2021 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

CT::Option<size_t> CT::copy_output(CT::Choice accept,
                                   std::span<uint8_t> output,
                                   std::span<const uint8_t> input,
                                   size_t offset) {
   // This leaks information about the input length, but this happens
   // unavoidably since we are unable to ready any bytes besides those
   // in input[0..n]
   BOTAN_ARG_CHECK(output.size() >= input.size(), "Invalid span lengths");

   /*
   * We do not poison the input here because if we did we would have
   * to unpoison it at exit. We assume instead that callers have
   * already poisoned the input and will unpoison it at their own
   * time.
   */
   CT::poison(offset);

   /**
   * Zeroize the entire output buffer to get started
   */
   clear_mem(output);

   /*
   * If the offset is greater than input length, then the arguments are
   * invalid. Ideally we would throw an exception, but that leaks
   * information about the offset. Instead treat it as if the input
   * was invalid.
   */
   accept = accept && CT::Mask<size_t>::is_lte(offset, input.size()).as_choice();

   /*
   * If the input is invalid, then set offset == input_length
   */
   offset = CT::Mask<size_t>::from_choice(accept).select(offset, input.size());

   /*
   * Move the desired output bytes to the front using a slow (O^n)
   * but constant time loop that does not leak the value of the offset
   */
   for(size_t i = 0; i != input.size(); ++i) {
      /*
      * If bad_input was set then we modified offset to equal the input_length.
      * In that case, this_loop will be greater than input_length, and so is_eq
      * mask will always be false. As a result none of the input values will be
      * written to output.
      *
      * This is ignoring the possibility of integer overflow of offset + i. But
      * for this to happen the input would have to consume nearly the entire
      * address space.
      */
      const size_t this_loop = offset + i;

      /*
      start index from i rather than 0 since we know j must be >= i + offset
      to have any effect, and starting from i does not reveal information
      */
      for(size_t j = i; j != input.size(); ++j) {
         const uint8_t b = input[j];
         const auto is_eq = CT::Mask<size_t>::is_equal(j, this_loop);
         output[i] |= is_eq.if_set_return(b);
      }
   }

   // This will always be zero if the input was invalid
   const size_t output_bytes = input.size() - offset;

   CT::unpoison_all(output, output_bytes);

   return CT::Option<size_t>(output_bytes, accept);
}

size_t CT::count_leading_zero_bytes(std::span<const uint8_t> input) {
   size_t leading_zeros = 0;
   auto only_zeros = Mask<uint8_t>::set();
   for(uint8_t b : input) {
      only_zeros &= CT::Mask<uint8_t>::is_zero(b);
      leading_zeros += only_zeros.if_set_return(1);
   }
   return leading_zeros;
}

secure_vector<uint8_t> CT::strip_leading_zeros(std::span<const uint8_t> input) {
   const size_t leading_zeros = CT::count_leading_zero_bytes(input);

   secure_vector<uint8_t> output(input.size());

   const auto written = CT::copy_output(CT::Choice::yes(), output, input, leading_zeros);

   /*
   This is potentially not const time, depending on how std::vector is
   implemented. But since we are always reducing length, it should
   just amount to setting the member var holding the length.
   */
   output.resize(written.value_or(0));

   return output;
}

}  // namespace Botan
/*
* DataSource
* (C) 1999-2007 Jack Lloyd
*     2005 Matthew Gregan
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
   #include <fstream>
#endif

namespace Botan {

/*
* Read a single byte from the DataSource
*/
size_t DataSource::read_byte(uint8_t& out) {
   return read(&out, 1);
}

/*
* Read a single byte from the DataSource
*/
std::optional<uint8_t> DataSource::read_byte() {
   uint8_t b = 0;
   if(this->read(&b, 1) == 1) {
      return b;
   } else {
      return {};
   }
}

/*
* Peek a single byte from the DataSource
*/
size_t DataSource::peek_byte(uint8_t& out) const {
   return peek(&out, 1, 0);
}

/*
* Discard the next N bytes of the data
*/
size_t DataSource::discard_next(size_t n) {
   uint8_t buf[64] = {0};
   size_t discarded = 0;

   while(n > 0) {
      const size_t got = this->read(buf, std::min(n, sizeof(buf)));
      discarded += got;
      n -= got;

      if(got == 0) {
         break;
      }
   }

   return discarded;
}

/*
* Read from a memory buffer
*/
size_t DataSource_Memory::read(uint8_t out[], size_t length) {
   const size_t got = std::min<size_t>(m_source.size() - m_offset, length);
   copy_mem(out, m_source.data() + m_offset, got);
   m_offset += got;
   return got;
}

bool DataSource_Memory::check_available(size_t n) {
   return (n <= (m_source.size() - m_offset));
}

/*
* Peek into a memory buffer
*/
size_t DataSource_Memory::peek(uint8_t out[], size_t length, size_t peek_offset) const {
   const size_t bytes_left = m_source.size() - m_offset;
   if(peek_offset >= bytes_left) {
      return 0;
   }

   const size_t got = std::min(bytes_left - peek_offset, length);
   copy_mem(out, &m_source[m_offset + peek_offset], got);
   return got;
}

/*
* Check if the memory buffer is empty
*/
bool DataSource_Memory::end_of_data() const {
   return (m_offset == m_source.size());
}

/*
* DataSource_Memory Constructor
*/
DataSource_Memory::DataSource_Memory(std::string_view in) : DataSource_Memory(as_span_of_bytes(in)) {}

/*
* Read from a stream
*/
size_t DataSource_Stream::read(uint8_t out[], size_t length) {
   m_source.read(cast_uint8_ptr_to_char(out), length);
   if(m_source.bad()) {
      throw Stream_IO_Error("DataSource_Stream::read: Source failure");
   }

   const size_t got = static_cast<size_t>(m_source.gcount());
   m_total_read += got;
   return got;
}

bool DataSource_Stream::check_available(size_t n) {
   const std::streampos orig_pos = m_source.tellg();
   m_source.seekg(0, std::ios::end);
   const size_t avail = static_cast<size_t>(m_source.tellg() - orig_pos);
   m_source.seekg(orig_pos);
   return (avail >= n);
}

/*
* Peek into a stream
*/
size_t DataSource_Stream::peek(uint8_t out[], size_t length, size_t offset) const {
   if(end_of_data()) {
      throw Invalid_State("DataSource_Stream: Cannot peek when out of data");
   }

   size_t got = 0;

   if(offset > 0) {
      secure_vector<uint8_t> buf(offset);
      m_source.read(cast_uint8_ptr_to_char(buf.data()), buf.size());
      if(m_source.bad()) {
         throw Stream_IO_Error("DataSource_Stream::peek: Source failure");
      }
      got = static_cast<size_t>(m_source.gcount());
   }

   if(got == offset) {
      m_source.read(cast_uint8_ptr_to_char(out), length);
      if(m_source.bad()) {
         throw Stream_IO_Error("DataSource_Stream::peek: Source failure");
      }
      got = static_cast<size_t>(m_source.gcount());
   }

   if(m_source.eof()) {
      m_source.clear();
   }
   m_source.seekg(m_total_read, std::ios::beg);

   return got;
}

/*
* Check if the stream is empty or in error
*/
bool DataSource_Stream::end_of_data() const {
   return (!m_source.good());
}

/*
* Return a human-readable ID for this stream
*/
std::string DataSource_Stream::id() const {
   return m_identifier;
}

#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)

/*
* DataSource_Stream Constructor
*/
DataSource_Stream::DataSource_Stream(std::string_view path, bool use_binary) :
      m_identifier(path),
      m_source_memory(std::make_unique<std::ifstream>(std::string(path), use_binary ? std::ios::binary : std::ios::in)),
      m_source(*m_source_memory),
      m_total_read(0) {
   if(!m_source.good()) {
      throw Stream_IO_Error(fmt("DataSource: Failure opening file '{}'", path));
   }
}

#endif

/*
* DataSource_Stream Constructor
*/
DataSource_Stream::DataSource_Stream(std::istream& in, std::string_view name) :
      m_identifier(name), m_source(in), m_total_read(0) {}

DataSource_Stream::~DataSource_Stream() = default;

}  // namespace Botan
/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

std::string to_string(ErrorType type) {
   switch(type) {
      case ErrorType::Unknown:
         return "Unknown";
      case ErrorType::SystemError:
         return "SystemError";
      case ErrorType::NotImplemented:
         return "NotImplemented";
      case ErrorType::OutOfMemory:
         return "OutOfMemory";
      case ErrorType::InternalError:
         return "InternalError";
      case ErrorType::IoError:
         return "IoError";
      case ErrorType::InvalidObjectState:
         return "InvalidObjectState";
      case ErrorType::KeyNotSet:
         return "KeyNotSet";
      case ErrorType::InvalidArgument:
         return "InvalidArgument";
      case ErrorType::InvalidKeyLength:
         return "InvalidKeyLength";
      case ErrorType::InvalidNonceLength:
         return "InvalidNonceLength";
      case ErrorType::LookupError:
         return "LookupError";
      case ErrorType::EncodingFailure:
         return "EncodingFailure";
      case ErrorType::DecodingFailure:
         return "DecodingFailure";
      case ErrorType::TLSError:
         return "TLSError";
      case ErrorType::HttpError:
         return "HttpError";
      case ErrorType::InvalidTag:
         return "InvalidTag";
      case ErrorType::RoughtimeError:
         return "RoughtimeError";
      case ErrorType::CommonCryptoError:
         return "CommonCryptoError";
      case ErrorType::Pkcs11Error:
         return "Pkcs11Error";
      case ErrorType::TPMError:
         return "TPMError";
      case ErrorType::DatabaseError:
         return "DatabaseError";
      case ErrorType::ZlibError:
         return "ZlibError";
      case ErrorType::Bzip2Error:
         return "Bzip2Error";
      case ErrorType::LzmaError:
         return "LzmaError";
   }

   // No default case in above switch so compiler warns
   return "Unrecognized Botan error";
}

Exception::Exception(std::string_view msg) : m_msg(msg) {}

Exception::Exception(std::string_view msg, const std::exception& e) : m_msg(fmt("{} failed with {}", msg, e.what())) {}

Exception::Exception(const char* prefix, std::string_view msg) : m_msg(fmt("{} {}", prefix, msg)) {}

Invalid_Argument::Invalid_Argument(std::string_view msg) : Exception(msg) {}

Invalid_Argument::Invalid_Argument(std::string_view msg, std::string_view where) :
      Exception(fmt("{} in {}", msg, where)) {}

Invalid_Argument::Invalid_Argument(std::string_view msg, const std::exception& e) : Exception(msg, e) {}

namespace {

std::string format_lookup_error(std::string_view type, std::string_view algo, std::string_view provider) {
   if(provider.empty()) {
      return fmt("Unavailable {} {}", type, algo);
   } else {
      return fmt("Unavailable {} {} for provider {}", type, algo, provider);
   }
}

}  // namespace

Lookup_Error::Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider) :
      Exception(format_lookup_error(type, algo, provider)) {}

Internal_Error::Internal_Error(std::string_view err) : Exception("Internal error:", err) {}

Unknown_PK_Field_Name::Unknown_PK_Field_Name(std::string_view algo_name, std::string_view field_name) :
      Invalid_Argument(fmt("Unknown field '{}' for algorithm {}", field_name, algo_name)) {}

Invalid_Key_Length::Invalid_Key_Length(std::string_view name, size_t length) :
      Invalid_Argument(fmt("{} cannot accept a key of length {}", name, length)) {}

Invalid_IV_Length::Invalid_IV_Length(std::string_view mode, size_t bad_len) :
      Invalid_Argument(fmt("IV length {} is invalid for {}", bad_len, mode)) {}

Key_Not_Set::Key_Not_Set(std::string_view algo) : Invalid_State(fmt("Key not set in {}", algo)) {}

PRNG_Unseeded::PRNG_Unseeded(std::string_view algo) : Invalid_State(fmt("PRNG {} not seeded", algo)) {}

Algorithm_Not_Found::Algorithm_Not_Found(std::string_view name) :
      Lookup_Error(fmt("Could not find any algorithm named '{}'", name)) {}

Provider_Not_Found::Provider_Not_Found(std::string_view algo, std::string_view provider) :
      Lookup_Error(fmt("Could not find provider '{}' for algorithm '{}'", provider, algo)) {}

Invalid_Algorithm_Name::Invalid_Algorithm_Name(std::string_view name) :
      Invalid_Argument(fmt("Invalid algorithm name: '{}'", name)) {}

Encoding_Error::Encoding_Error(std::string_view name) : Exception("Encoding error:", name) {}

Decoding_Error::Decoding_Error(std::string_view name) : Exception(name) {}

Decoding_Error::Decoding_Error(std::string_view category, std::string_view err) :
      Exception(fmt("{}: {}", category, err)) {}

Decoding_Error::Decoding_Error(std::string_view msg, const std::exception& e) : Exception(msg, e) {}

Invalid_Authentication_Tag::Invalid_Authentication_Tag(std::string_view msg) :
      Exception("Invalid authentication tag:", msg) {}

Stream_IO_Error::Stream_IO_Error(std::string_view err) : Exception("I/O error:", err) {}

System_Error::System_Error(std::string_view msg, int err_code) :
      Exception(fmt("{} error code {}", msg, err_code)), m_error_code(err_code) {}

Not_Implemented::Not_Implemented(std::string_view err) : Exception("Not implemented", err) {}

}  // namespace Botan
/*
* (C) 2015,2017,2019 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <deque>

#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
   #include <dirent.h>
   #include <functional>
   #include <sys/stat.h>
   #include <sys/types.h>
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
   #define NOMINMAX 1
   #define _WINSOCKAPI_  // stop windows.h including winsock.h
   #include <windows.h>
#endif

namespace Botan {

namespace {

#if defined(BOTAN_TARGET_OS_HAS_POSIX1)

std::vector<std::string> impl_readdir(std::string_view dir_path) {
   std::vector<std::string> out;
   std::deque<std::string> dir_list;
   dir_list.push_back(std::string(dir_path));

   while(!dir_list.empty()) {
      const std::string cur_path = dir_list[0];
      dir_list.pop_front();

      std::unique_ptr<DIR, std::function<int(DIR*)>> dir(::opendir(cur_path.c_str()), ::closedir);

      if(dir) {
         while(struct dirent* dirent = ::readdir(dir.get())) {
            const std::string filename = dirent->d_name;
            if(filename == "." || filename == "..") {
               continue;
            }

            std::ostringstream full_path_sstr;
            full_path_sstr << cur_path << "/" << filename;
            const std::string full_path = full_path_sstr.str();

            struct stat stat_buf {};

            if(::stat(full_path.c_str(), &stat_buf) == -1) {
               continue;
            }

            if(S_ISDIR(stat_buf.st_mode)) {
               dir_list.push_back(full_path);
            } else if(S_ISREG(stat_buf.st_mode)) {
               out.push_back(full_path);
            }
         }
      }
   }

   return out;
}

#elif defined(BOTAN_TARGET_OS_HAS_WIN32)

std::vector<std::string> impl_win32(std::string_view dir_path) {
   std::vector<std::string> out;
   std::deque<std::string> dir_list;
   dir_list.push_back(std::string(dir_path));

   while(!dir_list.empty()) {
      const std::string cur_path = dir_list[0];
      dir_list.pop_front();

      WIN32_FIND_DATAA find_data;
      HANDLE dir = ::FindFirstFileA((cur_path + "/*").c_str(), &find_data);

      if(dir != INVALID_HANDLE_VALUE) {
         do {
            const std::string filename = find_data.cFileName;
            if(filename == "." || filename == "..")
               continue;
            const std::string full_path = cur_path + "/" + filename;

            if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
               dir_list.push_back(full_path);
            } else {
               out.push_back(full_path);
            }
         } while(::FindNextFileA(dir, &find_data));
      }

      ::FindClose(dir);
   }

   return out;
}
#endif

}  // namespace

bool has_filesystem_impl() {
#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
   return true;
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
   return true;
#else
   return false;
#endif
}

std::vector<std::string> get_files_recursive(std::string_view dir) {
   std::vector<std::string> files;

#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
   files = impl_readdir(dir);
#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
   files = impl_win32(dir);
#else
   BOTAN_UNUSED(dir);
   throw No_Filesystem_Access();
#endif

   std::sort(files.begin(), files.end());

   return files;
}

}  // namespace Botan
/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len) {
   return CT::is_equal(x, y, len).value();
}

bool constant_time_compare(std::span<const uint8_t> x, std::span<const uint8_t> y) {
   const auto min_size = CT::Mask<size_t>::is_lte(x.size(), y.size()).select(x.size(), y.size());
   const auto equal_size = CT::Mask<size_t>::is_equal(x.size(), y.size());
   const auto equal_content = CT::Mask<size_t>::expand(CT::is_equal(x.data(), y.data(), min_size));
   return (equal_content & equal_size).as_bool();
}

}  // namespace Botan
/*
* (C) 2025 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <cstring>

#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
   #include <string.h>
#endif

#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
   #define NOMINMAX 1
   #define _WINSOCKAPI_  // stop windows.h including winsock.h
   #include <windows.h>
#endif

namespace Botan {

void secure_scrub_memory(void* ptr, size_t n) {
#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
   ::RtlSecureZeroMemory(ptr, n);

#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
   ::explicit_bzero(ptr, n);

#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET)
   (void)::explicit_memset(ptr, 0, n);

#else
   /*
   * Call memset through a static volatile pointer, which the compiler should
   * not elide. This construct should be safe in conforming compilers, but who
   * knows. This has been checked to generate the expected code, which saves the
   * memset address in the data segment and unconditionally loads and jumps to
   * that address, with the following targets:
   *
   * x86-64: Clang 19, GCC 6, 11, 13, 14
   * riscv64: GCC 14
   * aarch64: GCC 14
   * armv7: GCC 14
   *
   * Actually all of them generated the expected jump even without marking the
   * function pointer as volatile. However this seems worth including as an
   * additional precaution.
   */
   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
   (memset_ptr)(ptr, 0, n);
#endif
}

}  // namespace Botan
/*
* Various string utils and parsing functions
* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/


#include <cctype>

namespace Botan {

uint16_t to_uint16(std::string_view str) {
   const uint32_t x = to_u32bit(str);

   if(x != static_cast<uint16_t>(x)) {
      throw Invalid_Argument("Integer value exceeds 16 bit range");
   }

   return static_cast<uint16_t>(x);
}

uint32_t to_u32bit(std::string_view str_view) {
   const std::string str(str_view);

   // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
   for(const char chr : str) {
      if(chr < '0' || chr > '9') {
         throw Invalid_Argument("to_u32bit invalid decimal string '" + str + "'");
      }
   }

   const unsigned long int x = std::stoul(str);

   if constexpr(sizeof(unsigned long int) > 4) {
      // x might be uint64
      if(x > std::numeric_limits<uint32_t>::max()) {
         throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
      }
   }

   return static_cast<uint32_t>(x);
}

/*
* Parse a SCAN-style algorithm name
*/
std::vector<std::string> parse_algorithm_name(std::string_view namex) {
   if(namex.find('(') == std::string::npos && namex.find(')') == std::string::npos) {
      return {std::string(namex)};
   }

   std::string name(namex);
   std::string substring;
   std::vector<std::string> elems;
   size_t level = 0;

   elems.push_back(name.substr(0, name.find('(')));
   name = name.substr(name.find('('));

   for(auto i = name.begin(); i != name.end(); ++i) {
      char c = *i;

      if(c == '(') {
         ++level;
      }
      if(c == ')') {
         if(level == 1 && i == name.end() - 1) {
            if(elems.size() == 1) {
               elems.push_back(substring.substr(1));
            } else {
               elems.push_back(substring);
            }
            return elems;
         }

         if(level == 0 || (level == 1 && i != name.end() - 1)) {
            throw Invalid_Algorithm_Name(namex);
         }
         --level;
      }

      if(c == ',' && level == 1) {
         if(elems.size() == 1) {
            elems.push_back(substring.substr(1));
         } else {
            elems.push_back(substring);
         }
         substring.clear();
      } else {
         substring += c;
      }
   }

   if(!substring.empty()) {
      throw Invalid_Algorithm_Name(namex);
   }

   return elems;
}

std::vector<std::string> split_on(std::string_view str, char delim) {
   std::vector<std::string> elems;
   if(str.empty()) {
      return elems;
   }

   std::string substr;
   for(char c : str) {
      if(c == delim) {
         if(!substr.empty()) {
            elems.push_back(substr);
         }
         substr.clear();
      } else {
         substr += c;
      }
   }

   if(substr.empty()) {
      throw Invalid_Argument(fmt("Unable to split string '{}", str));
   }
   elems.push_back(substr);

   return elems;
}

/*
* Join a string
*/
std::string string_join(const std::vector<std::string>& strs, char delim) {
   std::ostringstream out;

   for(size_t i = 0; i != strs.size(); ++i) {
      if(i != 0) {
         out << delim;
      }
      out << strs[i];
   }

   return out.str();
}

/*
* Convert a decimal-dotted string to binary IP
*/
std::optional<uint32_t> string_to_ipv4(std::string_view str) {
   // At least 3 dots + 4 1-digit integers
   // At most 3 dots + 4 3-digit integers
   if(str.size() < 3 + 4 * 1 || str.size() > 3 + 4 * 3) {
      return {};
   }

   // the final result
   uint32_t ip = 0;
   // the number of '.' seen so far
   size_t dots = 0;
   // accumulates one quad (range 0-255)
   uint32_t accum = 0;
   // # of digits pushed to accum since last dot
   size_t cur_digits = 0;

   for(char c : str) {
      if(c == '.') {
         // . without preceding digit is invalid
         if(cur_digits == 0) {
            return {};
         }
         dots += 1;
         // too many dots
         if(dots > 3) {
            return {};
         }

         cur_digits = 0;
         ip = (ip << 8) | accum;
         accum = 0;
      } else if(c >= '0' && c <= '9') {
         const auto d = static_cast<uint8_t>(c - '0');

         // prohibit leading zero in quad (used for octal)
         if(cur_digits > 0 && accum == 0) {
            return {};
         }
         accum = (accum * 10) + d;

         if(accum > 255) {
            return {};
         }

         cur_digits++;
         BOTAN_ASSERT_NOMSG(cur_digits <= 3);
      } else {
         return {};
      }
   }

   // no trailing digits?
   if(cur_digits == 0) {
      return {};
   }

   // insufficient # of dots
   if(dots != 3) {
      return {};
   }

   ip = (ip << 8) | accum;

   return ip;
}

/*
* Convert an IP address to decimal-dotted string
*/
std::string ipv4_to_string(uint32_t ip) {
   uint8_t bits[4];
   store_be(ip, bits);

   std::string str;

   for(size_t i = 0; i != 4; ++i) {
      if(i > 0) {
         str += ".";
      }
      str += std::to_string(bits[i]);
   }

   return str;
}

std::string tolower_string(std::string_view in) {
   std::string s(in);
   for(char& c : s) {
      const int cu = static_cast<unsigned char>(c);
      if(std::isalpha(cu) != 0) {
         c = static_cast<char>(std::tolower(cu));
      }
   }
   return s;
}

bool host_wildcard_match(std::string_view issued_, std::string_view host_) {
   const std::string issued = tolower_string(issued_);
   const std::string host = tolower_string(host_);

   if(host.empty() || issued.empty()) {
      return false;
   }

   /*
   If there are embedded nulls in your issued name
   Well I feel bad for you son
   */
   if(std::count(issued.begin(), issued.end(), char(0)) > 0) {
      return false;
   }

   // If more than one wildcard, then issued name is invalid
   const size_t stars = std::count(issued.begin(), issued.end(), '*');
   if(stars > 1) {
      return false;
   }

   // '*' is not a valid character in DNS names so should not appear on the host side
   if(std::count(host.begin(), host.end(), '*') != 0) {
      return false;
   }

   // Similarly a DNS name can't end in .
   if(host[host.size() - 1] == '.') {
      return false;
   }

   // And a host can't have an empty name component, so reject that
   if(host.find("..") != std::string::npos) {
      return false;
   }

   // Exact match: accept
   if(issued == host) {
      return true;
   }

   /*
   Otherwise it might be a wildcard

   If the issued size is strictly longer than the hostname size it
   couldn't possibly be a match, even if the issued value is a
   wildcard. The only exception is when the wildcard ends up empty
   (eg www.example.com matches www*.example.com)
   */
   if(issued.size() > host.size() + 1) {
      return false;
   }

   // If no * at all then not a wildcard, and so not a match
   if(stars != 1) {
      return false;
   }

   /*
   Now walk through the issued string, making sure every character
   matches. When we come to the (singular) '*', jump forward in the
   hostname by the corresponding amount. We know exactly how much
   space the wildcard takes because it must be exactly `len(host) -
   len(issued) + 1 chars`.

   We also verify that the '*' comes in the leftmost component, and
   doesn't skip over any '.' in the hostname.
   */
   size_t dots_seen = 0;
   size_t host_idx = 0;

   for(size_t i = 0; i != issued.size(); ++i) {
      if(issued[i] == '.') {
         dots_seen += 1;
      }

      if(issued[i] == '*') {
         // Fail: wildcard can only come in leftmost component
         if(dots_seen > 0) {
            return false;
         }

         /*
         Since there is only one * we know the tail of the issued and
         hostname must be an exact match. In this case advance host_idx
         to match.
         */
         const size_t advance = (host.size() - issued.size() + 1);

         if(host_idx + advance > host.size()) {  // shouldn't happen
            return false;
         }

         // Can't be any intervening .s that we would have skipped
         if(std::count(host.begin() + host_idx, host.begin() + host_idx + advance, '.') != 0) {
            return false;
         }

         host_idx += advance;
      } else {
         if(issued[i] != host[host_idx]) {
            return false;
         }

         host_idx += 1;
      }
   }

   // Wildcard issued name must have at least 3 components
   if(dots_seen < 2) {
      return false;
   }

   return true;
}

std::string check_and_canonicalize_dns_name(std::string_view name) {
   if(name.size() > 255) {
      throw Decoding_Error("DNS name exceeds maximum allowed length");
   }

   if(name.empty()) {
      throw Decoding_Error("DNS name cannot be empty");
   }

   if(name.starts_with(".") || name.ends_with(".")) {
      throw Decoding_Error("DNS name cannot start or end with a dot");
   }

   /*
   * Table mapping uppercase to lowercase and only including values for valid DNS names
   * namely A-Z, a-z, 0-9, hyphen, and dot, plus '*' for wildcarding. (RFC 1035)
   */
   // clang-format off
   constexpr uint8_t DNS_CHAR_MAPPING[128] = {
      '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
      '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
      '\0', '\0', '\0', '\0',  '*', '\0', '\0',  '-',  '.', '\0',  '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',
       '9', '\0', '\0', '\0', '\0', '\0', '\0', '\0',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',
       'l',  'm',  'n',  'o',  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z', '\0', '\0', '\0', '\0',
      '\0', '\0',  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',  'p',  'q',
       'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z', '\0', '\0', '\0', '\0', '\0',
   };
   // clang-format on

   std::string canon;
   canon.reserve(name.size());

   // RFC 1035: DNS labels must not exceed 63 characters
   size_t current_label_length = 0;

   for(size_t i = 0; i != name.size(); ++i) {
      char c = name[i];

      if(c == '.') {
         if(i > 0 && name[i - 1] == '.') {
            throw Decoding_Error("DNS name contains sequential period chars");
         }

         if(current_label_length == 0) {
            throw Decoding_Error("DNS name contains empty label");
         }
         current_label_length = 0;  // Reset for next label
      } else {
         current_label_length++;

         if(current_label_length > 63) {  // RFC 1035 Maximum DNS label length
            throw Decoding_Error("DNS name label exceeds maximum length of 63 characters");
         }
      }

      const uint8_t cu = static_cast<uint8_t>(c);
      if(cu >= 128) {
         throw Decoding_Error("DNS name must not contain any extended ASCII code points");
      }
      const uint8_t mapped = DNS_CHAR_MAPPING[cu];
      if(mapped == 0) {
         throw Decoding_Error("DNS name includes invalid character");
      }

      if(mapped == '-') {
         if(i == 0 || (i > 0 && name[i - 1] == '.')) {
            throw Decoding_Error("DNS name has label with leading hyphen");
         } else if(i == name.size() - 1 || (i < name.size() - 1 && name[i + 1] == '.')) {
            throw Decoding_Error("DNS name has label with trailing hyphen");
         }
      }
      canon.push_back(static_cast<char>(mapped));
   }

   if(current_label_length == 0) {
      throw Decoding_Error("DNS name contains empty label");
   }
   return canon;
}

}  // namespace Botan
/*
* (C) 2023 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

uint64_t prefetch_array_raw(size_t bytes, const void* arrayv) noexcept {
#if defined(__cpp_lib_hardware_interference_size)
   const size_t cache_line_size = std::hardware_destructive_interference_size;
#else
   // We arbitrarily use a 64 byte cache line, which is by far the most
   // common size.
   //
   // Runtime detection adds too much overhead to this function.
   const size_t cache_line_size = 64;
#endif

   const uint8_t* array = static_cast<const uint8_t*>(arrayv);

   volatile uint64_t combiner = 1;

   for(size_t idx = 0; idx < bytes; idx += cache_line_size) {
#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_prefetch)
      // we have no way of knowing if the compiler will emit anything here
      __builtin_prefetch(&array[idx]);
#endif

      combiner = combiner | array[idx];
   }

   /*
   * The combiner variable is initialized with 1, and we accumulate using OR, so
   * now combiner must be a value other than zero. This being the case we will
   * always return zero here. Hopefully the compiler will not figure this out.
   */
   return ct_is_zero(combiner);
}

}  // namespace Botan
/*
* Simple config/test file reader
* (C) 2013,2014,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

std::string clean_ws(std::string_view s) {
   const char* ws = " \t\n";
   auto start = s.find_first_not_of(ws);
   auto end = s.find_last_not_of(ws);

   if(start == std::string::npos) {
      return "";
   }

   if(end == std::string::npos) {
      return std::string(s.substr(start, end));
   } else {
      return std::string(s.substr(start, start + end + 1));
   }
}

}  // namespace

std::map<std::string, std::string> read_cfg(std::istream& is) {
   std::map<std::string, std::string> kv;
   size_t line = 0;

   while(is.good()) {
      std::string s;

      std::getline(is, s);

      ++line;

      if(s.empty() || s[0] == '#') {
         continue;
      }

      s = clean_ws(s.substr(0, s.find('#')));

      if(s.empty()) {
         continue;
      }

      auto eq = s.find('=');

      if(eq == std::string::npos || eq == 0 || eq == s.size() - 1) {
         throw Decoding_Error("Bad read_cfg input '" + s + "' on line " + std::to_string(line));
      }

      const std::string key = clean_ws(s.substr(0, eq));
      const std::string val = clean_ws(s.substr(eq + 1, std::string::npos));

      kv[key] = val;
   }

   return kv;
}

}  // namespace Botan
/*
* (C) 2018 Ribose Inc
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

std::map<std::string, std::string> read_kv(std::string_view kv) {
   std::map<std::string, std::string> m;
   if(kv.empty()) {
      return m;
   }

   std::vector<std::string> parts;

   try {
      parts = split_on(kv, ',');
   } catch(std::exception&) {
      throw Invalid_Argument("Bad KV spec");
   }

   bool escaped = false;
   bool reading_key = true;
   std::string cur_key;
   std::string cur_val;

   for(char c : kv) {
      if(c == '\\' && !escaped) {
         escaped = true;
      } else if(c == ',' && !escaped) {
         if(cur_key.empty()) {
            throw Invalid_Argument("Bad KV spec empty key");
         }

         if(m.contains(cur_key)) {
            throw Invalid_Argument("Bad KV spec duplicated key");
         }
         m[cur_key] = cur_val;
         cur_key = "";
         cur_val = "";
         reading_key = true;
      } else if(c == '=' && !escaped) {
         if(!reading_key) {
            throw Invalid_Argument("Bad KV spec unexpected equals sign");
         }
         reading_key = false;
      } else {
         if(reading_key) {
            cur_key += c;
         } else {
            cur_val += c;
         }

         if(escaped) {
            escaped = false;
         }
      }
   }

   if(!cur_key.empty()) {
      if(!reading_key) {
         if(m.contains(cur_key)) {
            throw Invalid_Argument("Bad KV spec duplicated key");
         }
         m[cur_key] = cur_val;
      } else {
         throw Invalid_Argument("Bad KV spec incomplete string");
      }
   }

   return m;
}

}  // namespace Botan
/*
* SCAN Name Abstraction
* (C) 2008-2009,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

namespace {

std::string make_arg(const std::vector<std::pair<size_t, std::string>>& name, size_t start) {
   std::string output = name[start].second;
   size_t level = name[start].first;

   size_t paren_depth = 0;

   for(size_t i = start + 1; i != name.size(); ++i) {
      if(name[i].first <= name[start].first) {
         break;
      }

      if(name[i].first > level) {
         output += "(" + name[i].second;
         ++paren_depth;
      } else if(name[i].first < level) {
         for(size_t j = name[i].first; j < level; j++) {
            output += ")";
            --paren_depth;
         }
         output += "," + name[i].second;
      } else {
         if(output[output.size() - 1] != '(') {
            output += ",";
         }
         output += name[i].second;
      }

      level = name[i].first;
   }

   for(size_t i = 0; i != paren_depth; ++i) {
      output += ")";
   }

   return output;
}

}  // namespace

SCAN_Name::SCAN_Name(const char* algo_spec) : SCAN_Name(std::string(algo_spec)) {}

SCAN_Name::SCAN_Name(std::string_view algo_spec) : m_orig_algo_spec(algo_spec) {
   if(algo_spec.empty()) {
      throw Invalid_Argument("Expected algorithm name, got empty string");
   }

   std::vector<std::pair<size_t, std::string>> name;
   size_t level = 0;
   std::pair<size_t, std::string> accum = std::make_pair(level, "");

   const std::string decoding_error = "Bad SCAN name '" + m_orig_algo_spec + "': ";

   for(char c : algo_spec) {
      if(c == '/' || c == ',' || c == '(' || c == ')') {
         if(c == '(') {
            ++level;
         } else if(c == ')') {
            if(level == 0) {
               throw Decoding_Error(decoding_error + "Mismatched parens");
            }
            --level;
         }

         if(c == '/' && level > 0) {
            accum.second.push_back(c);
         } else {
            if(!accum.second.empty()) {
               name.push_back(accum);
            }
            accum = std::make_pair(level, "");
         }
      } else {
         accum.second.push_back(c);
      }
   }

   if(!accum.second.empty()) {
      name.push_back(accum);
   }

   if(level != 0) {
      throw Decoding_Error(decoding_error + "Missing close paren");
   }

   if(name.empty()) {
      throw Decoding_Error(decoding_error + "Empty name");
   }

   m_alg_name = name[0].second;

   bool in_modes = false;

   for(size_t i = 1; i != name.size(); ++i) {
      if(name[i].first == 0) {
         m_mode_info.push_back(make_arg(name, i));
         in_modes = true;
      } else if(name[i].first == 1 && !in_modes) {
         m_args.push_back(make_arg(name, i));
      }
   }
}

std::string SCAN_Name::arg(size_t i) const {
   if(i >= arg_count()) {
      throw Invalid_Argument("SCAN_Name::arg " + std::to_string(i) + " out of range for '" + to_string() + "'");
   }
   return m_args[i];
}

std::string SCAN_Name::arg(size_t i, std::string_view def_value) const {
   if(i >= arg_count()) {
      return std::string(def_value);
   }
   return m_args[i];
}

size_t SCAN_Name::arg_as_integer(size_t i, size_t def_value) const {
   if(i >= arg_count()) {
      return def_value;
   }
   return to_u32bit(m_args[i]);
}

size_t SCAN_Name::arg_as_integer(size_t i) const {
   return to_u32bit(arg(i));
}

}  // namespace Botan
/*
* Version Information
* (C) 1999-2013,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/



namespace Botan {

const char* short_version_cstr() {
   return BOTAN_SHORT_VERSION_STRING;
}

const char* version_cstr() {
   return BOTAN_FULL_VERSION_STRING;
}

std::string version_string() {
   return std::string(version_cstr());
}

std::string short_version_string() {
   return std::string(short_version_cstr());
}

uint32_t version_datestamp() {
   return BOTAN_VERSION_DATESTAMP;
}

std::optional<std::string> version_vc_revision() {
#if defined(BOTAN_VC_REVISION)
   return std::string(BOTAN_VC_REVISION);
#else
   return std::nullopt;
#endif
}

std::optional<std::string> version_distribution_info() {
#if defined(BOTAN_DISTRIBUTION_INFO_STRING)
   return std::string(BOTAN_DISTRIBUTION_INFO_STRING);
#else
   return std::nullopt;
#endif
}

/*
* Return parts of the version as integers
*/
uint32_t version_major() {
   return BOTAN_VERSION_MAJOR;
}

uint32_t version_minor() {
   return BOTAN_VERSION_MINOR;
}

uint32_t version_patch() {
   return BOTAN_VERSION_PATCH;
}

bool unsafe_for_production_build() {
#if defined(BOTAN_UNSAFE_FUZZER_MODE) || defined(BOTAN_TERMINATE_ON_ASSERTS)
   return true;
#else
   return false;
#endif
}

std::string runtime_version_check(uint32_t major, uint32_t minor, uint32_t patch) {
   if(major != version_major() || minor != version_minor() || patch != version_patch()) {
      return fmt("Warning: linked version ({}) does not match version built against ({}.{}.{})\n",
                 short_version_cstr(),
                 major,
                 minor,
                 patch);
   }

   return "";
}

}  // namespace Botan
