Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fold batching challenge (alpha) #3291

Merged
merged 36 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6313f15
wip wip wip
maramihali Oct 23, 2023
40cedd0
styuff
maramihali Oct 24, 2023
c7cc936
how do I do this?
maramihali Oct 24, 2023
aed27df
pain
maramihali Oct 25, 2023
265f248
wip that works
maramihali Nov 6, 2023
44da63f
Merge branch 'master' into mm/pg-plumbing
maramihali Nov 6, 2023
7ffc364
cleanup
maramihali Nov 6, 2023
62df97a
Merge branch 'master' into mm/combiner-quotient
maramihali Nov 6, 2023
9d1b5c6
comment test
maramihali Nov 6, 2023
42ac7e2
comment test
maramihali Nov 6, 2023
75221fb
Merge remote-tracking branch 'origin/master' into mm/combiner-quotient
codygunton Nov 6, 2023
bc63909
Add weirder test
codygunton Nov 8, 2023
7d3e01b
Use clearer type names
codygunton Nov 8, 2023
2287a76
wip
maramihali Nov 8, 2023
86963a5
pr review additions
maramihali Nov 9, 2023
a816c4b
Merge branch 'master' into mm/combiner-quotient
maramihali Nov 9, 2023
91ca591
fix formatting
maramihali Nov 9, 2023
abb6ed2
add missing issue
maramihali Nov 9, 2023
81f7d89
Merge branch 'mm/combiner-quotient' into mm/pg-fold-alpha
maramihali Nov 10, 2023
73068a6
wip that doesn't work
maramihali Nov 10, 2023
a86a9b4
wip
maramihali Nov 10, 2023
a51d686
subrelation number
maramihali Nov 11, 2023
df7a62c
add stuff
maramihali Nov 13, 2023
1ff05b6
confusion
maramihali Nov 16, 2023
1ed19f2
pain
maramihali Nov 16, 2023
dea27c1
compiles
maramihali Nov 16, 2023
05bf621
Merge branch 'master' into mm/pg-fold-alpha
maramihali Nov 17, 2023
073db88
works
maramihali Nov 17, 2023
e9079f3
Merge branch 'master' into mm/pg-fold-alpha
maramihali Nov 17, 2023
348f561
more naming
maramihali Nov 17, 2023
c380cea
fix sumcheck test that was failing
maramihali Nov 18, 2023
068ea31
Merge branch 'master' into mm/pg-fold-alpha
maramihali Nov 18, 2023
238c0dc
remove unwanted comments
maramihali Nov 20, 2023
c136315
Merge branch 'master' into mm/pg-fold-alpha
codygunton Nov 21, 2023
350e9b9
comment fixes from PR review
maramihali Nov 21, 2023
ed3c28b
fix format.sh
maramihali Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ template <ECCVMFlavor Flavor> void ECCVMProver_<Flavor>::execute_relation_check_
using Sumcheck = sumcheck::SumcheckProver<Flavor>;

auto sumcheck = Sumcheck(key->circuit_size, transcript);

sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters);
auto alpha = transcript.get_challenge("alpha");
sumcheck_output = sumcheck.prove(prover_polynomials, relation_parameters, alpha);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ template <typename Flavor> class ECCVMTranscriptTests : public ::testing::Test {
round++;
manifest_expected.add_entry(round, "LOOKUP_INVERSES", size_G);
manifest_expected.add_entry(round, "Z_PERM", size_G);
manifest_expected.add_challenge(round, "Sumcheck:alpha", "Sumcheck:zeta");
manifest_expected.add_challenge(round, "alpha");

round++;
manifest_expected.add_challenge(round, "Sumcheck:zeta");

for (size_t i = 0; i < log_n; ++i) {
round++;
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ template <typename Flavor> bool ECCVMVerifier_<Flavor>::verify_proof(const plonk

// Execute Sumcheck Verifier
auto sumcheck = SumcheckVerifier<Flavor>(circuit_size);

auto alpha = transcript.get_challenge("alpha");
auto [multivariate_challenge, purported_evaluations, sumcheck_verified] =
sumcheck.verify(relation_parameters, transcript);
sumcheck.verify(relation_parameters, alpha, transcript);

// If Sumcheck did not verify, return false
if (sumcheck_verified.has_value() && !sumcheck_verified.value()) {
Expand Down
16 changes: 16 additions & 0 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,22 @@ template <typename Tuple, std::size_t Index = 0> static constexpr size_t compute
}
}

/**
* @brief Recursive utility function to find max TOTAL_RELATION_LENGTH among tuples of Relations.
* @details The "total length" of a relation is 1 + the degree of the relation, where any challenges used in the
* relation are regarded as variables.
*
*/
template <typename Tuple, std::size_t Index = 0> static constexpr size_t compute_number_of_subrelations()
{
if constexpr (Index >= std::tuple_size<Tuple>::value) {
return 0;
} else {
constexpr size_t subrelations_in_relation =
std::tuple_element_t<Index, Tuple>::SUBRELATION_PARTIAL_LENGTHS.size();
return subrelations_in_relation + compute_number_of_subrelations<Tuple, Index + 1>();
}
}
/**
* @brief Recursive utility function to construct a container for the subrelation accumulators of Protogalaxy folding.
* @details The size of the outer tuple is equal to the number of relations. Each relation contributes an inner tuple of
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class GoblinUltra {

static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length<Relations>();
static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length<Relations>();
static constexpr size_t NUMBER_OF_SUBRELATIONS = compute_number_of_subrelations<Relations>();

// BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta`
// random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation
Expand Down
4 changes: 3 additions & 1 deletion barretenberg/cpp/src/barretenberg/flavor/ultra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Ultra {
static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length<Relations>();
static_assert(MAX_PARTIAL_RELATION_LENGTH == 6);
static_assert(MAX_TOTAL_RELATION_LENGTH == 12);
static constexpr size_t NUMBER_OF_SUBRELATIONS = compute_number_of_subrelations<Relations>();

// BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta`
// random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation
Expand Down Expand Up @@ -384,7 +385,8 @@ class Ultra {
* @details During folding and sumcheck, the prover evaluates the relations on these univariates.
*/
template <size_t LENGTH>
using ProverUnivariates = AllEntities<barretenberg::Univariate<FF, LENGTH>, barretenberg::Univariate<FF, LENGTH>>;
using ProverUnivariates =
AllEntities<barretenberg::Univariate<FF, LENGTH>, barretenberg::Univariate<FF, LENGTH>>; // ??????

/**
* @brief A container for univariates produced during the hot loop in sumcheck.
Expand Down
12 changes: 11 additions & 1 deletion barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ template <class Fr, size_t domain_end, size_t domain_start = 0> class Univariate
Univariate(Univariate&& other) noexcept = default;
Univariate& operator=(const Univariate& other) = default;
Univariate& operator=(Univariate&& other) noexcept = default;
// Construct Univariate from scalar
// Construct constant Univariate from scalar which represents the value all the point in the domain should evalute
// to
explicit Univariate(Fr value)
: evaluations{}
{
Expand Down Expand Up @@ -81,6 +82,15 @@ template <class Fr, size_t domain_end, size_t domain_start = 0> class Univariate
return output;
};

static Univariate zero()
{
auto output = Univariate<Fr, domain_end, domain_start>();
for (size_t i = 0; i != LENGTH; ++i) {
output.value_at(i) = Fr::zero();
}
return output;
}

static Univariate random_element() { return get_random(); };

// Operations between Univariate and other Univariate
Expand Down
20 changes: 11 additions & 9 deletions barretenberg/cpp/src/barretenberg/protogalaxy/combiner.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ using Polynomial = typename Flavor::Polynomial;
using FF = typename Flavor::FF;
using RelationParameters = proof_system::RelationParameters<FF>;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/780): Improve combiner tests to check more than the
// arithmetic relation so we more than unit test folding relation parameters and alpha as well.
TEST(Protogalaxy, CombinerOn2Instances)
{
constexpr size_t NUM_INSTANCES = 2;
Expand All @@ -36,7 +38,6 @@ TEST(Protogalaxy, CombinerOn2Instances)
std::array<std::array<Polynomial, Flavor::NUM_ALL_ENTITIES>, NUM_INSTANCES> storage_arrays;
ProtoGalaxyProver prover;
std::vector<FF> pow_betas = { FF(1), FF(2) };
auto alpha = FF(0); // focus on the arithmetic relation only

for (size_t idx = 0; idx < NUM_INSTANCES; idx++) {
auto instance = std::make_shared<ProverInstance>();
Expand All @@ -49,8 +50,9 @@ TEST(Protogalaxy, CombinerOn2Instances)
}

ProverInstances instances{ instance_data };
instances.alpha = Univariate<FF, 13>(FF(0)); // focus on the arithmetic relation only

auto result = prover.compute_combiner(instances, pow_betas, alpha);
auto result = prover.compute_combiner(instances, pow_betas);
auto expected_result =
barretenberg::Univariate<FF, 13>(std::array<FF, 13>{ 87706,
13644570,
Expand All @@ -71,7 +73,6 @@ TEST(Protogalaxy, CombinerOn2Instances)
std::array<std::array<Polynomial, Flavor::NUM_ALL_ENTITIES>, NUM_INSTANCES> storage_arrays;
ProtoGalaxyProver prover;
std::vector<FF> pow_betas = { FF(1), FF(2) };
auto alpha = FF(0); // focus on the arithmetic relation only

for (size_t idx = 0; idx < NUM_INSTANCES; idx++) {
auto instance = std::make_shared<ProverInstance>();
Expand All @@ -84,6 +85,7 @@ TEST(Protogalaxy, CombinerOn2Instances)
}

ProverInstances instances{ instance_data };
instances.alpha = Univariate<FF, 13>(FF(0)); // focus on the arithmetic relation only

const auto create_add_gate = [](auto& polys, const size_t idx, FF w_l, FF w_r) {
polys.w_l[idx] = w_l;
Expand Down Expand Up @@ -129,7 +131,7 @@ TEST(Protogalaxy, CombinerOn2Instances)
relation value:
0 0 0 0 0 0 0 0 0 6 18 36 60 90 */

auto result = prover.compute_combiner(instances, pow_betas, alpha);
auto result = prover.compute_combiner(instances, pow_betas);
auto expected_result = barretenberg::Univariate<FF, 13>(
std::array<FF, 13>{ 0, 0, 12, 36, 72, 120, 180, 252, 336, 432, 540, 660, 792 });

Expand Down Expand Up @@ -162,7 +164,6 @@ TEST(Protogalaxy, CombinerOn4Instances)
std::vector<std::shared_ptr<ProverInstance>> instance_data(NUM_INSTANCES);
std::array<std::array<Polynomial, Flavor::NUM_ALL_ENTITIES>, NUM_INSTANCES> storage_arrays;
ProtoGalaxyProver prover;
auto alpha = FF(0); // focus on the arithmetic relation only
std::vector<FF> pow_betas = { FF(1), FF(2) };

for (size_t idx = 0; idx < NUM_INSTANCES; idx++) {
Expand All @@ -175,19 +176,20 @@ TEST(Protogalaxy, CombinerOn4Instances)
}

ProverInstances instances{ instance_data };
instances.alpha = Univariate<FF, 43>(FF(0)); // focus on the arithmetic relation only

zero_all_selectors(instances[0]->prover_polynomials);
zero_all_selectors(instances[1]->prover_polynomials);
zero_all_selectors(instances[2]->prover_polynomials);
zero_all_selectors(instances[3]->prover_polynomials);

auto result = prover.compute_combiner(instances, pow_betas, alpha);
std::array<FF, 37> zeroes;
auto result = prover.compute_combiner(instances, pow_betas);
std::array<FF, 43> zeroes;
std::fill(zeroes.begin(), zeroes.end(), 0);
auto expected_result = barretenberg::Univariate<FF, 37>(zeroes);
auto expected_result = barretenberg::Univariate<FF, 43>(zeroes);
EXPECT_EQ(result, expected_result);
};
run_test();
};

} // namespace barretenberg::test_protogalaxy_prover
} // namespace barretenberg::test_protogalaxy_prover
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ template <class ProverInstances> void ProtoGalaxyProver_<ProverInstances>::prepa
domain_separator + "_eta", domain_separator + "_beta", domain_separator + "_gamma");
instance->compute_sorted_accumulator_polynomials(eta);
instance->compute_grand_product_polynomials(beta, gamma);
instance->alpha = transcript.get_challenge(domain_separator + "_alpha");
}

