File Coverage

blib/lib/Math/GoldenBigMath.pm
Criterion Covered Total %
statement 307 368 83.4
branch 108 146 73.9
condition 6 15 40.0
subroutine 38 39 97.4
pod 0 24 0.0
total 459 592 77.5


line stmt bran cond sub pod time code
1             # perl 5
2             #
3             # GoldenBigMath.pm
4             #
5             # calc + - * / % with unbounded integers/decimals
6             #
7             # + - * / % == != > < <=> implemented
8             #
9             # handling of exponents ([eE][+-]?123456789) and decimal point "[,.]" still missing
10             #
11             # Ralf Peine, Mon Aug 18 17:01:06 2014
12            
13             $VERSION = "0.804";
14            
15 1     1   38336 use strict;
  1         3  
  1         38  
16 1     1   6 use warnings;
  1         3  
  1         47  
17            
18             $|=1;
19            
20             package Math::GoldenBigMath; # shorthand: GBM
21            
22 1     1   6 use Carp;
  1         6  
  1         118  
23            
24 1     1   6 use vars qw($version $_MAX_DIV_DIGITS $_COEFF_MAX_DIGITS);
  1         8  
  1         269  
25            
26             # stop calculation of division if digit count >= $_maxDivDigits
27             $_MAX_DIV_DIGITS = 100000;
28             # $_MAX_DIV_DIGITS = 100;
29             $_COEFF_MAX_DIGITS = 1000000;
30            
31             # --- overloads ------------------- sub ----------------------------------------
32            
33             # define usual mathematic operators
34             use overload
35 1         16 '+' => \&Addition, # sub
36             '-' => \&Subtraction, # sub
37             '*' => \&Multiplication, # sub
38             '/' => \&Division, # sub
39             '%' => \&DivisionModulus, # sub
40             '==' => \&CompareEqual, # sub
41             '!=' => \&CompareNotEqual, # sub
42             '>' => \&Greater, # sub
43             '<' => \&Smaller, # sub
44             '>=' => \&GreaterEqual, # sub
45             '<=' => \&SmallerEqual, # sub
46             '<=>' => \&Compare, # sub
47 1     1   1727 '""' => \&GetValue; # sub
  1         1095  
