# Integer Arithmetic Operations on Large Numbers

Maximum positive value that can be stored by integer type variable in ABAP is 2147483647.

For values beyond maximum limit of integer variable, workarounds can be used.

One such example is discussion MOD operation fail for large numbers started by Starlet Abraham

I was able to calculate correct result of 512^17 MOD 2773 using the methods detailed below.

I have written this program to do following operations on large integers.

• Subtraction
• Multiplication
• Division
• Mod
• Power

Full code snippet is posted at the end of this document.

Program has its flaws, but it works.

I would briefly go through how the logic works.

## How are big numbers stored?

Local class lcl_bignum has instance attributes that store big numbers.

LV_STRING – Number stored as string type.

LT_INT – an internal table of integers. The string is broken into substrings of size 4 (base 10000), and those substrings ( as int4 ) are stored in internal table.

LV_SIGN – integer type to store the sign of number. +1 for positive, -1 for negative.

For example, number 1234567890 will be stored in internal table as

LT_INT
7890
3456
12

## Why is number broken into base 10000?

Base 10000 is safe considering the capacity of integer variable type in ABAP.

Computing 9999 * 9999 results in 99980001, which is within the limits of integer type.

On the other hand, 99999 * 99999 results in 999980001, which would lead to overflow exception.

## How to create instance and store big number?

Below code snippets can be used to create instance of big number.

The constructor parameters are options.

CREATE OBJECT lr3.

Above code will create instance with zero value in it.

CREATE OBJECT lr2
EXPORTING
i_value = ‘533335555511111’.

Above code will create instance and populate lv_string and lt_int by breaking down input string.

Sign of number is positive by default.

Input string should have only numbers in it.

CREATE OBJECT lr1
EXPORTING
i_value = ‘6584389034291301828999’
i_negative = ‘X’.

Using above code, ‘X’ can be passed i_negative flag if we have a negative number.

It accepts 2 object reference that need to be added, and result is stored in object whose method gets called.

We are adding the numbers starting lowest weightage (LT_INT[1]), and carrying forward extra in case of overflow.

For example, we add 12345678 and 22223333. Program would add (5678,3333) first, and then (1234,2222).

For A = B + C, below code can be used.

i_obj2 = lr2 ).

For A = A + B, below code can be used.

i_obj2 = lr2 ).

## How to subtract numbers?

Instance method Subtract( ) can be used for subtracting numbers.

The usage is same as Add( ) method.

Looking at code, you would see that Add( ) and Subtract( ) are calling each other.

It is done the tackle how absolute values are treated depending on their sign.

For example, A = B + C, the values of B and C will be added, and common sign will be retained if B and C have same sign. Both positive, or both negative.

If the signs of B and C are different, we essentially have to keep sign of B, and calculate |B| – |C| (absolute values).

## How to multiply numbers?

Instance method Multiply( ) can be used.

It accepts 2 object references, and stores result in calling object.

I would explain the logic using example. It is similar to the method used in elementary school.

For elementary example of multiplying 123 and 456, below logic is used.

123

456

===

3*6 + (3*5+2*6) + (3*4+2*5+1*6) + (2*4+1*5) + 1*4

Same thing is done in program at base 10000 level.

At every stage we are carrying forward the overflow value.

## How to divide numbers?

For division, I am guessing the answer by multiplying.

Once the guess surpasses dividend, I take a step back, and start multiplying by square root of 10000.

A point comes when multiplier is reduced to 1, and multiplication can no longer be used to get closer to answer.

I then use addition to get closer to answer. Instead of square root, divide by 2 to get next “jump value”.

A dry run using elementary example would be something like this.

451

4

## How to do calculate remainder (MOD operation)

Instance method Mod( ) can be used, which internally uses Divide( ), Multiply( ) and Subtract( ).

To calculate A = B MOD C, program does something like:

A = B – C * ( B DIV C )

Here we are reusing the code written in previous methods.

## How to calculate Power?

Instance method Power( ) can be used, which internally uses Multiply( ) and Subtract( ).

For calculating A = B POWER C, program starts with 1 as answer, uses loop to multiply B to answer, C number of times.

## Full code snippet

