Oracle database experiences

Oracle database blog

Working with large integers using PL/SQL – Part IV – Multiplication

Multiplication

To handle the multiplication we will implement it like the “school method”. The following figure will serve as an example:

Working_large_integers_4a

  • We will multiply a digit of the first large integer by the second large integer (in green in the above figure),
  • We will multiply the result by 10 power the position of this digit minus one in the first large integer (in orange in the above figure),
  • We will add all the previous results to get the multiplication result (in blue in the above figure).

All the described operations will use “multiply_digit”, “multiply_10power” and “add_li” member procedures.

The member procedure “multiply_li” is shown as follow:

...
MEMBER PROCEDURE multiply_li(p_large_integer large_integer)
   IS
      v_large_integer_char VARCHAR2(1000);
      v_large_integer1     large_integer;
      v_large_integer2     large_integer := large_integer;
      v_length             INTEGER;
      v_index              PLS_INTEGER;
   BEGIN
      v_large_integer_char := p_large_integer.to_varchar('T');
      v_length := LENGTH(v_large_integer_char);
 
      FOR v_index IN 1..v_length
      LOOP 
         v_large_integer1 := SELF;
         v_large_integer1.multiply_digit(TO_NUMBER(SUBSTR(v_large_integer_char,v_length-v_index+1,1)));
         v_large_integer1.multiply_10power(v_index-1);
         v_large_integer2.add_li(v_large_integer1);
      END LOOP;
      SELF := v_large_integer2;
   END multiply_li; 
...

Modified specifications

You will find below the modified specifications:

CREATE OR REPLACE TYPE large_integer AS OBJECT (
   int_part0 NUMBER(37),
   int_part1 NUMBER(37),
   int_part2 NUMBER(37),
   --
   CONSTRUCTOR FUNCTION large_integer
   RETURN SELF AS RESULT,
   --
   CONSTRUCTOR FUNCTION large_integer(int_part0 number,int_part1 number,int_part2 number)
   RETURN SELF AS RESULT,
   --
   CONSTRUCTOR FUNCTION large_integer(p_large_integer VARCHAR2)
   RETURN SELF AS RESULT,
   --
   MEMBER PROCEDURE display,
   --
   MEMBER PROCEDURE add_li(p_large_integer large_integer), 
   --
   MEMBER PROCEDURE multiply_digit(p_digit NUMBER),
   --
   MEMBER FUNCTION to_varchar(p_mode CHAR DEFAULT 'N')
   RETURN VARCHAR2,
   --
   MEMBER FUNCTION length_li 
   RETURN NUMBER,
   --
   MEMBER PROCEDURE multiply_10power(p_power NATURALN),
   --
   MEMBER PROCEDURE multiply_li(p_large_integer large_integer)
) INSTANTIABLE NOT FINAL;

Modified body

You will find below the modified body:

