// Souffle - A Datalog Compiler
// Copyright (c) 2016, The Souffle Developers. All rights reserved
// Licensed under the Universal Permissive License v 1.0 as shown at:
// - https://opensource.org/licenses/UPL
// - <souffle root>/licenses/SOUFFLE-UPL.txt

// Grid
// Simple grid data structure.

// The default or NULL value for an element of the array.
.decl default(null:number)
.input default()

// The parameters to the grid, including its number of rows and columns.
.decl parameters(rows:number, columns:number)
.input parameters()

// The values of the elements in the grid, specified by row and column index. Note that any unspecified elements will take the default value.
.decl values(row:number, column:number, value:number)
.input values()

// The row and column indices of the grid, used as a background set to ground the row and column parameter of elements.
.decl indices(row:number, column:number)
.output indices()
indices(0, 0).
indices(i+1, j) :- indices(i, j), parameters(I, J), i >= 0, i+1 < I, j >= 0, j < J.
indices(i, j+1) :- indices(i, j), parameters(I, J), i >= 0, i < I, j >= 0, j+1 < J.

// The elements of the grid, referenced by a row and column index, and having a specified value. Note that if a value is not specified an element takes the default value.
.decl element(row:number, column:number, value:number)
.output element()
element(i, j, 0) :- indices(i, j), !values(i, j, _).
element(i, j, x) :- indices(i, j), values(i, j, x).

// The value of the element above the element specified by the index, or the default value if it is out of the grid bounds.
.decl up(row:number, column:number, value:number)
up(i, j, NULL) :- element(i, j, _), !element(i-1, j, _), default(NULL).
up(i, j, x) :- element(i, j, _), element(i-1, j, x).

// The value of the element below the element specified by the index, or the default value if it is out of the grid bounds.
.decl down(row:number, column:number, value:number)
down(i, j, NULL) :- element(i, j, _), !element(i+1, j, _), default(NULL).
down(i, j, x) :- element(i, j, _), element(i+1, j, x).

// The value of the element to the left of the element specified by the index, or the default value if it is out of the grid bounds.
.decl left(row:number, column:number, value:number)
left(i, j, NULL) :- element(i, j, _), !element(i, j-1, _), default(NULL).
left(i, j, x) :- element(i, j, _), element(i, j-1, x).

// The value of the element to the right of the element specified by the index, or the default value if it is out of the grid bounds.
.decl right(row:number, column:number, value:number)
right(i, j, NULL) :- element(i, j, _), !element(i, j+1, _), default(NULL).
right(i, j, x) :- element(i, j, _), element(i, j+1, x).

// The value of the element diagonally above and left of the element specified by the index, or the default value if it is out of the grid bounds.
.decl up_left(row:number, column:number, value:number)
up_left(i, j, NULL) :- element(i, j, _), !element(i-1, j-1, _), default(NULL).
up_left(i, j, x) :- element(i, j, _), element(i-1, j-1, x).

// The value of the element diagonally above and right of the element specified by the index, or the default value if it is out of the grid bounds.
.decl up_right(row:number, column:number, value:number)
up_right(i, j, NULL) :- element(i, j, _), !element(i-1, j+1, _), default(NULL).
up_right(i, j, x) :- element(i, j, _), element(i-1, j+1, x).

// The value of the element diagonally below and left of the element specified by the index, or the default value if it is out of the grid bounds.
.decl down_left(row:number, column:number, value:number)
down_left(i, j, NULL) :- element(i, j, _), !element(i+1, j-1, _), default(NULL).
down_left(i, j, x) :- element(i, j, _), element(i+1, j-1, x).

// The value of the element diagonally below and right of the element specified by the index, or the default value if it is out of the grid bounds.
.decl down_right(row:number, column:number, value:number)
down_right(i, j, NULL) :- element(i, j, _), !element(i+1, j+1, _), default(NULL).
down_right(i, j, x) :- element(i, j, _), element(i+1, j+1, x).

// The local neighbourhood of a specified element, giving the value of that element, and the elements surrounding it.
.decl neighbourhood(row:number, column:number,
  up_left:number, up:number, up_right:number,
  left:number, current:number, right:number,
  down_left:number, down:number, down_right:number)
.output neighbourhood()
neighbourhood(i, j, ul, u, ur, l, x, r, dl, d, dr) :-
 up_left(i, j, ul), up(i, j, u), up_right(i, j, ur),
 left(i, j, l), element(i, j, x), right(i, j, r),
 down_left(i, j, dl), down(i, j, d), down_right(i, j, dr).
