m1une's library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:heavy_check_mark: Sum of Two Squares
(math/two_square_sum.hpp)

Overview

Find representations of an integer as a sum of two non-negative squares.

#include "math/two_square_sum.hpp"

All names are in m1une::math.

For example, 25 has four ordered non-negative representations:

0^2 + 5^2
3^2 + 4^2
4^2 + 3^2
5^2 + 0^2

so two_square_sum(25) returns the pairs (0, 5), (3, 4), (4, 3), and (5, 0).

API

std::vector<std::pair<uint64_t, uint64_t>> two_square_sum(uint64_t value);

std::vector<std::pair<uint64_t, uint64_t>>
represent_as_two_square_sum(uint64_t value);

bool is_two_square_sum(uint64_t value);
Function Description
two_square_sum(value) Returns all ordered non-negative pairs (a, b) such that value = a * a + b * b.
represent_as_two_square_sum(value) Alias of two_square_sum(value).
is_two_square_sum(value) Returns whether at least one representation exists.

The returned vector is sorted in lexicographic order and contains no duplicate pairs. Both (a, b) and (b, a) are included when they are different.

two_square_sum(0) returns one pair: (0, 0).

Criterion

By Fermat’s theorem on sums of two squares, an integer has a representation as $a^2 + b^2$ exactly when every prime congruent to 3 modulo 4 appears with an even exponent in its prime factorization.

This is the condition tested by is_two_square_sum.

For construction, each prime congruent to 1 modulo 4 is first represented as a Gaussian integer norm, and all choices of conjugate factors are combined. Prime powers congruent to 3 modulo 4 only contribute to a real scale factor because their exponents must be even.

Complexity

The function first factors value with Pollard-Rho from prime_factorization.hpp. After factorization, the construction is linear in the number of generated Gaussian-factor choices and the number of returned pairs.

For a prime factorization

\[value = 2^e \prod p_i^{\alpha_i} \prod q_j^{2\beta_j},\]

where every p_i is 1 modulo 4 and every q_j is 3 modulo 4, the number of Gaussian-factor choices is

\[(e + 1) \prod_i (\alpha_i + 1).\]

The returned vector contains at most twice this many pairs before duplicate removal.

Example

#include "math/two_square_sum.hpp"

#include <cstdint>
#include <iostream>
#include <utility>
#include <vector>

int main() {
    uint64_t value = 65;
    std::vector<std::pair<uint64_t, uint64_t>> pairs =
        m1une::math::two_square_sum(value);

    for (const auto& pair : pairs) {
        std::cout << pair.first << " " << pair.second << "\n";
    }
}

Depends on

Required by

Verified with

Code

#ifndef M1UNE_MATH_TWO_SQUARE_SUM_HPP
#define M1UNE_MATH_TWO_SQUARE_SUM_HPP 1

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>

#include "integer_arithmetic.hpp"
#include "prime_factorization.hpp"

