Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.1 KiB

  1. package bigrat;
  2. require "bigint.pl";
  3. # Arbitrary size rational math package
  4. #
  5. # by Mark Biggar
  6. #
  7. # Input values to these routines consist of strings of the form
  8. # m|^\s*[+-]?[\d\s]+(/[\d\s]+)?$|.
  9. # Examples:
  10. # "+0/1" canonical zero value
  11. # "3" canonical value "+3/1"
  12. # " -123/123 123" canonical value "-1/1001"
  13. # "123 456/7890" canonical value "+20576/1315"
  14. # Output values always include a sign and no leading zeros or
  15. # white space.
  16. # This package makes use of the bigint package.
  17. # The string 'NaN' is used to represent the result when input arguments
  18. # that are not numbers, as well as the result of dividing by zero and
  19. # the sqrt of a negative number.
  20. # Extreamly naive algorthims are used.
  21. #
  22. # Routines provided are:
  23. #
  24. # rneg(RAT) return RAT negation
  25. # rabs(RAT) return RAT absolute value
  26. # rcmp(RAT,RAT) return CODE compare numbers (undef,<0,=0,>0)
  27. # radd(RAT,RAT) return RAT addition
  28. # rsub(RAT,RAT) return RAT subtraction
  29. # rmul(RAT,RAT) return RAT multiplication
  30. # rdiv(RAT,RAT) return RAT division
  31. # rmod(RAT) return (RAT,RAT) integer and fractional parts
  32. # rnorm(RAT) return RAT normalization
  33. # rsqrt(RAT, cycles) return RAT square root
  34. # Convert a number to the canonical string form m|^[+-]\d+/\d+|.
  35. sub main'rnorm { #(string) return rat_num
  36. local($_) = @_;
  37. s/\s+//g;
  38. if (m#^([+-]?\d+)(/(\d*[1-9]0*))?$#) {
  39. &norm($1, $3 ? $3 : '+1');
  40. } else {
  41. 'NaN';
  42. }
  43. }
  44. # Normalize by reducing to lowest terms
  45. sub norm { #(bint, bint) return rat_num
  46. local($num,$dom) = @_;
  47. if ($num eq 'NaN') {
  48. 'NaN';
  49. } elsif ($dom eq 'NaN') {
  50. 'NaN';
  51. } elsif ($dom =~ /^[+-]?0+$/) {
  52. 'NaN';
  53. } else {
  54. local($gcd) = &'bgcd($num,$dom);
  55. $gcd =~ s/^-/+/;
  56. if ($gcd ne '+1') {
  57. $num = &'bdiv($num,$gcd);
  58. $dom = &'bdiv($dom,$gcd);
  59. } else {
  60. $num = &'bnorm($num);
  61. $dom = &'bnorm($dom);
  62. }
  63. substr($dom,$[,1) = '';
  64. "$num/$dom";
  65. }
  66. }
  67. # negation
  68. sub main'rneg { #(rat_num) return rat_num
  69. local($_) = &'rnorm(@_);
  70. tr/-+/+-/ if ($_ ne '+0/1');
  71. $_;
  72. }
  73. # absolute value
  74. sub main'rabs { #(rat_num) return $rat_num
  75. local($_) = &'rnorm(@_);
  76. substr($_,$[,1) = '+' unless $_ eq 'NaN';
  77. $_;
  78. }
  79. # multipication
  80. sub main'rmul { #(rat_num, rat_num) return rat_num
  81. local($xn,$xd) = split('/',&'rnorm($_[$[]));
  82. local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
  83. &norm(&'bmul($xn,$yn),&'bmul($xd,$yd));
  84. }
  85. # division
  86. sub main'rdiv { #(rat_num, rat_num) return rat_num
  87. local($xn,$xd) = split('/',&'rnorm($_[$[]));
  88. local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
  89. &norm(&'bmul($xn,$yd),&'bmul($xd,$yn));
  90. }
  91. # addition
  92. sub main'radd { #(rat_num, rat_num) return rat_num
  93. local($xn,$xd) = split('/',&'rnorm($_[$[]));
  94. local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
  95. &norm(&'badd(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
  96. }
  97. # subtraction
  98. sub main'rsub { #(rat_num, rat_num) return rat_num
  99. local($xn,$xd) = split('/',&'rnorm($_[$[]));
  100. local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
  101. &norm(&'bsub(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
  102. }
  103. # comparison
  104. sub main'rcmp { #(rat_num, rat_num) return cond_code
  105. local($xn,$xd) = split('/',&'rnorm($_[$[]));
  106. local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
  107. &bigint'cmp(&'bmul($xn,$yd),&'bmul($yn,$xd));
  108. }
  109. # int and frac parts
  110. sub main'rmod { #(rat_num) return (rat_num,rat_num)
  111. local($xn,$xd) = split('/',&'rnorm(@_));
  112. local($i,$f) = &'bdiv($xn,$xd);
  113. if (wantarray) {
  114. ("$i/1", "$f/$xd");
  115. } else {
  116. "$i/1";
  117. }
  118. }
  119. # square root by Newtons method.
  120. # cycles specifies the number of iterations default: 5
  121. sub main'rsqrt { #(fnum_str[, cycles]) return fnum_str
  122. local($x, $scale) = (&'rnorm($_[$[]), $_[$[+1]);
  123. if ($x eq 'NaN') {
  124. 'NaN';
  125. } elsif ($x =~ /^-/) {
  126. 'NaN';
  127. } else {
  128. local($gscale, $guess) = (0, '+1/1');
  129. $scale = 5 if (!$scale);
  130. while ($gscale++ < $scale) {
  131. $guess = &'rmul(&'radd($guess,&'rdiv($x,$guess)),"+1/2");
  132. }
  133. "$guess"; # quotes necessary due to perl bug
  134. }
  135. }
  136. 1;