Parent Directory
|
Revision Log
Revision 2584 - (view) (download) (as text)
| 1 : | aubreyja | 2584 | sub textbook_ref { |
| 2 : | my ($text, $sec, $ex) = @_; | ||
| 3 : | return "$text section $sec, exercise $ex"; | ||
| 4 : | } | ||
| 5 : | |||
| 6 : | sub textbook_ref_corr { | ||
| 7 : | return "Similar to " . textbook_ref(@_) . "."; | ||
| 8 : | } | ||
| 9 : | |||
| 10 : | sub textbook_ref_exact { | ||
| 11 : | return "From " . textbook_ref(@_) . "."; | ||
| 12 : | } | ||
| 13 : | |||
| 14 : | sub list_random_multi_uniq($@) { | ||
| 15 : | my ($n, @list) = @_; | ||
| 16 : | my @result; | ||
| 17 : | while (@result < $n) { | ||
| 18 : | my $i = random(0,$#list,1); | ||
| 19 : | if ($i==0) { | ||
| 20 : | push @result, shift @list; | ||
| 21 : | } elsif ($i==$#list) { | ||
| 22 : | push @result, pop @list; | ||
| 23 : | } else { | ||
| 24 : | push @result, $list[$i]; | ||
| 25 : | $list[$i] = pop @list; | ||
| 26 : | } | ||
| 27 : | } | ||
| 28 : | return @result; | ||
| 29 : | } | ||
| 30 : | |||
| 31 : | sub IINT { | ||
| 32 : | return '\int\!\!\int'; | ||
| 33 : | } | ||
| 34 : | |||
| 35 : | sub IIINT { | ||
| 36 : | return '\int\!\!\int\!\!\int'; | ||
| 37 : | } | ||
| 38 : | |||
| 39 : | $IINT = IINT(); | ||
| 40 : | $IIINT = IIINT(); | ||
| 41 : | |||
| 42 : | sub VUSAGE { | ||
| 43 : | return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a vector, for example \(\langle x,y,z\rangle\), type "' . $LTS . ' x, y, z ' . $GTS . '".'; | ||
| 44 : | } | ||
| 45 : | |||
| 46 : | $VUSAGE = VUSAGE(); | ||
| 47 : | |||
| 48 : | sub PUSAGE { | ||
| 49 : | return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a point, for example \( (x,y,z) \), type "(x, y, z)".'; | ||
| 50 : | } | ||
| 51 : | |||
| 52 : | $PUSAGE = PUSAGE(); | ||
| 53 : | |||
| 54 : | #sam# copied from bkellMacros.pl -- no reason to maintain two macro files. | ||
| 55 : | #sam# FIXME -- these should probably lose their "bkell_" prefixes. | ||
| 56 : | |||
| 57 : | # bkellMacros.pl | ||
| 58 : | # Brian Kell <bkell@cse.unl.edu> | ||
| 59 : | # Last updated 3:24 CDT, 24 Jun 2007 | ||
| 60 : | |||
| 61 : | ############################################################################### | ||
| 62 : | # bkell_linear_simplify($a, $b) | ||
| 63 : | # | ||
| 64 : | # Returns a string representing $a*x+$b in simplified form, where "simplified" | ||
| 65 : | # means something along the lines of the following: | ||
| 66 : | # | ||
| 67 : | # a b output | ||
| 68 : | # --- --- ------ | ||
| 69 : | # 0 0 0 | ||
| 70 : | # 1 1 x+1 | ||
| 71 : | # -1 -1 -x-1 | ||
| 72 : | # -1 5 5-x | ||
| 73 : | # 2 0 2x | ||
| 74 : | # -4 3 3-4x | ||
| 75 : | # -4 -3 -4x-3 | ||
| 76 : | # | ||
| 77 : | sub bkell_linear_simplify | ||
| 78 : | { | ||
| 79 : | my ($a, $b) = @_; | ||
| 80 : | |||
| 81 : | if ($a == 0) { | ||
| 82 : | return "$b"; | ||
| 83 : | } elsif ($b == 0) { | ||
| 84 : | if ($a == -1) { | ||
| 85 : | return "-x"; | ||
| 86 : | } elsif ($a == 1) { | ||
| 87 : | return "x"; | ||
| 88 : | } else { | ||
| 89 : | return "${a}x"; | ||
| 90 : | } | ||
| 91 : | } elsif ($a < 0 && $b > 0) { | ||
| 92 : | if ($a == -1) { | ||
| 93 : | return "$b-x"; | ||
| 94 : | } else { | ||
| 95 : | return "$b-".(abs $a)."x"; | ||
| 96 : | } | ||
| 97 : | } elsif ($a == 1) { | ||
| 98 : | return "x".sprintf("%+d", $b); | ||
| 99 : | } elsif ($a == -1) { | ||
| 100 : | return "-x".sprintf("%+d", $b); | ||
| 101 : | } else { | ||
| 102 : | return "${a}x".sprintf("%+d", $b); | ||
| 103 : | } | ||
| 104 : | } | ||
| 105 : | |||
| 106 : | ############################################################################### | ||
| 107 : | # bkell_simplify_fraction($num, $denom) | ||
| 108 : | # | ||
| 109 : | # Simplifies the fraction $num/$denom; returns the list ($num, $denom). | ||
| 110 : | # | ||
| 111 : | sub bkell_simplify_fraction | ||
| 112 : | { | ||
| 113 : | my ($num, $denom) = @_; | ||
| 114 : | |||
| 115 : | if ($num == 0) { return (0, 1); } | ||
| 116 : | |||
| 117 : | my $sign = +1; | ||
| 118 : | if ($num < 0) { $sign *= -1; $num *= -1; } | ||
| 119 : | if ($denom < 0) { $sign *= -1; $denom *= -1; } | ||
| 120 : | |||
| 121 : | for ($i = 2; $i <= $num && $i <= $denom; ++$i) { | ||
| 122 : | while ($num % $i == 0 && $denom % $i == 0) { | ||
| 123 : | $num /= $i; | ||
| 124 : | $denom /= $i; | ||
| 125 : | } | ||
| 126 : | } | ||
| 127 : | |||
| 128 : | return ($sign*$num, $denom); | ||
| 129 : | } | ||
| 130 : | |||
| 131 : | ############################################################################### | ||
| 132 : | # bkell_simplify_fraction_string($num, $denom [, $flags]) | ||
| 133 : | # | ||
| 134 : | # Returns a string like "$num/$denom" in simplified form. If the string $flags | ||
| 135 : | # contains "+", then a leading "+" is included if the fraction is non-negative. | ||
| 136 : | # If $flags contains "0", then a fraction with a value of 0 will cause the | ||
| 137 : | # empty string to be returned. If $flags contains "1", then a fraction with a | ||
| 138 : | # value of 1 or -1 will cause only the sign to be returned (or the empty | ||
| 139 : | # string, if the fraction is non-negative and $flags does not contain "+"). If | ||
| 140 : | # $flags contains "(", then parentheses will be placed around "$num/$denom" | ||
| 141 : | # (the sign, if it exists, will be outside the parentheses); if $flags contains | ||
| 142 : | # "[", then parentheses will be used only if a sign is used and the denominator | ||
| 143 : | # is other than 1. A flag of "(" overrides "]". If $flags contains "f", then | ||
| 144 : | # the string returned will be in the form "{$num \over $denom}" instead of the | ||
| 145 : | # normal slashed version. | ||
| 146 : | # | ||
| 147 : | sub bkell_simplify_fraction_string | ||
| 148 : | { | ||
| 149 : | my ($num, $denom, $flags) = @_; | ||
| 150 : | if (!defined $flags) { $flags = ""; } | ||
| 151 : | |||
| 152 : | ($num, $denom) = bkell_simplify_fraction($num, $denom); | ||
| 153 : | |||
| 154 : | my $sign = ($num >= 0 ? ($flags =~ /\+/ ? "+" : "") : "-"); | ||
| 155 : | $num = abs $num; | ||
| 156 : | |||
| 157 : | my ($pre, $post); | ||
| 158 : | if ($flags =~ /\(/ || ($flags =~ /\[/ && $sign ne "" && $denom != 1)) { | ||
| 159 : | ($pre, $post) = ("(", ")"); | ||
| 160 : | } else { | ||
| 161 : | ($pre, $post) = ("", ""); | ||
| 162 : | } | ||
| 163 : | |||
| 164 : | if ($num == 0 && $flags =~ /0/) { return ""; } | ||
| 165 : | if ($denom == 1) { | ||
| 166 : | if ($num == 1 && $flags =~ /1/) { return "$sign"; } | ||
| 167 : | return "$sign$pre$num$post"; | ||
| 168 : | } | ||
| 169 : | if ($flags =~ /f/) { return "$sign$pre {$num \\over $denom}$post"; } | ||
| 170 : | return "$sign$pre$num/$denom$post"; | ||
| 171 : | } | ||
| 172 : | |||
| 173 : | ############################################################################### | ||
| 174 : | # bkell_poly_term($coeff_num, $coeff_denom, $var [, "+"]) | ||
| 175 : | # | ||
| 176 : | # Produces and simplifies a term of a polynomial of the general form | ||
| 177 : | # "($coeff_num/$coeff_denom)$var". Handles special cases (such as $num==0, | ||
| 178 : | # $denom==1, etc.). If the fourth argument is "+", then a leading "+" is | ||
| 179 : | # included if the term is positive. | ||
| 180 : | # | ||
| 181 : | sub bkell_poly_term | ||
| 182 : | { | ||
| 183 : | my ($coeff_num, $coeff_denom, $var, $plus) = @_; | ||
| 184 : | if (!defined $plus) { $plus = ""; } | ||
| 185 : | |||
| 186 : | if ($coeff_num == 0) { return ""; } | ||
| 187 : | |||
| 188 : | my $sign = +1; | ||
| 189 : | if ($coeff_num < 0) { $sign *= -1; $coeff_num *= -1; } | ||
| 190 : | if ($coeff_denom < 0) { $sign *= -1; $coeff_denom *= -1; } | ||
| 191 : | $sign = ($sign > 0 ? ($plus eq "+" ? "+" : "") : "-"); | ||
| 192 : | |||
| 193 : | my ($num, $denom) = bkell_simplify_fraction($coeff_num, $coeff_denom); | ||
| 194 : | |||
| 195 : | if ($denom == 1) { | ||
| 196 : | if ($num == 1) { | ||
| 197 : | return "$sign$var"; | ||
| 198 : | } else { | ||
| 199 : | return "$sign$num$var"; | ||
| 200 : | } | ||
| 201 : | } | ||
| 202 : | |||
| 203 : | return "$sign($num/$denom)$var"; | ||
| 204 : | } | ||
| 205 : | |||
| 206 : | ############################################################################### | ||
| 207 : | # bkell_gcd($x, $y) | ||
| 208 : | # | ||
| 209 : | # Returns the greatest common divisor of $x and $y. | ||
| 210 : | # | ||
| 211 : | sub bkell_gcd { | ||
| 212 : | my ($x, $y) = @_; | ||
| 213 : | $x = abs $x; | ||
| 214 : | $y = abs $y; | ||
| 215 : | if ($x > $y) { ($x, $y) = ($y, $x); } | ||
| 216 : | if ($x == 0) { return $y; } | ||
| 217 : | my $r = $y % $x; | ||
| 218 : | if ($r == 0) { return $x; } | ||
| 219 : | return bkell_gcd($x, $r); | ||
| 220 : | } | ||
| 221 : | |||
| 222 : | ############################################################################### | ||
| 223 : | # bkell_poly_eval($x, $a_n, ..., $a_0) | ||
| 224 : | # | ||
| 225 : | # Evaluates the polynomial a_n*x^n + ... + a_1*x + a_0 at the given value of x. | ||
| 226 : | # | ||
| 227 : | sub bkell_poly_eval | ||
| 228 : | { | ||
| 229 : | my $x = shift; | ||
| 230 : | my $value = shift; | ||
| 231 : | while (@_) { $value *= $x; $value += shift; } | ||
| 232 : | return $value; | ||
| 233 : | } | ||
| 234 : | |||
| 235 : | ############################################################################### | ||
| 236 : | # bkell_real_zeros_finder($a_n, ..., $a_0) | ||
| 237 : | # | ||
| 238 : | # Returns a list of numerical approximations of the zeros of the polynomial | ||
| 239 : | # a_n*x^n + ... + a_1*x + a_0, in order from least to greatest. | ||
| 240 : | # | ||
| 241 : | # The possibility of overflow or underflow is ignored. Overflow is likely to be | ||
| 242 : | # a bigger problem than underflow. | ||
| 243 : | # | ||
| 244 : | # Do not use this code to guide missiles or control nuclear power plants. | ||
| 245 : | # | ||
| 246 : | sub bkell_real_zeros_finder | ||
| 247 : | { | ||
| 248 : | my @coeffs = @_; | ||
| 249 : | |||
| 250 : | while (@coeffs && $coeffs[0] == 0) { shift @coeffs; } | ||
| 251 : | my $deg = $#coeffs; | ||
| 252 : | |||
| 253 : | if ($deg == -1) { | ||
| 254 : | return ("x"); # zero polynomial is zero everywhere | ||
| 255 : | } elsif ($deg == 0) { | ||
| 256 : | return (); # constant nonzero polynomial has no zeros | ||
| 257 : | } elsif ($deg == 1) { | ||
| 258 : | return (-$coeffs[1]/$coeffs[0]); # linear polynomial has one zero | ||
| 259 : | } | ||
| 260 : | |||
| 261 : | # find critical points | ||
| 262 : | my @derivative = @coeffs; | ||
| 263 : | pop @derivative; | ||
| 264 : | for (my $i = 0; $i < $#derivative; ++$i) { | ||
| 265 : | $derivative[$i] *= @derivative - $i; | ||
| 266 : | } | ||
| 267 : | my @cp = bkell_real_zeros_finder(@derivative); | ||
| 268 : | |||
| 269 : | # if no critical points, we have a monotone function | ||
| 270 : | if (!@cp) { | ||
| 271 : | my ($lb, $rb) = (-1, 1); | ||
| 272 : | my $y1 = bkell_poly_eval($lb, @coeffs); | ||
| 273 : | my $y2 = bkell_poly_eval($rb, @coeffs); | ||
| 274 : | my $sign = ($y1 < $y2 ? +1 : -1); | ||
| 275 : | while ($sign * $y1 > 0) { | ||
| 276 : | $lb *= 2; | ||
| 277 : | $y1 = bkell_poly_eval($lb, @coeffs); | ||
| 278 : | } | ||
| 279 : | while ($sign * $y2 < 0) { | ||
| 280 : | $rb *= 2; | ||
| 281 : | $y2 = bkell_poly_eval($rb, @coeffs); | ||
| 282 : | } | ||
| 283 : | my $guess_x = ($lb + $rb) / 2; | ||
| 284 : | while ($lb < $guess_x && $guess_x < $rb && | ||
| 285 : | (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) | ||
| 286 : | { | ||
| 287 : | if ( ($y1 < 0 && $guess_y < 0) || ($y1 > 0 && $guess_y > 0) ) { | ||
| 288 : | $lb = $guess_x; | ||
| 289 : | } else { | ||
| 290 : | $rb = $guess_x; | ||
| 291 : | } | ||
| 292 : | $guess_x = ($lb + $rb) / 2; | ||
| 293 : | } | ||
| 294 : | return ($guess_x); | ||
| 295 : | } | ||
| 296 : | |||
| 297 : | my @zeros = (); | ||
| 298 : | |||
| 299 : | # search for a zero to the left of the first critical point | ||
| 300 : | { | ||
| 301 : | my $y = bkell_poly_eval($cp[0], @coeffs); | ||
| 302 : | # we catch this case when we check between critical points: | ||
| 303 : | last if $y == 0; | ||
| 304 : | # not really the limit, but only the sign matters: | ||
| 305 : | my $lim = $coeffs[0] * ($deg % 2 ? -1 : +1); | ||
| 306 : | if ( ($y > 0 && $lim < 0) || ($y < 0 && $lim > 0) ) { | ||
| 307 : | my ($lb, $rb) = (undef, $cp[0]); | ||
| 308 : | my $guess_x = $rb - 10; | ||
| 309 : | if ($guess_x >= 0) { $guess_x = -10; } | ||
| 310 : | while ((!defined $lb || $lb < $guess_x) && $guess_x < $rb && | ||
| 311 : | (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) | ||
| 312 : | { | ||
| 313 : | if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) { | ||
| 314 : | $rb = $guess_x; | ||
| 315 : | if (defined $lb) { | ||
| 316 : | $guess_x = ($lb + $guess_x) / 2; | ||
| 317 : | } else { | ||
| 318 : | $guess_x *= 2; | ||
| 319 : | } | ||
| 320 : | } else { | ||
| 321 : | $lb = $guess_x; | ||
| 322 : | $guess_x = ($guess_x + $rb) / 2; | ||
| 323 : | } | ||
| 324 : | } | ||
| 325 : | push @zeros, $guess_x; | ||
| 326 : | } | ||
| 327 : | } | ||
| 328 : | |||
| 329 : | # search for zeros between critical points | ||
| 330 : | for (my $i = 0; $i < $#cp; ++$i) { | ||
| 331 : | my $y1 = bkell_poly_eval($cp[$i], @coeffs); | ||
| 332 : | if ($y1 == 0) { | ||
| 333 : | push @zeros, $cp[$i]; | ||
| 334 : | next; | ||
| 335 : | } | ||
| 336 : | my $y2 = bkell_poly_eval($cp[$i+1], @coeffs); | ||
| 337 : | if ($y2 == 0) { | ||
| 338 : | push @zeros, $cp[$i+1]; | ||
| 339 : | ++$i; | ||
| 340 : | next; | ||
| 341 : | } | ||
| 342 : | next if ($y1 > 0 && $y2 > 0) || ($y1 < 0 && $y2 < 0); | ||
| 343 : | my ($lb, $rb) = ($cp[$i], $cp[$i+1]); | ||
| 344 : | my $guess_x = ($lb + $rb) / 2; | ||
| 345 : | while ($lb < $guess_x && $guess_x < $rb && | ||
| 346 : | (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) | ||
| 347 : | { | ||
| 348 : | if ( ($y1 > 0 && $guess_y > 0) || ($y1 < 0 && $guess_y < 0) ) { | ||
| 349 : | $lb = $guess_x; | ||
| 350 : | } else { | ||
| 351 : | $rb = $guess_x; | ||
| 352 : | } | ||
| 353 : | $guess_x = ($lb + $rb) / 2; | ||
| 354 : | } | ||
| 355 : | push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x; | ||
| 356 : | } | ||
| 357 : | |||
| 358 : | # search for a zero to the right of the last critical point | ||
| 359 : | { | ||
| 360 : | my $y = bkell_poly_eval($cp[-1], @coeffs); | ||
| 361 : | if ($y == 0 && $zeros[-1] != $cp[-1]) { | ||
| 362 : | push @zeros, $cp[-1]; | ||
| 363 : | last; | ||
| 364 : | } | ||
| 365 : | if ( ($y > 0 && $coeffs[0] < 0) || ($y < 0 && $coeffs[0] > 0) ) { | ||
| 366 : | my ($lb, $rb) = ($cp[-1], undef); | ||
| 367 : | my $guess_x = $lb + 10; | ||
| 368 : | if ($guess_x <= 0) { $guess_x = 10; } | ||
| 369 : | while ($lb < $guess_x && (!defined $rb || $guess_x < $rb) && | ||
| 370 : | (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) | ||
| 371 : | { | ||
| 372 : | if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) { | ||
| 373 : | $lb = $guess_x; | ||
| 374 : | if (defined $rb) { | ||
| 375 : | $guess_x = ($guess_x + $rb) / 2; | ||
| 376 : | } else { | ||
| 377 : | $guess_x *= 2; | ||
| 378 : | } | ||
| 379 : | } else { | ||
| 380 : | $rb = $guess_x; | ||
| 381 : | $guess_x = ($lb + $guess_x) / 2; | ||
| 382 : | } | ||
| 383 : | } | ||
| 384 : | push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x; | ||
| 385 : | } | ||
| 386 : | } | ||
| 387 : | |||
| 388 : | return @zeros; | ||
| 389 : | } | ||
| 390 : | |||
| 391 : | ############################################################################### | ||
| 392 : | # bkell_floor($x) | ||
| 393 : | # | ||
| 394 : | # Returns the floor of $x. Normally this would be done with POSIX::floor, but | ||
| 395 : | # WeBWorK doesn't allow you to use standard modules like POSIX. | ||
| 396 : | # | ||
| 397 : | sub bkell_floor | ||
| 398 : | { | ||
| 399 : | my $x = shift; | ||
| 400 : | my $floor = int $x; | ||
| 401 : | if ($x < 0 && $x != $floor) { $floor -= 1; } | ||
| 402 : | return $floor; | ||
| 403 : | } | ||
| 404 : | |||
| 405 : | ############################################################################### | ||
| 406 : | # bkell_ceil($x) | ||
| 407 : | # | ||
| 408 : | # Returns the ceiling of $x. | ||
| 409 : | # | ||
| 410 : | sub bkell_ceil | ||
| 411 : | { | ||
| 412 : | return -bkell_floor(-shift); | ||
| 413 : | } | ||
| 414 : | |||
| 415 : | ############################################################################### | ||
| 416 : | # bkell_sigfigs($x, $n) | ||
| 417 : | # | ||
| 418 : | # Returns a string containing $x rounded to $n significant figures. | ||
| 419 : | # | ||
| 420 : | sub bkell_sigfigs | ||
| 421 : | { | ||
| 422 : | my ($x, $n) = @_; | ||
| 423 : | |||
| 424 : | if ($x == 0) { return "0".($n > 1 ? "." : "").("0" x ($n-1)); } | ||
| 425 : | |||
| 426 : | my $minus = ""; | ||
| 427 : | if ($x < 0) { | ||
| 428 : | $minus = "-"; | ||
| 429 : | $x = -$x; | ||
| 430 : | } | ||
| 431 : | |||
| 432 : | my $floor_log = bkell_floor(log($x)/log(10)); | ||
| 433 : | |||
| 434 : | if ($floor_log+1 >= $n) { | ||
| 435 : | my $sf = 10**($floor_log-$n+1); | ||
| 436 : | return $minus.(sprintf("%.0f", $x/$sf)*$sf); | ||
| 437 : | } else { | ||
| 438 : | my $digits = $n-$floor_log-1; | ||
| 439 : | return $minus.sprintf("%.${digits}f", $x); | ||
| 440 : | } | ||
| 441 : | } | ||
| 442 : | |||
| 443 : | ############################################################################### | ||
| 444 : | # bkell_125($x) | ||
| 445 : | # | ||
| 446 : | # Returns the value logarithmically nearest $x in the sequence | ||
| 447 : | # ..., -1000, -500, -200, -100, -50, -20, -10, -5, -2, -1, -0.5, -0.2, | ||
| 448 : | # -0.1, -0.05, -0.02, -0.01, ..., 0, ..., 0.01, 0.02, 0.05, 0.1, | ||
| 449 : | # 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, ... . | ||
| 450 : | # | ||
| 451 : | sub bkell_125 | ||
| 452 : | { | ||
| 453 : | my $x = shift; | ||
| 454 : | |||
| 455 : | if ($x == 0) { return 0; } | ||
| 456 : | |||
| 457 : | my $sign = +1; | ||
| 458 : | if ($x < 0) { $sign = -1; $x = -$x; } | ||
| 459 : | |||
| 460 : | my $log = log($x)/log(10); | ||
| 461 : | my $characteristic = bkell_floor($log); | ||
| 462 : | my $mantissa = $log - $characteristic; | ||
| 463 : | |||
| 464 : | my $log2 = log(2)/log(10); | ||
| 465 : | my $log5 = log(5)/log(10); | ||
| 466 : | my $m; | ||
| 467 : | if ($mantissa < $log2 / 2) { | ||
| 468 : | $m = 0; | ||
| 469 : | } elsif ($mantissa < ($log2 + $log5) / 2) { | ||
| 470 : | $m = $log2; | ||
| 471 : | } elsif ($mantissa < ($log5 + 1) / 2) { | ||
| 472 : | $m = $log5; | ||
| 473 : | } else { | ||
| 474 : | $m = 1; | ||
| 475 : | } | ||
| 476 : | |||
| 477 : | return $sign * (10 ** ($characteristic + $m)); | ||
| 478 : | } | ||
| 479 : | |||
| 480 : | ############################################################################### | ||
| 481 : | # bkell_list_random_selection($n, @list) | ||
| 482 : | # | ||
| 483 : | # Returns a selection of $n distinct elements of @list. This is like | ||
| 484 : | # list_random_multi_uniq in freemanMacros.pl, except that this function will | ||
| 485 : | # always return the elements in the same order as they appear in @list. | ||
| 486 : | # | ||
| 487 : | sub bkell_list_random_selection | ||
| 488 : | { | ||
| 489 : | my $n = int abs shift; # so strange $n won't cause infinite loop | ||
| 490 : | my @list = @_; | ||
| 491 : | |||
| 492 : | my $needed = $n; | ||
| 493 : | my @result = (); | ||
| 494 : | while ($needed && @list) { | ||
| 495 : | if (random(1, scalar @list) <= $needed) { | ||
| 496 : | push @result, shift @list; | ||
| 497 : | --$needed; | ||
| 498 : | } else { | ||
| 499 : | shift @list; | ||
| 500 : | } | ||
| 501 : | } | ||
| 502 : | |||
| 503 : | return @result; | ||
| 504 : | } | ||
| 505 : | |||
| 506 : | ############################################################################### | ||
| 507 : | # bkell_graph_axis($a, $b) | ||
| 508 : | # | ||
| 509 : | # Returns a list ($min, $max, $step), where $min <= $a, $max >= $b, $step is a | ||
| 510 : | # power of 10, $min and $max are multiples of $step, abs($min) <= 9*$step, and | ||
| 511 : | # abs($max) <= 9*$step. Useful for deciding bounds for the axis of a graph. For | ||
| 512 : | # example, to make a graph axis that can handle values between $a and $b, call | ||
| 513 : | # bkell_graph_axis, and then set the minimum value of the axis to $min and the | ||
| 514 : | # maximum to $max, and put tick marks every $step units. | ||
| 515 : | # | ||
| 516 : | sub bkell_graph_axis | ||
| 517 : | { | ||
| 518 : | my ($a, $b) = @_; | ||
| 519 : | |||
| 520 : | if ($a > $b) { ($a, $b) = ($b, $a); } | ||
| 521 : | |||
| 522 : | my $s1 = 0; | ||
| 523 : | my $s2 = 0; | ||
| 524 : | |||
| 525 : | if ($a != 0) { $s1 = bkell_floor(log(abs $a)/log(10)); } | ||
| 526 : | if ($b != 0) { $s2 = bkell_floor(log(abs $b)/log(10)); } | ||
| 527 : | |||
| 528 : | my $s = ($s1 > $s2 ? $s1 : $s2); | ||
| 529 : | |||
| 530 : | $step = 10**$s; | ||
| 531 : | $min = $step * bkell_floor($a/$step); | ||
| 532 : | $max = $step * bkell_ceil($b/$step); | ||
| 533 : | |||
| 534 : | return ($min, $max, $step); | ||
| 535 : | } | ||
| 536 : | |||
| 537 : | ####################################################################### EOF ### |
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |