// This tutorial is part of a series that explains the basic concepts of the APRON simulation system.
// Please ensure you have read and understood any previous tutorial examples.
//
// © David R W Barr 2009
//
// Name: 		BASIC REGISTER OPERATIONS
// Description:	This tutorial will introduce the first-stage fundamentals of how to configure
//				an APRON simulation. This is demonstrated through the use of some simple examples
//				of array manipulation.
// Usage:		Please STEP-THROUGH this algorithm in the simulator.


// Always include this line for an APRON simulation. This file includes many pre-defined
// types and functions that are used in a standard APRON simulation.
!include('apron.aps')

// We now want to define some registers. A register is a 2D array of floating-point elements.
// The purpose of APRON is to provide quick, yet flexible combinations of operations that
// are performed on registers.

!create_reg('a', 128, 128) // Creates a register called 'a', and the dimensions are 128x128
!create_reg('b', 128, 128) // initially registers are filled with 0.
!create_reg('c', 128, 128)

// Now we have 3 registers: a, b & c. They are the same size, which is important for APRON
// Most array operations can opnly be performed on registers of equal dimensions.

// Lets fill every element of 'a' with 30. This is done with the assignment symbol '='
// To specify a register we must use either the 'r' or 'reg' prefix, with the name as
// an index
r[a] = 30 // Short-hand way of implying a register
reg[b] = 40 // Long-hand way of implying a register

// We can add the two registers together very simply. Below an element-wise addition is
// performed.
r[c] = r[a] + r[b]

// TODO: use the 'point analysis' tool in the simulator to inspect the register contents. To display
// a register it must be selected from the side-window. After all three have been selected, choose
// 'Window' -> 'Tile Vertically' from the menu

// Registers can also be added to themselves
r[a] = r[a] + r[b]
r[b] = r[b] + r[b]

// Now lets clear the registers
r[a] = 0
r[b] = 0
r[c] = 0

// Of course it is necessary to allow each element to be indvidual, i.e. have different values
// The following example shows how a boundary box can be constructed using shift operations.
// Shift operations move the entire array one element in the specified direction, This creates
// a gap at the opposite side. This gap is filled with a value specified as an argument to the
// shift functions

// Create vertical edges
r[a] = shift_east(r[a], 50)
r[b] = r[a]
r[a] = shift_west(r[a], 50)
r[b] = r[b] + r[a] // combine

// Create horizontal edges
r[a] = 0
r[a] = shift_north(r[a], 50)
r[c] = r[a]
r[a] = shift_south(r[a], 50)
r[c] = r[c] + r[a] // combine


// Combine both horizontal and vertical edges
r[a] = 0
r[a] = r[b] + r[c]

// As well as addition, we can also multiply, subtract and divide registers. All of these operations
// are performed in an element-wise fashion. We can also specify constants and variables to be part
// of these operations

// Invert the box, so it becomes red!
n = -1 // Creates and initialises a variable
r[a] = r[a] * n

// Note that the order of types is important here. We just performed REG = REG * VAR. This is valid,
// however REG = VAR * REG is invalid. The compiler will prompt you with warnings if any invalid
// combintions are discovered

// This is the end of the tutorial. We can stop the simulation by...
end