❌ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Before yesterdayAda – The Craft of Coding

Coding Ada: get vs. get_line (ii) – some caveats

By: spqr
9 February 2022 at 15:08

When used separately, get and get_line rarely cause issues. When used together, there can be some issues. Consider the following code:

a : integer;
s : string(1..4);

get(a);
s := get_line;
put(a);
put_line(s);

Here is the dialog obtained when running the program containing this code:

4

raised CONSTRAINT_ERROR : testgetm.adb:14 length check failed

It allows the user to input the number 4, but then balks and pulls an exception. What went wrong? To see what happened we have to consider exactly what these input functions do. As we have seen, calls to get for numeric and string input skip over blanks and line terminators that come before the number. That’s why it is possible to press the <return> key several times between numbers being entered. However these get functions do not skip over any blanks and line terminators that come after the number. After the integer variable a is read, the input pointer is set to the line terminator after 4.

Because get_line stops reading when it encounters a line terminator, it stops and sets the length of the string to zero to indicate a null string. As the size of the (fixed) string is expected to be 4 characters in length, an exception is actioned. One way to circumvent this is to type the string on the same line as the number 4. For example:

6tool
          6tool

This illustrates another feature of numeric input. The get function for numeric data stops reading when a non-numeric character is encountered. The non-numeric character “t” is not read and is still available to be read by the next call to an input function.

This solution is not really optimal. A better idea is to use the function skip_line. When skip_line is called, it moves the input pointer to the next line. It is most useful when using a get_line function after getting a numeric value. Here is an example of its use:

a : integer;
s : string(1..4);

get(a);
skip_line;
s := get_line;
put(a);
put_line(s);

Note that if we use a unbound_string instead of the fixed string, then the program will not convulse, but it also will skip the string input… but it will print out a string. Why? Well the unbound_string will actually store the line terminator in the string… so it does read something, just not what it wanted.

To make things more consistent, it is also possible to read all input in as strings using get_line, and convert the string to numbers. Below is an example of the code. The first value is input to the unbound_string buf, which is then converted to a string, and then an integer, and assigned to the integer variable a.

a : integer;
buf : unbounded_string;

buf := get_line;
a := integer'value(to_string(buf));
buf := get_line;
put(a);
put_line(buf);

Note that weird things can also occur when using consecutive get_line() statements. For example consider the code:

s,t : string(1..4);
get_line(s,len);
put(s); put(len);
get_line(t,len);
put(t); put(len);

Here is an example of it running:

tool
tool          4�#           0

The first string input “tool”. works, the second string reads in the line terminator. The same does not occur using the alternate form of get_line. For example the code below works as it should.

s,t : string(1..4);
s := get_line;
put(s);
t := get_line;
put(t);

Ada is defensive by default

By: spqr
4 February 2022 at 20:40

The type system of Ada is not merely strongly typed, but sometimes referred to as being super-strongly typed, because it does not allow for any level of implicit conversions. None. Consider the following code in C:

#include <stdio.h>
int main(void) {
   float e;
   int a;
   e = 4.7631;
   a = e;
   printf("%d\n", a);
   return 0;
}

This is valid code that will compile, run and produce the expected result – the result printed out is 4. C has no issues performing an implicit conversion. Ada on the other hand won’t even compile similar code. Here is the Ada equivalent:

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure testtyped is
    a : integer;
    e : float;
begin
   e := 4.7631;
   a := e;
   put(a);
end testtyped;

When an attempt is made to compile this, the following error will occur:

testtyped.adb:8:09: expected type "Standard.Integer"
testtyped.adb:8:09: found type "Standard.Float"

The problem here is that a and e are clearly not the same. Instead, one has to explicitly convert between types, which promotes good design by preventing the mixing of types. In this case, Line 9 in the above code becomes:

a := integer(e);

Also at run-time, errors such as illegal memory accesses, buffer overflows, range violations, off-by-one errors, and array access are tested. These errors can then be handled safely instead of the way languages like C do. For example, consider the following C code:

#include <stdio.h>
int main(void) {
   int x[100];
   x[101] = 1;
   printf("%d\n", x[101]);
   return 0;
}

Again, this is valid code that will compile, run and produce unexpected results. A value is actually stored outside the array x. Consider the same code written in Ada:

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure testarrOOB is
    x : array (1..100) of integer;
begin
   x(101) := 1;
   put(x(101));
end testarrOOB;

Compiling the program will result in the following warnings:

testarroob.adb:6:06: warning: value not in range of subtype of "Standard.Integer" defined at line 4
testarroob.adb:6:06: warning: "Constraint_Error" will be raised at run time
testarroob.adb:7:10: warning: value not in range of subtype of "Standard.Integer" defined at line 4
testarroob.adb:7:10: warning: "Constraint_Error" will be raised at run time

It does produce an executable, but running the executable results in an exception being triggered:

raised CONSTRAINT_ERROR : testarroob.adb:6 range check failed

Why is this important? Because in real-time systems, errors such as this have to be avoided. Now ask yourself why it is possible the F-35 code written in C++ does work as intended.

Coding Ada: get vs. get_line (ii) – some caveats

By: spqr
9 February 2022 at 15:08

When used separately, get and get_line rarely cause issues. When used together, there can be some issues. Consider the following code:

a : integer;
s : string(1..4);

get(a);
s := get_line;
put(a);
put_line(s);

Here is the dialog obtained when running the program containing this code:

4

raised CONSTRAINT_ERROR : testgetm.adb:14 length check failed

It allows the user to input the number 4, but then balks and pulls an exception. What went wrong? To see what happened we have to consider exactly what these input functions do. As we have seen, calls to get for numeric and string input skip over blanks and line terminators that come before the number. That’s why it is possible to press the <return> key several times between numbers being entered. However these get functions do not skip over any blanks and line terminators that come after the number. After the integer variable a is read, the input pointer is set to the line terminator after 4.

Because get_line stops reading when it encounters a line terminator, it stops and sets the length of the string to zero to indicate a null string. As the size of the (fixed) string is expected to be 4 characters in length, an exception is actioned. One way to circumvent this is to type the string on the same line as the number 4. For example:

6tool
          6tool

This illustrates another feature of numeric input. The get function for numeric data stops reading when a non-numeric character is encountered. The non-numeric character “t” is not read and is still available to be read by the next call to an input function.

