A consequence of the strong typing model implemented in Ada even Strings are to be of fixed size - requiring a declaration before being used. While reading text files, a String buffer is provided though it is almost certain that all the lines are not of the same length. Such variable length dynamic datastructures are a necessity yet the safety of the data structures at run time.
In this projectlet, a simple container library package Ada.Containers.Vectors is exploited as a way to provide dynamically sized arrays of e.g. Integers.
4.1 Learning Objectives
Safe manipulation of dynamically sized arrays (vectors)
Iteration
Fixed Point datatype
4.2 Library foundation
The predefined library offers the generic package Ada.Containers.Vectors which needs to be instantiated with a specific data type in order to support the dynamic arrays of that datatype. In our projectlet, we use the facility to create dynamic arrays of say octal, decimal or hexadecimal digits. In addition dynamic arrays for Natural numbers is also created. The support is utilized for a variety of recreational mathematical algorithms.
0006 | subtype OctalDigit_Type is Natural range 0 .. 7;
0007 | subtype DecimalDigit_Type is Natural range 0 .. 9;
0008 | subtype HexadecimalDigit_Type is Natural range 0 .. 15;
0009 |
0010 | package OctalVector_Pkg is new Ada.Containers.Vectors
0011 | (Natural, OctalDigit_Type);
0012 | subtype OctalDigits_Type is OctalVector_Pkg.Vector;
0013 |
0014 | package DecimalVector_Pkg is new Ada.Containers.Vectors
0015 | (Natural, DecimalDigit_Type);
0016 | subtype DecimalDigits_Type is DecimalVector_Pkg.Vector;
0017 |
0018 | package HexadecimalVector_Pkg is new Ada.Containers.Vectors
0019 | (Natural, HexadecimalDigit_Type);
0020 | subtype HexadecimalDigits_Type is HexadecimalVector_Pkg.Vector;
As shown above we define 3 custom data types - all numerics but with different range specifications. Each then requires an instantiation of the basic Containers.Vectors. The created packages have the support for dynamic arrays of the appropriate data type. The HexadecimalVectors_Pkg supports vectors of HexadecimalDigit_Type elements.
0033 | function Convert (value : Natural) return DecimalVector_Pkg.Vector is
0034 | result : DecimalVector_Pkg.Vector;
0035 | valnow : Natural := value;
0036 | begin
0037 | if valnow < 10 then
0038 | result.Append (valnow);
0039 | return result;
0040 | end if;
0041 | loop
0042 | result.Prepend (valnow mod 10);
0043 | valnow := valnow / 10;
0044 | if valnow < 1 then
0045 | exit;
0046 | end if;
0047 | end loop;
0048 | return result;
0049 | end Convert;
In the above example, a number is split into its decimal digits the result being a dynamically sized array. The reverse operation is performed by retrieving individual elements:
~/bin/codemd ../../toolkit/adalib/src/numbers.adb -x Value -l
0097 | function Value (digs : DecimalVector_Pkg.Vector) return Natural is
0098 | use DecimalVector_Pkg;
0099 | result : Natural := 0;
0100 | curs : Cursor := digs.First;
0101 | begin
0102 | loop
0103 | result := result * 10 + Element (curs);
0104 | if curs = Last (digs) then
0105 | exit;
0106 | end if;
0107 | curs := Next (curs);
0108 | end loop;
0109 | return result;
0110 | end Value;
Alternatively, the library and thus the instantiated version, supports a cleaner and more elegant way to iterate over the elements:
0199 | procedure ShowNumber (cursor : NumbersVector_Pkg.Cursor) is
0200 | begin
0201 | Put (NumbersVector_Pkg.Element (cursor)'Image);
0202 | Put (", ");
0203 | end ShowNumber;
0204 |
0205 | procedure Show (num : NumbersVector_Pkg.Vector) is
0206 | begin
0207 | num.Iterate (ShowNumber'Access);
0208 | end Show;
4.4 Fixed Point Data type
We have so far dealt with variations of Integer and Float data types; neither of these are convenient for a notion such as abundance. Somewhat unique to Ada is the fixed point data type which can take on discrete values but in an arbitrary range including less than 1 and is a better fit.
0046 | -- Ref: https://mathworld.wolfram.com/Abundancy.html
0047 | type AbundancyRatio is delta 0.01 digits 6;
0048 | function Abundance (num : Natural) return AbundancyRatio;
This is fundamendal to identifying Multiperfect numbers for example.
4.5 Applications
Even a casual visit to a site like Wolframalpha inspires one to start excursions. With a foundational support for computing the list of divisors and factors many applications become far simpler.
0227 |
0228 | function DivisorSum (num : Natural) return Natural is
0229 | use NumbersVector_Pkg;
0230 | divs : constant Vector := Divisors (num);
0231 | result : Natural := 0;
0232 | procedure Summer (cur : Cursor) is
0233 | begin
0234 | result := result + Element (cur);
0235 | end Summer;
0236 | begin
0237 | divs.Iterate (Summer'Access);
0238 | return result;
0239 | end DivisorSum;
0240 |
0241 | function Abundance (num : Natural) return AbundancyRatio is
0242 | result : AbundancyRatio;
0243 | divsum : constant Natural := DivisorSum (num);
0244 | begin
0245 | result := AbundancyRatio (Float (divsum) / Float (num));
0246 | return result;
0247 | end Abundance;
0248 |
0249 | function Factors (num : Natural) return NumbersVector_Pkg.Vector is
0250 | result : NumbersVector_Pkg.Vector;
0251 | curfac : Natural := 2;
0252 | curnum : Natural := num;
0253 | sqrtnum : Natural;
0254 | begin
0255 | sqrtnum := 1 + Natural (Sqrt (Float (num)));
0256 | if curfac > sqrtnum then
0257 | result.Append (num);
0258 | return result;
0259 | end if;
0260 | loop
0261 | if curnum mod curfac = 0 then
0262 | result.Append (curfac);
0263 | curnum := curnum / curfac;
0264 | if curnum <= 1 then
0265 | exit;
0266 | end if;
0267 | else
0268 | curfac := curfac + 1;
0269 | end if;
0270 | end loop;
0271 | return result;
0272 | end Factors;
~/bin/codemd ../../toolkit/adalib/src/numbers.adb -x Prime -l
0287 | function IsPrime (num : Natural) return Boolean is
0288 | use Ada.Containers, NumbersVector_Pkg;
0289 | facs : constant Vector := Factors (num);
0290 | begin
0291 | if facs.Length > 1 then
0292 | return False;
0293 | end if;
0294 | return True;
0295 | end IsPrime;
4.5.1 Example usage
Unary algorithms
~/bin/num U 5749 8128 272
================= Number 5749
Octal 13165 Octal back conversion Ok
Decimal 5749 Decimal back conversion Ok
Hexadecimal 1675 HexaDecimal back conversion Ok
Divisors 1, 5749,
DivisorSum 5750
Prime Factors 5749,
Prime No Yes
Perfect No No
MultiPerfect No No
Kaprekar No No
================= Number 8128
Octal 17700 Octal back conversion Ok
Decimal 8128 Decimal back conversion Ok
Hexadecimal 1fc0 HexaDecimal back conversion Ok
Divisors 1, 2, 4, 8, 16, 32, 64, 127, 254, 508, 1016, 2032, 4064, 8128,
DivisorSum 16256
Prime Factors 2, 2, 2, 2, 2, 2, 127,
Prime No No
Perfect No Yes
MultiPerfect No Yes
Kaprekar No No
================= Number 272
Octal 420 Octal back conversion Ok
Decimal 272 Decimal back conversion Ok
Hexadecimal 110 HexaDecimal back conversion Ok
Divisors 1, 2, 4, 8, 16, 17, 34, 68, 136, 272,
DivisorSum 558
Prime Factors 2, 2, 2, 2, 17,
Prime No No
Perfect No No
MultiPerfect No No
Kaprekar No No
Distinctness - given a vector, develop an algorithm to determine if there are any duplicate members.
Basic statistical support including functions for mean, standard deviation, kurtosis, skewness for vectors and correlation coefficient for multiple vectors will be a great lightweight addition to the toolkit.
https://en.wikipedia.org/wiki/ISBN describes multiple forms of ISBN and the algorithms for calculating the checksum. A function that validates an ISBN - including the check digit is a reasonable exercise in extending the numbers package outlined in this projectlet.