1. *———————————————————————-*
2. *       CLASS lcl_bignum DEFINITION
3. *———————————————————————-*
4. *
5. *———————————————————————-*
6. CLASS lcl_bignum DEFINITION.
7.   PUBLIC SECTION.
8.     METHODS constructor IMPORTING i_value TYPE string OPTIONAL
9.                     i_negative  TYPE c OPTIONAL.
10.     METHODS get_sign RETURNING value(r_sign) TYPE i.
11.     METHODS get_string RETURNING value(r_string) TYPE string.
12.     METHODS get_int RETURNING value(r_int) TYPE int4_table.
13.     METHODS set_data IMPORTING i_value TYPE string
14.                    i_negative TYPE c OPTIONAL.
15.     METHODS add IMPORTING i_obj1 TYPE REF TO lcl_bignum
16.                 i_obj2 TYPE REF TO lcl_bignum.
17.     METHODS subtract IMPORTING  i_obj1 TYPE REF TO lcl_bignum
18.                   i_obj2 TYPE REF TO lcl_bignum.
19.     METHODS multiply IMPORTING  i_obj1 TYPE REF TO lcl_bignum
20.                   i_obj2 TYPE REF TO lcl_bignum.
21.     METHODS divide IMPORTING i_obj1 TYPE REF TO lcl_bignum
22.                  i_obj2 TYPE REF TO lcl_bignum.
23.     METHODS mod IMPORTING i_obj1 TYPE REF TO lcl_bignum
24.                 i_obj2 TYPE REF TO lcl_bignum.
25.     METHODS power IMPORTING i_obj1 TYPE REF TO lcl_bignum
26.                 i_obj2 TYPE REF TO lcl_bignum.
27.     METHODS invert_sign.
28.     METHODS copy_values IMPORTING i_obj TYPE REF TO lcl_bignum.
29.     METHODS abs_compare IMPORTING i_obj TYPE REF TO lcl_bignum
30.               RETURNING value(r_result) TYPE i.
31.   PROTECTED SECTION.
32.     METHODS int_to_string.
33.     METHODS split_into_int.
35.     METHODS divide_by_2.
36.   PRIVATE SECTION.
37.     DATA lv_string TYPE string.
38.     DATA lv_sign TYPE i VALUE 1.
39.     DATA lt_int TYPE int4_table.
40.     “sqrt of max int4 is 46340
41.     CONSTANTS: gc_unit_size TYPE i VALUE 10000,
42.            gc_unit_digits TYPE i VALUE 4,
43.            gc_gt TYPE i VALUE 1“greater than
44.            gc_lt TYPE i VALUE 2“less than
45.            gc_eq TYPE i VALUE 0“equals
46. ENDCLASS.                    “lcl_bignum DEFINITION
47. *———————————————————————-*
48. *       CLASS lcl_bignum IMPLEMENTATION
49. *———————————————————————-*
50. *
51. *———————————————————————-*
52. CLASS lcl_bignum IMPLEMENTATION.
53.   METHOD constructor.
54.     IF i_value IS SUPPLIED.
55.       IF i_value CO ‘ 1234567890’.
56.         lv_string = i_value.
57.         CONDENSE lv_string.
58.       ENDIF.
59.       split_into_int( ).
60.     ENDIF.
61.     IF i_negative IS SUPPLIED.
62.       IF i_negative EQ ‘X’.
63.         lv_sign = –1.
64.       ENDIF.
65.     ENDIF.
66.   ENDMETHOD.                    “constructor
67.   METHOD set_data.
68.     CLEAR: lv_string, lt_int.
69.     lv_sign = 1.
70.     IF i_value CO ‘ 1234567890’.
71.       lv_string = i_value.
72.       CONDENSE lv_string.
73.     ENDIF.
74.     split_into_int( ).
75.     IF i_negative IS SUPPLIED.
76.       IF i_negative EQ ‘X’.
77.         lv_sign = –1.
78.       ENDIF.
79.     ENDIF.
80.   ENDMETHOD.                    “set_data
81.   METHOD split_into_int.
82.     DATA lv_string TYPE string.
83.     DATA lv_int TYPE i.
84.     lv_string = me->lv_string.
85.     WHILE lv_string IS NOT INITIAL.
86.       IF STRLEN( lv_string ) GT gc_unit_digits.
87.         SHIFT lv_string RIGHT BY gc_unit_digits PLACES CIRCULAR.
88.         lv_int = lv_string+0(gc_unit_digits).
89.         SHIFT lv_string LEFT BY gc_unit_digits PLACES.
90.       ELSE.
91.         lv_int = lv_string.
92.         CLEAR lv_string.
93.       ENDIF.
94. *      INSERT lv_int INTO lt_int INDEX 1.
95.       APPEND lv_int TO lt_int.
96.     ENDWHILE.
97.   ENDMETHOD.                    “split_into_int
99.     DATA: lt_temp TYPE int4_table,
100.         lv_temp TYPE i,
101.         lv_count TYPE i.
102.     CHECK lt_int IS NOT INITIAL.
103.     lt_temp = lt_int.
104.     CLEAR lt_int.
105.     lv_count = LINES( lt_temp ).
106.     DO lv_count TIMES.
107.       READ TABLE lt_temp INTO lv_temp INDEX lv_count.
108.       IF lv_temp IS NOT INITIAL.
109.         EXIT.
110.       ENDIF.
111.       lv_count = lv_count – 1.
112.     ENDDO.
113.     IF lv_count IS NOT INITIAL AND
114.        lv_count LE LINES( lt_temp ).
115.       APPEND LINES OF lt_temp FROM 1 TO lv_count TO lt_int.
116.     ENDIF.
118.   METHOD int_to_string.
119.     DATA lv_int TYPE i.
120.     DATA lv_char TYPE n LENGTH gc_unit_digits.
121.     LOOP AT lt_int INTO lv_int.
122.       lv_char = lv_int.
123.       CONCATENATE lv_char lv_string INTO lv_string.
124.     ENDLOOP.
125.     SHIFT lv_string LEFT DELETING LEADING ‘0’.
126.   ENDMETHOD.                    “int_to_string
127.   METHOD get_int.
128.     r_int = lt_int.
129.   ENDMETHOD.                    “get_int
130.   METHOD get_sign.
131.     r_sign = lv_sign.
132.   ENDMETHOD.                    “get_sign
133.   METHOD get_string.
134.     r_string = lv_string.
135.   ENDMETHOD.                    “get_string
136.   METHOD copy_values.
137.     lv_string = i_obj->get_string( ).
138.     lv_sign = i_obj->get_sign( ).
139.     lt_int = i_obj->get_int( ).
140.   ENDMETHOD.                    “copy_values
141.   METHOD abs_compare.
142.     DATA: lt_int2 TYPE int4_table,
143.         lv_int TYPE i,
144.         lv_int2 TYPE i,
145.         lv_max TYPE i.
146.     lt_int2 = i_obj->get_int( ).
147.     lv_max = LINES( lt_int ).
148.     IF LINES( lt_int2 ) GT lv_max.
149.       lv_max = LINES( lt_int2 ).
150.     ENDIF.
151.     WHILE lv_max GT 0.
152.       CLEAR: lv_int,
153.          lv_int2.
154.       READ TABLE lt_int INTO lv_int INDEX lv_max.
155.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_max.
156.       IF lv_int EQ lv_int2.
157.         lv_max = lv_max – 1.
158.       ELSE.
159.         IF lv_int GT lv_int2.
160.           r_result = gc_gt.
161.         ELSEIF lv_int LT lv_int2.
162.           r_result = gc_lt.
163.         ENDIF.
164.         EXIT.
165.       ENDIF.
166.     ENDWHILE.
167.   ENDMETHOD.                    “abs_compare
168.   METHOD divide_by_2.
169.     DATA: lv_int TYPE i,
170.         lt_temp TYPE int4_table,
171.         lv_mod TYPE i,
172.         lv_count TYPE i.
173.     lv_count = LINES( lt_int ).
174.     DO lv_count TIMES.
175.       READ TABLE lt_int INTO lv_int INDEX lv_count.
176.       IF sy-subrc EQ 0.
177.         lv_int = lv_int + lv_mod * gc_unit_size.
178.         lv_mod = lv_int MOD 2.
179.         lv_int = lv_int DIV 2.
180.         INSERT lv_int INTO lt_temp INDEX 1.
181.       ENDIF.
182.       lv_count = lv_count – 1.
183.     ENDDO.
184.     lt_int = lt_temp.
186.     int_to_string( ).
187.   ENDMETHOD.                    “divide_by_2
188.   METHOD invert_sign.
189.     lv_sign = lv_sign * –1.
190.   ENDMETHOD.                    “invert_sign
192.     DATA: lt_int1 TYPE int4_table,
193.         lt_int2 TYPE int4_table,
194.         lv_int TYPE i,
195.         lv_int1 TYPE i,
196.         lv_int2 TYPE i,
197.         lv_index TYPE i,
198.         lv_count TYPE i,
199.         lv_extra TYPE i.
200.     lt_int1 = i_obj1->get_int( ).
201.     lt_int2 = i_obj2->get_int( ).
202.     IF i_obj1->get_sign( ) NE i_obj2->get_sign( ).
203.       subtract( i_obj1 = i_obj1
204.           i_obj2 = i_obj2 ).
205.       EXIT.
206.     ENDIF.
207.     lv_count = LINES( lt_int1 ).
208.     IF lv_count LT LINES( lt_int2 ).
209.       lv_count = LINES( lt_int2 ).
210.     ENDIF.
211.     CLEAR: lt_int, lv_string.
212.     lv_sign = i_obj1->get_sign( ).
213.     DO lv_count TIMES.
214.       CLEAR: lv_int1,
215.          lv_int2.
216.       lv_index = sy-index.
217.       READ TABLE lt_int1 INTO lv_int1 INDEX lv_index.
218.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_index.
219.       lv_int = lv_extra + lv_int1 + lv_int2.
220.       lv_extra = lv_int DIV gc_unit_size.
221.       lv_int = lv_int MOD gc_unit_size.
222.       APPEND lv_int TO lt_int.
223.     ENDDO.
224.     int_to_string( ).
226.   METHOD subtract.
227.     DATA: lt_int1 TYPE int4_table,
228.         lt_int2 TYPE int4_table,
229.         lv_int TYPE i,
230.         lv_int1 TYPE i,
231.         lv_int2 TYPE i,
232.         lv_index TYPE i,
233.         lv_count TYPE i,
234.         lv_extra TYPE i.
235.     FIELD-SYMBOLS: <fs_int> TYPE i.
236.     lt_int1 = i_obj1->get_int( ).
237.     lt_int2 = i_obj2->get_int( ).
238.     IF i_obj1->get_sign( ) NE i_obj2->get_sign( ).
239.       i_obj2->invert_sign( ).
241.          i_obj2 = i_obj2 ).
242.       EXIT.
243.     ENDIF.
244.     lv_count = LINES( lt_int1 ).
245.     IF lv_count LT LINES( lt_int2 ).
246.       lv_count = LINES( lt_int2 ).
247.     ENDIF.
248.     CLEAR: lt_int, lv_string.
249.     lv_sign = i_obj1->get_sign( ).
250.     DO lv_count TIMES.
251.       CLEAR: lv_int1,
252.          lv_int2.
253.       lv_index = sy-index.
254.       READ TABLE lt_int1 INTO lv_int1 INDEX lv_index.
255.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_index.
256.       lv_int = lv_extra + lv_int1 – lv_int2.
257.       CLEAR lv_extra.
258.       IF lv_int LT 0.
259.         lv_int = lv_int + gc_unit_size.
260.         lv_extra = –1.
261.       ENDIF.
262.       lv_int = lv_int MOD gc_unit_size.
263.       APPEND lv_int TO lt_int.
264.     ENDDO.
265.     IF lv_extra IS NOT INITIAL.
266.       lv_sign = lv_sign * lv_extra.
267.       LOOP AT lt_int ASSIGNING <fs_int>.
268.         IF sy-tabix EQ 1.
269.           <fs_int> = gc_unit_size – <fs_int>.
270.         ELSE.
271.           <fs_int> = gc_unit_size – <fs_int> + 1.
272.         ENDIF.
273.       ENDLOOP.
274.     ENDIF.
276.     int_to_string( ).
277.   ENDMETHOD.                    “subtract
278.   METHOD multiply.
279.     DATA: lt_int1 TYPE int4_table,
280.         lt_int2 TYPE int4_table,
281.         lv_int TYPE i,
282.         lv_int1 TYPE i,
283.         lv_int2 TYPE i,
284.         lv_int_tmp TYPE i,
285.         lv_index TYPE i,
286.         lv_index_start TYPE i,
287.         lv_index_end   TYPE i,
288.         lv_index_start_tmp TYPE i,
289.         lv_index_end_tmp TYPE i,
290.         lv_count TYPE i,
291.         lv_carry TYPE i,
292.         lv_extra TYPE i.
293.     lt_int1 = i_obj1->get_int( ).
294.     lt_int2 = i_obj2->get_int( ).
295.     lv_count = LINES( lt_int1 ).
296.     IF lv_count LT LINES( lt_int2 ).
297.       lv_count = LINES( lt_int2 ).
298.     ENDIF.
299.     CLEAR: lt_int, lv_string.
300.     lv_sign = i_obj1->get_sign( ) * i_obj2->get_sign( ).
301.     CHECK lv_count IS NOT INITIAL.
302.     lv_index_start = 1.
303.     lv_index_end   = 1.
304.     DO.
305.       CLEAR lv_int.
306.       lv_index_start_tmp = lv_index_start.
307.       lv_index_end_tmp   = lv_index_end.
308.       lv_int = lv_extra.
309.       CLEAR lv_extra.
310.       WHILE lv_index_start_tmp LE lv_index_end.
311.         CLEAR: lv_int1,
312.              lv_int2.
313.         READ TABLE lt_int1 INTO lv_int1 INDEX lv_index_start_tmp.
314.         READ TABLE lt_int2 INTO lv_int2 INDEX lv_index_end_tmp.
315.         lv_int_tmp = ( lv_int1 * lv_int2 ) MOD gc_unit_size.
316.         lv_extra = lv_extra + ( lv_int1 * lv_int2 ) DIV gc_unit_size.
317.         lv_int = lv_int + lv_int_tmp.
318.         lv_extra = lv_extra + lv_int DIV gc_unit_size.
319.         lv_int = lv_int MOD gc_unit_size.
320.         lv_index_start_tmp = lv_index_start_tmp + 1.
321.         lv_index_end_tmp = lv_index_end_tmp – 1.
322.       ENDWHILE.
323.       APPEND lv_int TO lt_int.
324.       IF lv_index_end LT lv_count.
325.         lv_index_end = lv_index_end + 1.
326.       ELSEIF lv_index_start LT lv_count.
327.         lv_index_start = lv_index_start + 1.
328.       ELSEIF lv_index_start EQ lv_count AND
329.        lv_index_end   EQ lv_count.
330.         EXIT.
331.       ENDIF.
332.     ENDDO.
333.     IF lv_extra IS NOT INITIAL.
334.       APPEND lv_extra TO lt_int.
335.     ENDIF.
337.     int_to_string( ).
338.   ENDMETHOD.                    “multiply
339.   METHOD divide.
340.     DATA: lt_int1 TYPE int4_table,
341.         lt_int2 TYPE int4_table,
342.         lv_int TYPE i,
343.         lv_int1 TYPE i,
344.         lv_int2 TYPE i,
345.         lv_string1 TYPE string,
346.         lv_string2 TYPE string,
347.         lv_index TYPE i,
348.         lv_count TYPE i,
349.         lv_extra TYPE i,
350.         lv_sign1 TYPE i,
351.         lr1 TYPE REF TO lcl_bignum,
352.         lr2 TYPE REF TO lcl_bignum,
353.         lr_guess TYPE REF TO lcl_bignum,
354.         lr_step TYPE REF TO lcl_bignum,
355.         lv_step_str TYPE string,
356.         lv_step TYPE i,
357.         lr_temp  TYPE REF TO lcl_bignum.
358.     CREATE OBJECT lr1.
359.     CREATE OBJECT lr2.
360.     lr1->copy_values( i_obj1 ).
361.     lr2->copy_values( i_obj2 ).
362. *   quotient is zero when divisor is bigger than dividend
363.     CHECK lr1->abs_compare( lr2 ) EQ gc_gt.
364.     IF lr1->get_sign( ) LT 0.
365.       lr1->invert_sign( ).
366.     ENDIF.
367.     IF lr2->get_sign( ) LT 0.
368.       lr2->invert_sign( ).
369.     ENDIF.
371.     CREATE OBJECT lr_guess
372.       EXPORTING
373.         i_value = ‘1’.
374.     CREATE OBJECT lr_temp.
375.     CREATE OBJECT lr_step.
376.     lv_step = gc_unit_size.
377.     DO.
378.       DO.
379.         lr_temp->multiply( i_obj1 = lr_guess
380.                    i_obj2 = lr2 ).
381.         CASE lr_temp->abs_compare( lr1 ).
382.           WHEN gc_eq.
383.             copy_values( lr_guess ).
384.             RETURN.
385.           WHEN gc_gt.
386.             EXIT.
387.           WHEN gc_lt.
388. * increment
389. *            lv_step = gc_unit_size.
390.             copy_values( lr_guess ).
391.             lv_step_str = lv_step.
392.             lr_step->set_data( lv_step_str ).
393.             lr_guess->multiply( i_obj1 = lr_guess
394.                       i_obj2 = lr_step ).
395. *        when others.
396.        ENDCASE.
397.       ENDDO.
398.       lr_guess->copy_values( me ).
399.       lv_step = SQRT( lv_step ).
400.       IF lv_step EQ 1.
401.         EXIT.
402.       ENDIF.
403.     ENDDO.
404. * answer is between guess and guess * 2
406. *    lv_step = gc_unit_size.
407.     lr_step->copy_values( lr_guess ).
408.     DO.
409.       DO.
410.         lr_temp->multiply( i_obj1 = lr_guess
411.                    i_obj2 = lr2 ).
412.         CASE lr_temp->abs_compare( lr1 ).
413.           WHEN gc_eq.
414.             copy_values( lr_guess ).
415.             RETURN.
416.           WHEN gc_gt.
417.             EXIT.
418.           WHEN gc_lt.
419. * increment
420.             copy_values( lr_guess ).
422.                      i_obj2 = lr_step ).
423. *        when others.
424.         ENDCASE.
425.       ENDDO.
426.       lr_guess->copy_values( me ).
427. * quick way to divide by 2
428.       lr_step->divide_by_2( ).
429.       IF lr_step->get_int( ) IS INITIAL.
430.         EXIT.
431.       ENDIF.
432.     ENDDO.
433. *    int_to_string( ).
434.   ENDMETHOD.                    “divide
435.   METHOD mod.
436.     DATA lr_div TYPE REF TO lcl_bignum.
437.     CREATE OBJECT lr_div.
438.     lr_div->divide( i_obj1 = i_obj1
439.             i_obj2 = i_obj2 ).
440.     lr_div->multiply( i_obj1 = lr_div
441.               i_obj2 = i_obj2 ).
442.     subtract( i_obj1 = i_obj1
443.           i_obj2 = lr_div ).
444.   ENDMETHOD.                    “mod
445.   METHOD power.
446.     DATA: lr_power TYPE REF TO lcl_bignum,
447.         lr_one   TYPE REF TO lcl_bignum.
448.     CREATE OBJECT lr_one
449.       EXPORTING
450.         i_value = ‘1’.
451.     CREATE OBJECT lr_power.
452.     lr_power->copy_values( i_obj2 ).
454. * only positive power
455.     IF get_sign( ) NE lr_power->get_sign( ).
456.       lr_power->invert_sign( ).
457.     ENDIF.
458. * result 1 when power is 0
459.     set_data( ‘1’ ).
460.     DO.
461.       IF lr_power->get_int( ) IS INITIAL.
462.         EXIT.
463.       ELSE.
464.         lr_power->subtract( i_obj1 = lr_power
465.                   i_obj2 = lr_one ).
466.         multiply( i_obj1 = me
467.               i_obj2 = i_obj1 ).
468.       ENDIF.
469.     ENDDO.
470.   ENDMETHOD.                    “power
471. ENDCLASS.                    “lcl_bignum IMPLEMENTATION
472. START-OF-SELECTION.
473.   DATA lr1 TYPE REF TO lcl_bignum.
474.   DATA lr2 TYPE REF TO lcl_bignum.
475.   DATA lr3 TYPE REF TO lcl_bignum.
476.   TRY .
477.       CREATE OBJECT lr1
478.         EXPORTING
479.           i_value = ‘6584389034291301828999’.
480. *          i_negative = ‘X’.
481.       CREATE OBJECT lr2
482.         EXPORTING
483.           i_value = ‘533335555511111’.
484.       CREATE OBJECT lr3.
486.           i_obj2 = lr2 ).
487. *     lr3->subtract( i_obj1 = lr1
488. *                    i_obj2 = lr2 ).
489. *     lr3->multiply( i_obj1 = lr1
490. *                    i_obj2 = lr2 ).
491. *     lr3->divide( i_obj1 = lr1
492. *                  i_obj2 = lr2 ).
493. *     lr3->mod( i_obj1 = lr1
494. *               i_obj2 = lr2 ).
495.       lr1->set_data( ‘512’ ).
496.       lr2->set_data( ’17’ ).
497.       lr3->power( i_obj1 = lr1
498.             i_obj2 = lr2 ).
499.       lr2->set_data( ‘2773’ ).
500.       lr3->mod( i_obj1 = lr3
501.           i_obj2 = lr2 ).
502.     CATCH cx_root.
503.   ENDTRY.

/.

### Assigned Tags

You must be Logged on to comment or reply to a post.

Hello Manish,