This solution is not really optimal. A better idea is to use the function skip_line. When skip_line is called, it moves the input pointer to the next line. It is most useful when using a get_line function after getting a numeric value. Here is an example of its use:

a : integer;
s : string(1..4);

get(a);
skip_line;
s := get_line;
put(a);
put_line(s);

Note that if we use a unbound_string instead of the fixed string, then the program will not convulse, but it also will skip the string input… but it will print out a string. Why? Well the unbound_string will actually store the line terminator in the string… so it does read something, just not what it wanted.

To make things more consistent, it is also possible to read all input in as strings using get_line, and convert the string to numbers. Below is an example of the code. The first value is input to the unbound_string buf, which is then converted to a string, and then an integer, and assigned to the integer variable a.

a : integer;
buf : unbounded_string;

buf := get_line;
a := integer'value(to_string(buf));
buf := get_line;
put(a);
put_line(buf);

Note that weird things can also occur when using consecutive get_line() statements. For example consider the code:

s,t : string(1..4);
get_line(s,len);
put(s); put(len);
get_line(t,len);
put(t); put(len);

Here is an example of it running:

tool
tool          4�#           0

The first string input “tool”. works, the second string reads in the line terminator. The same does not occur using the alternate form of get_line. For example the code below works as it should.

s,t : string(1..4);
s := get_line;
put(s);
t := get_line;
put(t);

spqr

Coding Ada: Using multiple packages

By: spqr
8 February 2022 at 15:25

So Ada uses packages to implement a form of encapsulation, which actually existed before the whole hoopla with C++ evolved. Packages have a specification, and an implementation. In the example below, we will create three packages to generate an image comprised of random values, i.e. a noise image. Here is an example (it can be saved as a text image).

The first package only has a specification, and is called datas. It includes the data structures needed by the other packages (i) image, and (ii) print (and also the main program). Here is what the specification looks like (datas.ads):

package datas is
   type randRange is new Integer range 0..255;
   type randImg is array(1..500,1..500) of randRange;
end datas;

It includes a type randRange, which specifies integers in the range 0..255, and a type randImg, which is a 2D array of type randRange. Next is the package image – its task is to generate an image of random numbers in the range 0..255. Here is the specification (image.ads):

with ada.numerics.discrete_random;
with datas; use datas;

package image is
   package Rand_Int is new ada.numerics.discrete_random(randRange);
   use Rand_Int;
   procedure makeImage(img : out randImg);
end image;

It creates a new instance of the package discrete_random, to deal with generating numbers in the range 0..255. It also contains a function makeImage(), which uses the types in package datas to generate an image. Here is the package implementation (image.adb):

package body image is

procedure makeImage(img : out randImg) is
   gen : Generator;
begin
   reset(gen);
   for i in 1..img'last(1) loop
      for j in 1..img'last(2) loop
         img(i,j) := random(gen);
      end loop;
   end loop;
end;

end image;

The last package, print just prints out the image as a series of rows (to standard output, but it can be redirected to a file, e.g. a.out >randim.txt). Here is the specification (print.ads):

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with datas; use datas;

package print is
   procedure printImage(img : in randImg);
end print;

And the associated implementation (print.adb):

package body print is

