The SELECT. Language

Interpretation and Code Generation

About SELECT.

SELECT. is an esoteric programming language designed in 2012 by Esolang wiki user "Por gammer" (not me!). It is ostensibly a Brainfuck derivative, but in practice it is very different to program. The specification is given on the Esolang wiki, as well as some rules for how to derive the major mathematical operations, a short example, and a proof of Turing-completeness. On this page, you will find a download of a functioning implementation of the language (in the form of an interpreter), documentation for said implementation, and a program for generating SELECT. code from a higher-level language, as well as a long list of complete and functioning example programs.

SELECT. Interpreter

Download Python Source

Download select.py here.

Dependencies

This interpreter depends on the mpmath library (use "easy_install mpmath") and the Python/TK graphics library (see link for installation details). It is written for and tested on Python 2.6. Please let me know if you've proven it functional on or patched it for another version, and I'll include it here.

Usage

python select [-v] <filename> [p=<precision>] [v=<defaultvalue>]

Options:

  • -v specifies verbose output. Among other things, it echoes the value of any pixel drawn via the PRINT. command to the command line.
  • p=<precision> lets you specify the number of decimal digits of precision. The interpreter does not support arbitrary precision, because mpmath does not. Default value is 15.
  • v=<defaultvalue> lets you specify which value unmodified tape cells contain. By default, it uses a random number between 1 and 1.0000001. Be aware that small increases in this value can quickly overflow your precision and result in strange and perplexing behavior.

Extra-specificational features of the interpreter

The most useful feature provided by this interpreter which is not specified in the language spec is the text output granted by the -v option. In addition to echoing PRINT.ed values to the shell (one line per PRINT.), it prints =======Clearing Display======= whenever CLEAR. is issued, allowing you to visually separate outputted numbers.

This interpreter also accepts several commands that the langage does not include. Currently, these are:

  • COLOR. Set the current color according to the value of the current cell like so: For x+yi, red=x/256, green=x%256, blue=y%256.
  • HALT. Stop interpretation immediately (without closing the output canvas window).

Other implementational details

  • The first line of a valid program must contain the string (x,y,z) where x, y, and z are positive integers. The interpreter will create a Tk window x*z by y*z plus window decoration, if any. Yes, it will try to make a window bigger than your screen if you tell it to!
  • The interpreter will allow you to raise zero to a negative power, giving infinity as a result, rather than giving an error. This is an intentional design decision.
  • The interpreter WILL give an error and quit if you try to take a log base one.
  • Although I made it as fast as the libraries on which it depends allow, there's just no getting around the fact that doing hundreds of operations on 100-digit complex numbers is slow. Be patient.

SELECT. Code Generator

Go directly to generator function listing.

Why generate SELECT. code?

SELECT. is a very verbose language, much much more so than Brainfuck (to which the author demeaningly compares it). For example, here is an (original-value-preserving) addition in BF:

[>>+>+<<-]>>>[<<<+>>>-]<<[>+>+<<-]>>[<<+>>-]<

And here is the same thing in SELECT.:

      ADD:
      RIGHT. RIGHT. EXP. LEFT. LEFT. SELECT.
      RIGHT. RIGHT. RIGHT. EXP. LEFT. LEFT. SELECT.
      RIGHT. 
         
         (multiply)
         RIGHT. RIGHT. EXP. LEFT. LEFT. SELECT.
         RIGHT. RIGHT. EXP. LEFT. SELECT.
         RIGHT. LOG. RIGHT. SELECT.
         LEFT. 
      LOG. RIGHT. SELECT.
       
      END ADD

The difference in number of commands is not that great. BF uses 45, SELECT. uses 33, so SELECT. actually comes out ahead. For more complex computations, SELECT. is even better. However, the length of a command in BF is one character, while in SELECT., the average length of a command is SIX characters! Obviously, it could become quite tedious to write SELECT. programs by hand, and this isn't even the best reason to do automatic code generation! The best reason is that writing it by hand isn't very fun, but let's instead discuss a peculiarity of the language that makes it especially hard to hand-implement anything.

FACT: The amount of information on the SELECT. tape must always increase. The tape starts out with nothing but the value k on it, everywhere, but in order to create a different value (other than one), you need two different cells initialized with the same number. Sometimes you even need more than that!