namespace m1une {
namespace math {

namespace two_square_sum_detail {

struct GaussianInteger {
    __int128_t real;
    __int128_t imaginary;
};

inline GaussianInteger multiply(GaussianInteger first, GaussianInteger second) {
    return GaussianInteger{first.real * second.real - first.imaginary * second.imaginary,
                           first.real * second.imaginary + first.imaginary * second.real};
}

inline GaussianInteger power(GaussianInteger base, int exponent) {
    GaussianInteger result{1, 0};
    while (exponent > 0) {
        if ((exponent & 1) != 0) result = multiply(result, base);
        exponent >>= 1;
        if (exponent != 0) base = multiply(base, base);
    }
    return result;
}

inline uint64_t absolute_value(__int128_t value) {
    return static_cast<uint64_t>(value < 0 ? -value : value);
}

inline uint64_t pow_uint64(uint64_t base, int exponent) {
    uint64_t result = 1;
    while (exponent > 0) {
        if ((exponent & 1) != 0) result *= base;
        exponent >>= 1;
        if (exponent != 0) base *= base;
    }
    return result;
}

inline GaussianInteger prime_one_mod_four_representation(uint64_t prime) {
    assert(prime % 4 == 1);
    uint64_t non_residue = 2;
    while (internal::power_mod(non_residue, (prime - 1) / 2, prime) != prime - 1) {
        non_residue++;
    }

    uint64_t root = internal::power_mod(non_residue, (prime - 1) / 4, prime);
    uint64_t previous = prime;
    uint64_t current = root;
    while (static_cast<__uint128_t>(current) * current > prime) {
        uint64_t next = previous % current;
        previous = current;
        current = next;
    }

    const uint64_t real = current;
    const uint64_t remaining = prime - real * real;
    const uint64_t imaginary = isqrt(remaining);
    assert(imaginary * imaginary == remaining);
    return GaussianInteger{static_cast<__int128_t>(real), static_cast<__int128_t>(imaginary)};
}

inline std::vector<GaussianInteger> prime_power_choices(GaussianInteger factor, int exponent) {
    GaussianInteger conjugate{factor.real, -factor.imaginary};

    std::vector<GaussianInteger> positive_powers(exponent + 1);
    std::vector<GaussianInteger> negative_powers(exponent + 1);
    positive_powers[0] = GaussianInteger{1, 0};
    negative_powers[0] = GaussianInteger{1, 0};
    for (int i = 0; i < exponent; i++) {
        positive_powers[i + 1] = multiply(positive_powers[i], factor);
        negative_powers[i + 1] = multiply(negative_powers[i], conjugate);
    }

    std::vector<GaussianInteger> result;
    result.reserve(exponent + 1);
    for (int take_positive = 0; take_positive <= exponent; take_positive++) {
        result.push_back(multiply(positive_powers[take_positive], negative_powers[exponent - take_positive]));
    }
    return result;
}

inline void add_nonnegative_pairs(std::vector<std::pair<uint64_t, uint64_t>>& result, GaussianInteger value,
                                  uint64_t scale) {
    const uint64_t first = absolute_value(value.real) * scale;
    const uint64_t second = absolute_value(value.imaginary) * scale;
    result.emplace_back(first, second);
    result.emplace_back(second, first);
}

}  // namespace two_square_sum_detail

inline std::vector<std::pair<uint64_t, uint64_t>> two_square_sum(uint64_t value) {
    using two_square_sum_detail::GaussianInteger;

    if (value == 0) return std::vector<std::pair<uint64_t, uint64_t>>{std::pair<uint64_t, uint64_t>(0, 0)};

    uint64_t real_scale = 1;
    std::vector<std::vector<GaussianInteger>> choices_by_prime;
    for (const auto& factor : prime_factorize(value)) {
        const uint64_t prime = factor.first;
        const int exponent = factor.second;

        if (prime % 4 == 3) {
            if ((exponent & 1) != 0) return {};
            real_scale *= two_square_sum_detail::pow_uint64(prime, exponent / 2);
            continue;
        }

        GaussianInteger gaussian_factor;
        if (prime == 2) {
            gaussian_factor = GaussianInteger{1, 1};
        } else {
            gaussian_factor = two_square_sum_detail::prime_one_mod_four_representation(prime);
        }
        choices_by_prime.push_back(two_square_sum_detail::prime_power_choices(gaussian_factor, exponent));
    }

    std::vector<GaussianInteger> values;
    values.push_back(GaussianInteger{1, 0});
    for (const std::vector<GaussianInteger>& choices : choices_by_prime) {
        std::vector<GaussianInteger> next;
        next.reserve(values.size() * choices.size());
        for (GaussianInteger value_so_far : values) {
            for (GaussianInteger choice : choices) {
                next.push_back(two_square_sum_detail::multiply(value_so_far, choice));
            }
        }
        values.swap(next);
    }

    std::vector<std::pair<uint64_t, uint64_t>> result;
    result.reserve(values.size() * 2);
    for (GaussianInteger gaussian : values) {
        two_square_sum_detail::add_nonnegative_pairs(result, gaussian, real_scale);
    }

    std::sort(result.begin(), result.end());
    result.erase(std::unique(result.begin(), result.end()), result.end());
    return result;
}

inline std::vector<std::pair<uint64_t, uint64_t>> represent_as_two_square_sum(uint64_t value) {
    return two_square_sum(value);
}

inline bool is_two_square_sum(uint64_t value) {
    if (value == 0) return true;
    for (const auto& factor : prime_factorize(value)) {
        if (factor.first % 4 == 3 && (factor.second & 1) != 0) return false;
    }
    return true;
}

}  // namespace math
}  // namespace m1une

