Skip to content

Alk Language ‐ Reference Manual

Dorel Lucanu edited this page Oct 5, 2023 · 1 revision

1 Introduction

1.1 Language motivation

Alk is a programming language developed by the Alk Language Community, mainly formed by developers and professors of Faculty of Computer Science, University "Alexandru Ioan Cuza" University, Iasi. It firstly came out as an educational tool in 2016, meant to be used by the freshmen in order to develop and run specific algorithms.
Its main purpose is to deliver a system easy to use and platform-independent, as its main user target is the students. Due to its educational reasons, Alk should display as many theoretical principles as possible in a light-weighted environment: no heavy dependencies, allow a flexible syntax and intuitive semantics.

1.2 Configuration principle

A configuration refers to a set of mappings between ids and values. Semantically, this represents an environment state at a given moment. Its main usage is related to Alk's input configuration and eventually the final metadata. Note than a configuration can have more than one such mapping. In case multiple mappings are defined, they should be space separated. It is recommended to keep these cells on separate lines where possible to increase visibility.

Syntax
var-name |-> value
var-name1 |-> value1 var-name2 |-> value2

Var-name:
This refers to the variable name to which the given value will be assigned.
Value:
The value can be represented in any way. When an initial configuration is used, the Alk interpreter will parse the value as it does inside a normal Alk algorithm, which means that any kind of representation can be used (for example the interval or specification representations for an iterable data type value).
Alk allows the initialization of the environment through an input configuration, which can be specified by using the input option (-i). Also, if the metadata option (-m) is used, the final configuration will be printed.

Examples:

  • Configuration with only one component: a |-> 5
  • Configuration with multiple components: a |-> 5 b |-> 6.5 c |-> "abc" d |-> true e |-> [1, 2, 3] f |-> <1, 2, 3> g |-> {1, 2, 3} h |-> {x -> 1 y -> 2}
  • Configuration making use of the Alk interpreter for parsing complex representations: a |-> [2 * x | x from [1..10]]

2 Language

2.1 Keywords

Below there is a table of all the keywords which can be interpreted. Alongside, there is a short description containing links to the topics which exploit those keywords.

Keyword Description
break Part of the break statement
choose Part of the choose statement
continue Part of the continue statement
do Part of the do-while statement
else Part of the conditional statement
emptyList Used to defined empty list
emptySet Used to defined empty set
emptyStructure Used to defined empty structure
for Part of the for statement
foreach Part of the foreach statement
from Used to represent specifications
failure Part of the failure statement
if Part of the conditional statement
in Used as inclusive operator
include Part of the include directive
modifies Used to represent global references inside functions
out Used to represent output parameters
repeat Part of the repeat-until statement
return Part of the return statement
s.t. Part of the choose statement
success Part of the success statement
uniform Part of the uniform statement
uses Used to represent global references inside functions
until Part of the repeat-until statement
while Part of the while statement
xor Used as xor operator

2.2 Preprocessing

The preprocessing is a stage before the visiting one, which handles the directives. The only directive supported now is the include directive which allows the user to attach an external file into the current code-base. These directives are processed recursively, so the final generated program should have no include node. In case a cycle is detected, the Alk interpreter will report this and abort the execution.

Syntax
#include "file-path"

File-path:
The file path should point to a file which should be included in this current code. The inclusion will be done where the directive is used. In fact, this directive will be automatically replaced with the content of the file specified. The path can be absolute or relative. If the relative path is used, the origin is the file which includes it, not the initial file which is used in the -a option.
Examples:

Initial file Secondary file (side.alk) Final configuration
#include "side.alk" b = a + 2; a = 5; a |-> 5 b |-> 7
a = 9; #include "side.alk" b = a + 2; a++; a |-> 10 b |-> 12
a = 9; b = a + 2; #include "side.alk" a++; a |-> 10 b |-> 11

2.3 Expressions

2.3.1 Data type values

2.3.1a Simple data type values

2.3.1a.1 Integer

The integer data type is a numeric type which allows storing data in form of integer numbers. There is no fixed bound for the dimension of one integer, as it is backed by an unlimited data structure designed for numeric usage. The complexity of the operations is dependent upon the size of the integer, thus not all computations should be considered to be working in constant time.
To represent an integer, one should just simply write it in the decimal base, as this is the only way Alk can interpret it. For negative integers, the unary operator - should be used in the representation.

Examples:

  • Integer: 123456789
  • Integer with a large number of digits: 1234567891011121314151617181920
  • Integer with minus sign: -123
  • Zero integer: 0
2.3.1a.2 Float

The float data type is a numeric type which allows storing data in form of numbers with floating point. There is no fixed bound for the dimension of the integer part, while the fractional part is limited by a precision constant. By default, the precision is set to 10 decimals after the floating point and rounded after a HALF_EVEN strategy. However this can be changed at the command line using the precision option. The complexity of the operations is dependent upon the size of the float, thus not all computations should be considered to be working in constant time.
To represent a float, one should write it in the decimal base using a floating point which separates the integer part from the fractional part. These two parts should not be empty. The fractional part shouldn't necessarily respect the precision, it will be automatically trimmed. For negative floats, the unary operator - should be used in the representation.

Examples:

  • Float: 1234.098765
  • Float with a large number of digits and decimals: 123456789.201918171615141312111
  • Float with minus sign: -123.456
  • Float with 0 integer part: 0.123
  • Float with 0 fractional part: 123.0
  • Zero float: 0.0
2.3.1a.3 Bool

The bool data type is a logical type which allows storing data in form of logical primitives. Therefore, a bool can have one out of the two possible values: true or false. The complexity of the operations with such types should be considered constant.
To represent a bool, one should write it using the true or false keywords.

Examples:

  • True bool: true
  • False bool: false
2.3.1a.4 String

The string data type is a character type which allows storing data in form of character strings. There is no fixed bound for the length of the string and can be used as a single character representation when its size is one, or the empty string when its size is zero. The complexity of the operations is dependent upon the size of the string, thus not all computations should be considered to be working in constant time.
To represent a string, one should enclose a character sequence in double quotes. For the empty string, provide an empty sequence inside the double quotes pair.

Examples:

  • String: "abcxyz"
  • String also containing non-alphanumerical characters: "a1%x]\0 and &p./?"
  • String of size 1 (representation of a single character): "a"
  • Empty string: ""

2.3.1b Compound data type values

2.3.1b.1 Array

An array is a heterogeneous compound data type which can store multiple values in a sequential way with contiguous allocation. The array is limited in size by a MAX_SIZE constant which is initially set to one billion. This can be however altered by the size option providing another maximum. The array is dynamically allocated, so there is no need to initialize the array beforehand.
The types of values contained in one array can be both simple or compound. Also, it doesn't have a limit in the nesting depth, so one can enclose a unlimited number of values inside the array. The complexities are either linear or constant, depending on the operations.
To represent an array, one should choose out of the four possible ways to define an iterable data type. The canonical representation is the expression based one, as is the most comprehensive.

Examples:

2.3.1b.2 List

A list is a heterogeneous compound data type which can store multiple values in a sequential way with discontinuous allocation. The list is limited in size by a MAX_SIZE constant which is initially set to one billion. This can be however altered by the size option providing another maximum.
The types of values contained in one list can be both simple or compound. Also, it doesn't have a limit in the nesting depth, so one can enclose a unlimited number of values inside the list. The complexities are either linear or constant, depending on the operations.
To represent a list, one should choose out of the four possible ways to define an iterable data type. The canonical representation is the expression based one, as is the most comprehensive. In order to represent the empty list, use the emptyList keyword, or the simple open/close syntax.

Examples:

2.3.1b.3 Set

A set is a heterogeneous compound data type which can store multiple unique values in a non-sequential way with discontinuous allocation. The set is limited in size by a MAX_SIZE constant which is initially set to one billion. This can be however altered by the size option providing another maximum.
The types of values contained in one set can be both simple or compound. Also, it doesn't have a limit in the nesting depth, so one can enclose a unlimited number of values inside the set. The complexities are either linear or constant, depending on the operations.
To represent a set, one should choose out of the four possible ways to define an iterable data type. The canonical representation is the expression based one, as is the most comprehensive. In order to represent the empty set, use the emptySet keyword, or the simple open/close syntax.

Examples:

2.3.1b.4 Structure

A structure is a heterogeneous compound data type which can store multiple values based on a unique identifier. The structure is limited in size by a MAX_SIZE constant which is initially set to one billion. This can be however altered by the size option providing another maximum. The structure is dynamically allocated, so there is no need to initialize the structure beforehand.
The types of values contained in one structure can be both simple or compound. Also, it doesn't have a limit in the nesting depth, so one can enclose a unlimited number of values inside the structure.
To represent a structure, one should enclose in curly braces a sequence of space separated pair components which are of form key -> value. An empty structure can't be defined, so it should contain at least one component.

Examples:

  • Empty structure: emptyStructure
  • Empty structure with open/close syntax: {->}
  • Simple structure with two components: {x -> 2 y -> 5}
  • Complex structure: {a -> 0.5 b -> [1, 2, 3, 4, 5] c -> < "a", "b" > d -> {0.5, 0.6} e -> {x -> 2 y -> 3} x -> 1 y -> "abc" z -> true}

2.3.2 Data type operators

