Math::BigInt::LTM _log_int() fix by pjacklam
Karel Miko
7 years ago
117 | 117 | # based on _log_int() in Math::BigInt::GMP |
118 | 118 | |
119 | 119 | sub _log_int { |
120 | my ($c,$x,$base) = @_; | |
120 | my ($c, $x, $base) = @_; | |
121 | 121 | |
122 | 122 | # X == 0 => NaN |
123 | return if _is_zero($c,$x); | |
124 | ||
125 | $base = _new($c,2) unless defined $base; | |
126 | $base = _new($c,$base) unless ref $base; | |
123 | return if _is_zero($c, $x); | |
124 | ||
125 | $base = _new($c, 2) unless defined $base; | |
126 | $base = _new($c, $base) unless ref $base; | |
127 | 127 | |
128 | 128 | # BASE 0 or 1 => NaN |
129 | return if (_is_zero($c, $base) || | |
130 | _is_one($c, $base)); | |
131 | ||
132 | my $cmp = _acmp($c,$x,$base); # X == BASE => 1 | |
129 | return if _is_zero($c, $base) || _is_one($c, $base); | |
130 | ||
131 | # X == 1 => 0 (is exact) | |
132 | if (_is_one($c, $x)) { | |
133 | _set($c, $x, 0); | |
134 | return $x, 1; | |
135 | } | |
136 | ||
137 | my $cmp = _acmp($c, $x, $base); | |
138 | ||
139 | # X == BASE => 1 (is exact) | |
133 | 140 | if ($cmp == 0) { |
134 | # return one | |
135 | return (_one($c), 1); | |
136 | } | |
137 | # X < BASE | |
141 | _set($c, $x, 1); | |
142 | return $x, 1; | |
143 | } | |
144 | ||
145 | # 1 < X < BASE => 0 (is truncated) | |
138 | 146 | if ($cmp < 0) { |
139 | return (_zero($c),undef); | |
140 | } | |
147 | _set($c, $x, 0); | |
148 | return $x, 0; | |
149 | } | |
150 | ||
151 | my $x_org = _copy($c, $x); | |
152 | ||
153 | # Alternative 1: | |
141 | 154 | |
142 | 155 | # Compute a guess for the result based on: |
143 | # $guess = int ( length_in_base_10(X) / ( log(base) / log(10) ) ) | |
144 | my $len = _len($c,$x); | |
145 | my $log = log( _str($c,$base) ) / log(10); | |
146 | ||
147 | # calculate now a guess based on the values obtained above: | |
148 | my $x_org = _copy($c,$x); | |
149 | ||
150 | # keep the reference to $x, modifying it in place | |
156 | # $guess = int( length_in_base_10(X) / ( log(base) / log(10) ) ) | |
157 | ||
158 | my $len = _alen($c, $x); | |
159 | my $log = log(_num($c, $base)) / log(10); | |
160 | ||
151 | 161 | _set($c, $x, int($len / $log) - 1); |
152 | 162 | |
153 | my $trial = _pow ($c, _copy($c, $base), $x); | |
154 | my $a = _acmp($c,$trial,$x_org); | |
155 | ||
156 | if ($a == 0) { | |
157 | return ($x,1); | |
158 | } | |
159 | elsif ($a > 0) { | |
160 | # too big, shouldn't happen | |
161 | _div($c,$trial,$base); _dec($c, $x); | |
162 | } | |
163 | ||
164 | # find the real result by going forward: | |
165 | my $base_mul = _mul($c, _copy($c,$base), $base); | |
166 | my $two = _two($c); | |
167 | ||
168 | while (($a = _acmp($c, $trial, $x_org)) < 0) { | |
169 | _mul($c,$trial,$base_mul); _add($c, $x, $two); | |
170 | } | |
171 | ||
172 | my $exact = 1; | |
173 | if ($a > 0) { | |
174 | # overstepped the result | |
175 | _dec($c, $x); | |
176 | _div($c,$trial,$base); | |
177 | $a = _acmp($c,$trial,$x_org); | |
178 | if ($a > 0) { | |
163 | my $trial = _pow($c, _copy($c, $base), $x); | |
164 | my $acmp = _acmp($c, $trial, $x_org); | |
165 | ||
166 | # Exact result? | |
167 | ||
168 | return $x, 1 if $acmp == 0; | |
169 | ||
170 | # Too small? | |
171 | ||
172 | while ($acmp < 0) { | |
173 | _mul($c, $trial, $base); | |
174 | _inc($c, $x); | |
175 | $acmp = _acmp($c, $trial, $x_org); | |
176 | } | |
177 | ||
178 | # Too big? | |
179 | ||
180 | while ($acmp > 0) { | |
181 | _div($c, $trial, $base); | |
179 | 182 | _dec($c, $x); |
180 | } | |
181 | $exact = 0 if $a != 0; | |
182 | } | |
183 | ||
184 | return ($x, $exact); | |
185 | } | |
186 | ||
183 | $acmp = _acmp($c, $trial, $x_org); | |
184 | } | |
185 | ||
186 | return $x, 1 if $acmp == 0; # result is exact | |
187 | return $x, 0; # result is too small | |
188 | } | |
187 | 189 | 1; |
188 | 190 | |
189 | 191 | __END__ |