The addition program above changes four cells into new values, and this is minimal (as opposed to BF's two clobbered cells). If you take a pair of identical cells and change one of them into a useful number, you usually cannot change it back using only that pair again. You need another cell with just the right value in it. So another cell gets clobbered. In practice, this means successively changing the k's on the tape into some other value. There is no equivalent to BF's clear routine [-].

The implications are obvious: there is no way, in practice to maintain a modifiable data structure (other than a boolean array) at a fixed position on the tape. Almost all interesting data must be treated as immutable. (One can maintain a fixed boolean array by using i and -i as the alphabet. The CONJ. instruction then serves as a bit flip. These make good choices for booleans because you can enter and maintain a loop from an i but nothing from a -i.) As a result, it becomes extremely helpful to have a collection of algorithms worked out which:

  1. Minimize the number of cells used in calculating a given result
  2. Can place a desired result at a desired or known position
  3. And can guarantee a specific number of tape cells will be clobbered by the calculation, no matter which code branch is taken
  4. All within a system which can automatically track the location of previously derived values and deliver them to the location on the tape where they are required.

Having such a library in place removes some of the SELECT. programmer's mental load. The code generator described and linked below does all of the above while outputting well-commented usefully-indented code which could be manually edited and debugged if desired.

Download Python Source

Download selectcodegen.py here.

Dependencies

Python 2.6. w/ standard libraries.

Usage

python selectcodegen.py <filename>

File format

The file from which code is generated should contain the exact string: ######GENERATION CODE######
That's six (6) # symbols on each side. This string should appear on its own line followed by a valid python program containing a series of function calls to code generators. The first call must be to init() and the last call must be to writetofile(). (Calling init() or writetofile() multiple times in the same file with different target filenames is fine, but every writetofile() should be followed immediately by another init() in order to generate functional code.)

Note that everything from the first occurrence of ######GENERATION CODE###### to the second occurrence of the same (or the end of the file if it is only used once) will be executed and then appended to the generated code file, which thus automatically places the SELECT. code and the code used to generate it in the same file. Thus, each .sel file so created is a valid input to both select.py (as a SELECT. program) and selectcodegen.py (as code to generate itself).

Properties of Generated Code

Unless otherwise stated in a function's documentation, the code generated by this generator is "right-growing," meaning that no function will modify cells to the left of the location of the tape pointer at codepoint where the function begins generating. In addition, no function will modify the cells which contain its arguments. Furthermore, the code generated by a given function will leave the tape pointer no further left than the rightmost cell that code modifies. In the case that the function in question is intended to generate code which computes a function or specific value (which is most of them), the final result of that computation will be under the tape pointer at the codepoint corresponding to the end of the code generated by that function.

To summarize: Unless otherwise stated, no function generates code which modifies the function's arguments, the tape to the left of where it started generating, or the tape to the right of where it finished generating. (startleftgr() and endleftgr() can be wrapped around a code section to make it left-growing instead.)

Functions for Generating Code

The "Tape Requirements/Results" column uses the following notation: k means a cell still in its original state, so k*15 is a sequence of 15 consecutive identical cells. ? is a cell containing any value. x and y are arguments. () is placed around the value which is located at the tape pointer. ' after a value means conjugation. ... means the list continues in different ways subject to arguments.

Function Name Tape Requirements/ Results Description
Code Generation Control Functions
startleftgr()/ endleftgr() N/A All code generated between calls to startleftgr() and endleftgr() will be left-growing instead of the default right-growing. (Namely, LEFT. and RIGHT. commands generated in the code will be swapped.)
turnoff()/ turnon() N/A Stop or start generating code, respectively. Any function calls placed between a turnoff() and a subsequent turnon() will not emit any code. Useful for "commenting out" code you don't wish to generate.
writetofile('name') N/A Writes the generated code to a file named name.sel, appending the program used to generate the code to the end of the file, then resets the code generator.
File Formatting And Decoration
comment('comment') N/A Writes the text 'comment' onto its own line in the generated code (unless stifled by turnoff()). Yes, you can insert your own code this way, but that would not be wise, as it will break things. Besides, you could just do s+='code' and accomplish the same (though turnoff() won't prevent the addition of code added this way).
init(x,y,z) N/A Emits the (x,y,z) string which begins a valid SELECT. program and ensures it's at the beginning of the file (by erasing any code generated before it was called). Should always be the first function called.
upindent()/ downindent() N/A Increases/decreases the indentation level by 3 spaces. Most of the functions below already call these at the beginning/end of their execution, so only use them to indent your own functions.
I/O Code Generators
output() None Draw the current tape cell's value. (Emits a PRINT.)
outputleft(n)/ outputright(n) None Draw n tape cell values beginning with the current cell and moving left or right respectively. Returns tape pointer to original position.
clear() None Clear the output canvas. (Emits a CLEAR.)
color() None Set the current color to the current cell's value. (Emits a COLOR.)
drawdigitXY(x,y) INPUT: (x) k*86
OUTPUT: x 1/2 x* |x| x {...} 1|k (k) (see the documentation for switch() for more detail)
Draw the digit corresponding to the positive real integer under the tapehead at location (x,y) measured from the top left corner of the display.
drawstringliteral(string,x,y,[top=0],[bottom=height],[left=0],[right=width]) INPUT: (k) k*(at most 25*len(string))
OUTPUT: {...} (k)
Draw string to the screen beginning at position (x,y) measured from the top left corner. Wrap (breaking on spaces and hyphens if possible) back to cursorx=left if cursorx would exceed right. If cursory would exceed bottom, wait for user to click before clearing the screen and beginning drawing again from (left,top).
drawletterXY(c,x,y,[simulate=False]) INPUT: (k) {k k? k? k? k?}*(at most 25)
OUTPUT: differentnumbers*(at most 125) (k)
Draw the printable ASCII character c to the position (x,y) measured from the top left corner of the display.
drawletter(c) INPUT: input: (x+yi) {k k k k k k? k? k? k?}*(at most 25)
OUTPUT: x+yi {? ?? ?? ?? ?? x+yi k^(?) k^(x+yi) (x+?)+(y+?)i} (k)
Draw the printable ASCII character c to the position (x,y) measured from the origin (where said location was read from the tape). THIS FUNCTIONALITY IS NOT YET IMPLEMENTED.
Common Actions
left(n) INPUT: (?)
OUTPUT: (?) ... (n-2 ?'s) ... ?
Move the tape pointer n cells left. (Emits n LEFT.s)
right(n) INPUT: (?)
OUTPUT: ? ... (n-2 `?'s) ... (?)
Move the tape pointer n cells right. (Emits n RIGHT.s)
go(n) See above. Move the tape pointer |n| cells according to the sign of n. Equivalent to right(n) for positive n and left(-n) for negative n.
exptarget(n) INPUT: y ... (x) OR (x) ... y
OUTPUT: (y) ... x^y OR x^y ... (y)
Raise the current cell to the power in the cell in position n relative to the current cell, leaving the tape pointer on the power. (This is an exception to the "right-growing" contract.) Emits an EXP., the result of go(n), and a SELECT.
logtarget(n) INPUT: y ... (x) OR (x) ... y
OUTPUT: (y) ... log_y(x) OR log_y(x) ... (y)
Take the log of the current cell with the base in the cell in position n relative to the current cell, leaving the tape pointer on the base. (This is an exception to the "right-growing" contract.) Analogous to exptarget(n) but with a LOG. instead.
copyfrom(n) INPUT: x ... (k) k OR (k) k ... x
OUTPUT: x ... (x) k OR (x) k ... x
Copy the contents of the cell in position n relative to the current position into the current cell.
var(name) N/A Associates the current cell position with the name name for use with fetch() and getoffset().
fetch('name') INPUT: 'name'=x ... (k) k OR (k) k ... 'name'=x
OUTPUT: 'name'=x ... (x) k OR (x) k ... 'name'=x
Copy the contents of the cell named name to the current cell. Equivalent to copyfrom(getoffset('name')) and therefore subject to the caveats of getoffset().
getoffset(['name']) N/A Returns the offset between the current cell and the cell named name in such a way that go(getoffset('name')) would move the tape pointer to that cell. If called without an argument, returns the current position of the tape pointer. Note that this function ignores the potential movement of the tape pointer due to looping and branching, and should therefore only be used to calculate offsets WITHIN a certain loop/branch unless the user can make certain guarantees.
halt() None Emits a HALT.
Loops
loop('name',start,end,[step=1],[computei=False],[savelist=None]) INPUT: between 20 and 33 k's depending on arguments
OUTPUT: 1/sqrt(2) -1 1/2 i -1 k^i k^-1 -1+i (-1+i)/sqrt(2) -1 1/n 1/2 i^(1/(2n)) k k*i^(1/(2n)) (sentinel) {k}...
... 1/2 sentinel' sentinel ... when computei=True and...
... (i) k ...step=1 or
... {loopcount step} ... ...step>1 and...
... (i) k ...start=0
... loopcount*step start k^(loopcount*step) k^start (i) k ...start>0
... loopcount*step -1 -start start loopcount*step k^start k^loopcount*step (i) k...start<0
Start a loop called name that will loop from start to end in increments of step. If computei is True, then the tape pointer will be left on a cell containing the current iteration count at the beginning of each loop (which corresponds to the code point immediately following this function). Due to the impossibility of the fetch function knowing how to fetch variables from within a loop, this function accepts a savelist which contains a list of variable names which were previously saved via var(). All variables on this list will be copied forward in each iteration so that fetch() will succeed. All other variables will be forgotten as "lost".
endloop('name',[computei=False],[start=0]) INPUT: between 4 and 17 k's depending on arguments.
OUTPUT: oldsentinel step (newsentinel) {k}...
...1/2 sentinel' loopcount step... when computei=True and...
...(i) k ...start=0
...loopcount*step start k^(loopcount*step) k^start (i) k ...start>0
...loopcount*step 2 -1 -start start loopcount*step k^start k^loopcount*step (i) k ...start<0
End the loop named name. If computei is True, will leave the final iteration count under the tapehead at the codepoint following this function. If a non-zero value of start is given, this will be added to the iteration count so computed. If the value of start used here matches the value in the loop() call for this loop, the computed value will be equal to the value of end used in that call. If that is what's needed, it's faster to follow the endloop() with a call to makenum(end). Not ending a loop with this function will result in a syntax error.
repeat([savelist=None]) INPUT: {?}
OUTPUT: ...{k}
Start a while loop. This uses the built-in LOOP. command directly, not assuming any responsibility for checking and modifying a conditional, but will copy forward the cells on savelist in the same way loop() does.
endrepeat([savelist=None]) INPUT: ...{?}...
OUTPUT: ...{?}... OR ...{k}...
End a while loop. This uses the built-in END. command directly, not assuming any responsibility for checking and modifying a conditional, but will copy forward the cells on savelist in the same way loop() does. If the savelist is used, the data pointer will land on an empty cell afterwards.
Conditionals
ifnonpositive('name')/ els('name',[copy=True])/ endif('name') INPUT: (x) k {k...} k k OR (x) {...} k k k(depending on branch taken)
OUTPUT: x {...} k 1 (k) OR x 0 x {...} (k)
If the cell value under the tape pointer is a non-positive real number, perform the code generated between this function and the corresponding els('name'). Otherwise, perform the code between the els('name') and the endif('name'). The copy argument to els() will, if False, prevent the argument x from appearing under the tapehead at the beginning of the second branch. This saves some cycles if this value is not needed in the else branch. Also, note that not including the endif() will generate code which causes a syntax error. It is advisable that one use ifzero() instead to branch on complex numbers, as ifnonpositive() behaves identically with LOOP. in that it compares the real and imaginary parts of the argument.
ifzero('name')/ els('name',[copy=True])/ endif('name') INPUT: (x) k k k {...} k k k OR (x) k k k k {...} k k(depending on branch taken)
OUTPUT: x 1/2 x* |x| {...} k 1 (k) OR x 1/2 x* |x| 0 x {...} (k)
If the magnitude of the cell value under the tape point is zero, perform the code generated between this function and the corresponding els('name'). Otherwise, perfrorm the code between the els('name') and the endif('name'). This is equivalent to calling abs() before using ifnonpositive().
switch('name',n,f,[fetchx=False]) INPUT: (x) k*(10+f's max tape usage)
OUTPUT: x=0: x 1/2 x* |x| x {...} k 1 (k)
x<n: x 1/2 x' |x| x 1/x -1 2^(1/x) i^(1/4) {...} 1 (k) else: x 1/2 x' |x| x 1/x -1 2^(1/x) fin^2 {...} k (k) where fin=i^(2^((n-1)/x-2))
Generates code which tests whether x is each of the values 0 through n-1 and performs only the code that would be generated by calling f(x), or by f(n) if x>=n. If fetchx is set to true, it copies the value of x to the location of the tape at the point f(x) is called, in case the code generated by f requires that value to be on the tape.
padpoint(n,'name') INPUT: See description.
OUTPUT: See description.
padpoint(1,'name') is intended to be placed somewhere between an ifzero('name') or ifnonpositive('name') and the corresponding els('name'), while padpoint(2,'name') goes between els('name') and endif('name'). padpoint(n,'name') should be called somewhere within a function f passed to switch('name') when n has been passed to that function. These functions mark the position at which one would like a series of LEFT. or RIGHT. commands automatically inserted in order to ensure that both branches of the conditional move the tape pointer the same amount. If padpoint() is not called in some branch, the insertion point for that branch will be assumed to be the code point immediately before the branch ends.
Logic
lnot() INPUT: (x) k
OUTPUT: x (~x)
Computes the univariate Kronecker delta function of a real-valued argument, thereby serving as logical NOT for the purposes of the above conditionals.
tobit() INPUT: (x) k k k k k k
OUTPUT: x 1/2 x' x |x| ~|x| (~~|x|)
Converts any complex number but zero into the number one.
land([target1=1],[target2=0]) INPUT: (x) y k k OR x ... (y) k k OR x ... y ... (k) k OR any horizontal reflection of these.
OUTPUT: x y (x&y) k OR x ... y (x&y) k OR x ... y ... (x&y) OR any reflection of these, respectively.
Equivalent to multiply([target1],[target2]), but adds comments indicating that it is being used as a logical AND.
lor([target=1]) INPUT: (x) y k k k k OR x ... (y) k k k k OR a horizontal reflection of these
OUTPUT: x y x?k:1 y?k:1 (x|y) k OR x ... y x?k:1 y?k:1 (x|y) k OR a horizontal reflection of these, respectively.
Equivalent to add([target]), but adds comments indicating that it is being used as a logical OR.
Constants
makenum([real=0],[imag=0]) INPUT: k for 1, k*2 for non-negative real or -1, k*3 for negative real. Add k*2 for any of these times i, and an additional k for a complex.
OUTPUT: (1) OR (real) k OR -1 (real) k OR 1/2 i (imag*i) k OR 1/2 i -1 (imag*i) k OR 1/2 i k^(imag*i) (real+imag*i) k OR 1/2 i k^(imag*i) -1 (real+imag*i) k OR 1/2 i -1 k^(imag*i) (real+imag*i) k OR 1/2 i -1 k^(imag*i) -1 (real+imag*i) k
Arguments must be integers. Just constructs the desired Gaussian integer on the tape.
makehalf() INPUT: (k) k
OUTPUT: (1/2) k
Constructs the number 1/2 with no overhead.
makeone() INPUT: (k)
OUTPUT: (1)
Equivalent to makenum(1).
makezero() INPUT: (k) k
OUTPUT: (0) k
Equivalent to makenum(0).
makeroot2() INPUT: (k) k k
OUTPUT: 1/2 (sqrt(2)) k
Constructs the square root of 2 on the tape.
makehalfroot2() INPUT: (k) k
OUTPUT: (1/sqrt(2)) k
Constructs the reciprocal of the square root of 2 on the tape.
makei() INPUT: (k) k k
OUTPUT: 1/2 (i) k
Constructs the imaginary unit i. Equivalent to makenum(0,1).
makeneg1() INPUT: (k) k
OUTPUT: (-1) k
Constructs the number -1. Equivalent to makenum(-1).
makee() INPUT: (k) k k k k k k
OUTPUT: 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) (e) k
Constructs the natural base e (approximately).
maketaufourth() INPUT: k*12
OUTPUT: 1/2 logK(i) 3.09485e+26 2 i 3.23117e-27 k^(3.23117e-27) 1/logK(e) tau/4i -tau/4i (tau/4) k
Constructs the number ⲧ/4, also known as π/2.
maketauhalf() INPUT: k*13
OUTPUT: 1/2 logK(i) 3.09485e+26 2 i 3.23117e-27 k^(3.23117e-27) tau/4i tau/4 (tau/2) k
Constructs the number ⲧ/4, also known as π.
maketau() INPUT: k*14
OUTPUT: 1/2 logK(i) 3.09485e+26 2 i 3.23117e-27 k^(3.23117e-27) tau/4i tau/4 tau/2 (tau) k
Constructs the number ⲧ, also known as 2π.
Mathematical Functions
multiply([target1=1],[target2=0]) INPUT: (x) y k k OR x ... (y) k k OR x ... y ... (k) k OR any horizontal reflection of these
OUTPUT: x y (x*y) k OR x ... y (x*y) k OR x ... y ... (x*y) OR any reflection of these, respectively.
With no arguments, it multiplies the current cell with the one to its right, storing the result two cells to the right. With one argument set to any value other than 1, it multiplies the current cell with the located at the given offset relevant to the current cell and stores the result one cell to the right. With two arguments (neither being zero), it multiplies the values in the two cells at the specified offset relative to the current cell and stores the result in the current cell.
square([target=0]) INPUT: (x) k k OR x ... (k) k OR (k) k ... x
OUTPUT: x x^2 k OR x ... (x^2) k OR (x^2) k ... x
With no arguments, square the current cell, storing the result one cell right. With one argument, squares the cell specified by the given offset relative to the current cell and stores the result in the current cell.
inc() INPUT: (x) k k
OUTPUT: x (x+1) k
Increment the current cell, storing the result one cell to the right.
dec() INPUT: (x) k k k
OUTPUT: x x^-2 (x-1) k
Decrement the current cell, storing the result two cells to the right.
reciprocal() INPUT: (x) k k
OUTPUT: x (1/x) k
Take the reciprocal of the current cell, storing the result one cell to the right. This method should only be used when one can guarantee the argument will not be 1. Otherwise, use makeneg1() with exptarget().
opposite() INPUT: (x) k k
OUTPUT: x (-x) k
Put the opposite of the current cell in the cell to the right.
add([target=1]) INPUT: (x) y k k k k OR (x) k k k k ... y OR y ... (x) k k k k
OUTPUT: x y k^x k^y (x+y) k OR x k^x k^y (x+y) k ... y OR y ... x k^x k^y (x+y) k
With no arguments, adds the current cell with the one to the right, and stores the result 4 cells to the right. With 1 argument (not in [1,4]), adds the current cell with the one specified by the given offset relative to the current cell and stores the result 3 cells to the right.
conj() INPUT: (x)
OUTPUT: (x')
Emits a CONJ.
ln([clobber=False]) INPUT: (x) 9*k or (x) 8*k if the argument is True.
OUTPUT: x logK(x) 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) logK(e)^-1 (ln(x)) k OR omit the initial x if the argument is True.
Computes the natural log of the current cell. Clobbers the argument with its log base k if the argument is true, saving one tape cell for the computation, though this breaks the contract of immutable arguments stated above.
absval() INPUT: (x) k*4
OUTPUT: x 1/2 x' (|x|) k
Computes the magnitude of the current cell.
re() INPUT: (x) k*8
OUTPUT: x x' k^x k^x' 2Re(x) 1/2 (Re(x)) k
Computes the real part of the current cell.
im() INPUT: (x) k*13
OUTPUT: x 2 1/2 -1 i*x -i*x (-i*x)' k^(-i*x) k^(-i*x)' 2Re(-i*x) 1/2 2Re(-i*x) (Im(x)) k
Computes the imaginary part of the current cell (the real coefficient of i).
arg() INPUT: (x) k*14
OUTPUT: x 1/2 x' 1/|x| -i logK(x/|x|) 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) 1/logK(e) ln(x/|x|) (arg(x)) k
Computes the argument (phase) of the current cell.
sin([hyperbolic=False]) INPUT: (x) k*17 (16 for hyperbolic)
OUTPUT: x 1/2 i i*x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(i*x) -e^(i*x) e^(-i*x) k^(-e^(i*x)) k^(e^(-i*x)) 2*i*sin(x) -i*sin(x) (sin(x)) k OR x 1/2 -1 -x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(-x) -e^(-x) e^(x) k^(-e^(-x)) k^(e^(x)) 2*i*sin(ix) (sinh(x)) k
Computes the (complex) sine of the current cell, or the hyperbolic sine if the argument is True.
cos([hyperbolic=False]) INPUT: (x) k*16
OUTPUT: x 1/2 i i*x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(i*x) e^(-i*x) k^(e^(i*x)) k^(e^(-i*x)) 2*cos(x) (cos(x)) k OR x k -1 -x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(-x) e^(x) k^(e^(-x)) k^(e^(x)) 2*cosh(x) (cosh(x)) k
Computes the (complex) cosine of the current cell, or the hyperbolic cosine if the argument is True.
tan([hyperbolic=False]) INPUT: (x) k*18 (17 for hyperbolic)
OUTPUT: x 1/2 i i*x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(-2*i*x) (-e^(-2*i*x)) k^(e^(-2*i*x)) k^(-e^(-2*i*x)) 1-e^(-2*i*x) (1+e^(-2*i*x))^-1 i*tan(x) -i*tan(x) (tan(x)) k OR x k -1 -x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) e^(2*x) (-e^(2*x)) k^(e^(2*x)) k^(-e^(2*x)) 1-e^(2*x) (1+e^(2*x))^-1 i*tan(i*x) (tanh(x)) k
Computes the (complex) tangent of the current cell, or the hyperbolic tangent if the argument is True.
asin([hyperbolic=False]) INPUT: (x) k*20 (19 for hyperbolic)
OUTPUT: x 1/2 i x^2 k^(-x^2) (1-x^2)^(1/2) i i*x k^(i*x) k^((1-x^2)^(1/2)) (1-x^2)^(1/2)+i*x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) logK(e)^-1 i*asin(x) -i*asin(x) (asin(x)) k OR x 1/2 i x^2 k^(x^2) (1+x^2)^(1/2) -1 -x k^(-x) k^((1+x^2)^(1/2)) (1+x^2)^(1/2)-x 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) logK(e)^-1 -arsinh(x) (arsinh(x)) k
Computes the (complex) inverse sine of the current cell, or the inverse hyperbolic sine if the argument is True.
acos([hyperbolic=False]) INPUT: (x) k*19 (18 for hyperbolic)
OUTPUT: x 1/2 i x^2 k^(-x^2) (1-x^2)^(1/2) (1-x^2)^(1/2)*i k^((1-x^2)^(1/2)*i) k^x logK((1-x^2)^(1/2)*i+x) 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) logK(e)^-1 i*acos(x) -i*acos(x) (acos(x)) k OR x 1/2 i x^2 k^(-x^2) (1-x^2)^(1/2) (1-x^2)^(1/2)*i k^((1-x^2)^(1/2)*i) k^x logK((1-x^2)^(1/2)*i+x) 3.09485e+26 2 -1 3.23117e-27 k^(3.23117e-27) logK(e)^-1 -arcosh(x) (arcosh(x)) k
Computes the (complex) inverse cosine of the current cell, or the inverse hyperbolic cosine if the argument is True.
atan([hyperbolic=False]) INPUT:
OUTPUT:
Computes the (complex) inverse tangent of the current cell, or the inverse hyperbolic tangent if the argument is True. THIS FUNCTIONALITY IS NOT YET IMPLEMENTED.
atan2() INPUT: (x) k*28
OUTPUT:
Computes the two-parameter (complex) inverse tangent. Note that argument order is y x. THIS FUNCTIONALITY IS NOT YET IMPLEMENTED.

Example SELECT. Programs

Using the above-described code generator, I have written a number of example programs to show test the features of both the interpreter and code generator. Many of them do not include the code that generated them. Of particular interest are "circle.sel" which demonstrates bounded loops by drawing a circle, and "fulltest.sel" which tests all of the other functions in the code generator library, including every elementary function. These examples may also include works-in-progress or non-working code. Try them and find out!

Example Repository

Explore the example repository.