The majority of the operators are C-like. However there are new sets of operators meant to simplify the operations between certain data types (setwise operators, inclusive operators). In this section, each operator will be explained in terms of behavior, data types and the result of the evaluation. Below it is a table meant to illustrate the priority of operators inside an expression.
In the table, if the number in the priority column is lower, then the operator will be executed quicker. For example, the conditional operator will be evaluated at the end. The ones which share the same priority, are executed in the order they appear in the expression. In order to suppress this order of evaluation, one can use the round parenthesis (for example: (2 + 3) * 4 will result in 20).
To eliminate the confusion, in case of multiple ++ or -- signs, they are parsed from left to right, which means that in an expression like 2+++3, the evaluation will consider that there is a postfix operator and a plus sign: (2++) + 3. In case of +++2, there is a prefix operator and a unary plus sign: ++(+2).

Priority Operator set Operators
1 Unary [], .
2 Increment / Decrement ++ (post), -- (post)
3 Unary + (unary), - (unary), !
4 Increment / Decrement ++ (pre), -- (pre)
5 Arithmetic *, /, %
6 Arithmetic +, -
7 Bitwise <<, >>
8 Bitwise &
9 Bitwise |, xor
10 Setwise U, ^, \
11 Relational <, >, <=, >=
12 Relational ==, !=
13 Inclusive in
14 Logical &&
15 Logical ||
16 Conditional ?:

2.3.2.1 Unary

Some unary operators are used in order to suggest the mathematical representations of positive and negative numeric values. Other operators are used for data access (over arrays or structures). The complexity of these operations is constant. The table below explains the behavior of the unary operators when the values a and x are used. Note that in the example below we consider id as a literal identifier.

Operator Representation Description
Positive +a Keeps the sign of a
Negative -a Changes the sign of a
Not !a Changes the truth value of the bool value a
Bracket a[x] Access the xth element of a
Dot a.id Access the element stored under the id identifier inside a

Depending on the operator used and the types of the operands, there can be several return types described in the table below:

Operator Data type 1 Data type 2 Return type
Positive integer integer
Positive float float
Negative integer integer
Negative float float
Not boolean boolean
Bracket array integer greater or equal to 0 and less than the size of the array data type of the element on the xth position
Dot structure data type of the element stored at the specified identifier

Examples:

Data type Positive Negative Not Bracket Dot
Integer +(-2) is -2 -(-2) is 2
Float -(+2.2) is -2.2 +(+2.2) is 2.2
Bool !true is false
Array [1, 2, 3][2] is 3
Structure {x -> 2 y -> 3}.x is 2

2.3.2.2 Increment/Decrement

There are four types of operators to easily increment or decrement a numeric value. The complexity of these operators is constant. The table below explains the behavior of the operators when a numeric value a is used.

Operator Representation Description
Postfix increment a++ Increases the value of a by 1, but the old value is used in the current expression
Postfix decrement a-- Decreseas the value of a by 1, but the old value is used in the current expression
Prefix increment ++a Increases the value of a by 1 and the new value is used in the current expression
Prefix decrement --a Decreases the value of a by 1 and the new value is used in the current expression

There is a difference between a postfix and prefix operator:

  • The prefix operator changes the value of the operand and the expression continues to work with the updated value.
  • The postfix operator changes the value of the operand, but the expression continues to work with its old value.

Note that only numeric values are valid for this kind of operations. Also, the result always has the same type as the initial value.
Examples:

Data type Postfix increment Postfix decrement Prefix increment Prefix decrement
Integer 3++ is 3 3-- is 3 ++3 is 4 --3 is 2
Float 3.2++ is 3.2 3.2-- is 3.2 ++3.2 is 4.2 --3.2 is 2.2

2.3.2.3 Arithmetic

There are two subcategories of arithmetic operators: additive (+, -) and multiplicative (*, /, %). The complexity of those operators are theoretically constant. However, the size of the numbers can matter, so the real complexity is in fact O(N + M) for additive operators, and O(N * M) for the multiplicative operators, where N and M are the number of digits in the operands.
The table below explains the behavior of the operators when two numeric values a and b are used.

Operator Representation Description
Addition a + b Computes the sum of a and b
Subtraction a - b Computes the difference between a and b
Multiplication a * b Computes the product of a and b
Division a / b Computes the division's result of a and b
Mod operator a % b Computes the remainder left over when a is divided by b

The string data type also has an implementation for the addition operator. The complexity of such operation is O(N) + O(M), where N and M are the lengths of the string operands. The table below explains the behavior of the additive operator when two strings a and b are used.

Operator Representation Description
Concatenation a + b Computes a string representing the concatenation of a and b

The results of these first operators are numeric, but the exact resulted data type is sometimes dependent upon the operands. It means that it is relevant what data types a and b have: either integer or float. The table below illustrates of what data type the result is depending on the operator and operands. To be noted than an error is thrown when the mod operator works with a float operand.

Operator Result data type Operands data types
Concatenation string string, string
Addition / Subtraction / Multiplication integer integer, integer
Addition / Subtraction / Multiplication float float, integer / integer, float / float, float
Division float integer, integer / float, integer / integer, float / float, float
Mod operator integer integer, integer

Examples:

Data type 1 Data type 2 Addition / Concatenation Subtraction Multiplication Division Mod operator
Integer Integer 3 + 2 is 5 3 - 2 is 1 3 * 2 is 6 3 / 2 is 1 3 % 2 is 1
Integer Float 3 + 2.2 is 5.2 3 - 2.2 is 0.8 3 * 2.2 is 6.6 3 / 2.2 is 1.3636363636
Float Integer 3.2 + 2 is 5.2 3.2 - 2 is 1.2 3.2 * 2 is 6.4 3.2 / 2 is 1.6
Float Float 3.2 + 2.2 is 5.4 3.2 - 2.2 is 1.0 3.2 * 2.2 is 7.04 3.2 / 2.2 is 1.4545454545
String String "abc" + "xyz" is "abcxyz"

2.3.2.4 Bitwise

There are five types of operators which work on the bits of integer values. The complexity of these operators is theoretically constant. However, it can be severely influenced by the size of the numbers used. Therefore we can assume that the real complexity is in fact max(log(N), log(M)), where N and M are the operands. The table below explains the behavior of the operators when the integer values a and b are used.

Operator Representation Description
Bitwise and a & b Computes the result of a bitwise and operation between a and b
Bitwise or a | b Computes the result of a bitwise or operation between a and b
Bitwise xor a xor b Computes the result of an bitwise xor operation between a and b
Left shift a << b Computes the result after a bit shift of a with b bits to the left
Right shift a >> b Computes the result after a bit shift of a with b bits to the right

Note that only integer operands are valid for this kind of operations. Also, the result of these operations is always integer.
Examples:

Data type 1 Data type 2 Bitwise and Bitwise or Bitwise xor Left shift Right shift
Integer Integer 12 & 10 is 8 12 | 10 is 14 12 xor 10 is 6 12 << 10 is 12288 12 >> 2 is 3

2.3.2.5 Setwise

There are three types of operators which work exclusively on sets and represent the mathematical set operators. The complexity of these operators is linear, so we can consider the complexity class O(max(|A|, |B|)), where A and B are the set operands. The table below explains the behavior of the operators when the set values a and b are used.

Operator Representation Description
Union a U b Computes the union of the represented sets by a and b
Intersection a ^ b Computes the intersection of the represented sets by a and b
Difference a \ b Computes the difference of the represented sets by a and b

Note that the result of these operators is always a set and the operands should be evaluated to set.
Examples:

Data type 1 Data type 2 Union Intersection Difference
Set Set {1, 2, 3} U {2, 3, 4} is {1, 2, 3, 4} {1, 2, 3} ^ {2, 3, 4} is {2, 3} {1, 2, 3} \ {2, 3, 4} is {1}

2.3.2.6 Relational

There are two subcategories of relational operators: equality (==, !=) and comparing (<, <=, >=, >).
There are six types of operators which work on multiple type of values in order to evaluate relational expressions. The complexity of these operators is linear over the size of the operands. For some simple data types, the complexity is O(max(N, M)) where N and M are the number of digits in the numeric operands or the length of the string operands. In case of bool types, the complexity is linear. For compound structures, the complexity is the same, but it multiplies with the complexity of checking for the equality of each member. The table below explains the behavior of the operators when the values a and b are used.

Operator Representation Description
Equal a == b Checks if a and b have equal values
Not equal a != b Checks if a and b don't have equal values
Lower than a < b Checks if a has a value lower than b
Lower than or equal a <= b Checks if a has a value lower than b or if a and b have equal values
Greater than or equal a >= b Checks if a has a value greater than b or if a and b have equal values
Greater than a > b Checks if a has a value greater than b

These kind of operations have different meanings depending on the data types used for the operands. However, the result is always consistent in data type; these operators always deliver a bool result. The table below describes how these checks are made for each kind of data type.

Operator Result data type Operands data types Description
Equality: ==, != bool integer, integer / float, integer / integer, float / float, float Check if the represented numeric values are equal or not
Comparing: <, <=, =>, > bool integer, integer / float, integer / integer, float / float, float Check the relation between the represented numeric values
Equality: ==, != bool string, string Check if the represented character strings are the same or not
Comparing: <, <=, =>, > bool string, string Check the lexicographical relation between the represented character strings
Equality: ==, != bool bool, bool Check if both bool have the same truth value or not
Equality: ==, != bool array, array Check if both arrays have the same size. In case they do, check each pair of elements from same positions in the two arrays to detect if they are equal or not.
Equality: ==, != bool list, list Check if both lists have the same size. In case they do, check each pair of elements from same positions in the two lists to detect if they are equal or not.
Equality: ==, != bool set, set Check if both sets have the same size. In case they do, check that both coincide with their intersection or not.
Equality: ==, != bool structure, structure Check if both structures have the same set of identifiers. In case they do, check if the values associated with each identifier in the structures are equal or not.

