# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0.  If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright 1997 - July 2008 CWI, August 2008 - 2016 MonetDB B.V.

# 
module algebra;

# TO BE REMOVED OR CHANGED FOR HEADLESS

command groupby(gids:bat[:oid], cnts:bat[:lng]) :bat[:oid]
address ALGgroupby
comment "Produces a new BAT with groups identified by the head column. The result contains tail times the head value, ie the tail contains the result group sizes.";

#END OF HEADLESS PLANS

command find(b:bat[:any_1], t:any_1):oid
address ALGfind
comment "Returns the index position of a value.  If no such BUN exists return OID-nil." ;

command fetch(b:bat[:any_1], x:oid) :any_1
address ALGfetchoid
comment "Returns the value of the BUN at x-th position with 0 <= x < b.count";

pattern project(b:bat[:any_1],v:any_3) :bat[:any_3]
address ALGprojecttail
comment "Fill the tail with a constant";

command projection( left:bat[:oid], right:bat[:any_3] ) :bat[:any_3]
address ALGprojection
comment "Project left input onto right input.";

# BAT copying
command copy( b:bat[:any_1]) :bat[:any_1]
address ALGcopy
comment "Returns physical copy of a BAT.";

command exist(b:bat[:any_1], val:any_1):bit
address ALGexist
comment "Returns whether 'val' occurs in b.";

# The range selections are targeted at the tail of the BAT.
command subselect(b:bat[:any_1], low:any_1, high:any_1, li:bit, hi:bit, anti:bit) :bat[:oid]
address ALGsubselect1
comment "Select all head values for which the tail value is in range.
	Input is a dense-headed BAT, output is a dense-headed BAT with in
	the tail the head value of the input BAT for which the tail value
	is between the values low and high (inclusive if li respectively
	hi is set).  The output BAT is sorted on the tail value.  If low
	or high is nil, the boundary is not considered (effectively - and
	+ infinity).  If anti is set, the result is the complement.  Nil
	values in the tail are never matched, unless low=nil, high=nil,
	li=1, hi=1, anti=0.  All non-nil values are returned if low=nil,
	high=nil, and li, hi are not both 1, or anti=1.
	Note that the output is suitable as second input for the other
	version of this function.";

command subselect(b:bat[:any_1], s:bat[:oid], low:any_1, high:any_1, li:bit, hi:bit, anti:bit) :bat[:oid]
address ALGsubselect2
comment "Select all head values of the first input BAT for which the tail value
	is in range and for which the head value occurs in the tail of the
	second input BAT.
	The first input is a dense-headed BAT, the second input is a
	dense-headed BAT with sorted tail, output is a dense-headed BAT
	with in the tail the head value of the input BAT for which the
	tail value is between the values low and high (inclusive if li
	respectively hi is set).  The output BAT is sorted on the tail
	value.  If low or high is nil, the boundary is not considered
	(effectively - and + infinity).  If anti is set, the result is the
	complement.  Nil values in the tail are never matched, unless
	low=nil, high=nil, li=1, hi=1, anti=0.  All non-nil values are
	returned if low=nil, high=nil, and li, hi are not both 1, or anti=1.
	Note that the output is suitable as second input for this
	function.";

command thetasubselect(b:bat[:any_1], val:any_1, op:str) :bat[:oid]
address ALGthetasubselect1
comment "Select all head values for which the tail value obeys the relation
	value OP VAL.
	Input is a dense-headed BAT, output is a dense-headed BAT with in
	the tail the head value of the input BAT for which the
	relationship holds.  The output BAT is sorted on the tail value.";

command thetasubselect(b:bat[:any_1], s:bat[:oid], val:any_1, op:str) :bat[:oid]
address ALGthetasubselect2
comment "Select all head values of the first input BAT for which the tail value
	obeys the relation value OP VAL and for which the head value occurs in
	the tail of the second input BAT.
	Input is a dense-headed BAT, output is a dense-headed BAT with in
	the tail the head value of the input BAT for which the
	relationship holds.  The output BAT is sorted on the tail value.";


command selectNotNil(b:bat[:any_2]):bat[:any_2]
address ALGselectNotNil
comment "Select all not-nil values";

command subsort(b:bat[:any_1], reverse:bit, stable:bit) :bat[:any_1]
address ALGsubsort11
comment "Returns a copy of the BAT sorted on tail values.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid])
address ALGsubsort12
comment "Returns a copy of the BAT sorted on tail values and a BAT that
         specifies how the input was reordered.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid], :bat[:oid])