procedure printImage(img : in randImg) is
begin
   for i in 1..img'last(1) loop
      for j in 1..img'last(2) loop
         put(randRange'Image(img(i,j)));
      end loop;
      new_line;
   end loop;
end;

end print;

Finally the main program that calls them all (test.adb)

with image; use image;
with print; use print;
with datas; use datas;

procedure test is
   im : randImg;

begin
   makeImage(im);
   printImage(im);
end test;

This program can be compiled simply using the main file:

% gnatmake -Wall test.adb

Obviously packages can be compiled,bu they can’t be linked or executed. Each of the packages can also be compiled separately:

% gnatmake -Wall image.ads image.adb
% gnatmake -Wall print.ads print.adb
% gnatmake -Wall datas.ads
% gnatmake -Wall test.adb

spqr

Ada is defensive by default

By: spqr
4 February 2022 at 20:40

The type system of Ada is not merely strongly typed, but sometimes referred to as being super-strongly typed, because it does not allow for any level of implicit conversions. None. Consider the following code in C:

#include <stdio.h>
int main(void) {
   float e;
   int a;
   e = 4.7631;
   a = e;
   printf("%d\n", a);
   return 0;
}

This is valid code that will compile, run and produce the expected result – the result printed out is 4. C has no issues performing an implicit conversion. Ada on the other hand won’t even compile similar code. Here is the Ada equivalent:

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure testtyped is
    a : integer;
    e : float;
begin
   e := 4.7631;
   a := e;
   put(a);
end testtyped;

When an attempt is made to compile this, the following error will occur:

testtyped.adb:8:09: expected type "Standard.Integer"
testtyped.adb:8:09: found type "Standard.Float"

The problem here is that a and e are clearly not the same. Instead, one has to explicitly convert between types, which promotes good design by preventing the mixing of types. In this case, Line 9 in the above code becomes:

a := integer(e);

Also at run-time, errors such as illegal memory accesses, buffer overflows, range violations, off-by-one errors, and array access are tested. These errors can then be handled safely instead of the way languages like C do. For example, consider the following C code:

#include <stdio.h>
int main(void) {
   int x[100];
   x[101] = 1;
   printf("%d\n", x[101]);
   return 0;
}

Again, this is valid code that will compile, run and produce unexpected results. A value is actually stored outside the array x. Consider the same code written in Ada:

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure testarrOOB is
    x : array (1..100) of integer;
begin
   x(101) := 1;
   put(x(101));
end testarrOOB;

Compiling the program will result in the following warnings:

testarroob.adb:6:06: warning: value not in range of subtype of "Standard.Integer" defined at line 4
testarroob.adb:6:06: warning: "Constraint_Error" will be raised at run time
testarroob.adb:7:10: warning: value not in range of subtype of "Standard.Integer" defined at line 4
testarroob.adb:7:10: warning: "Constraint_Error" will be raised at run time

It does produce an executable, but running the executable results in an exception being triggered:

raised CONSTRAINT_ERROR : testarroob.adb:6 range check failed

Why is this important? Because in real-time systems, errors such as this have to be avoided. Now ask yourself why it is possible the F-35 code written in C++ does work as intended.

spqr

C++ versus Ada for safety critical software (ii)

By: spqr
2 April 2021 at 16:35

The problem with switch

The switch statement in C/C++ is horrible. It was never really that well thought out. The main problem is that the default label is entirely optional. There is no requirement to handle all possible values of an expression. The other issue is the break statement, which if not included may result in an unintended fall-through. In comparison, Ada provides a case statement which is safer. Firstly, a case statement in Ada must cover every possible value of the case expression, it will be checked at compile time. For example here is an example which classifies temperatures for an oven.

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safecase is

   n : integer;

begin
   put("Enter an oven temperature (F): ");
   get(n);
   case n is
      when 200 .. 299 => put_line("cool oven");
      when 300 .. 325 => put_line("slow oven");
      when 326 .. 350 => put_line("moderately slow oven");
      when 351 .. 375 => put_line("moderate oven");
      when 376 .. 400 => put_line("moderately hot oven");
      when 401 .. 450 => put_line("hot oven");
      when 451 .. 500 => put_line("very hot oven");
      when others => put_line("temperature out of bounds");
   end case;

end safecase;

If the others line were omitted, then the following compiler error would propagate, implying values 199 and below, and 501 and above are not accounted for.

safecase.adb:11:04: missing case values: -16#8000_0000# .. 199
safecase.adb:11:04: missing case values: 501 .. 16#7FFF_FFFF#

A when branch can specify a single value, or a range of values, or any combination separated by the | symbol. The optional others branch covers the values not included in earlier branches. When execution of the statements in the selected branch has been completed, control resumes after the end case statement. Unlike C++, Ada does not allow fall-through to the next branch.

The problem with statements

In C/C++ there are simple statements , and compound statements enclosed in {}. It is therefore easy to create a logic error simply by failing to enclose more than one statement in a compound. Below is an example of a piece of code in which the logic is erroneous:

for (i=0, i<50; i=i+2)
   a[i] = 0;
   a[i+1] = 1;

The code is not contained within a compound statement, so only the first statement is associated with the for loop. Ada has no concept of a simple statement, and all control structures are “blocked”. For example the code above in Ada would be:

for i in 1..50 loop
   a(i*2-1) := 0;
   a(i*2) := 1;
end loop;

The problem with function returns

C++ only provides a single type of subprogram – the function. It is possible for a function to omit the name of the return type, in which case the return type is set to int. A function that returns nothing (i.e. a procedure) has a void return type. Theoretically every function should have a return type as it should be used to indicate whether a function succeeds or fails (hence the default to int). Many programmers of course don’t bother with indicating the success of a function, and hence ignore the return value. Failure to heed a function failure can result in the failure of a safety-critical system. Consider the following C++ program:

#include <iostream>

int doubled(int x){
   return x*2;
}

int quadruple(int x){
   return doubled(doubled(x));
}

int main(){
   quadruple(5);
   return 0;
}

When this is compiled (even with warnings) there is no issue, even though the function call to quadruple() is treated like like a statement, with the return sent off to the nether world. It is then easy to have such failings happen in a large program, and hard to trace a source of error. Ada return values can be used for the same purposes as C/C++ return values, but they cannot be ignored. Here is the same program coded in Ada.

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safefunc is
   n : Integer;

   function quadruple (x: Integer) return Integer is
      function double (x: Integer) return Integer is
      begin
         return x * 2;
      end double;
      result : Integer;
   begin
      result := double(double(x));
      return result;
   end Quadruple;

begin
   quadruple(5);
end safefunc;

Compiling this will lead to an error on line 18, the source of the call to quadruple():

safefunc.adb:18:04: cannot use call to function "quadruple" as a statement
safefunc.adb:18:04: return value of a function call cannot be ignored

This has to be fixed by adding a return value, of the form:

n := quadruple(5);

Ada was designed to support safety in modern safety critical systems. The examples outlined offer an insight into how Ada is better than C++ for such systems. Neither C nor C++ were designed to support the needs of real-time safety-critical systems.

spqr

C++ versus Ada for safety critical software (i)

By: spqr
1 April 2021 at 17:33

Safety critical software is incredibly important for systems designed for applications such as aerospace, rail transportation, power stations and the like. This the F-35 hey, built largely in C++. In some cases these systems have been built in languages such as C and C++, largely because they have a very inexpensive labour force. However the use of these languages, as opposed to Ada have significant hidden costs and incur safety risks due in part to the unweildy and awkward nature of the languages. This post compares some of the features of C++ and Ada.

The problem with arrays

There are a number of issues with arrays in C++:

  • Every array index must begin at zero. Due to this array indexing is always off-by-one from the element number of the array resulting in a tendency to index past the end of the array.
  • C++ does not do bounds checking automatically, instead relying on it to be manually coded.
  • The use of 0-based arrays means that 2D arrays are indexed using a single index which is derived using a formula. This maps very closely to how the array is stored in memory, but does not support the idea of matrix in an unambiguous manner.
  • Arrays are indexed using integers.

In comparison, Ada arrays include a declaration of both the index-type, and the element type. All operations performed on the array are subjected to bounds checking, and the array indices may be of any discrete type. Below is an example of creating an array in Ada to hold a 1000×1000 image.

procedure safearrays is

subtype imgIndex is integer range 1..1000;
subtype uint8 is integer range 0..255;
type image is array(imgIndex,imgIndex) of uint8;
image1 : image;

begin
   for x in image1'range(1) loop
      for y in image1'range(2) loop
         image1(x,y) := 0;
      end loop;
   end loop;

end safearrays;

Subtypes can be used to constrain the valid range on index and array values. In this case the subtype uint8 is created to hold unsigned integers with values 0..255, and the subtype imgIndex is used to hold the index range for the image. The type image is then created using these types. If any attempt is made to access an element outside the array image, the following error occurs (where X is the line number):

raised CONSTRAINT_ERROR : safearrays.adb:X index check failed

The Ada syntax has a number of benefits. Firstly Ada arrays are first class types, not structures created as an afterthought using pointers. No pointers are involved, to at least they are handled internally. Ada also implicitly checks array bounds, meaning unlike C/C++ there will never be an issue with accessing a piece of memory not associated with an array. The Ada Range attribute means that bounds do no have to be hard coded, which makes it easier to maintain a piece of code.

The problem with strings

Let’s face it strings in C/C++ are generally horrible. Nobody should have to deal with a string terminator, the null character, ‘\0’. This means strings always have to be one element larger than the characters being stored. This means if a string is created, the null character must be added, otherwise there is the risk of issues with array bounds, or garbage being extracted from elements with no value. C/C++ provide a whole string library to manipulate strings, for example strcpy() to copy a string. Like numeric arrays, no bounds checking is performed on strings.

There are of course inherent safety concerns with C++ strings. The first is that they do not automatically maintain information about the length of the information contained in a string, and need the strlen() function to do that, assuming the string is null terminated. The second is that they do not prevent information being stored beyond the physical end of the string. A missing null character might mean the standard string functions process beyond the bounds of the string. C++ does provide a String class. Below is an example which creates a string, str, of length 10 (although in reality it should only hold 9 characters). Values are then assigned to all 10 elements of str, and 20 elements of str are printed out, as well as the length of the string.

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

int main(){
   char str[10];
   for (int i=0; i<10; i=i+1)
      str[i] = 65+i;
   for (int i=0; i<20; i=i+1)
      cout << str[i];
   cout << strlen(str) << endl;
   return 0;
}

The output is as expected from C++ – the first 10 uppercase characters followed by a bunch of garbage, and a length of 11.

    ABCDEFGHIJ
    
    `�11

Ada provides a predefined array fixed type, String. This can either be specified using bounds (line 1), or an unconstrained array (line 2), where the bounds are deduced from the initialization expression.

1   quoteyoda : String(1..30) := "Do or do not, there is no try!";
2   quoteyoda : constant String := "Do or do not, there is no try!";

Ada provides three forms of strings: the type String is a fixed length string, the type Bounded_String provides a fixed buffer than contains string data up to the length of the buffer, and Unbounded_String which uses a dynamically allocated buffer to contain data without having to define the length. There is no need for special functions to copy from one String to another, or one Bounded_String to another, however functions are provided to convert between types, for example a String to an Unbounded_String. The main benefit of strings in Ada is that memory outside the buffer associated with the string cannot be corrupted. Here is a simple example in Ada which tries to access the 11th element of the array str.

with Ada.Text_IO; use Ada.Text_IO;

procedure safestr is
str : String(1..10);
begin
   str := "ABCDEFGHIJ";
   put(str);
   put(str(11));
end safestr;

It is not possible. Compiling it produces the following messages:

safestr.adb:8:12: warning: value not in range of subtype of "Standard.Integer" defined at line 4
safestr.adb:8:12: warning: "Constraint_Error" will be raised at run time

And executing the code causes “raised CONSTRAINT_ERROR : safestr.adb:8 range check failed“. So Ada catches the error, and C++ using the standard array of characters fails to.

spqr

Programming made easy – nested loops

By: spqr
1 May 2020 at 13:21

We’re not quite done with loops yet. There are still a few things to learn, although some can be left for later. There are certain situations where it is appropriate to use a loop inside another loop. Such a loop is called a nested loop. A nested loop is executed, essentially, from the inside out. Each loop is like a layer and has its own loop control, its own condition and its own loop body. Below is a schematic of three nested loops.

A loop inside a loop, inside a loop.

There are many different kinds of nested loops, and they are largely dependent on the underlying algorithm. Below are some examples of the types of nested loops that are possible.

Various examples of nested loops

We will consider how a nested loop can be used to create a times table. It basically works by iterating the inner loop 12 times for every iteration of the outer loop. So, 12 × 12 = 144 iterations. The outer loop is highlighted in blue, and the inner loop in red. Here is a sample output:

 1   2   3   4   5   6   7   8   9  10  11  12
 2   4   6   8  10  12  14  16  18  20  22  24
 3   6   9  12  15  18  21  24  27  30  33  36
 4   8  12  16  20  24  28  32  36  40  44  48
 5  10  15  20  25  30  35  40  45  50  55  60
 6  12  18  24  30  36  42  48  54  60  66  72
 7  14  21  28  35  42  49  56  63  70  77  84
 8  16  24  32  40  48  56  64  72  80  88  96
 9  18  27  36  45  54  63  72  81  90  99 108
10  20  30  40  50  60  70  80  90 100 110 120
11  22  33  44  55  66  77  88  99 110 121 132
12  24  36  48  60  72  84  96 108 120 132 144

Ada

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure nested is 
begin
   for x in 1..12 loop
      for y in 1..12 loop
         put(x*y, width=>4);
      end loop;
      new_line;
   end loop;
end nested;

Fortran

program nested
   integer :: x, y
   do x = 1, 12
      do y = 1, 12
         write(*,10,advance='no') xy
         10 format(I4)
      end do
      write(*,*)
   end do
end program nested

C

#include <stdio.h>
int main(void){
   int x, y;
   for (x=1; x<=12; x=x+1)
   {
      for (y=1; y<=12; y=y+1)
         printf("%4d", x*y);
      printf("\n");
   }
   return 0;
}

Julia

for x = 1:12
   for y = 1:12
      @printf("%4d", x*y)
   end
   println()
end
println(h)

spqr

Programming made easy – loops (iii)

By: spqr
27 April 2020 at 13:44

There are of course other loops. These loops are normally used when the number of times a loop iterates isn’t known. These type of loops generally use some form of condition to control the loop. The most prominent type of loop is the while loop. A while loop checks a condition, and if true, processes the code in the loop body, otherwise it exists the loop. Unlike the for loop, which controls the values of the loop index in terms of updating its value, in the loops like while, this process must be added, i.e. the value of i is given the value 1, before the loop is activated, and its value is incremented within the loop body.

We will consider some of these loops using the same Harmonic series, this time in increasing order. The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   i = 1
   do while (i <= n)
      h = h + 1.0/i
      i = i + 1
   end do
   write(*,*) h
end program harmonic

Here the index variable, i, must be initiated to the value 1 before the loop begins, and incremented within the loop. The condition states that the loop continues while the value of i is less than or equal to n, i.e. (i <= n) . Fortran also provides a more generic do/end do loop.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   i = 1;
   while (i <= n){
      h = h + 1.0/i;
      i = i + 1;
   }
   printf("%lf\n", h);
   return 0;
}

In the C version, the structure is almost the same, except that the do while of Fortran has been replace with a simple while. With the addition of the statement to increment the value of i, the loop “contents” are now enclosed within { and }. C also has another loop, the do/while() loop, that performs the loop once, then evaluates the condition at the end.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n, i : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   i := 1;
   while i <= n loop
      h := h + 1.0/float(i);
      i := i + 1;
   end loop;
   put(h);
end harmonic;

In Ada, the while loop looks similar to that of the other to Fortran and C, it is just subtleties in syntax that set them apart. Here there are no parenthesis around the condition. Also note that in the for loop, the index variable, i, did not have to be declared, but here it does. Ada also provides a generic loop/end loop loop.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
i = 1
while i <= n
   h = h + 1.0/i
   i = i + 1
end
println(h)

Same deal here. Similar to Ada, except the loop keyword after the condition isn’t needed. There are only two loops in Julia.

As you can see, over four languages, the only thing that fundamentally changes is the basic syntax of the loop.

spqr

Programming made easy – loops (ii)

By: spqr
24 April 2020 at 14:39

With the basics of for loops, let’s look at an example of an actual program which calculates the Harmonic series, which is an infinite series of the form:

h(n) = 1 + 1/2 + 1/3 + … + 1/n

The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.  For interest sake, the loops are presented in reverse, i.e. 1/n + 1/(n-1) + … + 1, as it illustrates clearly how each language deals with the simple issue of a decreasing index. In each case the starting value of the index variable i is n, and the ending value is 1. Here it is the algorithm for the Harmonic series depicted visually, clearly showing the role of the loop:

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   do i = n,1,-1
      h = h + 1.0/i
   end do
   write(*,*) h
end program harmonic

Here the third item (the modifier) in the Fortran loop denotes the type of change to the loop index, in this case, a decrease by 1. In a  normal loop incrementing by 1, the modifier can be omitted.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   for (i=n; i>=1; i=i-1)
      h = h + 1.0/i;
   printf("%lf\n", h);
   return 0;
}

In the C version, the loop index is decremented using the statement i=i–1. For more than one statement after the for loop definition, the statements would have to be encapsulated in { and }.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   for i in reverse 1..n loop
      h := h + 1.0/float(i);
   end loop;
   put(h);
end harmonic;

In Ada, the keyword reverse is used to specify a loop will be decreasing in value.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
for i = n:-1:1
   h = h + 1.0/i
end
println(h)

Here the index modifier is placed in the centre, n:-1:1, implying i has the values n to 1, modified by -1 each loop iteration. In a  normal loop incrementing by 1, the modifier can be omitted.

spqr

Programming made easy – loops (i)

By: spqr
23 April 2020 at 19:53

Humans are, by nature, repetitive beings. Watch the Star Trek: TNG episode “Cause and Effect”, and you will quickly understand what a loop is as the Enterprise is caught in a causality time loop. There are also times when programs must do a task more than once. If the repetition is limited to some countable number of recurrences with a foreseeable end then it is called a loop. And if, at least in the perception of the individual who is experiencing a certain situation, there is no end in sight then one is talking about endless or infinite loops. All of the above can be desirable or not. Here is a visual illustration:

The code in the program moves linearly from statement A, until it encounters a loop. At the start of the loop, the loop control tests some condition. If the condition is true, then the loop activates the piece of code (made up of one or more statements) in what is called the loop body. When that code is done the loop returns to the loop control, and the process starts all over. If the condition in the loop control becomes false, nothing happens, and the program ends the loop, and continues on to statement B. There is also a possibility that the first time into the loop, the condition becomes false, and in that case the loop is bypassed.

Loops are structures that allow for repetition – repeating an activity a number of times. There are different kinds of repetition:

  1. Loops where the number of repetitions is known before the loop activates.
  2. Loops where the number of repetitions isn’t known, and is controlled by some condition.
  3. Loops that are infinite, i.e. could continue to infinity and beyond.

The best example of the first case encountered in most languages is the for loop. Here we use a variable called a loop index to control the loop. Each time a loop loops it is called an iteration. So if the loop index is x, and it repeats 100 times, then x will begin with a value of 1, and increment by 1 each iteration, until x reaches 100. In Julia this is easily achieved in the following manner (in this case the loop simply prints out each value of x):

for x = 1:100
   println(x)
end

In Ada it takes a similar format (the printing part is a little different, but just ignore that bit):

for x in 1..100 loop
    put(x); 
    new_line;
end loop;

Ada uses the keyword loop to signify it as a loop. Fortran also uses a similar construct, but has never used the for loop, opting instead for the keyword do. Same concept, different name.

do x = 1, 100
   write(*,*) x
end do

C also uses a for loop, although it can be somewhat more convoluted:

for (x=1; x<=100; x=x+1)
   printf("%d\n",x);

All have their varied syntax, but achieve the same goal – a known number of iterations.

spqr

Coding Ada: Bitwise operators

By: spqr
7 April 2020 at 02:14

In Ada, you can find similar bitwise operators to other languages: and, or, xor, and operators that shift left and right. The trick is that they use modular types. These types are unsigned and have “wrap-around” semantics. Consider the following declaration:

subtype byte is unsigned_8;
package byteIO is new ada.text_io.modular_io(byte);
x, y, z: byte;

This defines an integer type whose values range from 0 to 255 (on most processors), i.e. the integers that will fit in a byte. Since it’s “modular”, it means that arithmetic operations on the type wrap around (i.e. if you add 130 + 130, you will get 4. With this declaration, you can create binary numbers:

x := 2#00011110#;
y := 2#11110100#;
z := 2#11110000#;

Here x=30, y=244, and z=240. So we can then use bitwise operators (found in the Ada package Interfaces) to swap the numbers:

x := x xor y;
y := y xor x;
x := x xor y;

The numbers can be printed out with this code:

put_line(unsigned_8'image(x));
put_line(unsigned_8'image(y));

You can also use put from the sub-package byteIO:

byteIO.put(item => z, base => 2);

If we want to convert a number input from integer to type byte, we can do so in the following manner:

w: integer;
get(w);
z := byte'val(integer'pos(w));
byteIO.put(item => z, base => 2);

The byte’val returns the base type of byte. For example, if the value input by the user is 17 (w), then the value assigned to z will be 10001. What about shift operators? There is shift_left() and shift_right(). Here’s an example:

zs: byte; 
zs := shift_left(z,1);
byteIO.put(item => zs, base => 2);

If the user inputs 12, then the value of zs will be 24, as it has been shifted left 1 bit.

spqr

C++ versus Ada for safety critical software (ii)

By: spqr
2 April 2021 at 16:35

The problem with switch

The switch statement in C/C++ is horrible. It was never really that well thought out. The main problem is that the default label is entirely optional. There is no requirement to handle all possible values of an expression. The other issue is the break statement, which if not included may result in an unintended fall-through. In comparison, Ada provides a case statement which is safer. Firstly, a case statement in Ada must cover every possible value of the case expression, it will be checked at compile time. For example here is an example which classifies temperatures for an oven.

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safecase is

   n : integer;

begin
   put("Enter an oven temperature (F): ");
   get(n);
   case n is
      when 200 .. 299 => put_line("cool oven");
      when 300 .. 325 => put_line("slow oven");
      when 326 .. 350 => put_line("moderately slow oven");
      when 351 .. 375 => put_line("moderate oven");
      when 376 .. 400 => put_line("moderately hot oven");
      when 401 .. 450 => put_line("hot oven");
      when 451 .. 500 => put_line("very hot oven");
      when others => put_line("temperature out of bounds");
   end case;

end safecase;

If the others line were omitted, then the following compiler error would propagate, implying values 199 and below, and 501 and above are not accounted for.

safecase.adb:11:04: missing case values: -16#8000_0000# .. 199
safecase.adb:11:04: missing case values: 501 .. 16#7FFF_FFFF#

A when branch can specify a single value, or a range of values, or any combination separated by the | symbol. The optional others branch covers the values not included in earlier branches. When execution of the statements in the selected branch has been completed, control resumes after the end case statement. Unlike C++, Ada does not allow fall-through to the next branch.

The problem with statements

In C/C++ there are simple statements , and compound statements enclosed in {}. It is therefore easy to create a logic error simply by failing to enclose more than one statement in a compound. Below is an example of a piece of code in which the logic is erroneous:

for (i=0, i<50; i=i+2)
   a[i] = 0;
   a[i+1] = 1;

The code is not contained within a compound statement, so only the first statement is associated with the for loop. Ada has no concept of a simple statement, and all control structures are “blocked”. For example the code above in Ada would be:

for i in 1..50 loop
   a(i*2-1) := 0;
   a(i*2) := 1;
end loop;

The problem with function returns

C++ only provides a single type of subprogram – the function. It is possible for a function to omit the name of the return type, in which case the return type is set to int. A function that returns nothing (i.e. a procedure) has a void return type. Theoretically every function should have a return type as it should be used to indicate whether a function succeeds or fails (hence the default to int). Many programmers of course don’t bother with indicating the success of a function, and hence ignore the return value. Failure to heed a function failure can result in the failure of a safety-critical system. Consider the following C++ program:

#include <iostream>

int doubled(int x){
   return x*2;
}

int quadruple(int x){
   return doubled(doubled(x));
}

int main(){
   quadruple(5);
   return 0;
}

When this is compiled (even with warnings) there is no issue, even though the function call to quadruple() is treated like like a statement, with the return sent off to the nether world. It is then easy to have such failings happen in a large program, and hard to trace a source of error. Ada return values can be used for the same purposes as C/C++ return values, but they cannot be ignored. Here is the same program coded in Ada.

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safefunc is
   n : Integer;

   function quadruple (x: Integer) return Integer is
      function double (x: Integer) return Integer is
      begin
         return x * 2;
      end double;
      result : Integer;
   begin
      result := double(double(x));
      return result;
   end Quadruple;

begin
   quadruple(5);
end safefunc;

Compiling this will lead to an error on line 18, the source of the call to quadruple():

safefunc.adb:18:04: cannot use call to function "quadruple" as a statement
safefunc.adb:18:04: return value of a function call cannot be ignored

This has to be fixed by adding a return value, of the form:

n := quadruple(5);

Ada was designed to support safety in modern safety critical systems. The examples outlined offer an insight into how Ada is better than C++ for such systems. Neither C nor C++ were designed to support the needs of real-time safety-critical systems.

spqr

C++ versus Ada for safety critical software (i)

By: spqr
1 April 2021 at 17:33

Safety critical software is incredibly important for systems designed for applications such as aerospace, rail transportation, power stations and the like. This the F-35 hey, built largely in C++. In some cases these systems have been built in languages such as C and C++, largely because they have a very inexpensive labour force. However the use of these languages, as opposed to Ada have significant hidden costs and incur safety risks due in part to the unweildy and awkward nature of the languages. This post compares some of the features of C++ and Ada.

The problem with arrays

There are a number of issues with arrays in C++:

  • Every array index must begin at zero. Due to this array indexing is always off-by-one from the element number of the array resulting in a tendency to index past the end of the array.
  • C++ does not do bounds checking automatically, instead relying on it to be manually coded.
  • The use of 0-based arrays means that 2D arrays are indexed using a single index which is derived using a formula. This maps very closely to how the array is stored in memory, but does not support the idea of matrix in an unambiguous manner.
  • Arrays are indexed using integers.

In comparison, Ada arrays include a declaration of both the index-type, and the element type. All operations performed on the array are subjected to bounds checking, and the array indices may be of any discrete type. Below is an example of creating an array in Ada to hold a 1000×1000 image.

procedure safearrays is

subtype imgIndex is integer range 1..1000;
subtype uint8 is integer range 0..255;
type image is array(imgIndex,imgIndex) of uint8;
image1 : image;

begin
   for x in image1'range(1) loop
      for y in image1'range(2) loop
         image1(x,y) := 0;
      end loop;
   end loop;

end safearrays;

Subtypes can be used to constrain the valid range on index and array values. In this case the subtype uint8 is created to hold unsigned integers with values 0..255, and the subtype imgIndex is used to hold the index range for the image. The type image is then created using these types. If any attempt is made to access an element outside the array image, the following error occurs (where X is the line number):

raised CONSTRAINT_ERROR : safearrays.adb:X index check failed

The Ada syntax has a number of benefits. Firstly Ada arrays are first class types, not structures created as an afterthought using pointers. No pointers are involved, to at least they are handled internally. Ada also implicitly checks array bounds, meaning unlike C/C++ there will never be an issue with accessing a piece of memory not associated with an array. The Ada Range attribute means that bounds do no have to be hard coded, which makes it easier to maintain a piece of code.

The problem with strings

Let’s face it strings in C/C++ are generally horrible. Nobody should have to deal with a string terminator, the null character, ‘\0’. This means strings always have to be one element larger than the characters being stored. This means if a string is created, the null character must be added, otherwise there is the risk of issues with array bounds, or garbage being extracted from elements with no value. C/C++ provide a whole string library to manipulate strings, for example strcpy() to copy a string. Like numeric arrays, no bounds checking is performed on strings.

There are of course inherent safety concerns with C++ strings. The first is that they do not automatically maintain information about the length of the information contained in a string, and need the strlen() function to do that, assuming the string is null terminated. The second is that they do not prevent information being stored beyond the physical end of the string. A missing null character might mean the standard string functions process beyond the bounds of the string. C++ does provide a String class. Below is an example which creates a string, str, of length 10 (although in reality it should only hold 9 characters). Values are then assigned to all 10 elements of str, and 20 elements of str are printed out, as well as the length of the string.

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

int main(){
   char str[10];
   for (int i=0; i<10; i=i+1)
      str[i] = 65+i;
   for (int i=0; i<20; i=i+1)
      cout << str[i];
   cout << strlen(str) << endl;
   return 0;
}

The output is as expected from C++ – the first 10 uppercase characters followed by a bunch of garbage, and a length of 11.

    ABCDEFGHIJ
    
    `�11

Ada provides a predefined array fixed type, String. This can either be specified using bounds (line 1), or an unconstrained array (line 2), where the bounds are deduced from the initialization expression.

1   quoteyoda : String(1..30) := "Do or do not, there is no try!";
2   quoteyoda : constant String := "Do or do not, there is no try!";

Ada provides three forms of strings: the type String is a fixed length string, the type Bounded_String provides a fixed buffer than contains string data up to the length of the buffer, and Unbounded_String which uses a dynamically allocated buffer to contain data without having to define the length. There is no need for special functions to copy from one String to another, or one Bounded_String to another, however functions are provided to convert between types, for example a String to an Unbounded_String. The main benefit of strings in Ada is that memory outside the buffer associated with the string cannot be corrupted. Here is a simple example in Ada which tries to access the 11th element of the array str.

with Ada.Text_IO; use Ada.Text_IO;

procedure safestr is
str : String(1..10);
begin
   str := "ABCDEFGHIJ";
   put(str);
   put(str(11));
end safestr;

It is not possible. Compiling it produces the following messages:

safestr.adb:8:12: warning: value not in range of subtype of "Standard.Integer" defined at line 4
safestr.adb:8:12: warning: "Constraint_Error" will be raised at run time

And executing the code causes “raised CONSTRAINT_ERROR : safestr.adb:8 range check failed“. So Ada catches the error, and C++ using the standard array of characters fails to.

spqr

Programming made easy – nested loops

By: spqr
1 May 2020 at 13:21

We’re not quite done with loops yet. There are still a few things to learn, although some can be left for later. There are certain situations where it is appropriate to use a loop inside another loop. Such a loop is called a nested loop. A nested loop is executed, essentially, from the inside out. Each loop is like a layer and has its own loop control, its own condition and its own loop body. Below is a schematic of three nested loops.

A loop inside a loop, inside a loop.

There are many different kinds of nested loops, and they are largely dependent on the underlying algorithm. Below are some examples of the types of nested loops that are possible.

Various examples of nested loops

We will consider how a nested loop can be used to create a times table. It basically works by iterating the inner loop 12 times for every iteration of the outer loop. So, 12 × 12 = 144 iterations. The outer loop is highlighted in blue, and the inner loop in red. Here is a sample output:

 1   2   3   4   5   6   7   8   9  10  11  12
 2   4   6   8  10  12  14  16  18  20  22  24
 3   6   9  12  15  18  21  24  27  30  33  36
 4   8  12  16  20  24  28  32  36  40  44  48
 5  10  15  20  25  30  35  40  45  50  55  60
 6  12  18  24  30  36  42  48  54  60  66  72
 7  14  21  28  35  42  49  56  63  70  77  84
 8  16  24  32  40  48  56  64  72  80  88  96
 9  18  27  36  45  54  63  72  81  90  99 108
10  20  30  40  50  60  70  80  90 100 110 120
11  22  33  44  55  66  77  88  99 110 121 132
12  24  36  48  60  72  84  96 108 120 132 144

Ada

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure nested is 
begin
   for x in 1..12 loop
      for y in 1..12 loop
         put(x*y, width=>4);
      end loop;
      new_line;
   end loop;
end nested;

Fortran

program nested
   integer :: x, y
   do x = 1, 12
      do y = 1, 12
         write(*,10,advance='no') xy
         10 format(I4)
      end do
      write(*,*)
   end do
end program nested

C

#include <stdio.h>
int main(void){
   int x, y;
   for (x=1; x<=12; x=x+1)
   {
      for (y=1; y<=12; y=y+1)
         printf("%4d", x*y);
      printf("\n");
   }
   return 0;
}

Julia

for x = 1:12
   for y = 1:12
      @printf("%4d", x*y)
   end
   println()
end
println(h)

spqr

Programming made easy – loops (iii)

By: spqr
27 April 2020 at 13:44

There are of course other loops. These loops are normally used when the number of times a loop iterates isn’t known. These type of loops generally use some form of condition to control the loop. The most prominent type of loop is the while loop. A while loop checks a condition, and if true, processes the code in the loop body, otherwise it exists the loop. Unlike the for loop, which controls the values of the loop index in terms of updating its value, in the loops like while, this process must be added, i.e. the value of i is given the value 1, before the loop is activated, and its value is incremented within the loop body.

We will consider some of these loops using the same Harmonic series, this time in increasing order. The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   i = 1
   do while (i <= n)
      h = h + 1.0/i
      i = i + 1
   end do
   write(*,*) h
end program harmonic

Here the index variable, i, must be initiated to the value 1 before the loop begins, and incremented within the loop. The condition states that the loop continues while the value of i is less than or equal to n, i.e. (i <= n) . Fortran also provides a more generic do/end do loop.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   i = 1;
   while (i <= n){
      h = h + 1.0/i;
      i = i + 1;
   }
   printf("%lf\n", h);
   return 0;
}

