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.

你可能感兴趣的:(fibonacci)