address ALGsubsort13
comment "Returns a copy of the BAT sorted on tail values, a BAT that specifies
         how the input was reordered, and a BAT with group information.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], reverse:bit, stable:bit) :bat[:any_1]
address ALGsubsort21
comment "Returns a copy of the BAT sorted on tail values.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid])
address ALGsubsort22
comment "Returns a copy of the BAT sorted on tail values and a BAT that
         specifies how the input was reordered.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid], :bat[:oid])
address ALGsubsort23
comment "Returns a copy of the BAT sorted on tail values, a BAT that specifies
         how the input was reordered, and a BAT with group information.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], g:bat[:oid], reverse:bit, stable:bit) :bat[:any_1]
address ALGsubsort31
comment "Returns a copy of the BAT sorted on tail values.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], g:bat[:oid], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid])
address ALGsubsort32
comment "Returns a copy of the BAT sorted on tail values and a BAT that
         specifies how the input was reordered.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";
command subsort(b:bat[:any_1], o:bat[:oid], g:bat[:oid], reverse:bit, stable:bit) (:bat[:any_1], :bat[:oid], :bat[:oid])
address ALGsubsort33
comment "Returns a copy of the BAT sorted on tail values, a BAT that specifies
         how the input was reordered, and a BAT with group information.
         The input and output are (must be) dense headed.
         The order is descending if the reverse bit is set.
		 This is a stable sort if the stable bit is set.";

command subunique(b:bat[:any_1], s:bat[:oid]) :bat[:oid]
address ALGsubunique2
comment "Select all unique values from the tail of the first input.
	Input is a dense-headed BAT, the second input is a
	dense-headed BAT with sorted tail, output is a dense-headed
	BAT with in the tail the head value of the input BAT that was
	selected.  The output BAT is sorted on the tail value.  The
	second input BAT is a list of candidates.";
command subunique(b:bat[:any_1]) :bat[:oid]
address ALGsubunique1
comment "Select all unique values from the tail of the input.
	Input is a dense-headed BAT, output is a dense-headed BAT with
	in the tail the head value of the input BAT that was selected.
	The output BAT is sorted on the tail value.";


# @+ Join operations
# The core of every relational engine.
# The join collection provided by the GDK kernel.
command crossproduct( left:bat[:any_1], right:bat[:any_2])
		(l:bat[:oid],r:bat[:oid])
address ALGcrossproduct2
comment "Returns 2 columns with all BUNs, consisting of the head-oids
	  from 'left' and 'right' for which there are BUNs in 'left'
	  and 'right' with equal tails";

command subjoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubjoin
comment "Join";

command subleftjoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubleftjoin
comment "Left join with candidate lists";

command subouterjoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubouterjoin
comment "Left outer join with candidate lists";

command subsemijoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubsemijoin
comment "Semi join with candidate lists";

command subthetajoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],op:int,nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubthetajoin
comment "Theta join with candidate lists";

function subantijoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) (:bat[:oid],:bat[:oid]);
	 # JOIN_NE == -3
	(r1,r2) := subthetajoin(l,r,sl,sr,-3:int,nil_matches,estimate);
	return (r1,r2);
end subantijoin;

command subbandjoin(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],c1:any_1,c2:any_1,li:bit,hi:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubbandjoin
comment "Band join: values in l and r match if r - c1 <[=] l <[=] r + c2";

command subrangejoin(l:bat[:any_1],r1:bat[:any_1],r2:bat[:any_1],sl:bat[:oid],sr:bat[:oid],li:bit,hi:bit,estimate:lng) (:bat[:oid],:bat[:oid])
address ALGsubrangejoin
comment "Range join: values in l and r1/r2 match if r1 <[=] l <[=] r2";

command subdiff(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) :bat[:oid]
address ALGsubdiff
comment "Difference of l and r with candidate lists";

command subinter(l:bat[:any_1],r:bat[:any_1],sl:bat[:oid],sr:bat[:oid],nil_matches:bit,estimate:lng) :bat[:oid]
address ALGsubinter
comment "Intersection of l and r with candidate lists (i.e. half of semi-join)";

# @+ Projection operations
pattern firstn(b:bat[:any], n:lng, asc:bit, distinct:bit) :bat[:oid]
address ALGfirstn
comment "Calculate first N values of B";
pattern firstn(b:bat[:any], s:bat[:oid], n:lng, asc:bit, distinct:bit) :bat[:oid]
address ALGfirstn
comment "Calculate first N values of B with candidate list S";
pattern firstn(b:bat[:any], s:bat[:oid], g:bat[:oid], n:lng, asc:bit, distinct:bit) :bat[:oid]
address ALGfirstn
comment "Calculate first N values of B with candidate list S";
pattern firstn(b:bat[:any], n:lng, asc:bit, distinct:bit) (:bat[:oid],:bat[:oid])
address ALGfirstn
comment "Calculate first N values of B";
pattern firstn(b:bat[:any], s:bat[:oid], n:lng, asc:bit, distinct:bit) (:bat[:oid],:bat[:oid])
address ALGfirstn
comment "Calculate first N values of B with candidate list S";
pattern firstn(b:bat[:any], s:bat[:oid], g:bat[:oid], n:lng, asc:bit, distinct:bit) (:bat[:oid],:bat[:oid])
address ALGfirstn
comment "Calculate first N values of B with candidate list S";

command reuse(b:bat[:any_1]):bat[:any_1]
address ALGreuse
comment "Reuse a temporary BAT if you can. Otherwise,
	allocate enough storage to accept result of an
 	operation (not involving the heap)";

# The second group uses the head to perform the range selection
command slice(b:bat[:any_1], x:oid, y:oid) :bat[:any_1]
address ALGslice_oid
comment "Return the slice based on head oid x till y (exclusive).";

command slice(b:bat[:any_1], x:lng, y:lng) :bat[:any_1]
address ALGslice
comment "Return the slice with the BUNs at position x till y.";

command slice(b:bat[:any_1], x:int, y:int) :bat[:any_1]
address ALGslice_int
comment "Return the slice with the BUNs at position x till y.";

command slice(b:bat[:any_1], x:lng, y:lng) :bat[:any_1]
address ALGslice_lng
comment "Return the slice with the BUNs at position x till y.";

command subslice(b:bat[:any_1], x:lng, y:lng) :bat[:oid]
address ALGsubslice_lng
comment "Return the oids of the slice with the BUNs at position x till y.";

# @+ Common BAT Aggregates
# These operations examine a BAT, and compute some simple aggregate result
# over it.
#  BAT size
module aggr;

command count( b:bat[:any] ) :lng
address ALGcount_bat
comment "Return the current size (in number of elements) in a BAT.";
command count ( b:bat[:any], ignore_nils:bit ) :lng
address ALGcount_nil
comment "Return the number of elements currently in a BAT ignores
		BUNs with nil-tail iff ignore_nils==TRUE.";
command count_no_nil ( b:bat[:any_2]) :lng
address ALGcount_no_nil
comment "Return the number of elements currently
	in a BAT ignoring BUNs with nil-tail";

#  Default Min and Max
# Implementations a generic Min and Max routines get declared first. The
# @emph{min()} and @emph{max()} routines below catch any tail-type.
# The type-specific routines defined later are faster, and will
# override these any implementations.
command cardinality( b:bat[:any_2] ) :lng
address ALGcard
comment "Return the cardinality of the BAT tail values.";
# Implementations a generic Min and Max routines get declared first. The
# @emph{ min()} and @emph{ max()} routines below catch any tail-type.
# The type-specific routines defined later are faster, and will
# override these any implementations.

#SQL uses variable head types
command min(b:bat[:any_2]):any_2
address ALGminany
comment "Return the lowest tail value or nil.";

command max(b:bat[:any_2]):any_2
address ALGmaxany
comment "Return the highest tail value or nil.";

pattern avg (b:bat[:any_2] ) :dbl
address CMDcalcavg
comment "Gives the avg of all tail values";

# Standard deviation
# The standard deviation of a set is the square root of its variance.
# The variance is the sum of squares of the deviation of each value in the set
# from the mean (average) value, divided by the population of the set.
command stdev(b:bat[:any_2]) :dbl
address ALGstdev
comment "Gives the standard deviation of all tail values";
command stdevp(b:bat[:any_2]) :dbl
address ALGstdevp
comment "Gives the standard deviation of all tail values";
command variance(b:bat[:any_2]) :dbl
address ALGvariance
comment "Gives the variance of all tail values";
command variancep(b:bat[:any_2]) :dbl
address ALGvariancep
comment "Gives the variance of all tail values";