I am not sure on which version of SAP you are on; but if you are on releases > 702 you can use the decimal floating types(DECFLOAT) to get faster & more accurate results (ref - http://help.sap.com/abapdocu_740/en/abennews-71-decfloat.htm).

For values beyond maximum limit of integer variable, workarounds can be used.

I would rather use the proper type instead of using some workaround which is not full/fool proof. The choice on which numeric type to use can be made by keeping these points in mind - http://help.sap.com/abapdocu_740/en/abenselect_numeric_type_guidl.htm.

I have used the type DECFLOAT34 to illustrate the calculations (using the numbers you have used in your example) -

DATA:
lv1     TYPE decfloat34,
lv2     TYPE decfloat34,
result  TYPE decfloat34.

lv1 = '6584389034291301828999'.
lv2 = '533335555511111'.

result = lv1 + lv2.
20 result NO-GROUPING. CLEAR result.

*Subtract
result = lv1 - lv2.
WRITE: / `Subtraction:`,
20 result NO-GROUPING. CLEAR result.

*Multiply
result = lv1 * lv2.
WRITE: / `Multiplication:`,
20 result NO-GROUPING. CLEAR result.

*Divide
result = lv1 / lv2.
WRITE: / `Division:`,
20 result NO-GROUPING. CLEAR result.

result = lv1 MOD lv2.
WRITE: / `Modulo:`,
20 result NO-GROUPING. CLEAR result.

Of course there is a limit to the accuracy using this data type as well, i would not argue that. But IMO, SAP is a business software & i'll excuse it if it does not perform calculations with scientific accuracy 😛

BR,

Suhas

Former Member
Blog Post Author

Hi Suhas

Thanks for the illustration. I am also on 702 release.

My motive was to calculate the exact answer for ( 512 ^ 17 ) mod 2773.

Decfloat34 gave 0 answer, and that is why program was written, purely as an academic exercise.

Hi Manish,

Q) Could you please tell me why this was used  in the constructor "split_into_int( )".

Googling for this I have received only replies pertaining to C#.

Q) What is this kind of function called ? And are there others like this.

Thanks.

Former Member
Blog Post Author

Hi Starlet

Split_into_int( ) is a method of same local class lcl_bignum. Code is enclosed between Method Split_into_int and Endmethod statements.

Since we are passing continuous string like '1234567890' during Create Object, that string is broken down into internal table of integers using method split_into_int( ).

After method call, table of integers would have 3 integers, 12(last entry), 3456, 7890 (first entry).

It is a protected instance method of local class. Definition is kept in protected section because method is called only by other methods of same class, and not from outside.

Sorry Manish,

My bad. I did not see the code thoroughly 😥 . Thought it was some ABAP standard functionality or something 🙁 . Silly me

Thanks Manish.

While going through Project Euler, i found this class being very helpful at first. Later on, it showed a bunch of errors and limitations. Thus i rewrote it for elemination of errors, better performance, better usability and extended functionality.

``````class Z04_BIGINT definition
public
create public .

public section.
*"* public components of class Z04_BIGINT
*"* do not include other source files here!!!

types DF type DECFLOAT34 .
types DTYP type F .
types:
dtab type table of dtyp .
types:
tsign type c length 1 .
types OBJREF type ref to Z04_BIGINT .

class-data FZERO type DTYP read-only .
class-data FHALF type DTYP read-only .
class-data FMAXF type DTYP read-only .
class-data ZERO type OBJREF read-only .
class-data ONE type OBJREF read-only .
class-data TWO type OBJREF read-only .
class-data MAXINT type OBJREF read-only .
class-data MAXINT_2 type OBJREF read-only .
class-data FMANTSIZE type I read-only value 15. "#EC NOTEXT .
class-data DFMANTSIZE type I read-only value 34. "#EC NOTEXT .
class-data cdiv type i .
class-data cdivloop type i .
class-data cmul type i .
class-data cmulloop type i .
class-data cget type i .
class-data cset type i .

methods CONSTRUCTOR .
methods SETOBJ
importing
value(SRC) type OBJREF
returning
value(THIS) type OBJREF .
methods SETF
importing
value(SRC) type F
value(EXP) type I default 0
returning
value(THIS) type OBJREF .
methods SETDF
importing
value(SRC) type DF
value(EXP) type I default 0
returning
value(THIS) type OBJREF .
methods SETI
importing
value(SRC) type I
returning
value(THIS) type OBJREF .
methods SETSTR
importing
value(SRC) type STRING
returning
value(THIS) type OBJREF .
methods GETSTR
importing
value(WITH_SIGN) type I default 1
returning
value(S) type STRING .
methods GETF
exporting
value(MNT) type F
value(EXP) type I .
methods GETDF
exporting
value(MNT) type DF
value(EXP) type I .
methods GETSIGN
returning
value(S) type TSIGN .
methods GETCIFCOUNT
returning
value(R) type I .
methods ABS
returning
value(THIS) type OBJREF .
methods NEG
returning
value(THIS) type OBJREF .
methods IS_ZERO
returning
value(R) type I .
methods ABSCMP
importing
!X type OBJREF
returning
value(R) type I .
methods CMP
importing
!X type OBJREF
returning
value(R) type I .
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods SUB
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods MUL
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods DIVX
importing
!X type OBJREF
returning
value(R) type OBJREF
exceptions
DIV_ZERO .
methods DIV
importing
!X type OBJREF
value(USEBUF) type I default 0
returning
value(THIS) type OBJREF
exceptions
DIV_ZERO .
methods MOD
importing
!X type OBJREF
value(USEBUF) type I default 0
returning
value(THIS) type OBJREF
exceptions
DIV_ZERO .
methods POW
importing
!X type OBJREF
returning
value(THIS) type OBJREF
exceptions
NEGATIVE_EXPONENT
TOO_BIG .
methods POWMOD
importing
!X type OBJREF
!M type OBJREF
returning
value(THIS) type OBJREF
exceptions
NEGATIVE_EXPONENT
TOO_BIG .
class-methods CLASS_CONSTRUCTOR .
methods DEBUG .
protected section.
*"* protected components of class Z04_BIGINT
*"* do not include other source files here!!!

data SGN type I .
data DAT type dtab .
class-data CHUNKSIZE type I value 7. "#EC NOTEXT .
class-data DCHUNKSIZE type I value 14. "#EC NOTEXT .
class-data CHUNKMOD type dtyp value `1e7`. "#EC NOTEXT .
class-data DCHUNKMOD type dtyp value `1e14`. "#EC NOTEXT .
class-data O_D type OBJREF .
class-data O_N type OBJREF .
class-data O_Q type OBJREF .
class-data O_R type OBJREF .

importing
value(N) type I
value(C) type C
value(S) type STRING
returning
value(R) type STRING .
private section.
*"* private components of class Z04_BIGINT
*"* do not include other source files here!!!
ENDCLASS.

CLASS Z04_BIGINT IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->ABS
* +-------------------------------------------------------------------------------------------------+
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ABS.
sgn = 1.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->ABSCMP
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ABSCMP.

data: n1 type i,
n2 type i,
i  type i,
v1 type dtyp,
v2 type dtyp.

n1 = lines(    dat ).
n2 = lines( x->dat ).

if ( n1 > n2 ).
r = 1.
return.
endif.
if ( n1 < n2 ).
r = -1.
return.
endif.

do n1 times.
i = n1 - sy-index + 1.
read table    dat into v1 index i.
read table x->dat into v2 index i.
if ( v1 > v2 ).
r = 1.
return.
endif.
if ( v1 < v2 ).
r = -1.
return.
endif.
enddo.

r = 0.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->ADD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
" bei Unterschied im Vorzeichen auf Subtraktion wechseln;
" dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
if ( sgn <> x->sgn ).
x->neg( ).
sub( x ).
x->neg( ).
this = me.
return.
endif.

data: n type i,
nx type i,
result type dtab,
i type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp.

n  = lines( dat ).
nx = lines( x->dat ).
if ( n < nx ). n = nx. endif.

ov = 0.
do n times.
i = sy-index.
" Das Lesen aus den Tabellen ist tolerant gegenüber Wertebereichsüberschreitung des Index.
" Aber in so einem Fall wird das Datenziel nicht genullt.
clear: v1, v2.  " Nullsetzung auf kürzestmögliche Weise
read table    dat into v1 index i.
read table x->dat into v2 index i.
v3 = v1 + v2 + ov.
" Aufbereitungs-Variante mit div/mod:
" => signifikant schneller als if/sub!
" -> Diese Operationen werden offenbar in Hardware durchgeführt.
" -> Sie sind deutlich schneller als elementare ABAP-Operationen.
ov = v3 div chunkmod.
v3 = v3 mod chunkmod.
" Aufbereitungs-Variante mit Vergleich/sub:
*    if ( v3 >= chunkmod ).
*      ov = 1.
*      v3 = v3 - chunkmod.
*    else.
*      ov = 0.
*    endif.
append v3 to result.
enddo.

if ( ov <> 0 ).
append ov to result.
endif.

free dat.
dat = result.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method Z04_BIGINT=>CLASS_CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CLASS_CONSTRUCTOR.
fmaxf = '1.5e308'.
fzero = 0.
fhalf = '0.5'.
create object: zero, one, two, maxint, o_d, o_n, o_r, o_q.", maxint_2
zero->seti( 0 ).
one->seti( 1 ).
two->seti( 2 ).
maxint->seti(   2 ** 31 - 1 ).
"maxint_2->seti( 2 ** 30 - 1 ).
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->CMP
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CMP.

" Hier wird eine sophistische Vorzeichenbehandlung angesetzt,
" die auch vorzeichenbehaftete Nullen einbezieht.
" Wenn eine negative Zahl zu Null verkleinert wurde - etwa durch Division -,
" wird diese als kleiner als eine positive zu Null verkleinerte Zahl betrachtet.

data: iszero1 type i,
iszero2 type i.

iszero1 = is_zero( ).
iszero2 = x->is_zero( ).

if ( iszero1 + iszero2 = 2 ).
r = 0.
return.
endif.
if ( sgn = +1 and x->sgn = -1 ).
r = +1.
return.
endif.
if ( sgn = -1 and x->sgn = +1 ).
r = -1.
return.
endif.

data: cmp type i.
cmp = abscmp( x ).

if ( cmp > 0 ).
if ( sgn = 1 ).
r = +1.
else.
r = -1.
endif.
elseif ( cmp < 0 ).
if ( sgn = 1 ).
r = -1.
else.
r = +1.
endif.
else.
r = 0.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CONSTRUCTOR.
sgn = 1.
append fzero to dat.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->DEBUG
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DEBUG.
data: t type dtyp,
cs type c.

cs = getsign( ). write: / cs.
loop at dat into t.
write: / t.
endloop.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->DIV
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] USEBUF                         TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DIV.
if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
me->setobj( o_q ).
this = me.
return.
endif.

o_d->setobj( me ).
o_n->setobj( x ).
o_r = divx( x ).
o_q->setobj( me ).

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->DIVX
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DIVX.
data: z   type objref,  " Zwischenergebnis eines Näherungsschritts
mr  type dtyp,  " Mantisse Rest
er  type i,     " Exponent Rest
mx  type dtyp,  " Mantisse Nenner (X)
ex  type i,     " Exponent Nenner (X)
mv  type dtyp,  " Mantisse Verhältnis
ev  type i,     " Exponent Verhältnis
eva type i,     " Verhältnis-Exponent, der direkt als Nullen eingesetzt wird
evb type i.     " Verhältnis-Exponent, der über setf eingesetzt wird

cget = 0.
cset = 0.
cmul = 0.
cmulloop = 0.
cdivloop = 0.

create object: r, z.

r->setobj( me ).
me->setobj( zero ).

x->GETF( IMPORTING MNT = mx EXP = ex ).

cget = cget + 1.

while ( r->abscmp( x ) >= 0 ).

r->GETF( IMPORTING MNT = mr EXP = er ).
" Das im folgenden ermittelte Verhältnis ist eine Schätzung auf etwa 15 Stellen Genauigkeit.
" Die kann etwas zu klein, aber auch etwas zu groß ausfallen.
" Dies ist kein Problem: Der Rest darf zwischendurch ruhig das Vorzeichen wechseln,
" er wird trotzdem immer in Richtung kleiner werdender Beträge abgebaut.
" Wenn wir am Punkt angekommen sind, dass der Rest-Betrag kleiner als x geworden ist,
" müssen wir nochmal testen, ob der Rest das selbe Vorzeichen hat wie zu Anfang
" (was identisch ist zum Produkt der Vorzeichen von Quotient und Nenner).
" Falls nicht, muss der Quotient um ein 1 und der Rest um x korrigiert werden.

mv = mr / mx.
ev = er - ex.
z->setf( src = mv exp = ev ).
r->sub( z->mul( x ) ).

cdivloop = cdivloop + 1.
cget = cget + 1.
cset = cset + 1.
cmul = cmul + 1.

endwhile.

" Rest-Underflow?
" -> Korrektur des Quotienten um -1 relativ zu seinem Vorzeichen
"     und des Rests um jene Quotientenkorrektur mal -x
" (Um die Faktoren besser lesbar auseinanderzuhalten, wird das aktuelle Objekt,
" welches den Quotienten darstellt, explizit mit "me->" gekennzeichnet.)
if ( r->sgn <> me->sgn * x->sgn ).
z->seti( me->sgn ).
me->sub( z ).

cset = cset + 1.
cmul = cmul + 1.
endif.

*  write: / 'divx get count:',cget.
*  write: / 'divx set count:',cset.
*  write: / 'divx mul count:',cmul.
*  write: / 'divx mulloop count:',cmulloop.
*  write: / 'divx divloop count:',cdivloop.

free z.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->GETCIFCOUNT
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETCIFCOUNT.
data: n type i,
v type dtyp,
s type string.

n = lines( dat ).
r = ( n - 1 ) * chunksize.
read table dat into v index n.
r = r + ceil( log10( v + fhalf ) ).
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->GETDF
* +-------------------------------------------------------------------------------------------------+
* | [<---] MNT                            TYPE        DF
* | [<---] EXP                            TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETDF.
data: i     type i,
imin  type i,
v     type dtyp.

mnt = 0.
exp = 0.
i = lines( dat ).
imin = nmax( val1 = 1 val2 = i - 5 ).
while ( i >= imin ).
read table dat into v index i.
mnt = mnt * chunkmod + v.
i = i - 1.
endwhile.
if ( imin > 1 ).
exp = i * chunksize.
endif.
mnt = mnt * sgn.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->GETF
* +-------------------------------------------------------------------------------------------------+
* | [<---] MNT                            TYPE        F
* | [<---] EXP                            TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETF.
data: i     type i,
imin  type i,
v     type dtyp.

mnt = 0.
exp = 0.
i = lines( dat ).
imin = nmax( val1 = 1 val2 = i - 2 ).
while ( i >= imin ).
read table dat into v index i.
mnt = mnt * chunkmod + v.
i = i - 1.
endwhile.
if ( imin > 1 ).
exp = i * chunksize.
endif.
mnt = mnt * sgn.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->GETSIGN
* +-------------------------------------------------------------------------------------------------+
* | [<-()] S                              TYPE        TSIGN
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETSIGN.
if ( sgn = 1 ).
s = '+'.
else.
s = '-'.
endif.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->GETSTR
* +-------------------------------------------------------------------------------------------------+
* | [--->] WITH_SIGN                      TYPE        I (default =1)
* | [<-()] S                              TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETSTR.
data: v type dtyp,
st type string,
n type i.

n = lines( dat ).
loop at dat into v.
st = |{ v }|.
"write: / ' getstr:',st.
if ( sy-tabix < n ).
s = strpad( s = st n = chunksize c = '0' ) && s.
else.
s = st && s.
endif.
endloop.

if ( with_sign = 1 ).
s = getsign( ) && s.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->IS_ZERO
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method IS_ZERO.

if ( lines( dat ) > 1 ).
r = 0.
return.
endif.

data d type dtyp.

read table dat into d index 1.
if ( d > 0 ).
r = 0.
else.
r = 1.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->MOD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] USEBUF                         TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method MOD.
if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
me->setobj( o_r ).
this = me.
return.
endif.

o_d->setobj( me ).
o_n->setobj( x ).
o_r = divx( x ).
o_q->setobj( me ).
me->setobj( o_r ).

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->MUL
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method MUL.
sgn = sgn * x->sgn.

data: n1 type i,
n2 type i,
result type dtab,
i1 type i,
i2 type i,
i3 type i,
i1min type i,
i3max type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp,
vr type dtyp.

if ( is_zero( ) + x->is_zero( ) = 1 ).
clear dat.
append fzero to dat.
this = me.
return.
endif.

n1 = lines( dat ).
n2 = lines( x->dat ).

" i3 gibt die Zielstelle an.
" Es sind alle i1 + i2 = i3 zu durchlaufen!
" Nullbasiert wäre das:
"   i1 = i1min...i3   i1min = max( 0, i3 - n2 )
"   i2 = i3 - i1
" Das bei SAP 1-basiert indiziert wird,
" muss an den richtigen Stellen noch +1 ergänzt werden!
"
" Es ist zu bedenken, dass die Menge der bearbeiteten chunks SEHR groß sein kann!
" Das resultiert in großem Wertebereich für den Overflow!
" Der ist halt nicht bloß eine eventuelle "1", sondern nimmt maximal den Wert
" der Menge der beteiligten chunks an!
" Wir haben bei den Zwischen-Additionen also große Zahlen
" aus drei chunks zu verarbeiten.
" Die chunksize ist so bemessen, dass das Ergebnis einer Multiplikation
" plus eine Überlaufstelle im zugrundeliegenden chunk-Datentyp aufgenommen werden kann.
i3max = ( n1 + n2 - 1 ).
i3 = 1.
do i3max times.

i1min = nmax( val1 = 1 val2 = i3 - n2 + 1 ).
i1 = i1min.
i2 = i3 - i1 + 1.

while ( i1 <= n1 and i2 > 0 ).
" Da wir hier explizit nur Elemente durchgehen, die tatsächlich existieren,
" werden alle read tables erfolgreich durchgehen.
" Das Nullen der Zielwerte können wir uns deshalb ersparen.
read table    dat into v1 index i1.
read table x->dat into v2 index i2.

"data s  type string.
"s = `  1: ` && v1 && `[` && i1 && `]  2: ` && v2 && `[` && i2 && `]`.
"write: / s.

v3 = v3 + v1 * v2.
ov = ov + v3 div dchunkmod.
v3 =      v3 mod dchunkmod.
i1 = i1 + 1.
i2 = i2 - 1.

cmulloop = cmulloop + 1.

endwhile.

vr = v3 mod chunkmod.
v3 = v3 div chunkmod + ov * chunkmod.
ov = fzero.
append vr to result.

i3 = i3 + 1.
enddo.
" Im Ergebnis wird es nie mehr Stellen geben als in den letzten v3 gepasst haben.
" v3 wird im letzten Schleifendurchlauf innerhalb chunkmod bleiben.

if ( v3 <> 0 ).
append v3 to result.
endif.

free dat.
dat = result.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->NEG
* +-------------------------------------------------------------------------------------------------+
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method NEG.
sgn = - sgn.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->POW
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] NEGATIVE_EXPONENT
* | [EXC!] TOO_BIG
* +--------------------------------------------------------------------------------------</SIGNATURE>
method POW.
if ( x->cmp( zero ) < 0 ).
raise NEGATIVE_EXPONENT.
endif.

" Wir haben einen Ziel-Speicherbedarf von: Originalgröße * x.
" Zwischendurch haben wir denselben Speicherbedarf für Zwischenwerte nochmal.
" Wobei nicht dokumentiert ist, wieviele Elemente ein Array alias interne Tabelle
" halten kann. Denkbar ist ohne weiteres, dass dies auf 64-bit-Systemen ausschließlich
" durch die Adressierbarkeit der Array-Blöcke und deren Begrenzung durch den
" Integer-Wertebereich der Indizes gegeben ist.
" Außerdem ist nicht dokumentiert, wieviel RAM eine Session verwalten kann.
" Ich setze hier also erstmal an, dass ich - falls nicht ein Speicherüberlauf
" vom Betriebssystem kommt - bis zu maxint Blöcke adressieren können sollte.
"
" Letztlich können wir hier eh nur eine Abschätzung geben.
" Ob und wann eine Exception kommt, entscheidet die Laufzeitumgebung und die
" konkrete Füllung der Argumente.
" => Die folgende sehr grobe Schätzung wurde wieder deaktiviert.
"
"is_too_big = x->cmp( maxint_2 ).
"if ( is_too_big > 0 ).
"  raise TOO_BIG.
"endif.

data: cifcount type i,
is_too_big type i,
exptmp  type i,
expmax  type i,
exp     type i,
ei      type i,
facs    type table of objref,
fac     type objref,
factmp  type objref.

try.
"------------------------
" Potenz-Faktoren anlegen
"------------------------
create object factmp.
factmp->setobj( me ).

exptmp = 1.
exp = x->getstr( ).
expmax = exp div 2.
"write: / '  exp:',exp,', expmax:',expmax.

create object fac.
fac->setobj( factmp ).
append fac to facs.

while ( exptmp <= expmax ).
create object fac.
fac->setobj( factmp ).
fac->mul( fac ).
exptmp = exptmp + exptmp.
append fac to facs.
factmp->setobj( fac ).

"data s type string.
"s = fac->getstr( ).
"write: / '  faktor:',s.

endwhile.

"loop at facs into fac.
"  s = '  facs[' && sy-tabix && ']:' && fac->getstr( ).
"  write: / s.
"endloop.

"------------------------
" Potenz bilden
"------------------------
factmp->seti( 1 ).
"exptmp wird aus der Init-Schleife übernommen
ei = lines( facs ).
while ( exp > 0 ).
read table facs into fac index ei.
if ( exptmp <= exp ).
factmp->mul( fac ).

"s = '  fac:' && fac->getstr( ) && ', exptmp:' && exptmp && ', exp:' && exp.
"write: / s.

exp = exp - exptmp.
endif.
exptmp = exptmp div 2.
ei = ei - 1.
endwhile.

setobj( factmp ).
loop at facs into fac.
free fac.
endloop.
free factmp.

catch cx_root.
if ( not factmp is initial ).
loop at facs into fac.
free fac.
endloop.
free factmp.
endif.
raise TOO_BIG.
endtry.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->POWMOD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] M                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] NEGATIVE_EXPONENT
* | [EXC!] TOO_BIG
* +--------------------------------------------------------------------------------------</SIGNATURE>
method POWMOD.
" Restklassen-Rechnung erlaubt, den numerischen Aufwand
" bei Multiplikation und Potenzierung krass zu reduzieren.
"   .
" - Multiplikation einer Zahl mit einem beliebigen Faktor modulo m:
"       z mod m = (    x * m +     r) mod m =     r
"   k * z mod m = (k * x * m + k * r) mod m = k * r
"   .
" - auch der Faktor kann modulo m reduziert werden:
"       a = xa * m + ra
"       b = xb * m + rb
"   a * b mod m = (xa*xb * m^2 + (xa*rb + xb*ra) * m + ra*rb) mod m = ra*rb
"   .
" -> Da in einer Restklassenbetrachtung das k*x*m völlig wurscht ist,
" kann man sich, solange man nur mit ganzen Zahlen k rechnet, das k*x*m klemmen.
" In jedem Multiplikations-Rechenschritt. Indem man stets auf die Restklasse reduziert.
" .
" Das machen wir hier.
" Dadurch fallen alle Beschränkungen des Exponenten.
" Die Prozedur ist kopiert vom normalen pow.

data: cmp type i.

cmp = x->cmp( zero ).
if ( cmp < 0 ).
raise NEGATIVE_EXPONENT.
endif.

" Extrabehandlung des Falls x = 0 (passt nicht in die Logik weiter unten)
if ( cmp = 0 ).
me->setobj( one ).
this = me.
return.
endif.

data: cifcount type i,
is_too_big type i,
exp     type objref,
exptmp  type objref,
en      type i,
ei      type i,
facs    type table of objref,
exps    type table of objref,
fac     type objref.

try.
"------------------------
" Potenz-Faktoren anlegen
"------------------------
" Die Zweierpotenzreihe zu speichern ist effektiver als sie
" beim Abwärtswandern der Faktorenliste auszudividieren.
create object: exptmp.

exptmp->setobj( one ). " me = original me ^ 1
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse

while ( exptmp->cmp( x ) <= 0 ).
create object fac. fac->setobj( me ).
create object exp. exp->setobj( exptmp ).
append fac to facs.
append exp to exps.
me->mul( me ).          " me = me^2 = original me ^ exp
exptmp->add( exptmp ).  " exp = 2 ^ schleifenindex
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
endwhile.

" debugging...
"data s type string.
"ei = lines( facs ).
"if ( ei > 0 ).
"  write: / 'potenz-faktoren:'.
"  while ( ei > 0 ).
"    read table facs into fac index ei.
"    read table exps into exp index ei.
"    s = exp->getstr( ) && ' -> ' && fac->getstr( ).
"    write: / s.
"    ei = ei - 1.
"  endwhile.
"endif.

"------------------------
" Potenz bilden
"------------------------
me->setobj( one ).
exptmp->setobj( x ).
en = lines( facs ).
ei = en.
do en times.
if ( exptmp->cmp( zero ) <= 0 ).
exit.
endif.
read table facs into fac index ei.
read table exps into exp index ei.
cmp = exp->cmp( exptmp ).
if ( cmp <= 0 ).
me->mul( fac ).
exptmp->sub( exp ).
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
if ( cmp = 0 ).
exit.
endif.
endif.
ei = ei - 1.
enddo.

loop at facs into fac. free fac. endloop.
loop at exps into exp. free exp. endloop.
free exptmp.

catch cx_root.
if ( not exptmp is initial ). free exptmp. endif.
loop at facs into fac. free fac. endloop.
loop at exps into exp. free exp. endloop.
raise TOO_BIG.
endtry.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SETDF
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        DF
* | [--->] EXP                            TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETDF.
data: val   type df,
rest  type df,
t     type df,
xexp  type i, " Zehnerpotenz des Quellwertes
texp  type i, " Zehnerpotenz-Übertrag in/aus Nullenkette
mexp  type i,
nzch  type i, " Anzahl der abschließend zu ergänzenden Null-Chunks
izch  type i.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

" Der reinkommende float wird dezimal so zurechtgerückt,
" dass er nach Möglichkeit gerade genau dfmantsize Ziffern enthält.
" wobei die kleinste Ziffer den Stellenwert 1 haben soll.
" Dazu wird die Zehnerpotenz korrigiert, indem überflüssige Zehnerpotenzen
" nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
" Nebenbei wird dafür gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
" ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
" geradlinig schlicht als Null-Chunks angehängt werden können.
"-----------------
"xexp = ceil( log10( val ) ).
" Die Funktion log10 ist für decfloats EXTREM aufwendig implementiert
" (rund 500 mikrosec -> das mehr als 1000-fache des Wertes für float).
" div (und powmod als abhängige Funktion) werden dadurch extrem ausgebremst.
xexp = CL_ABAP_MATH=>GET_NUMBER_OF_DIGITS( floor( val ) ).

