10
W.S.R. Comput. Maths Math. Phys. Vol. 18, pp. 192-201 @ Per8amon Press Ltd. 1979.Printedin Great Britain. 0041-5553/78/0601-00192/07.50/O A NEW LANGUAGE FOR STRUCTURED PROGRAMMING* I. GVORETSKII Bratislava, Czechoslovakia (Received 6 Januury 1976; revised 4 Jury 1977) THE IDEAS of structured programming for a simple and immediate check of the partial correctness of programs during their debugging are explained. A language (called STOL) is presented for the construction of structured programs “in pure form”, making it possible to check them by a given set of operators. 1. Introduction The theory and validation of the proposed approach to structured programming are described in section 2. The idea of this approach is based on the principle of successive precisionings of the blocks of the original program. Each block is “opened up” until there remain in it only assignment operators and simple conditions. Simultaneously a collection of checking operators is constructed, constituting an independent part of the program. The semantics of the program and the principles of the proof of its correctness are described by means of these operators. In section 3 the syntactical definition of the STOL language is given. The syntax of this language differs from known languages precisely because it is oriented to the course of thought and ideas of the programmer at the time of construction of the program, the notation in this language being in the form of a documentation of the program. The checking of such a program is based on two assumptions: (1) the program transforms the input data, corresponding to some input predicate, into output data, corresponding to an output predicate; the programmer must decide the action of his program before beginning programming; (2) the final program will be correct (that is, it will correspond to the assumed transformation of the input data into output data) only when each step of the precisioning process does not change this transformation. It is assumed that the programmer creates the necessary quantity of program documentation during the programming. This documentation is directly expressed in two ways: by invariants of cycles and by operators of inference, which helps considerably to increase the volume of program checked and to simplify the verification. The latter is mainly based on a collection of operators specified by the programmer. The verification operators in turn are organized structurally. Therefore the relation of the input data to the output data in STOL language can be precisioned simultaneously with the construction of the program. In section 4 an example of such a program is given, which was checked manually (according to the collection of operators in it). At the present time work is proceeding to realize this system and to apply it to the construction of programs. *Zh. vj%hisl. Mat. mat. Fiz., 18,3,718-727,1978. 192

A new language for structured programming

Embed Size (px)

Citation preview

Page 1: A new language for structured programming

W.S.R. Comput. Maths Math. Phys. Vol. 18, pp. 192-201 @ Per8amon Press Ltd. 1979. Printed in Great Britain.

0041-5553/78/0601-00192/07.50/O

A NEW LANGUAGE FOR STRUCTURED PROGRAMMING*

I. GVORETSKII

Bratislava, Czechoslovakia

(Received 6 Januury 1976; revised 4 Jury 1977)

THE IDEAS of structured programming for a simple and immediate check of the partial

correctness of programs during their debugging are explained. A language (called STOL) is presented

for the construction of structured programs “in pure form”, making it possible to check them by a given set of operators.

1. Introduction

The theory and validation of the proposed approach to structured programming are described in section 2. The idea of this approach is based on the principle of successive precisionings of the blocks of the original program. Each block is “opened up” until there remain in it only assignment operators and simple conditions. Simultaneously a collection of checking operators is constructed, constituting an independent part of the program. The semantics of the program and the principles of the proof of its correctness are described by means of these operators.

In section 3 the syntactical definition of the STOL language is given. The syntax of this language differs from known languages precisely because it is oriented to the course of thought and ideas of the programmer at the time of construction of the program, the notation in this language being in the form of a documentation of the program. The checking of such a program is based on two assumptions:

(1) the program transforms the input data, corresponding to some input predicate, into output data, corresponding to an output predicate; the programmer must decide the action of his program before beginning programming;

(2) the final program will be correct (that is, it will correspond to the assumed transformation

of the input data into output data) only when each step of the precisioning process does not change this transformation.

It is assumed that the programmer creates the necessary quantity of program documentation during the programming. This documentation is directly expressed in two ways: by invariants of cycles and by operators of inference, which helps considerably to increase the volume of program checked and to simplify the verification. The latter is mainly based on a collection of operators specified by the programmer. The verification operators in turn are organized structurally. Therefore the relation of the input data to the output data in STOL language can be precisioned simultaneously with the construction of the program.

