/*
Copyright 2017 Free Software Foundation, Inc.
This file is part of the GNU MP Library test suite.
The GNU MP Library test suite is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
The GNU MP Library test suite is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with
the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */
/* Usage:
./sqrtrem_1_2 x
Checks mpn_sqrtrem() exhaustively, starting from 0, incrementing
the operand by a single unit, until all values handled by
mpn_sqrtrem{1,2} are tested. SLOW.
./sqrtrem_1_2 s 1
Checks some special cases for mpn_sqrtrem(). I.e. values of the form
2^k*i and 2^k*(i+1)-1, with k=2^n and 0<i<2^k, until all such values,
handled by mpn_sqrtrem{1,2}, are tested.
Currently supports only the test of values that fits in one limb.
Less slow than the exhaustive test.
./sqrtrem_1_2 c
Checks all corner cases for mpn_sqrtrem(). I.e. values of the form
i*i and (i+1)*(i+1)-1, for each value of i, until all such values,
handled by mpn_sqrtrem{1,2}, are tested.
Slightly faster than the special cases test.
For larger values, use
./try mpn_sqrtrem
*/
#include <stdlib.h>
#include <stdio.h>
#include "gmp-impl.h"
#include "longlong.h"
#include "tests.h"
#define STOP(x) return (x)
/* #define STOP(x) x */
#define SPINNER(v) \
do { \
MPN_SIZEINBASE_2EXP (spinner_count, q, v, 1); \
--spinner_count; \
spinner(); \
} while (0)
int something_wrong (mp_limb_t er, mp_limb_t ec, mp_limb_t es)
{
fprintf (stderr, "root = %lu , rem = {%lu , %lu}\n", (long unsigned) es,(long unsigned) ec,(long unsigned) er);
return -1;
}
int
check_all_values (int justone, int quick)
{
mp_limb_t es, mer, er, s[1], r[2], q[2];
mp_size_t x;
unsigned bits;
es=1;
if (quick) {
printf ("Quick, skipping some... (%u)\n", GMP_NUMB_BITS - 2);
es <<= GMP_NUMB_BITS / 2 - 1;
}
er=0;
mer= es << 1;
*q = es * es;
printf ("All values tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 1);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (UNLIKELY (er == mer)) {
++es;
if (UNLIKELY ((es & 0xff) == 0))
SPINNER(1);
mer +=2; /* mer = es * 2 */
er = 0;
} else
++er;
++*q;
} while (*q != 0);
q[1] = 1;
SPINNER(2);
printf ("\nValues of a single limb, tested.\n");
if (justone) return 0;
printf ("All values tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 2);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (UNLIKELY (er == mer)) {
++es;
if (UNLIKELY ((es & 0x7f) == 0))
SPINNER(2);
mer +=2; /* mer = es * 2 */
if (UNLIKELY (mer == 0))
break;
er = 0;
} else
++er;
q[1] += (++*q == 0);
} while (1);
SPINNER(2);
printf ("\nValues with at most a limb for reminder, tested.\n");
printf ("Testing more values not supported, jet.\n");
return 0;
}
mp_limb_t
upd (mp_limb_t *s, mp_limb_t k)
{
mp_limb_t _s = *s;
while (k > _s * 2)
{
k -= _s * 2 + 1;
++_s;
}
*s = _s;
return k;
}
mp_limb_t
upd1 (mp_limb_t *s, mp_limb_t k)
{
mp_limb_t _s = *s;
if (LIKELY (k < _s * 2)) return k + 1;
*s = _s + 1;
return k - _s * 2;
}
int
check_some_values (int justone, int quick)
{
mp_limb_t es, her, er, k, s[1], r[2], q[2];
mp_size_t x;
unsigned bits;
es = 1 << 1;
if (quick) {
es <<= GMP_NUMB_BITS / 4 - 1;
printf ("Quick, skipping some... (%u)\n", GMP_NUMB_BITS / 2);
}
er = 0;
*q = es * es;
printf ("High-half values tested, up to bits:\n");
do {
k = *q - 1;
do {
x = mpn_sqrtrem (s, r, q, 1);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (UNLIKELY ((es & 0xffff) == 0))
SPINNER(1);
if ((*q & k) == 0) {
*q |= k;
er = upd (&es, k + er);
} else {
++*q;
er = upd1 (&es, er);
}
} while (es & k);
} while (*q != 0);
q[1] = 1;
SPINNER(2);
printf ("\nValues of a single limb, tested.\n");
if (justone) return 0;
if (quick) {
es <<= GMP_NUMB_BITS / 2 - 1;
q[1] <<= GMP_NUMB_BITS - 2;
printf ("Quick, skipping some... (%u)\n", GMP_NUMB_BITS - 2);
}
printf ("High-half values tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 2);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (*q == 0) {
*q = GMP_NUMB_MAX;
if (UNLIKELY ((es & 0xffff) == 0)) {
if (UNLIKELY (es == GMP_NUMB_HIGHBIT))
break;
SPINNER(2);
}
/* er = er + GMP_NUMB_MAX - 1 - es*2 // postponed */
++es;
/* er = er + GMP_NUMB_MAX - 1 - 2*(es-1) =
= er +(GMP_NUMB_MAX + 1)- 2* es = er - 2*es */
er = upd (&es, er - 2 * es);
} else {
*q = 0;
++q[1];
er = upd1 (&es, er);
}
} while (1);
SPINNER(2);
printf ("\nValues with at most a limb for reminder, tested.\n");
er = GMP_NUMB_MAX; her = 0;
printf ("High-half values tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 2);
if (UNLIKELY (x != (her?2:(er != 0))) || UNLIKELY (*s != es)
|| UNLIKELY ((x != 0) && ((er != *r) || ((x == 2) && (r[1] != 1)))))
STOP (something_wrong (er, her, es));
if (*q == 0) {
*q = GMP_NUMB_MAX;
if (UNLIKELY ((es & 0xffff) == 0)) {
SPINNER(2);
}
if (her) {
++es;
her = 0;
er = er - 2 * es;
} else {
her = --er != GMP_NUMB_MAX;
if (her & (er > es * 2)) {
er -= es * 2 + 1;
her = 0;
++es;
}
}
} else {
*q = 0;
if (++q[1] == 0) break;
if ((her == 0) | (er < es * 2)) {
her += ++er == 0;
} else {
er -= es * 2;
her = 0;
++es;
}
}
} while (1);
printf ("| %u\nValues of at most two limbs, tested.\n", GMP_NUMB_BITS*2);
return 0;
}
int
check_corner_cases (int justone, int quick)
{
mp_limb_t es, er, s[1], r[2], q[2];
mp_size_t x;
unsigned bits;
es = 1;
if (quick) {
es <<= GMP_NUMB_BITS / 2 - 1;
printf ("Quick, skipping some... (%u)\n", GMP_NUMB_BITS - 2);
}
er = 0;
*q = es*es;
printf ("Corner cases tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 1);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (er != 0) {
++es;
if (UNLIKELY ((es & 0xffff) == 0))
SPINNER(1);
er = 0;
++*q;
} else {
er = es * 2;
*q += er;
}
} while (*q != 0);
q[1] = 1;
SPINNER(2);
printf ("\nValues of a single limb, tested.\n");
if (justone) return 0;
if (quick) {
es <<= GMP_NUMB_BITS / 2 - 1;
q[1] <<= GMP_NUMB_BITS - 2;
printf ("Quick, skipping some... (%u)\n", GMP_NUMB_BITS - 2);
--es;
--q[1];
q[0] -= es*2+1;
}
printf ("Corner cases tested, up to bits:\n");
do {
x = mpn_sqrtrem (s, r, q, 2);
if (UNLIKELY (x != (er != 0)) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 1) && (er != *r)))
STOP (something_wrong (er, 0, es));
if (er != 0) {
++es;
if (UNLIKELY ((es & 0xff) == 0))
SPINNER(2);
er = 0;
q[1] += (++*q == 0);
if (UNLIKELY (es == GMP_NUMB_HIGHBIT))
break;
} else {
er = es * 2;
add_ssaaaa (q[1], *q, q[1], *q, 0, er);
}
} while (1);
SPINNER(2);
printf ("\nValues with at most a limb for reminder, tested.\nCorner cases tested, up to bits:\n");
x = mpn_sqrtrem (s, r, q, 2);
if ((*s != es) || (x != 0))
STOP (something_wrong (0, 0, es));
q[1] += 1;
x = mpn_sqrtrem (s, r, q, 2);
if ((*s != es) || (x != 2) || (*r != 0) || (r[1] != 1))
STOP (something_wrong (0, 1, es));
++es;
q[1] += (++*q == 0);
do {
x = mpn_sqrtrem (s, r, q, 2);
if (UNLIKELY (x != (er != 0) * 2) || UNLIKELY (*s != es)
|| UNLIKELY ((x == 2) && ((er != *r) || (r[1] != 1))))
STOP (something_wrong (er, er != 0, es));
if (er != 0) {
++es;
if (UNLIKELY (es == 0))
break;
if (UNLIKELY ((es & 0xff) == 0))
SPINNER(2);
er = 0;
q[1] += (++*q == 0);
} else {
er = es * 2;
add_ssaaaa (q[1], *q, q[1], *q, 1, er);
}
} while (1);
printf ("| %u\nValues of at most two limbs, tested.\n", GMP_NUMB_BITS*2);
return 0;
}
int
main (int argc, char **argv)
{
int mode = 0;
int justone = 0;
int quick = 0;
for (;argc > 1;--argc,++argv)
switch (*argv[1]) {
default:
fprintf (stderr, "usage: sqrtrem_1_2 [x|c|s] [1|2] [q]\n");
exit (1);
case 'x':
mode = 0;
break;
case 'c':
mode = 1;
break;
case 's':
mode = 2;
break;
case 'q':
quick = 1;
break;
case '1':
justone = 1;
break;
case '2':
justone = 0;
}
switch (mode) {
default:
return check_all_values (justone, quick);
case 1:
return check_corner_cases (justone, quick);
case 2:
return check_some_values (justone, quick);
}
}