long-decimal 0.00.06

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1319 @@
1
+ #
2
+ # longdecimal.rb -- Arbitrary precision decimals with fixed decimal point
3
+ #
4
+ # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $
5
+ # CVS-Label: $Name: PRE_ALPHA_0_06 $
6
+ # Author: $Author: bk1 $ (Karl Brodowsky)
7
+ #
8
+ require "complex"
9
+ require "rational"
10
+ # require "bigdecimal"
11
+ # require "bigdecimal/math"
12
+
13
+ #
14
+ # add a functionality to find gcd with a high power of some number
15
+ # probably Integer is not the right place for this stuff, because it
16
+ # is quite special and should go to some kind of Math-like class in the
17
+ # future.
18
+ #
19
+ class Integer
20
+
21
+ MAX_FLOATABLE = Float::MAX.to_i
22
+ MIN_FLOATABLE = Float::MIN.to_i
23
+
24
+ #
25
+ # find the gcd of self with b^n0 where n0 is a sufficiently high
26
+ # exponent such that gcd(self, b^m) = gcd(self, b^n)
27
+ # for all m, n > n0
28
+ #
29
+ def gcd_with_high_power(b)
30
+ raise ZeroDivisionError, "gcd_with_high_power of zero with \"#{b.inspect}\" would be infinity" if self.zero?
31
+ raise TypeError, "gcd_with_high_power can only be calculated for integers \"#{b.inspect}\" is no integer" unless b.kind_of? Integer
32
+ raise ZeroDivisionError, "gcd_with_high_power with b < 2 is not defined. b=\"#{b.inspect}\"" if b < 2
33
+ s = self.abs
34
+ exponent = 1
35
+ b = b.abs
36
+ if (b < s && s < MAX_FLOATABLE)
37
+ exponent = (Math.log(s) / Math.log(b)).ceil
38
+ end
39
+ power = b**exponent
40
+ result = 1
41
+ begin
42
+ f = s.gcd(power)
43
+ s /= f
44
+ result *= f
45
+ end while f > 1
46
+ result
47
+ end
48
+
49
+ #
50
+ # find the exponent of the highest power of prime number p that divides
51
+ # self. Only works for prime numbers
52
+ # works even for numbers that exceed the range of Float
53
+ #
54
+ def multiplicity_of_factor(prime_number)
55
+ power = gcd_with_high_power(prime_number)
56
+ if (power.abs < MAX_FLOATABLE) then
57
+ result = (Math.log(power) / Math.log(prime_number)).round
58
+ else
59
+ e = (Math.log(MAX_FLOATABLE) / Math.log(prime_number)).floor
60
+ result = 0
61
+ partial = prime_number ** e
62
+ while (power > partial) do
63
+ power /= partial
64
+ result += e
65
+ end
66
+ result += (Math.log(power) / Math.log(prime_number)).round
67
+ # result = (BigMath.log(BigDecimal(power.to_s + ".0", power.size)) / BigMath.log(BigDecimal(prime_number.to_s + ".0", prime_number.size))).round
68
+ # raise TypeError, "numbers are too big p=#{prime_number} power=#{power}"
69
+ end
70
+ result
71
+ end
72
+ end
73
+
74
+ #
75
+ # add some functionality to Rational.
76
+ # probably Rational is not the right place for this stuff, because it
77
+ # is quite special and should go to some kind of Math-like class in the
78
+ # future.
79
+ #
80
+ class Rational
81
+
82
+ #
83
+ # find the exponent of the highest power of b that divides
84
+ # self. Count negative, if it divides the denominator
85
+ # Only works for prime numbers
86
+ # @todo: needs some improvements, in order to work well for numbers
87
+ # that exceed the range of Float
88
+ #
89
+ def multiplicity_of_factor(prime_number)
90
+ m1 = numerator.multiplicity_of_factor(prime_number)
91
+ m2 = denominator.multiplicity_of_factor(prime_number)
92
+ m1 - m2
93
+ end
94
+
95
+ end
96
+
97
+ #
98
+ # define rounding modes to be used for LongDecimal
99
+ # this serves the purpose of an "enum" in C/C++
100
+ #
101
+ module LongDecimalRoundingMode
102
+ RoundingModeClass = Struct.new(:name, :num)
103
+ class RoundingModeClass
104
+ include Comparable
105
+
106
+ #
107
+ # introduce some ordering for rounding modes
108
+ #
109
+ def <=>(o)
110
+ if o.respond_to?:num
111
+ self.num <=> o.num
112
+ else
113
+ self.num <=> o
114
+ end
115
+ end
116
+ end
117
+
118
+ # rounding modes as constants
119
+ #
120
+ ROUND_UP = RoundingModeClass.new(:ROUND_UP, 0)
121
+ ROUND_DOWN = RoundingModeClass.new(:ROUND_DOWN, 1)
122
+ ROUND_CEILING = RoundingModeClass.new(:ROUND_CEILING, 2)
123
+ ROUND_FLOOR = RoundingModeClass.new(:ROUND_FLOOR, 3)
124
+ ROUND_HALF_UP = RoundingModeClass.new(:ROUND_HALF_UP, 4)
125
+ ROUND_HALF_DOWN = RoundingModeClass.new(:ROUND_HALF_DOWN, 5)
126
+ ROUND_HALF_EVEN = RoundingModeClass.new(:ROUND_HALF_EVEN, 6)
127
+ ROUND_UNNECESSARY = RoundingModeClass.new(:ROUND_UNNECESSARY, 7)
128
+
129
+ end
130
+
131
+ #
132
+ # class for holding fixed point long decimal numbers
133
+ # these can be considered as a pair of two integer. One contains the
134
+ # digits and the other one the position of the decimal point.
135
+ #
136
+ class LongDecimal < Numeric
137
+ @RCS_ID='-$Id: longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $-'
138
+
139
+ include LongDecimalRoundingMode
140
+
141
+ # MINUS_ONE = LongDecimal(-1)
142
+ # ZERO = LongDecimal(0)
143
+ # ONE = LongDecimal(1)
144
+ # TWO = LongDecimal(2)
145
+ # TEN = LongDecimal(10)
146
+
147
+ #
148
+ # initialization
149
+ # parameters:
150
+ # 1. LongDecimal.new!(x) where x is a string or a number
151
+ # the resulting LongDecimal holds the number x, possibly rounded
152
+ # 2. LongDecimal.new!(x, s) where x is a string or a number and s is the scale
153
+ # the resulting LongDecimal holds the number x / 10**s
154
+ def LongDecimal.new!(x, s = 0)
155
+ new(x, s)
156
+ end
157
+
158
+ #
159
+ # creates a LongDecimal representing zero with the given number of
160
+ # digits after the decimal point (scale=s)
161
+ #
162
+ def LongDecimal.zero!(s = 0)
163
+ new(0, s)
164
+ end
165
+
166
+
167
+ #
168
+ # creates a LongDecimal representing one with the given number of
169
+ # digits after the decimal point (scale=s)
170
+ #
171
+ def LongDecimal.one!(s = 0)
172
+ new(1, s)
173
+ end
174
+
175
+
176
+ #
177
+ # creates a LongDecimal representing two with the given number of
178
+ # digits after the decimal point (scale=s)
179
+ #
180
+ def LongDecimal.two!(s = 0)
181
+ new(2, s)
182
+ end
183
+
184
+
185
+ #
186
+ # creates a LongDecimal representing ten with the given number of
187
+ # digits after the decimal point (scale=s)
188
+ #
189
+ def LongDecimal.ten!(s = 0)
190
+ new(10, s)
191
+ end
192
+
193
+
194
+ #
195
+ # creates a LongDecimal representing minus one with the given number of
196
+ # digits after the decimal point (scale=s)
197
+ #
198
+ def LongDecimal.minus_one!(s = 0)
199
+ new(-1, s)
200
+ end
201
+
202
+
203
+ #
204
+ # creates a LongDecimal representing a power of ten with the given
205
+ # exponent e and with the given number of digits after the decimal
206
+ # point (scale=s)
207
+ #
208
+ def LongDecimal.power_of_ten!(e, s = 0)
209
+ new(10**e, s)
210
+ end
211
+
212
+
213
+ #
214
+ # initialization
215
+ # parameters:
216
+ # LongDecimal.new(x, s) where x is a string or a number and s is the scale
217
+ # the resulting LongDecimal holds the number x / 10**s
218
+ #
219
+ def initialize(x, s)
220
+
221
+ # handle some obvious errors with x first
222
+ raise TypeError, "non numeric 1st arg \"#{x.inspect}\"" if ! (x.kind_of? Numeric) && ! (x.kind_of? String)
223
+ # we could maybe even work with complex number, if their imaginary part is zero.
224
+ # but this is not so important to deal with, so we raise an error anyway.
225
+ raise TypeError, "complex numbers not supported \"#{x.inspect}\"" if x.kind_of? Complex
226
+
227
+ # handle some obvious errors with optional second parameter, if present
228
+ raise TypeError, "non integer 2nd arg \"#{s.inspect}\"" if ! s.kind_of? Integer
229
+ raise TypeError, "negative 2nd arg \"#{s.inspect}\"" if s < 0
230
+
231
+ # scale is the second parameter or 0 if it is missing
232
+ scale = s
233
+ # int_val is the integral value that is multiplied by some 10**-n
234
+ int_val = 0
235
+
236
+ if x.kind_of? Integer then
237
+ # integers are trivial to handle
238
+ int_val = x
239
+
240
+ elsif x.kind_of? Rational then
241
+ # rationals are rounded somehow
242
+ # we need to come up with a better rule here.
243
+ # if denominator is any product of powers of 2 and 5, we do not need to round
244
+ denom = x.denominator
245
+ mul_2 = denom.multiplicity_of_factor(2)
246
+ mul_5 = denom.multiplicity_of_factor(5)
247
+ iscale = [mul_2, mul_5].max
248
+ scale += iscale
249
+ denom /= 2 ** mul_2
250
+ denom /= 5 ** mul_5
251
+ iscale2 = Math.log10(denom).ceil
252
+ scale += iscale2
253
+ # int_val = (x * 10 ** scale).to_i
254
+ int_val = (x * 10 ** (iscale2+iscale)).to_i
255
+
256
+ else
257
+ # we assume a string or a floating point number
258
+ # floating point number is converted to string, so we only deal with strings
259
+ num_str = x.to_s
260
+ len = num_str.length
261
+
262
+ # handle the obvious error that string is empty
263
+ raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
264
+
265
+ # remove spaces and underscores
266
+ num_str.gsub! /\s/, ""
267
+ num_str.gsub! /_/, ""
268
+
269
+ # handle sign
270
+ num_str.gsub! /^\+/, ""
271
+ negative = false
272
+ if num_str.gsub! /^-/, "" then
273
+ negative = true
274
+ end
275
+
276
+ # split in parts before and after decimal point
277
+ num_arr = num_str.split /\./
278
+ if num_arr.length > 2 then
279
+ raise TypeError, "1st arg contains more than one . \"#{num_str.inspect}\""
280
+ end
281
+ num_int = num_arr[0]
282
+ num_rem = num_arr[1]
283
+ num_frac = nil
284
+ num_exp = nil
285
+ unless num_rem.nil? then
286
+ num_arr = num_rem.split /[Ee]/
287
+ num_frac = num_arr[0]
288
+ num_exp = num_arr[1]
289
+ end
290
+
291
+ if num_frac.nil? then
292
+ num_frac = ""
293
+ end
294
+
295
+ if num_exp.nil? || num_exp.empty? then
296
+ num_exp = "0"
297
+ end
298
+ num_exp = num_exp.to_i
299
+ iscale = num_frac.length - num_exp
300
+ scale += iscale
301
+ int_val = (num_int + num_frac).to_i
302
+ if negative then
303
+ int_val = -int_val
304
+ end
305
+ end
306
+ @scale = scale
307
+ @int_val = int_val
308
+
309
+ end # initialize
310
+
311
+ #
312
+ # get the integer value of self, disregarding the decimal point.
313
+ # Mostly for internal use.
314
+ #
315
+ def int_val
316
+ @int_val
317
+ end
318
+
319
+ #
320
+ # get the scale, i.e. the position of the decimal point.
321
+ # Mostly for internal use.
322
+ #
323
+ def scale
324
+ @scale
325
+ end
326
+
327
+ #
328
+ # alter scale
329
+ # only for internal use.
330
+ # changes self
331
+ # use round_to_scale instead
332
+ #
333
+ def scale=(s)
334
+ raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
335
+ raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
336
+
337
+ # do not work too hard, if scale does not really change.
338
+ unless @scale == s then
339
+ # multiply int_val by a power of 10 in order to compensate for
340
+ # the change of scale and to keep number in the same order of magnitude.
341
+ d = s - @scale
342
+ f = 10 ** (d.abs)
343
+ if (d >= 0) then
344
+ @int_val = (@int_val * f).to_i
345
+ else
346
+ # here we actually do rounding
347
+ @int_val = (@int_val / f).to_i
348
+ end
349
+ @scale = s
350
+ end
351
+ end
352
+
353
+ protected :scale=
354
+
355
+ #
356
+ # create copy of self with different scale
357
+ # param1: new_scale new scale for result
358
+ # param2: mode rounding mode to be applied when information is lost
359
+ #
360
+ def round_to_scale(new_scale, mode = ROUND_UNNECESSARY)
361
+
362
+ raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
363
+ raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
364
+ raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
365
+ if @scale == new_scale then
366
+ self
367
+ else
368
+ diff = new_scale - scale
369
+ factor = 10 ** (diff.abs)
370
+ if (diff > 0) then
371
+ # we become more precise, no rounding issues
372
+ new_int_val = int_val * factor
373
+ else
374
+ quot, rem = int_val.divmod(factor)
375
+ if (rem == 0) then
376
+ new_int_val = quot
377
+ elsif (mode == ROUND_UNNECESSARY) then
378
+ raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
379
+ else
380
+ return LongDecimalQuot(self, LongDecimal(1)).round_to_scale(new_scale, mode)
381
+ end
382
+ end
383
+ LongDecimal(new_int_val, new_scale)
384
+ end
385
+ end
386
+
387
+ #
388
+ # convert self into String, which is the decimal representation.
389
+ # Use trailing zeros, if int_val has them.
390
+ #
391
+ def to_s(shown_scale = @scale, mode = ROUND_UNNECESSARY, base = 10)
392
+ if (base == 10) then
393
+ if (shown_scale == @scale)
394
+ to_s_10
395
+ else
396
+ s = self.round_to_scale(shown_scale, mode)
397
+ s.to_s_10
398
+ end
399
+ else
400
+ # base is not 10
401
+ unless (base.kind_of? Integer) && 2 <= base && base <= 36 then
402
+ raise TypeError, "base must be integer between 2 and 36"
403
+ end
404
+ quot = (self.move_point_right(scale) * base ** shown_scale) / 10 ** scale
405
+ p(quot)
406
+ rounded = quot.round_to_scale(0, mode)
407
+ p(rounded)
408
+ rounded.to_s_internal(base, shown_scale)
409
+ end
410
+ end
411
+
412
+ def to_s_10
413
+ to_s_internal(10, scale)
414
+ end
415
+
416
+ def to_s_internal(b, sc)
417
+ sg = sgn
418
+ i = int_val.abs
419
+ str = i.to_s(b)
420
+ if sc > 0 then
421
+ missing = sc - str.length + 1
422
+ if missing > 0 then
423
+ str = ("0" * missing) + str
424
+ end
425
+ str[-sc, 0] = "."
426
+ end
427
+ str = "-" + str if sg < 0
428
+ str
429
+ end
430
+
431
+ protected :to_s_10
432
+ protected :to_s_internal
433
+
434
+ #
435
+ # convert self into Rational
436
+ # this works quite straitforward. use int_val as numerator and a
437
+ # power of 10 as denominator
438
+ #
439
+ def to_r
440
+ Rational(@int_val, 10**@scale)
441
+ end
442
+
443
+ #
444
+ # convert self into Float
445
+ # this works straitforward by dividing int_val by power of 10 in
446
+ # float-arithmetic.
447
+ #
448
+ def to_f
449
+ int_val.to_f / 10**scale
450
+ end
451
+
452
+ #
453
+ # convert self into Integer
454
+ # This may loose information. In most cases it is preferred to
455
+ # control this by calling round_to_scale first and then applying
456
+ # to_i when the number represented by self is actually an integer.
457
+ #
458
+ def to_i
459
+ to_r.to_i
460
+ end
461
+
462
+ #
463
+ # convert self into LongDecimal (returns self)
464
+ #
465
+ def to_ld
466
+ self
467
+ end
468
+
469
+ #
470
+ # LongDecimals can be seen as a fraction with a power of 10 as
471
+ # denominator for compatibility with other numeric classes this
472
+ # method is included, returning 10**scale
473
+ #
474
+ def denominator
475
+ 10**scale
476
+ end
477
+
478
+ #
479
+ # LongDecimals can be seen as a fraction with its int_val as its
480
+ # numerator
481
+ #
482
+ alias numerator int_val
483
+
484
+ #
485
+ # before adding or subtracting two LongDecimal numbers
486
+ # it is mandatory to set them to the same scale. The maximum of the
487
+ # two summands is used, in order to avoid loosing any information.
488
+ # this method is mostly for internal use
489
+ #
490
+ def equalize_scale(other)
491
+ o, s = coerce(other)
492
+ if (s.kind_of? LongDecimal) then
493
+ # make sure Floats do not mess up our number of significant digits when adding
494
+ if (other.kind_of? Float) then
495
+ o = o.round_to_scale(s.scale, ROUND_HALF_UP)
496
+ else
497
+ new_scale = [s.scale, o.scale].max
498
+ s = s.round_to_scale(new_scale)
499
+ o = o.round_to_scale(new_scale)
500
+ end
501
+ end
502
+ return s, o
503
+ end
504
+
505
+ #
506
+ # before dividing two LongDecimal numbers, it is mandatory to set
507
+ # make them both to integers, so the result is simply expressable as
508
+ # a rational
509
+ # this method is mostly for internal use
510
+ #
511
+ def anti_equalize_scale(other)
512
+ o, s = coerce(other)
513
+ if (s.kind_of? LongDecimal) then
514
+ exponent = [s.scale, o.scale].max
515
+ factor = 10**exponent
516
+ s *= factor
517
+ o *= factor
518
+ s = s.round_to_scale(0)
519
+ o = o.round_to_scale(0)
520
+ end
521
+ return s, o
522
+ end
523
+
524
+ #
525
+ # successor for ranges
526
+ #
527
+ def succ
528
+ LongDecimal(int_val + 1, scale)
529
+ end
530
+
531
+ alias next succ
532
+
533
+ #
534
+ # predecessor
535
+ #
536
+ def pred
537
+ LongDecimal(int_val - 1, scale)
538
+ end
539
+
540
+ #
541
+ # self + 1
542
+ #
543
+ def inc
544
+ self + 1
545
+ end
546
+
547
+ #
548
+ # self - 1
549
+ #
550
+ def dec
551
+ self - 1
552
+ end
553
+
554
+ #
555
+ # return the unit by which self is incremented by succ
556
+ #
557
+ def unit
558
+ LongDecimal(1, scale)
559
+ end
560
+
561
+ #
562
+ # apply unary +
563
+ # (returns self)
564
+ #
565
+ def +@
566
+ self
567
+ end
568
+
569
+ #
570
+ # apply unary -
571
+ # (returns negated self)
572
+ #
573
+ def -@
574
+ if self.zero? then
575
+ self
576
+ else
577
+ LongDecimal(-int_val, scale)
578
+ end
579
+ end
580
+
581
+ #
582
+ # add two numbers
583
+ # if both can immediately be expressed as LongDecimal, the result is
584
+ # a LongDecimal as well. The number of digits after the decimal
585
+ # point is the max of the scales of the summands
586
+ # if LongDecimal does not cover the two summands, call addition of
587
+ # Complex, Float or LongRationalQuot
588
+ #
589
+ def +(other)
590
+ s, o = equalize_scale(other)
591
+ if s.kind_of? LongDecimal then
592
+ LongDecimal(s.int_val + o.int_val, s.scale)
593
+ else
594
+ s + o
595
+ end
596
+ end
597
+
598
+ #
599
+ # subtract two numbers
600
+ # if both can immediately be expressed as LongDecimal, the result is
601
+ # a LongDecimal as well. The number of digits after the decimal
602
+ # point is the max of the scales of self and other.
603
+ # if LongDecimal does not cover self and other, the subtraction of
604
+ # Complex, Float or LongRationalQuot is used
605
+ #
606
+ def -(other)
607
+ s, o = equalize_scale(other)
608
+ if s.kind_of? LongDecimal then
609
+ LongDecimal(s.int_val - o.int_val, s.scale)
610
+ else
611
+ s - o
612
+ end
613
+ end
614
+
615
+ #
616
+ # multiply two numbers
617
+ # if both can immediately be expressed as LongDecimal, the result is
618
+ # a LongDecimal as well. The number of digits after the decimal
619
+ # point is the sum of the scales of both factors.
620
+ # if LongDecimal does not cover self and other, the multiplication of
621
+ # Complex, Float or LongRationalQuot is used
622
+ #
623
+ def *(other)
624
+ o, s = coerce(other)
625
+ if s.kind_of? LongDecimal then
626
+ LongDecimal(s.int_val * o.int_val, s.scale + o.scale)
627
+ else
628
+ s * o
629
+ end
630
+ end
631
+
632
+ def divide(other, rounding_mode)
633
+ divide_s(other, nil, rounding_mode)
634
+ end
635
+
636
+ def divide_s(other, new_scale, rounding_mode)
637
+ q = self / other
638
+ if (q.kind_of? Float) then
639
+ q = LongDecimal(q)
640
+ end
641
+ if (q.kind_of? LongDecimal) || (q.kind_of? LongDecimalQuot) then
642
+ if (new_scale.nil?) then
643
+ new_scale = q.scale
644
+ end
645
+ q.round_to_scale(new_scale, rounding_mode)
646
+ else
647
+ q
648
+ end
649
+ end
650
+
651
+ def rdiv(other)
652
+ q = self / other
653
+ if (q.kind_of? LongDecimalQuot) || (q.kind_of? LongDecimalQuot) then
654
+ r.to_r
655
+ else
656
+ q
657
+ end
658
+ end
659
+
660
+ def /(other)
661
+ o, s = coerce(other)
662
+ if (s.kind_of? LongDecimal) then
663
+ LongDecimalQuot(s, o)
664
+ else
665
+ s / o
666
+ end
667
+ end
668
+
669
+ def **(other)
670
+ if ((other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)) && other.is_int? then
671
+ other = other.to_i
672
+ end
673
+ if other.kind_of? Integer then
674
+ if other >= 0 then
675
+ LongDecimal(int_val ** other, scale * other)
676
+ else
677
+ abs_other = -other
678
+ new_scale = abs_other * scale
679
+ LongDecimalQuot(Rational(10 ** new_scale, int_val ** abs_other), new_scale)
680
+ end
681
+ else
682
+ if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
683
+ other = other.to_f
684
+ end
685
+ self.to_f ** other
686
+ end
687
+ end
688
+
689
+ def divmod(other)
690
+ if (other.kind_of? Complex) then
691
+ raise TypeError, "divmod not supported for Complex"
692
+ end
693
+ q = (self / other).to_i
694
+ return q, self - other * q
695
+ end
696
+
697
+ #
698
+ # find the exponent of the highest power of prime number p that divides
699
+ # self. Only works for prime numbers
700
+ # works even for numbers that exceed the range of Float
701
+ #
702
+ def multiplicity_of_factor(prime_number)
703
+ m1 = numerator.multiplicity_of_factor(prime_number)
704
+ if (prime_number == 2 || prime_number == 5) then
705
+ m1 - scale
706
+ else
707
+ m1
708
+ end
709
+ end
710
+
711
+ def %(other)
712
+ q, r = divmod other
713
+ r
714
+ end
715
+
716
+ #
717
+ # performs bitwise AND between self and Numeric
718
+ #
719
+ def &(other)
720
+ s, o = equalize_scale(other)
721
+ if s.kind_of? LongDecimal then
722
+ LongDecimal(s.int_val & o.int_val, s.scale)
723
+ else
724
+ s & o
725
+ end
726
+ end
727
+
728
+ #
729
+ # performs bitwise OR between self and Numeric
730
+ #
731
+ def |(other)
732
+ s, o = equalize_scale(other)
733
+ if s.kind_of? LongDecimal then
734
+ LongDecimal(s.int_val | o.int_val, s.scale)
735
+ else
736
+ s | o
737
+ end
738
+ end
739
+
740
+ #
741
+ # performs bitwise XOR between self and Numeric
742
+ #
743
+ def ^(other)
744
+ s, o = equalize_scale(other)
745
+ if s.kind_of? LongDecimal then
746
+ LongDecimal(s.int_val ^ o.int_val, s.scale)
747
+ else
748
+ s ^ o
749
+ end
750
+ end
751
+
752
+ #
753
+ # bitwise inversion
754
+ #
755
+ def ~
756
+ LongDecimal(~int_val, scale)
757
+ end
758
+
759
+ #
760
+ # performs bitwise left shift of self by Numeric
761
+ #
762
+ def <<(other)
763
+ unless (other.kind_of? Integer) && other >= 0 then
764
+ raise TypeError, "cannot shift by something other than integer >= 0"
765
+ end
766
+ LongDecimal(s.int_val << other, s.scale)
767
+ end
768
+
769
+ #
770
+ # performs bitwise right shift of self by Numeric
771
+ #
772
+ def >>(other)
773
+ unless (other.kind_of? Integer) && other >= 0 then
774
+ raise TypeError, "cannot shift by something other than integer >= 0"
775
+ end
776
+ LongDecimal(s.int_val >> other, s.scale)
777
+ end
778
+
779
+ #
780
+ # gets binary digit
781
+ #
782
+ def [](other)
783
+ int_val[other]
784
+ end
785
+
786
+ #
787
+ # gets size of int_val
788
+ #
789
+ def size
790
+ int_val.size
791
+ end
792
+
793
+ #
794
+ # divide by 10**n
795
+ #
796
+ def move_point_left(n)
797
+ raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
798
+ if (n >= 0) then
799
+ move_point_left_int(n)
800
+ else
801
+ move_point_right_int(-n)
802
+ end
803
+ end
804
+
805
+ #
806
+ # multiply by 10**n
807
+ #
808
+ def move_point_right(n)
809
+ raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
810
+ if (n < 0) then
811
+ move_point_left_int(-n)
812
+ else
813
+ move_point_right_int(n)
814
+ end
815
+ end
816
+
817
+ #
818
+ # divide by 10**n
819
+ #
820
+ def move_point_left_int(n)
821
+ raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
822
+ LongDecimal(int_val, scale + n)
823
+ end
824
+
825
+ #
826
+ # multiply by 10**n
827
+ #
828
+ def move_point_right_int(n)
829
+ raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
830
+ if (n > scale) then
831
+ LongDecimal(int_val * 10**(n-scale), 0)
832
+ else
833
+ LongDecimal(int_val, scale-n)
834
+ end
835
+ end
836
+
837
+ protected :move_point_left_int, :move_point_right_int
838
+
839
+ #
840
+ # calculate the sqare of self
841
+ #
842
+ def square
843
+ self * self
844
+ end
845
+
846
+ #
847
+ # calculate the multiplicative inverse
848
+ #
849
+ def reciprocal
850
+ 1 / self
851
+ end
852
+
853
+ #
854
+ # Absolute value
855
+ #
856
+ def abs
857
+ LongDecimal(int_val.abs, scale)
858
+ end
859
+
860
+ #
861
+ # square of absolute value
862
+ #
863
+ def abs2
864
+ self.abs.square
865
+ end
866
+
867
+ #
868
+ # Compares the absolute values of the two numbers.
869
+ #
870
+ def <=> (other)
871
+ diff = (self - other)
872
+ if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
873
+ diff.sgn
874
+ else
875
+ diff <=> 0
876
+ end
877
+ end
878
+
879
+ def scale_ufo(other)
880
+ raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
881
+ self.scale <=> other.scale
882
+ end
883
+
884
+ def scale_equal(other)
885
+ scale_ufo(other).zero?
886
+ end
887
+
888
+ def coerce(other)
889
+ if other.kind_of? LongDecimal then
890
+ return other, self
891
+ elsif other.kind_of? LongDecimalQuot then
892
+ return other, LongDecimalQuot(self.to_r, scale)
893
+ elsif other.kind_of? Rational then
894
+ s = scale
895
+ return LongDecimalQuot(other, s), LongDecimalQuot(self.to_r, s)
896
+ elsif (other.kind_of? Integer) || (other.kind_of? Float) then
897
+ other = LongDecimal(other)
898
+ if (other.scale > scale) then
899
+ other = other.round_to_scale(scale, ROUND_HALF_UP)
900
+ end
901
+ return other, self
902
+ elsif other.kind_of? Numeric then
903
+ s, o = other.coerce(self.to_f)
904
+ return o, s
905
+ else
906
+ raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimal"
907
+ end
908
+ end
909
+
910
+ # is self expressable as an integer without loss of digits?
911
+ def is_int?
912
+ scale == 0 || int_val % 10**scale == 0
913
+ end
914
+
915
+ def sgn
916
+ int_val <=> 0
917
+ end
918
+ alias signum sgn
919
+ alias sign sgn
920
+
921
+ def ==(other)
922
+ (other.kind_of? LongDecimal) && (self <=> other) == 0 && self.scale == other.scale
923
+ end
924
+
925
+ def zero?
926
+ int_val.zero?
927
+ end
928
+
929
+ #
930
+ # Returns a hash code for the complex number.
931
+ #
932
+ def hash
933
+ int_val.hash ^ scale.hash
934
+ end
935
+
936
+ #
937
+ # Returns "<tt>LongDecimal(<i>int_val</i>, <i>scale</i>)</tt>".
938
+ #
939
+ def inspect
940
+ sprintf("LongDecimal(%s, %s)", int_val.inspect, scale.inspect)
941
+ end
942
+
943
+
944
+ end
945
+
946
+ #
947
+ # This class is used for storing intermediate results after having
948
+ # performed a division. The division cannot be completed without
949
+ # providing additional information on how to round the result.
950
+ #
951
+ class LongDecimalQuot < Numeric
952
+
953
+ @RCS_ID='-$Id: longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $-'
954
+
955
+ include LongDecimalRoundingMode
956
+
957
+ def LongDecimalQuot.new!(first, second)
958
+ new(first, second)
959
+ end
960
+
961
+ #
962
+ # create a new LongDecimalQuot from a rational and a scale or a
963
+ # pair of LongDecimals
964
+ def initialize(first, second)
965
+ if (first.kind_of? Rational) && (second.kind_of? Integer) then
966
+ @rat = Rational(first.numerator, first.denominator)
967
+ @scale = second
968
+ elsif (first.kind_of? LongDecimal) && (second.kind_of? LongDecimal) then
969
+ orig_scale = first.scale
970
+ first, second = first.anti_equalize_scale(second)
971
+ @rat = Rational(first.to_i, second.to_i)
972
+ @scale = orig_scale
973
+ else
974
+ raise TypeError, "parameters must be (LongDecimal, LongDecimal) or (Rational, Integer): first=#{first.inspect} second=#{second.inspect}";
975
+ end
976
+ end
977
+
978
+
979
+ def scale
980
+ @scale
981
+ end
982
+
983
+ def rat
984
+ @rat
985
+ end
986
+
987
+ def numerator
988
+ rat.numerator
989
+ end
990
+
991
+ def denominator
992
+ rat.denominator
993
+ end
994
+
995
+ # alter scale
996
+ def scale=(s)
997
+ raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
998
+ raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
999
+ @scale = s
1000
+ end
1001
+
1002
+ private :scale=
1003
+
1004
+ def to_s
1005
+ str = @rat.to_s
1006
+ str + "[" + scale.to_s + "]"
1007
+ end
1008
+
1009
+ def to_r
1010
+ Rational(numerator, denominator)
1011
+ end
1012
+
1013
+ # convert into Float
1014
+ def to_f
1015
+ to_r.to_f
1016
+ end
1017
+
1018
+ # convert into Integer
1019
+ def to_i
1020
+ to_r.to_i
1021
+ end
1022
+
1023
+ def to_ld
1024
+ LongDecimal(self, scale)
1025
+ end
1026
+
1027
+ def +@
1028
+ self
1029
+ end
1030
+
1031
+ def -@
1032
+ if self.zero? then
1033
+ self
1034
+ else
1035
+ LongDecimalQuot(-rat, scale)
1036
+ end
1037
+ end
1038
+
1039
+ def +(other)
1040
+ o, s = coerce(other)
1041
+ if (s.kind_of? LongDecimalQuot) then
1042
+ LongDecimalQuot(s.rat + o.rat, [s.scale, o.scale].max)
1043
+ else
1044
+ s + o
1045
+ end
1046
+ end
1047
+
1048
+ def -(other)
1049
+ o, s = coerce(other)
1050
+ if (s.kind_of? LongDecimalQuot) then
1051
+ LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
1052
+ else
1053
+ s - o
1054
+ end
1055
+ end
1056
+
1057
+ def *(other)
1058
+ o, s = coerce(other)
1059
+ if (s.kind_of? LongDecimalQuot) then
1060
+ LongDecimalQuot(s.rat * o.rat, s.scale + o.scale)
1061
+ else
1062
+ s * o
1063
+ end
1064
+ end
1065
+
1066
+ def /(other)
1067
+ o, s = coerce(other)
1068
+ if (s.kind_of? LongDecimalQuot) then
1069
+ LongDecimalQuot(s.rat / o.rat, scale)
1070
+ else
1071
+ s / o
1072
+ end
1073
+ end
1074
+
1075
+ def **(other)
1076
+ if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1077
+ if other.is_int? then
1078
+ other = other.to_i
1079
+ else
1080
+ other = other.to_r
1081
+ end
1082
+ end
1083
+ rat_result = rat ** other
1084
+ if (rat_result.kind_of? Rational) then
1085
+ if (other.kind_of? Integer) && other >= 0 then
1086
+ new_scale = scale * other
1087
+ else
1088
+ new_scale = scale
1089
+ end
1090
+ LongDecimalQuot(rat_result, new_scale)
1091
+ else
1092
+ rat_result
1093
+ end
1094
+ end
1095
+
1096
+ def divmod(other)
1097
+ if (other.kind_of? Complex) then
1098
+ raise TypeError, "divmod not supported for Complex"
1099
+ end
1100
+ q = (self / other).to_i
1101
+ return q, self - other * q
1102
+ end
1103
+
1104
+ def %(other)
1105
+ q, r = divmod other
1106
+ r
1107
+ end
1108
+
1109
+ # def %(other)
1110
+ # o, s = coerce(other)
1111
+ # if (s.kind_of? LongDecimalQuot) then
1112
+ # LongDecimalQuot(s.rat % o.rat, scale)
1113
+ # else
1114
+ # s % o
1115
+ # end
1116
+ # end
1117
+
1118
+ #
1119
+ # find the exponent of the highest power of prime number p that divides
1120
+ # self. Only works for prime numbers
1121
+ # works even for numbers that exceed the range of Float
1122
+ #
1123
+ def multiplicity_of_factor(prime_number)
1124
+ rat.multiplicity_of_factor(prime_number)
1125
+ end
1126
+
1127
+ def square
1128
+ self * self
1129
+ end
1130
+
1131
+ #
1132
+ # calculate the multiplicative inverse
1133
+ #
1134
+ def reciprocal
1135
+ 1 / self
1136
+ end
1137
+
1138
+ #
1139
+ # Absolute value
1140
+ #
1141
+ def abs
1142
+ LongDecimalQuot(rat.abs, scale)
1143
+ end
1144
+
1145
+ def abs2
1146
+ self.abs.square
1147
+ end
1148
+
1149
+ #
1150
+ # convert LongDecimalQuot to LongDecimal with the given precision
1151
+ # and the given rounding mode
1152
+ #
1153
+ def round_to_scale(new_scale = @scale, mode = ROUND_UNNECESSARY)
1154
+
1155
+ raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
1156
+ raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
1157
+ raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
1158
+
1159
+ factor = 10**new_scale
1160
+ sign_quot = numerator <=> 0
1161
+ if sign_quot == 0 then
1162
+ return LongDecimal(0, new_scale)
1163
+ end
1164
+ prod = numerator * factor
1165
+ divisor = denominator
1166
+ quot, rem = prod.divmod(divisor)
1167
+ sign_rem = rem <=> 0
1168
+ # puts("self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem} sign_rem=#{sign_rem.to_s} sign_quot=#{sign_quot.to_s}")
1169
+ if (sign_rem == 0)
1170
+ return LongDecimal(quot, new_scale)
1171
+ end
1172
+ raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
1173
+ if (sign_quot < 0) then
1174
+ rem -= divisor
1175
+ quot += 1
1176
+ sign_rem = rem <=> 0
1177
+ raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem >= 0
1178
+ end
1179
+ # puts("self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem} sign_rem=#{sign_rem.to_s} sign_quot=#{sign_quot.to_s}")
1180
+
1181
+ if mode == ROUND_UNNECESSARY then
1182
+ raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
1183
+ end
1184
+
1185
+ if (mode == ROUND_CEILING)
1186
+ mode = (sign_quot > 0) ? ROUND_UP : ROUND_DOWN
1187
+ elsif (mode == ROUND_FLOOR)
1188
+ mode = (sign_quot < 0) ? ROUND_UP : ROUND_DOWN
1189
+ else
1190
+ abs_rem = rem.abs
1191
+ half = (abs_rem << 1) <=> denominator
1192
+ if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
1193
+ if (half < 0) then
1194
+ mode = ROUND_DOWN
1195
+ elsif half > 0 then
1196
+ mode = ROUND_UP
1197
+ else
1198
+ # half == 0
1199
+ if (mode == ROUND_HALF_UP) then
1200
+ mode = ROUND_UP
1201
+ elsif (mode == ROUND_HALF_DOWN) then
1202
+ mode = ROUND_DOWN
1203
+ else
1204
+ # mode == ROUND_HALF_EVEN
1205
+ mode = (quot[0] == 1 ? ROUND_UP : ROUND_DOWN)
1206
+ end
1207
+ end
1208
+ end
1209
+ end
1210
+
1211
+ if mode == ROUND_UP
1212
+ quot += sign_quot
1213
+ end
1214
+ new_int_val = quot
1215
+ LongDecimal(new_int_val, new_scale)
1216
+ end
1217
+
1218
+ def coerce(other)
1219
+ if other.kind_of? LongDecimal then
1220
+ return LongDecimalQuot(other.to_r, other.scale), self
1221
+ elsif other.kind_of? LongDecimalQuot then
1222
+ return other, self
1223
+ elsif other.kind_of? Rational then
1224
+ s = scale
1225
+ return LongDecimalQuot(other, s), self
1226
+ elsif (other.kind_of? Integer) then
1227
+ return LongDecimalQuot(other.to_r, scale), self
1228
+ elsif other.kind_of? Float then
1229
+ return LongDecimalQuot(other.to_ld.to_r, scale), self
1230
+ elsif other.kind_of? Numeric then
1231
+ s, o = other.coerce(self.to_f)
1232
+ return o, s
1233
+ else
1234
+ raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimalQuot"
1235
+ end
1236
+ end
1237
+
1238
+ def ==(other)
1239
+ (other.kind_of? LongDecimalQuot) && (self <=> other) == 0 && self.scale == other.scale
1240
+ end
1241
+
1242
+ #
1243
+ # Compares the two numbers.
1244
+ #
1245
+ def <=> (other)
1246
+ diff = (self - other)
1247
+ if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
1248
+ diff.sgn
1249
+ else
1250
+ diff <=> 0
1251
+ end
1252
+ end
1253
+
1254
+ def scale_ufo(other)
1255
+ raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
1256
+ self.scale <=> other.scale
1257
+ end
1258
+
1259
+ def scale_equal(other)
1260
+ scale_ufo(other).zero?
1261
+ end
1262
+
1263
+ # is self expressable as an integer without loss of digits?
1264
+ def is_int?
1265
+ denominator == 1
1266
+ end
1267
+
1268
+ def sgn
1269
+ numerator <=> 0
1270
+ end
1271
+ alias signum sgn
1272
+ alias sign sgn
1273
+
1274
+ #
1275
+ # Returns a hash code for the complex number.
1276
+ #
1277
+ def hash
1278
+ rat.hash ^ scale.hash
1279
+ end
1280
+
1281
+
1282
+ def scale
1283
+ @scale
1284
+ end
1285
+
1286
+
1287
+ #
1288
+ # Returns "<tt>LongDecimalQuot(<i>int_val</i>, <i>scale</i>, <i>num</i>, <i>denom</i>)</tt>".
1289
+ #
1290
+ def inspect
1291
+ sprintf("LongDecimalQuot(Rational(%s, %s), %s)", numerator.inspect, denominator.inspect, scale.inspect)
1292
+ end
1293
+
1294
+ end
1295
+
1296
+ #
1297
+ # Creates a LongDecimal number. +a+ and +b+ should be Numeric.
1298
+ #
1299
+ def LongDecimal(a, b = 0)
1300
+ if b == 0 && (a.kind_of? LongDecimal) then
1301
+ a
1302
+ else
1303
+ LongDecimal.new!(a, b)
1304
+ end
1305
+ end
1306
+
1307
+ def LongDecimalQuot(first, second)
1308
+ LongDecimalQuot.new!(first, second)
1309
+ end
1310
+
1311
+ class Numeric
1312
+
1313
+ def to_ld
1314
+ LongDecimal(self)
1315
+ end
1316
+
1317
+ end
1318
+
1319
+ # end of file longdecimal.rb