In the C version, the structure is almost the same, except that the do while of Fortran has been replace with a simple while. With the addition of the statement to increment the value of i, the loop “contents” are now enclosed within { and }. C also has another loop, the do/while() loop, that performs the loop once, then evaluates the condition at the end.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n, i : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   i := 1;
   while i <= n loop
      h := h + 1.0/float(i);
      i := i + 1;
   end loop;
   put(h);
end harmonic;

In Ada, the while loop looks similar to that of the other to Fortran and C, it is just subtleties in syntax that set them apart. Here there are no parenthesis around the condition. Also note that in the for loop, the index variable, i, did not have to be declared, but here it does. Ada also provides a generic loop/end loop loop.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
i = 1
while i <= n
   h = h + 1.0/i
   i = i + 1
end
println(h)

Same deal here. Similar to Ada, except the loop keyword after the condition isn’t needed. There are only two loops in Julia.

As you can see, over four languages, the only thing that fundamentally changes is the basic syntax of the loop.

spqr

Programming made easy – loops (ii)

By: spqr
24 April 2020 at 14:39

With the basics of for loops, let’s look at an example of an actual program which calculates the Harmonic series, which is an infinite series of the form:

h(n) = 1 + 1/2 + 1/3 + … + 1/n

The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.  For interest sake, the loops are presented in reverse, i.e. 1/n + 1/(n-1) + … + 1, as it illustrates clearly how each language deals with the simple issue of a decreasing index. In each case the starting value of the index variable i is n, and the ending value is 1. Here it is the algorithm for the Harmonic series depicted visually, clearly showing the role of the loop:

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   do i = n,1,-1
      h = h + 1.0/i
   end do
   write(*,*) h