texp = xexp - dfmantsize. " durch 10 ** texp wird val nachher dividiert
exp = exp + texp.         " ...und diese Nullen später direkt angehängt
if ( exp < 0 ).
texp = texp - exp.
exp = 0.
endif.
mexp = exp mod chunksize.
nzch = exp div chunksize.
texp = texp - mexp.

"write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.

" Zehnerpotenz-Korrektur und Rundung (auf höchstens runter bis Stellenwert 1)
val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - dfmantsize ) ) ) .

clear dat.
if ( nzch > 0 ).
append fzero to dat.
izch = 1.
nzch = nzch - izch.
while ( nzch >= izch ).
append lines of dat from 1 to izch to dat.
nzch = nzch - izch.
izch = izch + izch.
endwhile.
if ( nzch > 0 ).
append lines of dat from 1 to nzch to dat.
endif.
endif.

while ( val >= chunkmod ).
"rest = val mod chunkmod.
"val  = val div chunkmod.
t = val div chunkmod.
rest = val - t * chunkmod.
val = t.

append rest to dat.
endwhile.
append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SETF
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        F
* | [--->] EXP                            TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETF.
data: val   type f,
rest  type f,
xexp  type i, " Zehnerpotenz des Quellwertes
texp  type i, " Zehnerpotenz-Übertrag in/aus Nullenkette
mexp  type i,
nzch  type i, " Anzahl der abschließend zu ergänzenden Null-Chunks
izch  type i.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

" Der reinkommende float wird dezimal so zurechtgerückt,
" dass er nach Möglichkeit gerade genau fmantsize Ziffern enthält.
" wobei die kleinste Ziffer den Stellenwert 1 haben soll.
" Dazu wird die Zehnerpotenz korrigiert, indem überflüssige Zehnerpotenzen
" nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
" Nebenbei wird dafür gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
" ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
" geradlinig schlicht als Null-Chunks angehängt werden können.
xexp = ceil( log10( val ) ).
texp = xexp - fmantsize.  " durch 10 ** texp wird val nachher dividiert
exp = exp + texp.         " ...und diese Nullen später direkt angehängt
if ( exp < 0 ).
texp = texp - exp.
exp = 0.
endif.
mexp = exp mod chunksize.
nzch = exp div chunksize.
texp = texp - mexp.

"write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.

" Zehnerpotenz-Korrektur und Rundung (auf höchstens runter bis Stellenwert 1)
val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - fmantsize ) ) ) .

clear dat.
if ( nzch > 0 ).
append fzero to dat.
izch = 1.
nzch = nzch - izch.
while ( nzch >= izch ).
append lines of dat from 1 to izch to dat.
nzch = nzch - izch.
izch = izch + izch.
endwhile.
if ( nzch > 0 ).
append lines of dat from 1 to nzch to dat.
endif.
endif.

while ( val >= chunkmod ).
rest = val mod chunkmod.
"val  = val div chunkmod.
" Die Operation "div" ist EIGENTLICH das zu "mod" passende Komplement.
" Sie liefert allerdings Nachkommastellen, wenn die Zahl ursprünglich Nachkommastellen enthält
" (so, dass (x div y)*y+(x mod y) == x).
" Eigentlich kommt val hier gerundet rein, aber beim Dividieren durch chunkmod
" können Rundungsfehler auftreten.
" mod arbeitet aber nur wie gebraucht, wenn eben KEINE Nachkommastellen vorhanden sind.
" Deshalb wird hier die aufwendigere Form mit floor gewählt.
val = floor( val / chunkmod ).
append rest to dat.
endwhile.
append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SETI
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        I
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETI.
clear dat.

data: val type f,
rest type f.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

if ( val >= chunkmod ).
rest = val mod chunkmod.
append rest to dat.
val = val div chunkmod.
endif.
append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SETOBJ
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETOBJ.
dat = src->dat.
sgn   = src->sgn.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SETSTR
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        STRING
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETSTR.
clear dat.

data: ofs type i,
mpart type string,
xpart type string,
ipart type string,
fpart type string,
chunk type f,
ilen type i,
flen type i,
sc type c,
x type i.

"write: / '  originale src:', src.

" Entschrottung, Vereinfachung und Zerlegung
"-----------------------
condense src.
translate src to lower case.
" to do: Prüfung mit regex
split src   at 'e' into mpart xpart.
split mpart at '.' into ipart fpart.
ilen = strlen( ipart ).
flen = strlen( fpart ).

"write: / '  parts:', ipart,',', fpart,' * 10^ ', xpart.

" Vorzeichen-Erkennung
"-----------------------
sgn = +1.
if ( ilen > 0 ).
sc = ipart(1).
if ( sc = '-' or sc = '+' ).
if ( sc = '-' ). sgn = -1. endif.
ipart = ipart+1.
ilen = ilen - 1.
endif.
endif.
"write: / '  ipart nach Vorzeichenerkennung:', ipart.

" Zehnerpotenz-Erkennung
"-----------------------
x = xpart.
"write: / '  Zehnerpotenz / originale Länge: ',x,ilen.
ilen = ilen + x.
"write: / '  Zehnerpotenz / korrigierte Länge: ',ilen.
if ( x < 0 ).
if ( ilen < 1 ).
ilen = 1.
ipart = '0'.
endif.
"write: / '  negative Zehnerpotenz -> verbleibende Länge: ',ilen.
ipart = ipart(ilen).
elseif ( x > 0 ).
if ( x < flen ).
ipart = ipart && fpart(x).
else.
x = x - flen.
ipart = ipart && fpart && repeat( val = '0' occ = x ).
endif.
endif.
"write: / '  ipart nach Zehnerpotenz-Erkennung:', ipart.

" Integer-Zerlegung
"-----------------------
while ( ilen > 0 ).
ilen = ilen - chunksize.
if ( ilen < 0 ).
ilen = ilen + chunksize.
chunk = ipart(ilen).
append chunk to dat.
exit.
endif.
chunk = ipart+ilen(chunksize).
append chunk to dat.
endwhile.

this = me.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Protected Method Z04_BIGINT=>STRPAD
* +-------------------------------------------------------------------------------------------------+
* | [--->] N                              TYPE        I
* | [--->] C                              TYPE        C
* | [--->] S                              TYPE        STRING
* | [<-()] R                              TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
data: d type i.
d = n - strlen( s ).
if ( d < 0 ). r = s. return. endif.

" repeat ist 10% schneller als das Ausschneiden aus einem vorbereiteten String...
r = repeat( val = '0' occ = d ) && s.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINT->SUB
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SUB.
" bei Unterschied im Vorzeichen auf Addition wechseln;
" dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
if ( sgn <> x->sgn ).
x->neg( ).
x->neg( ).
this = me.
return.
endif.

" Den größeren Wert vom kleineren Wert abziehen
" (Alternative wäre blind modulo zu subtrahieren und zum Schluss
" bei verbleibendem Überlauf das Ergebnis nochmal komplett zu negieren.
" Das ist allerdings mit mehr Aufwand verbunden als beim Tausch vorweg.)
data: cmp type i.

field-symbols:
<d1> type dtab,
<d2> type dtab.

" ...Dazu den Fall Null extra behandeln, weil der recht häufig vorkommen kann
"   und besonders fix über die Bühne zu bringen ist...
cmp = abscmp( x ).
if ( cmp = 0 ).
clear dat.
append fzero to dat.
this = me.
return.
endif.

if ( cmp < 0 ).
assign dat    to <d2>.
assign x->dat to <d1>.
neg( ).
else.
assign dat    to <d1>.
assign x->dat to <d2>.
endif.

" Letztlich die eigentliche Subtraktion durchführen
data: n type i,
result type dtab,
i type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp.

n  = lines( <d1> ).

" ov = 0. " überflüssig
do n times.
i = sy-index.
" Das Lesen aus den Tabellen ist tolerant gegenüber Wertebereichsüberschreitung des Index.
" Aber in so einem Fall wird das Datenziel nicht genullt.
clear: v1, v2.  " Nullsetzung auf kürzestmögliche Weise
read table <d1> into v1 index i.
read table <d2> into v2 index i.
v3 = v1 - v2 + ov.

" das folgende ist signifikant effektiver als ein if/sub
ov = v3 div chunkmod. " Überlauf ist hier -1
v3 = v3 mod chunkmod.

append v3 to result.
enddo.
" ...Ein Überlauf nach Abschluss kann wegen vorherigem Sortieren nicht auftreten.

" Führende Nullen löschen
n = lines( result ).
do n times.
i = n - sy-index + 1.
read table result into v1 index i.
if ( v1 <> fzero ).
exit.
endif.
enddo.
" -> alles von i bis n löschen! Aber nur, wenn i > 1 ist!
delete result from i + 1." to n.

free dat.
dat = result.

this = me.
endmethod.
ENDCLASS.``````

class documentation:

CL Z04_BIGINT

____________________________________________________

Kurztext

Big Integer Arithmetik auf Float-Arrays

Funktionalität

Stellt BigInteger-Arithmetik auf Basis von internem floating point array bereit.

Kann bis zu Speicherlimit/Typlimit Zahlenlänge verarbeiten (Begrenzung nur durch die Maximalgröße einer internen Tabelle für Floats bzw. den Integer-Wertebereich für die Indizierung von Ziffern).

Philosophie der Schnittstellen-Organisation ist, dass - außer in "get..." und "cmp..."-Funktionen - immer das aktuelle Objekt manipuliert wird, gegebenenfalls unter Hinzuziehung eines weiteren Objekts. Das heißt, dass der erste Operand aller Operationen immer das aktuelle Objekt ist. Das bedeutet weiterhin, dass alle Operationen mit zwei Operanden (add, mul, ...) - der erste davon immer das aktuelle Objekt - inkrementell auf das aktuelle Objekt wirken.

Zusammen mit dem Umstand, dass alle Operationen, die das aktuelle Objekt verändern, eine Referenz auf das aktuelle Objekt liefern, bedeutet dies, dass Operationen nahtlos verkettet werden können:

a->seti( 1 )->add( b )->mul( c ).

Eine Ausnahme davon stellt "divx()" dar: Dieses kann den Rest als return-Argument zurückliefern, manipuliert aber auch inkrementell das aktuelle Objekt. Der Name bekommt deshalb keine Vorsilbe GET, aber nach "->divx()" kann nicht nahtlos weiter verkettet werden. Für letzteres gibt es "div()" und "mod()" getrennt. "div()" und "mod()" benutzen intern "divx()". "divx" puffert statisch den Rest. Dieser kann bei einem anschließenden "mod()" mit denselben Argumenten unmittelbar danach ohne Neuberechnung abgerufen werden.

Weiterhin stellen die Vergleichsfunktionen ("cmp()" und "abscmp()") eine Ausnahme von dieser Regel dar. Die Vorsilbe "get" würde bei denen einfach albern wirken. Der Vergleich liefert "-1", wenn das aktuelle Objekt kleiner ist als das Argument "x", "+1" wenn größer und "0" wenn gleich.

Es gibt eine weitere Klasse "z04_bigintx" (mit Suffix "x"), die mit DecFloat34 arbeitet ud Zahlen mit mehr als 100 Ziffern deutlich schneller verarbeiten kann. Die hier vorliegende Klasse "z04_bigint" ist also eher für "kleine große" Zahlen geeignet, während "z04_bigintx" für "echt große große" Zahlen sinnvoll ist.

Beispiel

data: a type ref to BigInt,

b type ref to BigInt,

s type string.

create object a, b.

a->setstr( '+0123e200' ).

s = b->setstr( '-0123e200' )->add( a )->getstr( ).

write: / s. " liefert: 0

Hinweise

Setter / Getter:

Es gibt eine ganze Palette von Funktionen zum Einstellen und Abfragen eines Wertes - aus Performance-Gründen: Ein Teil dieser Funktionen wird intern für die Implementierung eingesetzt. Sie unterscheiden sich von anderen Funktionen durch einen Präfix "set" bzw. "get". Sie unterscheiden sich untereinander durch ein Suffix, welches den Typ des Arguments kennzeichnet (weil von SAP bisher kein Überladen für nutzerdefinierte Funktionen bereitgestellt wird). Die schnellsten davon sind die für Integers (mit "i" als Suffix) bzw. Floats (mit "f" als Suffix). Für alle nicht explizit unterstützten Zahlenformate ist eine gemeinsame Schnittstelle über String ("setstr()" / "getstr()") vorgesehen.

"setstr()" versteht Vorzeichen ("+" und "-") und Zehnerpotenzen in der üblichen Notationsform ('+123e100') und toleriert führende Nullen sowie Leerzeichen um das ganze drumrum (' -00001234e567 ' ).

Alle anderen Setter (mit Zahlen-Argument) können optional einen extra Integer als Zehner-Exponent aufnehmen.

"setf()" ist nicht wirklich für die Übergabe sehr großer Zahlen vorgesehen, denn solche float-Werte haben zu Ganzahlen gewandelt eh nur einen Bruchteil ihrer Stellen (die höchsten 15) sinnvoll gesetzt. Der Rest ist Schrott oder Null. Mit "setf()" soll lediglich eine vereinfachte Übernahme von floats bis maximal 10^15 ermöglicht werden, wenn diese ursprünglich als float vorliegen. Dies wird unter anderem intern für das Einstellen von Schätzwerten beim Dividieren genutz. Falls also viele hängende Nullen übergeben werden sollen, ist dazu das optionale Argument "exp" zu nutzen!

