Skip to content

Commit b8d995f

Browse files
committed
use new square_to_hilo functions from the util library
1 parent 270c6ee commit b8d995f

File tree

11 files changed

+70
-46
lines changed

11 files changed

+70
-46
lines changed

modular_arithmetic/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ include(FetchContent)
7575
FetchContent_Declare(
7676
hurchalla_util
7777
GIT_REPOSITORY https://github.com/hurchalla/util.git
78-
GIT_TAG 38272aeb2b19a8bce2f96c11bf6e0ecbd9eed25b
78+
GIT_TAG 5ff4af01d0a769968e56bc668f4c9a9ad1896749
7979
)
8080
FetchContent_MakeAvailable(hurchalla_util)
8181

montgomery_arithmetic/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ include(FetchContent)
7979
FetchContent_Declare(
8080
hurchalla_util
8181
GIT_REPOSITORY https://github.com/hurchalla/util.git
82-
GIT_TAG 38272aeb2b19a8bce2f96c11bf6e0ecbd9eed25b
82+
GIT_TAG 5ff4af01d0a769968e56bc668f4c9a9ad1896749
8383
)
8484
FetchContent_MakeAvailable(hurchalla_util)
8585

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/MontyCommonBase.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "hurchalla/montgomery_arithmetic/detail/MontyTags.h"
2121
#include "hurchalla/util/traits/ut_numeric_limits.h"
2222
#include "hurchalla/util/unsigned_multiply_to_hilo_product.h"
23+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
2324
#include "hurchalla/util/compiler_macros.h"
2425
#include "hurchalla/util/branchless_shift_left.h"
2526
#include "hurchalla/util/branchless_shift_right.h"
@@ -540,8 +541,7 @@ class MontyCommonBase {
540541
HPBC_CLOCKWORK_INVARIANT2(r_squared_mod_n_ < n_);
541542
namespace hc = ::hurchalla;
542543
T u_lo;
543-
T u_hi = hc::unsigned_multiply_to_hilo_product(u_lo,
544-
r_squared_mod_n_, r_squared_mod_n_);
544+
T u_hi = hc::unsigned_square_to_hilo_product(u_lo, r_squared_mod_n_);
545545
HPBC_CLOCKWORK_ASSERT2(u_hi < n_); // verify that (u_hi*R + u_lo) < n*R
546546
T result = hc::REDC_standard(u_hi, u_lo, n_, inv_n_, PTAG());
547547
HPBC_CLOCKWORK_POSTCONDITION2(result < n_);

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/MontyFullRange.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "hurchalla/modular_arithmetic/absolute_value_difference.h"
2020
#include "hurchalla/util/traits/ut_numeric_limits.h"
2121
#include "hurchalla/util/unsigned_multiply_to_hilo_product.h"
22+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
2223
#include "hurchalla/util/conditional_select.h"
2324
#include "hurchalla/util/cselect_on_bit.h"
2425
#include "hurchalla/util/compiler_macros.h"
@@ -277,7 +278,7 @@ class MontyFullRange final :
277278
namespace hc = ::hurchalla;
278279
T a = sv.getbits();
279280
T sqlo;
280-
T sqhi = hc::unsigned_multiply_to_hilo_product(sqlo, a, a);
281+
T sqhi = hc::unsigned_square_to_hilo_product(sqlo, a);
281282
T a_or_zero = sv.get_subtrahend();
282283
T u_hi = static_cast<T>(sqhi - a_or_zero - a_or_zero);
283284
T u_lo = sqlo;
@@ -305,7 +306,7 @@ class MontyFullRange final :
305306
namespace hc = ::hurchalla;
306307
T a = sv.getbits();
307308
T sqlo;
308-
T sqhi = hc::unsigned_multiply_to_hilo_product(sqlo, a, a);
309+
T sqhi = hc::unsigned_square_to_hilo_product(sqlo, a);
309310
T a_or_zero = sv.get_subtrahend();
310311
T u_hi = static_cast<T>(sqhi - a_or_zero - a_or_zero);
311312
T u_lo = sqlo;
@@ -357,7 +358,7 @@ class MontyFullRange final :
357358
HURCHALLA_FORCE_INLINE T squareToHiLo(T& u_lo, V x) const
358359
{
359360
namespace hc = ::hurchalla;
360-
return hc::unsigned_multiply_to_hilo_product(u_lo, x.get(), x.get());
361+
return hc::unsigned_square_to_hilo_product(u_lo, x.get());
361362
}
362363
HURCHALLA_FORCE_INLINE bool isValid(V x) const
363364
{

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/MontyHalfRange.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "hurchalla/util/traits/ut_numeric_limits.h"
2121
#include "hurchalla/util/traits/extensible_make_signed.h"
2222
#include "hurchalla/util/signed_multiply_to_hilo_product.h"
23+
#include "hurchalla/util/signed_square_to_hilo_product.h"
2324
#include "hurchalla/util/conditional_select.h"
2425
#include "hurchalla/util/cselect_on_bit.h"
2526
#include "hurchalla/util/compiler_macros.h"
@@ -703,7 +704,7 @@ class MontyHalfRange final :
703704
{
704705
HPBC_CLOCKWORK_PRECONDITION2(isValid(x));
705706
namespace hc = ::hurchalla;
706-
S tmp_hi = hc::signed_multiply_to_hilo_product(u_lo, x.get(), x.get());
707+
S tmp_hi = hc::signed_square_to_hilo_product(u_lo, x.get());
707708
// The same logic as given in multiplyToHiLo shows that
708709
// -(n+1)/2 <= tmp_hi <= (n-1)/2. But additionally, since the square
709710
// of an integer is always >= 0, we therefore know

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/MontyQuarterRange.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "hurchalla/util/traits/ut_numeric_limits.h"
2222
#include "hurchalla/util/traits/extensible_make_signed.h"
2323
#include "hurchalla/util/unsigned_multiply_to_hilo_product.h"
24+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
2425
#include "hurchalla/util/cselect_on_bit.h"
2526
#include "hurchalla/util/compiler_macros.h"
2627
#include "hurchalla/modular_arithmetic/detail/clockwork_programming_by_contract.h"
@@ -498,7 +499,7 @@ class MontyQuarterRange final : public
498499
HURCHALLA_FORCE_INLINE T squareToHiLo(T& u_lo, V x) const
499500
{
500501
namespace hc = ::hurchalla;
501-
return hc::unsigned_multiply_to_hilo_product(u_lo, x.get(), x.get());
502+
return hc::unsigned_square_to_hilo_product(u_lo, x.get());
502503
}
503504
HURCHALLA_FORCE_INLINE bool isValid(V x) const
504505
{

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/experimental/MontyFullRangeMasked.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "hurchalla/util/conditional_select.h"
2121
#include "hurchalla/util/cselect_on_bit.h"
2222
#include "hurchalla/util/unsigned_multiply_to_hilo_product.h"
23+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
2324
#include "hurchalla/util/compiler_macros.h"
2425
#include "hurchalla/modular_arithmetic/detail/clockwork_programming_by_contract.h"
2526
#include <type_traits>
@@ -428,7 +429,7 @@ class MontyFullRangeMasked final :
428429
HPBC_CLOCKWORK_PRECONDITION2(isValid(x));
429430
T a = x.getbits();
430431
T umlo;
431-
T umhi = ::hurchalla::unsigned_multiply_to_hilo_product(umlo, a, a);
432+
T umhi = ::hurchalla::unsigned_square_to_hilo_product(umlo, a);
432433
T masked_a = static_cast<T>(x.getmask() & a);
433434
T result_hi = static_cast<T>(umhi - masked_a - masked_a);
434435
u_lo = umlo;
@@ -511,9 +512,9 @@ class MontyFullRangeMasked final :
511512
// Recall that asqrHi and asqrLo are simply any values that satisfy
512513
// asqrHi*R + asqrLo == a*a, with 0 <= asqrHi < R and 0 <= asqrLo < R.
513514
// When we compute
514-
// T umlo; T umhi = unsigned_multiply_to_hilo_product(umlo, a, a);
515+
// T umlo; T umhi = unsigned_square_to_hilo_product(umlo, a);
515516
// umhi and umlo satisfy these requirements, since all type T variables
516-
// have bounds [0, R), and unsigned_multiply_to_hilo_product() computes
517+
// have bounds [0, R), and unsigned_square_to_hilo_product() computes
517518
// the full two-word product of a*a.
518519
// Therefore, since
519520
// x*x == (asqrHi + s*(R-2*a))*R + asqrLo, we can substitute and get
@@ -565,7 +566,7 @@ class MontyFullRangeMasked final :
565566
// We can express all of this in code via
566567
// T a = x.getbits();
567568
// T umlo;
568-
// T umhi = ::hurchalla::unsigned_multiply_to_hilo_product(umlo, a, a);
569+
// T umhi = ::hurchalla::unsigned_square_to_hilo_product(umlo, a);
569570
// T neg2a = static_cast<T>(-2) * a;
570571
// T result_hi = umhi + (x.getmask() & neg2a);
571572
// u_lo = umlo;

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/experimental/montgomery_pow_2kary/testbench_montgomery_pow_2kary.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "hurchalla/montgomery_arithmetic/low_level_api/detail/platform_specific/impl_array_get_Rsquared_mod_n.h"
99
#include "hurchalla/montgomery_arithmetic/MontgomeryForm.h"
1010
#include "hurchalla/montgomery_arithmetic/montgomery_form_aliases.h"
11+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
1112
#include "hurchalla/util/count_leading_zeros.h"
1213
#include "hurchalla/util/traits/ut_numeric_limits.h"
1314
#include "hurchalla/util/traits/safely_promote_unsigned.h"
@@ -796,7 +797,7 @@ bench_array_pow(U min, U range, U& totalU, unsigned int max_modulus_bits_reduce,
796797
for (size_t j=0; j < ARRAY_SIZE; ++j) {
797798
U modulus = tmpvec[i+j];
798799
U u_lo;
799-
U u_hi = hurchalla::unsigned_multiply_to_hilo_product(u_lo, r_mod_n[j], r_mod_n[j]);
800+
U u_hi = hurchalla::unsigned_square_to_hilo_product(u_lo, r_mod_n[j]);
800801
U remainder;
801802
//U quotient = div_2U_by_1U(u_hi, u_lo, modulus, remainder);
802803
div_2U_by_1U(u_hi, u_lo, modulus, remainder);

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/detail/experimental/montgomery_two_pow/testbench_montgomery_two_pow.cpp

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "hurchalla/montgomery_arithmetic/low_level_api/detail/platform_specific/impl_array_get_Rsquared_mod_n.h"
99
#include "hurchalla/montgomery_arithmetic/MontgomeryForm.h"
1010
#include "hurchalla/montgomery_arithmetic/montgomery_form_aliases.h"
11+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
1112
#include "hurchalla/util/count_leading_zeros.h"
1213
#include "hurchalla/util/traits/ut_numeric_limits.h"
1314
#include "hurchalla/util/traits/safely_promote_unsigned.h"
@@ -532,7 +533,7 @@ bench_array_two_pow(U min, U range, U& totalU, unsigned int max_modulus_bits_red
532533
for (size_t j=0; j < ARRAY_SIZE; ++j) {
533534
U modulus = tmpvec[i+j];
534535
U u_lo;
535-
U u_hi = hurchalla::unsigned_multiply_to_hilo_product(u_lo, r_mod_n[j], r_mod_n[j]);
536+
U u_hi = hurchalla::unsigned_square_to_hilo_product(u_lo, r_mod_n[j]);
536537
U remainder;
537538
//U quotient = div_2U_by_1U(u_hi, u_lo, modulus, remainder);
538539
div_2U_by_1U(u_hi, u_lo, modulus, remainder);
@@ -953,7 +954,21 @@ using namespace hurchalla;
953954
for (size_t i=0; i<4; ++i) {
954955
for (size_t j=0; j<timingA[i].size(); ++j) {
955956

956-
#if 1
957+
timingA[i][j].push_back(
958+
bench_array_two_pow<0, 31, 3, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
959+
timingA[i][j].push_back(
960+
bench_array_two_pow<0, 31, 4, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
961+
timingA[i][j].push_back(
962+
bench_array_two_pow<0, 31, 5, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
963+
timingA[i][j].push_back(
964+
bench_array_two_pow<0, 31, 6, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
965+
timingA[i][j].push_back(
966+
bench_array_two_pow<0, 31, 7, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
967+
timingA[i][j].push_back(
968+
bench_array_two_pow<0, 31, 8, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
969+
970+
971+
#if 0
957972
timingA[i][j].push_back(
958973
bench_array_two_pow<0, 27, 3, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
959974
timingA[i][j].push_back(
@@ -1134,7 +1149,7 @@ using namespace hurchalla;
11341149
}
11351150
#endif
11361151

1137-
#if 1
1152+
#if 0
11381153
timingA[i][j].push_back(
11391154
bench_array_two_pow<0, 0, 10, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
11401155
timingA[i][j].push_back(
@@ -1719,9 +1734,9 @@ using namespace hurchalla;
17191734

17201735
std::cout << "(ignore)" << uint_to_string(dummy) << "\n\n";
17211736

1722-
for (size_t j=0; j < best_timingA[0].size(); ++j) {
1723-
for (size_t i=0; i<4; ++i) {
1724-
const auto& t = best_timingA[i][j];
1737+
std::cout << "OVERALL BEST:\n";
1738+
for (size_t j=0; j < overall_bestA.size(); ++j) {
1739+
const auto& t = overall_bestA[j];
17251740
std::cout << 10.0 * t.time << " " << t.table_bits << " ";
17261741
if (t.code_section < 10)
17271742
std::cout << "0";
@@ -1734,14 +1749,12 @@ using namespace hurchalla;
17341749
std::cout << " 0" << t.array_size;
17351750
else
17361751
std::cout << " " << t.array_size;
1737-
if (i != 3)
1738-
std:: cout << " ";
1739-
}
17401752
std::cout << "\n";
17411753
}
1742-
std::cout << "OVERALL BEST:\n";
1743-
for (size_t j=0; j < overall_bestA.size(); ++j) {
1744-
const auto& t = overall_bestA[j];
1754+
std::cout << "Timings By Test Type:\n";
1755+
for (size_t j=0; j < best_timingA[0].size(); ++j) {
1756+
for (size_t i=0; i<4; ++i) {
1757+
const auto& t = best_timingA[i][j];
17451758
std::cout << 10.0 * t.time << " " << t.table_bits << " ";
17461759
if (t.code_section < 10)
17471760
std::cout << "0";
@@ -1754,6 +1767,9 @@ std::cout << "OVERALL BEST:\n";
17541767
std::cout << " 0" << t.array_size;
17551768
else
17561769
std::cout << " " << t.array_size;
1770+
if (i != 3)
1771+
std:: cout << " ";
1772+
}
17571773
std::cout << "\n";
17581774
}
17591775
#endif
@@ -1767,7 +1783,7 @@ std::cout << "OVERALL BEST:\n";
17671783
std::cout << "\nbegin benchmarks - scalar two_pow\n";
17681784

17691785
// warm up to get cpu boost (or throttle) going
1770-
for (size_t i=0; i<4; ++i)
1786+
for (size_t i=0; i<2; ++i)
17711787
bench_range<0, true , 0, MontType, false>(static_cast<U>(maxU - range), range, dummy, max_modulus_bits_reduce, seed, exponent_bits_reduce);
17721788

17731789
// std::array<std::vector<Timing>, 4> timings;
@@ -1779,6 +1795,8 @@ std::cout << "OVERALL BEST:\n";
17791795

17801796
// format is bench_range<TABLE_BITS, USE_SLIDING_WINDOW_OPTIMIZATION, CODE_SECTION,
17811797
// MontType, USE_SQUARING_VALUE_OPTIMIZATION>
1798+
timings[i][j].push_back(
1799+
bench_range<0, false, 22, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
17821800

17831801
#if 0
17841802
// This is a copy/paste of the "best of best" code sections from further below (nothing is new here).
@@ -1999,7 +2017,7 @@ std::cout << "OVERALL BEST:\n";
19992017

20002018

20012019

2002-
#if 1
2020+
#if 0
20032021
timings[i][j].push_back(
20042022
bench_range<0, true , 17, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
20052023
timings[i][j].push_back(
@@ -2309,7 +2327,7 @@ std::cout << "OVERALL BEST:\n";
23092327
bench_range<4, true , 1, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
23102328
#endif
23112329

2312-
#if 1
2330+
#if 0
23132331
timings[i][j].push_back(
23142332
bench_range<4, true , 0, MontType, false>(static_cast<U>(maxU - range), range, dummy, mmbr[i], seed, ebr[i]));
23152333
timings[i][j].push_back(
@@ -2572,9 +2590,9 @@ std::cout << "OVERALL BEST:\n";
25722590

25732591
std::cout << "(ignore)" << uint_to_string(dummy) << "\n\n";
25742592

2575-
for (size_t j=0; j < best_timings[0].size(); ++j) {
2576-
for (size_t i=0; i<4; ++i) {
2577-
const auto& t = best_timings[i][j];
2593+
std::cout << "OVERALL BEST:\n";
2594+
for (size_t j=0; j < overall_best.size(); ++j) {
2595+
const auto& t = overall_best[j];
25782596
std::cout << 10.0 * t.time;
25792597
if (t.uses_sliding_window)
25802598
std::cout << " t";
@@ -2587,14 +2605,12 @@ std::cout << "OVERALL BEST:\n";
25872605
std::cout << t.table_bits << " " << t.code_section;
25882606
if (t.code_section < 10)
25892607
std::cout << " ";
2590-
if (i != 3)
2591-
std:: cout << " ";
2592-
}
25932608
std::cout << "\n";
25942609
}
2595-
std::cout << "OVERALL BEST:\n";
2596-
for (size_t j=0; j < overall_best.size(); ++j) {
2597-
const auto& t = overall_best[j];
2610+
std::cout << "Timings By Test Type:\n";
2611+
for (size_t j=0; j < best_timings[0].size(); ++j) {
2612+
for (size_t i=0; i<4; ++i) {
2613+
const auto& t = best_timings[i][j];
25982614
std::cout << 10.0 * t.time;
25992615
if (t.uses_sliding_window)
26002616
std::cout << " t";
@@ -2607,6 +2623,9 @@ std::cout << "OVERALL BEST:\n";
26072623
std::cout << t.table_bits << " " << t.code_section;
26082624
if (t.code_section < 10)
26092625
std::cout << " ";
2626+
if (i != 3)
2627+
std:: cout << " ";
2628+
}
26102629
std::cout << "\n";
26112630
}
26122631
#endif

montgomery_arithmetic/include/hurchalla/montgomery_arithmetic/low_level_api/detail/platform_specific/impl_array_get_Rsquared_mod_n.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "hurchalla/modular_arithmetic/modular_addition.h"
1515
#include "hurchalla/modular_arithmetic/modular_multiplication.h"
1616
#include "hurchalla/util/traits/ut_numeric_limits.h"
17-
#include "hurchalla/util/unsigned_multiply_to_hilo_product.h"
17+
#include "hurchalla/util/unsigned_square_to_hilo_product.h"
1818
#include "hurchalla/util/compiler_macros.h"
1919
#include "hurchalla/modular_arithmetic/detail/clockwork_programming_by_contract.h"
2020
#include <array>
@@ -77,7 +77,7 @@ struct impl_array_get_Rsquared_mod_n {
7777
// use montgomery multiplication to square tmp on each iteration
7878
T u_hi, u_lo;
7979
HURCHALLA_REQUEST_UNROLL_LOOP for (size_t j=0; j<ARRAY_SIZE; ++j) {
80-
u_hi = hc::unsigned_multiply_to_hilo_product(u_lo, tmp[j], tmp[j]);
80+
u_hi = hc::unsigned_square_to_hilo_product(u_lo, tmp[j]);
8181
tmp[j] = hc::REDC_standard(u_hi, u_lo, n[j], inverse_n_modR[j], PTAG());
8282
}
8383
}
@@ -152,7 +152,7 @@ struct impl_array_get_Rsquared_mod_n<true, PTAG> {
152152
// use montgomery multiplication to square tmp on each iteration
153153
T u_hi, u_lo;
154154
HURCHALLA_REQUEST_UNROLL_LOOP for (size_t j=0; j<ARRAY_SIZE; ++j) {
155-
u_hi = hc::unsigned_multiply_to_hilo_product(u_lo, tmp[j], tmp[j]);
155+
u_hi = hc::unsigned_square_to_hilo_product(u_lo, tmp[j]);
156156
// use the same logic as MontyQuarterRange's montyREDC():
157157
tmp[j] = hc::REDC_incomplete(u_hi, u_lo, n[j], inverse_n_modR[j], PTAG());
158158
tmp[j] = static_cast<T>(tmp[j] + n[j]);
@@ -165,7 +165,7 @@ struct impl_array_get_Rsquared_mod_n<true, PTAG> {
165165
// use standard REDC, which will end with tmp in the range [0, n).
166166
T u_hi, u_lo;
167167
HURCHALLA_REQUEST_UNROLL_LOOP for (size_t j=0; j<ARRAY_SIZE; ++j) {
168-
u_hi = hc::unsigned_multiply_to_hilo_product(u_lo, tmp[j], tmp[j]);
168+
u_hi = hc::unsigned_square_to_hilo_product(u_lo, tmp[j]);
169169
tmp[j] = hc::REDC_standard(u_hi, u_lo, n[j], inverse_n_modR[j], PTAG());
170170
}
171171
}

0 commit comments

Comments
 (0)