#endif  // M1UNE_MATH_TWO_SQUARE_SUM_HPP
#line 1 "math/two_square_sum.hpp"



#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>

#line 1 "math/integer_arithmetic.hpp"



#line 5 "math/integer_arithmetic.hpp"
#include <concepts>
#include <limits>
#include <optional>
#include <type_traits>

namespace m1une {
namespace math {

namespace integer_arithmetic_detail {

template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr std::optional<T> checked_multiply(T first, T second) {
    constexpr T minimum = std::numeric_limits<T>::min();
    constexpr T maximum = std::numeric_limits<T>::max();

    if constexpr (std::unsigned_integral<T>) {
        if (second != 0 && maximum / second < first) return std::nullopt;
    } else {
        if (0 < first) {
            if (0 < second) {
                if (maximum / second < first) return std::nullopt;
            } else if (second < minimum / first) {
                return std::nullopt;
            }
        } else if (first < 0) {
            if (0 < second) {
                if (first < minimum / second) return std::nullopt;
            } else if (second < maximum / first) {
                return std::nullopt;
            }
        }
    }
    return T(first * second);
}

template <std::unsigned_integral T>
constexpr bool kth_power_leq(T base, unsigned exponent, T limit) {
    assert(exponent > 0);
    if (base <= 1) return base <= limit;

    const T multiplication_limit = limit / base;
    T product = 1;
    for (unsigned i = 0; i < exponent; i++) {
        if (product > multiplication_limit) return false;
        product *= base;
    }
    return true;
}

}  // namespace integer_arithmetic_detail

// Returns floor(sqrt(value)) exactly, without floating-point arithmetic.
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T isqrt(T value) {
    if constexpr (std::signed_integral<T>) assert(0 <= value);
    if (value <= 1) return value;

    T low = 1;
    T high = value / 2 + 1;
    while (low < high) {
        T middle = low + (high - low + 1) / 2;
        if (middle <= value / middle) {
            low = middle;
        } else {
            high = middle - 1;
        }
    }
    return low;
}

template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T floor_sqrt(T value) {
    return isqrt(value);
}

// Returns ceil(sqrt(value)) exactly, without floating-point arithmetic.
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T ceil_sqrt(T value) {
    T result = isqrt(value);
    if (result == 0) return 0;
    if (result != 0 && value / result == result && value % result == 0) {
        return result;
    }
    return result + 1;
}

// Returns floor(value^(1 / degree)) exactly, without floating-point arithmetic.
template <std::integral T, std::integral Degree>
requires(
    !std::same_as<std::remove_cv_t<T>, bool>
    && !std::same_as<std::remove_cv_t<Degree>, bool>
)
constexpr T floor_kth_root(T value, Degree degree) {
    if constexpr (std::signed_integral<T>) {
        assert(0 <= value);
        if (value < 0) return T();
    }
    assert(0 < degree);
    if (degree <= 0) return T();
    if (value <= 1 || degree == 1) return value;
    if (degree == 2) return isqrt(value);

    using U = std::make_unsigned_t<T>;
    using UDegree = std::make_unsigned_t<Degree>;
    constexpr int digits = std::numeric_limits<U>::digits;
    const UDegree unsigned_degree = static_cast<UDegree>(degree);
    if (unsigned_degree >= static_cast<UDegree>(digits)) return T(1);
    const unsigned exponent = static_cast<unsigned>(unsigned_degree);
    const U unsigned_value = static_cast<U>(value);

    int bit_width = 0;
    for (U remaining = unsigned_value; remaining != 0; remaining >>= 1) {
        bit_width++;
    }
    const int root_bits =
        (bit_width + static_cast<int>(exponent) - 1) /
        static_cast<int>(exponent);

    U low = 1;
    U high = U(1) << root_bits;
    while (high - low > 1) {
        const U middle = low + (high - low) / 2;
        if (
            integer_arithmetic_detail::kth_power_leq(
                middle, exponent, unsigned_value
            )
        ) {
            low = middle;
        } else {
            high = middle;
        }
    }
    return static_cast<T>(low);
}

// Returns base^exponent, or nullopt when the result does not fit in T.
template <std::integral T, std::unsigned_integral Exponent>
requires(
    !std::same_as<std::remove_cv_t<T>, bool>
    && !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr std::optional<T> checked_ipow(T base, Exponent exponent) {
    T result = 1;
    while (exponent != 0) {
        if (exponent & 1) {
            auto product =
                integer_arithmetic_detail::checked_multiply(result, base);
            if (!product.has_value()) return std::nullopt;
            result = *product;
        }
        exponent >>= 1;
        if (exponent != 0) {
            auto square =
                integer_arithmetic_detail::checked_multiply(base, base);
            if (!square.has_value()) return std::nullopt;
            base = *square;
        }
    }
    return result;
}

template <std::integral T, std::unsigned_integral Exponent>
requires(
    !std::same_as<std::remove_cv_t<T>, bool>
    && !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr std::optional<T> checked_integer_pow(T base, Exponent exponent) {
    return checked_ipow(base, exponent);
}

// Returns base^exponent. The result must be representable by T.
template <std::integral T, std::unsigned_integral Exponent>
requires(
    !std::same_as<std::remove_cv_t<T>, bool>
    && !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr T ipow(T base, Exponent exponent) {
    std::optional<T> result = checked_ipow(base, exponent);
    assert(result.has_value());
    return result.value_or(T());
}

template <std::integral T, std::unsigned_integral Exponent>
requires(
    !std::same_as<std::remove_cv_t<T>, bool>
    && !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr T integer_pow(T base, Exponent exponent) {
    return ipow(base, exponent);
}

}  // namespace math
}  // namespace m1une


#line 1 "math/prime_factorization.hpp"



#line 7 "math/prime_factorization.hpp"
#include <numeric>
#line 10 "math/prime_factorization.hpp"

namespace m1une {
namespace math {

namespace internal {

inline uint64_t multiply_mod(uint64_t a, uint64_t b, uint64_t mod) {
    return static_cast<uint64_t>(static_cast<unsigned __int128>(a) * b % mod);
}

inline uint64_t power_mod(uint64_t base, uint64_t exponent, uint64_t mod) {
    uint64_t result = 1;
    while (exponent > 0) {
        if (exponent & 1) result = multiply_mod(result, base, mod);
        base = multiply_mod(base, base, mod);
        exponent >>= 1;
    }
    return result;
}

inline uint64_t pollard_random() {
    static uint64_t state = 0x123456789abcdef0ULL;
    state += 0x9e3779b97f4a7c15ULL;
    uint64_t value = state;
    value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ULL;
    value = (value ^ (value >> 27)) * 0x94d049bb133111ebULL;
    return value ^ (value >> 31);
}

}  // namespace internal

inline bool is_prime(uint64_t value) {
    if (value < 2) return false;
    for (uint64_t prime : {2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL, 37ULL}) {
        if (value % prime == 0) return value == prime;
    }

    uint64_t odd_part = value - 1;
    int power_of_two = 0;
    while ((odd_part & 1) == 0) {
        odd_part >>= 1;
        power_of_two++;
    }

    for (uint64_t base : {2ULL, 325ULL, 9375ULL, 28178ULL, 450775ULL, 9780504ULL, 1795265022ULL}) {
        if (base % value == 0) continue;
        uint64_t x = internal::power_mod(base % value, odd_part, value);
        if (x == 1 || x == value - 1) continue;

        bool composite = true;
        for (int i = 1; i < power_of_two; i++) {
            x = internal::multiply_mod(x, x, value);
            if (x == value - 1) {
                composite = false;
                break;
            }
        }
        if (composite) return false;
    }
    return true;
}

namespace internal {

inline uint64_t pollard_rho(uint64_t value) {
    for (uint64_t prime : {2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL, 37ULL}) {
        if (value % prime == 0) return prime;
    }

    while (true) {
        const uint64_t constant = pollard_random() % (value - 1) + 1;
        uint64_t y = pollard_random() % (value - 1) + 1;
        uint64_t x = 0;
        uint64_t saved_y = 0;
        uint64_t gcd = 1;
        uint64_t segment_length = 1;

        auto advance = [&](uint64_t current) {
            return static_cast<uint64_t>(
                (static_cast<unsigned __int128>(multiply_mod(current, current, value)) + constant) % value);
        };

        while (gcd == 1) {
            x = y;
            for (uint64_t i = 0; i < segment_length; i++) y = advance(y);

            for (uint64_t offset = 0; offset < segment_length && gcd == 1; offset += 128) {
                saved_y = y;
                uint64_t product = 1;
                const uint64_t block = std::min<uint64_t>(128, segment_length - offset);
                for (uint64_t i = 0; i < block; i++) {
                    y = advance(y);
                    const uint64_t difference = x > y ? x - y : y - x;
                    product = multiply_mod(product, difference, value);
                }
                gcd = std::gcd(product, value);
            }
            segment_length <<= 1;
        }

        if (gcd == value) {
            do {
                saved_y = advance(saved_y);
                const uint64_t difference = x > saved_y ? x - saved_y : saved_y - x;
                gcd = std::gcd(difference, value);
            } while (gcd == 1);
        }
        if (gcd != value) return gcd;
    }
}

inline void factor_recursively(uint64_t value, std::vector<uint64_t>& factors) {
    if (value == 1) return;
    if (is_prime(value)) {
        factors.push_back(value);
        return;
    }
    const uint64_t divisor = pollard_rho(value);
    factor_recursively(divisor, factors);
    factor_recursively(value / divisor, factors);
}

}  // namespace internal

inline std::vector<uint64_t> prime_factors(uint64_t value) {
    assert(value >= 1);
    std::vector<uint64_t> result;
    internal::factor_recursively(value, result);
    std::sort(result.begin(), result.end());
    return result;
}

inline std::vector<std::pair<uint64_t, int>> prime_factorize(uint64_t value) {
    std::vector<uint64_t> factors = prime_factors(value);
    std::vector<std::pair<uint64_t, int>> result;
    for (uint64_t prime : factors) {
        if (result.empty() || result.back().first != prime) {
            result.emplace_back(prime, 1);
        } else {
            result.back().second++;
        }
    }
    return result;
}

inline std::vector<uint64_t> divisors(uint64_t value) {
    std::vector<uint64_t> result = {1};
    for (const auto& factor : prime_factorize(value)) {
        const int current_size = int(result.size());
        uint64_t power = 1;
        for (int exponent = 1; exponent <= factor.second; exponent++) {
            power *= factor.first;
            for (int i = 0; i < current_size; i++) {
                result.push_back(result[i] * power);
            }
        }
    }
    std::sort(result.begin(), result.end());
    return result;
}

inline uint64_t euler_phi(uint64_t value) {
    assert(value >= 1);
    uint64_t result = value;
    for (const auto& factor : prime_factorize(value)) {
        result = result / factor.first * (factor.first - 1);
    }
    return result;
}

inline int mobius(uint64_t value) {
    assert(value >= 1);
    int result = 1;
    for (const auto& factor : prime_factorize(value)) {
        if (factor.second >= 2) return 0;
        result = -result;
    }
    return result;
}

}  // namespace math
}  // namespace m1une


#line 12 "math/two_square_sum.hpp"

namespace m1une {
namespace math {

namespace two_square_sum_detail {

struct GaussianInteger {
    __int128_t real;
    __int128_t imaginary;
};

inline GaussianInteger multiply(GaussianInteger first, GaussianInteger second) {
    return GaussianInteger{first.real * second.real - first.imaginary * second.imaginary,
                           first.real * second.imaginary + first.imaginary * second.real};
}

inline GaussianInteger power(GaussianInteger base, int exponent) {
    GaussianInteger result{1, 0};
    while (exponent > 0) {
        if ((exponent & 1) != 0) result = multiply(result, base);
        exponent >>= 1;
        if (exponent != 0) base = multiply(base, base);
    }
    return result;
}

inline uint64_t absolute_value(__int128_t value) {
    return static_cast<uint64_t>(value < 0 ? -value : value);
}

inline uint64_t pow_uint64(uint64_t base, int exponent) {
    uint64_t result = 1;
    while (exponent > 0) {
        if ((exponent & 1) != 0) result *= base;
        exponent >>= 1;
        if (exponent != 0) base *= base;
    }
    return result;
}

inline GaussianInteger prime_one_mod_four_representation(uint64_t prime) {
    assert(prime % 4 == 1);
    uint64_t non_residue = 2;
    while (internal::power_mod(non_residue, (prime - 1) / 2, prime) != prime - 1) {
        non_residue++;
    }

    uint64_t root = internal::power_mod(non_residue, (prime - 1) / 4, prime);
    uint64_t previous = prime;
    uint64_t current = root;
    while (static_cast<__uint128_t>(current) * current > prime) {
        uint64_t next = previous % current;
        previous = current;
        current = next;
    }

    const uint64_t real = current;
    const uint64_t remaining = prime - real * real;
    const uint64_t imaginary = isqrt(remaining);
    assert(imaginary * imaginary == remaining);
    return GaussianInteger{static_cast<__int128_t>(real), static_cast<__int128_t>(imaginary)};
}

inline std::vector<GaussianInteger> prime_power_choices(GaussianInteger factor, int exponent) {
    GaussianInteger conjugate{factor.real, -factor.imaginary};

    std::vector<GaussianInteger> positive_powers(exponent + 1);
    std::vector<GaussianInteger> negative_powers(exponent + 1);
    positive_powers[0] = GaussianInteger{1, 0};
    negative_powers[0] = GaussianInteger{1, 0};
    for (int i = 0; i < exponent; i++) {
        positive_powers[i + 1] = multiply(positive_powers[i], factor);
        negative_powers[i + 1] = multiply(negative_powers[i], conjugate);
    }

    std::vector<GaussianInteger> result;
    result.reserve(exponent + 1);
    for (int take_positive = 0; take_positive <= exponent; take_positive++) {
        result.push_back(multiply(positive_powers[take_positive], negative_powers[exponent - take_positive]));
    }
    return result;
}

inline void add_nonnegative_pairs(std::vector<std::pair<uint64_t, uint64_t>>& result, GaussianInteger value,
                                  uint64_t scale) {
    const uint64_t first = absolute_value(value.real) * scale;
    const uint64_t second = absolute_value(value.imaginary) * scale;
    result.emplace_back(first, second);
    result.emplace_back(second, first);
}

}  // namespace two_square_sum_detail

inline std::vector<std::pair<uint64_t, uint64_t>> two_square_sum(uint64_t value) {
    using two_square_sum_detail::GaussianInteger;

    if (value == 0) return std::vector<std::pair<uint64_t, uint64_t>>{std::pair<uint64_t, uint64_t>(0, 0)};

    uint64_t real_scale = 1;
    std::vector<std::vector<GaussianInteger>> choices_by_prime;
    for (const auto& factor : prime_factorize(value)) {
        const uint64_t prime = factor.first;
        const int exponent = factor.second;

        if (prime % 4 == 3) {
            if ((exponent & 1) != 0) return {};
            real_scale *= two_square_sum_detail::pow_uint64(prime, exponent / 2);
            continue;
        }

        GaussianInteger gaussian_factor;
        if (prime == 2) {
            gaussian_factor = GaussianInteger{1, 1};
        } else {
            gaussian_factor = two_square_sum_detail::prime_one_mod_four_representation(prime);
        }
        choices_by_prime.push_back(two_square_sum_detail::prime_power_choices(gaussian_factor, exponent));
    }

    std::vector<GaussianInteger> values;
    values.push_back(GaussianInteger{1, 0});
    for (const std::vector<GaussianInteger>& choices : choices_by_prime) {
        std::vector<GaussianInteger> next;
        next.reserve(values.size() * choices.size());
        for (GaussianInteger value_so_far : values) {
            for (GaussianInteger choice : choices) {
                next.push_back(two_square_sum_detail::multiply(value_so_far, choice));
            }
        }
        values.swap(next);
    }

    std::vector<std::pair<uint64_t, uint64_t>> result;
    result.reserve(values.size() * 2);
    for (GaussianInteger gaussian : values) {
        two_square_sum_detail::add_nonnegative_pairs(result, gaussian, real_scale);
    }

    std::sort(result.begin(), result.end());
    result.erase(std::unique(result.begin(), result.end()), result.end());
    return result;
}

inline std::vector<std::pair<uint64_t, uint64_t>> represent_as_two_square_sum(uint64_t value) {
    return two_square_sum(value);
}

inline bool is_two_square_sum(uint64_t value) {
    if (value == 0) return true;
    for (const auto& factor : prime_factorize(value)) {
        if (factor.first % 4 == 3 && (factor.second & 1) != 0) return false;
    }
    return true;
}

}  // namespace math
}  // namespace m1une
Back to top page