Analoges gilt für "setdf()".

Die Funktionen "getf()" und "getdf()" liefern 8-byte-floats bzw. 16-byte-decfloats als Mantisse und einen extra Integer als Zehner-Exponent zurück. Letzteres ist notwendig, weil die Exponentenbereiche gewöhnlicher floats nur für winzige Bigints ausreichen (lediglich für ein paar hundert bis ein paar tausend Stellen). Die Mantissen stellen einen Näherungswert mit mindestens 15 bzw. 29 Dezimalstellen dar.

Alle Getter liefern den abgefragten Wert zurück und unterbrechen damit die Verkettbarkeit.

Alle Setter liefern eine Referenz auf das aktuelle Objekt zurück und können somit bevorzugt zum Start von Verkettungssequenzen genutzt werden.

=============================

Ausnahmen von der syntaktischen Systematik:

Die Syntax-Ausnahme beim "divx()" ist gewollt. Es soll dort verwendet werden, wo beim Dividieren der Rest sofort gegriffen werden soll. Ist dagegen nahtloses Verketten gewünscht, kann "div()" zusammen mit einem späteren "mod()" genutzt werden, ohne dass für letzteres die Berechnung wiederholt wird. Der Rest wird intern statisch zwischengespeichert. Wenn allerdings zwischen einem "div()" und dem dazugehörenden "mod()" eine andere Operation "div()" oder "divx()" (mit anderen Operanden) aufgerufen wurde, wird beim Aufruf von "mod()" die Division erneut ausgeführt. Wenn der Programmierer hier Blödsinn baut, hat er vom internen Zwischenspeichern nichts. Die Ergebnisse sind dennoch immer korrekt.

Es könnte zusätzlich auch Funktionen mit zwei Operanden geben (Präfix "e"), die jeweils alle ihre Operanden als Argumente übernehmen. Das Beispiel aus dem einleitenden Abschnitt mit dieser Schnittstellenvariante sähe so aus:

a->emul( x = a->eadd( x = a->seti( 1 ) y = b ) y = c ).

-> Die Syntax wäre auf jeden Fall aufwendiger und bei Verkettungen schlechter lesbar. Momentan sehe ich keinen Anlass, dies zu implementieren.

=============================

Warum benutzt Du nicht einfach den Typ "packed" mit 16 Bytes? Oder "DecFloat34"?

Weil deren Wertebereich nur bis 31 bzw. 34 Dezimalstellen reicht. Schon für mathematische Aufgaben auf Anfänger-Niveau (vor Beginn eines Mathematik-Studiums, nur bis Klasse 12/13) sind größere Wertebereiche zwingend notwendig. Schon Mathematiker bei den alten Arabern und im europäischen Mittelalter haben mit größeren Zahlen experimentiert (die sie damals von Hand berechneten - das waren echte Enthusiasten!) . Wer sich für solche Experimente nicht begeistern möchte, möge die Finger davon lassen. Wer in die Fußstapfen der Entwickler von moderner Verschlüsselung treten möchte oder sich sonstwie für alle möglichen mathematischen Fragestellungen interessiert, muss sich zwingend auch auf solche akademischen Zahlenexperimente mit großen Zahlen einlassen. 4k-RSA etwa braucht für normale Funktion Zahlen mit etwa 1200 Dezimalstellen (in üblichen "richtigen" Computern werden solche 4-K-RSA-Faktoren als 500 Byte-Werte gespeichert, aber SAP stellt Bytes nur über Umwege bereit, die sich nur marginal für direkte Verarbeitung eignen und auch dies nur in sehr kleinen Blöcken (4 Byte)). Mit DecFloat34 könnte man etwa gerade so die Berechnung von veralteten 64-bit-Hash-Verfahren nachstellen (bzw. entwickeln). Natürlich wird man echte Krypto-Bibliotheken niemals in ABAP schreiben - aus besten auf der Hand liegenden Gründen. Aber ABAP - wenn man es nun schon mal verpflichtend als Sprache im beruflichen Alltag verwenden muss - lässt sich nichtsdestotrotz für Experimente - zum Lernen und Forschen - perfekt einsetzen. Wenn man es ein wenig aufbohrt.

=============================

Performance:

ABAP verbrät pro elementarem Statement der Sprache größenordnungsmäig 100 bis 200 CPU-Takte (analog etwa PHP). Das entspricht auf derzeitigen CPUs einigen zig Nanosekunden pro simpelster Anweisung. Da jede BigInt-Funktion - selbst im einfachsten Fall etwa der Addition - aus mindestens um 10 elementaren Anweisungen einschließlich Argumenttests und Sonderfallbehandlung besteht, ist keine BigInt-Funktion unter ein paar hundert Nanosekunden zu bekommen. Dazu kommen Operationswiederholungen proportional der Menge der chunks in den Zahlen.

Der Aufwand für Add/Cmp/Set/Get wächst linear mit der Zahlengröße.

Der Aufwand für Mul wächst quadratisch.

Der Aufwand für Pow wächst quadratisch zur Basis / linear zum Exponenten.

Der Aufwand für Div/Mod/PowMod wächst kubisch.

Weiterführende Informationen

Anwendungsbereiche: akademische Mathematik, wissenschaftliche Berechnungen, mathematische Experimente, Erlernen der Sprache ABAP, "Euler Project" ab Aufgabe 13:

https://projecteuler.net/archives

=============================

Anregungen zur Bibliothek: Manish Kumar "Integer Arithmetic Operations on Large Numbers"

https://blogs.sap.com/2013/09/09/integer-arithmetic-operations-on-large-numbers/

Dieser gegenüber fehlerbereinigte und durch Nutzung von floats statt integers stark beschleunigte Operationen (Addition/Multiplikation: 3-fach, Division: 1000-fach, Potenz: zig bis zigmilliardenfach - je nach Zahlengröße).

Hi,

can you share the class code that handle the very very large numbers Z04_bigintx

Disclaimer: Unfortunately, i'm currently not using any SAP system, so this post is from an archive, with no maintenance since two years. I had successfully used it up to euler project / problem 67. No guarantee to be error free beyond that application.

``````class Z04_BIGINTX definition
public
create public .

public section.
*"* public components of class Z04_BIGINTX
*"* do not include other source files here!!!

types DF type DECFLOAT34 .
types DTYP type DF .
types:
dtab type table of dtyp .
types:
tsign type c length 1 .
types OBJREF type ref to Z04_BIGINTX .

class-data FZERO type DTYP read-only .
class-data FHALF type DTYP read-only .
class-data FMAXF type DTYP read-only .
class-data ZERO type OBJREF read-only .
class-data ONE type OBJREF read-only .
class-data TWO type OBJREF read-only .
class-data MAXINT type OBJREF read-only .
class-data MAXINT_2 type OBJREF read-only .
class-data FMANTSIZE type I read-only value 15. "#EC NOTEXT .
class-data DFMANTSIZE type I read-only value 34. "#EC NOTEXT .

methods CONSTRUCTOR .
methods SETOBJ
importing
value(SRC) type OBJREF
returning
value(THIS) type OBJREF .
methods SETF
importing
value(SRC) type F
value(EXP) type I default 0
returning
value(THIS) type OBJREF .
methods SETDF
importing
value(SRC) type DF
value(EXP) type I default 0
returning
value(THIS) type OBJREF .
methods SETI
importing
value(SRC) type I
returning
value(THIS) type OBJREF .
methods SETSTR
importing
value(SRC) type STRING
returning
value(THIS) type OBJREF .
methods GETSTR
importing
value(WITH_SIGN) type I default 1
returning
value(S) type STRING .
methods GETF
exporting
value(MNT) type F
value(EXP) type I .
methods GETDF
exporting
value(MNT) type DF
value(EXP) type I .
methods GETSIGN
returning
value(S) type TSIGN .
methods GETCIFCOUNT
returning
value(R) type I .
methods ABS
returning
value(THIS) type OBJREF .
methods NEG
returning
value(THIS) type OBJREF .
methods IS_ZERO
returning
value(R) type I .
methods ABSCMP
importing
!X type OBJREF
returning
value(R) type I .
methods CMP
importing
!X type OBJREF
returning
value(R) type I .
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods SUB
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods MUL
importing
!X type OBJREF
returning
value(THIS) type OBJREF .
methods DIVX
importing
!X type OBJREF
returning
value(R) type OBJREF
exceptions
DIV_ZERO .
methods DIV
importing
!X type OBJREF
value(USEBUF) type I default 0
returning
value(THIS) type OBJREF
exceptions
DIV_ZERO .
methods MOD
importing
!X type OBJREF
value(USEBUF) type I default 0
returning
value(THIS) type OBJREF
exceptions
DIV_ZERO .
methods POW
importing
!X type OBJREF
returning
value(THIS) type OBJREF
exceptions
NEGATIVE_EXPONENT
TOO_BIG .
methods POWMOD
importing
!X type OBJREF
!M type OBJREF
returning
value(THIS) type OBJREF
exceptions
NEGATIVE_EXPONENT
TOO_BIG .
class-methods CLASS_CONSTRUCTOR .
methods DEBUG .
importing
value(N) type I
value(C) type C
value(S) type STRING
returning
value(R) type STRING .
protected section.
*"* protected components of class Z04_BIGINTX
*"* do not include other source files here!!!

data SGN type I .
data DAT type DTAB .
class-data CHUNKSIZE type I value 16. "#EC NOTEXT .
class-data DCHUNKSIZE type I value 32. "#EC NOTEXT .
class-data CHUNKMOD type DTYP value `1e16`. "#EC NOTEXT .
class-data DCHUNKMOD type DTYP value `1e32`. "#EC NOTEXT .
class-data O_D type OBJREF .
class-data O_N type OBJREF .
class-data O_Q type OBJREF .
class-data O_R type OBJREF .
private section.
*"* private components of class Z04_BIGINTX
*"* do not include other source files here!!!
ENDCLASS.

CLASS Z04_BIGINTX IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->ABS
* +-------------------------------------------------------------------------------------------------+
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ABS.
sgn = 1.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->ABSCMP
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method ABSCMP.

data: n1 type i,
n2 type i,
i  type i,
v1 type dtyp,
v2 type dtyp.

n1 = lines(    dat ).
n2 = lines( x->dat ).

if ( n1 > n2 ).
r = 1.
return.
endif.
if ( n1 < n2 ).
r = -1.
return.
endif.

do n1 times.
i = n1 - sy-index + 1.
read table    dat into v1 index i.
read table x->dat into v2 index i.
if ( v1 > v2 ).
r = 1.
return.
endif.
if ( v1 < v2 ).
r = -1.
return.
endif.
enddo.

r = 0.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->ADD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
" bei Unterschied im Vorzeichen auf Subtraktion wechseln;
" dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
if ( sgn <> x->sgn ).
x->neg( ).
sub( x ).
x->neg( ).
this = me.
return.
endif.

data: n type i,
nx type i,
result type dtab,
i type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp.

n  = lines( dat ).
nx = lines( x->dat ).
if ( n < nx ). n = nx. endif.

ov = 0.
do n times.
i = sy-index.
" Das Lesen aus den Tabellen ist tolerant gegenüber Wertebereichsüberschreitung des Index.
" Aber in so einem Fall wird das Datenziel nicht genullt.
clear: v1, v2.  " Nullsetzung auf kürzestmögliche Weise
read table    dat into v1 index i.
read table x->dat into v2 index i.
v3 = v1 + v2 + ov.
" Aufbereitungs-Variante mit div/mod:
" => signifikant langsamer als if/sub!
" -> Diese Operationen werden offenbar in Software durchgeführt.
" -> Sie sind deutlich langsamer als elementare ABAP-Operationen.
"ov = v3 div chunkmod.
"v3 = v3 mod chunkmod.
" Aufbereitungs-Variante mit Vergleich/sub:
if ( v3 >= chunkmod ).
ov = 1.
v3 = v3 - chunkmod.
else.
ov = 0.
endif.
append v3 to result.
enddo.

if ( ov <> 0 ).
append ov to result.
endif.

free dat.
dat = result.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method Z04_BIGINTX=>CLASS_CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CLASS_CONSTRUCTOR.
fmaxf = '1.5e308'.
fzero = 0.
fhalf = '0.5'.
create object: zero, one, two, maxint, o_d, o_n, o_r, o_q.", maxint_2
zero->seti( 0 ).
one->seti( 1 ).
two->seti( 2 ).
maxint->seti(   2 ** 31 - 1 ).
"maxint_2->seti( 2 ** 30 - 1 ).
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->CMP
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CMP.

" Hier wird eine sophistische Vorzeichenbehandlung angesetzt,
" die auch vorzeichenbehaftete Nullen einbezieht.
" Wenn eine negative Zahl zu Null verkleinert wurde - etwa durch Division -,
" wird diese als kleiner als eine positive zu Null verkleinerte Zahl betrachtet.

data: iszero1 type i,
iszero2 type i.

iszero1 = is_zero( ).
iszero2 = x->is_zero( ).

if ( iszero1 + iszero2 = 2 ).
r = 0.
return.
endif.
if ( sgn = +1 and x->sgn = -1 ).
r = +1.
return.
endif.
if ( sgn = -1 and x->sgn = +1 ).
r = -1.
return.
endif.

data: cmp type i.
cmp = abscmp( x ).

