From 65397be71373e71fd364cc3bb436dfe34e4c6a31 Mon Sep 17 00:00:00 2001 From: mdw Date: Thu, 22 Jun 2000 19:01:44 +0000 Subject: [PATCH] Compute (approximations to) integer square roots. --- mp-sqrt.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 mp-sqrt.c diff --git a/mp-sqrt.c b/mp-sqrt.c new file mode 100644 index 0000000..98741e8 --- /dev/null +++ b/mp-sqrt.c @@ -0,0 +1,165 @@ +/* -*-c-*- + * + * $Id: mp-sqrt.c,v 1.1 2000/06/22 19:01:44 mdw Exp $ + * + * Compute integer square roots + * + * (c) 2000 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mp-sqrt.c,v $ + * Revision 1.1 2000/06/22 19:01:44 mdw + * Compute (approximations to) integer square roots. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "mp.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @mp_sqrt@ --- * + * + * Arguments: @mp *d@ = pointer to destination integer + * @mp *a@ = (nonnegative) integer to take square root of + * + * Returns: The largest integer %$x$% such that %$x^2 \le a$%. + * + * Use: Computes integer square roots. + * + * The current implementation isn't very good: it uses the + * Newton-Raphson method to find an approximation to %$a$%. If + * there's any demand for a better version, I'll write one. + */ + +mp *mp_sqrt(mp *d, mp *a) +{ + unsigned long z; + mp *q = MP_NEW, *r = MP_NEW; + + /* --- Sanity preservation --- */ + + assert(((void)"imaginary root in mp_sqrt", !(a->f & MP_NEG))); + + /* --- Deal with trivial cases --- */ + + MP_SHRINK(a); + if (a->v == a->vl) { + if (d) + mp_drop(d); + return (MP_ZERO); + } + + /* --- Find an initial guess of about the right size --- */ + + z = mp_bits(a); + z >>= 1; + mp_copy(a); + d = mp_lsr(d, a, z); + mp_drop(a); + + /* --- Main approximation --- * + * + * We use the Newton-Raphson recurrence relation + * + * %$x_{i+1} = x_i - \frac{x_i^2 - a}{2 x_i}$% + * + * We inspect the term %$q = x^2 - a$% to see when to stop. Increasing + * %$x$% is pointless when %$-q < 2 x + 1$%. + */ + + for (;;) { + q = mp_sqr(q, d); + q = mp_sub(q, q, a); + if (q->v == q->vl) + break; + if (q->f & MP_NEG) { + r = mp_lsl(r, d, 1); + r->f |= MP_NEG; + if (MP_CMP(q, <=, r)) + break; + } + mp_div(&r, &q, q, d); + r = mp_lsr(r, r, 1); + if (r->v == r->vl) + d = mp_sub(d, d, MP_ONE); + else + d = mp_sub(d, d, r); + } + + /* --- Finished, at last --- */ + + mp_drop(q); + if (r) + mp_drop(r); + return (d); +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include + +static int verify(dstr *v) +{ + mp *a = *(mp **)v[0].buf; + mp *qq = *(mp **)v[1].buf; + mp *q = mp_sqrt(MP_NEW, a); + int ok = 1; + + if (MP_CMP(q, !=, qq)) { + ok = 0; + fputs("\n*** sqrt failed", stderr); + fputs("\n*** a = ", stderr); mp_writefile(a, stderr, 10); + fputs("\n*** result = ", stderr); mp_writefile(q, stderr, 10); + fputs("\n*** expect = ", stderr); mp_writefile(qq, stderr, 10); + fputc('\n', stderr); + } + + mp_drop(a); + mp_drop(q); + mp_drop(qq); + assert(mparena_count(MPARENA_GLOBAL) == 0); + + return (ok); +} + +static test_chunk tests[] = { + { "sqrt", verify, { &type_mp, &type_mp, 0 } }, + { 0, 0, { 0 } }, +}; + +int main(int argc, char *argv[]) +{ + sub_init(); + test_run(argc, argv, tests, SRCDIR "/tests/mp"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ -- 2.11.0