
/* stdlib.q: library of standard Q functions
   $Id: stdlib.q,v 1.17 2007/10/03 10:04:31 agraef Exp $ */

/* originally written by Albert Graef 12-31-1992 */

/* This file is part of the Q programming system.

   The Q programming system is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   The Q programming system is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/* Most of the definitions included here have been adopted from Richard Bird
   and Philip Wadler: "Introduction to Functional Programming", Prentice Hall,
   1988. Note that some of the definitions here are actually overridden by the
   clib module, but they are still in here for historical reasons. */

/* abs. Absolute value of a number: */

public abs X;
abs X:Real		= X if X>=0;
                	= -X otherwise;

/* all. Verifies that all members of a list satisfy a given predicate: */

public all P Xs;
all P []		= true;
all P [X|Xs]		= P X and then all P Xs;

/* any. Verifies that any member of a list satisfies a given predicate: */

public any P Xs;
any P []		= false;
any P [X|Xs]		= P X or else any P Xs;

/* append. Appends an element to a list: */

public append Xs Y;
append [] Y		= [Y];
append [X|Xs] Y		= [X|append Xs Y];

/* cat. Concatenates a list of lists: */

public cat Xs;

/* The naive implementation (takes quadratic time). Never use this. */
//cat Xs:List		= foldl (++) [] Xs;

/* Better: a linear time implementation using recursive list traversal. */
cat []			= [];
cat [[]|Yss]		= cat Yss;
cat [[X|Xs]|Yss]	= [X|cat [Xs|Yss]];

/* cons. Prepends an element to a list: */

public cons X Xs;
cons X Xs:List		= [X|Xs];

/* cst. Constant-valued function: */

public cst X;
cst X _			= X;

/* curry. Convert an uncurried function operating on pairs to a curried
   function with two arguments: */

public curry F;
curry F X Y		= F (X,Y);

/* curry3. Curry with three arguments: */

public curry3 F;
curry3 F X Y Z		= F (X,Y,Z);

/* do. Applies a function to every member of a list, returns (): */

public do F Xs;
do F []			= ();
do F [X|Xs]		= F X || do F Xs;

/* dowith. Takes two lists and applies a binary function to corresponding
   elements, returns (): */

public dowith F Xs Ys;
dowith F [X|Xs] [Y|Ys]	= F X Y || dowith F Xs Ys;
dowith F _:List _:List	= () otherwise;

/* dowith3. Dowith with three lists: */

public dowith3 F Xs Ys Zs;
dowith3 F [X|Xs] [Y|Ys] [Z|Zs]
			= F X Y Z || dowith3 F Xs Ys Zs;
dowith3 F _:List _:List _:List
			= () otherwise;

/* drop. Selects a final segment of a list: */

public drop N Xs;
drop N:Int []		= [];
drop N:Int [X|Xs]	= drop (N-1) Xs if N>0;
			= [X|Xs] otherwise;

/* dropwhile. Removes the longest initial segment of a list all of whose
   elements satisfy a given predicate: */

public dropwhile P Xs;
dropwhile P []          = [];
dropwhile P [X|Xs]      = dropwhile P Xs if P X;
			= [X|Xs] otherwise;

/* eq. Checks whether two terms are syntactically equal: */

public eq X Y;
eq X X			= true;
eq _ _			= false otherwise;

/* filter. Filters a list with a predicate: */

public filter P Xs;
filter P []             = [];
filter P [X|Xs]         = [X|filter P Xs] if P X;
			= filter P Xs otherwise;

/* foldl. Fold-left: */

public foldl F A Xs;
foldl F A []            = A;
foldl F A [X|Xs]        = foldl F (F A X) Xs;

/* foldl1: Fold-left over nonempty lists: */

public foldl1 F Xs;
foldl1 F [X|Xs]         = foldl F X Xs;

/* foldr. Fold-right: */

public foldr F A Xs;
foldr F A []            = A;
foldr F A [X|Xs]        = F X (foldr F A Xs);

/* foldr1. Fold-right over non-empty lists: */

public foldr1 F Xs;
foldr1 F [X]            = X;
foldr1 F [X,Y|Xs]       = F X (foldr1 F [Y|Xs]);

/* hd. Returns the head element of a list: */

public hd Xs;
hd [X|_]               = X;

/* hds. Extracts all head elements from a list of lists: */

public null Xs;

public hds Xss;
hds []			= [];
hds [Xs|Xss]		= hds Xss if null Xs;
			= [hd Xs|hds Xss] otherwise;

/* id. Identity function: */

public id;
id X			= X;

/* init. Returns a list without its last element: */

public init Xs;
init [X]                = [];
init [X|Xs]             = [X|init Xs] otherwise;

/* iter. Constructs the list of the first N values A, F A, F (F A), ... */

public iter N F A;
iter N:Int F A		= [A|iter (N-1) F (F A)] if N>0;
			= [] otherwise;

/* last. Returns the last element of a list: */

public last Xs;
last [X]                = X;
last [X|Xs]             = last Xs otherwise;

/* map. Applies a function to every element of a list: */

public map F Xs;
map F []                = [];
map F [X|Xs]            = [F X|map F Xs];

/* max. Returns the maximum of two values: */

public max X Y;
max X Y                 = X if X>=Y;
                        = Y otherwise;

/* min. Returns the minimum of two values: */

public min X Y;
min X Y                 = X if X<=Y;
                        = Y otherwise;

/* mklist. Creates a list of given length: */

public mklist X N;
mklist X N:Int		= [] if N<=0;
			= [X|mklist X (N-1)] otherwise;

/* neg. Negates a predicate: */

public neg P X;
neg P X			= not P X;
				
/* neq. Checks whether two terms are syntactically inequal: */