if ( cmp > 0 ).
if ( sgn = 1 ).
r = +1.
else.
r = -1.
endif.
elseif ( cmp < 0 ).
if ( sgn = 1 ).
r = -1.
else.
r = +1.
endif.
else.
r = 0.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CONSTRUCTOR.
sgn = 1.
append fzero to dat.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->DEBUG
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DEBUG.
data: t type dtyp,
cs type c.

cs = getsign( ). write: / cs.
loop at dat into t.
write: / t.
endloop.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->DIV
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] USEBUF                         TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DIV.
if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
me->setobj( o_q ).
this = me.
return.
endif.

o_d->setobj( me ).
o_n->setobj( x ).
o_r = divx( x ).
o_q->setobj( me ).

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->DIVX
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] R                              TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DIVX.
data: z   type objref,  " Zwischenergebnis eines Näherungsschritts
mr  type dtyp,  " Mantisse Rest
er  type i,     " Exponent Rest
mx  type dtyp,  " Mantisse Nenner (X)
ex  type i,     " Exponent Nenner (X)
mv  type dtyp,  " Mantisse Verhältnis
ev  type i,     " Exponent Verhältnis
eva type i,     " Verhältnis-Exponent, der direkt als Nullen eingesetzt wird
evb type i.     " Verhältnis-Exponent, der über setf eingesetzt wird

create object: r, z.

r->setobj( me ).
me->setobj( zero ).

x->getdf( IMPORTING MNT = mx EXP = ex ).

while ( r->abscmp( x ) >= 0 ).

r->getdf( IMPORTING MNT = mr EXP = er ).
" Das im folgenden ermittelte Verhältnis ist eine Schätzung auf etwa 15 Stellen Genauigkeit.
" Die kann etwas zu klein, aber auch etwas zu groß ausfallen.
" Dies ist kein Problem: Der Rest darf zwischendurch ruhig das Vorzeichen wechseln,
" er wird trotzdem immer in Richtung kleiner werdender Beträge abgebaut.
" Wenn wir am Punkt angekommen sind, dass der Rest-Betrag kleiner als x geworden ist,
" müssen wir nochmal testen, ob der Rest das selbe Vorzeichen hat wie zu Anfang
" (was identisch ist zum Produkt der Vorzeichen von Quotient und Nenner).
" Falls nicht, muss der Quotient um ein 1 und der Rest um x korrigiert werden.

mv = mr / mx.
ev = er - ex.
z->setdf( src = mv exp = ev ).
r->sub( z->mul( x ) ).

endwhile.

" Rest-Underflow?
" -> Korrektur des Quotienten um -1 relativ zu seinem Vorzeichen
"     und des Rests um jene Quotientenkorrektur mal -x
" (Um die Faktoren besser lesbar auseinanderzuhalten, wird das aktuelle Objekt,
" welches den Quotienten darstellt, explizit mit "me->" gekennzeichnet.)
if ( r->sgn <> me->sgn * x->sgn ).
z->seti( me->sgn ).
me->sub( z ).
endif.

free z.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->GETCIFCOUNT
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETCIFCOUNT.
data: n type i,
v type dtyp,
s type string.

n = lines( dat ).
r = ( n - 1 ) * chunksize.
read table dat into v index n.
"r = r + ceil( log10( v + fhalf ) ).
" Die Funktion log10 ist für decfloats EXTREM aufwendig implementiert
" (rund 500 mikrosec -> das mehr als 1000-fache des Wertes für float).
" div (und powmod als abhängige Funktion) werden dadurch extrem ausgebremst.
r = r + CL_ABAP_MATH=>GET_NUMBER_OF_DIGITS( floor( v + fhalf ) ).
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->GETDF
* +-------------------------------------------------------------------------------------------------+
* | [<---] MNT                            TYPE        DF
* | [<---] EXP                            TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETDF.
data: i     type i,
imin  type i,
v     type dtyp.

mnt = 0.
exp = 0.
i = lines( dat ).
imin = nmax( val1 = 1 val2 = i - 2 ).
while ( i >= imin ).
read table dat into v index i.
mnt = mnt * chunkmod + v.
i = i - 1.
endwhile.
if ( imin > 1 ).
exp = i * chunksize.
endif.
mnt = mnt * sgn.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->GETF
* +-------------------------------------------------------------------------------------------------+
* | [<---] MNT                            TYPE        F
* | [<---] EXP                            TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETF.
data: i     type i,
imin  type i,
v     type dtyp.

mnt = 0.
exp = 0.
i = lines( dat ).
imin = nmax( val1 = 1 val2 = i - 1 ).
while ( i >= imin ).
read table dat into v index i.
mnt = mnt * chunkmod + v.
i = i - 1.
endwhile.
if ( imin > 1 ).
exp = i * chunksize.
endif.
mnt = mnt * sgn.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->GETSIGN
* +-------------------------------------------------------------------------------------------------+
* | [<-()] S                              TYPE        TSIGN
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETSIGN.
if ( sgn = 1 ).
s = '+'.
else.
s = '-'.
endif.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->GETSTR
* +-------------------------------------------------------------------------------------------------+
* | [--->] WITH_SIGN                      TYPE        I (default =1)
* | [<-()] S                              TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GETSTR.
data: v type dtyp,
st type string,
n type i.

n = lines( dat ).
loop at dat into v.
st = |{ v }|.
"write: / ' getstr:',st.
if ( sy-tabix < n ).
s = strpad( s = st n = chunksize c = '0' ) && s.
else.
s = st && s.
endif.
endloop.

if ( with_sign = 1 ).
s = getsign( ) && s.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->IS_ZERO
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R                              TYPE        I
* +--------------------------------------------------------------------------------------</SIGNATURE>
method IS_ZERO.

if ( lines( dat ) > 1 ).
r = 0.
return.
endif.

data d type dtyp.

read table dat into d index 1.
if ( d > 0 ).
r = 0.
else.
r = 1.
endif.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->MOD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] USEBUF                         TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] DIV_ZERO
* +--------------------------------------------------------------------------------------</SIGNATURE>
method MOD.
if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
me->setobj( o_r ).
this = me.
return.
endif.

o_d->setobj( me ).
o_n->setobj( x ).
o_r = divx( x ).
o_q->setobj( me ).
me->setobj( o_r ).

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->MUL
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method MUL.
sgn = sgn * x->sgn.

data: n1 type i,
n2 type i,
result type dtab,
i1 type i,
i2 type i,
i3 type i,
i1min type i,
i3max type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp,
vr type dtyp.

if ( is_zero( ) + x->is_zero( ) = 1 ).
clear dat.
append fzero to dat.
this = me.
return.
endif.

n1 = lines( dat ).
n2 = lines( x->dat ).

" i3 gibt die Zielstelle an.
" Es sind alle i1 + i2 = i3 zu durchlaufen!
" Nullbasiert wäre das:
"   i1 = i1min...i3   i1min = max( 0, i3 - n2 )
"   i2 = i3 - i1
" Das bei SAP 1-basiert indiziert wird,
" muss an den richtigen Stellen noch +1 ergänzt werden!
"
" Es ist zu bedenken, dass die Menge der bearbeiteten chunks SEHR groß sein kann!
" Das resultiert in großem Wertebereich für den Overflow!
" Der ist halt nicht bloß eine eventuelle "1", sondern nimmt maximal den Wert
" der Menge der beteiligten chunks an!
" Wir haben bei den Zwischen-Additionen also große Zahlen
" aus drei chunks zu verarbeiten.
" Die chunksize ist so bemessen, dass das Ergebnis einer Multiplikation
" plus eine Überlaufstelle im zugrundeliegenden chunk-Datentyp aufgenommen werden kann.
i3max = ( n1 + n2 - 1 ).
i3 = 1.
do i3max times.

i1min = nmax( val1 = 1 val2 = i3 - n2 + 1 ).
i1 = i1min.
i2 = i3 - i1 + 1.

while ( i1 <= n1 and i2 > 0 ).
" Da wir hier explizit nur Elemente durchgehen, die tatsächlich existieren,
" werden alle read tables erfolgreich durchgehen.
" Das Nullen der Zielwerte können wir uns deshalb ersparen.
read table    dat into v1 index i1.
read table x->dat into v2 index i2.

"data s  type string.
"s = `  1: ` && v1 && `[` && i1 && `]  2: ` && v2 && `[` && i2 && `]`.
"write: / s.

v3 = v3 + v1 * v2.

"ov = ov + v3 div dchunkmod.
"v3 =      v3 mod dchunkmod.

if ( v3 >= dchunkmod ).
ov = ov + 1.
v3 = v3 - dchunkmod.
endif.

i1 = i1 + 1.
i2 = i2 - 1.

endwhile.

"vr = v3 mod chunkmod.
"v3 = v3 div chunkmod + ov * chunkmod.

data vt type dtyp.

vt = v3 div chunkmod.
vr = v3 - vt * chunkmod.
v3 = vt + ov * chunkmod.

ov = fzero.
append vr to result.

i3 = i3 + 1.
enddo.
" Im Ergebnis wird es nie mehr Stellen geben als in den letzten v3 gepasst haben.
" v3 wird im letzten Schleifendurchlauf innerhalb chunkmod bleiben.

if ( v3 <> 0 ).
append v3 to result.
endif.

free dat.
dat = result.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->NEG
* +-------------------------------------------------------------------------------------------------+
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method NEG.
sgn = - sgn.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->POW
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] NEGATIVE_EXPONENT
* | [EXC!] TOO_BIG
* +--------------------------------------------------------------------------------------</SIGNATURE>
method POW.
if ( x->cmp( zero ) < 0 ).
raise NEGATIVE_EXPONENT.
endif.

" Wir haben einen Ziel-Speicherbedarf von: Originalgröße * x.
" Zwischendurch haben wir denselben Speicherbedarf für Zwischenwerte nochmal.
" Wobei nicht dokumentiert ist, wieviele Elemente ein Array alias interne Tabelle
" halten kann. Denkbar ist ohne weiteres, dass dies auf 64-bit-Systemen ausschließlich
" durch die Adressierbarkeit der Array-Blöcke und deren Begrenzung durch den
" Integer-Wertebereich der Indizes gegeben ist.
" Außerdem ist nicht dokumentiert, wieviel RAM eine Session verwalten kann.
" Ich setze hier also erstmal an, dass ich - falls nicht ein Speicherüberlauf
" vom Betriebssystem kommt - bis zu maxint Blöcke adressieren können sollte.
"
" Letztlich können wir hier eh nur eine Abschätzung geben.
" Ob und wann eine Exception kommt, entscheidet die Laufzeitumgebung und die
" konkrete Füllung der Argumente.
" => Die folgende sehr grobe Schätzung wurde wieder deaktiviert.
"
"is_too_big = x->cmp( maxint_2 ).
"if ( is_too_big > 0 ).
"  raise TOO_BIG.
"endif.

data: cifcount type i,
is_too_big type i,
exptmp  type i,
expmax  type i,
exp     type i,
ei      type i,
facs    type table of objref,
fac     type objref,
factmp  type objref.

try.
"------------------------
" Potenz-Faktoren anlegen
"------------------------
create object factmp.
factmp->setobj( me ).

exptmp = 1.
exp = x->getstr( ).
expmax = exp div 2.
"write: / '  exp:',exp,', expmax:',expmax.

create object fac.
fac->setobj( factmp ).
append fac to facs.

while ( exptmp <= expmax ).
create object fac.
fac->setobj( factmp ).
fac->mul( fac ).
exptmp = exptmp + exptmp.
append fac to facs.
factmp->setobj( fac ).

"data s type string.
"s = fac->getstr( ).
"write: / '  faktor:',s.

endwhile.

"loop at facs into fac.
"  s = '  facs[' && sy-tabix && ']:' && fac->getstr( ).
"  write: / s.
"endloop.

"------------------------
" Potenz bilden
"------------------------
factmp->seti( 1 ).
"exptmp wird aus der Init-Schleife übernommen
ei = lines( facs ).
while ( exp > 0 ).
read table facs into fac index ei.
if ( exptmp <= exp ).
factmp->mul( fac ).

"s = '  fac:' && fac->getstr( ) && ', exptmp:' && exptmp && ', exp:' && exp.
"write: / s.

exp = exp - exptmp.
endif.
exptmp = exptmp div 2.
ei = ei - 1.
endwhile.

setobj( factmp ).
loop at facs into fac.
free fac.
endloop.
free factmp.

catch cx_root.
if ( not factmp is initial ).
loop at facs into fac.
free fac.
endloop.
free factmp.
endif.
raise TOO_BIG.
endtry.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->POWMOD
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [--->] M                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* | [EXC!] NEGATIVE_EXPONENT
* | [EXC!] TOO_BIG
* +--------------------------------------------------------------------------------------</SIGNATURE>
method POWMOD.
" Restklassen-Rechnung erlaubt, den numerischen Aufwand
" bei Multiplikation und Potenzierung krass zu reduzieren.
"   .
" - Multiplikation einer Zahl mit einem beliebigen Faktor modulo m:
"       z mod m = (    x * m +     r) mod m =     r
"   k * z mod m = (k * x * m + k * r) mod m = k * r
"   .
" - auch der Faktor kann modulo m reduziert werden:
"       a = xa * m + ra
"       b = xb * m + rb
"   a * b mod m = (xa*xb * m^2 + (xa*rb + xb*ra) * m + ra*rb) mod m = ra*rb
"   .
" -> Da in einer Restklassenbetrachtung das k*x*m völlig wurscht ist,
" kann man sich, solange man nur mit ganzen Zahlen k rechnet, das k*x*m klemmen.
" In jedem Multiplikations-Rechenschritt. Indem man stets auf die Restklasse reduziert.
" .
" Das machen wir hier.
" Dadurch fallen alle Beschränkungen des Exponenten.
" Die Prozedur ist kopiert vom normalen pow.