end program harmonic

Here the third item (the modifier) in the Fortran loop denotes the type of change to the loop index, in this case, a decrease by 1. In a  normal loop incrementing by 1, the modifier can be omitted.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   for (i=n; i>=1; i=i-1)
      h = h + 1.0/i;
   printf("%lf\n", h);
   return 0;
}

In the C version, the loop index is decremented using the statement i=i–1. For more than one statement after the for loop definition, the statements would have to be encapsulated in { and }.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   for i in reverse 1..n loop
      h := h + 1.0/float(i);
   end loop;
   put(h);
end harmonic;

In Ada, the keyword reverse is used to specify a loop will be decreasing in value.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
for i = n:-1:1
   h = h + 1.0/i
end
println(h)

Here the index modifier is placed in the centre, n:-1:1, implying i has the values n to 1, modified by -1 each loop iteration. In a  normal loop incrementing by 1, the modifier can be omitted.

spqr

Programming made easy – loops (i)

By: spqr
23 April 2020 at 19:53

Humans are, by nature, repetitive beings. Watch the Star Trek: TNG episode “Cause and Effect”, and you will quickly understand what a loop is as the Enterprise is caught in a causality time loop. There are also times when programs must do a task more than once. If the repetition is limited to some countable number of recurrences with a foreseeable end then it is called a loop. And if, at least in the perception of the individual who is experiencing a certain situation, there is no end in sight then one is talking about endless or infinite loops. All of the above can be desirable or not. Here is a visual illustration:

The code in the program moves linearly from statement A, until it encounters a loop. At the start of the loop, the loop control tests some condition. If the condition is true, then the loop activates the piece of code (made up of one or more statements) in what is called the loop body. When that code is done the loop returns to the loop control, and the process starts all over. If the condition in the loop control becomes false, nothing happens, and the program ends the loop, and continues on to statement B. There is also a possibility that the first time into the loop, the condition becomes false, and in that case the loop is bypassed.

Loops are structures that allow for repetition – repeating an activity a number of times. There are different kinds of repetition:

  1. Loops where the number of repetitions is known before the loop activates.
  2. Loops where the number of repetitions isn’t known, and is controlled by some condition.
  3. Loops that are infinite, i.e. could continue to infinity and beyond.

The best example of the first case encountered in most languages is the for loop. Here we use a variable called a loop index to control the loop. Each time a loop loops it is called an iteration. So if the loop index is x, and it repeats 100 times, then x will begin with a value of 1, and increment by 1 each iteration, until x reaches 100. In Julia this is easily achieved in the following manner (in this case the loop simply prints out each value of x):

for x = 1:100
   println(x)
end

In Ada it takes a similar format (the printing part is a little different, but just ignore that bit):

for x in 1..100 loop
    put(x); 
    new_line;
end loop;

