
/* prelude.q: The standard prelude.
   $Id: prelude.q,v 1.48 2007/10/03 10:25:17 agraef Exp $ */

/* 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. */

/* Builtin operators. These are already predeclared by the compiler anyway,
   but we list them here for documentation and sanity-checking purposes. */

private (::||) X Y @0, (::$) X Y @1;
private (::<) X Y @2, (::<=) X Y @2, (::>) X Y @2, (::>=) X Y @2,
  (::=) X Y @2, (::<>) X Y @2;
private special (::==) X Y @2;
private (::++) X Y @3, (::+) X Y @3, (::-) X Y @3,
  (::or) X Y @3;
private special (::or else) ~X Y @3;
private (::*) X Y @4, (::/) X Y @4, (::div) X Y @4, (::mod) X Y @4,
  (::and) X Y @4;
private special (::and then) ~X Y @4;
private (::#) X @5, (::not) X @5;
private (::^) X Y @6, (::!) X Y @6;
private (::.) X Y @7;
private special const (::') X @9;
private (::`) X @9, (::~) X @9, (::&) X @9;

/* Syntactic inequality operator (!=). Note that the syntactic equality
   operator (==) is available as a builtin as of Q 7.7. In difference to the
   eq and neq functions in stdlib.q, these are both implemented as special
   forms. */

public special (!=) X Y @2;

X != Y			= not (X == Y);

/* Include the standard library modules. */

include assert, clib, complex, cond, error, math, rational, sort, stdlib,
  stream, string, tuple, typec;

/* Q 7.8 features a new "slim" prelude, in order to reduce namespace pollution
   and to improve startup times of the interpreter. That is, the system
   interface and the container types have been unbundled from the prelude, and
   must now be imported explicitly. Uncomment the following line to (mostly)
   get back the "fat" prelude from previous releases. */

// include getopt, system, stdtypes;

/* In ancient times (Q <= 2.1), this also used to be part of the prelude. */

// include graphics;

/* Default rules for arithmetic and other numeric functions operating on
   custom types derived from Real. To make this work, the data type must
   provide a definition of `float' so that values of the type can be coerced
   to `Float'. It is then sufficient to only define those operations on the
   type which need special treatment. Note that the data type can still
   override the definitions below, since they are at the lowest priority
   level. */

private custom X;
custom X:Real		= not isint X and then not isfloat X;

@-0x80000000

abs X:Real		= abs X	  where X:Float = float X if custom X;
sgn X:Real		= sgn X   where X:Float = float X if custom X;
sqrt X:Real		= sqrt X  where X:Float = float X if custom X;
exp X:Real		= exp X   where X:Float = float X if custom X;
ln X:Real		= ln X    where X:Float = float X if custom X;
lg X:Real		= lg X    where X:Float = float X if custom X;
log X:Real		= log X   where X:Float = float X if custom X;
sin X:Real		= sin X   where X:Float = float X if custom X;
cos X:Real		= cos X   where X:Float = float X if custom X;
tan X:Real		= tan X   where X:Float = float X if custom X;
asin X:Real		= asin X  where X:Float = float X if custom X;
acos X:Real		= acos X  where X:Float = float X if custom X;
atan X:Real		= atan X  where X:Float = float X if custom X;
atan2 X:Real Y:Real	= atan2 X Y where X:Float = float X, Y:Float = float Y
				    if custom X or else custom Y;
sinh X:Real		= sinh X  where X:Float = float X if custom X;
cosh X:Real		= cosh X  where X:Float = float X if custom X;
tanh X:Real		= tanh X  where X:Float = float X if custom X;
asinh X:Real		= asinh X where X:Float = float X if custom X;
acosh X:Real		= acosh X where X:Float = float X if custom X;
atanh X:Real		= atanh X where X:Float = float X if custom X;
trunc X:Real		= trunc X where X:Float = float X if custom X;
round X:Real		= round X where X:Float = float X if custom X;
int X:Real		= int X   where X:Float = float X if custom X;
frac X:Real		= frac X  where X:Float = float X if custom X;
floor X:Real		= floor X where X:Float = float X if custom X;
ceil X:Real		= ceil X  where X:Float = float X if custom X;

-X:Real			= -X where X:Float = float X if custom X;

X:Real + Y:Real		= X+Y where X:Float = float X, Y:Float = float Y
			      if custom X or else custom Y;
X:Real - Y:Real		= X-Y where X:Float = float X, Y:Float = float Y
			      if custom X or else custom Y;
X:Real * Y:Real		= X*Y where X:Float = float X, Y:Float = float Y
			      if custom X or else custom Y;
X:Real / Y:Real		= X/Y where X:Float = float X, Y:Float = float Y
			      if custom X or else custom Y;
X:Real ^ Y:Int		= X^Y if (X<>0.0) or else (Y<>0.0)
			      where X:Float = float X
			      if custom X or else custom Y;
X:Real ^ Y:Real		= X^Y if (X<>0.0) or else (Y<>0.0)
			      where X:Float = float X, Y:Float = float Y
			      if custom X or else custom Y;

(X:Real = Y:Real)	= (X=Y)  where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;
(X:Real <> Y:Real)	= (X<>Y) where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;
(X:Real < Y:Real)	= (X<Y)  where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;
(X:Real <= Y:Real)	= (X<=Y) where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;
(X:Real > Y:Real)	= (X>Y)  where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;
(X:Real >= Y:Real)	= (X>=Y) where X:Float = float X, Y:Float = float Y
				 if custom X or else custom Y;

@0

/* Tuple equality. */

(() = ())		= true;
(() = (X|Xs))		= false;
((X|Xs) = ())		= false;
((X|Xs) = (Y|Ys))	= (X=Y) and then (Xs=Ys);

() <> ()		= false;
() <> (X|Xs)		= true;
(X|Xs) <> ()		= true;
(X|Xs) <> (Y|Ys)	= (X<>Y) or else (Xs<>Ys);

/* Compare lists and streams lexicographically. */

([] = [])		= true;
([] = [X|Xs])		= false;
([X|Xs] = [])		= false;
([X|Xs] = [Y|Ys])	= (X=Y) and then (Xs=Ys);

[] <> []		= false;
[] <> [X|Xs]		= true;
[X|Xs] <> []		= true;
[X|Xs] <> [Y|Ys]	= (X<>Y) or else (Xs<>Ys);

[] < []			= false;
[] < [X|Xs]		= true;
[X|Xs] < []		= false;
[X|Xs] < [Y|Ys]		= (X<Y) or else (X=Y) and then (Xs<Ys);

[] > []			= false;
[] > [X|Xs]		= false;
[X|Xs] > []		= true;
[X|Xs] > [Y|Ys]		= (X>Y) or else (X=Y) and then (Xs>Ys);

[] <= []		= true;
[] <= [X|Xs]		= true;
[X|Xs] <= []		= false;
[X|Xs] <= [Y|Ys]	= (X<Y) or else (X=Y) and then (Xs<=Ys);

[] >= []		= true;
[] >= [X|Xs]		= false;
[X|Xs] >= []		= true;
[X|Xs] >= [Y|Ys]	= (X>Y) or else (X=Y) and then (Xs>=Ys);

({} = {})		= true;
({} = {_|_})		= false;
({_|_} = {})		= false;
({X|Xs} = {Y|Ys})	= (X=Y) and then (Xs=Ys);

{} <> {}		= false;
{} <> {_|_}		= true;
{_|_} <> {}		= true;
{X|Xs} <> {Y|Ys}	= (X<>Y) or else (Xs<>Ys);

{} < {}			= false;
{} < {_|_}		= true;
{_|_} < {}		= false;
{X|Xs} < {Y|Ys}	= (X<Y) or else (X=Y) and then (Xs<Ys);

{} > {}			= false;
{} > {_|_}		= false;
{_|_} > {}		= true;
{X|Xs} > {Y|Ys}	= (X>Y) or else (X=Y) and then (Xs>Ys);

{} <= {}		= true;
{} <= {_|_}		= true;
{_|_} <= {}		= false;
{X|Xs} <= {Y|Ys}	= (X<Y) or else (X=Y) and then (Xs<=Ys);

{} >= {}		= true;
{} >= {_|_}		= false;
{_|_} >= {}		= true;
{X|Xs} >= {Y|Ys}	= (X>Y) or else (X=Y) and then (Xs>=Ys);

/* Overloaded successor and predecessor functions on integers. */

pred N:Int		= N-1;
succ N:Int		= N+1;

/* Overloaded enumeration operations on numbers. */

enum X:Real Y:Real	= nums X Y;
[X:Real..Y:Real]	= nums X Y;
[X:Real,Y:Real..Z:Real]	= numsby (Y-X) X Z;

/* Tuple enumerations (Q 7.8). These are simply implemented in terms of list
   enumerations for now. */

(X..Y)			= tuple Xs where Xs:List = [X..Y];
(X,Y..Z)		= tuple Xs where Xs:List = [X,Y..Z];
tupleenum X Y		= tuple Xs where Xs:List = enum X Y;

(X..)			= tuple Xs where Xs:List = [X..];
(X,Y..)			= tuple Xs where Xs:List = [X,Y..];
tupleenum_from X	= tuple Xs where Xs:List = enum_from X;

/* Additional list and tuple enumeration operations on characters. These are
   not implemented as builtins and are actually pretty useless because of
   efficiency issues, but we provide them here anyway. */

[X:Char..]		= list {X..};
[X:Char,Y:Char..]	= list {X,Y..};
enum_from X:Char	= list (streamenum_from X);

/* Stream enumeration operations on numbers. */

{X:Real..}		= numstream X;
{X:Real,Y:Real..}	= numstreamby (Y-X) X if X<>Y;
streamenum_from X:Real	= numstream X;

{X:Real..Y:Real}	= takewhile (<=Y) (numstream X);
{X:Real,Y:Real..Z:Real}	= takewhile P (numstreamby D X) if D<>0
			    where D = Y-X, P = if D>0 then (<=Z) else (>=Z);
streamenum X:Real Y:Real= takewhile (<=Y) (numstream X);

/* Stream enumeration operations on characters. */

{X:Char..}		= takewhile ischar (iterate (+1) X);
{X:Char,Y:Char..}	= takewhile ischar (iterate (+D) X) if D<>0
			    where D = Y-X;
streamenum_from X:Char	= takewhile ischar (iterate (+1) X);

{X:Char..Y:Char}	= takewhile (<=Y) Xs
			    where Xs:Stream = {X..};
{X:Char,Y:Char..Z:Char}	= takewhile P Xs where Xs:Stream = {X,Y..} if D<>0
			    where D = Y-X, P = if D>0 then (<=Z) else (>=Z);
streamenum X:Char Y:Char= takewhile (<=Y) Xs
			    where Xs:Stream = streamenum_from X;

/* These will work with arbitrary enumeration types. */

{X..}			= takewhile isconst (iterate (+1) X) if isenum X;
{X,Y..}			= takewhile isconst (iterate (+D) X) if D<>0
			    where D:Int = Y-X if isenum X;
streamenum_from X	= takewhile isconst (iterate (+1) X) if isenum X;

{X..Y}			= takewhile (<=Y) Xs
			    where Xs:Stream = {X..}, _:Int = Y-X;
{X,Y..Z}		= takewhile P Xs where Xs:Stream = {X,Y..} if D<>0
			    where D:Int = Y-X, _:Int = Z-X,
			      P = if D>0 then (<=Z) else (>=Z) if isenum X;
streamenum X Y		= takewhile (<=Y) Xs
			    where Xs:Stream = streamenum_from X, _:Int = Y-X;

/* Lambda expansion. This isn't actually needed for proper operation of the
   lambda function, but is provided for consistency with the other lambda
   binding operations. */

lambdax (\X.Y)		= '~(\X.Y);