In section 4 an example of such a program is given, which was checked manually (according to the collection of operators in it). At the present time work is proceeding to realize this system and to apply it to the construction of programs.

*Zh. vj%hisl. Mat. mat. Fiz., 18,3,718-727,1978.

192

Page 2: A new language for structured programming

A new language for structured programming 193

2. Theory

1. Smcntred programming

Structured programming is the theoretical basis of programming for computers. The method

proceeds from the principle of the successive development of the original program up to a level at which the program contains only assigmnent operators and simple conditions, The original program

consists of a unique operator, which (informally) reflects the desired action of the program. The method of successive precisioning consists of the successive replacement of such informal operators

by operators, continually more and more close to the operators of the object language. Here each informal operator may be replaced by an operator of the object language, or by another informal operator, or by a structure of them:

{a) a sequence of operators

a,; . . . ; a,,

(b) a conditional operator

(c) a cycle

if pi then a, else a2

while pz do US

where pl, p2 are conditions, and al, . . . , ak are operators. Each of the operators and conditions (as yet not elements of the object language) must be precisioned in the way mentioned in proportion to the programing of the problem. The process is continued until all the informaf operators are precisioned up to the level of the object language [ 1 ] .

O~~nating from the form of the tr~sition from general concepts to the details, this method is called “programming from the top downwards”. It has been proved (see, for example, [2]), that any program written as a block-diagram, can also be written as a block-diagram consisting of the

three structures mentioned above alone.

But there are also some difficulties connected with the organization of the process of programming. Let us consider two of them.

Firstly, the method described permits us to execute the decomposition of functions (operators), but not of conditions. At the same time, in practice, conditions often occur of the form “if k is a prime number”, “ if the polynomial 4x) has only integral roots” etc., which it is impossible to regard as elementary. In the STOL language the decomposition of conditions is accomplished by means of logical functions without side effects. In turn each of the functions must be a structured program.

Secondly, there is not yet a language in which a program could be written as a sequence of precisionings. Usually the sequence of precisionings obtained is synthesized (from top to bottom), eliminating the informal operators, As a result a program is obtained as a single block. In essence there is nothing to prevent the program being represented directly as a sequence of precisionings, if the appropriate mathematical machinery exists. Conversely, at least four important reasons require precisely such a solution:

Page 3: A new language for structured programming

194 I. Gvorehkii

(1) the synthesis of a program is a mechanical task; it is obvious that a computer will perform it more quickly and without errors;

(2) the tree formed by precisionings, and the tree of syntactical analysis of the object program are identical in general features; it is possible not to eliminate this important information in synthesis, in order to reconstruct it again when translating;

(3) a program in the form of a sequence of precision&s can easily be modified by variation of the precisioning of an operator or condition.

(4) a program in the form of a sequence of precisionings is a sequence of replacements of informal operators by blocks, hence the advantages of languages with block structure (ALGOL, PASCAL) and the fle~b~ity of languages with sequential instructions (FORTRAN, COBOL) are preserved.

For these reasons a program in STOL language is written as a sequence of precisionings of the form

X=#.

where x is an informal operator and y is its precisioning. To simplify the translation all informal operators are provided with the prefm $ . In the precisioningy there may occur not only the

structures mentioned, but also sequences of them. Experience has shown that it is more convenient for the programmer if he can record all the actions proceeding at one level, without introducing

redun~t informal operators.

As an example we present a simple program in STOL for finding the greatest of N elements

of the array A:

program Q ~I~~G~ (con& N: integer;

var G, i: integer;

A: array [I..N] of real)

$FINDGR=i:=2; G:=A[I];

while i<N do $ TRYN~XT.

.$ TRYNEXT=if A [ i] >G then G:=A [i] else null;

i:=i+l.

In the program there are two informal operators. The operator ~~~~G~ describes

the whole program, the operator $ TRYNEXT describes the body of the cycle in which it is checked whether the next element of the array is greater than the existing maximum. The value of the greatest element of the array must be found in the variable G. Although it is decided by the syntax to introduce a iarge number of informal operators, they are introduced only in places where they represent blocks. As a rule the decrease in the number of informal operators increases the clarity of the program.

2. Correctness of the programs

On considering the given program the natural question arises: does it do what the author expected that is, at the end of the program will the value of the variable G be identical with the greatest element of the array A. It is easy for a programmer of average qualifications to answer

Page 4: A new language for structured programming

A new language for rmrcrured ptvgmmming 195

this question for such a simple program. On the other hand, can this be answered by giving a formal

proof of the correctness of the program or by indicating an error in it.

The STOL language permits us to check formally the correctness of a program while debugging it. At the present time there already exist several systems for formally checking the correctness of programs (see, for example, [3]). For the most part they are based on Hoare’s axiomatic system [4] and other authors (see [5-71). In order to describe the working of our system we must first introduce some definitions.

We will call the program S partly correct (below, only correct) relative to the input predicate P and the output predicate Q, if from the fact that the program begins to act with data satisfying condition P, and stops, it follows that the result will satisfy the condition Q. We will denote the correctness of the program S relative to P and Q by

VER(P, & Q)

The verification rules introduced in [4] were reprocessed into the recursive operators 133, which were used in the program in the language IIJI3HEP - BXM-6 (see [S]) in the

following form.

Rule 1. The empty operator:

(a) VER (P, A; null, Q) -VER (P, A, Q)

(b) VER (P, null, Q) =PROVE (PIQ)

where PROVE is the program which will be described below.

Rule 2. The assignment operator:

VER(P, A; x:=e, Q) -=VER(P, A, SUBST(Q, x, e))

where SUBST is the substitution of the expression e in place of the variable x in the condition Q. If x is an element of an array, say B [i] , then instead of all the entries of B in Q we substitute the triad <B, i, e>.

Here (B, t, e) means that to the i-th element of the array B there is assigned the value of the expression e. The sequence of assignment operators

BItI : =c; B[j] : -f

will be interpreted by the system as the substitution of ((B, t, G), f, p in place of all the entries of B.

Rule 3. The conditional operator:

VER(P, A; if p then Bi else Br, Q)-VER(P, A; IF(p); Bl, Q)A VER (P, A; ZW’p) ; Bz, Q)

The function IF(p) is used to remember the condition p.

After contracting the operator B1 by transforming it by rules l-5 we use the rule

VER(P, A; ZF(p), Q) --vERV’, A, p=Q)

Page 5: A new language for structured programming

196 Z. Cvoretskii

which also applies to IF (1~).

Rule 4. The cycle:

lrER(P,A; whilep do inv: ZNV inI?, Q)=VEZ?(P, A,ZNV)r\VER(ZNV A\p, B, ZNV) A L?D? (ZNVA’p, null, Q)

The predicate ZNV is called the invariant of the cycle, and it is introduced by the programmer

in the entry

while p do inv: ZNV in B

Rule 5. Precisioning:

VER(P, A; $B, Q)A$B=C=VER(P, A; C, Q)

After supplementing the preceding program by input and output predicates and an invariant of the cycle we obtain the program in the following form:

program $ FZNDGR (const N: integer; var G, i: integer; A: array11 . . N]of real)

pre N>l; post GREATEST (A, N, G) ; $FZNDGR==i:=2; G:=A[l];

while i<N do inv: GREATEST(A, i-l, G) Ai-l<N in $ TRYNEXT.

$TRYNEXT==if A[i]>Gthen G:=A[i] else null; i:=i+.Z.

The checking of the program is initiated by the instruction

VER (N>l, $ FZNDGR, GREATEST (A, N, G) ) .

The entry GREATEST (A, i, G) is a contraction of the entry

(Vi, l<jBi:A[j]<G)A(ab, l<Ki:A[b]=G).

The possibility of such a contraction of predicates permits a structural approach to the formulation of the input and output predicates, the invariants of cycles and the inferential operators. It is undoubtedly difficult to expect of the programmer that he will know absolutely exactly the predicate expressing the action of a program while it has not yet been composed. With the precisioning of the action of the program it is also possible to precision successively the predicates themselves. One such possibility will be demonstrated by the definition of the inferential operator which permits us to deduce facts connected with the predicate GREATEST.

Inferential operators are necessary to explain the meaning of predicates and determine which transformation of these predicates we need. For example, the predicate GREATEST is mainly used to prove the correctness of a cycle in precisioning the informal operator $ FZNDGR. Due to the fact that the checking of the correctness of a cycle proceeds inductively, it is necessary to consider two fundamental cases: the case of the least number and the inductive step.

In the case of the least number (that is, the number 1) it is obvious that the greatest element of a one-element array, is the first element, that is, we have