Ada uses the keyword loop to signify it as a loop. Fortran also uses a similar construct, but has never used the for loop, opting instead for the keyword do. Same concept, different name.

do x = 1, 100
   write(*,*) x
end do

C also uses a for loop, although it can be somewhat more convoluted:

for (x=1; x<=100; x=x+1)
   printf("%d\n",x);

All have their varied syntax, but achieve the same goal – a known number of iterations.

spqr

Coding Ada: Bitwise operators

By: spqr
7 April 2020 at 02:14

In Ada, you can find similar bitwise operators to other languages: and, or, xor, and operators that shift left and right. The trick is that they use modular types. These types are unsigned and have “wrap-around” semantics. Consider the following declaration:

subtype byte is unsigned_8;
package byteIO is new ada.text_io.modular_io(byte);
x, y, z: byte;

This defines an integer type whose values range from 0 to 255 (on most processors), i.e. the integers that will fit in a byte. Since it’s “modular”, it means that arithmetic operations on the type wrap around (i.e. if you add 130 + 130, you will get 4. With this declaration, you can create binary numbers:

x := 2#00011110#;
y := 2#11110100#;
z := 2#11110000#;

Here x=30, y=244, and z=240. So we can then use bitwise operators (found in the Ada package Interfaces) to swap the numbers:

x := x xor y;
y := y xor x;
x := x xor y;

The numbers can be printed out with this code:

put_line(unsigned_8'image(x));
put_line(unsigned_8'image(y));

You can also use put from the sub-package byteIO:

byteIO.put(item => z, base => 2);

If we want to convert a number input from integer to type byte, we can do so in the following manner:

w: integer;
get(w);
z := byte'val(integer'pos(w));
byteIO.put(item => z, base => 2);

The byte’val returns the base type of byte. For example, if the value input by the user is 17 (w), then the value assigned to z will be 10001. What about shift operators? There is shift_left() and shift_right(). Here’s an example:

zs: byte; 
zs := shift_left(z,1);
byteIO.put(item => zs, base => 2);

If the user inputs 12, then the value of zs will be 24, as it has been shifted left 1 bit.

spqr

❌
❌