fold_parameters(instances);
fold_relation_parameters(instances);
fold_alpha(instances);
}

// TODO(#https://github.com/AztecProtocol/barretenberg/issues/689): finalise implementation this function
Expand All @@ -40,13 +42,13 @@ ProverFoldingResult<typename ProverInstances::Flavor> ProtoGalaxyProver_<ProverI
// TODO(#https://github.com/AztecProtocol/barretenberg/issues/740): Handle the case where we are folding for the
// first time and accumulator is 0
// TODO(#https://github.com/AztecProtocol/barretenberg/issues/763): Fold alpha
auto [alpha, delta] = transcript.get_challenges("alpha", "delta");
auto delta = transcript.get_challenge("delta");
auto accumulator = get_accumulator();
auto instance_size = accumulator->prover_polynomials.get_polynomial_size();
const auto log_instance_size = static_cast<size_t>(numeric::get_msb(instance_size));
auto deltas = compute_round_challenge_pows(log_instance_size, delta);

auto perturbator = compute_perturbator(accumulator, deltas, alpha);
auto perturbator = compute_perturbator(accumulator, deltas);
for (size_t idx = 0; idx <= log_instance_size; idx++) {
transcript.send_to_verifier("perturbator_" + std::to_string(idx), perturbator[idx]);
}
Expand All @@ -62,7 +64,7 @@ ProverFoldingResult<typename ProverInstances::Flavor> ProtoGalaxyProver_<ProverI