Examples:

Data type 1 Data type 2 Equal Not equal Lower than Lower than or equal Greater than or equal Greater than
Integer Integer 1 == 5 is false 1 != 5 is true 1 < 5 is true 1 <= 5 is true 1 >= 5 is false 1 > 5 is false
Integer Float 1 == 1.0 is true 1 != 1.0 is false 1 < 1.0 is false 1 <= 1.0 is true 1 >= 1.0 is true 1 > 1.0 is false
Float Integer 1.2 == 1 is false 1.2 != 1 is true 1.2 < 1 is false 1.2 <= 1 is false 1.2 >= 1 is true 1.2 > 1 is true
Float Float 1.2 == 1.3 is false 1.2 != 1.3 is true 1.2 < 1.3 is true 1.2 <= 1.3 is true 1.2 >= 1.3 is false 1.2 > 1.3 is false
String String "abc" == "xyz" is false "abc" != "xyz" is true "abc" < "xyz" is true "abc" <= "xyz" is true "abc" >= "xyz" is false "abc" > "xyz" is false
Bool Bool true == false is false true != false is true
Array Array [1, "abc"] == [1, "abc"] is true [1, "abc"] != [1, "abc"] is false
List List <1, "abc"> == <1.1, "abc"> is false <1, "abc"> != <1.1, "abc"> is true
Set Set {1, "abc"} == {1, "abc"} is true {1, "abc"} != {1, "abc"} is false
Structure Structure {x->1.1 y->"abc"} == {x->"abc" y->1.1} is false {x->1.1 y->"abc"} != {x->"abc" y->1.1} is true

2.3.2.7 Inclusive

The inclusive operator is used in order to detect if a specific element is contained by an iterable data type. The complexity is linear as each element in the iterable structure is checked against the probe, so we can consider O(N * X), where N is the size of the structure and X is the complexity to test equality. The table below explains the behavior of the inclusive operator when the value a and iterable value b are used.

Operator Representation Description
Inclusive a in b Checks if a is contained in b or not.

Note that the inclusive operator always returns a bool value. Also, b should always evaluate to an iterable data type. In order to check the inclusion, the equal operator is used.
Examples:

Data type 1 In array In list In set
Integer 2 in [1, 2, 3] is true 4 in <1, 2, 3> is false 4 in {1, 2, 3} is false
Float 2.2 in [1, 2, 3] is false 2.0 in <1, 2, 3> is true 2.0 in {1, 2, 3} is true
Bool true in [false] is false true in < true, false > is true true in {true, false} is true
String "abc" in ["abc", "xyz"] is true "abc" in <"Abc", "xyz"> is false "abc" in {"Abc", "xyz"} is false
Array [1, 2] in [1, [1, 2.2], 3] is false [1, 2] in <1, [1, 2.0], 3> is true [1, 2] in {1, [1, 2], 3} is true
List <1, 2> in [1, <1, 2.2>, 3] is false <1, 2> in <1, <1, 2.0>, 3> is true <1, 2> in {1, <1, 2>, 3} is true
Set {1, 2} in [1, {1, 2.2}, 3] is false {1, 2} in <1, {1, 2.0}, 3> is true {1, 2} in {1, {1, 2}, 3} is true
Structure {x->2 y->3} in [1, {x->2.2 y->3}, 3] is false {x->2 y->3} in <1, {x->2.0 y->3}, 3> is true {x->2 y->3} in {1, {x->2 y->3}, 3} is true

2.3.2.8 Logical

There are three types of operators which work on the bool values in order to evaluate logical expressions. The complexity for these operands is constant. The table below explains the behavior of the operators when the bool values a and b are used.

Operator Representation Description
Logical and a && b Computes the result of a logical and operation between a and b
Logical or a || b Computes the result of a logical or operation between a and b

Note that only bool values are valid for this kind of operations. Also, the result of these operations is always bool.
Examples:

Data type 1 Data type 2 Logical and Logical or
Bool Bool true && false is false true || false is true

2.3.2.9 Conditional

The conditional operator is used as an inline conditional statement and is the only ternary operator by now. The complexity for this operator is constant. The table below explains the behavior of the operator when the bool value a and other values b and c are used.

Operator Representation Description
Conditional a ? b : c Evaluates to b only if a is true, otherwise it evaluates to _c_

Note that a should always evaluate to a bool value, while there is no restriction over the data types of b and c. The result is either b or c.
Examples:

Data type 1 Data type 2 Data type 3 Conditional
Bool Integer String true ? 5 : "abc" is 5
Bool Float Bool false ? 2.5 : true is true
Bool Array List true ? [1, 2, 3] : <4, 5, 6> is [1, 2, 3]
Bool Set Structure false ? {1, 2, 3} : {x->2 y->3} is {x->2 y->3}

2.3.3 Builtin Functions

The builtin functions have the highest priority as long as they can be seen as predicates. An important aspect is that there can't be custom defined functions with the same name as the builtin functions.

2.3.3.1 Mathematical

There are several mathematical builtin functions. All of them work with numeric types and also evaluate to numeric values. The table below explains the behavior of the mathematical builtin functions when the numeric values a and b are used.

Builtin Function Representation Description
Sin sin(a) Returns the sine of a, where a is a numeric value representing radian units
Cos cos(a) Returns the cosine of a, where a is a numeric value representing radian units
Tan tan(a) Returns the tangent of a, where a is a numeric value representing radian units
Asin asin(a) Returns the inverse sine of a, where a is a numeric value which should be in _[-1, 1]_ interval
Acos acos(a) Returns the inverse cosine of a, where a is a numeric value which should be in _[-1, 1]_ interval
Atan atan(a) Returns the inverse tangent of a, where a is a numeric value which should be in _[-1, 1]_ interval
Log log(a) Returns the natural logarithm of a, where a is a positive numeric value
Pow pow(a, b) Returns a power b
Sqrt sqrt(a) Returns the square root of a
Pi pi() Returns the value of _π_
Abs abs(a) Returns the absolute value of a

Note that all the functions above are working with numeric type values: integer or float. The result of these functions are all float, no matter the operands' types. In the examples section we presume that the precision was set to 3 decimals after the floating point.
Examples:

Operand 1 Operand 2 Sin Cos Tan Asin Acos Atan Log Pow Sqrt Pi Abs
Integer Integer pow(2, 3) is 8.0
Integer Float pow(2, 3.2) is 11.313
Float Integer pow(2.5, 3) is 15.625
Float Float pow(2.5, 3.5) is 24.705
Integer sin(2) is 0.909 cos(2) is -0.412 tan(2) is -2.185 asin(1) is 1.570 acos(-1) is 3.141 atan(1) is 0.785 log(2) is 0.693 sqrt(2) is 1.414 abs(2) is 2.0
Float sin(-2.5) is -0.598 cos(-2.5) is -0.801 tan(-2.5) is 0.747 asin(-0.5) is -0.523 acos(-0.5) is 2.094 atan(-0.5) is -0.463 log(0.5) is -0.693 sqrt(0.5) is 0.707 abs(-0.5) is 0.5
pi() is 3.141

2.3.3.2 Conversion

There are only two conversion builtin functions while are used in order to change the data type of one numerical value from float to integer or from integer to float. The table below explains the behavior of the conversion builtin functions when the numeric value a is used.

Builtin Function Representation Description
Int int(a) Converts the numeric value of a to an integer representation.
Float float(a) Converts the numeric value of a to a float representation.

Note that the int function can produce data loss if a has a non-zero fractional part. Also, the only data type allowed for a is either float or integer.
Examples:

Data type Int Float
Integer int(2) is 2 float(2) is 2.0
Float int(2.5) is 2 float(2.5) is 2.5

2.3.3.3 String based

There is only one string based function which is used in order to determine the size of a string. The table below explains the behavior of the string based builtin function when the string value a is used.

Builtin Function Representation Description
Len len(a) Returns the size of the string a in number of characters.

Note that the len builtin function returns a positive integer and the only valid operand data type is string.
Examples:

Data type Len
String len("abc") is 3

2.3.3.4 IO

There is only one IO function which is used in order to print to the terminal a provided value. The table below explains the behavior of the IO builtin function when the value a is used.

Builtin Function Representation Description
Print print(a) Writes to the terminal a string which represents the value a

Note than any kind of value is valid for the print function as all data types do have a string representation. This can't be used inside an expression as it doesn't return any value. It can however be used as a standalone function call.
Examples:

Data type Print
Integer print(2) prints 2
Float print(2.5) prints 2.5
Bool print(true) prints true
String print("abc") prints "abc"
Array print([1, 2, 3]) prints [1, 2, 3]
List print(< 1, 2, 3 >) prints < 1, 2, 3 >
Set print({1, 2, 3}) prints {1, 2, 3}
Structure print({x -> 2 y -> 5}) prints {x -> 2 y -> 5}

2.3.3.5 Probabilistic

There is only one probabilistic function which is used to randomly generate an integer value using an uniform distribution. The table below explains the behavior of the probabilistic builtin function when the integer value a is used.

Builtin Function Representation Description
UniformNat uniformNat(a) Returns a random integer number which is strictly less than a and greater than or equal to 0

Note than only integer values are valid for the operand. Also, the use of this function triggers an probabilistic execution.
Examples:

Data type Print
Integer uniformNat(4) returns a value in {0, 1, 2, 3} with uniform probability

2.3.3.6 Structural

There is only one structural function which is used to compute a set containing a single element. The table below explains the behavior of the structural builtin function when the value a is used.

Builtin Function Representation Description
SingletonSet singletonSet(a) Returns a set containing a single element a

Note that this function always return a set and the operand data type is irrelevant. This can also be reproduced with a simpler syntax {a}, where a is the only element in the set.
Examples:

Data type SingletonSet
Integer singletonSet(2) is {2}
Float singletonSet(2.5) is {2.5}
Bool singletonSet(true) is {true}
String singletonSet("abc") is {"abc"}
Array singletonSet([1, 2, 3]) is {[1, 2, 3]}
List singletonSet(<1, 2, 3>) is {<1, 2, 3>}
Set singletonSet({1, 2, 3}) is {{1, 2, 3}}
Structure singletonSet({x -> 2 y -> 3}) is {{x -> 2 y -> 3}}

2.3.4 Builtin Methods

The builtin methods have a high priority; in fact they have the same priority as the dot operator and the bracket operator. However, the order of these operators is relevant in execution. Also, the parenthesis can override these rules. In order to call a builtin method, there is a special syntax which should be used.

Syntax for method-call
reference.method-name(method-parameters)

Reference:
The reference usually has to point to a specific location which should be already created (the dynamic allocation doesn't activate in this scenario). The most basic type of reference is an identifier, which already exists or will be automatically created in the environment. A more complex reference is composed out of an identifier and a sequence of brackets and dot operators to indicate a specific location inside a compound data type value.
Method-name:
The methods are all builtin, meaning that the method-name should be chosen from a predefined list of names, depending upon the desired behavior. Custom methods are not supported at the moment.
Method-parameters:
The method-parameters component is a list of comma separated expressions. The number of expressions should match the number of parameters requested by the builtin method. Also, depending on the method, the evaluation data type values should match the parameters constraints (ex: at method works only with specific integers)

2.3.4.1 Query

There are several query methods which are used in order to get information about some values (strings or compound). They do not modify the value. The table below explains the behavior of the query builtin methods when the target value a, the integer value x and the string value y are used.

Builtin Method Representation Description
At a.at(x) Evaluates to the element on the xth position in the compound value a. _x_ should be greater than 0 and less than the size
Size a.size() Computes the size of a in constant time
TopBack a.topBack() Evaluates to the last element in the value a
TopFront a.topFront() Evaluates to the first element in the value a
Split a.split() Returns an array of characters resulted after spliting the string a
Split with regex a.split(y) Returns an array of strings resulted after splitting the string a with a regular expression _y_

Note that all these methods are returning different kind of data types (depending on the operand and method). Also, not all compound data types are eligible for all these methods. The table below illustrates these aspects.

Builtin Method Data type Return Return Type
At string, array or list the element on the xth position in the value the data type of the element at the xth position
Size array, list or set the number of values nested in the value integer
Size string the number of characters in the string integer
TopBack list the last element in the value the data type of the last element in the value
TopFront list the first element in the value the data type of the first element in the value
TopFront list the first element in the value the data type of the first element in the value
Split string the characters which can be found in the string an array of characters
Split with regex string the strings resulted after splitting the string with a regex an array of strings

For the at method, one should provide a valid integer value x.
Examples:

Data type At Size TopBack TopFront Split Split with regex
String "abc".at(2) is "c" "abc".size() is 3 "abc".split() is ["a", "b", "c"] "a c".split(" ") is ["a", "c"]
Array [5, 7, 10].at(1) is 7 [5, 7, 10].size() is 3
List <5, 7, 10>.at(1) is 7 <5, 7, 10>.size() is 3 <5, 7, 10>.topBack() is 10 <5, 7, 10>.topFront() is 6
Set {5, 7, 10}.size() is 3

2.3.4.2 Update

There are several update methods which are used in order to modify some values (string or compound). They do modify the value and return a reference to the modified object. The table below explains the behavior of the query builtin methods when the target value a and values x, y are used.

Builtin Method Representation Description
Non-Sequential Insert a.insert(x) Insert value _x_ into a non-sequential value a
Sequential Insert a.insert(x, y) Insert value _y_ into a sequential value a at position _x_
PopBack a.popBack() Remove the last element in a sequential value a
PopFront a.popFront() Remove the first element in a sequential value a
PushBack a.pushBack(x) Add the value _x_ as the last element of a sequential value a
PushFront a.pushFront(x) Add the value _x_ as the first element of a sequential value a
Remove a.remove(x) Removes value _x_ from a non-sequential value a
RemoveAt a.removeAt(x) Removes the element at position _x_ from a sequential value a
RemoveAllEqTo a.removeAllEqTo(x) Removes all elements equal to _x_ from value a
Update a.update(x, y) Updates the value from position _x_ to value _y_ inside the sequential value a

Note that there is a distinction between the sequential (array, list) values and non-sequential values (set). Also, there are restrictions on the values of x and y. All of these are described in the table below.

Builtin Method Data type First parameter Second parameter
Non-Sequential Insert set Any kind of value
Sequential Insert array, list An integer greater or equal to 0 and less or equal to the size of the target value Any kind of value
PopBack array, list
PopFront array, list
PushBack array, list Any kind of value
PushFront array, list Any kind of value
Remove set Any kind of value
RemoveAt array, list An integer greater or equal to 0 and less than the size of the target value
RemoveAllEqTo array, list Any kind of value
Update array, list An integer greater or equal to 0 and less than the size of the target value Any kind of value

Note that all these methods return the modified value (which is the same as the target value used). This means that multiple methods can be chained: a.pushBack(x).popBack() is a valid expression.
Examples:

Method Array List Set
Non-Sequential Insert {1, 2, 3}.insert(4) is {1, 2, 3, 4}
Insert [1, 2, 3].insert(1, 4) is [1, 4, 2, 3] <1, 2, 3>.insert(1, 4) is <1, 4, 2, 3>
PopBack [1, 2, 3].popBack() is [1, 2] <1, 2, 3>.popBack() is <1, 2>
PopFront [1, 2, 3].popFront() is [2, 3] <1, 2, 3>.popFront() is <2, 3>
PushBack [1, 2, 3].pushBack(4) is [1, 2, 3, 4] <1, 2, 3>.pushBack(4) is <1, 2, 3, 4>
PushFront [1, 2, 3].pushFront(4) is [4, 1, 2, 3] <1, 2, 3>.pushFront(4) is <4, 1, 2, 3>
Remove {1, 2, 3}.remove(2) is {1, 3}
RemoveAt [1, 2, 3].removeAt(1) is [1, 3] <1, 2, 3>.removeAt(1) is <1, 3>
RemoveAllEqTo [1, 2, 1].removeAllEqTo(1) is [2] <1, 2, 1>.removeAllEqTo(1) is <2>
Update [1, 2, 3].update(1, 4) is [1, 4, 3] <1, 2, 3>.update(1, 4) is <1, 4, 3>

2.4 Declarations and Initializations

2.4.1 Default initialization values

In the context of using Alk, the variables should not be declared, so there is no concept alike the default initialization. This is due to the fact that, Alk does not create pristine variables which do have a default value (or a garbage value). The variables created are in fact spawned because they are needed into an assign, so no intermediary value is used.
However, there is a similar concept to the default values present in Alk. The idea of unknown value is used when dealing with non-returning functions inside expressions or empty spaces inside dynamically allocated arrays. The next section will show that this unknown value is.

2.4.2 Dynamic allocation

There is an important feature of Alk regarding the allocation. Due to the fact that one wants to use uninitialized compound data values, Alk offers support for dynamically allocation arrays/structures such that the references used do make sense and point to a valid memory address.
For this matter, imagine the following example: a[2].x = 5;. Note that this is the only instruction in the parsed algorithm. As variables should not be declared, this statement should work without any other specifications. For this job, Alk will automatically assign to the variable a an array data type value, which will have the size of 3, in order to allow the use of the bracket operator. Furthermore, at the index 2, a structure should be allocated in order to match the dot operator. Finally, after those two enclosed values were created, the assignment will work as Alk will automatically allocate a component with id x which will be assigned to 5. However, the process above has a practical flow: what happens to the extra allocated space? In the example above, what values will be assigned to a[0] and a[1]. For this matter, Alk uses a special kind of representation, the ? character which will symbolize that the specified additional space is unusable in the current state. So any usage of a[0] or a[1] will result in an error. However, one can assign new values to these references in order to properly access them afterwards.
Examples:

Assignment statement Final configuration
a[2][3].x.y[1].z = 2; a |-> [?, ?, [?, ?, ?, {x -> {y -> [?, {z -> 2}]}}]]
a[0][1][2][3][4] = 2; a |-> ?, [?, ?, [?, ?, ?, [?, ?, ?, ?, 2]]]
a.x.y.z = 2; a |-> {x -> {y -> {z -> 2}}}

2.4.3 Iterable data types definition

2.4.3.1 Expression based definition

The expression based definitions are the basic way to define one iterable data type value. It allows one to enclose between specific characters, a series of comma separated expressions. Each expression will be evaluated and the result will be considered to be contained by the specified data type in the order provided. Note that a set can change the order of the elements.
Examples:

Representation Final configuration
a = [1+1, {1, 2, 3} U {2, 3, 4}, 1.2/2]; a |-> [2, {1, 2, 3, 4}, 0.6]
a = <1+1, {1, 2, 3} U {2, 3, 4}, 1.2/2>; a |-> <2, {1, 2, 3, 4}, 0.6>
a = {1+1, {1, 2, 3} U {2, 3, 4}, 1.2/2}; a |-> {0.6, 2, {1, 2, 3, 4}}

2.4.3.2 Interval based definition

The interval based definition provides a way to easily instantiate an iterable data type value with a sequence of integers contained in a specified interval. The order of the elements will be ascending.
Examples:

Representation Final configuration
a = [1..5]; a |-> [1, 2, 3, 4, 5]
a = <1..5>; a |-> <1, 2, 3, 4, 5>
a = {1..5}; a |-> {1, 2, 3, 4, 5}

2.4.3.3 Filter specification based definition

This type of representation is well suited in scenarios in which one wants to apply a certain filtering over the elements of a container. The filtering specification has a boolean expression provided which will be run using each element in a source value in order to detect if the chosen element shall pass the filtering or not. This depends on the result of the boolean expression.
Examples:

Representation Final configuration
a = [x from [1..5] | x % 2 == 0]; a |-> [2, 4]
a = < x from [1..5] | x % 2 == 1 >; a |-> <1, 3, 5>
a = {x from [1..5] | x % 2 == 0}; a |-> {2, 4}

2.4.3.4 Mapping-based specification

This type of representation is well suited in scenarios in which one wants to apply a certain mapping function over all elements in a source value. The mapping process has an expression whose resulting values will be contained inside the final value.
Examples:

Representation Final configuration
a = [2 * x | x from [1..5]]; a |-> [2, 4, 6, 8, 10]
a = <2 * x | x from [1..5]>; a |-> <2, 4, 6, 8, 10>
a = {2 * x | x from [1..5]}; a |-> {2, 4, 6, 8, 10}

2.4.3.5 Empty definition

There are several ways to instantiate an empty iterable data type value. Note that for some of those there are special keywords which automatically evaluated to an empty specified compound value.
Examples:

Representation Final configuration
a = []; a |-> []
a = < >; a |-> < >
a = {}; a |-> {}
a = emptyList; a |-> < >
a = emptySet; a |-> {}
a = emptyStructure; a |-> {->}
a = {->}; a |-> {->}

2.5 Functions

2.5.1 Function declaration

Functions can be declared anywhere inside the algorithm and their score is global. This is due to the fact that function declarations are statements, so they can be used inside any sequence of statements. Note that it is recommended to declare the functions to the global scope anyway.

Syntax
function-name(param-list) modifies global-list
function-name(param-list) uses global-list
function-name(param-list)
function-name()
function-name() modifies global-list
function-name() uses global-list

Function-name:
The function name should be an id after which the function will be identified and eventually be called.
Param-list:
The parameters list is a component which should be replaced with a list of comma separated parameters. These can belong to a set of two types: input and output parameters. The input parameters can be changed, but the modifications won't be reflected in the calling state (so this parameters are in fact copies of the arguments used). The output parameters reference however the values provided at the function call, so any modification over these values will be made on the argument values as well. In order to mark which are the output parameters and which are not, the following syntax is used:

Syntax Meaning
param-name input parameter
out param-name Output parameter

The param-name component should be an id with which the value given will be referenced. Note that functions open separate environments, so any variable used here should not affect the ones in the outer scope - so the same id can be used for different values.
Global-list:
The list which is after the modifies keyword is a list of variables from the global scope which can be changed by this function. This syntax is used in order to allow the reuse of some ids which were initially used in the global scope. Note that all the parameters which come after the modifies keyword are output parameters, so any change on those will reflect in the global scope.
Examples:

Algorithm Output
func(a, b, c) { c = a + b; print(c); } a = 2; b = 3; c = 4; func(a, b, c); print(c); 5 4
func(a, b, out c) { c = a + b; print(c); } a = 2; b = 3; c = 4; func(a, b, c); print(c); 5 5
func(a, b) modifies c { c = a + b; print(c); } a = 2; b = 3; c = 4; func(a, b); print(c); 5 5
func(a, b) { c = a + b; print(c); } a = 2; b = 3; c = 4; func(a, b); print(c); 5 4

2.5.2 Function call

The function call is the main way to trigger the code inside a declared function. Note that a function can be called only after it was defined. The number of parameters should match, and the modifies list should have only ids of variables which are already in the global environment. Also, an important fact is that the output parameters can't take expressions or static values as argument. Therefore, the argument for an output parameter can be only an id (a reference).
A function call can be used inside a single function call statement. This kind of function should not necessarily return, as the returning value will be lost. The other use case is function calls inside expressions. For this scenario, the functions should return, otherwise an error will be shown.
There are already examples of function calls inside single statements in the previous section. Function calls inside expressions are shown in the next section.

2.5.3 Return call

In order to make a function return a value, the return statement should be used. This will take a computed value and return it to the calling context. Note that the execution of the function is halted at this point, so no other statement after the return will execute.

Syntax
return;
return expression;

Note that there can be no return value. In this case, the return call is used only to trigger the stopping of the function. If a value is used however, the value will be propagated to the calling context.
Expression:
The expression component should evaluate to a valid value which is meant to be sent to the calling context.
Examples:

Algorithm Output
func(a, b) { return a + b; } a = 2; b = 3; print(func(a, b)); 5
func() modifies a { return 2 * a; } a = 2; a += func() * 2; print(a); 10
func(out a, out b) { a = 2 return ; b = 2; } a = 5; b = 5; func(a, b); print(a); print(b); 2 5

2.6 Instructions

2.6.1 Simple instructions

2.6.1.1 Assignment

The assignment statement is a way to directly assign a specific value to a reference. The value can be of any kind as the variable can change types at the runtime according to dynamic typing. The reference can be a simple identifier or a more complex reference which will be eventually subject to dynamic allocation.

Syntax
reference assignment-operator expression;

Reference:
The reference usually has to point to a specific location in the store which eventually can be created by the dynamic allocation procedure for arrays and structures. The most basic type of reference is an identifier which already exists or will be automatically created in the environment. A more complex reference is composed out of an identifier and a sequence of brackets and dot operators to indicate a specific location inside a compound data type value.
Assignment-operator:
There are several assignment operators which behave suggestively to their representation. The most basic assignment operator is represented through an equal sign =, denoting that the value in the right side will be simply copied to the location suggested by the reference in the left side. The table below shows all the assignment operators and their behaviors:

Assignment operator Representation Description
Simple assign = Assigns the value without modifing it
Addition assign += Assigns the result from the addition operation between the left-value and the specified right-value
Subtraction assign -= Assigns the result from the subtraction operation between the left-value and the specified right-value
Multiplication assign *= Assigns the result from the multiplication operation between the left-value and the specified right-value
Division assign /= Assigns the result from the division operation between the left-value and the specified right-value
Mod assign %= Assigns the result from the mod operation between the left-value and right-value
Left shift assign <<= Assigns the result from the left shift operation between the left-value and right-value
Right shift assign >>= Assigns the result from the right shift operation between the left-value and right-value
Bitwise and assign &= Assigns the result from the bitwise and operation between the left-value and right-value
Bitwise or assign |= Assigns the result from the bitwise or operation between the left-value and right-value

Expression:
The expression should evaluate to the value which is desired to be assigned to the specified reference. As previously stated, the data type of the result shouldn't respect any limitations due to the dynamic allocation feature.
Examples:

Initial configuration Assignment Final configuration
a |-> [1, 2, 3] a = true; a |-> true
a |-> "abc" a += "def"; a |-> "abcdef"
a |-> 10.4 a -= 2.7 * 2; a |-> 5.0
a |-> 2 a *= (1 << 1) + 1; a |-> 6
a |-> 13 a /= (6 >> 1); a |-> 4
a |-> 13 a %= 1 + 1 + 1; a |-> 1
a |-> 5 a <<= 6 * 2 / 4; a |-> 40
a |-> 47 a >>= 8 % 5; a |-> 5
a |-> 5 a &= a + 1; a |-> 4
a |-> 6 a |= a - 1; a |-> 7
a |-> [1, 2, 3, 4] a[2] = 5; a |-> [1, 2, 5, 4]
a |-> {x -> 3 y -> 2} a.x = 7; a |-> {x -> 7 y -> 2}
a |-> [1, {x -> 3 y -> 2}, 2, 3] a[1].y = 7; a |-> [1, {x -> 3 y -> 7}, 2, 3]
a |-> {x -> [1, 2, 3] y -> 2} a.x[1] = 7; a |-> {x -> [1, 7, 3] y -> 2}
a[0][2].x[2].y = 7; a |-> ?, ?, {x -> [?, ?, {y -> 7}]}

2.6.1.2 Function Call

The function call statement is used in order to execute a specific function whose return value is not intended to be used in any expression. It is recommended to run in this way only the functions which do not return values. As any function call, this should respect the norms described in the function-call section: match the name of the function, the number of parameters and pass only references to output parameters. Note that this statement can be used in order to also call builtin functions like print.

Syntax
function-call;

Function-call:
This is the only part of the statement which should be defined. The definition is already described in the function declaration section.
Examples:

Initial configuration Function Call Description Final configuration
print([1, 2, 3]); Use the print builtin function in order to print [1, 2, 3]
a |-> 15 b |-> 12 c |-> 0 gcd(a, b); Presume that the custom gcd function computes the greatest common division of a and b and assigns it to _c_ a |-> 15 b |-> 12 c |-> 3
a |-> 2 b |-> 3 swap(a, b); Presume that the custom swap function interchanges the values of a and b a |-> 3 b |-> 2
a |-> [5, 2, 4, 1, 3] sort(a); Presume that the custom sort function sorts the elements of a in-place a |-> [1, 2, 3, 4, 5]

2.6.1.3 Method Call

The method call statement is used in order to execute a specific builtin method over a given target value. Is is not recommended to use query builtin methods in this fashion as they will have no effect after all. This is mainly meant to be used together with update builtin methods.

Syntax
method-call;

Method-call:
This is the only part of the statement which should be defined. The definition is already described in the methods section.
Examples:

Initial configuration Method Call Final configuration
a |-> [1, 2, 3] a.pushBack(4); a |-> [1, 2, 3, 4]
a |-> [1, <1, 2, 3>, 3] a[1].popBack(); a |-> [1, <1, 2>, 3]
a |-> {x -> [1, 2, 3] y -> [1]} a.x.popFront(); a |-> {x -> [2, 3] y -> [1]}

2.6.1.4 Increment/Decrement

The increment/decrement statement is a sugar syntax for a simple assignment. This makes use of the increment/decrement operator described in the expressions section. The meaning of each syntax is related to the meaning of the operator used: increment or decrement, prefix or postfix.

Syntax
reference++;
reference--;
++reference;
--reference;

Reference:
The reference usually has to point to a specific location in the store which eventually can be created by the dynamic allocation procedure for arrays and structures. The most basic type of reference is an identifier which already exists or will be automatically created in the environemnt. A more complex reference is composed out of an identifier and a sequence of brackets and dot operators to indicate a specific location inside a compound data type value.
Examples:

Initial configuration Increment/Decrement Final configuration
a |-> 5 a++; a |-> 6
a |-> 5.5 ++a; a |-> 6.5
a |-> 5.5 a--; a |-> 4.5
a |-> 5 --a; a |-> 4

2.6.2 Compound instructions

Compound instructions are used in order to group several statement into a single one. The execution is still sequential; basically there is no impact on efficiency when using compound instructions. The main reason to use those is to allow the developer to put multiple statements inside a complex statement like a conditional or repetitive instruction. The syntax is intuitive: one should enclose the sequence of statement inside curly braces.

Syntax
{ statements }

Statements:
The statement component is a list of statements which are meant to be grouped inside the compound instruction. There is no restriction whatsoever on the type of statements. Examples:

Initial configuration Assignment Final configuration
a |-> 5 { a = 5; a = 8; } a |-> 8
a |-> 5.5 { a += 1; print(a); a -= 2; } a |-> 4.5

2.6.3 Conditional instruction - if/else

The conditional instruction is used in order to put a condition over the execution of a statement (or implicitly a sequence of statements if the compound instruction is used). The conditioning is based on an expression which should evaluate to a bool value (the use of any other kind of data type value will result into an error). In case the execution should choose between two statements based on a condition, the if and else keywords should be used. Otherwise, meaning that only one statement should be be executed or not, it is enough to use only the if keyword.

Syntax
if (bool-expression) statement1
if (bool-expression) statement1 else statement2

Bool-expression:
The bool-expression component refers to the condition used for the conditional instruction. It is clear the fact that this expression should evaluate to a bool value. In case the expression evaluates to true, then statement1 will be executed. Otherwise, it depends upon the syntax used. If no else keyword is used, then nothing happens (the expression evaluates to false). In case the else keyword is used and the expression evaluates to false, then statement2 will be executed.
Statement1 and statement2:
Statement1 refers to the statement which will be executed in case the expression evaluates to true. This can be any kind of statement (compound instruction included and even recommended). Statement2 refers to the statement which will be executed in case the else keyword is used and the expression evaluates to false. This also can represent any kind of statement (compound instruction included and even recommended).
Examples:

Initial configuration Conditional instruction Final configuration
a |-> 5 if (a % 2 == 0) b = "a is even"; else b = "a is odd"; a |-> 5 b |-> "a is odd"
a |-> 10 if (a % 2 == 0) b = "a is even"; else b = "a is odd"; a |-> 5 b |-> "a is even"
a |-> 12345 if (a > 100) a = 100; a |-> 100
a |-> 24 if (a > 100) a = 100; a |-> 24
a |-> 2 b |-> 5 if (a > 0 && b > 0) { a = -a; b = -b; } a |-> -2 b |-> -5
a |-> -2 b |-> -100 if (a > 0 && b > 0) { a = -a; b = -b; } else { a = 0; b = 1; } a |-> 0 b |-> 1

2.6.4 Repetitve instructions

2.6.4.1 While instruction

The while instruction is a repetitive statement with initial check which will execute a specific statement multiple times until a given condition evaluates to false. Note that the condition provided should always evaluate to a bool value, otherwise an error will be displayed. The statement can be of any type as there is no restriction over it (the statement can be even a compound instruction which is in fact recommended).

Syntax
while (bool-expression) statement

Bool-expression:
The bool-expression component refers to the condition used for the while instruction. It is clear that this expression should evaluate to a bool value. Before the first iteration, the expression is evaluated. In case it evaluates to true then the statement is executed. Otherwise the process is halted and the while instruction is skipped. After each iteration, the condition is re-evaluated following the same behavior: if the condition evaluates to true the next iteration is triggered. Otherwise, the looping process ends and the while instruction is skipped.
Statement:
Statement refers to the component which should be executed every time the expression evaluates to true. Note that the statement can be of any kind. Due to instruction's definition, there are cases in which this statement will never be executed.
Examples:

Initial configuration While instruction Final configuration
a |-> 156 while (a > 100) a--; a |-> 100
a |-> 28 while (a > 100) a--; a |-> 28
a |-> 1 b |-> 10 while (abs(a - b) > 5 && a < b) { a++; b--; } a |-> 3 b |-> 8
a |-> 15 b |-> 12 while (b > 0) { r = a % b; a = b; b = r; } a |-> 3 b |-> 0 r |-> 0

2.6.4.2 Do-while instruction

The do-while instrucion is a repetitive statement with final check which will execute a specific statement multiple times until a given condition evaluates to false. Note that the condition provided should always evaluate to a bool value, otherwise an error will be displayed. The statement can be of any type as there is no restriction over it (the statement can be even a compound instruction which is in fact recommended). Comparing to a simple while instruction, the do-while instruction evaluates the condition only at the end. This means that the statement will be executed at least once.

Syntax
do statement while (bool-expression);

Bool-expression:
The bool-expression component refers to the condition used for the do-while instruction. It is clear the fact that this expression should evaluate to a bool value. The first iteration will be done without checking the condition. After each iteration, the condition is evaluated: if the condition evaluates to true the next iteration is triggered. Otherwise, the looping process ends and the while instruction is skipped.
Statement:
Statement refers to the component which should be executed every time the expression evaluates to true. Note that the statement can be of any kind. Due to instruction's definition, the statement will execute at least once no matter the condition.
Examples:

Initial configuration Do-while instruction Final configuration
a |-> 156 do a--; while (a > 100); a |-> 100
a |-> 28 do a--; while (a > 100); a |-> 27
a |-> 5 b |-> 5 do { a++; b--; } while (abs(a - b) > 5 && a < b); a |-> 6 b |-> 4
a |-> 15 b |-> 12 do { r = a % b; a = b; b = r; } while (b > 0); a |-> 3 b |-> 0 r |-> 0

2.6.4.3 Repeat-until intruction

The repeat-until instruction is a repetitive statement with final check which will execute a specific statement multiple times until a given condition evaluates to true. Note that the condition provided should always evaluate to a bool value, otherwise an error will be displayed. The statement can be of any type as there is no restriction over it (the statement can be even a compound instruction which is in fact recommended). Comparing to a do-while instruction, the do-while is halted when the condition evaluates to false. The repeat-until statement is halted when the condition is evaluated to true

Syntax
repeat statement until (bool-expression);

Bool-expression:
The bool-expression component refers to the condition used for the repeat-until instruction. It is clear the fact that this expression should evaluate to a bool value. The first iteration will be done without checking the condition. After each iteration, the condition is evaluated: if the condition evaluates to false the next iteration is triggered. Otherwise, the looping process ends and the while instruction is skipped.
Statement:
Statement refers to the component which should be executed every time the expression evaluates to false. Note that the statement can be of any kind. Due to instruction's definition, the statement will execute at least once no matter the condition.
Examples:

Initial configuration Repeat-until instruction Final configuration
a |-> 156 repeat a--; until (a > 100); a |-> 155
a |-> 234 repeat a--; until (a == 100); a |-> 100
a |-> -10 b |-> 10 repeat { a++; b--; } until (abs(a - b) < 5); a |-> -2 b |-> 2
a |-> 15 b |-> 12 repeat { r = a % b; a = b; b = r; } until (b == 0); a |-> 3 b |-> 0 r |-> 0

2.6.4.4 For instruction

The for instruction is a repetitive statement with initial check which will execute a specific statement multiple times until a given condition evaluates to true. Note that the condition provided should always evaluate to a bool value, otherwise an error will be displayed. The statement can be of any type as there is no restriction over it (the statement can be even a compound instruction which is in fact recommended). Comparing to a while instruction, the for instruction also provides support for initial assignment (oftenly used for initializing an iterator) and support for intermediary assignment or increase/decrease (oftenly used to change the iterator).

Syntax
for (initial-expression; bool-expression; intermediary-expression) statement

Bool-expression:
The bool-expression component refers to the condition used for the for instruction. It is clear the fact that this expression should evaluate to a bool value. At first, the initial-assign is executed. Before the first iteration, the expression is evaluated. In case it evaluates to true then the statement is executed. Otherwise the process is halted and the for instruction is skipped. After each iteration, the condition is re-evaluated following the same behavior: if the condition evaluates to true the next iteration is triggered. Otherwise, the looping process ends and the while instruction is skipped. Note that before the expression is re-evaluated, the intermediary-assign is executed.
Initial-expression:
The initial expression is used in order to assign to an eventual iterator a value. This will be executed before anything else. Note that the initial assign can be omitted.
Intermediary-expression:
The intermediary expression is executed after each loop before the expression is re-evaluated. This should be used in order to change the iterator in a more complex way than an increase/decrease (which can be done with the other syntax version). Statement:
Statement refers to the component which should be executed every time the expression evaluates to true. Note that the statement can be of any kind. Due to instruction's definition, there are cases in which this statement will never be executed.
Examples:

Initial configuration For instruction Final configuration
s |-> 0 for(i = 1; i <= 10; i++) s += i; s |-> 55 i |-> 11
s |-> 0 for(; s < 10; s++) s *= 2; s |-> 15
s |-> 0 for(i = 1; i <= 10; i += 2) s += i; s |-> 25 i |-> 11
s |-> 0 for(i = 1; i != 5 && i < 100; i++) s += i; s |-> 10 i |-> 5
for(s = 0; false; s = 10) s += 2; s |-> 0
s |-> 0 for(i = 10; i >= 1; i--) { s += i; s *= 2; } s |-> 18434 i |-> 0

2.6.4.5 Foreach instruction

The foreach instruction is a repetitive statement which does not depend upon a given conditional expression. The foreach instruction is meant to be used when one wants to sequentially access the elements of an iterable compound data type value. The statement will assign a given variable to each element from the collection given and afterwards will execute the underlying statement. Note that the values do not reference the original ones inside the data type value (this means that any modification on the foreach variable won't take effect in the original structure).

Syntax
foreach id from iterable statement

Id:
The id represent the variable which is meant to be used in order to access the given iterable. This variable can be used afterwards in the underlying statement. The denoted variable shouldn't be necessarily initialized in any way - it will be dynamically assigned if not already in the environment.
Iterable:
The iterable component represent the value which is meant to be iterated. There are only three data type values which are eligible for this operation: array, list and set. The order in which the elements will be iterated depends upon the way in which they are stored. For the array and list data types, the elements will be iterated normally from the lower index to the higher index. The sets however are iterated randomly (however, multiple iterations can iterate the set in the same order).
Statement:
Statement refers to the component which will be executed multiple types. Note that there is no limitation on the type of the statement (it can be a compound instruction). The number of executions is equal to the size of the iterated value. Each iteration will change the environment (as the iterator itself will be changed) so the statement will work on different values of id each time.
Examples:

Initial configuration Foreach instruction Final configuration
A |-> [1, 2, 3] s |-> 0 foreach x from A s += x; A |-> [1, 2, 3] s |-> 6 x |-> 3
s |-> 0 foreach x from {5, 2, 3, 4} s = s * 10 + x; s |-> 2354 x |-> 4
s |-> 0 foreach x from <5, 2, 3, 4> { s = s * 10 + x; A[x] = 1; } A |-> [?, ?, 1, 1, 1, 1] s |-> 5234 x |-> 4
s |-> 0 A |-> [0, 0, 0, 0, 0] foreach x from {1, 2, 3} U {2, 3, 4} { s += x; A[x]++; } A |-> [0, 1, 1, 1, 1] s |-> 10 x |-> 1

2.6.4.6 Break and continue instruction

The break and continue instructions are not repetitive statements themselves. They are controlling the looping in a sense in which a continue statement can halt the execution of a loop while the break statement halts the execution of the entire lopping structure itself. They should be used when only inside of a looping statement (but not necessarily directly inside - it can be enclosed in a conditional statement for example). These only take effect on the closest looping statement, while the outer ones are not effected.

Syntax
continue;
break;

Examples:

Initial configuration Foreach instruction Final configuration
s |-> 0 while (s < 10) { s++; if (s == 5) { break; } } s |-> 5
s |-> 0 for (i = 1; i < 5; i++) { if (i == 3) { continue; } s = s * 10 + i; } s |-> 124 i |-> 5
s |-> 0 i |-> 1 do { if (i == 3) { break; } s = s * 10 + i; i++; } while (i < 5); s |-> 12 i |-> 3
s |-> 0 i |-> 1 repeat { i++; if (i == 3) { continue; } s = s * 10 + i; } until (i == 5); s |-> 245 i |-> 5
s |-> 0 A |-> [5, 7, 2, 3, 9] foreach x in A { if (i == 3) { break; } s = s * 10 + x; } A |-> [5, 7, 2, 3, 9] s |-> 572 x |-> 3

2.6.5 Nondeterministic instructions

2.6.5.1 Choose/such that instruction

The choose statement is the main tool to trigger a nondeterministic execution. A choose statement nondeterministically chooses an element from a given iterable value and assigns it to a specified variable. One can also specify an expression which should evaluate to a bool value and can be used as a hint for the statement to choose only values validating the expression. If the choose instruction can't find a suitable value, the execution will result in failure.

Syntax
choose exp-name from iterable;
choose exp-name from iterable s.t. exp-cond;

We can consider the first syntax version as a sugar syntax for the case when one doesn't want to put constraints on the selected element: choose id in iterable s.t. true; is equivalent to choose id in iterable;
Exp-name:
The exp-name is an expression that denotes the component which defines the variable which will be used to store the chosen value selected by the choose statement from the iterable value (e.g., x, a[i], p.x, p.x[i], ...).
Iterable:
The iterable is a compound data type value which can be normally iterated and is valid for such operation: array, list and set. The data type used for this is totally irrelevant. However, an empty iterable will always cause a failure in the execution. For the choose to succeed, one should provide a non-empty iterable which eventually has at least one element satisfying the such that clause.
Exp-cond:
The expression exp-cond is not mandatory as it can be seen in the first syntax version. If such expression is defined, the choose statement will only assign the given variable a value which also makes the expression exp-cond evaluate to true. If the expression invalidates all the elements from the list, the execution will fail.
Examples:

Initial configuration Choose/such that instruction Final configuration (nondeterministic) Success/Failure
choose x from {1, 2, 3} U {2, 3, 4}; x |-> 3 success
choose x from {}; failure
A |-> [1, 2, 3, 4, 5] choose x from A; A |-> [1, 2, 3, 4, 5] x |-> 1 success
choose x from <1..10> s.t. x % 2 == 0; x |-> 6 success
choose x from {1, 3, 5, 7} s.t. x % 2 == 0; failure
choose x from <1..10> s.t. x % 2 == 1; x |-> 5 success
choose x from [4, 3, 2, 1] s.t. x > 4; failure

2.6.5.2 Success and failure instructions

The success and failure statements are used to halt the entire execution of a program. These should be used only in nondeterministic executions in order to symbolize the fact that a selection branch fails or succeeds. An execution can also fail due to the "such that" clause inside the choose statement. However, if one can't decide at that point if a branch should fail or not, it should use the failure statement afterwards. Opposite to this, the success statement marks the end of a branch in a desired state.

Syntax
failure;
success;

Examples:

Initial configuration Success/Failure Instructions Final configuration (nondeterministic) Success/Failure
choose x in {1, 2, 3, 4}; if (x <= 2) failure; a = x; x = -2; success; x |-> 1 failure
choose x in {1, 2, 3, 4}; if (x <= 2) failure; a = x; x = -2; success; a |-> 3 x |-> -2 success

2.6.6 Probabilistic instructions

2.6.6.1 UniformNat builtin function

Beside the classic builtin functions, which are used in deterministic executions, the random builtin function is the main way to trigger a probabilistic execution. It is used to get a number in a specified range with uniform distribution. That being said, this is a returning function so it should be used inside expressions.

Syntax
uniformNat(right-limit)

Right-limit
This limit represents the upper bound of the selection interval. Note that the lower bound is always 0.
Examples:

Initial configuration UniformNat Function Final configuration (nondeterministic)
x = uniformNat(5); x |-> 1 with 20% probability
x = uniformNat(5); x |-> 4 with 20% probability
a |-> 1 c = uniformNat(a); a |-> 1 c |-> 0 with 100% probability

2.6.6.2 Uniform instruction

The uniform instruction is the probabilistic version of the nondeterministic choose statement. The uniform instruction itself has the same syntax as the choose statement, but it used the uniform keyword in the beginning. The major difference is that this uniform statement returns a number from the given source with uniform probabilistic distribution.

Syntax
uniform exp-name from iterable;

Id:
The exp-name expression should denote the component which defines the variable which will be used to store a value uniformly selected from the iterable value (e.g., x, a[i], p.x, p.x[i], ...).
Iterable:
The iterable should evaluate to a compound data type value, which can be normally iterated and is valid for such operation: array, list and set. The data type used for this is totally irrelevant. However, an empty iterable will always cause a failure in the execution. For the uniform to succeed, one should provide a non-empty iterable which eventually has at least one element satisfying the such that clause.
Examples:

Initial configuration Uniform instruction Final configuration (probabilistic)
uniform x from {1, 2, 3} U {2, 3, 4}; x |-> 3 with 25% probability
A |-> [1, 2, 3, 4, 5] uniform x from A; A |-> [1, 2, 3, 4, 5] x |-> 1 with 20% probability

3 Options

3.1 Setting the algorithm to be run

This option is the most important as it defines the file which should be parsed. One can select a single file as an entry-point. However, if there are several files which are to be taken in consideration, make sure to use the include directive in order to copy all the content needed in a single file which will be at the end executed. By copy, one should understand the automatic preprocessing stage done by the Alk interpreter. The files on the disk won't be modified.

Syntax
-a "file-path"

File-path:
This component should be replaced with a proper path toward a file containing an alk algorithm. The interpreter will consider the file there as the entry-point. The path can be either absolute or relative. In case the path is relative, the origin will be the current working directory.
Examples:

Command line Description
./alki.sh -a "main.alk" the option will tell the interpreter to start interpreting the main.alk file (Linux/Mac)
./alki.sh -a "/home/user/work/main.alk" the option will tell the interpreter to start interpreting the /home/user/work/main.alk file (Linux/Mac)
alki.bat -a "main.alk" the option will tell the interpreter to start interpreting the main.alk file (Windows)
alki.bat -a "C:\work\main.alk" the option will tell the interpreter to start interpreting the C:\work\main.alk file (Windows)

3.2 Setting the initial configuration

The initial configuration option will allow the user to set a starting environment. This should be used when testing an algorithm with specific testcases. These testcases should follow the syntax described in the configuration section. The same type of configuration will be eventually showed by the interpreter (if using the metadata option) and can be used as input for other algorithms.

Syntax
-i "configuration"
-i "file-path"

Configuration:
The configuration can be written inline. This means that the interpreter will parse the value of the option in order to retrieve the configuration. Note that no spaces should be used inside and the string quotes should be skipped using the backslash character.
File-path:
As an alternative, one can pass the path to a file containing the initial configuration. This is in fact the recommended version as it is more flexible in terms of syntax. The path can be either absolute or relative. If the relative path is used, the origin is the working directory.
Note that in the examples below, the -a option is required in order to specify an entry-point file and trigger the execution. Also, the extension for the file used as input doesn't matter (in the examples the fictive .in extension is used).
Examples:

Command line Description
./alki.sh -a "main.alk" -i "a|->12b|->15" the option will tell the interpreter to use the specified starting configuration (Linux/Mac)
./alki.sh -a "main.alk" -i "main.in" the option will tell the interpreter to use the content in the main.in as starting configuration (Linux/Mac)
./alki.sh -a "main.alk" -i "/home/user/work/main.in" the option will tell the interpreter to use the content in the /home/user/work/main.in as starting configuration (Linux/Mac)
alki.bat -a "main.alk" -i "a|->12b|->15" the option will tell the interpreter to use the specified starting configuration (Windows)
alki.bat -a "main.alk" -i "main.in" the option will tell the interpreter to use the content in the main.in as starting configuration (Windows)
alki.bat -a "main.alk" -i "C:\work\main.in" the option will tell the interpreter to use the content in the C:\work\main.in as starting configuration (Windows)

3.3 Setting the precision

The precision option is used in order to specify the number of digits which should be used after the floating point when using float operations. If this option is not used, the default of 10 is used. In case the precision is not that important, one can set a lower precision like 1 or 2. If computations with higher precision should be used, one should set this option to numbers greater than 10.

Syntax
-p digits

Digits:
The digits component should be replaced with a valid positive integer. The number here will represent the number of digits after the floating point when float operations are used.
Examples:

Command line Description
./alki.sh -a "main.alk" -p 2 the option will tell the interpreter to use a precision of 2 decimals after the floating point (Linux/Mac)
./alki.sh -a "main.alk" -p 200 the option will tell the interpreter to use a precision of 200 decimals after the floating point (Linux/Mac)
./alki.sh -a "main.alk" the lack of this option will tell the interpreter to use a precision of 10 decimals after the floating point (Linux/Mac)
alki.bat -a "main.alk" -p 2 the option will tell the interpreter to use a precision of 2 decimals after the floating point (Windows)
alki.bat -a "main.alk" -p 200 the option will tell the interpreter to use a precision of 200 decimals after the floating point (Windows)
alki.bat -a "main.alk" the lack of this option will tell the interpreter to use a precision of 10 decimals after the floating point (Windows)

3.4 Show the metadata

This option is used in order to trigger the metadata showing. The metadata represents the final configuration and eventually information about the execution: if it was deterministic, probabilistic or nondeterminstic. If the execution was probabilistic, the probability will be printed. Note that this is a boolean option, which means that no value should be assigned to the option. The simple presence of this option will trigger the behavior described.

Syntax
-m

Examples:

Algorithm stored in main.alk Command line Platform Output
a = 8; b = 10; c = a * b; print("abc"); ./alki.sh -a "main.alk" -m Linux/Mac "abc" a |-> 8 b |-> 10 c |-> 80
a = 8; choose x in [1..a]; print(x); ./alki.sh -a "main.alk" -m Linux/Mac 5 a |-> 8 x |-> 5 Note that the executed algorithm is nondeterministic.
uniform x from {0..7}; print(x); ./alki.sh -a "main.alk" -m Linux/Mac 5 x |-> 5 Note that the executed algorithm is probabilistic. The probability for this execution is: 0.125
a = 8; b = 10; c = a * b; print("abc"); alki.bat -a "main.alk" -m Windows "abc" a |-> 8 b |-> 10 c |-> 80
a = 8; choose x in [1..a]; print(x); alki.bat -a "main.alk" -m Windows 5 a |-> 8 x |-> 5 Note that the executed algorithm is nondeterministic.
uniform x from {0..7}; print(x); alki.bat -a "main.alk" -m Windows 5 x |-> 5 Note that the executed algorithm is probabilistic. The probability for this execution is: 0.125

3.5 Increase maximum allocation size

This option allows the increasing of the maximum allocation size of one compound data type. All of these dimensions are capped to one billion elements by default, but can be altered through this option. Such capping is a sanity measure to ensure that the dynamic allocation does not overflow. This can somehow limit the memory used by Alk compound data type values.

Syntax
-z size

Size:
The size component should be replaced with a valid positive integer number which will symbolize the maximum number of elements allowed inside one compound data. Note that this does not refer to physical memory, so this number is not the number of bytes or similar.
Examples:

Command line Description
./alki.sh -a "main.alk" -z 2 the option will tell the interpreter to allow the compound data values to store at most two elements (Linux/Mac)
./alki.sh -a "main.alk" -p 2000000000 the option will tell the interpreter to allow the compound data values to store at most 2 billion elements (Linux/Mac)
./alki.sh -a "main.alk" the lac of this option will tell the interpreter to allow the compound data values to store at most 1 billion elements (Linux/Mac)
alki.bat -a "main.alk" -p 2 the option will tell the interpreter to allow the compound data values to store at most two elements (Windows)
alki.bat -a "main.alk" -p 2000000000 the option will tell the interpreter to allow the compound data values to store at most 2 billion elements (Windows)
alki.bat -a "main.alk" the lack of this option will tell the interpreter to allow the compound data values to store at most 1 billion elements (Windows)

3.6 Trigger exhaustive execution

The exhaustive execution option is a very powerful feature of Alk. It allows the user to generate multiple executions when using nondeterministic algorithms such that the final result is in fact an exhaustive search over all choices. This is highly valuable as it makes use of multithreading, so an efficient way of computing the results for nondeterminstic algorithms. Note that the option is a boolean option, meaning that it should not have any value assigned to it. The simple presence of this option will tell the interpreter to split the execution when a choose statement is found, such that all choice paths are evaluated in parallel.

Syntax
-e

Note that the output is grouped for each execution, this means that the written lines are separated for each execution path. However, they are not sorted in any way. As a common fact, the shorter executions will be written first, while the executions which are way more complex are written at the end (as they end up later). Examples:

Algorithm in main.alk Command line Platform Output
a = 4; choose x in [1..a]; print(x); ./alki.sh -a "main.alk" -e Linux/Mac 4 Note that the executed algorithm is nondeterministic. 2 Note that the executed algorithm is nondeterministic. 1 Note that the executed algorithm is nondeterministic. 3 Note that the executed algorithm is nondeterministic.
choose x in [1..3]; for (i=0; i < pow(10, 4) * x; i++) continue; print(x); ./alki.sh -a "main.alk" -e Linux/Mac 1 Note that the executed algorithm is nondeterministic. 2 Note that the executed algorithm is nondeterministic. 3 Note that the executed algorithm is nondeterministic.
a = 4; choose x in [1..a]; print(x); alki.bat -a "main.alk" -e Windows 4 Note that the executed algorithm is nondeterministic. 2 Note that the executed algorithm is nondeterministic. 1 Note that the executed algorithm is nondeterministic. 3 Note that the executed algorithm is nondeterministic.
choose x in [1..3]; for (i=0; i < pow(10, 4) * x; i++) continue; print(x); alki.bat -a "main.alk" -e Windows 1 Note that the executed algorithm is nondeterministic. 2 Note that the executed algorithm is nondeterministic. 3 Note that the executed algorithm is nondeterministic.

3.7 Help and version

The help and version options are used in order to retrieve some information not execution related. This is why these are the only options which can be used without providing an entry-point file with the -a option. The help option provides a list of options which can be used and a short description for each of those. The version option will show the current version used of Alk. Note that these are boolean values.

Syntax
-h
-v

Examples:

Command line Description
./alki.sh -h this will show the list of options and how they should be used (Linux/Mac)
./alki.sh -v this will show the version of the used Alk (Linux/Mac)
alki.bat -h this will show the list of options and how they should be used (Windows)
alki.bat -v this will show the version of the used Alk (Windows)

Alk Reference Manual

written by Lungu Alexandru-Ioan

scientific coordinator Prof. dr. Lucanu Dorel

Faculty of Computer Science, UAIC Iasi

Clone this wiki locally