0

I am trying to implement the Grade-School Multiplication algorithm in Ada, and am currently getting an index out of bounds error. I'd appreciate any input on how to fix the error, and successfully implement the Algorithm. Thanks in advance!

I have a package BigNumPkg which defines type BigNum is array(0..Size - 1) of Integer;

The function I am trying to implement currently looks like this:

FUNCTION "*" (X, Y : BigNum) RETURN BigNum IS
  Product : BigNum:= Zero;
  Carry : Natural := 0;
  Base : Constant Integer := 10;
BEGIN
  FOR I IN REVERSE 0..Size-1 LOOP
     Carry := 0;
     FOR N IN REVERSE 0..Size-1 LOOP
        Product(N + I - 1) := Product(N + I - 1) + Carry + X(N) * Y(I);
        Carry := Product(N + I -1) / Base;
        Product(N + I -1) := Product(N +I-1) mod Base;
     END LOOP;
     Product(I+Size-1) := Product(I+Size-1) + Carry;
  END LOOP;
  RETURN Product;
END "*"; 
Justiciar
  • 356
  • 1
  • 3
  • 20

2 Answers2

2

Package specification:

package Big_Integer is

   Base : constant := 10;
   Size : constant := 3;

   type Extended_Digit is range 0 .. Base * Base;
   subtype Digit is Extended_Digit range 0 .. Base - 1;

   type Instance is array (0 .. Size - 1) of Digit;

   function "*" (Left, Right : in Instance) return Instance;

   function Image (Item : in Instance) return String;

end Big_Integer;

You can of course adjust the parameters as needed, but these are nice for manual inspection of the results. Note that I haven't assured myself that the range of Extended_Digit is correct, but it seems to work in this case.

Package implementation:

with Ada.Strings.Unbounded;

package body Big_Integer is

   function "*" (Left, Right : in Instance) return Instance is
      Carry : Extended_Digit := 0;
      Sum   : Extended_Digit;
   begin
      return Product : Instance := (others => 0) do
         for I in Left'Range loop
            for J in Right'Range loop
               if I + J in Product'Range then
                  Sum := Left (I) * Right (J) + Carry + Product (I + J);

                  Product (I + J) := Sum mod Base;
                  Carry           := Sum / Base;
               else
                  Sum := Left (I) * Right (J) + Carry;

                  if Sum = 0 then
                     Carry := 0;
                  else
                     raise Constraint_Error with "Big integer overflow.";
                  end if;
               end if;
            end loop;

            if Carry /= 0 then
               raise Constraint_Error with "Big integer overflow.";
            end if;
         end loop;
      end return;
   end "*";

   function Image (Item : in Instance) return String is
      Buffer : Ada.Strings.Unbounded.Unbounded_String;
   begin
      for E of reverse Item loop
         Ada.Strings.Unbounded.Append (Buffer, Digit'Image (E));
      end loop;

      return Ada.Strings.Unbounded.To_String (Buffer);
   end Image;

end Big_Integer;

Test driver:

with Ada.Text_IO;

with Big_Integer;

procedure Use_Big_Integers is
   use all type Big_Integer.Instance;

   procedure Multiply (A, B : in     Big_Integer.Instance);

   procedure Multiply (A, B : in     Big_Integer.Instance) is
      use Ada.Text_IO;
   begin
      Put (Image (A));
      Put (" * ");
      Put (Image (B));
      Put (" = ");
      Put (Image (A * B));
      New_Line;
   exception
      when Constraint_Error =>
         Put_Line ("Constraint_Error");
   end Multiply;

begin
   Multiply (A => (0 => 1, others => 0),
             B => (others => Big_Integer.Digit'Last));

   Multiply (A => (0 => Big_Integer.Digit'Last, others => 0),
             B => (0 => Big_Integer.Digit'Last, others => 0));

   Multiply (A => (0 => 2, others => 0),
             B => (others => Big_Integer.Digit'Last));

   Multiply (A => (2 => 0, 1 => 1, 0 => 2),
             B => (2 => 0, 1 => 4, 0 => 5));

   Multiply (A => (2 => 0, 1 => 2, 0 => 2),
             B => (2 => 0, 1 => 4, 0 => 5));

   Multiply (A => (2 => 0, 1 => 2, 0 => 3),
             B => (2 => 0, 1 => 4, 0 => 5));
end Use_Big_Integers;
Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
1

It is good style to provide a complete reproducer, but never mind...

When I is going to be used as an index into Y, it is good style to write the loop statement as for I in reverse Y'Range ... end loop;. Similarly for N.

Are you certain that N + I - 1 always is a valid index for Product? I'm pretty sure that you can get both too large and too small indices with your current implementation. I suspect that the too small indices is an off-by-one error in the implementation of the algorithm. The too large indices are because you haven't thought clearly about how to handle integer overflow (the traditional way in Ada is to raise Constraint_Error).

Shouldn't yo check the value of Carry at the end of the function?

Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
  • I can provide a complete solution, but it isn't as much fun as getting it right yourself. – Jacob Sparre Andersen Apr 09 '18 at 06:19
  • Thanks for the response! I was thinking the same thing for the `N + I - 1` index, specifically when N and I are 0. I couldn't think of what to change it to though. I'd love to see a complete solution if you're willing! – Justiciar Apr 09 '18 at 13:36