The Standard Library (stdlib)

Broad collection of Rel functions that perform essential and commonly used tasks.

add

add[x, y]
x + y

Addition.

Examples:

  • 1 + 3 is an expression with value 4
  • add[1, 3] is an expression with value 4
  • add(1, 3, 4) is a formula that is true.
1 + 3 = 4
add[1, 3] = 4
equal(add(1, 3, 4), true)

Definition

@inline
def add(x, y, z) = rel_primitive_add(x, y, z)


@inline
def (+)(x, y, z) = add(x, y, z)

subtract

subtract[x, y]
x - y

Subtraction.

Examples:

8 - 5 = 3
subtract[2, 3] = -1
equal(subtract(8, 5, 3), true)

Definition

@inline
def subtract(x, y, z) = rel_primitive_subtract(x, y, z)

@inline
def (-)(x, y, z) = subtract(x, y, z)

divide

divide[x, y]
x / y

Division.

Divide uses the following overloads:

  • Float64 / Float64 = Float64
  • Int64 / Int64 = Float64
  • Int64 / Float64 = Float64
  • Float64 / Int64 = Float64
  • FixedDecimal / FixedDecimal = FixedDecimal
  • Int64 / FixedDecimal = FixedDecimal
  • FixedDecimal / Int64 = FixedDecimal

Examples:

3.4 / 2 = 1.7
divide[6, 3] = 2.0
equal(divide(6, 3, 2.0), true)
equal(divide(6, 3, 2), false)

Definition

@inline
def divide[x, y] = rel_primitive_divide[x, y]

@inline
def (/)(x, y, z) = divide(x, y, z)

trunc_divide

trunc_divide[x, y]

Integer Division.

Quotient from Euclidean division. Computes x/y rounded towards zero.

Derives to false when y = 0 for Integer arguments.

Examples:

trunc_divide[5, 2] = 2
trunc_divide[-5, 2] = -2
trunc_divide[5.1, 2] = 2.0
empty(trunc_divide[5, 0])

Definition

@inline
def trunc_divide[x, y] = rel_primitive_trunc_divide[x, y]

@inline
def (÷)(x, y, z) = trunc_divide(x, y, z)

floor_divide

floor_divide[x, y]

Floor Division.

Largest integer less than or equal to x/y. Computes x/y rounded towards negative infinity.

Derives to false when y = 0 for Integer arguments.

Examples:

floor_divide[5, 2] = 2
floor_divide[-5, 2] = -3
floor_divide[5.1, 2] = 2.0

Definition

@inline
def floor_divide[x, y] = rel_primitive_floor_divide[x, y]

remainder

remainder[x, y]
x % y

Remainder.

Remainder from Euclidean division, returning a value of the same sign as x, and smaller in magnitude than y. This value is always exact.

Satisfies x = (x / y) * y + (x % y).

Derives to false when y = 0 for Integer arguments.

Examples:

8 % 3 = 2
remainder[8, 3] = 2
remainder[8, -3] = 2
remainder[-8, 3] = -2

Definition

@inline
def remainder[x, y] = rel_primitive_remainder[x, y]

@inline
def (%)(x, y, z) = remainder(x, y, z)

modulo

modulo[x, y]

Modulus after division, returning in the range [0,y), if y is positive, or (y,0] if y is negative.

Satisfies x = floor_divide[x,y] * y + modulo[x, y].

Derives to false when y = 0 for Integer arguments.

Examples:

modulo[8, 3] = 2
modulo[8, -3] = -1
modulo[-8, 3] = 1

Definition

@inline
def modulo[x, y] = rel_primitive_modulo[x, y]

multiply

multiply[x, y]
x * y

Multiplication.

Examples:

3 * 2 = 6
equal(multiply(3.0, 2, 6.0), true)

Definition

@inline
def multiply[x, y] = rel_primitive_multiply[x, y]

@inline
def (*)(x, y, z) = multiply(x, y, z)

bitwise_and

bitwise_and[x, y]

Bitwise and of two integers.

Example:

bitwise_and[3,2] = 2
bitwise_and[0x10011, 0x11100] = 0x00010000

Definition

@inline
def bitwise_and[x, y] = rel_primitive_bitwise_and[x, y]

bitwise_or

bitwise_or[x, y]

Bitwise or of two integers.

Example:

bitwise_or[3, 2] = 3
bitwise_or[0x00011, 0x11100] = 0x00011111

Definition

@inline
def bitwise_or[x, y] = rel_primitive_bitwise_or[x, y]

bitwise_xor

bitwise_xor[x, y]

Bitwise xor of two integers.

Example:

bitwise_xor[3, 2] = 1
bitwise_xor[0x00011, 0x11100] = 0x00011111

Definition

@inline
def bitwise_xor[x, y] = rel_primitive_bitwise_xor[x, y]

bitwise_left_shift

bitwise_left_shift[x, n]

Bitwise left shift of an integer x by n bits.

Examples:

bitwise_left_shift[8, 1] = 16
bitwise_left_shift[1, 10] = 1024
bitwise_left_shift[0xF, 1] = 0x1d

Definition

@inline
def bitwise_left_shift[x, n] = rel_primitive_bitwise_left_shift[x, n]

bitwise_right_shift

bitwise_right_shift[x, n]

Bitwise right shift of an integer x by n bits while preserving the sign.

Examples:

bitwise_right_shift[1024, 1] = 512
bitwise_right_shift[-1024, 1] = -512

Definition

@inline
def bitwise_right_shift[x, n] = rel_primitive_bitwise_signed_right_shift[x, n]

bitwise_unsigned_right_shift

bitwise_unsigned_right_shift[x,n]

Bitwise right shift of an integer by n bits.

Examples:

bitwise_unsigned_right_shift[8, 1] = 4
bitwise_unsigned_right_shift[-8, 1] = 9223372036854775804

Definition

@inline
def bitwise_unsigned_right_shift[x, n] = rel_primitive_bitwise_unsigned_right_shift[x, n]

bitwise_not

bitwise_not[x]

Bitwise not of an integer.

Examples:

bitwise_not[8] = -9
bitwise_not[-9] = 8
bitwise_not[0x00011] = 0xffffffee

Definition

@inline
def bitwise_not[x] = rel_primitive_bitwise_not[x]

power

power[x, y]
x ^ y

Exponentiation: $x$ to the power of $y$.

Examples:

power[2, 3] = 8
power[3.0, 2] = 9.0
power[9, 0.5] = 3.0

Definition

@inline
def power[x, y] = rel_primitive_power[x, y]

@inline
def (^)(x, y, z) = power(x, y, z)

floor

floor[x]

Round down to the nearest integer.

Examples:

floor[4.5] = 4.0
floor[-4.5] = -5.0
floor[4] = 4

Definition

@inline
def floor[x] = round[:ROUND_DOWN, x]

floor_to_int

floor_to_int[x]

General float-to-int conversion, floor. Argument must be a float.

Examples:

floor_to_int[3.1] = 3
floor_to_int[-3.1] = -4
floor_to_int[3] : error

Definition

@inline
def floor_to_int[x] = float_int_convert[floor[x]]

ceil

ceil[x]

Round up to the nearest integer.

Examples:

ceil[4.5] = 5.0
ceil[-4.5] = -4.0
ceil[4] = 4

Definition

@inline
def ceil[x] = round[:ROUND_UP, x]

trunc

trunc[x]

Round toward zero to the nearest integer. Result has the same type as the argument.

Examples:

trunc[3.9] = 3.0
trunc[-4.9] = -4.0
trunc[4] = 4

Definition

@inline
def trunc[x] = round[:ROUND_TO_ZERO, x]

trunc_to_int

trunc_to_int[x]

General float-to-int conversion, truncating. Argument must be a float.

Examples:

trunc_to_int[3.1] = 3
trunc_to_int[-3.1] = -3
trunc_to_int[3] : error

Definition

@inline
def trunc_to_int[x] = float_int_convert[trunc[x]]

round

round[mode, x]

Round to the nearest integer according to the given rounding mode.

Definition

@inline
def round(mode, x, rounded) = {
(mode = :ROUND_UP, rounded = rel_primitive_round_up[x]);
(mode = :ROUND_DOWN, rounded = rel_primitive_round_down[x]);
(mode = :ROUND_NEAREST, rounded = rel_primitive_round_nearest[x]);
(mode = :ROUND_NEAREST_TIES_AWAY, rounded = rel_primitive_round_nearest_ties_away[x]);
(mode = :ROUND_NEAREST_TIES_UP, rounded = rel_primitive_round_nearest_ties_up[x]);
(mode = :ROUND_TO_ZERO, rounded = rel_primitive_round_to_zero[x]) }

maximum

maximum[x, y]

Maximum of two arguments. The arguments should have the same type.

Examples:

maximum[3, 4] = 4
maximum[3.0, 4.0] = 4.0

Definition

@inline
def maximum[x, y] = rel_primitive_max[x, y]

minimum

minimum[x, y]

Minimum of two arguments. The arguments should have the same type.

Examples:

minimum[3, 4] = 3
minimum[3.0, 4.0] = 3.0

Definition

@inline
def minimum[x, y] = rel_primitive_min[x, y]

abs

abs[x]

The absolute value of x.

Examples:

abs[-2] = 2
abs[-2.0] = 2.0

Definition

@inline
def abs[x] = rel_primitive_abs[x]

sqrt

sqrt[x]

The non-negative square root of x.

Defined for non-negative x.

Definition

@inline
def sqrt[x] = rel_primitive_sqrt[x]

log

log[x, y]

Logarithm of y with given base x.

Example:

log[2, 8] = 3.0

Definition

@inline
def log[x, y] = rel_primitive_log[x, y]

log10

log10[x]

Base 10 logarithm.

log10[1000] = 3.0

Definition

@inline
def log10[x] = rel_primitive_log10[x]

natural_log

natural_log[x]

Natural logarithm (ln).

Definition

@inline
def natural_log[x] = rel_primitive_natural_log[x]

natural_exp

natural_exp[x]

Exponentiation with the base of the natural log, e.

Definition

@inline
def natural_exp[x] = rel_primitive_natural_exp[x]

erf

erf[x]

Returns the error function of x.

External Links: https://en.wikipedia.org/wiki/Error_function

Examples:

erf[2] = 0.9953222650189527
erf[-0.5] = -0.5204998778130465

Definition

@inline
def erf[x] = rel_primitive_error_function[x]

erfinv

erfinv[x]

Returns the inverse error function of x.

External Links: https://en.wikipedia.org/wiki/Error_function#Inverse_functions

Examples:

erfinv[0.1] = 0.08885599049425769
erfinv[-0.5] = -0.4769362762044699
erfinv[erf[1]] = 1.0

Definition

@inline
def erfinv[x] = rel_primitive_error_function_inverse[x]

cbrt

cbrt[x]

The cube root of x.

Example:

cbrt[27] = 3.0

Definition

@inline
def cbrt[x] = rel_primitive_cbrt[x]

factorial

factorial[x]

Factorial of x

Definition

@inline
def factorial[x] = rel_primitive_factorial[x]

sign

sign[x]

The sign of x: either 0, -1, or 1.

Definition

@inline
def sign[x] = rel_primitive_sign[x]

pi_float64

pi_float64

The constant pi.

Example:

cos[pi_float64] = -1.0

Definition

@inline
def pi_float64 = 3.14159265358979323846

sin

sin[x]

Sine of x (given in radians). Example:

sin[pi_float64/2] = 1.0

Definition

@inline
def sin[x] = rel_primitive_sin[x]

asin

asin[x]

Inverse sine (in radians). Gives an error if ${\rm abs}[x] > 1$.

Example:

asin[1] = 1.5707963267948966

Definition

@inline
def asin[x] = rel_primitive_asin[x]

cos

cos[x]

Cosine of x (given in radians).

Example:

cos[pi_float64] = -1.0

Definition

@inline
def cos[x] = rel_primitive_cos[x]

acos

acos[x]

Inverse cosine (in radians). Gives an error if ${\rm abs}[x] > 1$.

Example:

acos[0] = 1.5707963267948966

Definition

@inline
def acos[x] = rel_primitive_acos[x]

tan

tan[x]

Tangent of x (given in radians).

Example:

tan[pi_float64/4] = 0.9999999999999999

Definition

@inline
def tan[x] = rel_primitive_tan[x]

atan

atan[x]

Inverse tangent (in radians).

Definition

@inline
def atan[x] = rel_primitive_atan[x]

atan2

atan2[y, x]

Inverse tangent returning an angle (in radians) between the positive x-axis and the ray to the point (x, y).

Definition

@inline
def atan2[x,y] = rel_primitive_atan2[x,y]

cot

cot[x]

Cotangent of x (given in radians).

Definition

@inline
def cot[x] = rel_primitive_cot[x]

acot

acot[x]

Inverse cotangent (in radians).

Definition

@inline
def acot[x] = rel_primitive_acot[x]

sinh

sinh[x]

Hyperbolic sine.

Definition

@inline
def sinh[x] = rel_primitive_sinh[x]

cosh

cosh[x]

Hyperbolic cosine.

Definition

@inline
def cosh[x] = rel_primitive_cosh[x]

tanh

tanh[x]

Hyperbolic tangent.

Definition

@inline
def tanh[x] = rel_primitive_tanh[x]

asinh

asinh[x]

Inverse hyperbolic sine.

Definition

@inline
def asinh[x] = rel_primitive_asinh[x]

acosh

acosh[x]

Inverse hyperbolic cosine.

Definition

@inline
def acosh[x] = rel_primitive_acosh[x]

atanh

atanh[x]

Inverse hyperbolic tangent.

Definition

@inline
def atanh[x] = rel_primitive_atanh[x]

deg2rad

deg2rad[x]

Convert degrees to radians.

Definition

@inline
def deg2rad[x] = x * pi_float64 / 180.0

rad2deg

rad2deg[x]

Convert radians to degrees.

Definition

@inline
def rad2deg[x] = x / pi_float64 * 180.0

haversine

haversine[r, x1, y1, x2, y2]

The great circle distance of two points, i.e $(x1,y1)$ and $(x2,y2)$, on a sphere of radius $r$, using the Haversine formula. The two points are specified by their latitude and longitude in radians.

Example:

def output = haversine[10, 0, 0, 0, pi_float64/2]
15.707963267948964

Definition

@inline
def haversine[r, x1, y1, x2, y2] =
2 * r * asin[sqrt[sin[(x2 - x1)/2] ^ 2 + cos[x1] * cos[x2] * sin[(y2 - y1) / 2] ^ 2]]

range

range(low, hi, stride, x)

Generate a relation with integer values between low and high, inclusive, advancing by stride each time.

  • If low <= hi and stride > 0, then range[low, hi, stride] returns all values x = low + i * stride <= hi for non-negative integers i.
  • Otherwise (i.e. if low > hi or stride <= 0), then range[low, hi, stride] is empty.

Example:

def output = range[1, 10, 4]
1
5
9

Definition

@inline
def range = rel_primitive_range

eq

eq(x, y)
x = y

Equality between scalar (single) values, such as integers, strings, symbols. (For equality between relations, use equal.)

Examples:

  • 1 = 1 is true.
  • :a = :a is true
  • "a" = "a" is true
  • 2 = 2.0 is false
  • 1 = "a" is false

Definition

@inline
def eq(x, y) = rel_primitive_eq(x, y)

@inline
def (=)(x, y) = eq(x, y)

neq

neq(x, y)
x ≠ y
x != y

Examples:

  • 1 != 2 is true
  • 1 != 1.0 is true
  • 1 != "a" is true

Definition

@inline
def neq(x, y) = rel_primitive_neq(x, y)

@inline
def (≠)(x, y) = neq(x, y)

@inline
def (!=)(x, y) = neq(x, y)

lt

lt(x, y) x < y

Definition

@inline
def lt(x, y) = rel_primitive_lt(x, y)

@inline
def (<)(x, y) = lt(x, y)

gt

gt(x, y) x > y

Definition

@inline
def gt(x, y) = rel_primitive_gt(x, y)

@inline
def (>)(x, y) = gt(x, y)

lt_eq

lt_eq(x, y) x ≤ y x <= y

Definition

@inline
def lt_eq(x, y) = rel_primitive_lt_eq(x, y)

@inline
def (≤)(x, y) = lt_eq(x, y)

@inline
def (<=)(x, y) = lt_eq(x, y)

gt_eq

gt_eq(x, y) x ≥ y x >= y

Definition

@inline
def gt_eq(x, y) = rel_primitive_gt_eq(x, y)

@inline
def (≥)(x, y) = gt_eq(x, y)

@inline
def (>=)(x, y) = gt_eq(x, y)

equal

equal(R, S)

Relational equality. Note that eq and = should be used only for scalar values, while equal should be used to check that two relations are the same.

Definition

@inline
def equal(R, S) = forall(x... where R(x...): S(x...)) and forall(x... where S(x...): R(x...))

count

count[R]

Number of tuples in R, if R is not empty. If R is empty, count[R] is false.

Examples:

  • count[employee] returns the number of tuples in relation employee
  • d in department: count[d.member] returns the member count for every department with at least 1 member
  • count[5] returns 1 because 5 is a singleton relation
  • count[{}] is false.

Definition

@inline
def count[R] = sum[{R,1}]

sum

sum[R]
Σ[R]

Sum of (the last argument of) the relation R, if non-empty. If R is empty, sum[R] is false.

Example:

def salary = {("John", 10) ; ("Mary", 20); ("Paul", 17) ; ("Peter", 15) }
def output = sum[salary]
62

def member = {("A", "John"); ("B", "Mary") ; ("A", "Paul") ; ("C" , "Peter") }
def department = {"A"; "B"; "C" }
def output = d in department: sum[salary[p] for p in member[d]]
("A", 27)
("B", 20)
("C", 15)

Definition

@inline
def sum[R] = rel_primitive_reduce_noinit[add, R]

Σ

sum[R]
Σ[R]

Sum of (the last argument of) the relation R, if non-empty. false if R is empty.

Example:

d in department: Σ[salary[p] for p in member[d]]

Definition

@inline
def Σ = sum

sum_int

sum_int[R]

Integer sum of (the last argument of) the relation R, which should be ints. sum_int[R] = 0 for empty R.

Definition

@inline
def sum_int[R] = rel_primitive_reduce[add, 0, R]

product

product[R]

Product of (the last argument of) the relation R, if non-empty. If R is empty, product[R] is false.

Example:

product[{(1, 4); (2, 5)}] = 20

Definition

@inline
def product[R] = rel_primitive_reduce_noinit[multiply, R]

max

max[R]

Maximum of (the last argument of) the relation R, if non-empty. If R is empty, max[R] is false.

Example:

max[{(2, 3); (1, 6)}] = 6

Definition

@inline
def max[R] = rel_primitive_reduce_noinit[maximum, R]

Max

Max[R]

Please use max[R]. Deprecates in near future

Definition

@inline
def Max = max

min

min[R]

Minimum of (the last argument of) the relation R, if non-empty. If R is empty, min[R] is false.

Example:

min[{(2, 3); (1, 6)}] = 3

Definition

@inline
def min[R] = rel_primitive_reduce_noinit[minimum, R]

Min

Min[R]

Please use min[R]. Deprecates in near future

Definition

@inline
def Min = min

clamp

clamp[lo, hi, v]

clamp limits the value v to a given range(lo, hi), changing values outside the range to the closest lo, hi value if necessary. The arguments should have the same type.

Examples:

clamp[2, 5, 1] = 2
clamp[2, 5, 6] = 5
clamp[2, 5, 3] = 3

Definition

@inline
def clamp[lo, hi, v] = min[{max[{lo; v}]; hi}]

argmax

argmax[R]

Find the keys for the largest value of the relation R.

Examples:

def output = argmax[{(2, 3); (1, 6)}]
1

def output = argmax[{(2, 6); (1, 6); (5, 0)}]
1
2

// find the teams with the largest aggregated salary:
argmax[d in team: sum[salary[p] for p in member[d]]]

Definition

@inline
def argmax[R] = R.(max[R])

ArgMax

ArgMax[R]

Please use argmax[R]. Deprecates in near future

Definition

@inline
def ArgMax = argmax

argmin

argmin[R]

Find the keys for the smallest value of the relation R.

Examples:

def output = argmin[{(2, 3); (1, 6)}]
2

def output = argmin[{(2, 6); (1, 6); (5, 10)}]
1
2

// find the teams with the smallest aggregated salary:
argmin[d in team: sum[salary[p] for p in member[d]]]

Definition

@inline
def argmin[R] = R.(min[R])

ArgMin

ArgMin[R]

Please use argmin[R]. Deprecates in near future

Definition

@inline
def ArgMin = argmin

min_k

min_k[K, R]

Returns the minimum K distinct values of (the last column of) the relation R, if non-empty. If R contains less than K distinct values (in its last column), then less than K values will be returned. If R is empty, min_k[K, R] is false. Currently, the maximum supported value of K is 10000.

min_k[2, {(2, 3); (1, 6); (3, 5); (1, 1); (4, 1); (2, 6)}]

returns {1; 3}.

Definition

@inline
def min_k[K, R] = rel_primitive_mink[R, K, 0]

max_k

max_k[K, R]

Returns the maximum K distinct values of (the last column of) the relation R, if non-empty. If R contains less than K distinct values (in its last column), then less than K values will be returned. If R is empty, max_k[K, R] is false. Currently, the maximum supported value of K is 10000.

max_k[2, {(2, 3); (1, 6); (3, 5); (1, 1); (4, 1); (2, 6)}]

returns {5; 6}.

Definition

@inline
def max_k[K, R] = rel_primitive_mink[R, K, 1]

mean

mean[R]
average[R]

The arithmetic mean of (the last argument of) the relation R, if non-empty. False if R is empty.

Definition

@inline
def mean[R] = sum[R] / count[R]

average

mean[R]
average[R]

The arithmetic mean of (the last argument of) the relation R, if non-empty. False if R is empty.

Definition

@inline
def average = mean

geometric_mean

geometric_mean[R]

The geometric mean of the values of a relation.

Example:

