Austin Schuh | bb1338c | 2024-06-15 19:31:16 -0700 | [diff] [blame] | 1 | /* mpn_fib2m -- calculate Fibonacci numbers, modulo m. |
| 2 | |
| 3 | Contributed to the GNU project by Marco Bodrato. |
| 4 | |
| 5 | THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST |
| 6 | CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN |
| 7 | FUTURE GNU MP RELEASES. |
| 8 | |
| 9 | Copyright 2001, 2002, 2005, 2009, 2018 Free Software Foundation, Inc. |
| 10 | |
| 11 | This file is part of the GNU MP Library. |
| 12 | |
| 13 | The GNU MP Library is free software; you can redistribute it and/or modify |
| 14 | it under the terms of either: |
| 15 | |
| 16 | * the GNU Lesser General Public License as published by the Free |
| 17 | Software Foundation; either version 3 of the License, or (at your |
| 18 | option) any later version. |
| 19 | |
| 20 | or |
| 21 | |
| 22 | * the GNU General Public License as published by the Free Software |
| 23 | Foundation; either version 2 of the License, or (at your option) any |
| 24 | later version. |
| 25 | |
| 26 | or both in parallel, as here. |
| 27 | |
| 28 | The GNU MP Library is distributed in the hope that it will be useful, but |
| 29 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 30 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 31 | for more details. |
| 32 | |
| 33 | You should have received copies of the GNU General Public License and the |
| 34 | GNU Lesser General Public License along with the GNU MP Library. If not, |
| 35 | see https://www.gnu.org/licenses/. */ |
| 36 | |
| 37 | #include <stdio.h> |
| 38 | #include "gmp-impl.h" |
| 39 | |
| 40 | /* Stores |{ap,n}-{bp,n}| in {rp,n}, |
| 41 | returns the sign of {ap,n}-{bp,n}. */ |
| 42 | static int |
| 43 | abs_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) |
| 44 | { |
| 45 | mp_limb_t x, y; |
| 46 | while (--n >= 0) |
| 47 | { |
| 48 | x = ap[n]; |
| 49 | y = bp[n]; |
| 50 | if (x != y) |
| 51 | { |
| 52 | ++n; |
| 53 | if (x > y) |
| 54 | { |
| 55 | ASSERT_NOCARRY (mpn_sub_n (rp, ap, bp, n)); |
| 56 | return 1; |
| 57 | } |
| 58 | else |
| 59 | { |
| 60 | ASSERT_NOCARRY (mpn_sub_n (rp, bp, ap, n)); |
| 61 | return -1; |
| 62 | } |
| 63 | } |
| 64 | rp[n] = 0; |
| 65 | } |
| 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | /* Computes at most count terms of the sequence needed by the |
| 70 | Lucas-Lehmer-Riesel test, indexing backward: |
| 71 | L_i = L_{i+1}^2 - 2 |
| 72 | |
| 73 | The sequence is computed modulo M = {mp, mn}. |
| 74 | The starting point is given in L_{count+1} = {lp, mn}. |
| 75 | The scratch pointed by sp, needs a space of at least 3 * mn + 1 limbs. |
| 76 | |
| 77 | Returns the index i>0 if L_i = 0 (mod M) is found within the |
| 78 | computed count terms of the sequence. Otherwise it returns zero. |
| 79 | |
| 80 | Note: (+/-2)^2-2=2, (+/-1)^2-2=-1, 0^2-2=-2 |
| 81 | */ |
| 82 | |
| 83 | static mp_bitcnt_t |
| 84 | mpn_llriter (mp_ptr lp, mp_srcptr mp, mp_size_t mn, mp_bitcnt_t count, mp_ptr sp) |
| 85 | { |
| 86 | do |
| 87 | { |
| 88 | mpn_sqr (sp, lp, mn); |
| 89 | mpn_tdiv_qr (sp + 2 * mn, lp, 0, sp, 2 * mn, mp, mn); |
| 90 | if (lp[0] < 5) |
| 91 | { |
| 92 | /* If L^2 % M < 5, |L^2 % M - 2| <= 2 */ |
| 93 | if (mn == 1 || mpn_zero_p (lp + 1, mn - 1)) |
| 94 | return (lp[0] == 2) ? count : 0; |
| 95 | else |
| 96 | MPN_DECR_U (lp, mn, 2); |
| 97 | } |
| 98 | else |
| 99 | lp[0] -= 2; |
| 100 | } while (--count != 0); |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | /* Store the Lucas' number L[n] at lp (maybe), computed modulo m. lp |
| 105 | and scratch should have room for mn*2+1 limbs. |
| 106 | |
| 107 | Returns the size of L[n] normally. |
| 108 | |
| 109 | If F[n] is zero modulo m, or L[n] is, returns 0 and lp is |
| 110 | undefined. |
| 111 | */ |
| 112 | |
| 113 | static mp_size_t |
| 114 | mpn_lucm (mp_ptr lp, mp_srcptr np, mp_size_t nn, mp_srcptr mp, mp_size_t mn, mp_ptr scratch) |
| 115 | { |
| 116 | int neg; |
| 117 | mp_limb_t cy; |
| 118 | |
| 119 | ASSERT (! MPN_OVERLAP_P (lp, MAX(2*mn+1,5), scratch, MAX(2*mn+1,5))); |
| 120 | ASSERT (nn > 0); |
| 121 | |
| 122 | neg = mpn_fib2m (lp, scratch, np, nn, mp, mn); |
| 123 | |
| 124 | /* F[n] = +/-{lp, mn}, F[n-1] = +/-{scratch, mn} */ |
| 125 | if (mpn_zero_p (lp, mn)) |
| 126 | return 0; |
| 127 | |
| 128 | if (neg) /* One sign is opposite, use sub instead of add. */ |
| 129 | { |
| 130 | #if HAVE_NATIVE_mpn_rsblsh1_n || HAVE_NATIVE_mpn_sublsh1_n |
| 131 | #if HAVE_NATIVE_mpn_rsblsh1_n |
| 132 | cy = mpn_rsblsh1_n (lp, lp, scratch, mn); /* L[n] = +/-(2F[n-1]-(-F[n])) */ |
| 133 | #else |
| 134 | cy = mpn_sublsh1_n (lp, lp, scratch, mn); /* L[n] = -/+(F[n]-(-2F[n-1])) */ |
| 135 | if (cy != 0) |
| 136 | cy = mpn_add_n (lp, lp, mp, mn) - cy; |
| 137 | #endif |
| 138 | if (cy > 1) |
| 139 | cy += mpn_add_n (lp, lp, mp, mn); |
| 140 | #else |
| 141 | cy = mpn_lshift (scratch, scratch, mn, 1); /* 2F[n-1] */ |
| 142 | if (UNLIKELY (cy)) |
| 143 | cy -= mpn_sub_n (lp, scratch, lp, mn); /* L[n] = +/-(2F[n-1]-(-F[n])) */ |
| 144 | else |
| 145 | abs_sub_n (lp, lp, scratch, mn); |
| 146 | #endif |
| 147 | ASSERT (cy <= 1); |
| 148 | } |
| 149 | else |
| 150 | { |
| 151 | #if HAVE_NATIVE_mpn_addlsh1_n |
| 152 | cy = mpn_addlsh1_n (lp, lp, scratch, mn); /* L[n] = +/-(2F[n-1]+F[n])) */ |
| 153 | #else |
| 154 | cy = mpn_lshift (scratch, scratch, mn, 1); |
| 155 | cy+= mpn_add_n (lp, lp, scratch, mn); |
| 156 | #endif |
| 157 | ASSERT (cy <= 2); |
| 158 | } |
| 159 | while (cy || mpn_cmp (lp, mp, mn) >= 0) |
| 160 | cy -= mpn_sub_n (lp, lp, mp, mn); |
| 161 | MPN_NORMALIZE (lp, mn); |
| 162 | return mn; |
| 163 | } |
| 164 | |
| 165 | int |
| 166 | mpn_strongfibo (mp_srcptr mp, mp_size_t mn, mp_ptr scratch) |
| 167 | { |
| 168 | mp_ptr lp, sp; |
| 169 | mp_size_t en; |
| 170 | mp_bitcnt_t b0; |
| 171 | TMP_DECL; |
| 172 | |
| 173 | #if GMP_NUMB_BITS % 4 == 0 |
| 174 | b0 = mpn_scan0 (mp, 0); |
| 175 | #else |
| 176 | { |
| 177 | mpz_t m = MPZ_ROINIT_N(mp, mn); |
| 178 | b0 = mpz_scan0 (m, 0); |
| 179 | } |
| 180 | if (UNLIKELY (b0 == mn * GMP_NUMB_BITS)) |
| 181 | { |
| 182 | en = 1; |
| 183 | scratch [0] = 1; |
| 184 | } |
| 185 | else |
| 186 | #endif |
| 187 | { |
| 188 | int cnt = b0 % GMP_NUMB_BITS; |
| 189 | en = b0 / GMP_NUMB_BITS; |
| 190 | if (LIKELY (cnt != 0)) |
| 191 | mpn_rshift (scratch, mp + en, mn - en, cnt); |
| 192 | else |
| 193 | MPN_COPY (scratch, mp + en, mn - en); |
| 194 | en = mn - en; |
| 195 | scratch [0] |= 1; |
| 196 | en -= scratch [en - 1] == 0; |
| 197 | } |
| 198 | TMP_MARK; |
| 199 | |
| 200 | lp = TMP_ALLOC_LIMBS (4 * mn + 6); |
| 201 | sp = lp + 2 * mn + 3; |
| 202 | en = mpn_lucm (sp, scratch, en, mp, mn, lp); |
| 203 | if (en != 0 && LIKELY (--b0 != 0)) |
| 204 | { |
| 205 | mpn_sqr (lp, sp, en); |
| 206 | lp [0] |= 2; /* V^2 + 2 */ |
| 207 | if (LIKELY (2 * en >= mn)) |
| 208 | mpn_tdiv_qr (sp, lp, 0, lp, 2 * en, mp, mn); |
| 209 | else |
| 210 | MPN_ZERO (lp + 2 * en, mn - 2 * en); |
| 211 | if (! mpn_zero_p (lp, mn) && LIKELY (--b0 != 0)) |
| 212 | b0 = mpn_llriter (lp, mp, mn, b0, lp + mn + 1); |
| 213 | } |
| 214 | TMP_FREE; |
| 215 | return (b0 != 0); |
| 216 | } |