auto pow_betas_star = compute_pow_polynomial_at_values(betas_star, instance_size);

auto combiner = compute_combiner(instances, pow_betas_star, alpha);
auto combiner = compute_combiner(instances, pow_betas_star);
auto combiner_quotient = compute_combiner_quotient(compressed_perturbator, combiner);
for (size_t idx = ProverInstances::NUM; idx < combiner.size(); idx++) {
transcript.send_to_verifier("combiner_quotient_" + std::to_string(idx), combiner_quotient.value_at(idx));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
using RowEvaluations = typename Flavor::AllValues;
using ProverPolynomials = typename Flavor::ProverPolynomials;
using Relations = typename Flavor::Relations;
using AlphaType = ProverInstances::AlphaType;

using BaseUnivariate = Univariate<FF, ProverInstances::NUM>;
// The length of ExtendedUnivariate is the largest length (==degree + 1) of a univariate polynomial obtained by
// composing a relation with folded instance + challenge data.
using ExtendedUnivariate = Univariate<FF, (Flavor::MAX_TOTAL_RELATION_LENGTH - 1) * (ProverInstances::NUM - 1) + 1>;
using ExtendedUnivariateWithRandomization =
Univariate<FF, (Flavor::BATCHED_RELATION_TOTAL_LENGTH - 1) * (ProverInstances::NUM - 1) + 1>;
Univariate<FF,
(Flavor::MAX_TOTAL_RELATION_LENGTH - 1 + ProverInstances::NUM - 1) * (ProverInstances::NUM - 1) + 1>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you leave a comment explaining this formula for the length? Why this sum?


using ExtendedUnivariates = typename Flavor::template ProverUnivariates<ExtendedUnivariate::LENGTH>;

using TupleOfTuplesOfUnivariates =
Expand Down Expand Up @@ -181,11 +184,10 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
*
*/
static Polynomial<FF> compute_perturbator(const std::shared_ptr<Instance> accumulator,
const std::vector<FF>& deltas,
const FF& alpha)
const std::vector<FF>& deltas)
{
auto full_honk_evaluations =
compute_full_honk_evaluations(accumulator->prover_polynomials, alpha, accumulator->relation_parameters);
auto full_honk_evaluations = compute_full_honk_evaluations(
accumulator->prover_polynomials, accumulator->alpha, accumulator->relation_parameters);
const auto betas = accumulator->folding_parameters.gate_separation_challenges;
assert(betas.size() == deltas.size());
auto coeffs = construct_perturbator_coefficients(betas, deltas, full_honk_evaluations);
Expand Down Expand Up @@ -237,8 +239,7 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
*
*/
ExtendedUnivariateWithRandomization compute_combiner(const ProverInstances& instances,
const std::vector<FF>& pow_betas_star,
const FF& alpha)
const std::vector<FF>& pow_betas_star)
{
size_t common_circuit_size = instances[0]->prover_polynomials.get_polynomial_size();

Expand All @@ -256,6 +257,7 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
// Constuct univariate accumulator containers; one per thread
std::vector<TupleOfTuplesOfUnivariates> thread_univariate_accumulators(num_threads);
for (auto& accum : thread_univariate_accumulators) {
// just normal relation lengths
maramihali marked this conversation as resolved.
Show resolved Hide resolved
Utils::zero_univariates(accum);
}

Expand Down Expand Up @@ -288,8 +290,25 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
Utils::add_nested_tuples(univariate_accumulators, accumulators);
}
// Batch the univariate contributions from each sub-relation to obtain the round univariate
return Utils::template batch_over_relations<ExtendedUnivariateWithRandomization>(univariate_accumulators,
alpha);
return batch_over_relations(univariate_accumulators, instances.alpha);
}
static ExtendedUnivariateWithRandomization batch_over_relations(TupleOfTuplesOfUnivariates univariate_accumulators,
AlphaType alpha)
{

// First relation does not get multiplied by a batching challenge
auto result = std::get<0>(std::get<0>(univariate_accumulators))
.template extend_to<ProverInstances::BATCHED_EXTENDED_LENGTH>();
auto scale_and_sum = [&]<size_t outer_idx, size_t>(auto& element) {
auto extended = element.template extend_to<ProverInstances::BATCHED_EXTENDED_LENGTH>();
extended *= alpha;
result += extended;
};

Utils::template apply_to_tuple_of_tuples<0, 1>(univariate_accumulators, scale_and_sum);
Utils::zero_univariates(univariate_accumulators);

return result;
}