GREATEST(A, I, A[l]).

Page 6: A new language for structured programming

A new language for structured programming 197

The inductive step consists of two alternatives: either the i-th element is greater than the

existing maximum and becomes the greatest, that is,

3X: GREATEST (A, i-l, X) l”\A [i] >X/jGREATEST (A, i, A[ il)

or it is not greater than the existing maximum and

3X: GREATEST (A, i-l, X) /jA[ i] 6XAGREATEST (A, i, X).

In the set of inferential operators these facts are written as a set of alternatives by means of which

it is possible to prove the supposition (hypothesis), at the beginning of the operator:

goal GREATEST (B, j, M) alt j=l/jM=B [I];

GREATEST (B, j-l, M) /jB [ j]<M;

exist(X) : GREATEST (B, j-.2, X) I\B[j]>X/jw=B[j]

tla

At this point of the program the hypothesis is real if we have succeeded in proving one of the alternatives.

The set of inferential operators is added at the end of the program. In our case it will consist of only the one operator mentioned.

3. Verification

The use of the operator VER gives the so-called conditions of checking which the operator PROVE must verify by rule 1, b). In connection with the fact that the checking system will be realized in the language IIJIBHEP - 1;3CM-6, the checking is based on the deduction theorem:

if I’ is a set of formulas, A and B are formulas and I?, AtB, then IT-A=>B.

Therefore, the inference Pi2 (Pz=) (PJ= . . . =I (P,,IQ) . . . ) ) is replaced by Pi, . . . , PJ-Q

using the rule

PROVE(R):=if R=PzQ then (ASSERT(P); PROVE(Q)) elseACHIEVE

Here ihe procedure ASSERT writes into the associative memory the condition P (as real), and

the procedure ACHIEVE seeks the result of the predicate R by the given rules. In accordance with

the given operator KER we obtain four checking conditions which must be proved by the procedure PROVE.

Condition 1. N>II(GREATEST(A, I, A[l])A_Z<N)

Condition 2. (GREATEST (A, i-l, G) Ai-l<N/\i<N) 3 (A [ i]$G= (GREATEST (A, i, G) /ji<N) ) )

Condition 3. (GREATEST (A, i-l, G) Ai-l<N/ji<N) 1 (A[ i] >Gx

(GREATEST(A, i, A[i])/ji<N))

Condition 4. (GREATEST (A, i-l, G) /ji-l<N/ji>N) 1GREATEST(A, N, G)

Page 7: A new language for structured programming

198 I. Gvoretskii

Besides rules permitting inference about information specified by the programmer, the procedure ACHIER,!? also contains a collection of constant rule permitting inference by

means of manipulation with equations and inequalities. Because of this it is possible to prove, for example, condition 4, the result of which is not based on rules given by the programmer.

3. Syntax of the language

The title of the STOL language is formed from the first letters of the English words “structure-oriented Language”. In the syntactical description of the language the starting point was

the description of the language PASCAL [9, lo]. In all suitable cases identical syntax is used.

In accordance with the Backus normal form the elements of the syntax are words enclosed in angular brackets < and >. At the same time these words denote the sense in which the element of the syntax is used. The possible repetition of an element is indicated by an asterisk (0 and more repetitions) or by the symbol + (1 and more repetitions). A sequence of elements of the syntax which it is necessary to repeat is enclosed in curly brackets { H }. The alphabet used is part of the alphabet of the PASCAL language with the addition of the symbols

-, null, program, pre, post, inv, goal, alt, tla, exist.

If some concepts are undefined their meaning is the same as in [9].

1. The program

A program in our sense consists of two parts: a sequence of precisioning operators (provided

with input and output predicates and invariants of cycles) and a sequence of inferential operators:

( program > :: = ( first part > ( second part )

2. First part of the program

The first part is the core of the entire program. If it is translated into a program in the object

language of the computer, it must calculate the function defined by the input and output predicates

given in the contents of this part:

( first part ) ::= ( contents > ( list of precisionings ) ( list of functions )

( contents > :: = program ( name of precisioning ) (( block of description of constants and variables 1) pre ( input predicate ) ; post ( output predicate ) ;

( list of precisionings ) :: = ( precisioning operator > + ( list of functions ) :: = ( description of function > *

Names of precisionings and precisionings

The name of a precisioning is always part of a program - a block. It is used in order to avoid describing a block at the given instant and to preserve the possibility of describing it later, using for that the precisioning operator. For the precisioning of a syntactical analysis all the names of precisionings begin with the symbol $ :

Page 8: A new language for structured programming

A new language for structured programming 199

( name of precisioning ) :: = $ ( identifier >

Each name of a precisioning must occur only once on the left side of the precisioning operator:

( precisioning operator ) ::= ( name of precisioning > Z ( precisioning ). ( precisioning ) ::= ( operator > (; ( operator )} *

Operators

The language uses types of operators which do not permit the structural formula of a precisioning to pass over to a level below the level of the precisioning operator. The only exception is the case where only one assignment occurs at the lower level. This preserves the sense of the concept of precisioning as the subdivision of the given problem into subproblems. Subproblems consisting of more than one assignment are expressed only by the names of precisionings, which permits the programmer not to be distracted by details (they will be precisioned at succeeding stages of programming):

( operator > :: = ( simple operator 1 I ( operator if > ( operator while >

( operator if) :: = if ( condition > then ( simple operator 1 else ( simple operator ) ( operator while > :: = while ( condition ) do ( invariant > in ( simple operator )

( simple operator ) :: = ( operator of assignment > I null I ( name of precisioning ) (invariant > ::. = inv: ( predicate )

( condition ) this is either a Boolean expression, or the result of a Boolean function. The fundamental difference between the concepts ( condition > and ( predicate ) is that in ( condition 1 they are used as the effects of functions of a Boolean function, defined as function, and in ( predicate > as functions

defined as goal.

Boolean functions

All the functions defined in the part ( list of functions >, are Boolean functions used in conditions. The description of function in turn is in the form of a program, that in it there must occur at least one assignment of the value of a Boolean expression to the identifier of a function. The value is assigned to the latter in the course of calculation, and will be the value of the condition. We add that in our system recursion in the definition of a function is not assumed:

( description of function ) :: = ( contents of function > ( list of precisionings ) ( list of functions >

The contents of a function is of exactly the same form as the contents of a program, except for the word program, which is replaced by function.

3. Second part of program

In the first part of the program mainly its calculation occurs, and in the second part its verification. The second part consists of a collection of checking operators, which make it possible to verify the correctness of the program written in the first part. Every operator has one or more alternatives, by which it can be proved. An operator is regarded as proved if at least one of its alternatives is proved. Due to the fact that the system is realized in the language ILIKIHEP, universal quantifiers are absent from the alternatives (see, for example, section 2, subsection 2). All variables which are not connected with a quantifier of existence are treated by the system as connected by a universal quantifier:

Page 9: A new language for structured programming

200 I. Gvoretxkii

( second part > :: = ( list of checking operators ) ( list of checking operators > :: = ( checking operator > *

( checking operator ) : := ( contents of operator > ( alternatives ) ( contents of operator > :: = goal ( name of operator ) ( ( list of parameters 1) ( alternatives > ::= alt ( alternative ); ( alternative > tla ( alternative ) :: = ( predicate > I exist ( ( list of variables ) ): ( predicate >

By predicate we mean a Boolean expression containing variables and constants determined within the checking operator, and (or) the results of checking operators. Variables and constants are introduced by their indication in the list of parameters of the checking operator or existence quantifier. Since in the language IIJIBHEP comparison is made by the external form of the entry, the definition of types of variables becomes to a considerable extent meaningless and is not introduced in checking operators.

4. Example of a program

In conclusion we present an example of a program in the language, which does “bubble” sorting (leaving it to the reader himself to judge the plusses and minuses given by this approach):

program $ BUBBLE (const N: integer;

var i, j, U: integer; A : array [I . . N] of integer)

pre (1’0 1) A (AZNIT=A) ;

post ORDERED (A, 1, N) APERMUT (AINIT, A) ;

$BUBBLE=i:= 1;

while i<N do inv: ORDERED (A, 1, i) A PERMUT

(AZNZT, A) A (i<N+l) in $ORD.

$ORD=j:=i+l;

if A[j]<A[j-I] then .$BACKelsenull; i: =i+Z.

$ BACK=while(A [j] <A [j-l] A (j>l) do inv: ORDERED (A, 1, j-1)AORDERED (A, j, i)APERMUT (AINIT, A) A (l<j) A (j<i+l) in $CHANGE.

CHANGE=U:=A[j]; A[j]:=A[j-11; A[j-l]:=U; j:=j-1.

goal ORDERED (A, i, j) alt j<i;

exist(k) : ORDERED (A, i, k) AORDERED (A, k+l, j) A(A[k] <A[k+l]); exist (k, m) ; ORDERED (A, k, m) A (k<i) A (j<m) ;

exist (k, m) : ORDERED (A, k, m) A (k<i) A (j<m)

tla goal PERMUT (A, B)

alt A=B; B=EXCHANGE (A, i, j); PERMUT (B, A) ;

exist (C) : PEBMUT (A, C) APERMUT (C, B) tla

goal EXCHANGE (A, i, j) alt <(A, i, A[j]>, j, A[il>;

EXCHANGE (A, j, i) tla

nanslated by J. Berry.

Page 10: A new language for structured programming

1.

2.

3.

4.

5.

6.

1.

8.

9.

10.

Local algorithms simplifying the disjunctive normal forms of Boolean functions 201

REFERENCES

DAHL, 0. J., DIJKSTRA, E. W. and HOARE, C. A. R. Structured programming. Academic Press, London-New York, 1972.

MILLS, H. D. Mathematical foundations for Wuctured programming. IBM Federal System Division, Gaithersburg, 1973.

IGARASHI, S., LONDON, R. L. and LUCKHAM, D. C. Automatic program verification I: A logical basis and its implementation. Acta Information, 4, 145-182, 1975.

HOARE, C. A. R. An axiomatic basis for computer programming. Communs ACM, 12,10, 576-581,1969.

BAKKER, J. W. de MEERTENS, L. G. L. T. On the completeness of the inductive assertion method. Lectures in Banach Centre, Warszawa, 1974.

MILLS, H. D. The new math. of computer programming. Communs ACM, l&1,43-48, 1975.

SUZUKI, N. Automatic program verification II: Verifying programs ny algebraic and logical reduction. Stanford Artificial Intelligence Lab., December 1974.

PIL’SHCHIKOV, V. N. PLENER - BESM-6. Dissertations of candidates In phys.-math. sciences (Diss. kand. fir.-matem. n.), MGU, Moscow, 1976.

WIRTH, N. The programming language Pascal. Acta Znformatica, 1,35-63, 1971.

HOARE, C. A. R. and WIRTH, N. An axiomatic definition of the programming language Pascal. Acta Informatica, 2, 335-355, 1973.

LY.S.R. Comput. Maths Math. Phys. Vol. 18, pp. 201-201 0 Pergamon Press Ltd. 1979. Printed in Great Britain.

0041-5553/78/0601-0201$07.50/0

LOCAL ALGORITHMS SIMPLIFYING THE DISJUNCTIVE NORMAL FORMS OF BOOLEAN FUNCTIONS*

A. V. KABULOV and G. F. LOSEV

Moscow

(Received 22 February 1977)

A BRIEF exposition of the theory of local algorithms for simplifying the disjunctive normal forms

of Boolean functions is presented. A proof of the essential nature of all the conditions used in the construction of one of the algorithms is presented, and its majorantness is proved.

Among the problems of mathematical cybernetics an important place is occupied by problems

of the synthesis of control systems, and in particular, by the problem of simplifying the Boolean

functions of the algebra of logic. A function of the algebra of logic is completely defined by its disjunctive normal form (d.n.f.). The definitions of the fundamental concepts associated with the

theory of Boolean functions (the elementary conjunction, complete, dead-end, minimal and shortest d.n.f. etc.), are regarded as already known (they can be looked up, for example, in [l]).

The simplification of a Boolean function consists of constructing from a given d.n.f. of a Boolean function of n variables fIxI, . . . , x,) its minimal (or shortest) d.n.f. A considerable number of algorithms deciding this problem exist. All these algorithms can be subdivided into three basic groups: methods based on the enumeration of variants, local methods and approximate methods. Enumerative methods cannot be used to minimize many functions. To increase the efficiency of such methods local algorithms for selecting all the elementary conjunctions, not occurring in any dead-end d.n.f. of the function considered, are applied to the contracted d.n.f. of the function

*Zh. vj&X Mat. mat. Fiz., 18,3, 728-734,1918.