def output = geometric_mean[{('a',1); ('b',2); ('a',3); ('b',4); ('c',5); ('d',6}]
2.99379516552

Definition

@inline
def geometric_mean[R] = power[product[R], 1 / count[R]]

harmonic_mean

harmonic_mean[R]

The harmonic mean of the values of a relation.

Example:

def output = harmonic_mean[{('a',1); ('b',2); ('a',3); ('b',4); ('c',5); ('d',6}]
2.44897959184

Definition

@inline
def harmonic_mean[R] = count[R] / sum[a..., v, i: R(a..., v) and i = 1 / v]

weighted_mean

weighted_mean[R, W]

The weighted mean of values in relation R with weights defined by W. The last attribute of the relation representing the weights is used to compute the weighted mean. When the arity of the relation that represents the weights is different from the arity of the input relation the user is expected to align the arities before passing them to weighted_mean.(See examples 2 and 3)

Example 1 (The arity of both R and W are the same):

def inputs = {(1,'a',10); (2,'b',10); (3,'c',30); (4,'d',40); (5,'e',50)}

def weights = {(1,'a',11); (2,'b',12); (3,'c',13); (4,'d',14); (5,'e',15)}

def output = weighted_mean[inputs, weights]

29.692307692307693

Example 2 (The arity of R and W are different):

def inputs = {(1,'a',10); (2,'b',10); (3,'c',30); (4,'d',40); (5,'e',50)}

def weights = {(1,11); (2,12); (3,13); (4,14); (5,15)}

def output = weighted_mean[inputs, (n: _, weights[n])]

29.692307692307693

Example 3 (The arity of R and W are different):

def inputs = {(1,'a',100,10); (2,'a',200,10); (3,'b',300,30); (4,'c',400,40); (5,'b',500,50)}

def weights = {(1,'a',11); (2,'a',12); (3,'b',13); (4,'c',14); (5,'b',15)}

def output = weighted_mean[input, (n, c: _, weights[n,c])]

29.692307692307693

Definition

@inline
def weighted_mean[R,W] = sum[k...: R[k...] * W[k...]] / sum[domain[R] <: W]

frequency

frequency(R, elem, c)
frequency[R, elem]
frequency[R]

Finds the frequency c of elem in (the last argument of) the relation R.

Example:

def example = {(1, "a"); (2, "b"); (3, 123); (4, 12.5); (3, "b")}
def output = frequency[example, "b"]
2

def output = frequency[example]
("a", 1)
("b", 2)
(123, 1)
(12.5, 1)

Definition

@inline
def frequency[R, elem] = count[x...: R(x..., elem)]

mode

mode[R]

Finds the most common value in (the last argument of) the relation R. If there are multiple most common values, then mode chooses the first one according to the sort order.

Examples:

def example = {(1, "a"); (2, "b"); (3, 123); (4, 12.5); (3, "b")}
mode[example] = "b"

def example = {(1, 1); (2, 1); (3, 1); (4, "b"); (5, "b"); (6, "c"); (7, "c")}
mode[example] = 1

Definition

@inline
def mode[R] = last[top[1, argmax[frequency[R]]]]

squared

squared[R]

Square of a relation: the value of each last element is squared.

Examples:

def example = {(1, 2); (3, 4)}
equal(square[example], {(1, 4); (3, 16)}

Definition

@inline
def squared[F][x...] = F[x...] ^ 2

squared_deviation

squared_deviation[R]

Squared deviation: squared deviation from the mean of (the last argument of) the relation R.

Definition

@inline
def squared_deviation[F][x...] = (F[x...] - mean[F]) ^ 2

pop_variance

pop_variance[R]

Population variance

Definition

@inline
def pop_variance[F] = mean[squared_deviation[F]], count[F] > 1

sample_variance

sample_variance[R]

Sample variance

Definition

@inline
def sample_variance[F] = sum[squared_deviation[F]] / (count[F] - 1), count[F] > 1

pop_standard_deviation

pop_standard_deviation[R]
pop_stddev[R]

Population standard deviation

Definition

@inline
def pop_standard_deviation[F] = sqrt[pop_variance[F]]

@inline
def pop_stddev = pop_standard_deviation

sample_standard_deviation

sample_standard_deviation[R]
sample_stddev[R]

Sample standard deviation of (the last argument of) the relation R.

Definition

@inline
def sample_standard_deviation[F] = sqrt[sample_variance[F]]

sample_stddev

see: sample_standard_deviation

Definition

@inline
def sample_stddev = sample_standard_deviation

min_max_normalization

min_max_normalization[R]

Min-Max normalization: $x_n = (x_n - {\rm min}[R]) / {\rm max}[R] - {\rm min}[R]$.

Also known as min-max scaling or rescaling.

Definition

@inline
def min_max_normalization[R][x...] =
(R[x...] - min[R]) / (max[R] - min[R]), (max[R] > min[R])

mean_normalization

mean_normalization[R]

Mean normalization: $x_n = (x_n - {\rm mean}[R]) / {\rm max}[R] - {\rm min}[R]$

Definition

@inline
def mean_normalization[R][x...] =
(R[x...] - mean[R]) / (max[R] - min[R]), (max[R] > min[R])

pop_zscore_normalization

pop_zscore_normalization[R]

Z-score normalization (population) of (the last argument of) the relation R.

Definition

@inline
def pop_zscore_normalization[R][x...] =
(R[x...] - mean[R]) / pop_stddev[R]

sample_zscore_normalization

sample_zscore_normalization[R]

Z-score normalization (sample) of (the last argument of) the relation R.

Often simply referred to as ‘standardization’.

Definition

@inline
def sample_zscore_normalization[R][x...] = (R[x...] - mean[R]) / sample_stddev[R]

unit_normalization

unit_normalization[R]

Unit vector normalization: $x_n = x_n / ( Σ (x^2) ) ^ {0.5}$

Definition

@inline
def unit_normalization[R][x...] = R[x...] / sqrt[sum[squared[R]]]

mse

mse[YHAT, Y]

Mean squared error (MSE, L2)

Definition

@inline
def mse[YHAT, Y] = Σ[x... : (Y[x...] - YHAT[x...]) ^ 2] / count[Y]

rmse

rmse[YHAT, Y]

Root mean square error (RMSE)

Definition

@inline
def rmse[YHAT,Y] = sqrt[mse[YHAT,Y]]

mae

mae[YHAT, Y]

Mean absolute error (MAE, L1)

Definition

@inline
def mae[YHAT,Y] = mean[x... : abs[Y[x...] - YHAT[x...]]]

sort

sort[R]

Sort a relation.

sort takes a relation R(xs...) and produces a relation R(i, xs...) where i is an Int64 index starting at 1. The index indicates the ordering of the tuples of R.

The ordering is the native sort order of values in the system and cannot be customized with a comparator. It is possible though to transform the input or output of sort to achieve a different sort order (see reverse_sort for an example).

Example:

def sample = 'a'
def output = sort[sample]
(1, 'a')

def sample = {'a'; 'b'; 'c'}
def output = sort[sample]
(1, 'a')
(2, 'b')
(3, 'c')

Similar to aggregations, it is possible to separately sort per group. In the following example, the sort is grouped by free variable g.

def sample = {(:g, 'a'); (:g, 'b'); (:g, 'c'); (:h, 'd'); (:h, 'e'); (:h, 'f')}
def output = x: sort[sample[x]]
(:g, 1, 'a')
(:g, 2, 'b')
(:g, 3, 'c')
(:h, 1, 'd')
(:h, 2, 'e')
(:h, 3, 'f')

Definition

@inline
def sort = rel_primitive_sort

reverse_sort

reverse_sort[R]

Reverse Sort

Like sort, except the ordering is reversed.

Example:

def sample = {'a'; 'b'; 'c'}
def output = reverse_sort[sample]
(1, 'c')
(2, 'b')
(3, 'a')

def sample = {(:g, 'a'); (:g, 'b'); (:g, 'c'); (:h, 'd'); (:h, 'e'); (:h, 'f')}
def output = x: reverse_sort[sample[x]]
(:g, 1, 'c')
(:g, 2, 'b')
(:g, 3, 'a')
(:h, 1, 'f')
(:h, 2, 'e')
(:h, 3, 'd')

Definition

@inline
def reverse_sort[R](ri, args...) =
sort[R](i, args...) and
ri = count[R] - i + 1
from i

docstring

docstring[:R]

Return the docstring of a relation as a string.

Example:

doc"Returns 0." def zero = 0
def output = docstring[:zero]
"Returns 0."

Definition

@inline
def docstring = rel_primitive_docstring

auto_number

auto_number[R]

AutoNumber a relation.

auto_number takes a relation R(xs...) and produces a relation R(xs..., i) where i is a distinct AutoNumberValue index.

Note that auto_number can give different results each time it is called.

Example:

def sample = 'a'
def output = auto_number[sample]
('a', AutoNumberValue(0x12))

def sample = {'a'; 'b'; 'c'}
def output = auto_number[sample]
('a', AutoNumberValue(0x132))
('b', AutoNumberValue(0x133))
('c', AutoNumberValue(0x134))

Definition

@inline
def auto_number = rel_primitive_auto_number

hash128

hash128[R]

Hash128 each of the tuples in a relation.

hash128 takes a relation R(xs...) and produces a relation R(xs..., i) where i is a HashValue index. The index indicates the hash128 of each tuple of R.

Example:

def output = hash128['a']
('a', HashValue(0xbd1defa3be4617e0))

def sample = {'a'; 'b'; 'c'}
def output = hash128[sample]
('a', HashValue(0xbd1defa3be4617e0))
('b', HashValue(0x2423a34d925fc9ed))
('c', HashValue(0xd2cffd2ea6c73a7))

Definition

@inline
def hash128 = rel_primitive_hash

hash

hash[R]

see hash128

Definition

@inline
def hash = hash128

uint128_hash_value_convert

uint128_hash_value_convert[v]

Convert a provided UInt128 value v to the HashValue type. This is an internal-facing function, used for the implementation of hashing.

Definition

@inline
def uint128_hash_value_convert[v] = rel_primitive_uint128_hash_value_convert[v]

murmurhash3f_with_seed

murmurhash3f_with_seed[seed in (Int64 ∪ UInt128), key]

Hash key with seed via the MurmurHash3F algorithm, and yield the result as a UInt128.

The key may be any singleton value supported in Rel, (e.g. [U]Int{8,16,32,64,128}, Float{16,32,64}, Char, String, RelName, FilePos, Missing, …).

The seed must be a value in (Int64 ∪ UInt128).

Note that the standard specification for MurmurHash3F does not include 128-bit seeds, so the result of hashing with a 128-bit seed will be different than a hash with the same value stored in a smaller integer type (i.e. hashing with the seeds uint64[1] and uint128[1] will result in different hashes).

This function is an implementation (in Julia) of the reference implementation of MurmurHash3F, i.e. MurmurHash3_x64_128. The original can be retrieved via:

https://github.com/aappleby/smhasher/blob/ 61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash3.cpp

The reference implementation was written by Austin Appleby and made public domain.

Note that this implementation departs from the standard, in order to provide different hashes for byte-identical values of different types. While this implementation’s internals should produce byte-identical results to the reference implementation, the primary entrypoint here, (murmurhash3f[seed, key]) typically will not: To distinguish byte-identical values of different types, this function mixes type information into seeds prior to hashing.

Also note that the reference implementation supports only UInt32 seeds; for such seeds, this implementation’s internals should provide byte-identical results. But this implementation accepts seeds up to and including 128-bit integer types. (For UInt32 seeds, the reference implementation initializes h1/h2, the two UInt64s in which it accumulates/mixes the digest, with copies of seed. This implementation does the same for UInt32 seeds, and also for all other seed types <= 64 bits. For 128-bit seeds, this implementation initializes h1 with the seed’s low bits and h2 with the seed’s high bits.)

Examples

// Without a seed
murmurhash3f["cat"] = 0x670c9caac40b25a8f2bd407bd1ddbec7
// With a seed
murmurhash3f[8675309, "cat"] = 0xdbde75f53ecda9e37734271e899eaf87
murmurhash3f[8675309, :bunny] = 0x32822e3f70a1d87e5f86fea6c763b67f

// byte-identival values of different types hash differently
murmurhash3f[0x01] = 0x6ed3439777f613f7df39df0849d45e09
murmurhash3f[1] = 0x9116cd3f0a651c49f1674100935b29bf

Definition

@inline
def murmurhash3f_with_seed[seed, v] = rel_primitive_murmurhash3f_with_seed[seed, v]

murmurhash3f

murmurhash3f[v]

Hash v according to the MurmurHash3f algorithm with the default seed 0, returning the result as a UInt128.

Equivalent to murmurhash3f_with_seed[0, v].

Definition

@inline
def murmurhash3f[v] = murmurhash3f_with_seed[int64[0], v]

top

top[k, R]

Select the top k facts of relation R according to the sort order of R.

top is sort restricted to first k tuples. top takes a relation R(xs...) and produces a relation R(i, xs...) where i is an Int64 index starting at 1. The index indicates the ordering of the tuples of R.

If k is bigger than number of tuples in the relation, then only the sorted relation itself is returned. This means that the cardinality of the result is the minimum of k and the cardinality of R.

Example:

def output = top[2, {'a'; 'b'; 'c'; 'd'}]
(1, 'a')
(2, 'b')

Note: Currently top[k, R] only supports groupbys in the R-expression but not in the k-expression. For example, the following is supported:

def output[g] = top[K, R[g]]  // `g` here is a groupby for `R`

However the following is NOT supported:

def output[g] = top[K[g], R]  // `g` here is a groupby for `K`

Definition

@inline
def top[K, R] = rel_primitive_top[R, K]

bottom

bottom[k, R]

Select the bottom k facts of relation R according to the sort order of R.

bottom is reverse_sort (which sorts highest to lowest) restricted to the first k facts.

Example:

def output =  bottom[2, {'a'; 'b'; 'c'; 'd'}]
(1, 'd')
(2, 'c')

Definition

@inline
def bottom[k, R] =
reverse_sort[R][i] for i where i <= k

percentile_nearest

percentile_nearest[R, p]

Select the element of R corresponding to the p-th percentile.

The p-th percentile is the smallest value in the list such that no more than p percent of the data is strictly less than the value and at least p percent of the data is less than or equal to that value (nearest-rank method in https://en.wikipedia.org/wiki/Percentile). This function computes the p-th percentile based on ranking the last argument of R.

Note that percentile_nearest is not defined for p <= 0 or p > 100.

Example, here 33% of the values are strictly less than 16, and 66% of the values are less than or equal to 16, so the 60-th percentile is 16:

def output = percentile_nearest[{(1, 12); (2, 24); (3, 16)}, 60]
16

Definition

@inline
def percentile_nearest[R, p] =
// we need to sort[y, x...] instead of sort[last[R]] to handle duplicates properly
first[(sort[y, x...: R(x..., y)][i] from i
where i = float_int_convert[ceil[(p / 100.0) * count[R]]])]

percentile

percentile[R, p]

Select the element of R corresponding to the p-th percentile, computed with linear interpolation.

If the p-th percentile does not correspond to a single element, then percentile_continuous computes a weighted average of the two values surrounding the p-th percentile.

There are a number of different approaches for computing linearly interpolated percentiles. Here, the rank of the percentile is computed by x = p(N - 1) + 1, which corresponds to the C = 1 variant in https://en.wikipedia.org/wiki/Percentile. This is also the approach used in Julia’s libraries and NumPy.

Note that percentile is not defined for p <= 0 or p > 100.

Example:

def output = percentile[{(1, 12); (2, 24); (3, 16)}, 60]
17.6

Definition

@inline
def percentile[R, p] = value :
// index is a float value, we multiply by count[R] - 1 first rather than divide by
// 100.0 first to avoid imprecision
index = (p * (count[R] - 1.0)) / 100.0 + 1.0 and

// compute the lower and upper bounds surrounding the value for the p-th percentile
value = sum[i, s:
// the value at index i contributes (1 - abs[i - index]) proportion of the final
// percentile
s = v * (1 - abs[i - index]) and

// sort[y, x...] instead of sort[last[R]] to handle duplicate values
sort[y, x...: R(x..., y)](i, v, rest...) and

// only for the values surrounding index
index - 1.0 <= 1.0 * i <= index + 1.0
from v, rest...
]
from index

median

median[R]

Returns the median of R according to the sort order of the last element. If the median index is between two values, then median takes the mean of the middle two values.

Example:

def output = median[{(1, 12); (2, 16); (3, 24)}]
16

Definition

@inline
def median[R] = percentile[R, 50]

describe

describe[R]

Gives a summary of statistics for a dataset R. R should be a relation given in the form R(:feature_name, keys..., value). For example, data loaded using load_csv.

For each feature in the dataset, describe computes a set of statistics depending on the data type of that feature.

For numerical data, describe computes the count, mean, standard deviation, minimum, 25th, 50th, 75th percentiles, and maximum for that feature.

For non-numerical data, describe computes the count, unique count, mode, and mode frequency. It will also compute maximum and minimum values if the data is sortable.

If a dataset has multiple data types, it will print the numerical stats for the numerical subset, and non-numerical stats for the non-numerical subset of the data.

For example, given a dataset R in the form (produced by the CSV loader):

R = {
(:date, FilePos(29), 2020-01-01);
(:date, FilePos(60), 2020-02-02);
(:date, FilePos(91), 2020-03-03);
(:date, FilePos(127), 2020-04-04);
(:price, FilePos(29), 12.5);
(:price, FilePos(60), 14.25);
(:price, FilePos(91), 11.0);
(:price, FilePos(127), 12.25);
(:quantity, FilePos(29), 2);
(:quantity, FilePos(60), 4);
(:quantity, FilePos(91), 4);
(:quantity, FilePos(127), 3);
(:cocktail, FilePos(29), "martini");
(:cocktail, FilePos(60), "sazerac");
(:cocktail, FilePos(91), "cosmopolitan");
(:cocktail, FilePos(127), "bellini");
}

describe will compute a summary:

Example:

def d = describe[R]

d[:date]
(:mode, Dates.Date("2020-01-01"))
(:count, 4)
(:mode_freq, 1)
(:min, Dates.Date("2020-01-01"))
(:max, Dates.Date("2020-04-04"))
(:unique, 4)

d[:quantity]
(:max, 4)
(:count, 4)
(:std, 0.9574271077563381)
(:min, 2)
(:percentile75, 4.0)
(:percentile25, 2.75)
(:percentile50, 3.5)
(:mean, 3.25)

A describe_full method is also provided, which does not aggregate over columns, and instead computes the statistics for the whole dataset. However, describe_full may cause errors if any unsortable data (e.g., symbols) exists in the relation.

For example, given the relation R defined above:

Example:

describe_full[R]
(:min, 32)
(:unique, 3)
(:mode, "a")
(:std, 49.47186262580647)
(:percentile25, 89.2175)
(:min, 3.27)
(:percentile50, 53.6)
(:mean, 16.75)
(:mean, 18.8675)
(:count, 7)
(:max, 72.2)
(:max, 35)
(:mode_freq, 1)

Definition

@inline
def describe[R] = column: describe_full[R[column]]


// Stats for all types
@inline def describe_full[R, :count] = count[R]
@inline def describe_full[R, :min] = min[R]
@inline def describe_full[R, :max] = max[R]

// Stats for non-numeric types
@inline def describe_full[R, :unique] = count[last[R :> (x: not Number(x))]]
@inline def describe_full[R, :mode] = mode[R :> (x: not Number(x))]
@inline def describe_full[R, :mode_freq] = max[frequency[R :> (x: not Number(x))]]

// Stats for numeric types
@inline def describe_full[R, :mean] = mean[R :> Number]
@inline def describe_full[R, :std] = sample_stddev[R :> Number]
@inline def describe_full[R, :percentile25] = percentile[(R :> Number), 25]
@inline def describe_full[R, :percentile50] = median[R :> Number]
@inline def describe_full[R, :percentile75] = percentile[(R :> Number), 75]

load_csv

load_csv[URI_string]
load_csv[R]

This is the main in-language API for loading CSV data. To load a CSV, either provide the string URI where the data resides, or a configuration relation R that describes the data load.

In the first case, all columns are imported with type String.

The relation R, which must be an EDB or an IDB but not a formula, can configure the data load with the following options:

Required: Either

  • :path: The path to the CSV file, e.g. "azure://storage.blob.core.net/container/file.csv" Or
  • :data: A CSV formatted string value

Optional:

  • :schema: A relation containing schema information.
  • :syntax: A relation containing syntax configuration.
  • :integration: A relation containing storage integration configuration.

The resulting relation is of the form (column_name, file_position, value). The file_position uniquely identifies each row in the CSV.

Examples:

Loading from literal string value:

def config[:data] = "A,B,C\n1,2,3"
def csv = load_csv[config]

Loading a CSV file using a constant path:

def y = load_csv["test.csv"]
def x = y[:A, _]

In the example above, all values for column :A (symbolized from “A” in the original file) are stored in x.

This can also be used to only load specific columns: load_csv[path](:A, xs...).

Loading a CSV file using a configuration relation R:

def config[:path] = "/path/to/file.csv"
def config[:syntax, :delim] = '_'
def config[:syntax, :header] = (1, :A); (2, :B); (3, :C)
def config[:schema, :A] = "int"
def config[:schema, :B] = "string"
def config[:schema, :C] = "decimal(64,2)"

def csv = load_csv[config]

Lastly, load_csv also supports error handling. If the CSV can’t be read (e.g. if it is badly formatted), parsing errors are stored in the :load_errors “column”, which can be accessed the same way as any other column. It has the format (:load_errors, file_position, error_column, raw_line). Note : Since the arities of regular columns and the error relations differ, use the splat operator ("…") to make your program as resilient as possible (cf. examples above).

Definition

@inline
def load_csv = rel_primitive_load_csv

load_csv_row_wise

load_csv_row_wise[R]

Load a CSV file into a “row-wise” relation, with the position as the first argument, and the column name second. Included for backwards-compatiblity, we now recommend using load_csv instead, which puts the column name first.

Definition

@inline
def load_csv_row_wise[R][pos, col] = rel_primitive_load_csv[R][col, pos]

lined_csv

lined_csv[R]

Given an already loaded CSV relation, returns a copy of the relation, but with the file position field replaced with a line number. Only lines containing data, including errors, are included in the numbering. Headers and empty lines are not counted. Thus, line number 1 is the first non-empty, non-header line of the CSV relation.

Example:

def csv = load_csv[config]
def csv_with_lines = lined_csv[csv]

Definition

@inline
def lined_csv[R](col, line, rest...) = sort[second[R]](line, pos) and R(col, pos, rest...) from pos

export_csv

export_csv[R]

This is the main entry point to specify relations that should be exported to CSV. The payload relation R is expected to be a configuration relation mapping keys to relations.

Required keywords are:

  • :path: A string specifying the location of the file that is to be created.

Optional keywords:

  • :data: A set of relations mapping a file position, which can be a key of arbitrary length or type, to the corresponding value. This is the data that will be exported. We expect the data relation to be of form: data(pos…, :COLUMN_NAME, val)
  • :integration: A relation containing storage integration configuration.
  • :schema: A relation containing schema information.
  • :syntax: A relation containing syntax configuration.

Example using default CSV schema and syntax and no storage integration.

def csv_data(:ORDER, pos, v) = ((1,1); (2,2); (3,3))(pos, v)
def csv_data(:LINEITEM, pos, v) = ((1,100); (2,101); (3,102))(pos, v)
def csv_data(:QUANTITY, pos, v) = ((1,2); (2,15); (3,42))(pos, v)

def export = export_csv[(:path, "/path/to/local/file.csv"); (:data, csv_data)]

Example using a custom CSV schema.

def csv_syntax[:delim] = ';'
def csv_syntax[:quotechar] = '_'

def export = export_csv[(:path, "/path/to/local/file.csv");
(:data, csv_data);
(:syntax, csv_syntax)]

Example using an Azure storage integration using a SAS token:

def integration[:provider] = "azure"
def integration[:credentials, :azure_sas_token] = "<azure_sas_token>"

def export = export_csv[(:path, "azure://<account_name>.blob.core.windows.net/container/file.csv");
(:data, csv_data);
(:integration, integration)]

Example with compound keys:

def csv_data(:ORDER, pos..., v) = (1, 2, 100; 2, 1, 40)(pos..., v)
def csv_data(:QUANTITY, pos..., v) = (1, 3, 10; 2, 3, 11; 3, 1, 12; 3, 2 ,13)(pos..., v)

def export = export_csv[(:data, csv_data; :path, "/path/to/data.csv")]

Definition

@inline
def export_csv[R](ys..., xs...) = (:envelope, (:payload, (
(:data, ((key..., col, val): R[:data](col, key..., val))) ; (x, y...: R(x, y...), (x != :data))
)(xs...); (:content_type, "text/csv")(xs...)))(ys...)

@inline
def export_csv_row_wise[R](ys..., xs...) = (:envelope, (:payload, R(xs...); (:content_type, "text/csv")(xs...)))(ys...)

load_json

    load_json[filename_or_url]
    load_json[R]

The relation R, which currently needs to either be an EDB or an IDB but no formula, can be used to configure the data load with the following options:

Required: Either

  • :path: The path to the JSON file, e.g. "azure://storage.blob.core.net/container/file.json" Or
  • :data: A JSON formatted string value

Optional:

  • :integration: A relation containing storage integration configuration.

Examples:

Loading literal JSON values:

def data = "{\\"name\\": \\"Anton\\", \\"age\\": 56}"
def config[:data] = data
def json = load_json[config]

Loading a JSON file using a constant path:

def y = load_json["test.json"]

Loading a JSON file using a configuration relation:

def config[:path] = "/path/to/file.json"
def json = load_json[config]

Loading a JSON file from behind a private Azure container using a SAS token:

def config[:path] = "azure://<account_name>.blob.core.windows.net/container/file.json"
def config[:integration, :provider] = "azure"
def config[:integration, :credentials, :azure_sas_token] = "<azure_sas_token>"
def json = load_json[config]

Definition

@inline
def load_json = rel_primitive_load_json

parse_json

    parse_json[json_string_value]

Parses a JSON value directly from a string.

Example:

def json = parse_json[\"\"\"{"name": "Anton", "age": 56}\"\"\"]

Definition

@inline
def parse_json = rel_primitive_parse_json

json_string

    json_string[json_relation]

Returns the string representation of a relation encoding JSON data.

Example:

def json_relation[:name] = "Amira"
def json_relation[:age] = 32
def json_relation[:height] = missing
def json_relation[:pets, :[], 1] = "dog"
def json_relation[:pets, :[], 2] = "rabbit"

def result = json_string[json_relation]

results in the following JSON:

{
  "name": "Amira",
  "age": 32,
  "height": null,
  "pets": [
    "dog",
    "rabbit"
  ]
}

Definition

@inline
def json_string = rel_primitive_json_string

export_json

export_json[R]

This is the main entry point to specify relations that should be exported to JSON. The payload relation R is expected to be a configuration relation mapping keys to relations.

Required keywords are:

  • :path: A string specifying the location of the file that is to be created.

Optional keywords:

  • :data: The relation(s) that should be exported.
  • :integration: A relation containing storage integration configuration.
  • :indent: Number of spaces to indent the resulting document. If not present, we’ll write a compact form.

Example for a simple JSON object

def json[:author] = "David Foster Wallace"
def json[:title, :name] = "Infinite Jest"
def json[:title, :isbn] = "978-0-316-92004-9"

def export = export_json[(:path, "/path/to/file.json");
(:data, json);
(:indent, 2)]

This results in the following JSON:

{
  "author": "David Foster Wallace",
  "title": {
    "name": "Infinite Jest",
    "isbn": "10: 0316921173"
  }
}

Since authors usually write more than one book, we’ll make the :title path an array of objects.

Examples:

def json[:author] = "David Foster Wallace"

def titles = (1, "Infinite Jest", "978-0-316-92004-9");
(2, "The Pale King", "978-0-316-07423-0")

def json[:titles, :[], idx] = ((:name, name);
(:isbn, isbn)
from name, isbn where titles(idx, name, isbn))

def export = export_json[(:path, "/path/to/file.json");
(:data, json);
(:indent, 2)]

The :[] marker, i.e. array marker, needs to be present when an array is to be correctly produced.

This yields the following output:

{
  "author": "David Foster Wallace",
  "titles": [
    {
      "name": "Infinite Jest",
      "isbn": "978-0-316-92004-9"
    },
    {
      "name": "The Pale King",
      "isbn": "978-0-316-07423-0"
    }
  ]
}

Definition

@inline
def export_json[R](ys..., xs...) =
(:envelope, (
(:payload, R(xs...));
(:content_type, "application/json")(xs...)
))(ys...)

substring

substring[string, index1, index2]

Substring of a string, selecting characters in range [index1:index2] (both inclusive).

The index of the first character is 1.

The supplied indexes are character indexes, corresponding to the char[s, i] function.

Example:

substring["abcd", 2, 3] = "bc"
substring["中文例子", 2, 3] = "文例"

Definition

@inline
def substring = rel_primitive_substring

string_replace

string_replace[string, old_str, new_str]

Replace a string or character in string with the specific string or character.

Examples:

string_replace["One Two Three", "One", "1"] = "1 Two Three"
string_replace["Rel", 'R', 'r'] = "rel"

Definition

@inline
def string_replace = rel_primitive_replace

string_replace_multiple

string_replace_multiple[input, R]

Replaces all occurrences of old string in input with the corresponding new string, for each tuple (old, new) in relation R.

Examples:

string_replace_multiple["One Two Three", {("One", "1"),("Two", "2")}] = "1 2 Three"
string_replace_multiple["One Two Three", {('O', 'o'),('T', 't')}] = "one two three"

The behavior is undefined if new generates more occurrences of old.

Definition

@inline
def string_replace_multiple[input, R] = rel_primitive_reduce[(x,y,z: z=string_replace[x,y,R[y]]), input, (x: R(x,_))]

string_trim

string_trim[s]

To remove leading and tailing whitespaces in given string s

Example:

string_trim["  Good Day!!!  "] = "Good Day!!!"

Definition

@inline
def string_trim[s] = string_replace[s, regex_compile["^\\s+|\\s+$"], ""]

regex_match

regex_match(regex_string, string)

Match string with a regular expression

Note that if the entire string needs to be matched, then a leading ^ and trailing $ is required.

Example:

regex_match("^.*@.*$", "a@b")

Definition

@inline
def regex_match = rel_primitive_regex_match

like_match

like_match(like_string, string)

Match string with a SQL “LIKE” pattern; case-sensitive.

The ‘%’ character is a wildcard, and is not escapable.

Example:

like_match("%PROMO%", "this is a PROMOtion")
()

Definition

@inline
def like_match = rel_primitive_like_match

regex_compile

regex_compile(regex_string, pattern)

Compile a regular expression to a pattern.

Example:

def p = regex_compile["^.*@.*$"]
pattern_match(p, "foo@example.com")
()

Definition

@inline
def regex_compile = rel_primitive_regex_compile

pattern_match

pattern_match(pattern, string)

Match string with a pattern (pre-compiled regular expression)

Example:

def p = regex_compile["^.*@.*$"]
def output = pattern_match(p, "foo@example.com")
true

Definition

@inline
def pattern_match = rel_primitive_pattern_match

parse_int

parse_int[string]

Parse a string representation of an integer to a SignedInt[64]

Derives to false if the string fails to parse as an integer.

Example:

parse_int["123"] = 123
empty(parse_int["hello"])

Definition

@inline
def parse_int = rel_primitive_parse_int

parse_float

parse_float[string]

Parse a string representation of a float to a Floating[64]

Note that this currently does not consider a locale (decimal separator is always .)

Derives to false if the string fails to parse as an integer.

Examples:

parse_float["3.14"] = 3.14
parse_float["3"] = 3.0
empty(parse_int["hello"])

Definition

@inline
def parse_float = rel_primitive_parse_float

parse_decimal

parse_decimal[bits, digits, string]

Parse a string representation of a fixed-point decimal number to a decimal[a,b]

The implementation does not consider a locale. The decimal separator is always . and the thousands separator is always ,. The thousands separator is optional.

If the string has more digits after the decimal point than supported by the b parameter, then the number is rounded to the nearest number.

This function is only defined for valid arguments, and invalid arguments will return false (the empty relation, {}).

Examples:

parse_decimal[64, 2, "3.14"]
FixedDecimal{Int64,2}(3.14)

parse_decimal[64, 1, "4.27"]
FixedDecimal{Int64,1}(4.3)

parse_decimal[64, 2, ""]
FixedDecimal{Int64,2}(0.00)

parse_decimal[64, 2, "1,234.567"]
FixedDecimal{Int64,2}(1234.57)

parse_decimal[64, 2, "invalid"]
{}

Definition

@inline
def parse_decimal = rel_primitive_parse_decimal

decimal_bit_length

decimal_bit_length[decimal]

Returns the bit length of a fixed-point decimal number.

Example:

def g = parse_decimal[64, 2, "3.14"]
decimal_bit_length[g] = 64

Definition

@inline
def decimal_bit_length(x, y) = rel_primitive_decimal_type(x, y, _)

decimal_precision

decimal_precision[decimal]

Returns the precision of a fixed-point decimal number.

Example:

def g = parse_decimal[64, 2, "3.14"]
decimal_precision[g] = 2

Definition

@inline
def decimal_precision(x, y) = rel_primitive_decimal_type(x, _, y)

parse_uuid

parse_uuid[str]

Parse a UUID string (in the standard 8-4-4-4-12 format) to a UInt128 value.

Example:

parse_uuid["22b4a8a1-e548-4eeb-9270-60426d66a48e"] = 0x22b4a8a1e5484eeb927060426d66a48e

Definition

@inline
def parse_uuid = rel_primitive_parse_uuid

uuid_string

uuid_string[v]

Convert a UInt128 value to the standard UUID format

Example:

uuid_string[0x22b4a8a1e5484eeb927060426d66a48e] = "22b4a8a1-e548-4eeb-9270-60426d66a48e"

Definition

@inline
def uuid_string = rel_primitive_uuid_string

concat

concat[str_or_char1, str_or_char2]

String concatenation between strings and/or characters.

Example:

concat["a", "b"] = "ab"
concat["a", 'b'] = "ab"
concat['a', "b"] = "ab"
concat['a', 'b'] = "ab"

Definition

@inline
def concat = rel_primitive_concat

string_length

string_length[string]

DEPRECATED
This function is deprecated. Please prefer num_chars[string] or num_bytes[string], depending on your need.

Example:

string_length["foo"] = 3

Definition

@inline
def string_length = num_chars

uppercase

uppercase[string1]

Convert all characters to uppercase.

Example:

def output = uppercase["aB1c"]
"AB1C"

lowercase does not take a locale option and does not handle local-specific case mapping rules.

Definition

@inline
def uppercase = rel_primitive_uppercase

lowercase

lowercase[string1]

Convert all characters to lowercase.

Example:

lowercase["aB1c"]
"ab1c"

lowercase does not take a locale option and does not handle local-specific case mapping rules.

Definition

@inline
def lowercase = rel_primitive_lowercase

levenshtein

levenshtein[string1, string2]

Calculate the Levenshtein distance between two strings.

Example:

levenshtein["kitten", "sitting"]
3

Definition

@inline
def levenshtein = rel_primitive_levenshtein

num_chars

num_chars[str]

Returns the number of (Unicode) characters in a string, i.e. the length of the string.

A character is also known as a “Code Point” in the Unicode specification.

Example:

num_chars["abcd"] = 4
num_chars["中文例子"] = 4

Definition

@inline
def num_chars = rel_primitive_num_chars

char

char[str]
char[str, i]
char(str, i, c)

Indexes into a string at (Int) position i, returning the i-th character, c.

Since this is indexed using character positions, the returned character will always be a whole Unicode character.

A character is also known as a “Code Point” in the Unicode specification.

Both i and c can be optionally bound externally. When only str is bound, this returns the mapping from each character index to its corresponding character.

Examples

Indexing into a known character index:

char["abcd", 2] = 'b'
char["中文例子", 2] = '文'

Abstracting over the character index:

equal(char["中文"],
{ 1, "中"; 2, "文" })
equal((i : char["awesome", i]('e')), {3; 7})

Definition

@inline
def char[s](i, c) =
rel_primitive_char(s, i, c) and range[1, num_chars[s], 1](i)

num_bytes

num_bytes[str]

Returns the number of bytes in a string value.

If a string contains Unicode characters, it’s possible that some characters take up more than one byte, so num_bytes[str] may be larger than num_chars[str].

Example:

num_bytes["abcd"] = 4
num_bytes["中文例子"] = 12

Definition

@inline
def num_bytes = rel_primitive_num_bytes

byte

byte[str]
byte[str, i]
byte(str, i, b)

Indexes into a string at byte position i, returning the byte b as a UInt8 value.

If a string contains Unicode characters, it’s possible that a byte at index i might be only a partial character. Take care with your indexing logic.

Both i and b can be optionally bound externally. When only str is bound, this returns the mapping from each index to its corresponding byte.

Examples: Indexing into a known byte index:

byte["abcd", 2] = 0x62
byte["中文例子", 2] = 0xb8

Abstracting over the byte index:

equal(byte["中文"],
{ 1, 0xe4;
2, 0xb8;
3, 0xad;
4, 0xe6;
5, 0x96;
6, 0x87; })
equal((i : byte["awesome", i](0x65)), {3; 7})

Definition

@inline
def byte[s](i, b) =
rel_primitive_byte(s, i, b) and range[1, num_bytes[s], 1](i)

string

string[x]

Convert ints, floats, dates, periods, to strings:

Examples:

string[1] = "1"
string[3.4] = "3.4"
string[unix_epoch] = "1970-01-01T00:00:00"
string[Hour[1]] = "1 hour"
string["a"] = "a"
string[:a] = "a"

Definition

@inline
def string = rel_primitive_string

string_join

string_join[separator, R]

Concatenate together all the String values in the second argument of the binary relation R, if non-empty, separated by the separator string, following the ordering provided by the Ints in the first argument of R. If R is empty, string_join[sep, R] is false.

R is required to be a binary relation, containing only (Int, String) tuples, where the Int serves as the sort key for the concatenation. Otherwise, string_join[sep, R] is false. (Note: this restriction may be lifted in the future.)

Example:

string_join[", ", {(1, "a"); (2, "b"); (3, "c")}] = "a, b, c"
string_join[" ", {(1, "hello"); (2, "world")}] = "hello world"
string_join["->", sort[{"x"; "y"; "z"}]] = "x->y->z"

Definition

@inline
def string_join[sep, R] =
rel_primitive_reduce_noinit[_str_join2[sep], R],
// R is required to be a Binary relation with numeric keys to provide an ordering
R (Int, String)

// This helper function is reduced over the values in R, with the provided separator.
@inline
def _str_join2[sep, a,b] = concat[concat[a, sep], b]

parse_date

parse_date[d, format]

Parse Date

Examples:

def output = parse_date["2018-06-12", "Y-m-d"]
2018-06-12

For details on the format parameter, see the Julia documentation for Dates.DateFormat.

Definition

@inline
def parse_date = rel_primitive_parse_date

parse_datetime

parse_datetime[dt, format]

Parse DateTime

Examples:

def output = parse_datetime["2018-06-12 13:00 +00:00", "YYYY-mm-dd HH:MM zzzz"]
Dates.DateTime("2018-06-12T13:00:00")

def output = parse_datetime["2018-03-11 01:00 America/New_York", "Y-m-d H:M Z"]
Dates.DateTime("2018-03-11T06:00:00")

For details on the format parameter, see the Julia documentation for Dates.DateFormat.

Definition

@inline
def parse_datetime = rel_primitive_parse_datetime

format_date

format_date[d, format]

Format a Date d, returning a string.

Example:

def d = parse_date["2018-06-12", "Y-m-d"]
def output = format_date[d, "Y-mm-d"]
"2018-06-12"

For details on the format parameter, see the Julia documentation for Dates.DateFormat.

Definition

@inline
def format_date = rel_primitive_format_date

format_datetime

format_datetime[dt, format, tz]

Format a DateTime dt, with timezone tz.

Example:

def format = "yyyy-mm-dd HH:MM ZZZ"
def dt = parse_datetime["2018-03-11 01:00 America/New_York", "Y-m-d H:M Z"]
def output = format_datetime[dt, format, "America/New_York"]
"2018-03-11 01:00 EST"

For details on the format parameter, see the Julia documentation for Dates.DateFormat.

Definition

@inline
def format_datetime = rel_primitive_format_datetime

date_year

date_year[d]

Year of a Date, as an integer.

Example:

date_year[parse_date["2020-05-22", "Y-m-d"]]
2020

Year[date_year[parse_date["2020-05-22", "Y-m-d"]]]
(2020 years,)

Definition

@inline
def date_year = rel_primitive_date_year

datetime_year

datetime_year[dt, tz]

Year of a DateTime dt, as an Int. Note that DateTime properties require a timezone tz.

Examples:

def dt = parse_datetime["2021-01-01 01:00:00", "Y-m-d H:M:S"]
datetime_year[dt, "Europe/Berlin"] = 2021
datetime_year[dt, "America/New_York"] = 2020
datetime_year[dt, "-03:00"] = 2020

Definition

@inline
def datetime_year = rel_primitive_datetime_year

date_month

date_month[d]

Month of a Date, as an integer between 1 and 12.

Definition

@inline
def date_month = rel_primitive_date_month

datetime_month

datetime_month[dt, tz]

Month of a DateTime, as an integer between 1 and 12.

Note that DateTime properties require a timezone tz.

Examples:

def dt = parse_datetime["2021-01-01 01:00:00", "Y-m-d H:M:S"]
datetime_month[dt, "Europe/Berlin"] = 1
datetime_month[dt, "America/New_York"] = 12
datetime_month[dt, "-03:00"] = 12

Definition

@inline
def datetime_month = rel_primitive_datetime_month

date_week

date_week[d]

Week of the year for a Date, as an integer, where the first week is the week that contains the first Thursday of the year. Ranges between 1 and 53, inclusive.

Example:

week[parse_date["2005-01-01", "Y-m-d"]] = 53 // 53rd week of 2004.
week[parse_date["2001-01-01", "Y-m-d"]] = 1

Definition

@inline
def date_week = rel_primitive_date_week

datetime_week

datetime_week[dt, tz]

Week of the year for a DateTime dt, as an integer between 0 and 53 (see date_week).

Note that DateTime properties require a timezone tz.

Definition

@inline
def datetime_week = rel_primitive_datetime_week

date_day

date_day[d]

Day of the month for a Date, as an integer between 1 and 31, inclusive.

Example:

def d = parse_date["2014-10-31", "Y-m-d"]
date_day[d] = 31

Definition

@inline
def date_day = rel_primitive_date_day

date_dayofweek

date_dayofweek[d]

Day of the week for a date, as an integer between 1 and 7, where 1 is Monday and 7 is Sunday. (That is, 5 for Friday.)

Example:

def d = parse_date["2014-01-31", "Y-m-d"]
date_dayofweek[d] = 5

Definition

@inline
def date_dayofweek = rel_primitive_date_dayofweek

date_dayname

date_dayname[d]

Name of week-day (returns a String, e.g., Friday)

Example:

def t = parse_date["2014-01-31", "Y-m-d"]
def output = date_dayname[t]
"Friday"

Definition

@inline
def date_dayname = rel_primitive_date_dayname

date_dayofweekofmonth

date_dayofweekofmonth[d]

Day of week of month, as an integer (e.g. 2 for second Friday of the month, 1 for the first Tuesday).

Example:

def t = parse_date["2014-01-31", "Y-m-d"]
date_dayofweekofmonth[t] = 5 // fifth Friday of that month

Definition

@inline
def date_dayofweekofmonth = rel_primitive_date_dayofweekofmonth

date_monthname

date_monthname[d]

The month name for date d, as a string.

Example:

def t = parse_date["2014-01-31", "Y-m-d"]
date_monthname[t] = "January"

Definition

@inline
def date_monthname = rel_primitive_date_monthname

date_daysinmonth

date_daysinmonth[d]

The number of days in the month for date d

Examples:

def d1 = parse_date["2014-01-30", "Y-m-d"]
date_daysinmonth[t] = 31
def d2 = parse_date["2014-02-11", "Y-m-d"]
date_daysinmonth[d2] = 28
def d = parse_date["2016-02-11", "Y-m-d"]
date_daysinmonth[d] = 29

Definition

@inline
def date_daysinmonth = rel_primitive_date_daysinmonth

date_isleapyear

date_isleapyear[d]

True iff the year for date d is a leap year.

Examples:

def notleap = parse_date["2014-01-31", "Y-m-d"]
def leap = parse_date["2016-01-31", "Y-m-d"]
def output = date_isleapyear[notleap]
false
def output = date_isleapyear[leap]
true

Definition

@inline
def date_isleapyear = rel_primitive_date_isleapyear

date_dayofyear

date_dayofyear[d]

Day of year

Example:

def t = parse_date["2014-01-31", "Y-m-d"]
def output = date_dayofyear[t]
31

Definition

@inline
def date_dayofyear = rel_primitive_date_dayofyear

date_quarterofyear

date_quarterofyear[d]

Quarter to year

Example:

def d = parse_date["2014-01-31", "Y-m-d"]
date_quarterofyear[d] = 1

Definition

@inline
def date_quarterofyear = rel_primitive_date_quarterofyear

date_dayofquarter

date_dayofquarter[d]

Day of quarter

Example:

def d = parse_date["2014-01-31", "Y-m-d"]
date_dayofquarter[d] = 31

Definition

@inline
def date_dayofquarter = rel_primitive_date_dayofquarter

datetime_day

datetime_day[dt, tz]

Day of the month, for a DateTime. Note that DateTime properties require a timezone string tz.

Example:

datetime_day[unix_epoch, "UTC"] = 1
datetime_day[unix_epoch, "UTC-10"] = 31

Definition

@inline
def datetime_day = rel_primitive_datetime_day

datetime_hour

datetime_hour[dt, tz]

Hour of a DateTime. Note that DateTime properties require a timezone string tz.

Definition

@inline
def datetime_hour = rel_primitive_datetime_hour

datetime_minute

datetime_minute[dt, tz]

Minute of a DateTime. Note that DateTime properties require a timezone string tz.

Definition

@inline
def datetime_minute = rel_primitive_datetime_minute

datetime_second

datetime_second[dt]

Second of a DateTime. Unlike other DateTime functions, datetime_second does not require a timezone argument.

Example:

datetime_second[unix_epoch] = 0

Definition

@inline
def datetime_second = rel_primitive_datetime_second

datetime_year_UTC

datetime_year_UTC[dt]

Year assuming datetime is in UTC.

Definition

@inline
def datetime_year_UTC[d] = rel_primitive_datetime_year[d, "UTC"]

datetime_month_UTC

datetime_month_UTC[dt]

Month assuming datetime is in UTC.

Definition

@inline
def datetime_month_UTC[d] = rel_primitive_datetime_month[d, "UTC"]

datetime_week_UTC

datetime_week_UTC[dt]

Week assuming datetime is in UTC.

Definition

@inline
def datetime_week_UTC[d] = rel_primitive_datetime_week[d, "UTC"]

datetime_day_UTC

datetime_day_UTC[dt]

Day assuming datetime is in UTC.

Definition

@inline
def datetime_day_UTC[d] = rel_primitive_datetime_day[d, "UTC"]

datetime_hour_UTC

datetime_hour_UTC[dt]

Hour assuming datetime is in UTC.

Definition

@inline
def datetime_hour_UTC[d] = rel_primitive_datetime_hour[d, "UTC"]

datetime_minute_UTC

datetime_minute_UTC[dt]

Minute assuming datetime is in UTC.

Definition

@inline
def datetime_minute_UTC[d] = rel_primitive_datetime_minute[d, "UTC"]

datetime_dayofweek

datetime_dayofweek[dt, tz]

Day of week (returns a number, e.g., 5 for Friday)

Example:

def dt = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_dayofweek[dt, "+03:00"] = 5
datetime_dayofweek[dt, "UTC"] = 4

Definition

@inline
def datetime_dayofweek = rel_primitive_datetime_dayofweek

datetime_dayname

datetime_dayname[t, tz]

Name of week-day (returns a String, e.g., Friday)

Example:

def t = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_dayname[t, "+03:00"] = "Friday"

Definition

@inline
def datetime_dayname = rel_primitive_datetime_dayname

datetime_dayofweekofmonth

datetime_dayofweekofmonth[dt, tz]

Day of week of month, as an integer (e.g. 2 for second Friday of the month, 1 for the first Tuesday).

Example:

def t = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_dayofweekofmonth[t, "+03:00"] = 5

Definition

@inline
def datetime_dayofweekofmonth = rel_primitive_datetime_dayofweekofmonth

datetime_monthname

datetime_monthname[dt, tz]

The month name

Example:

def t = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_monthname[t, "+03:00"] = "January"

Definition

@inline
def datetime_monthname = rel_primitive_datetime_monthname

datetime_daysinmonth

datetime_daysinmonth[dt, tz]

The number of days in a datetime’s month, adjusting for timezone tz.

Examples:

def t = parse_datetime["2014-02-28 23:00 +00:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_daysinmonth[t, "+03:00"] = 31
datetime_daysinmonth[t, "-03:00"] = 28

Definition

@inline
def datetime_daysinmonth = rel_primitive_datetime_daysinmonth

datetime_isleapyear

datetime_isleapyear[dt, tz]

Is it a leap year?

Examples:

def dt = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
def output = datetime_isleapyear[dt, "+03:00"]
false

def dtleap = parse_datetime["2016-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
def output = datetime_isleapyear[dtleap, "+00:00"]
true

Definition

@inline
def datetime_isleapyear = rel_primitive_datetime_isleapyear

datetime_dayofyear

datetime_dayofyear[dt, tz]

Day of year

Examples:

def dt = parse_datetime["2014-03-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_dayofyear[dt, "+03:00"] = 90
datetime_dayofyear[dt, "+00:00"] = 89

Definition

@inline
def datetime_dayofyear = rel_primitive_datetime_dayofyear

datetime_quarterofyear

datetime_quarterofyear[dt, tz]

Quarter of the year for datetime dt, as a number between 1 and 4.

Example:

def t = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_quarterofyear[t, "+03:00"] = 1

Definition

@inline
def datetime_quarterofyear = rel_primitive_datetime_quarterofyear

datetime_dayofquarter

datetime_dayofquarter[dt, tz]

Day of quarter

Example:

def t = parse_datetime["2014-01-31 01:00 +03:00", "YYYY-mm-dd HH:MM zzzz"]
datetime_dayofquarter[t, "+03:00"] = 31

Definition

@inline
def datetime_dayofquarter = rel_primitive_datetime_dayofquarter

date_add

date_add[d, period]
d + period

Add a Period to a Date

Example:

def d = parse_date["2021-09-21", "Y-m-d"]
def output = date_add[d, Day[20]]
Dates.Date("2021-10-11")

Definition

@inline
def date_add = rel_primitive_add_date_period

datetime_add

datetime_add[dt, period]
dt + period

Add a Period to a DateTime

Examples:

def dt = parse_datetime["2021-01-01 01:00:00", "Y-m-d H:M:S"]
def output = datetime_add[dt, Year[1]]
Dates.DateTime("2022-01-01T01:00:00")

def output = datetime_add[dt, Month[13]]
Dates.DateTime("2022-02-01T01:00:00")

def output = datetime_add[dt, Hour[1000]]
Dates.DateTime("2021-02-11T17:00:00")

Definition

@inline
def datetime_add = rel_primitive_add_datetime_period

date_subtract

date_subtract[d, period]
d - period

Subtract a Period from a Date, returning another Date.

Example:

def d = parse_date["2021-09-21", "Y-m-d"]
def output = date_subtract[d, Day[1000]]
Dates.Date("2018-12-26")

Definition

@inline
def date_subtract = rel_primitive_subtract_date_period

datetime_subtract

datetime_subtract[dt, period]
dt - period

Subtract a Period from a DateTime, returning another DateTime.

Example:

def dt = parse_datetime["2021-01-01 01:00:00", "Y-m-d H:M:S"]
def output = datetime_subtract[dt, Hour[1000]]
Dates.DateTime("2020-11-20T09:00:00")

Definition

@inline
def datetime_subtract = rel_primitive_subtract_datetime_period

period_add

period_add[period1, period2]

Add two periods. For now, they need to be of the same type.

def output = period_add[Minute[3], Minute[4]]
Minute[7]

Definition

@inline
def period_add = rel_primitive_add

period_min

period_min[period1, period2]

Minimum of two periods. The result is expressed as a nanosecond period.

Example:

def output = period_min[Minute[3],Minute[4]] = Minute[3]
true

Definition

@inline
def period_min[x,y] = rel_primitive_min_period[x,y]

period_max

period_max[period1, period2]

Maximum of two periods. The result is expressed as a nanosecond period.

Example:

def output = period_max[Minute[3],Minute[4]] = Minute[4]
true

Definition

@inline
def period_max[x,y] = rel_primitive_max_period[x,y]

unix_epoch

unix_epoch

The beginning of the UNIX Epoch, midnight on 1 January 1970 (UTC).

Definition

@inline
def unix_epoch = parse_datetime["1970-01-01 00 UTC", "Y-m-d H Z"]

datetimes_period_milliseconds

datetimes_period_milliseconds[dt1, dt2]

Computes the difference between two datetimes, dt1 - dt2, returning the result in milliseconds.

Definition

@inline
def datetimes_period_milliseconds = rel_primitive_datetimes_period_milliseconds

datetime_to_nanoseconds

datetime_to_nanoseconds[dt]

Convert datetime to nanoseconds since the epoch. Assumes dt is in UTC.

Definition

@inline
def datetime_to_nanoseconds(datetime, v) =
rel_primitive_datetimes_period_nanoseconds(unix_epoch, datetime, p) and
rel_primitive_period_nanoseconds(p, v)
from p

nanoseconds_to_datetime

nanoseconds_to_datetime[dt]

Convert nanoseconds to datetime. Equivalent to datetime.datetime.utcfromtimestamp(seconds) in python.

Definition

@inline
def nanoseconds_to_datetime[nanoseconds] = datetime_add[unix_epoch, Nanosecond[nanoseconds]]

Year

Year[n]

Return a Year period value for the given integer n.

Example:

datetime_add[unix_epoch, Year[10]]

Definition

@inline
def Year = rel_primitive_year

Month

Month[n]

Return a Month period value for the given integer n.

Example:

datetime_add[unix_epoch, Month[10]]

Definition

@inline
def Month = rel_primitive_month

Week

Week[n]

Return a Week period value for the given integer n.

Example:

datetime_add[unix_epoch, Week[10]]

Definition

@inline
def Week = rel_primitive_week

Day

Day[n]

Return a Day period value for the given integer n.

Example:

datetime_add[unix_epoch, Day[10]]

Definition

@inline
def Day = rel_primitive_day

Hour

Hour[n]

Return an Hour period value for the given integer n.

Example:

datetime_add[unix_epoch, Hour[10]]

Definition

@inline
def Hour = rel_primitive_hour

Minute

Minute[n]

Return a Minute period value for the given integer n.

Example:

datetime_add[unix_epoch, Minute[100]]

Definition

@inline
def Minute = rel_primitive_minute

Second

Second[n]

Return a Second period value for the given integer n.

Example:

datetime_add[unix_epoch, Second[100]]

Definition

@inline
def Second = rel_primitive_second

Millisecond

Millisecond[n]

Return a Millisecond period value for the given integer n.

Example:

datetime_add[unix_epoch, Millisecond[100000]]

Definition

@inline
def Millisecond = rel_primitive_millisecond

Microsecond

Microsecond[n]

Return a Microsecond period value for the given integer n.

Example:

datetime_add[unix_epoch, Microsecond[10000000]]

Definition

@inline
def Microsecond = rel_primitive_microsecond

Nanosecond

Nanosecond[n]

Return a Nanosecond period value for the given integer n.

Example:

datetime_add[unix_epoch, Nanosecond[10000000000]]
Dates.DateTime("1970-01-01T00:00:10")

Definition

@inline
def Nanosecond = rel_primitive_nanosecond

is_Year

is_Year(x)

Holds if x is a Year period.

Definition

@inline
def is_Year = rel_primitive_Year

is_Month

is_Month(x)

Holds if x is a Month period.

Definition

@inline
def is_Month = rel_primitive_Month

is_Week

is_Week(x)

Holds if x is a Week period.

Definition

@inline
def is_Week = rel_primitive_Week

is_Day

is_Day(x)

Holds if x is a Day period.

Definition

@inline
def is_Day = rel_primitive_Day

is_Hour

is_Hour(x)

Holds if x is a Hour period.

Definition

@inline
def is_Hour = rel_primitive_Hour

is_Minute

is_Minute(x)

Holds if x is a Minute period.

Definition

@inline
def is_Minute = rel_primitive_Minute

is_Second

is_Second(x)

Holds if x is a Second period.

Definition

@inline
def is_Second = rel_primitive_Second

is_Millisecond

is_Millisecond(x)

Holds if x is a Millisecond period.

Definition

@inline
def is_Millisecond = rel_primitive_Millisecond

is_Microsecond

is_Microsecond(x)

Holds if x is a Microsecond period.

Definition

@inline
def is_Microsecond = rel_primitive_Microsecond

is_Nanosecond

is_Nanosecond(x)

Holds if x is a Nanosecond period.

Definition

@inline
def is_Nanosecond = rel_primitive_Nanosecond

Date

Date(x)

Holds if x is a Date.

Definition

@inline
def Date = rel_primitive_Date

DateTime

DateTime(x)

Holds if x is a DateTime.

Definition

@inline
def DateTime = rel_primitive_DateTime

datetime_now

datetime_now

Returns current UTC time (start time of transaction) as a DateTime value.

Example:

def output = datetime_now

Definition

@inline
def datetime_now = rel_primitive_transaction_edb[:txn_start_time]

boolean_true

boolean_true(x)

Holds if x is a Boolean of value true.

Definition

@inline
def boolean_true = rel_primitive_boolean_true

boolean_false

boolean_false(x)

Holds if x is a Boolean of value false.

Definition

@inline
def boolean_false = rel_primitive_boolean_false

Boolean

Boolean(x)

Holds if x is a Boolean.

Example:

def json = parse_json[\"\"\"{"a": true, "b": false}\"\"\"]
def output(x) = json(:a, x) and Boolean(x)

Definition

@inline
def Boolean = rel_primitive_Boolean

boolean_and

boolean_and(x, y, z)

Logical AND operator for the Boolean data type.

Example:

def output(x, y, z) = boolean_and(x, y, z) and boolean_true(z)

Definition

@inline
def boolean_and(x in Boolean, y in Boolean, z in Boolean) =
if boolean_true(x) and boolean_true(y) then
boolean_true(z)
else
boolean_false(z)

boolean_or

boolean_or(x, y, z)

Logical or operator for the Boolean data type. Example:

def output(x, y, z) = boolean_or(x, y, z) and boolean_false(z)

Definition

@inline
def boolean_or(x in Boolean, y in Boolean, z in Boolean) =
if boolean_false(x) and boolean_false(y) then
boolean_false(z)
else
boolean_true(z)

boolean_not

boolean_not(x,y)

Negation(not) operator for the Boolean data type. Example:

def output(x, y) = boolean_not(x, y) and boolean_false(x)

Definition

@inline
def boolean_not(x in Boolean, y in Boolean) =
if boolean_true(x) then
boolean_false(y)
else
boolean_true(y)

F and G
F ∧ G

Logical and (conjunction).

Definition

@inline
def (∧)(F, G) = F and G

F or G
F ∨ G

Logical or (disjunction), for boolean (arity 0, true or false) arguments F and G.

Definition

@inline
def (∨)(F, G) = F or G

F implies G
F ⇒ G
G ⇐ F

Logical implication, for boolean (arity 0, true or false) arguments F and G.

Definition

@inline
def (⇒)(F, G) = F implies G

@inline
def (⇐)(F, G) = G implies F

¬

not F
¬F

Logical negation, for boolean (arity 0, true or false) argument F.

Definition

@inline
def (¬)(F) = not F

iff

F iff G
F ≡ G
F ⇔ G

If and only if (boolean equivalence), for boolean (arity 0, true or false) arguments F and G.

Definition

@inline
def iff(F, G) = (F implies G) and (G implies F)

@inline
def (≡)(F, G) = F iff G

@inline
def (⇔)(F, G) = F iff G

xor

F xor G
F ≢ G
F ⇎ G
F ⊻ G

Exclusive OR, for boolean (arity 0, true or false) arguments F and G.

Definition

@inline
def xor(F, G) = not(F G)

@inline
def (⊻)(F, G) = F xor G

@inline
def (≢)(F, G) = F xor G

@inline
def (⇎)(F, G) = F xor G

intersect

intersect[R, S]
R ∩ S

Intersect two n-ary relations R and S

Definition

@inline
def intersect[R, S](x...) = R(x...) and S(x...)

@inline
def (∩) = intersect

union

union[R, S]
R ∪ S

Union two n-ary relations R and S

Definition

@inline
def union[R, S](x...) = R(x...) or S(x...)

@inline
def (∪) = union

cart

cart[R, S]
R × S

Cartesian product.

Examples:

def output = 12
(1,2)
def output = {1; 2}{3; 4}
(1,3)
(1,4)
(2,3)
(2,4)

Definition

@inline
def cart[R, S](x..., y...) = R(x...) and S(y...)

@inline
def (×) = cart

diff

diff[R, S]

Set difference (complement): removes the tuples of S from R, if present.

Example:

def output = diff[{1;2;3;4} , {1;3} ]
2
4

Definition

@inline
def diff[R, S](x...) = R(x...) and not S(x...)

complement

complement[R]

The complement of relation R

Definition

@inline
def complement[R](x...) = not R(x...)

domain

domain[F]

The domain of a scalar function F. The domain represents all but the last argument of F.

Example:

def output = domain[{(1, 'a', 1}; (2, 'b', 4); (3, 'c', 9)}]
(1, 'a')
(2, 'b')
(3, 'c')

Definition

@inline
def domain[F](k...) = F(k..., _)

first

first[R]

Projection to the first argument of R.

first supports heterogeneous relations, both in arity and types.

Examples:

def output = first[(1, 2, 3); (4, 5, 6)]
1
4
def output = first[(1, 2, 3); (4, 5); 6]
1
4
6
def output = first[("a", 1); (2, 3)]
2
"a"

Definition

@inline
def first[R](x) =(y... : R(x, y...))

second

second[R]

Projection to the second argument of R.

second supports heterogeneous relations, both in arity and types.

Examples:

def output = second[(1, 2, 3); (4, 5)]
2
5
def output = second[(1, 2); (3, "abc")]
2
"abc"

Definition

@inline
def second[R](x) =(y... : R(_, x, y...))

last

last[R]

Projection to the last argument of the relation R.

last supports heterogeneous relations, both in arity and types.

Examples:

def output = last[(1, 2, 3); (4, 5, 6)]
3
6
def output = last[1; 2; 3]
1
2
3
def output = last[(1, 2); (3, "abc")]
2
"abc"
def output = last[(1, 2, 3); (4, 5)]
3
5

Definition

@inline
def last[R](y) =(x... : R(x..., y))

transpose

transpose[R]

Transpose a binary relation (swap the two arguments).

Example:

def output = transpose[{(1, 2); (3, 4)}]
(2, 1)
(4, 3)

Definition

@inline
def transpose[R](y, x) = R(x, y)

empty

empty(R)

Examples:

  • empty(1) is false
  • empty(true) is false
  • empty(false) is true

Definition

@inline
def empty(R) = not exists(x...: R(x...))

subset

subset(R, S)
R ⊆ S

R is a subset of S if all facts of R is also in S.

Subset is the relational variant of implication, but note that there is a subtle difference for arity-overloaded relations due to usage of varargs.

  • forall(x, y: R(x, y) implies S(x, y)) is true if the implication is true for all binary facts of R. If R also has facts of a different arity, then the implication remains true, even when R ⊄ S.

  • subset(R, S), defined as forall(xs... where R(xs...) : S(xs...)) is true only if there exists no fact in R, of any arity, that is not in S.

Definition

@inline
def subset(R, S) = forall(x... where R(x...): S(x...))

@inline
def (⊆) = subset

proper_subset

proper_subset(R, S)
R ⊂ S

R is a proper subset of S if subset(R, S) and R and S are not equal. This means there has to be at least one fact in S that is not in R.

Definition

@inline
def proper_subset(R, S) = subset(R, S) and not equal(R, S)

@inline
def (⊂) = proper_subset

superset

superset(R, S)
R ⊇ S

Inverse of subset. See subset.

Definition

@inline
def superset(R, S) = forall(x... where S(x...): R(x...))

@inline
def (⊇) = superset

proper_superset

proper_superset(R, S)
R ⊃ S

Inverse of proper_subset. See proper_subset.

Definition

@inline
def proper_superset(R, S) = superset(R, S) and not equal(R, S)

@inline
def (⊃) = proper_superset

disjoint

disjoint(R, S)

R and S are disjoint if they have no fact in common.

Definition

@inline
def disjoint(R, S) = empty(R S)

dot_join

dot_join[R, S]
R . S

Compose

Definition

@inline
def dot_join[R,S](x..., y...) = exists(t: R(x..., t) and S(t, y...))

@inline
def (.) = dot_join

prefix_join

prefix_join[R, S]
R <: S

The prefix join (or restriction) of S to R is the subset of tuples of S where there is a prefix of the tuple in R.

ExampleNormalized
n <: edgex, y: edge(x, y) and x = n
female <: parentx, y: female(x) and parent(x, y)
t <: playersx, p: players(x, p) and t = x
t.players <: agep, v: players(t, p) and age(p, v)
intern <: salaryp, v: intern(p) and salary(p, v)

The restriction operator can also be used to select subsets of JSON-like relations:

  • :a <: json where json is {"a": {"b": 1, "c": 2}, "d": 3} has value {"a": {"b": 1, "c": 2}}

  • (:[], _, :a) <: json where json is [ {"a": 1, "b": 2}, {"a": 3, "b": 4} ] has value [ {"a": 1}, {"a": 3} ].

Definition

@inline
def prefix_join[R, S](x..., y...) = R(x...) and S(x..., y...)

@inline
def (<:) = prefix_join

suffix_join

suffix_join[R, S]
R :> S

The suffix join (or restriction) of R to S is the subset of tuples of R where there is a suffix of the tuple in S.

ExampleNormalized
edge :> nx, y: edge(x, y) and y = n
parent :> femalex, y: parent(x, y) and female(y)
account_balance :> positivex, v: account_balance(x, v) and positive(v)
brand_name :> "Tesla"b, s: brand_name(b, s) and s = "Tesla"

The restriction operator can also be used to select subsets of JSON-like relations:

  • json :> even where json is [ {"a": 1, "b": 2}, {"a": 3, "b": 4, "c": 6} ] has value [ {"b": 2}, {"b": 4, "c": 6} ]

  • json :> (:b, even) where json is [ {"a": 1, "b": 2}, {"a": 3, "b": 4, "c": 6} ] has value [ {"b": 2}, {"b": 4} ]

Definition

@inline
def suffix_join[R, S](x..., y...) = R(x..., y...) and S(y...)

@inline
def (:>) = suffix_join

left_override

left_override[R, S]
R <++ S

The (left) override operator is intended to be applied to relations with functional dependencies from all but the last argument (the key) to the last argument (the value).

The (left) override of R and S consist of all the tuples of R and all the tuples in S for which the key is not in R. Often, S is used for default values.

ExampleValue
2 <++ 32
1.5 <++ 31.5
(1,2) <++ (1,3)(1,2)
(3,4) <++ (1,2); (3,5)(1,2); (3,4)
(3,"abc") <++ (1,2); (3,5)(1,2); (3,"abc")
(1,2); (3,5) <++ (3,4); (6,7)(1,2); (3,5); (6,7)

Override can be applied to heterogeneous relations, notably JSON-like relations. The following examples show <++ operators applied to JSON inputs.

LeftRightValue
{"a": 1, "b": 3}{"a": 2 }{"a": 1, "b": 3}
{"a": 2 }{"a": 1, "b": 3}{"a": 2, "b": 3}
{"a": 1, "b": 2}{"c": 3}{"a": 1, "b": 2, "c": 3}

Definition

@inline
def left_override[R, S](xs..., v) = R(xs..., v)

@inline
def left_override[R, S](xs..., v) = S(xs..., v) and not R(xs..., _)

@inline
def (<++) = left_override

right_override

right_override[R, S]
R ++> S

Right override is identical to left override, except the arguments are swapped.

A few examples to illustrate the difference with left-override:

ExampleValue
2 ++> 33
(1,2) ++> (1,3)(1,3)
(3,4) ++> (1,2); (3,5)(1,2); (3,5)
(3,"abc") ++> (1,2); (3,5)(1,2); (3,5)
(1,2); (3,5) ++> (3,4); (6,7)(1,2); (3,4); (6,7)

Definition

@inline
def right_override[R, S](xs..., v) = S(xs..., v)

@inline
def right_override[R, S](xs..., v) = R(xs..., v) and not S(xs..., _)

@inline
def (++>) = right_override

function

function(R)

Given a relation R, true if R is a function, and false if not.

Examples:

def output = function[{(1, 2); (2, 5)}]
true
def output = function[{(1, 2); (1, 3)}]
false

Definition

@inline
def function(R) =
forall(k..., v1, v2 where R(k..., v1) and R(k..., v2): v1 = v2)

total

total(D, F)

Given a domain D and a relation F, check that there is a value in F for every element of the domain D. The arity of F must be the arity of D + 1.

Definition

@inline
def total(D, F) =
forall(k... where D(k...): F(k..., _))

default_value

default_value[D, F, c]

Make function or relation F total for domain D by defaulting to c.

The arity of F must be the arity of D + 1.

Example:

def dom = {1;2;3;4}
def f = {(1, 321); (3, 123)}
def output = default_value[dom, f, 42]
(1, 321)
(2, 42)
(3, 123)
(4, 42)

Definition

@inline
def default_value[D, F, c](k..., v) =
F(k..., v) or (D(k...) and not F(k..., _) and v = c)

missing

missing

Singleton missing value.

Examples:

missing[] = missing
equal( (x : missing(x)) , {missing} )

Definition

@inline
def missing = rel_primitive_missing

relname_string

relname_string[relname]

Returns the string corresponding to the given relation name. Example: relname_string[:foobar] gives “foobar” .

Definition

@inline
def relname_string = rel_primitive_relname_string

arity

arity[R]

The arity of a relation.

Arity is a higher-order relation that is always evaluated at compile-time.

Examples:

def output = arity[3]
1
def output = arity[{1; 2; 3}]
1
def output = arity[(1, 2)]
2
def output = arity[add]
3
def output = arity[{1; 2; (1,2)}]
1
2

Arity can be used to do meta-programming in logic. For example, the following abstraction verbalize implements specific cases using arity.

Examples:

@inline def verbalize[R] = "nullary", arity[R] = 0;
"unary", arity[R] = 1;
"binary", arity[R] = 2
def output = verbalize[true]
"nullary"
def output = verbalize[1]
"unary"

Arity can be used in higher-order abstractions to check at compile-time that they are used correctly.

Arity can be used in integrity constraints to state expectation on EDB or IDB relations. Because arity is evaluated at compile-time, it can catch mistakes in the logic before the logic executes.

Example:

def p = 1,2,3
ic { arity[p] = 3 }

Note that there is a difference between R(_, _) and arity(R) = 2. The first requires R to be non-empty, which is an runtime property of R.

Definition

@inline
def arity[R] = rel_primitive_arity[R]

pivot

pivot[R]

Associates each element of an input tuple with its index in the tuple. The result is always a binary relation.

pivot[(t1, ..., tn)] generates pairs (1, t1);... (i, ti); ... (n, tn) This is useful to match up long tuples with names (see zip), for example.

Example:

def output = pivot[{(5, 60, 7, 2)}]
(1, 5)
(2, 60)
(3, 7)
(4, 2)

pivot[true] and pivot[false] are both false (the empty relation) For unary relations, pivot[{(x)}] is {(1,x)}

Definition

@inline
def pivot[R] = rel_primitive_pivot[R]

zip

zip[R, S]

Pair-wise combine two n-ary tuples into a relation of n pairs.

Example:

def foods = {(:hamburger, :chicken, :hot_dog, :fries, :macaroni, :pizza, :salad, :milk, :ice_cream)}
def cost = { (2.49, 2.89, 1.50, 1.89, 2.09, 1.99, 2.49, 0.89, 1.59) }
def output = zip[foods, cost]
(:chicken, 2.89)
(:fries, 1.89)
(:hamburger, 2.49)
(:hot_dog, 1.5)
(:ice_cream, 1.59)
(:macaroni, 2.09)
(:milk, 0.89)
(:pizza, 1.99)
(:salad, 2.49)

Definition

@inline def zip[R,S](x, y) = rel_primitive_pivot[R](i, x) and rel_primitive_pivot[S](i, y) from i

jacobian

jacobian[S, R]

Compute the Jacobian (e.g. the derivative or gradient) of relation S w.r.t. relation R.

Consider the following Rel program, whose inputs are two relations A and x corresponding to a matrix and a vector respectively. It defines a scalar J := x' * A * x. It also defines and H to be the gradient and Hessian of J w.r.t. x.

Examples:

// J = x' *A *x
def J = sum[i j : x[i] * A[i, j] * x[j]]
def= rel_primitive_jacobian[J, x]
def H = rel_primitive_jacobian[, x]

The AD transformation converts the above program into the following:

Examples:

// J = x' * A * x
def J = sum[i j : x[i] * A[i, j] * x[j]]

// ∇1 = A * x
def1[i] = sum[j v : x[i] = _ and v = A[i, j] * x[j]]

// ∇2 = A' * x
def2[j] = sum[i v : v = x[i] * A[i, j] and x[j] = _]

// ∇ = ∇1 + ∇2
def(i, v) = (v =1[i] +2[i])
or (not(1[i] = _) and v =2[i])
or (not(2[i] = _) and v =1[i])

// H1 = A
def H1[i, j] = sum[v : x[i] = _ and v = A[i, j] and x[j] = _]

// H2 = A'
def H2[j, i] = sum[v : x[i] = _ and v = A[i, j] and x[j] = _]

// H = H1 + H2
def H(i, j, v) = (v = H1[i, j] + H2[i, j])
or (not(H1[i, j] = _) and v = H2[i, j])
or (not(H2[i, j] = _) and v = H1[i, j])

Definition

@inline
def jacobian[S, R] = rel_primitive_jacobian[S, R]

int

int[n, v]

Return an n-bit signed integer value from the integer value v.

Definition

@inline
def int = rel_primitive_int

uint

uint[n, v]

Return an n-bit unsigned integer value from the integer value v.

Definition

@inline
def uint = rel_primitive_uint

decimal

decimal[n, digits, v]

Return an n-bit decimal value with digits precision from number value v.

Definition

@inline
def decimal = rel_primitive_decimal

rational

rational[n, num, denom]

Return an n-bit rational value from integer numerator num and denominator denom.

Definition

@inline
def rational = rel_primitive_rational

float

float[n, v]

Return an n-bit floating point value from the number v. Example: float[64, 3.0].

Definition

@inline
def float = rel_primitive_float

int64

int64[v]

Return a 64-bit signed integer value from the integer value v.

Definition

@inline
def int64 = int[64]

int128

int128[v]

Return a 128-bit signed integer value from the integer value v.

Definition

@inline
def int128 = int[128]

uint64

uint64[v]

Return a 64-bit unsigned integer value from the integer value v.

Definition

@inline
def uint64 = uint[64]

uint128

uint128[v]

Return a 128-bit unsigned integer value from the integer value v.

Definition

@inline
def uint128 = uint[128]

float64

float64[v]

Return a 64-bit floating point value from the number v, which must be a float.

Definition

@inline
def float64 = float[64]

numerator

numerator[x]

Numerator of a rational-like number.

Examples:

numerator[rational[64, 1, 3]] = 1
numerator[rational[64, 1, 100]] = 1
numerator[rational[64, 1, 100] + rational[64, 1, 3]] = 103
numerator[parse_decimal[64, 2, "3.14"]] = 314
numerator[parse_decimal[64, 5, "3.14159"]] = 314159
numerator[5] = 5

Definition

@inline
def numerator = rel_primitive_numerator

denominator

denominator[x]

Denominator of a rational-like number.

Examples:

denominator[rational[64, 1, 3]] = 3
denominator[rational[64, 1, 100]] = 100
denominator[rational[64, 1, 100] + rational[64, 1, 3]] = 300
denominator[parse_decimal[64, 2, "3.14"]] = 100
denominator[parse_decimal[64, 5, "3.14159"]] = 10000
denominator[5] = 1

Definition

@inline
def denominator = rel_primitive_denominator

int_float_convert

int_float_convert[x]

Conversion from int to float. Argument must be an int.

Example:

int_float_convert[3] = 3.0
int_float_convert[3.2] : error

Definition

@inline
def int_float_convert = rel_primitive_int_float_convert

float_int_convert

float_int_convert[x]

Conversion from float to int. Gives an error if the argument is not equivalent to an int. (See trunc_to_int, floor_to_int for general conversion.)

Example:

float_int_convert[3.0] = 3
float_int_convert[3.2] : error

Definition

@inline
def float_int_convert = rel_primitive_float_int_convert

rational_convert

rational_convert[n, x]

Convert a number x to a rational with n-bit integer representation.

Any type that supports numerator and denominator is supported.

Examples:

rational_convert[64, 3]  =  (3//1,)
rational_convert[64, decimal[64, 2, 0.75]] = (3//4,)

Definition

@inline
def rational_convert[n, a] =
rational[n, numerator[a], denominator[a]]

Any

Any(x)

Holds for any x

Definition

@inline
def Any(x) = true

Number

Number(x)

Holds if x is a number (for example, Int ,Float, or their fixed-width versions). See also Int and Float in intrinsics.rel.

Definition

@inline
def Number = rel_primitive_Number

Char

Char(x)

Holds if x is a Char

Definition

@inline
def Char = rel_primitive_Char

String

String(x)

Holds if x is a String

Definition

@inline
def String = rel_primitive_String

Missing

Missing(x)

Holds if x is Missing.

Definition

@inline
def Missing = rel_primitive_Missing

Floating

Floating(n, x)

Holds if x is an n-bit floating point number.

Definition

@inline
def Floating = rel_primitive_Floating

UnsignedInt

UnsignedInt(n, x)

Holds if x is an n-bit unsigned integer. Example: UnsignedInt(8, uint[8, 3]) is true.

Definition

@inline
def UnsignedInt = rel_primitive_UnsignedInt

SignedInt

SignedInt(n, x)

Holds if x is an n-bit signed integer. Example: SignedInt(32, int[32, 3]) is true.

Definition

@inline
def SignedInt = rel_primitive_SignedInt

Rational

Rational(n, x)

Holds if x is an n-bit rational.

Definition

@inline
def Rational = rel_primitive_Rational

FixedDecimal

FixedDecimal(n, digits, x)

Holds if x is an n-bit fixed decimal with digits precision.

Definition

@inline
def FixedDecimal = rel_primitive_FixedDecimal

RelName

RelName(x)

Holds if x is a relation name. Example: RelName(:myrel) is true.

Definition

@inline
def RelName = rel_primitive_RelName

Entity

Entity(x)

Test if the given value is an entity.

Definition

@inline
def Entity = rel_primitive_Entity

AutoNumber

AutoNumber(x)

Test if the given value is an AutoNumberValue.

Definition

@inline
def AutoNumber = rel_primitive_AutoNumberValue

Hash

Hash(x)

Test if the given value is a HashValue.

Definition

@inline
def Hash = rel_primitive_HashValue

FilePos

FilePos(x)

Test if the given value is a FilePos.

Definition

@inline
def FilePos = rel_primitive_FilePos

// Temporary alias for backward compatibility
@inline
def delve_period_days = rel_primitive_period_days

// Temporary alias for backward compatibility
@inline
def delve_date_daysinmonth = rel_primitive_date_daysinmonth

int_spread_by_even

int_spread_by_even[R, value]

Spreads value to the entries in the relation R evenly as whole units. If the value to spread = 50 and R has 5 elements, each element will get 10. In case of uneven spread (e.g. spread 51 to 5 elements), the remainder gets distributed as single units in order starting from the first element after the relation has been sorted lexicographically (for details see sort). If value is negative, an empty relation is returned.

Example:

def R = {("Atlanta", "Georgia");
("Seattle", "Washington");
("San Francisco", "California")}

def output = int_spread_by_even[R, 32]
("Atlanta", "Georgia", 11)
("San Francisco", "California", 11)
("Seattle", "Washington", 10)

Definition

@inline
def int_spread_by_even[R, val] = v..., s:
Int(val) and
R(v...) and
val >= 0 and
c = count[R] and
sort[R](i, v...) and
(
(i <= remainder[val, c] and s = trunc_divide[val, c] + 1) or
(i > remainder[val, c] and s = trunc_divide[val, c])
)
from i, c

int_spread_by_ratio

int_spread_by_ratio[R, value]

Spreads value to the entries in the relation R proportionally to their weight (which is the last element in each tuple), as whole units. For example, if the relation is {("X",90); ("Y",10)}, "X" will get 90% of value and "Y" will get 10%. Often, this split will result in a decimal number, but we want to spread whole units. To resolve this issue, we first distribute the floor value of the decimal number computed. The remainder gets distributed as single units in order starting from the element that had the highest decimal value. If value is negative, an empty relation is returned.

Example:

def R = {("Atlanta", "Georgia", 50);
    ("Seattle", "Washington", 80);
    ("San Francisco", "California", 10)}

def output = int_spread_by_ratio[R, 30]
("Atlanta", "Georgia", 11)
("San Francisco", "California", 2)
("Seattle", "Washington", 17)

Definition

@inline
def int_spread_by_ratio[R, val] = v..., s:
Int(val) and
R(v..., w) and
val >= 0 and
forall(t in last[R] : t > 0) and
s = floor_to_int[val * (w / sum[R])] + offset and
sort[(rr, vv...) : rr = remainder[R[vv...] * val, sum[R]]](i, r, v...) and
c = val - sum[vv... : floor_to_int[val * (R[vv...] / sum[R])]] and
(
(offset = 1 and i >= count[R] - c + 1) or
(offset = 0 and i < count[R] - c + 1)
)
from w, i, r, c, offset

FixedDecimal_spread

FixedDecimal_spread[mode, R, val]

This definition uses the two spread strategies implemented in int_spread_by_even and int_spread_by_ratio but specialized to input value that is a FixedDecimal. The major difference here is that in the integer case, the smallest unit to spread was 1, whereas here, the smallest unit to spread is 10^(-d) where d is the number of digits used in the FixedDecimal value val. For example, if we are trying to spread the value 3.140, the smallest unit to spread will be 0.001. mode can be :even or :ratio.

Example:

def R = {("Atlanta", "Georgia", 50);
("Seattle", "Washington", 80);
("San Francisco", "California", 10)}

def output = FixedDecimal_spread[:even, R, decimal[64, 3, 3.14]]
("Atlanta", "Georgia", 1.047)
("San Francisco", "California", 1.047)
("Seattle", "Washington", 1.046)

Definition

@inline
def FixedDecimal_spread[mode, R, val] = v..., s:
rel_primitive_decimal_type(val, decimal_bit_length[val], decimal_precision[val]) and
den = denominator[val] and
digits = floor_to_int[log10[den]] and
valinput = numerator[val] and
(
(mode = :even and s = int_spread_by_even[R, valinput][v...] / den) or
(mode = :ratio and s = int_spread_by_ratio[R, valinput][v...] / den)
)
from valinput, digits, den

spread

spread[mode, R, val]

This definition unifies the spread definitions on integers and FixedDecimal values using the two spreading strategies. Namely, this definition wraps FixedDecimal_spread, int_spread_by_even, and int_spread_by_ratio.

mode can be :even for even spread, or :ratio for ratio spread. R is the relation to execute the spread on. val is the value to spread.

Example:

def R = {("Atlanta", "Georgia", 50);
("Seattle", "Washington", 80);
("San Francisco", "California", 10)}

def out1 = spread[:even, R, decimal[64, 3, 31.0]]
("Atlanta", "Georgia", 50, 10.334)
("San Francisco", "California", 10, 10.333)
("Seattle", "Washington", 80, 10.333)

def out2 = spread[:even, R, 31]
("Atlanta", "Georgia", 50, 11)
("San Francisco", "California", 10, 10)
("Seattle", "Washington", 80, 10)

Definition

@inline
def spread[mode, R, val](v...) =
(
(Int(val) and mode = :even and int_spread_by_even[R, val](v...)) or
(Int(val) and mode = :ratio and int_spread_by_ratio[R, val](v...)) or
(
rel_primitive_decimal_type(val, decimal_bit_length[val], decimal_precision[val]) and
FixedDecimal_spread[mode, R, val](v...)
)
)

formula_card_est

formula_card_est[R]

Return the cardinality estimate of a given relational abstraction R as estimated by the physical query optimizer. The returned estimate is represented as a Float. It is typically an upper bound on the actual cardinality and can suffer from a numeric overflow, e.g. if the formula R is a big cross-product of relations. To avoid this, use the log-version log_card_est[R]. See log_card_est for more details.

Examples:

def card = formula_card_est[R]
def card = formula_card_est[a, b, c: R(a, b) and S(b, c) and T(a, c)]

Definition

@inline
def formula_card_est[R] = 2.0 ^ rel_log_card_est[R]

log_card_est

log_card_est[R]

Return the log base 2 of the cardinality estimate of a given relational abstraction R.

Definition

@inline
def log_card_est[R] = rel_log_card_est[R]