/**
Expand All @@ -299,13 +318,10 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
* polynomials and Lagrange basis and use batch_invert.
*
*/
static Univariate<FF,
(Flavor::BATCHED_RELATION_TOTAL_LENGTH - 1) * (ProverInstances::NUM - 1) + 1,
ProverInstances::NUM>
compute_combiner_quotient(FF compressed_perturbator, ExtendedUnivariateWithRandomization combiner)
static Univariate<FF, ProverInstances::BATCHED_EXTENDED_LENGTH, ProverInstances::NUM> compute_combiner_quotient(
FF compressed_perturbator, ExtendedUnivariateWithRandomization combiner)
{
std::array<FF, (Flavor::BATCHED_RELATION_TOTAL_LENGTH - 2) * (ProverInstances::NUM - 1)>
combiner_quotient_evals = {};
std::array<FF, ProverInstances::BATCHED_EXTENDED_LENGTH - ProverInstances::NUM> combiner_quotient_evals = {};

// Compute the combiner quotient polynomial as evaluations on points that are not in the vanishing set.
//
Expand All @@ -318,10 +334,8 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
(combiner.value_at(point) - compressed_perturbator * lagrange_0) * vanishing_polynomial.invert();
}

Univariate<FF,
(Flavor::BATCHED_RELATION_TOTAL_LENGTH - 1) * (ProverInstances::NUM - 1) + 1,
ProverInstances::NUM>
combiner_quotient(combiner_quotient_evals);
Univariate<FF, ProverInstances::BATCHED_EXTENDED_LENGTH, ProverInstances::NUM> combiner_quotient(
combiner_quotient_evals);
return combiner_quotient;
}

Expand All @@ -331,14 +345,17 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
* univariate (i.e., sum them against an appropriate univariate Lagrange basis) and then extended as needed during
* the constuction of the combiner.
*/
static void fold_parameters(ProverInstances& instances)

// THIS SHOULD BE CALLED ACCUMULATED
static void fold_relation_parameters(ProverInstances& instances)
{
// array of parameters to be computed
auto& folded_parameters = instances.relation_parameters.to_fold;
size_t param_idx = 0;
for (auto& folded_parameter : folded_parameters) {
Univariate<FF, ProverInstances::NUM> tmp(0);
size_t instance_idx = 0;
// it's not wrong i'm just dumb
for (auto& instance : instances) {
tmp.value_at(instance_idx) = instance->relation_parameters.to_fold[param_idx];
instance_idx++;
Expand All @@ -347,6 +364,23 @@ template <class ProverInstances_> class ProtoGalaxyProver_ {
param_idx++;
}
}

/**
* @brief Create folded univariate for the relation batching parameter (alpha).
*
*/

// THIS SHOULD BE CALLED ACCUMULATED
static void fold_alpha(ProverInstances& instances)
{
Univariate<FF, ProverInstances::NUM> accumulated_alpha;
size_t instance_idx = 0;
for (auto& instance : instances) {
accumulated_alpha.value_at(instance_idx) = instance->alpha;
instance_idx++;
}
instances.alpha = accumulated_alpha.template extend_to<ProverInstances::BATCHED_EXTENDED_LENGTH>();
}
};

extern template class ProtoGalaxyProver_<ProverInstances_<honk::flavor::Ultra, 2>>;
Expand Down
Loading
Loading