48            
49             # --- creating, getting and setting sub (s) ------------------------------------
50            
51             sub new {
52 1106     1106 0 26019 my $self = shift;
53            
54 1106   66     3927 my $type = ref($self) || $self;
55 1106         1347 my $number = shift;
56 1106 100       2358 $number = "0" unless $number;
57 1106         3315 my $elem = bless {}, $type;
58 1106         2153 $elem->SetValue($number);
59 1106         3855 return $elem;
60             }
61            
62             # --- set coefficient (value before 'e') ------------------------
63             sub _setCoeff {
64 2222     2222   4279 my $self = shift;
65 2222         5005 $self->{_coeff} = shift;
66 2222         3668 return $self;
67             }
68            
69             # --- get coefficient (value before 'e') ------------------------
70             sub _getCoeff {
71 2362     2362   2489 my $self = shift;
72 2362         6223 return $self->{_coeff};
73             }
74            
75             # --- set exponent (value after 'e') ------------------------
76             sub _setExp {
77 1459     1459   1707 my $self = shift;
78 1459         2532 $self->{_exp} = shift;
79 1459         1953 return $self;
80             }
81            
82             # --- get exponent (value after 'e') ------------------------
83             sub _getExp {
84 1566     1566   1773 my $self = shift;
85 1566         21988 return $self->{_exp};
86             }
87            
88             # --- get char for sign ('+', '-' for +1, -1)
89             sub _getSignChar {
90 2379     2379   2551 my $sign = shift;
91            
92 2379 100       6154 return '+' if $sign == 1;
93 269 50       662 return '-' if $sign == -1;
94            
95 0         0 croak "wrong sign given: $sign";
96             }
97            
98             # --- Get whole value ---
99             sub GetValue {
100 403     403 0 488 my $self = shift;
101            
102 403         741 my $coeff_sign = _getSignChar($self->_getCoeffSign());
103 403         735 my $exp_sign = _getSignChar($self->_getExpSign());
104            
105 403         727 return $coeff_sign.$self->_getCoeff().'e'.$self->GetExpValue();
106             }
107            
108             # --- Get exponent value ---
109             sub GetExpValue {
110 1085     1085 0 1144 my $self = shift;
111            
112 1085         1611 my $exp_sign = _getSignChar($self->_getExpSign());
113            
114 1085         1921 return $exp_sign.$self->_getExp();
115             }
116            
117             # --- Set sign of coeff ---
118             sub _setCoeffSign {
119 1225     1225   1350 my $self = shift;
120 1225         1990 $self->{_coeff_sign} = shift;
121 1225         1617 return $self;
122             }
123            
124             # --- Get sign of coeff ---
125             sub _getCoeffSign {
126 1104     1104   1218 my $self = shift;
127 1104         2675 return $self->{_coeff_sign};
128             }
129            
130             # --- Set sign of exp ---
131             sub _setExpSign {
132 1459     1459   1685 my $self = shift;
133 1459         1852 $self->{_exp_sign} = shift;
134 1459         1716 return $self;
135             }
136            
137             # --- Get sign of exponent ---
138             sub _getExpSign {
139 1948     1948   2279 my $self = shift;
140 1948         4129 return $self->{_exp_sign};
141             }
142            
143             # --- extract Sign out of $number_ref ---
144             sub _extractSign {
145 3552     3552   4143 my $number = shift;
146            
147 3552         3702 my $sign = 1;
148            
149 3552         5235 my $signStr = substr($number,0,1);
150 3552 100       9469 if ($signStr eq '-') {
    100          
151 312         421 $number = substr($number, 1);
152 312         358 $sign = -1;
153             }
154             elsif ($signStr eq '+') {
155 617         852 $number = substr($number, 1);
156 617         873 $sign = 1;
157             }
158            
159 3552 100       6559 $sign = 1 unless $number;
160            
161 3552         8613 return ($sign, $number);
162             }
163            
164             # --- split ExponentValue into sign and value and set ---
165             sub SetExpValue {
166 234     234 0 281 my $self = shift;
167 234         260 my $expValue = shift;
168            
169 234         371 my ($expSign, $exp) = _extractSign($expValue);
170            
171 234         732 return $self
172             ->_setExp($exp)
173             ->_setExpSign($expSign);
174             }
175            
176             # --- Parse and set value ---
177             sub SetValue {
178 1106     1106 0 1382 my $self = shift;
179 1106         1730 my $number = lc(shift);
180            
181             # --- Split up into coeffStr, expStr and frac ---
182 1106         1094 my $coeffStr;
183             my $expStr;
184 0         0 my $frac;
185            
186             # 1.e1
187             # +13.0e+2
188             # -123.010e-3
189 1106 100       7409 if ($number =~ /^([\+\-]?\d+)(\.\d*)e([\+\-]?\d+)$/) {
    100          
    100          
    50          
190 34         68 $coeffStr = $1;
191 34         263 $frac = $2;
192 34         62 $expStr = $3;
193             }
194             # 1e1
195             # +12e+2
196             # -123e-3
197             elsif ($number =~ /^([\+\-]?\d+)e([\+\-]?\d+)$/) {
198 347         786 $coeffStr = $1;
199 347         391 $frac = '.';
200 347         634 $expStr = $2;
201             }
202             # 1.
203             # +12.01
204             # -123.120
205             elsif ($number =~ /^([\+\-]?\d+)(\.\d*)$/) {
206 57         104 $coeffStr = $1;
207 57         77 $frac = $2;
208 57         76 $expStr = '0';
209             }
210             # 1
211             # +12
212             # -123
213             elsif ($number =~ /^([\+\-]?\d+)$/) {
214 668         1298 $coeffStr = $1;
215 668         786 $frac = '.';
216 668         917 $expStr = '0';
217             }
218             else {
219 0         0 croak "wrong number format: '$number'";
220             }
221            
222             # --- extract signs ---
223            
224 1106         1830 my ($exp_sign, $exp) = _extractSign($expStr);
225            
226             # --- remove not needed '0's ---
227            
228 1106         1731 $frac .= '0';
229 1106         1395 $frac = substr($frac, 1); # throw away '.' in first char
230 1106         3814 $frac =~ s/0+$//o; # throw away 0 at end
231            
232 1106 100       2444 $exp = 0 unless $exp; # in case exp not set
233            
234             # --- move decimal point '.' off to right to get empty fractional part ---
235            
236 1106         1130 my $exp_value = $exp;
237 1106 100       2168 $exp_value = '-' . $exp
238             if $exp_sign < 0;
239            
240             # TODO: replace standard '-' operator by subtraction of GBM, when available
241             # --- move '.' ---
242 1106         1349 $exp_value -= length($frac);
243            
244 1106         1674 ($exp_sign, $exp) = _extractSign($exp_value);
245            
246 1106         5701 $coeffStr .= $frac;
247            
248 1106         1610 my ($coeff_sign, $coeff) = _extractSign($coeffStr);
249            
250             # --- store values in instance ---
251            
252 1106         2407 $self->_setCoeff($coeff);
253 1106         2144 $self->_setExp($exp);
254 1106         1955 $self->_setCoeffSign($coeff_sign);
255 1106         1771 $self->_setExpSign($exp_sign);
256            
257 1106         3174 return $self->Simplify();
258             }
259            
260             # --- Simplify: Remove 0 at front and ascend exponent for every 0 removed at end ---
261             sub Simplify {
262 1116     1116 0 1173 my $self = shift;
263 1116         1626 my $coeff = $self->_getCoeff();
264            
265 1116 100       3001 if ($coeff =~ /(\d)(0+)$/) {
266 124         302 $coeff = $`.$1;
267 124         197 my $expAdd = length($2);
268            
269 124         250 $self->SetExpValue($self->_getExpSign() * $self->_getExp() + $expAdd);
270             }
271            
272 1116         1954 $coeff =~ s/^0+//o; # throw away 0 from beginning
273 1116 100       2151 $coeff = '0' if $coeff eq ''; # coeff was 0
274            
275 1116 100       2144 if ($coeff eq '0') {
276 119         217 $self->_setExp(0);
277 119         215 $self->_setCoeffSign(1);
278 119         182 $self->_setExpSign(1);
279             }
280            
281 1116         2392 return $self->_setCoeff($coeff);
282             }
283            
284             # --- move decimal point to the right until exponent is 0 ----
285             sub DispenseExponent {
286 32     32 0 42 my $self = shift;
287            
288 32 50       65 return $self if $self->_getExpSign() < 0;
289 32         60 my $exp = $self->_getExp();
290            
291 32 50       68 croak __PACKAGE__.'::'.(caller(0))[3].'(): '.
292             "Cannot deal numbers with more than $_COEFF_MAX_DIGITS"
293             if $exp > $_COEFF_MAX_DIGITS;
294            
295 32 100       120 return $self if $exp eq '0';
296 11         23 return $self->MoveDecimalPointToRight($exp);
297             }
298            
299             # --- MoveDecimalPointToRight for bigger exponent until exponents are equal ---
300             sub AdoptExponents {
301 286     286 0 324 my $gbm1 = shift; # GoldenBigMath 1
302 286         353 my $gbm2 = shift; # GoldenBigMath 2
303            
304             # print $gbm1->GetValue()."\n";
305             # print $gbm2->GetValue()."\n\n";
306            
307 286         653 my $exp1 = $gbm1->GetExpValue();
308 286         562 my $exp2 = $gbm2->GetExpValue();
309            
310 286         391 my $expDiff = 0;
311 286 100       836 if ($exp1 > $exp2) {
    100          
312 68         92 $expDiff = $exp1 - $exp2;
313 68         143 $gbm1->MoveDecimalPointToRight($expDiff);
314            
315             # print "expDiff $expDiff\n";
316             # print $gbm1->GetValue()."\n";
317             # print $gbm2->GetValue()."\n\n";
318            
319 68         117 return $gbm1;
320             }
321             elsif ($exp1 < $exp2) {
322 28         36 $expDiff = $exp2 - $exp1;
323 28         58 $gbm2->MoveDecimalPointToRight($expDiff);
324            
325             # print "expDiff $expDiff\n";
326             # print $gbm1->GetValue()."\n";
327             # print $gbm2->GetValue()."\n\n";
328            
329 28         50 return $gbm2;
330             }
331            
332             # print $gbm1->GetValue()."\n";
333             # print $gbm2->GetValue()."\n\n";
334            
335 190         284 return $gbm1;
336             }
337            
338             # --- move decimal point to the right and reduce exponent ----
339             sub MoveDecimalPointToRight {
340 110     110 0 194 my $self = shift;
341 110         129 my $moves = shift; # number of '0' digits to add at right end
342            
343 110 50       484 croak __PACKAGE__.'::'.(caller(0))[3].'(): '.
344             "Wrong number format for moving decimal point, should be [1-9][0-9]*, "
345             ."but was '$moves'"
346             unless $moves =~ /^[1-9][0-9]*$/;
347            
348 110 50       211 croak __PACKAGE__.'::'.(caller(0))[3].'(): '.
349             "Cannot deal numbers with more than $_COEFF_MAX_DIGITS"
350             if $moves > $_COEFF_MAX_DIGITS;
351            
352 110 50 33     334 return $self
353             if $moves <= 0 or $moves + length($self->_getCoeff()) > $_COEFF_MAX_DIGITS;
354            
355             # TODO: replace standard '-' operator by subtraction of GBM, when available
356 110         289 $self->SetExpValue($self->GetExpValue() - $moves);
357 110         274 $self->{_coeff} .= '0' x $moves;
358            
359 110         229 return $self;
360             }
361            
362             #=== Calculation sub (s) =======================================================
363            
364             # --- comparison sub (s) -------------------------------------------------------
365            
366             sub CompareEqual {
367 1     1 0 3 my $gbm1 = shift;
368 1         2 my $gbm2 = shift;
369            
370 1         4 $gbm1->Simplify();
371 1         2 $gbm2->Simplify();
372 1 50       3 return $gbm1->GetValue() eq $gbm2->GetValue() ? 1: 0;
373             }
374            
375             sub CompareNotEqual {
376 4     4 0 16 my $gbm1 = shift;
377 4         8 my $gbm2 = shift;
378            
379 4         10 $gbm1->Simplify();
380 4         9 $gbm2->Simplify();
381 4 100       8 return $gbm1->GetValue() ne $gbm2->GetValue() ? 1: 0;
382             }
383            
384             sub Greater {
385 1     1 0 2 my $gbm1 = shift;
386 1         3 my $gbm2 = shift;
387            
388 1 50       2 my $result = ($gbm1 <=> $gbm2) > 0 ? 1 : 0;
389 1         5 return $result;
390             }
391            
392             sub Smaller {
393 1     1 0 7 my $gbm1 = shift;
394 1         2 my $gbm2 = shift;
395 1 50       5 my $result = ($gbm1 <=> $gbm2) < 0 ? 1 : 0;
396 1         7 return $result;
397             }
398            
399             sub GreaterEqual {
400 1     1 0 2 my $gbm1 = shift;
401 1         2 my $gbm2 = shift;
402            
403 1 50       2 my $result = ($gbm1 <=> $gbm2) >= 0 ? 1 : 0;
404 1         6 return $result;
405             }
406            
407             sub SmallerEqual {
408 2     2 0 5 my $gbm1 = shift;
409 2         4 my $gbm2 = shift;
410 2 50       5 my $result = ($gbm1 <=> $gbm2) <= 0 ? 1 : 0;
411 2         8 return $result;
412             }
413            
414             # --- start Addition, handle exponents and sign ----
415             sub Addition {
416 216     216 0 1159 my $gbm1 = shift;
417 216         240 my $gbm2 = shift;
418            
419 216 100       585 return new Math::GoldenBigMath($gbm1)->Addition($gbm2) unless ref $gbm1;
420 161 100       434 return $gbm1->Addition(new Math::GoldenBigMath($gbm2)) unless ref $gbm2;
421            
422 106         212 AdoptExponents($gbm1, $gbm2);
423            
424 106         125 my $resultString;
425            
426 106         196 my $sign1 = $gbm1->_getCoeffSign();
427 106         177 my $sign2 = $gbm2->_getCoeffSign();
428            
429 106         250 my $z1 = $gbm1->_getCoeff();
430 106         181 my $z2 = $gbm2->_getCoeff();
431            
432 106 100       214 if ($sign1 == $sign2) {
433 85         160 $resultString = AdditionWithoutSignPointAndExponent($z1, $z2);
434             }
435             else {
436 21 100       78 my $result = $sign1 > 0
437             ? Subtraction($z1, $z2)
438             : Subtraction($z2, $z1);
439            
440 21         66 $result->DispenseExponent();
441 21 50       37 croak "something went wrong, exponent was not dispensed" if $result->_getExp() ne '0';
442            
443 21         46 $resultString = $result->_getCoeff();
444 21         42 $sign1 = $result->_getCoeffSign();
445             }
446            
447 106         180 my $signChar = _getSignChar($sign1);
448 106         223 my $exponent = _getSignChar($gbm1->_getExpSign()) . $gbm1->_getExp();
449            
450 106         329 return $gbm1->new("$signChar${resultString}e$exponent");
451             }
452            
453             # --- start Subtraction, handle exponents and sign ----
454             sub Subtraction {
455 230     230 0 1027 my $gbm1 = shift;
456 230         251 my $gbm2 = shift;
457            
458 230 100       594 return new Math::GoldenBigMath($gbm1)->Subtraction($gbm2) unless ref $gbm1;
459 154 100       366 return $gbm1->Subtraction(new Math::GoldenBigMath($gbm2)) unless ref $gbm2;
460            
461 78         135 AdoptExponents($gbm1, $gbm2);
462            
463 78         90 my $resultString;
464            
465 78         133 my $sign1 = $gbm1->_getCoeffSign();
466 78         124 my $sign2 = $gbm2->_getCoeffSign();
467            
468 78         130 my $z = $gbm1->_getCoeff();
469 78         149 my $subtr = $gbm2->_getCoeff();
470            
471 78 100       173 if ($sign1 == $sign2) {
472 58 100       147 my $swap = Compare ($z, $subtr) < 0 ? 1: 0;
473            
474 58 100       186 if ($swap) { # Second is greater, so swap args and set $sign1 = -$sign1
475 19         50 $resultString = SubtractionWithoutSignPointAndExponentAndFirstGreater($subtr, $z);
476 19 100       57 $sign1 = $sign1 < 0 ? 1: -1;
477             # print "# swap\n";
478             }
479             else {
480 39         83 $resultString = SubtractionWithoutSignPointAndExponentAndFirstGreater($z, $subtr);
481             }
482             }
483             else {
484 20         141 $resultString = AdditionWithoutSignPointAndExponent($z, $subtr);
485             }
486            
487             # print "# sign $sign1\n";
488            
489 78         140 my $signChar = _getSignChar($sign1);
490 78         144 my $exponent = _getSignChar($gbm1->_getExpSign()) . $gbm1->_getExp();
491            
492 78         289 return $gbm1->new("$signChar${resultString}e$exponent");
493             }
494            
495             # --- start multiplication, handle exponents and sign ---------------------
496             sub Multiplication {
497 128     128 0 1018 my $gbm1 = shift;
498 128         157 my $gbm2 = shift;
499            
500 128 100       376 return new Math::GoldenBigMath($gbm1)->Multiplication($gbm2) unless ref $gbm1;
501 102 100       491 return $gbm1->Multiplication(new Math::GoldenBigMath($gbm2)) unless ref $gbm2;
502            
503 76         115 my $coeff1 = $gbm1->_getCoeff();
504 76         242 my $coeff2 = $gbm2->_getCoeff();
505            
506 76 100 100     367 return $gbm1->new(0)
507             if $coeff1 == 0 or $coeff2 == 0;
508            
509 60         118 my $resultString = MultiplicationWithoutSignPointAndExponent
510             ($coeff1, $coeff2);
511            
512 60 100       132 my $signChar = $gbm1->_getCoeffSign() eq $gbm2->_getCoeffSign()
513             ? '+' : '-';
514            
515 60         133 my $exp1 = _getSignChar($gbm1->_getExpSign()) . $gbm1->_getExp();
516 60         135 my $exp2 = _getSignChar($gbm2->_getExpSign()) . $gbm2->_getExp();
517            
518             # TODO: replace standard '+' operator by addition of GBM
519 60         800 my $exponent = $exp1 + $exp2;
520            
521             # Add call of Simplify()
522 60         160 return $gbm1->new("$signChar${resultString}e$exponent");
523             }
524            
525             # --- Do Compare ----------------------------------------------------------
526             sub Compare {
527 269     269 0 1408 my $gbm1 = shift;
528 269         310 my $gbm2 = shift;
529            
530 269 100       696 return new Math::GoldenBigMath($gbm1)->Compare($gbm2) unless ref $gbm1;
531 183 100       525 return $gbm1->Compare(new Math::GoldenBigMath($gbm2)) unless ref $gbm2;
532            
533 96         167 my $sign = $gbm1->_getCoeffSign();
534 96         178 my $sign2 = $gbm2->_getCoeffSign();
535            
536 96 50       238 if ($sign ne $sign2) {
537 0         0 return $sign cmp $sign2;
538             }
539            
540 96         150 AdoptExponents($gbm1, $gbm2);
541            
542 96         171 my $coeff1 = $gbm1->_getCoeff();
543 96         166 my $coeff2 = $gbm2->_getCoeff();
544            
545 96         130 my $l1 = length($coeff1);
546 96         107 my $l2 = length($coeff2);
547            
548 96         135 my $coeffCmp = "";
549 96 100       274 if ($l1 == $l2) {
550 71         121 $coeffCmp = $coeff1 cmp $coeff2;
551             }
552             else {
553 25 100       58 $coeffCmp = $l1 > $l2 ? 1:-1;
554             }
555            
556 96 50       659 return $sign > 0 ? $coeffCmp: -$coeffCmp;
557             }
558            
559             # --- create multiplication table for fast multiplication and division ---
560             # internal, but should be tested
561             sub buildMultiplicationTableAsString {
562 86     86 0 21475 my $z = shift; # coeff without sign as string
563            
564 86 50       365 croak "not a number string " unless $z =~ /^\d+$/;
565            
566 86         96 my @mulTab;
567 86         287 $mulTab[9] = 0;
568 86         95 $mulTab[0] = 0;
569 86         109 $mulTab[1] = $z;
570 86         166 foreach my $i (2..9) {
571 688         1452 $mulTab[$i] = AdditionWithoutSignPointAndExponent($mulTab[$i-1], $z);
572             }
573            
574 86         249 return \@mulTab;
575             }
576            
577             #=== Worker subs with real calculation ====================================
578            
579             # FB-SCHR+ =====================================================================
580             # Addition without exponent, decimal point and sign
581             # Returns string containing [0-9]+
582             sub AdditionWithoutSignPointAndExponent {
583 894     894 0 1032 my $z1 = shift; # Number as string
584 894         1242 my $z2 = shift; # Number as string
585            
586 894         1380 my $i1 = length($z1) - 1;
587 894         7008 my $i2 = length($z2) - 1;
588 894 100       1429 my $maxIdx = $i1 > $i2 ? $i1: $i2;
589            
590             # print "# maxIdx $maxIdx\n";
591            
592             # result variables
593 894         886 my $result = ''; # result as string
594            
595             # index variables
596 894         789 my $i; # running index in GoldenBigMath
597             my $s; # sum of two digits
598 894         937 my $u = 0; # store carry (u for german uebertrag)
599 894         775 my $d1; # one digit
600             my $d2; # one digit
601            
602             # --- now calculate sum by schriftliche addition ---------------------------
603 894         1859 for ($i = $maxIdx; $i >= 0; $i--) {
604 4546         5150 $d1 = 0;
605 4546         4786 $d2 = 0;
606            
607 4546 100       8674 $d1 = substr($z1, $i1, 1) if $i1 >= 0;
608 4546 100       7804 $d2 = substr($z2, $i2, 1) if $i2 >= 0;
609            
610 4546         5315 $s = $d1 + $d2 + $u;
611             # print "# $s = $d1 + $d2\n";
612 4546 100       6778 if ($s > 9) {
613 1467         1468 $u = 1; # don't use slow divide or modulo
614 1467         1469 $s -= 10; # don't use slow divide or modulo
615             }
616             else {
617 3079         3014 $u = 0;
618             }
619             # print "# $u$s = $d1 + $d2\n";
620 4546         5415 $result .= $s; # much faster than $result = $s . $result
621            
622 4546         4467 $i1--;
623 4546         8780 $i2--;
624             }
625            
626 894 100       1495 $result .= $u if $u;
627            
628             # --- reverse strings to get the highest number at first position
629 894         1103 $result = reverse $result;
630            
631             # replace starting zeroes
632 894         1021 $result =~ s/^0+//go;
633 894 100       1801 $result = 0 unless $result;
634            
635 894         2522 return $result;
636             }
637            
638             # FB-SCHR- =====================================================================
639             # Subtraction without exponent, decimal point and sign
640             # and first number is greater than second
641             sub SubtractionWithoutSignPointAndExponentAndFirstGreater {
642 58     58 0 90 my $z1 = shift; # Number as string
643 58         65 my $z2 = shift; # Number as string
644            
645 58         80 my $maxIdx = length($z1) - 1;
646 58         88 my $addZeroCount = $maxIdx - length($z2) + 1;
647            
648 58 50       117 croak "second longer than first" if $addZeroCount < 0;
649            
650             # Add zeros in front of second to get both strings same length
651 58 100       145 $z2 = '0' x $addZeroCount . $z2 if $addZeroCount > 0;
652            
653             # print "\n $z1\n-$z2\n\n";
654            
655             # result variables
656 58         66 my $result = ''; # result as string
657 58         69 my $resultObj; # result as object
658            
659             # index variables
660             my $i; # running index in GoldenBigMath
661 0         0 my $d; # difference of two digits
662 58         67 my $u = 0; # store carry (u for german uebertrag)
663            
664             # --- now calculate difference by schriftliche subtraction -----------------
665 58         130 for ($i = $maxIdx; $i >= 0; $i--) {
666 348         466 $d = substr($z1, $i, 1) - substr($z2, $i, 1) - $u;
667 348 100       452 if ($d < 0) {
668 21         19 $u = 1; # don't use slow divide or modulo
669 21         22 $d += 10; # don't use slow divide or modulo
670             }
671             else {
672 327         300 $u = 0;
673             }
674 348         598 $result .= $d; # much faster than $result = $d . $result
675             }
676            
677             # --- reverse strings to get the highest number at first position
678 58         88 $result = reverse $result;
679            
680             # replace starting zeroes by ' '
681 58         161 $result =~ s/^0+//go;
682 58 100       132 $result = 0 unless $result;
683            
684 58         139 return $result;
685             }
686            
687             # FB-SCHR* =====================================================================
688             # Multiplication without exponent, decimal point and sign
689             sub MultiplicationWithoutSignPointAndExponent {
690 60     60 0 66 my $z1 = shift; # Number as string
691 60         71 my $z2 = shift; # Number as string
692            
693 60         108 my $mulTabRef = buildMultiplicationTableAsString($z2);
694            
695             # result variables
696 60         73 my $result = '';
697            
698             # help variables
699 60         69 my $c = 1; # dot counter
700            
701             # intermediate multiplication values
702 60         59 my $d; # next digit
703 60         65 my $addZeros = ''; # store zeros to append to multab value
704 60         50 my $add; # GoldenBigMath to be added as
705             # next multiplication by single digit
706            
707             # --- now calculate mul by schriftliche multiplication ---------------------
708            
709 60         134 for (my $i = length($z1)-1; $i >= 0; $i--) {
710 101         126 $d = substr($z1, $i, 1);
711 101 50       174 if ($d != '0') {
712             # --- $add = $d * $d2, much faster is using table as follows
713 101         125 $add = $mulTabRef->[$d] . $addZeros;
714 101         175 $result = AdditionWithoutSignPointAndExponent($result, $add);
715             }
716 101         134 $addZeros .= '0'; # next number position, mulTabRef *= 10;
717            
718             # --- print dots (.) to see its still running and not hanging ----------
719 101 50       181 if ($c > 16383) {
720 0         0 print "\n";
721 0         0 $c = 1;
722             }
723 101 50       327 print "." unless $c++ & 127;
724             }
725            
726 60         216 return $result;
727             }
728            
729             # FB-SCHR/ =====================================================================
730             # FB-SCHR% =====================================================================
731             # Really calc the division (without exponent, decimal point and sign)
732             sub CalcDivisionWithoutSignPointAndExponent {
733 0     0 0   my $gbm1 = shift;
734 0           my $z1 = $gbm1; # to get symmetric names
735 0           my $z2 = shift; # to get symmetric names
736            
737 0           $z1->prepareMulDiv($z2);
738            
739 0           my @mulTab = $gbm1->ConvertMultiplicationTableToGoldenBigMath();
740 0           my $maxDivDigits = $gbm1->GetMaxDivideDigits();
741            
742             # use string references for faster access
743 0           my $bm1Ref = \$z1->GetValue();
744 0           my $bm1Len = length($$bm1Ref);
745            
746             # result variables
747 0           my $result = '';
748 0           my $resultObj = new GoldenBigMath->new(0);
749            
750             # index and help variables
751 0           my $i = 0; # running index in GoldenBigMath
752 0           my $z; # next digit (z for german ziffer)
753 0           my $u = 0; # store carry (u for german uebertrag)
754 0           my $c = 1; # dot counter
755 0           my $firstIter = 'true'; # One iteration is needed !!
756            
757             # intermediate division values
758 0           my $rest = '0'; # Residue of actual division step,
759             # but rest is shorter and same in german
760 0           my %restHash = (); # will actually not be used,
761             # coming later to identify periods
762            
763             # --- now calculate div by schriftliche division ---------------------------
764            
765 0   0       while ($i < $maxDivDigits
      0        
766             && ($rest != 0 || $firstIter || $i <= $bm1Len)) {
767            
768 0           $firstIter = '';
769 0           my $bmRest = GoldenBigMath->new($rest); # Rest as GoldenBigMath
770 0           my $z = 0; # next digit of result
771            
772             # --- find next result digit -------------------------------------------
773            
774 0 0         unless ($bmRest < $mulTab[1]) {
775 0           while ($z < 9) {
776 0 0         if ($mulTab[$z+1] > $bmRest) {
777 0           last;
778             }
779 0 0         $z++ if $z < 9;
780             }
781            
782             # --- fire exit, should never be reached!! ---
783 0 0         if ($mulTab[$z] > $bmRest) {
784 0           print $bmRest->GetValue() . " < " . $mulTab[$z]->GetValue()
785             . "\n";
786 0           croak "problem during search for multiplication factor\n";
787             }
788            
789             # --- calc next rest for division ----------------------------------
790            
791 0           $bmRest = $bmRest - $mulTab[$z];
792             }
793            
794             # --- add digit found --------------------------------------------------
795            
796 0           $result .= $z;
797            
798             # --- Check, if decimal point reached ----------------------------------
799            
800 0           my $lz = '0';
801 0 0         if ($i < $bm1Len) {
    0          
802 0           $lz = substr($$bm1Ref, $i, 1);
803             }
804             # --- end of number reached --------------------------------------------
805             elsif ($i == $bm1Len) {
806             # --- modulo wanted ? ----------------------------------------------
807 0 0         if ($gbm1->GetOperator() eq '%') {
808 0           $result = $bmRest->GetValue();
809 0           last;
810             }
811            
812             # --- add decimal point --------------------------------------------
813            
814 0           $result .= '.';
815             }
816            
817             # --- Append next digit to rest ----------------------------------------
818            
819 0           $rest = $bmRest->GetValue() . $lz;
820            
821             # --- print dots (.) to see its still running and not hanging ----------
822 0 0         if ($c > 16383) {
823 0           print "\n";
824 0           $c = 1;
825             }
826 0 0         print "." unless $c++ %127;
827            
828 0           $i++;
829             }
830            
831             # --- create and fill result object ----------------------------------------
832 0           $z1->Normalize();
833 0           $z2->Normalize();
834 0           $resultObj = GoldenBigMath->new($result);
835 0           $resultObj->Normalize();
836 0           $resultObj->_storeOperator($gbm1->GetOperator());
837 0           $resultObj->_storeOperatorName("div string");
838 0           $resultObj->_storeU('...');
839 0           $resultObj->_setZ1($z1); #TODO: clean up storage??
840 0           $resultObj->_setZ2($z2); #TODO: clean up storage??
841            
842 0           return $resultObj;
843             }
844            
845             __END__