long-decimal 0.00.06

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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