public neq X Y;
neq X X			= false;
neq _ _			= true otherwise;

/* null. Tests whether a list is empty: */

public null Xs;
null []			= true;
null _:List		= false otherwise;

/* nums. Generates a list of numbers in a given range: */

public while P F A;

public nums N M;
nums N:Real M:Real	= while (<=M) (+1) N;

/* numsby. Generates a list of numbers with a given step size: */

public numsby K N M;
numsby K:Real N:Real M:Real
			= while (<=M) (+K) N if K>0;
			= while (>=M) (+K) N if K<0;

/* pop. Removes the head element from a list: */

public pop Xs;
pop [_|Xs]		= Xs;

/* prd. Product of a list of numbers: */

public prd Xs;
prd Xs:List		= foldl (*) 1 Xs;

/* push. Prepends an element to a list (cons with arguments reversed): */

public push Xs X;
push Xs:List X		= [X|Xs];

/* reverse. Reverses a list: */

public reverse Xs;
reverse Xs:List		= foldl push [] Xs;

/* scanl. Applies foldl to every initial segment of a list: */

public scanl F A Xs;
scanl F A []		= [A];
scanl F A [X|Xs]	= [A|scanl F (F A X) Xs];

/* scanl1. Applies foldl1 to every nonempty initial segment of a list: */

public scanl1 F Xs;
scanl1 F []		= [];
scanl1 F [X|Xs]		= scanl F X Xs;

/* scanr. Applies foldr to every final segment of a list: */

public scanr F A Xs;
scanr F A []		= [A];
scanr F A [X|Xs]	= [F X Y|Ys] where Ys = scanr F A Xs, [Y|_] = Ys;

/* scanr1. Applies foldr1 to every nonempty final segment of a list: */

public scanr1 F Xs;
scanr1 F []		= [];
scanr1 F [X]		= [X];
scanr1 F [X|Xs]		= [F X Y|Ys] where Ys = scanr1 F Xs, [Y|_] = Ys;

/* sgn. Sign of a number: */

public sgn X;
sgn X:Real		= 1 if X>0;
			= -1 if X<0;
			= 0 otherwise;

/* sum. Sum of a list of numbers: */

public sum Xs;
sum Xs:List		= foldl (+) 0 Xs;

/* take. Selects an initial segment of a list: */

public take N Xs;
take N:Int []		= [];
take N:Int [X|Xs]	= [X|take (N-1) Xs] if N>0;
			= [] otherwise;

/* takewhile. Selects the longest initial segment of a list all of
   whose elements satisfy a given predicate: */

public takewhile P Xs;
takewhile P []          = [];
takewhile P [X|Xs]      = [X|takewhile P Xs] if P X;
                        = [] otherwise;

/* tl. Removes the head element from a list: */

public tl Xs;
tl [_|Xs]               = Xs;

/* tls. Removes all head elements from a list of lists: */

public tls Xss;
tls []			= [];
tls [Xs:List|Xss]	= tls Xss if null Xs;
			= [tl Xs|tls Xss] otherwise;

/* top. Returns the head element of a list: */

public top Xs;
top [X|_]		= X;

/* transpose. Transposes a list of lists: */

public transpose Xss;
transpose []		= [];
transpose [Xs:List|Xss]	= transpose Xss if null Xs;
			= [[hd Xs|hds Xss]|transpose [tl Xs|tls Xss]]
			      otherwise;

/* uncurry. Convert a curried function with two arguments to an uncurried
   function operating on pairs: */

public uncurry F;
uncurry F (X,Y)		= F X Y;

/* uncurry3. Uncurry with triples: */

public uncurry3 F;
uncurry3 F (X,Y,Z)	= F X Y Z;

/* until. Applied to a predicate P, function F and a value X, repeats
   applying F to X until P is satisfied: */

public until P F X;
until P F X             = X if P X;
                        = until P F (F X) otherwise;

/* unzip. Transforms a list of pairs into a pair of lists: */

public unzip Xs;
unzip [(X,Y)|Us]	= ([X|Xs],[Y|Ys]) where (Xs,Ys) = unzip Us;
unzip []		= ([],[]);

/* unzip3. Unzip with triples: */

public unzip3 Xs;
unzip3 [(X,Y,Z)|Us]	= ([X|Xs],[Y|Ys],[Z|Zs]) where (Xs,Ys,Zs) = unzip3 Us;
unzip3 []		= ([],[],[]);

/* while. List values A, F A, F (F A), ... while a given predicate P is
   satisfied: */

public while P F A;
while P F A		= [A|while P F (F A)] if P A;
			= [] otherwise;

/* zip. Takes two lists and returns a list of corresponding pairs: */

public zip Xs Ys;
zip [X|Xs] [Y|Ys]	= [(X,Y)|zip Xs Ys];
zip _:List _:List	= [] otherwise;

/* zip3. Zip with three lists: */

public zip3 Xs Ys Zs;
zip3 [X|Xs] [Y|Ys] [Z|Zs]
			= [(X,Y,Z)|zip3 Xs Ys Zs];
zip3 _:List _:List _:List
			= [] otherwise;

/* zipwith. Takes two lists and maps a binary function to corresponding
   elements: */

public zipwith F Xs Ys;
zipwith F [X|Xs] [Y|Ys] = [F X Y|zipwith F Xs Ys];
zipwith F _:List _:List	= [] otherwise;

/* zipwith3. Zipwith with three lists: */

public zipwith3 F Xs Ys Zs;
zipwith3 F [X|Xs] [Y|Ys] [Z|Zs]
			= [F X Y Z|zipwith3 F Xs Ys Zs];
zipwith3 F _:List _:List _:List
			= [] otherwise;
