Oracle database experiences

Oracle database blog

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

Convert large integer

Even if the large_integer type can be stored in the database, it can be useful to convert a large_integer into a VARCHAR2 type. We will add a new function “to_varchar” as follow:

...
MEMBER FUNCTION to_varchar(p_mode CHAR DEFAULT 'N')
   RETURN VARCHAR2,
...
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;
...

The p_mode parameter stands for:

  • ‘N’ : normal mode that is a VARCHAR2 of 111 characters filled with ‘0’ at the left of the large integer if necessary,
  • ‘T’ : “trim mode” that is ‘0’ characters removed at the left of the large integer if necessary.

The member procedure “display” is modified in order to use this function. We have also added the member function “length_li” that gives the number of significative digits of a large_integer.

Multiplication by 10 power N

We will continue on a basic multiplication by multiplying a large integer by 10 power N. To handle this multiplication we will add ‘0’ digits at the end of the large integer. The member procedure “multiply_10power” is shown as follow:

...
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; 
...

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)
) 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; 
END;

Examples:

SA> DECLARE
  2     v_li1 large_integer;
  3     v_li2 large_integer;
  4  BEGIN
  5     DBMS_OUTPUT.PUT_LINE('-- Example5: Multiply_10power');
  6     v_li1 := large_integer('111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111');
  7     v_li1.display();
  8     v_li1.multiply_10power(0);
  9     v_li1.display();
 10     DBMS_OUTPUT.PUT_LINE('Length:' || v_li1.length_li);
 11
 12     DBMS_OUTPUT.PUT_LINE('-- Example6: Multiply_10power');
 13     v_li1 := large_integer('123456789');
 14     v_li1.display();
 15     v_li1.multiply_10power(102);
 16     v_li1.display();
 17     DBMS_OUTPUT.PUT_LINE('Length:' || v_li1.length_li);
 18     DBMS_OUTPUT.PUT_LINE('T:' || v_li1.to_varchar('T'));
 19     DBMS_OUTPUT.PUT_LINE('N:' || v_li1.to_varchar('N'));
 20
 21  END;
 22  /
-- Example5: Multiply_10power
#111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
#111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Length:111
-- Example6: Multiply_10power
#000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123456789
#123456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Length:111
T:123456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
N:123456789000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

PL/SQL procedure successfully completed.

Remark:

  • In the next post, we will finaly implement the multiplication of two large integers using add_li, multiply_10power and multiply_digit.
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: