Fibonacci with ADA and others (Part 2/3)

Now it goes the implementation of the package.

Some points about the design. Firstly the structure of the number is more clear to see here. As is said in the previous post, components of the number are represented by cells of the array. However, in regards to the maximum magnitude each component holds, there are two choices, one is make full use of the whole 32-digit integer, which is the most efficient in terms of memory utilization; and the other is use it to represent a largest multiple of ten it can take, which in this case, for a 32-digit integer type is 10^9. The benefit of the latter is the ease of print as a decimal number.

The current source code implements the second approach, where it declares that maximum value for each component as a constant in the package declaration. The constants are useful for the logic to determine in each step of the calculation of an operation whether a particular component has exceeded the maximum value so a bring-down and a carry to the component ahead is needed.

As the big integer to deal with there is signed integer, the sign of the number is carried by the highest component, and the design specifies that a valid big integer object should not have signs on components other than the highest (this makes the highest component the only one that needs to be flipped in a absolute/negative value operation). An alternative approach might use a separate field to store the sign, but it's not necessary and optimal for this design, as component is not fully utilized even as an signed integer.

Operations like add and subtract on big integers are implemented based on add and subtract on their corresponding absolute numbers; since ADA doesn't allow in any way changing the values of the parameters passed to a function (they are always 'in' parameters), so copies of these input parameters as local variables are always needed as long as changes to these numbers are needed in the course of the calculation. If more efficiency is required, one probably needs to consider using a dynamic internal array or data structure alike instead.

Note there is a method named 'compact' that takes in a big integer object and returns an object representing the same big integer number but having an internal array no greater in length than needed.

  1 with ada.Unchecked_Deallocation;
  2 
  3 with ada.Strings.fixed;
  4 use ada.Strings;
  5 use ada.Strings.fixed;
  6 
  7 package body ariane.numerics.biginteger is
  8 
  9   subtype cmpres_t is integer range -1..1;
 10   subtype sign_t is integer range -1..1;
 11 
 12   -- underlying deallocation method
 13   -- note: seems it has to be declared after the object definition and
 14   --       invoked by a public wrapper method, as the deallocation method
 15   --       needs information of the object type
 16   procedure deallocate is new ada.Unchecked_Deallocation(Object=>object,
 17                                                          Name=>objectptr);
 18 
 19   -- get the maximum of two instances of length_t type
 20   function max(a, b : length_t) return length_t is
 21   begin
 22     if a > b then
 23       return a;
 24     else
 25       return b;
 26     end if;
 27   end max;
 28 
 29   -- get the minimum of two instances of length_t type
 30   function min(a, b : length_t) return length_t is
 31   begin
 32     if a > b then
 33       return b;
 34     else
 35       return a;
 36     end if;
 37   end min;
 38 
 39   -- compacts a given number so that its effective length is the same as
 40   -- the same as its array length
 41   function compact(o : object) return object is
 42     res : object(o.actln);
 43   begin
 44     for i in 1 .. o.actln loop
 45       res.cells(i) := o.cells(i);
 46     end loop;
 47     res.actln := o.actln;
 48     return res;
 49   end;
 50 
 51   -- returns the sign of the given value
 52   function getsgn(o : object) return sign_t is
 53   begin
 54     if o.cells(o.actln) > 0 then
 55       return 1;
 56     elsif o.cells(o.actln) < 0 then
 57       return -1;
 58     else
 59       return 0;
 60     end if;
 61   end getsgn;
 62 
 63   -- returns the absolute value of the big integer object
 64   function getabs(o : object) return object is
 65     res : object := o;
 66   begin
 67     if res.cells(res.actln) < 0 then
 68       res.cells(res.actln) := -res.cells(res.actln);
 69     end if;
 70     return res;
 71   end getabs;
 72 
 73   -- compares the absolute values of the two operands of length_t type
 74   -- ensure the two numbers are non-negative
 75   function cmpasabs(lhs, rhs : object) return cmpres_t is
 76   begin
 77     if lhs.actln < rhs.actln then
 78       return -1;
 79     elsif lhs.actln > rhs.actln then
 80       return 1;
 81     end if;
 82 
 83     for i in reverse 1 .. lhs.actln loop
 84       if lhs.cells(i) < rhs.cells(i) then
 85         return -1;
 86       elsif lhs.cells(i) > rhs.cells(i) then
 87         return 1;
 88       end if;
 89     end loop;
 90 
 91     return 0;
 92 
 93   end cmpasabs;
 94 
 95   -- adds two numbers; ensure the two numbers are non-negative
 96   -- the return value is neither made definite nor compacted
 97   procedure addasabs(lhs, rhs : object; res : out object) is
 98     maxn : length_t := max(lhs.actln, rhs.actln);
 99     minn : length_t := min(lhs.actln, rhs.actln);
100     tmp : integer;
101     carry : integer := 0;
102 
103     procedure handlehighdigits(highref : cells_t) is begin
104       for i in minn + 1 .. maxn loop
105         tmp := highref(i) + carry;
106         if tmp > maxcellval then
107           tmp := tmp - maxmulten;
108           carry := 1;
109         end if;
110         res.cells(i) := tmp;
111       end loop;
112 
113       if carry > 0 then
114         res.cells(maxn + 1) := carry;
115         res.actln := maxn + 1;
116       else
117         res.actln := maxn;
118       end if;
119     end handlehighdigits;
120 
121   begin
122     for i in 1 .. minn loop
123       tmp := lhs.cells(i) + rhs.cells(i) + carry;
124       if tmp > maxcellval then
125         tmp := tmp - maxmulten;
126         carry := 1;
127       else
128         carry := 0;
129       end if;
130       res.cells(i) := tmp;
131     end loop;
132 
133     if lhs.actln > rhs.actln then
134       handlehighdigits(lhs.cells);
135     else
136       handlehighdigits(rhs.cells);
137     end if;
138 
139   end addasabs;
140 
141   -- subtracts rhs from lhs; ensure that lhs is greater than rhs
142   -- ensure the two numbers are non-negative
143   -- the return value is neither made definite nor compacted
144   procedure subasabs(lhs, rhs : object; res : out object) is
145     tmp : integer;
146     carry : integer := 0;
147   begin
148     for i in 1 .. rhs.actln loop
149       tmp := lhs.cells(i) - rhs.cells(i) - carry;
150       if tmp < 0 then
151         tmp := tmp + maxmulten;
152         carry := 1;
153       end if;
154       res.cells(i) := tmp;
155       if tmp /= 0 then
156         res.actln := i;
157       end if;
158     end loop;
159 
160     for i in rhs.actln + 1 .. lhs.actln loop
161       tmp := lhs.cells(i) - carry;
162       if tmp < 0 then
163         tmp := tmp + maxmulten;
164         carry := 1;
165       end if;
166       res.cells(i) := tmp;
167       if tmp /= 0 then
168         res.actln := i;
169       end if;
170     end loop;
171   end subasabs;
172 
173   -- create a big integer object
174   function create(cells : in cells_t) return object is
175     n : length_t := cells'Length;
176     actln : length_t := 1;
177   begin
178     for i in reverse 1 .. n loop
179       if cells(i) /= 0 then
180         actln := i;
181         exit;
182       end if;
183     end loop;
184     declare
185       res : object(actln);
186     begin
187       for i in 1 .. actln loop
188         res.cells(i) := cells(i);
189       end loop;
190       res.actln := actln;
191       return res;
192     end;
193   end create;
194 
195   -- creates a big integer object on heap with value given by the argument
196   function create(o : object) return objectptr is
197     res : objectptr := new object(o.actln);
198   begin
199     for i in 1 .. o.actln loop
200       res.cells(i) := o.cells(i);
201     end loop;
202     res.actln := o.actln;
203     return res;
204   end;
205 
206   -- gets the string representation of the big integer object
207   function tostring(o : in object) return string is
208     res : string := (integer(o.actln) * maxdigitspercell+1) * ' ';
209     wr : positive := 1;
210   begin
211     for i in reverse 1 .. o.actln loop
212       declare
213         tmp : string := integer'Image(o.cells(i));
214         trimmed : string := trim(tmp, both);
215       begin
216         if i = o.actln or else trimmed'length = 9 then
217           overwrite(res, wr, trimmed);
218           wr := wr + trimmed'Length;
219         else
220           declare
221             pad : string := 9 * '0';
222           begin
223             overwrite(pad, 9 - trimmed'length, trimmed);
224             overwrite(res, wr, pad);
225             wr := wr + 9;
226           end;
227         end if;
228       end;
229     end loop;
230 
231     return res;
232   end tostring;
233 
234   -- destroys the big integer object created on heap
235   procedure free(p : in out objectptr) is
236   begin
237     deallocate(p);
238   end free;
239 
240   -- defines operator "+" on big integers
241   function "+"(lhs, rhs : in object) return object is
242     res : object(lhs.actln + rhs.actln + 1);
243     cmp : integer;
244     labs : object := getabs(lhs);
245     rabs : object := getabs(rhs);
246     lsgn : sign_t := getsgn(lhs);
247     rsgn : sign_t := getsgn(rhs);
248   begin
249     if lsgn = rsgn or else lsgn = 0 or else rsgn = 0 then
250       addasabs(labs, rabs, res);
251       if lsgn < 0 or rsgn < 0 then
252         res.cells(res.actln) := -res.cells(res.actln);
253       end if;
254     else
255       cmp := cmpasabs(labs, rabs);
256       if cmp < 0 then
257         subasabs(rabs, labs, res);
258         if rsgn < 0 then
259           res.cells(res.actln) := -res.cells(res.actln);
260         end if;
261       elsif cmp > 0 then
262         subasabs(labs, rabs, res);
263         if lsgn < 0 then
264           res.cells(res.actln) := -res.cells(res.actln);
265         end if;
266       else
267         res.actln := 1;
268         res.cells(1) := 0;
269       end if;
270     end if;
271 
272     declare
273       compacted : object := compact(res);
274     begin
275       return compacted;
276     end;
277 
278   end "+";
279 
280   -- defines operator "-" on big integers
281   function "-"(lhs, rhs : in object) return object is
282     res : object(lhs.actln + rhs.actln + 1);
283     cmp : integer;
284     labs : object := getabs(lhs);
285     rabs : object := getabs(rhs);
286     lsgn : sign_t := getsgn(lhs);
287     rsgn : sign_t := getsgn(rhs);
288   begin
289     if lsgn /= rsgn and then lsgn /= 0 and then rsgn /= 0 then
290       cmp := cmpasabs(labs, rabs);
291       if cmp < 0 then
292         subasabs(rabs, labs, res);
293         if rsgn < 0 then
294           res.cells(res.actln) := -res.cells(res.actln);
295         end if;
296       elsif cmp > 0 then
297         subasabs(labs, rabs, res);
298         if lsgn < 0 then
299           res.cells(res.actln) := -res.cells(res.actln);
300         end if;
301       else
302         res.actln := 1;
303         res.cells(1) := 0;
304       end if;
305     else
306       addasabs(labs, rabs, res);
307       if lsgn < 0 or rsgn < 0 then
308         res.cells(res.actln) := -res.cells(res.actln);
309       end if;
310     end if;
311 
312     declare
313       compacted : object := compact(res);
314     begin
315       return compacted;
316     end;
317   end "-";
318 
319 end ariane.numerics.biginteger;

Also a few things to point out regarding the code and language features.

1. ADA allows counting down (reverse iteration) in a 'for' statement by using 'reverse' reserved word

2. 'declare' block is extremely useful and elegant for defining a variable anywhere in code, and fundamentally allocating space for and instantiating the object on stack. This essentially is an ADA equivalent of arbitrarily placed variable declaration of most C family languages, but with better clarity, explicitness and a good consistency with both the concept and mechanism of allocation and its type system.

3. There is no way to change the content of a input parameter of a record type by setting the member of the method to aliased. And formal parameters can never be declared aliased.

posted @ 2011-12-10 11:50  quanben  阅读(222)  评论(0编辑  收藏  举报