data: cmp type i.

cmp = x->cmp( zero ).
if ( cmp < 0 ).
raise NEGATIVE_EXPONENT.
endif.

" Extrabehandlung des Falls x = 0 (passt nicht in die Logik weiter unten)
if ( cmp = 0 ).
me->setobj( one ).
this = me.
return.
endif.

data: cifcount type i,
is_too_big type i,
exp     type objref,
exptmp  type objref,
en      type i,
ei      type i,
facs    type table of objref,
exps    type table of objref,
fac     type objref.

try.
"------------------------
" Potenz-Faktoren anlegen
"------------------------
" Die Zweierpotenzreihe zu speichern ist effektiver als sie
" beim Abwärtswandern der Faktorenliste auszudividieren.
create object: exptmp.

exptmp->setobj( one ). " me = original me ^ 1
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse

while ( exptmp->cmp( x ) <= 0 ).
create object fac. fac->setobj( me ).
create object exp. exp->setobj( exptmp ).
append fac to facs.
append exp to exps.
me->mul( me ).          " me = me^2 = original me ^ exp
exptmp->add( exptmp ).  " exp = 2 ^ schleifenindex
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
endwhile.

" debugging...
"data s type string.
"ei = lines( facs ).
"if ( ei > 0 ).
"  write: / 'potenz-faktoren:'.
"  while ( ei > 0 ).
"    read table facs into fac index ei.
"    read table exps into exp index ei.
"    s = exp->getstr( ) && ' -> ' && fac->getstr( ).
"    write: / s.
"    ei = ei - 1.
"  endwhile.
"endif.

"------------------------
" Potenz bilden
"------------------------
me->setobj( one ).
exptmp->setobj( x ).
en = lines( facs ).
ei = en.
do en times.
if ( exptmp->cmp( zero ) <= 0 ).
exit.
endif.
read table facs into fac index ei.
read table exps into exp index ei.
cmp = exp->cmp( exptmp ).
if ( cmp <= 0 ).
me->mul( fac ).
exptmp->sub( exp ).
o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
if ( cmp = 0 ).
exit.
endif.
endif.
ei = ei - 1.
enddo.

loop at facs into fac. free fac. endloop.
loop at exps into exp. free exp. endloop.
free exptmp.

catch cx_root.
if ( not exptmp is initial ). free exptmp. endif.
loop at facs into fac. free fac. endloop.
loop at exps into exp. free exp. endloop.
raise TOO_BIG.
endtry.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SETDF
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        DF
* | [--->] EXP                            TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETDF.
data: val   type df,
rest  type df,
t     type df,
xexp  type i, " Zehnerpotenz des Quellwertes
texp  type i, " Zehnerpotenz-Übertrag in/aus Nullenkette
mexp  type i,
nzch  type i, " Anzahl der abschließend zu ergänzenden Null-Chunks
izch  type i.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

" Der reinkommende float wird dezimal so zurechtgerückt,
" dass er nach Möglichkeit gerade genau dfmantsize Ziffern enthält.
" wobei die kleinste Ziffer den Stellenwert 1 haben soll.
" Dazu wird die Zehnerpotenz korrigiert, indem überflüssige Zehnerpotenzen
" nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
" Nebenbei wird dafür gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
" ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
" geradlinig schlicht als Null-Chunks angehängt werden können.
"-----------------
"xexp = ceil( log10( val ) ).
" Die Funktion log10 ist für decfloats EXTREM aufwendig implementiert
" (rund 500 mikrosec -> das mehr als 1000-fache des Wertes für float).
" div (und powmod als abhängige Funktion) werden dadurch extrem ausgebremst.
xexp = CL_ABAP_MATH=>GET_NUMBER_OF_DIGITS( floor( val ) ).

texp = xexp - dfmantsize. " durch 10 ** texp wird val nachher dividiert
exp = exp + texp.         " ...und diese Nullen später direkt angehängt
if ( exp < 0 ).
texp = texp - exp.
exp = 0.
endif.
mexp = exp mod chunksize.
nzch = exp div chunksize.
texp = texp - mexp.

"write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.

" Zehnerpotenz-Korrektur und Rundung (auf höchstens runter bis Stellenwert 1)
val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - dfmantsize ) ) ) .

clear dat.
if ( nzch > 0 ).
append fzero to dat.
izch = 1.
nzch = nzch - izch.
while ( nzch >= izch ).
append lines of dat from 1 to izch to dat.
nzch = nzch - izch.
izch = izch + izch.
endwhile.
if ( nzch > 0 ).
append lines of dat from 1 to nzch to dat.
endif.
endif.

while ( val >= chunkmod ).
"rest = val mod chunkmod.
"val  = val div chunkmod.
t = val div chunkmod.
rest = val - t * chunkmod.
val = t.

append rest to dat.
endwhile.
append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SETF
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        F
* | [--->] EXP                            TYPE        I (default =0)
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETF.
data: val   type f,
rest  type f,
xexp  type i, " Zehnerpotenz des Quellwertes
texp  type i, " Zehnerpotenz-Übertrag in/aus Nullenkette
mexp  type i,
nzch  type i, " Anzahl der abschließend zu ergänzenden Null-Chunks
izch  type i.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

" Der reinkommende float wird dezimal so zurechtgerückt,
" dass er nach Möglichkeit gerade genau fmantsize Ziffern enthält.
" wobei die kleinste Ziffer den Stellenwert 1 haben soll.
" Dazu wird die Zehnerpotenz korrigiert, indem überflüssige Zehnerpotenzen
" nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
" Nebenbei wird dafür gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
" ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
" geradlinig schlicht als Null-Chunks angehängt werden können.
xexp = ceil( log10( val ) ).
texp = xexp - fmantsize. " durch 10 ** texp wird val nachher dividiert
exp = exp + texp.        " ...und diese Nullen später direkt angehängt
if ( exp < 0 ).
texp = texp - exp.
exp = 0.
endif.
mexp = exp mod chunksize.
nzch = exp div chunksize.
texp = texp - mexp.

"write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.

" Zehnerpotenz-Korrektur und Rundung (auf höchstens runter bis Stellenwert 1)
val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - fmantsize ) ) ) .

clear dat.
if ( nzch > 0 ).
append fzero to dat.
izch = 1.
nzch = nzch - izch.
while ( nzch >= izch ).
append lines of dat from 1 to izch to dat.
nzch = nzch - izch.
izch = izch + izch.
endwhile.
if ( nzch > 0 ).
append lines of dat from 1 to nzch to dat.
endif.
endif.

data t type df.
while ( val >= chunkmod ).
rest = val mod chunkmod.
"val  = val div chunkmod.
" Die Operation "div" ist EIGENTLICH das zu "mod" passende Komplement.
" Sie liefert allerdings Nachkommastellen, wenn die Zahl ursprünglich Nachkommastellen enthält
" (so, dass (x div y)*y+(x mod y) == x).
" Eigentlich kommt val hier gerundet rein, aber beim Dividieren durch chunkmod
" können Rundungsfehler auftreten.
" mod arbeitet aber nur wie gebraucht, wenn eben KEINE Nachkommastellen vorhanden sind.
" Deshalb wird hier die aufwendigere Form mit floor gewählt.
val = floor( val / chunkmod ).
append rest to dat.
endwhile.
append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SETI
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        I
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETI.
clear dat.

data: val type df.

if ( src >= 0 ). sgn = +1. val =   src.
else.            sgn = -1. val = - src.
endif.

append val to dat.

this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SETOBJ
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETOBJ.
dat = src->dat.
sgn   = src->sgn.
this = me.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SETSTR
* +-------------------------------------------------------------------------------------------------+
* | [--->] SRC                            TYPE        STRING
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SETSTR.
clear dat.

data: ofs type i,
mpart type string,
xpart type string,
ipart type string,
fpart type string,
chunk type f,
ilen type i,
flen type i,
sc type c,
x type i.

"write: / '  originale src:', src.

" Entschrottung, Vereinfachung und Zerlegung
"-----------------------
condense src.
translate src to lower case.
" to do: Prüfung mit regex
split src   at 'e' into mpart xpart.
split mpart at '.' into ipart fpart.
ilen = strlen( ipart ).
flen = strlen( fpart ).

"write: / '  parts:', ipart,',', fpart,' * 10^ ', xpart.

" Vorzeichen-Erkennung
"-----------------------
sgn = +1.
if ( ilen > 0 ).
sc = ipart(1).
if ( sc = '-' or sc = '+' ).
if ( sc = '-' ). sgn = -1. endif.
ipart = ipart+1.
ilen = ilen - 1.
endif.
endif.
"write: / '  ipart nach Vorzeichenerkennung:', ipart.

" Zehnerpotenz-Erkennung
"-----------------------
x = xpart.
"write: / '  Zehnerpotenz / originale Länge: ',x,ilen.
ilen = ilen + x.
"write: / '  Zehnerpotenz / korrigierte Länge: ',ilen.
if ( x < 0 ).
if ( ilen < 1 ).
ilen = 1.
ipart = '0'.
endif.
"write: / '  negative Zehnerpotenz -> verbleibende Länge: ',ilen.
ipart = ipart(ilen).
elseif ( x > 0 ).
if ( x < flen ).
ipart = ipart && fpart(x).
else.
x = x - flen.
ipart = ipart && fpart && repeat( val = '0' occ = x ).
endif.
endif.
"write: / '  ipart nach Zehnerpotenz-Erkennung:', ipart.

" Integer-Zerlegung
"-----------------------
while ( ilen > 0 ).
ilen = ilen - chunksize.
if ( ilen < 0 ).
ilen = ilen + chunksize.
chunk = ipart(ilen).
append chunk to dat.
exit.
endif.
chunk = ipart+ilen(chunksize).
append chunk to dat.
endwhile.

this = me.

endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method Z04_BIGINTX=>STRPAD
* +-------------------------------------------------------------------------------------------------+
* | [--->] N                              TYPE        I
* | [--->] C                              TYPE        C
* | [--->] S                              TYPE        STRING
* | [<-()] R                              TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
data: d type i.
d = n - strlen( s ).
if ( d < 0 ). r = s. return. endif.

" repeat ist 10% schneller als das Ausschneiden aus einem vorbereiteten String...
r = repeat( val = '0' occ = d ) && s.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method Z04_BIGINTX->SUB
* +-------------------------------------------------------------------------------------------------+
* | [--->] X                              TYPE        OBJREF
* | [<-()] THIS                           TYPE        OBJREF
* +--------------------------------------------------------------------------------------</SIGNATURE>
method SUB.
" bei Unterschied im Vorzeichen auf Addition wechseln;
" dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
if ( sgn <> x->sgn ).
x->neg( ).
x->neg( ).
this = me.
return.
endif.

" Den größeren Wert vom kleineren Wert abziehen
" (Alternative wäre blind modulo zu subtrahieren und zum Schluss
" bei verbleibendem Überlauf das Ergebnis nochmal komplett zu negieren.
" Das ist allerdings mit mehr Aufwand verbunden als beim Tausch vorweg.)
data: cmp type i.

field-symbols:
<d1> type dtab,
<d2> type dtab.

" ...Dazu den Fall Null extra behandeln, weil der recht häufig vorkommen kann
"   und besonders fix über die Bühne zu bringen ist...
cmp = abscmp( x ).
if ( cmp = 0 ).
clear dat.
append fzero to dat.
this = me.
return.
endif.

if ( cmp < 0 ).
assign dat    to <d2>.
assign x->dat to <d1>.
neg( ).
else.
assign dat    to <d1>.
assign x->dat to <d2>.
endif.

" Letztlich die eigentliche Subtraktion durchführen
data: n type i,
result type dtab,
i type i,
v1 type dtyp,
v2 type dtyp,
v3 type dtyp,
ov type dtyp.

n  = lines( <d1> ).

" ov = 0. " überflüssig
do n times.
i = sy-index.
" Das Lesen aus den Tabellen ist tolerant gegenüber Wertebereichsüberschreitung des Index.
" Aber in so einem Fall wird das Datenziel nicht genullt.
clear: v1, v2.  " Nullsetzung auf kürzestmögliche Weise
read table <d1> into v1 index i.
read table <d2> into v2 index i.
v3 = v1 - v2 + ov.

" das folgende ist signifikant effektiver als ein if/sub
"ov = v3 div chunkmod. " Überlauf ist hier -1
"v3 = v3 mod chunkmod.
if ( v3 < 0 ).
ov = -1.
v3 = v3 + chunkmod.
else.
ov = 0.
endif.

append v3 to result.
enddo.
" ...Ein Überlauf nach Abschluss kann wegen vorherigem Sortieren nicht auftreten.

" Führende Nullen löschen
n = lines( result ).
do n times.
i = n - sy-index + 1.
read table result into v1 index i.
if ( v1 <> fzero ).
exit.
endif.
enddo.
" -> alles von i bis n löschen! Aber nur, wenn i > 1 ist!
delete result from i + 1." to n.

free dat.
dat = result.

this = me.
endmethod.
ENDCLASS.``````