00001 #include "multiprec.h"
00002 #include <string.h>
00003
00015 void
00016 ymp_iroot(mp_ref_t result, mp_cref_t self, size_t n)
00017 {
00018
00019
00020 size_t i;
00021 size_t ilog2, bit_len;
00022 struct multiprec x, x_prev;
00023 struct multiprec tmp;
00024 struct multiprec self_copy;
00025
00026 if (!self->sign)
00027 {
00028 result->sign = positive_sign;
00029 }
00030 else
00031 {
00032
00033 if ( (n & 0x01) == 0) ymp_fatal("負数の偶数乗根");
00034
00035
00036 result->sign = self->sign;
00037
00038
00039 memcpy(&self_copy, self, sizeof(struct multiprec));
00040 self_copy.sign = positive_sign;
00041 self = &self_copy;
00042 }
00043
00044
00045
00046
00047 ilog2 = ymp_ilog2(self); ++ilog2;
00048
00049 bit_len = ilog2 / n;
00050 if (bit_len == 0)
00051 {
00052 if (ymp_is_zero(self)) ymp_assign_abs_digit(result, 0);
00053 else ymp_assign_abs_digit(result, 1);
00054 return;
00055 }
00056 if (ilog2 % n) ++bit_len;
00057
00058 ymp_alloca_initialize(&x, self->used * n + 1);
00059 for (i=0; i<bit_len/DIGIT_BIT; ++i) x.digits[i] = 0;
00060 x.digits[i] = 1u << (bit_len%DIGIT_BIT);
00061 x.used = ++i;
00062
00063
00064
00065
00066
00067 ymp_alloca_initialize(&tmp, self->used * n + 1);
00068 ymp_alloca_initialize(&x_prev, self->used * n + 1);
00069
00070
00071
00072 for (;;)
00073 {
00074 l_newton_iteration:
00075 ymp_assign(&x_prev, &x);
00076
00077
00078 ymp_pow(&tmp, &x_prev, n-1);
00079
00080 ymp_assign(&x, &tmp);
00081 ymp_mul(&x, &x, &x_prev);
00082 ymp_mul_digit(&x, &x, n-1);
00083 ymp_add(&x, &x, self);
00084
00085 ymp_mul_digit(&tmp, &tmp, n);
00086
00087 ymp_div(&x, &x, &tmp);
00088
00089
00090 if (x.used != x_prev.used) continue;
00091
00092 for (i=1; i<x.used; ++i)
00093 {
00094 if (x.digits[i] != x_prev.digits[i])
00095 goto l_newton_iteration;
00096 }
00097
00098
00099 switch ((int)(x.digits[0] - x_prev.digits[0]))
00100 {
00101 case 0:
00102 ymp_assign_abs(result, &x);
00103 return;
00104
00105 case 1:
00106 for (;;)
00107 {
00108 ymp_pow(&tmp, &x, n);
00109 if (ymp_compare_abs(&tmp, self) > 0) break;
00110 ymp_add_digit(&x, &x, 1);
00111 }
00112 ymp_sub_digit(&x, &x, 1);
00113 ymp_assign_abs(result, &x);
00114 return;
00115
00116 case -1:
00117 for (;;)
00118 {
00119 ymp_pow(&tmp, &x, n);
00120 if (ymp_compare_abs(&tmp, self) <= 0) break;
00121 ymp_sub_digit(&x, &x, 1);
00122 }
00123 ymp_assign_abs(result, &x);
00124 return;
00125 }
00126 }
00127 }