Data types, variables and operations

NetSimLan knows the data types node, int, float, string and bool. Most of them you already know. You can create a variable v of one of this types with type v; as we did before or with type v=e; where e is a value or expression for a value which will be assigned to v.

Whenever a variable is declared by no value is assigned yet, it has a default value. Try it out: add the following method to your grid-example and run it on one node:

defaultValues(){
    node n;     print (n);
    int i;      print (i);
    float f;    print (f);
    string s;   print (s);
    bool b;     print (b);
}

To sum this up, here’s a overview for the types:

Type possible values default value meaning of default
node this, null or any node null no node
int MIN_INTEGER to MAX_INTEGER 0 0
float floating numbers, POSITIVE_INFINITY, NEGATIVE_INFINITY, PI, E, NaN NaN not a number
string strings "" empty string
bool true, false false false

Expressions and arithmetic operations

You’ve already seen expressions in the former examples for example (id(this)-1)/size. This section is to give you an overview about the possible operations in expressions and special cases.

Integers and floats can be combined with +-*/ as you would expect from any programming language. You can also use the power function a^^b meaning a to the power of b. For integers you can use the modulo operator %. Whenever a not solvable arithmetic exception occurs e.g. 5/0, the inner method call producing that error is canceled and a message is printed to the output. The rest of the method stack continues.

When you combine an integer and a float you’ll receive a float. To turn this in an integer you can use round rounding to the next integer. Be aware, this is a correct round, not a simple floor. In context that may looks as follows:

int i; i=round((2*2.5)/2.0); /* now i == 3 */

You can also calculate the square root of a number n using sqrt(n), the logarithm (base 2) of n using log(n) or get a random integer between 0 (inclusive) and n (exclusive) using random(n).

To work with geometry, the trigonometric functions, inverse trigonometric functions and hyperbolic functions sin(a), cos(a), tan(a), arcsin(a), arccos(a), arctan(a), sinh(a), cosh(a), tanh(a) for a value a can be used. All of these functions use radiant for angles, meaning for example that sin(PI)==0 holds.

Numbers can be compared using ==, !=, <=, <, >= and >.
Anything can be compared to a string using the +, e.g. 5+"edges". We’ll see how to do more complex things with strings next chapter.
Boolean expressions a and b can be negated !a or combined with and a&b, or a|b and xor a^b.
All of this expressions can be structured using brakes ().

Node Types and Hash Functions

As you’ve seen you can access a node’s id with id(n), where n is an expression for the node. You can also get a type of a node as a string. That’s useful for networks with different node types, e.g. server and client. For example you can get a nodes own type with string myType = type(this);.

You may hash any datatype to an integer with tree different hash functions hash, hash2 and hash3. All of them get one parameter to hash and return an integer between MIN_INTEGER and MAX_INTEGER, but map a given value to different results. There are three function for the case you need different hash functions in you algorithm, e.g. to store something redundantly over different nodes. Let’s use the hash functionality: Open your grid-example again and add a new function for sending a message and calculate the hash of the text. That may looks as follows:

startMessage(string text, int to_id){
    message(text,to_id,hash(text));
}

Now you need to extend a your message function by a parameter int hashValue. Take care to forward it by every recursive call. Instead of just printing the message, you can check the hash value. For example as follows:

if (hash(text) == hashValue){
    print("Message " + text + " received.");
}else{
    print("Message with wrong hash occured");
}

Now try it out: Start the grid and send a message with startMessage. It should end as we used to know. Afterwards send a message with the message function and an arbitrary hash. That will most likely end with a message from the goal node saying the hash is wrong. If you like you can change your program, such that a hash isn’t just hashing the text but the text and the goal node. Or you can check the hash in every step and give the error message by the first node checking the message instead of the goal node.

Coordinates

So far we had a fixed routing protocol first fixing the x, second the y dimension. Let’s turn of the auto position and imagine the positions in the graph where real positions and some nodes are on positions far away from others. We’d like to change the routing protocol as follows: If the x or the x dimension is wrong but the other is correct, route along the wrong one. If both are wrong, route along that dimension on which the next hop is closer to the current one. We can get every nodes coordinates as float with the build in functions longitude and latitude. In total the rewrite of the message function may looks as follows:

message (string text, int to_id, int hashValue){
    int x_n=(to_id-1)/size; int y_n=(to_id-1)%size;
    if (x_n == x_this & y_n == y_this){
        if (hash(text) == hashValue){
            print("Message " + text + " received.");
        }else{
            print("Message with wrong hash occured");
        }
    } else if (x_n != x_this & y_n == y_this) {
        if (x_n < x_this)
            left -> message(text, to_id, hashValue);
        else right -> message(text, to_id, hashValue);
    } else if (x_n == x_this & y_n != y_this) {
        if (y_n < y_this)
            bottom -> message(text, to_id, hashValue);
        else top -> message(text, to_id, hashValue);
    } else {
        node next_x; node next_y;
        if (x_n < x_this) next_x = left;
            else          next_x = right;
        if (y_n < y_this) next_y = bottom;
            else          next_y = top;
        float distance_x = sqrt(
            (longitude(next_x)-longitude(this))^^2 +
            (latitude(next_x)-latitude(this))^^2 );
        float distance_y = sqrt(
            (longitude(next_y)-longitude(this))^^2 +
            (latitude(next_y)-latitude(this))^^2 );
        if (distance_x < distance_y)
            next_x -> message(text, to_id, hashValue);
        else next_y -> message(text, to_id, hashValue);
    }
    mark (true);
}

Functions with return values

You may notice, in the last code we do the same thing for different values. NetSimLan also offers return values. This is of cause only possible for local function calls and not for remote ones. A function to calculate this distance may looks as follows:

distance float (node n){
    float d = sqrt(
        (longitude(n)-longitude(this))^^2 +
        (latitude(n)-latitude(this))^^2 );
    return d;
}

Please note, the return type is behind the function name.

The calculation of distances in the message function reduces to the following lines:

float distance_x = distance(next_x);
float distance_y = distance(next_y);

Previous: Debugging Next: Arrays and Loops

The University for the Information Society