CREATE OR REPLACE TYPE BODY large_integer
IS
   CONSTRUCTOR FUNCTION large_integer 
   RETURN SELF AS RESULT 
   IS
   BEGIN
      int_part0:=0;
      int_part1:=0;
      int_part2:=0;
      RETURN;
   END large_integer;
   --
   CONSTRUCTOR FUNCTION large_integer(int_part0 number,int_part1 number,int_part2 number) 
   RETURN SELF AS RESULT 
   IS
   BEGIN
      SELF.int_part0:=int_part0;
      SELF.int_part1:=int_part1;
      SELF.int_part2:=int_part2; 
      RETURN;
   END large_integer;
   --
   CONSTRUCTOR FUNCTION large_integer(p_large_integer VARCHAR2)
   RETURN SELF AS RESULT
   IS
      v_li_length     INTEGER;
      v_large_integer VARCHAR2(200);
   BEGIN
      IF LENGTH(p_large_integer) > 111 THEN
         RAISE_APPLICATION_ERROR(-20002,'Large integer too big!');
      ELSE

         v_large_integer := LPAD(p_large_integer,111,'0');
         v_li_length := LENGTH(v_large_integer);

         SELF.int_part0 := TO_NUMBER(SUBSTR(v_large_integer,v_li_length-36,37));
         SELF.int_part1 := TO_NUMBER(SUBSTR(v_large_integer,v_li_length-73,37));
         SELF.int_part2 := TO_NUMBER(SUBSTR(v_large_integer,v_li_length-110,37));

      END IF;
      
      RETURN;
   END large_integer;
   --
   MEMBER PROCEDURE display 
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('#' 
                        || SELF.to_varchar);
   END display;
   --
   MEMBER PROCEDURE add_li(p_large_integer large_integer) 
   IS
      v_carry    NUMBER    :=0;
      v_sub_add  NUMBER(38);
   BEGIN
      v_sub_add := SELF.int_part0 + p_large_integer.int_part0; 

      v_carry := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),1,1));

      SELF.int_part0 := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),2));

      v_sub_add := SELF.int_part1 + p_large_integer.int_part1 + v_carry;

      v_carry := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),1,1));

      SELF.int_part1 := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),2));

      v_sub_add := SELF.int_part2 + p_large_integer.int_part2 + v_carry;

      v_carry := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),1,1)); 

      SELF.int_part2 := TO_NUMBER(SUBSTR(LPAD(TO_CHAR(v_sub_add),38,'0'),2));

      IF v_carry > 0 THEN 
         RAISE_APPLICATION_ERROR(-20000,'Add_li:Large integer too big!'); 
      END IF;
     
   END add_li;
   --
   MEMBER PROCEDURE multiply_digit(p_digit NUMBER)
   IS
      v_large_integer large_integer;
      v_index         PLS_INTEGER;
   BEGIN
      IF p_digit NOT IN (0,1,2,3,4,5,6,7,8,9) 
      THEN
         RAISE_APPLICATION_ERROR(-20001,'Multiply_digit:p_digit should be between 0 and 9!');
      ELSIF p_digit = 0 
      THEN
         int_part0:=0;
         int_part1:=0;
         int_part2:=0;
      ELSE
         v_large_integer := SELF;
         FOR v_index IN 1..(p_digit-1)
         LOOP
            SELF.add_li(v_large_integer);  
         END LOOP;
      END IF;
   END multiply_digit; 
   -- 
   MEMBER FUNCTION to_varchar(p_mode CHAR DEFAULT 'N')
   RETURN VARCHAR2
   IS
      v_large_integer_char VARCHAR2(200);
   BEGIN
      
      v_large_integer_char := LPAD(TO_CHAR(SELF.int_part2),37,'0') 
                           || LPAD(TO_CHAR(SELF.int_part1),37,'0') 
                           || LPAD(TO_CHAR(SELF.int_part0),37,'0');
      IF p_mode = 'T' THEN
         v_large_integer_char := NVL(LTRIM(v_large_integer_char,'0'),'0'); 
      END IF;

      RETURN v_large_integer_char;
   END to_varchar;
   --
   MEMBER FUNCTION length_li 
   RETURN NUMBER
   IS
      v_length NUMBER;
   BEGIN
      v_length := LENGTH(SELF.to_varchar('T'));
      RETURN v_length;
   END length_li;
   -- 
   MEMBER PROCEDURE multiply_10power(p_power NATURALN)
   IS
     v_large_integer_char VARCHAR2(1000);
     v_length             NUMBER;
   BEGIN
      v_large_integer_char := SELF.to_varchar
                           || LPAD('0',p_power,'0');
  
      v_length := LENGTH(v_large_integer_char);

      IF LENGTH(LTRIM(v_large_integer_char,'0')) > 111 THEN 
         RAISE_APPLICATION_ERROR(-20003,'Multiply_10power:Large integer too big!'); 
      END IF;

      SELF.int_part0 := TO_NUMBER(SUBSTR(v_large_integer_char,v_length-36,37));
      SELF.int_part1 := TO_NUMBER(SUBSTR(v_large_integer_char,v_length-73,37));
      SELF.int_part2 := TO_NUMBER(SUBSTR(v_large_integer_char,v_length-110,37));
   END multiply_10power; 
   --
   MEMBER PROCEDURE multiply_li(p_large_integer large_integer)
   IS
      v_large_integer_char VARCHAR2(1000);
      v_large_integer1     large_integer;
      v_large_integer2     large_integer := large_integer;
      v_length             INTEGER;
      v_index              PLS_INTEGER;
   BEGIN
      v_large_integer_char := p_large_integer.to_varchar('T');
      v_length := LENGTH(v_large_integer_char);
 
      FOR v_index IN 1..v_length
      LOOP 
         v_large_integer1 := SELF;
         v_large_integer1.multiply_digit(TO_NUMBER(SUBSTR(v_large_integer_char,v_length-v_index+1,1)));
         v_large_integer1.multiply_10power(v_index-1);
         v_large_integer2.add_li(v_large_integer1);
      END LOOP;
      SELF := v_large_integer2;
   END multiply_li;
END;

Examples:

MATH> DECLARE
  2     v_li1 large_integer;
  3     v_li2 large_integer;
  4  BEGIN
  5     DBMS_OUTPUT.PUT_LINE('-- Example7: Multiply_li');
  6     v_li1 := large_integer('1234567890123456');
  7     v_li2 := large_integer('1234567890123456');
  8     v_li1.display();
  9     v_li2.display();
 10     v_li1.multiply_li(v_li2);
 11     v_li1.display();
 12
 13     DBMS_OUTPUT.PUT_LINE('-- Example8: Multiply_li');
 14     v_li1 := large_integer('9999999999999999999999999999999999999999999999999999999');
 15     v_li2 := large_integer('9999999999999999999999999999999999999999999999999999999');
 16     v_li1.display();
 17     v_li2.display();
 18     v_li1.multiply_li(v_li2);
 19     v_li1.display();
 20
 21  END;
 22  /
-- Example7: Multiply_li
#000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456
#000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456
#000000000000000000000000000000000000000000000000000000000000000000000000000000001524157875323881726870921383936
-- Example8: Multiply_li
#000000000000000000000000000000000000000000000000000000009999999999999999999999999999999999999999999999999999999
#000000000000000000000000000000000000000000000000000000009999999999999999999999999999999999999999999999999999999
#099999999999999999999999999999999999999999999999999999980000000000000000000000000000000000000000000000000000001

PL/SQL procedure successfully completed.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: