Oblivion
0 of 0

File information

Last updated

Original upload

Created by

TesaPlus

Uploaded by

TesaPlus

Virus scan

Safe to use

Documentation

Readme

View as plain text

Little Coda Toolbox
1 Copying
2 Introduction
2.1 About Coda
2.2 About The Little Coda Toolbox
2.3 Compatibility
2.4 Words of Wisdom
3 Conventions
4 Array Functions
4.1 Creation
4.1.1 ar.0
4.1.2 ar.Copy
4.1.3 ar.Enum
4.1.4 ar.Fill
4.1.5 ar.Make
4.1.6 ar.Random
4.2 Alteration
4.2.1 ar.Append
4.2.2 ar.Cut
4.2.3 ar.Flatten
4.2.4 ar.Join
4.2.5 ar.Push
4.3 Mapping and Filtering
4.3.1 ar.Map
4.3.2 ar.Filter
4.3.3 ar.Split
4.4 Introspection
4.4.1 ar.Nth
4.4.2 ar.GetIndex
4.4.3 ar.IsMember
4.4.4 ar.Census
4.4.5 ar.ShowStructure
4.5 Output
4.5.1 ar.Print
4.5.2 ar.PrintFmtNum
4.5.3 ar.Save
4.6 Association List
4.6.1 ar.assoc.Argv
4.6.2 ar.assoc.GetKeys
4.6.3 ar.assoc.Init
4.6.4 ar.assoc.Exists
4.6.5 ar.assoc.Lookup
4.6.6 ar.assoc.Pop
4.6.7 ar.assoc.Push
4.6.8 ar.assoc.Print
4.7 Reference Array
4.7.1 ar.ref.Make
4.7.2 ar.ref.Erase
4.7.3 ar.ref.GetIndex
4.7.4 ar.ref.GetIntersection
4.7.5 ar.ref.Join
4.7.6 ar.ref.Print
4.8 Rubber Array
4.8.1 ar.rubber.Make
4.8.2 ar.rubber.Get
4.8.3 ar.rubber.Set
4.8.4 ar.rubber.GetContent
4.8.5 ar.rubber.GetExtent
4.8.6 ar.rubber.Copy
4.8.7 ar.rubber.Merge
4.8.8 ar.rubber.Remove
4.8.9 ar.rubber.Invalid
4.9 Sorting
4.9.1 ar.Sort
4.9.2 ar.sort.Multi
4.9.3 ar.sort.Generic
5 Library Functions
5.1 Background Scripts
5.1.1 lib/Backgrounders/FloorArray
5.2 Comparisons
5.2.1 lib.comp.Compare
5.2.2 lib.comp.EidCompare
5.3 Predicates
5.3.1 lib.pred.CellEmpty
5.3.2 lib.pred.BFEidSubStr
5.3.3 lib.pred.Equal
5.3.4 lib.pred.hasBF
5.3.5 lib.pred.LandscapeTextureSubstringAt
5.3.6 lib.pred.MaxDistance
5.3.7 lib.pred.Negative
5.3.8 lib.pred.parentWorldspace
5.3.9 lib.pred.PersistentWorldspaceCell
5.3.10 lib.pred.sameBF
5.3.11 lib.pred.sameEID
5.3.12 lib.pred.sameWorldspace
5.4 Graphs
5.4.1 lib.graph.Make
5.4.2 lib.graph.SubGraphs
5.5 Map
5.5.1 lib.map.Make
5.5.2 lib.map.Get
5.5.3 lib.map.Copy
5.5.4 lib.map.SubMap
5.5.5 lib.map.GetCells
5.5.6 lib.map.GetExtent
5.5.7 lib.map.GetHood
5.5.8 lib.map.Join
5.5.9 lib.map.Remove
5.5.10 lib.map.Merge
5.5.11 lib.map.Print
5.5.12 lib.map.Save
5.5.13 lib.map.Equal
5.5.14 lib.map.Invalid
5.6 Pixmaps
5.6.1 lib.pixmap.Make
5.6.2 lib.pixmap.Draw
5.6.3 lib.pixmap.GetColorAt
5.6.4 lib.pixmap.Transpose
5.7 Formatting Output
5.7.1 lib.Fmt
5.7.2 lib.fmt.Array
5.7.3 lib.fmt.RubberArray
5.7.4 lib.fmt.Cell
5.7.5 lib.fmt.Concat
5.7.6 lib.fmt.Ints
5.7.7 lib.fmt.Ref
5.7.8 lib.fmt.Time
5.8 Utilities
5.8.1 lib.util.CheckThis
5.8.2 lib.util.CopyRef
5.8.3 lib.util.Div
5.8.4 lib.util.FTID
5.8.5 lib.util.GetAllCellsInRect
5.8.6 lib.util.GetCoordinates
5.8.7 lib.util.GetEditorID
5.8.8 lib.util.GetRefBaseForm
5.8.9 lib.util.makeMapFromLandscapeTextures
5.8.10 lib.util.RefDistance
5.8.11 lib.util.RotateVector
5.8.12 lib.util.Wiggle
5.9 Strings
5.9.1 lib.string.Match
5.9.2 lib.string.Replace
5.9.3 lib.string.Reverse
General Index
Functions
Destructive Functions
Little Coda Toolbox
*******************

This manual is for Little Coda Toolbox (version 2.0).

1 Copying
*********

The "Little Coda Toolbox" is the work of TesaPlus on nexusmods.com.
Used sources are:

* Wikipedia for pseudo code for shell sort. License: Creative
Commons Attribution-ShareAlike 3.0, see
. Affected are
'ar.sort.Shellsort/Generic/Strings' (*note ar.Sort::).
* opendatastructure.org for radix sort pseudo code. License:
Creative Commons Attribution 2.5 Canada, see
. Affected is
'ar.sort.Radix', *note ar.Sort::.

As I understand, of these two, the Creative Commons
Attribution-ShareAlike 3.0 is the most restrictive, and therefore is the
one that the Little Coda Toolbox inherits. There you have it.

2 Introduction
**************

"Coda" is a Scripting Language, part of the Construction Set Extender.
It allows for the automatisation of tedious / time-consuming /
mind-numbing edits.

This "Little Coda Toolbox" aims to facilitate the every-modding-day
dealings with The Construction Set Extender / Coda.

2.1 About Coda
==============

This is an excerpt of the very beginning of Coda's documentation.
ShadeMe speaking:

"Coda, simply put, is a powerful scripting language that all Bethesda
Game Studios Editor Extender plugins implement. It’s primarily used to
automate operations inside an editor environment. Tedious and
monotonous tasks like renaming objects, renumbering form IDs, batch
editing object attributes, etc, can be completed with just a few lines
of code."

2.2 About The Little Coda Toolbox
=================================

Coda's only compound data structure is the array. Now, this our "Little
Coda Toolbox" is a library whose main purpose is to provide for

* a nice set of common _array handling functions_, thus freeing the
modder from the perils of index-fiddling.

* a handful of _output functions_ for arrays, useful for debugging
and reporting.

Plus a couple of handy data types and routines:

* Graphs (think "Links" and "Edges"). Dead useful for structuring
objects in groups.(1) (*note Graphs::.)

* Maps. Grab cells by their coordinates. (*note Map::.)

* Pixmaps. Place patterns of objects. (*note Pixmaps::.)

---------- Footnotes ----------

(1) Actually, my interest with Coda started with these. While
exploring Coda and debugging my scripts, I felt the need of a trusty
library.

2.3 Compatibility
=================

This library does not work with Construction Set Extender versions prior
to 8.0. There is still the Little Coda Toolbox, version 1.0, that works
with old CSEs.

2.4 Words of Wisdom
===================

* New in CSE 8: Coda scripts are now compiled to byte-code! This
byte-code is then being held available for usage in a buffer. CSE
checks whether the top-level script has been changed since the last
run, and recompiles it. This is not the case for all the functions
your script might call! Therefore, when your Coda program
stubbornly refuses to do what you have just told it to do: -
perhaps you are running the same old buffered script? Select
'CSE-Menu / Coda / Reset Script Cache'.

I've made a habit out of it: Before typing "runcodascript FOOBAR
0", I select "Reset Script Cache."

* CSE's console window, when getting overloaded with messages to
print, gets updated only after the fact. Here's how to monitor
CSE's console output during execution, _if, and only if, you have
installed Microsoft's official "Windows Subsystem for Linux"
command line extension_: Open a Windows command window, 'cmd.exe',
and type 'tail -f "d:\Oblivion\Construction Set Extender.log"' (or
whereever that file is).

By the way, this extension only _adds_, and doesn't take away from
you (dl it already, dammit!).

* 'MarkAsModified'. After changing a game object, don't forget to
mark it as modified. Or weird things might happen, like this: I
copied a new plant over the original one, set the latter as
initially disabled. It showed in the CS as initially disabled; all
seemed well. But in the game, both plants appeared, which resulted
in flickering. Marking the disabled plant also as modified solved
the problem.

* When comparing bit masks, cast both sides to 'REF'!

* When dealing with references, cast them, just to play it safe, to
'(ref)thing'. I have aquired a custom to _always_ do a '(ref)'
when speaking about references.

A typical error message by Coda would be "expected 'r' but gor
'n"'; something like that.

* Massive use of 'Call()' may slow down your program considerably.
On my machine, a 'Call()' costs about six micro seconds. That's
about seventy times faster than what it was in CSE 7, but still
adds up when dealing with Little Big Data, like, filtering tons of
objects, while checking thousands of cells.

A few functions have been prepared for inlining (by using prefixed
variables, nothing more).

* Semicolons in strings. You may have never come across this bug,
and, maybe, never will.

_If you do, remember this._

There seems to be a bug in the Coda parser. On rare occasions, it
might complain about an unterminated string, where there clearly is
a properly formed one (Emacs and coda-mode agree, and they never
err)(1). There may be, however, a semicolon inside the string,
that presumably causes the parser to start a comment, and thus miss
the closing '"'. This bug has been recognised and fixed for
variable declarations (before 'begin'). In the actual program,
however, it is hideously elusive. A program that compiled just
fine, may, one innocent edit later, break with an unterminated
string error. Trust me: Remove that semicolon, and all is well
again. Concatenating 'var sc = ";"' helps.

But I have not been able to nail the problem and write a concise
script exposing it.

* ...

* (wit's end)

---------- Footnotes ----------

(1) That's a joke, but still true: I've come to totally rely on
coda-mode.

3 Conventions
*************

Naming Conventions
..................

* "Boolean values" are the number zero for "False" and anything
non-zero for "True". Throughout this library, we never check
against '#1' as "True"; we *always* check against '#0' as "False."
Nevertheless, in code, we likely indicate that we think of a value
as boolean by using '#0, #1'.

* Boolean functions a.k.a. Predicates: "Predicates" are functions
that return boolean values in the above sense. You should take
this into account when passing your own predicates to functions of
this library.

* Comparison functions: Those are required to return
* a number <0 for 'lower than'

* a number >0 for 'greater than'

* the number 'zero' for equality

* Never should your code expect to get an exact '-1' for 'lower
than' etc.

* Some functions take another user-defined function as argument, in
form of a string, e.g. '@ar.Filter1(aArray,
"\\fictitious\\Predicate", aArg)'. In the function declaration, we
call such a string "function," (*note ar.Filter::). Oh, and you
have to double those backslashes.

* ...

Calling Conventions
...................

With CSE 8.0 came the ablity to structure Coda programs and libraries in
directories, and we can use this unified path notation with the curl and
the dots. We can now interchangeably write
@ar.ref.Join(refs, x)
Call("ar\\ref\\Join", refs, x)
Of course we use the elegant new one most of the time. Functions
given as arguments, however, still need to be provided as strings. Oh,
and you have to double those backslashes.

We (strife to always) write the directory parts in lowercase letters,
the function name capitalised, like this
@ar.Sort(aArray)
@ar.sort.Generic(aArray, aPredicate)

4 Array Functions
*****************

The Common Array (Arraja vulgaris SHAEDMIUS). There are also some
derived datatypes: association lists, expandable arrays with negative
indizes, and sorted arrays specialised in dealing with references.
4.1 Creation
============

4.1.1 ar.0
----------

-- Function: array 0 ()
-- Function: array 1 (A0)
-- Function: array 2 (...)
-- Function: array 3 (...)
-- Function: array 4 (...)
-- Function: array 5 (...)
-- Function: array 6 (...)
-- Function: array 7 (...)
-- Function: array 8 (...)
-- Function: array 9 (...)
-- Function: array 10 (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)
Array creation. Example: Instead of
colours = ArCreate(3)
ArAppend(colours, "red")
ArAppend(colours, "green")
ArAppend(colours, "blue")
write
colours = call("ar\\3", "red", "green", "blue")
or, even better,
colours = @ar.3("red", "green", "blue")
How cool is that.

4.1.2 ar.Copy
-------------

-- Function: array Copy (array A)

Creates a deep copy of A. Calls itself recursively for elements
that happen to be arrays.

* _Return value:_ A freshly allocated copy of A.

* _Side effects:_ None.
a = @ar.3(0, @ar.1(1), 2)
b = @ar.Copy(a)
b[1] = pi
==> a = (0, (1), 2)
b = (0, pi, 2)

4.1.3 ar.Enum
-------------

-- Function: array Enum (int LEFT, int RIGHT, int type)
Return a new array '[left,...,right]', counting up/downards.

TYPE controls the type of the generated elements:
* 'TypeInfo_Numeric'.

* 'TypeInfo_Reference': Cast to '(ref)'. Bounds may not be
negative.

* 'TypeInfo_String': strings of numbers (float).

* 'TypeInfo_Array': arrays with one single number.

Suitable for quickly creating arrays for testing purposes.

4.1.4 ar.Fill
-------------

-- Function: array Fill ( FILL, int SIZE)
_Returns_ a new array of size SIZE, filled with FILL.

(; Exercise 1. In the example below, 'A' and 'B' behave very
differently. Explain. ;)
var A = @ar.Fill(@ar.1("foo"), 2)
var B = @ar.2(@ar.1("foo"), @ar.1("foo"))
A[1][0] = "bar"
B[1][0] = "bar"
==> A = [["bar"], ["bar"]]
B = [["foo"], ["bar"]]

4.1.5 ar.Make
-------------

-- Function: array Make (array DIM)
-- Function: array Make (int SIZE)

Returns a multi-dimenional array.
* DIM is an array of integers.

* SIZE: The returned array will have this size.

Examples:
cube = @ar.Make(@ar.3(3, 3, 3))
rectangle = @ar.Make(@ar.2(99, 33))
line = @ar.Make(@ar.1(42))
line = @ar.Make(42)

4.1.6 ar.Random
---------------

-- Function: array Random (uint SIZE, numeric LO, numeric HI, string
TYPE)
_Returns_ an array of SIZE random numbers ('Rand(lo,hi)').

Those numbers will be cast to TYPE:
* "ref": Only non-negative limits allowed!

* "float":

* "int": A float w/o the decimals. 'FmtNum("%g", i, 0)' works
nicely.

testAr = @ar.Random(5, -10, 10, "int")

4.2 Alteration
==============

4.2.1 ar.Append
---------------

-- Function: array Append (array A, array B)
This function adds all the elements of B to the rear of A.

* _Return value:_ A.

* _Side effect:_ A.

The following lines of code would have the same effect to A:
@ar.Append(a, @ar.2(pi, e))

ArAppend(a, pi)
ArAppend(a, e)

4.2.2 ar.Cut
------------

-- Function: array Cut (array A, int LO, int HI)
-- Function: array Slice (array A, int LO, int HI)
* 'Slice' Returns a new array, filled with this portion of A:
'A[lo],..,A[hi]'.

* 'Cut' Returns a new array, filled with elements of A, but the
slice '(A[lo] .. A[hi])' is left out.

HI = -1 means the highest index of A.

Examples:
A = @ar.5(0, 1, 2, 3, 4)
copy = @Ar.Slice(A, 0, -1)
empty = @Ar.Cut(A, 0, -1)
head = @Ar.Slice(A, 0, 0)
head = @Ar.Cut(A, 1, -1)
tail = @Ar.Cut(A, 0, 0)
tail = @Ar.Slice(A, 1, -1)

4.2.3 ar.Flatten
----------------

-- Function: array Flatten (array A)

Dissolve first-level arrays in A. Collect the result in a newly
allocated array.

A = @ar.3(@ar.2(1, 2), 3, @ar.2(4, 5))
flatterA = @ar.Flatten(A)
==> flatterA = [1, 2, 3, 4, 5]
* _Return value:_ A flatter version of A.

* _Side effects:_ None.

4.2.4 ar.Join
-------------

-- Function: array Join (array A), array B)
-- Function: array JoinGen (array A), array B)
Expand A with the elements of B, if not equal to an element already
there.

'ar.Join' uses '==' for comparison, 'ar.JoinGen' uses
'lib.pred.Equal' (slow).

_Side effect:_ A gets changed.. Conveniently, A is also the
_return_ value.

A = @ar.Enum(0, 10)
B = @ar.Enum(-5, 5)
C = @ar.Join(A, B)
; --> C = A = (-5, ..., 10)

4.2.5 ar.Push
-------------

-- Function: Push (array A, VALUE)
-- Function: Push (array A, VALUE)

'Push' adds VALUE to the rear end of A, 'Pop' retrieves and deletes
it. 'Note:' It is an error to call 'Pop' on an empty array.

* A, an array; maybe empty for 'Push', but not for 'Pop'

* VALUE, anything.

* _Return value:_ VALUE.

* _Side effects:_ That's the point.

4.3 Mapping and Filtering
=========================

4.3.1 ar.Map
------------

-- Function: array Map (array A, function CMD)
(_Note:_ If you are looking for the data type 'Map' that deals with
cells and worldspaces, please see *note Map::.)

Call CMD for each element of A. Collect the results in an array,
and _return_ it.

characters = @ar.5(0x460, 0x45f, 0x457, 0x456, 0x467)
names = @ar.Map(characters, "lib\\util\\GetEditorID")

;==> names = ("greenwarden", ..., "shadeMe")

4.3.2 ar.Filter
---------------

-- Function: array Filter (array A, function PREDICATE)
-- Function: array Filter1 (array A, function PREDICATE, ARG)
_Returns_ a fresh array consisting of all the elements of A for
which PREDICATE returned non-zero, while retaining their order.

For 'Filter1', PREDICATE takes two arguments. 'Filter1' passes the
element of A as the _first_ parameter to PREDICATE - like,
'call(predicate, el, arg)'.

Examples:
A = @ar.Enum(-10, 10, TypeInfo_Numeric)

B = @ar.Filter(A, "lib\\pred\\Negative")
; --> B = (-10, ...,-1)

B = @ar.Filter1(A, "lib\\pred\\Equal", 0)
; --> B = (0)
Three examples with a little more substance:
var emptyCells = @ar.Filter(\
@lib.util.GetCellsInWorldspace(\
GetFormByEditorID("Testworld")), \
"lib\\pred\\CellEmpty")

var rocks = @ar.Filter1(\
GetCellObjects(GetFormByEditorID("AerinsCamp")), \
"\\lib\\pred\\BFEidSubStr", \
"rock")

;; trees, not shrubs
var trees = @ar.Filter1(\
GetCellObjects(GetFormByEditorID("AerinsCamp")), \
"\\lib\\pred\\BFEidSubStrAt", @ar.2("tree", 0))

4.3.3 ar.Split
--------------

-- Function: array Split (array A, function PREDICATE)
-- Function: array Split1 (array A, function PREDICATE, ARG)
_Returns_ a fresh array consisting of all the elements of A for
which PREDICATE returned non-zero, while retaining their order. On
return, A is set to a new array with the "false" elements.

For 'ar.Split1', PREDICATE takes two arguments. 'Ar.Split1' passes
the element of A as the _first_ parameter to PREDICATE - like,
'call(predicate, el, arg)'.

Examples:
A = @ar.Enum(-5, 5)
B = @Ar.Split(A, "lib\\pred\\Negative")
; --> A = (0, ..., 5)
; --> B = (-5 , ..., -1)

A = @Ar.Enum(-5, 5)
B = @Ar.Split1(A, "lib\\pred\\Equal", 0)
; --> A = (-5, ..., -1, +1, ... 5)
; --> B = (0)

4.4 Introspection
=================

4.4.1 ar.Nth
------------

-- Function: Nth (array A, int N)
-- Function: at0 (array A)
-- Function: at1 (array A)
-- Function: at2 (array A)

Return the element at that index. Handy when eg dealing with
anonymous arrays.

'Nth' may handle negative indexes. It then starts counting from
the rear.

@ar.Nth(A, -1) == A[ArSize(A) - 1]

4.4.2 ar.GetIndex
-----------------

-- Function: int GetIndex(array A), EL)
-- Function: int GetIndexGen(array A), EL)

* _Return value:_ Index of first item in A that equals EL, '-1'
if not there.
* _Side effects:_ None
'Ar.GetIndex' uses "'=='", 'Ar.GetIndexGen' uses "'lib.pred.Equal'"
(slow).

4.4.3 ar.IsMember
-----------------

-- Function: boole IsMember (array A), EL)
-- Function: boole IsMemberGen (array A), EL)
* _Return value:_ "False" iff EL is equal to no element of A.

* _Side effects:_ None.

'ar.IsMember' uses "'=='", 'ar.IsMemberGen' uses
"'@lib.pred.Equal'".

Both functions use their respective 'ar.GetIndex' counterpart
(*note ar.GetIndex::).

4.4.4 ar.Census
---------------

-- Function: int Census (array A)
_Return_ the total number of elements in A that aren't arrays
themselves. Dive into sub-arrays. Breaks for insanely deep arrays
(Coda's recursion limit).

4.4.5 ar.ShowStructure
----------------------

-- Function: string ShowStructure (array A)

In case those arrays are driving you mad.

Returns a string showing the overall structure of not-too-large
arrays.

Currently, we insert an ellipsis for the tenth element and beyond.
This should keep output for nested arrays still informative. If
confused, paste the output into an editor that can highlight
matching parentheses.

Each item is replaced by a character indicating its type (e.g.
strings -> "s", references, numbers alike).

printc($ @ar.ShowStructure(GetDataHandlerFormList(@lib.util.FTID("cell"))))
--> (rrrrrrrrr...)

var a = @ar.3(@ar.0(), "foo", @ar.2(@ar.1(@ar.2((ref)1, 1)), "bar"))
printc($ @ar.ShowStructure(a))
--> (()s(((rn))s))
Handy!

4.5 Output
==========

4.5.1 ar.Print
--------------

-- Function: (void) Print (array A, string CMD, string PREFIX, string
POSTFIX)
CMD is either a user-defined Coda function taking one argument and
returning a string (e.g. 'lib.util.GetEditorID', see *Note
lib.util.GetEditorID::), or CMD is the empty string.

For each element in A _that is not an array_, print one line (let's
give that element a decorative name, 'ooo')

* If CMD is the empty string '""' then print 'PREFIXoooPOSTFIX'.
In this case A must consist of elements 'PrintToConsole' can
handle by means of '$'.

* If CMD is a string then replace the 'ooo' in the above example
by the output of 'Call (cmd, ooo)'.

If 'ooo' happens to be an array, this function calls itself
recursively, thus breaking for insanely deep arrays.

Examples: Put things in brackets.
@ar.Print(arrayOfStuff, "", "[", "]")
Write down the Editor IDs of the references in the array, seperated
by a space.
@ar.Print(arrayOfRefs, "lib\\util\\GetEditorID", "", " ")

4.5.2 ar.PrintFmtNum
--------------------

-- Function: (void) PrintFmtNum (array A), string FMT, boolean
UNSIGNED)
* FMT: A format string that 'FmtNum' can handle.
* UNSIGNED = 0 ==> interpret as signed
* UNSIGNED = 1 ==> interpret as unsigned
* A: array of elements which 'FmtNum' can handle; A may contain
other such arrays, in which case this function calls itself
recursively (limitations apply).

Example:
@ar.PrintFmtNum(arRefs, "Ref: 0x%.8", 1);

4.5.3 ar.Save
-------------

-- Function: Function void Save (array A, string NAME)

Works only for flat arrays (*note ar.Flatten::).

Write a portion of a Coda script that re-creates A (calling simply
'ArAppend' for each element of A). Retrieve it from Console
output.

The script looks like
@ar.Save(myArray, "FooBar")
--->
coda(FooBar)
var A = ArCreate(ArSize(A))
begin
ArAppend(A, ...)
...
return(A)
end

* A

* NAME The variable name of A in the script.

* _Return value:_ None.

4.6 Association List
====================

An "Assocciation list" (or "alist" for short) is of the form
[(key1, [value1_1, value1_2, ...]),
...]
where the array of values must at least contain one element.

There is no key without a value. When functions like 'Take' take the
last value from a key, the key will also be removed.

We create an alist by either calling 'Init' or filling an empty array
by means of 'Set'.

Both keys and values may be of any type.

_Note_ that we test keys that happen to be arrays by means of
'Equal', wich is comparatively slow (*note lib.pred.Equal::).

_Note:_ When looking up keys, we do it in the order of their
insertion.

4.6.1 ar.assoc.Argv
-------------------

-- Function: Argv (alist ARGV, PARAM, DEFAULT)
* ARGV, an assocciation list of named parameters.

* PARAM, a name of one such parameter. Normally a string, but
it doesn't matter.

* DEFAULT.

* _Return value:_ One of the values assocciated with PARAM, or
DEFAULT if lookup failed.

* _Side effects:_ None.

4.6.2 ar.assoc.GetKeys
----------------------

-- Function: array GetKeys (alist A)
* _Return value:_ An array of A'S keys.

* _Side effects:_ None.

4.6.3 ar.assoc.Init
-------------------

-- Function: array Init (array INIT)
* INIT is an array of the form '(key1, value1, key2, value2,
...)'. This function allows for only, and exactly, one
initial value per key.

* _Return value:_ A nice and proper assocciation array.

* _Side effects:_ None.
Example:
election72 = @ar.assoc.Init(\
@ar.8("black", 44.9, "red", 45.8, "yellow", 8.4, \
"turnout", 91.1))
winner = @ar.assoc.Get(election72, "turnout")
; == 91.1
_Note:_ If you give an odd INIT array, other than complain, this
function would simply leave out the last, remaining, value-less,
key.

4.6.4 ar.assoc.Exists
---------------------

-- Function: boolean Exists (alist A, KEY)

* _Return value:_ "False" iff KEY is not a key in A.

* _Side effects:_ None.

Note: Syntactic sugar. Calls '@ar.assoc.Lookup'.

4.6.5 ar.assoc.Lookup
---------------------

-- Function: array|boolean Lookup (alist A, KEY)
-- Function: array GetAll (alist A, KEY)
-- Function: array|boolean Delete (alist A, KEY)

Query for KEY. Return the associated list of values, if any.

* 'Lookup' returns "False" if KEY isn't there, such that, in
order to check for the existence of a key, one could say
if ((TypeId)@ar.assoc.Lookup(A, key) == TypeInfo_Array)
.. Lookup successful ..

* 'Delete': Same as 'Lookup', but removes the key from the list,
if there.

* 'GetAll' returns the list of values associated with KEY, but
signals an error if KEY isn't there.

* _Side effects:_ Positive for 'Delete', negative for the
others. _Note_, that these functions return the actual array
of values maintained by the alist. Changes made to that array
will affect the alist. Take care.

4.6.6 ar.assoc.Pop
------------------

-- Function: Pop (alist A, KEY)
-- Function: Get (alist A, KEY)

Lookup KEY in A, and a return an associated value. _Note:_ It is
an error to query for a non-existing key.

* 'Pop' returns the latest addition to KEY's values. It also
removes it from the list of values. Should this be the last
value, it also removes KEY from A.

* 'Get', however, returns the latest value without removing it.

* _Side effects:_ Positive for 'Pop', negative for 'Get'

4.6.7 ar.assoc.Push
-------------------

-- Function: Push (alist A, KEY, X)
-- Function: Set (alist A, KEY, X)
-- Function: numeric Inc (alist A, KEY, numeric ADDEND)

* 'Push': If KEY is not a key in A, then we add it, and
associate X. If there already is a key KEY, then X joins the
values. However, we do nothing if X is already associated
with KEY.

_Note_, that A may be an empty array, in which case A will
become a proper association list.

* 'Inc' adds ADDEND to the value most recently associated with
KEY. If KEY is not a key in A, add it, and associate ADDEND
with it. (Useful for taking count.)

* 'Set' lets become X the only value associated with KEY.
Existing values will be removed.

- - -

* _Return value:_ VALUE (for 'Inc', the sum).

* _Side effects:_ A.

4.6.8 ar.assoc.Print
--------------------

-- Function: void Print (array A)

Pretty-prints the association list A. Remains "pretty" for modest
As only.

Values are printed last-in-first-out.

* _Return value:_ None.

* _Side effects:_ None.

4.7 Reference Array
===================

A 'RefArray' is a _sorted_ array of references less than '0x80:FF:FF:FF'
that does not contain duplicates.

If handled exclusively by RefArray functions, a RefArray is
guaranteed to remain such one.

Quick looking-up of elements by bisection method. That's the point.

May speed up things by orders of magnitudes ...
4.7.1 ar.ref.Make
-----------------

-- Function: RefArray Make (array A)
A is an array of references.

* _Return value:_ A proper RefArray.

* _Side effects:_ A.
Note: There's no need to call this function if you are certain that
your array is sorted and doesn't contain duplicates, like here:
myRefArray = @ar.Sort(GetCellObjects(aCell))

4.7.2 ar.ref.Erase
------------------

-- Function: int Erase (RefArray X, ref EL)

* X, a RefArray.

* EL, the thing to remove, and not *"Pay attention, Watson."*
the index of it.

* _Return value:_
* ==> -1: When EL actually was not an element of X.
* ==> 0: On failure for other reasons ('ArErase' failed).
* ==> 1: Success.

* _Side effects:_ X

4.7.3 ar.ref.GetIndex
---------------------

-- Function: int GetIndex (RefArray X, ref Q)
-- Function: int GetIndexPP (RefArray X, ref Q, uint PP)
* _Return value:_ The index of Q in X, or -1 when not there.
'GetIndex' starts at index zero, 'GetIndexPP' starts at index
PP (>= 0).

* _Side effects:_ None.

4.7.4 ar.ref.GetIntersection
----------------------------

-- Function: RefArray GetIntersection (RefArray A, RefArray B)
* _Return value:_ New Ref-Array that contains those elements
that are elements of both, A and B. May be empty.

* _Side effects:_ None.

4.7.5 ar.ref.Join
-----------------

-- Function: RefArray Join (RefArray X, RefArray A)
-- Function: RefArray Join (RefArray X, ref O)
* A, a RefArray. Elements of A will be inserted into X if not
already there.

* O a reference. Will be inserted into X unless already there.

_Return value and side effect:_ the enlarged X.

4.7.6 ar.ref.Print
------------------

-- Function: void Print (RefArray X)
Syntactic sugar for 'ar.PrintFmtNum' (*note ar.PrintFmtNum::) for
hexes.

4.8 Rubber Array
================

Self-enlarging on need, and allowing for negative indices, RubArrays are
made for working with cells and worldspaces. (See Map, *note
lib.map.Make::).

Once present, however, I've found them coming in handy when preparing
logs and statistics. ^^

A "Rubber Array" is made out of the sort of rubber, that may expand,
but never bounce back again (although there is a way to do that, see
*note ar.rubber.Make::). Its index range widens as needed, negative
indices are welcome. An element is considered "empty" if its value is
zero, or if its supposed index is beyond current index range.

If you want to _set_ an element beyond the current index range, the
array expands. If you try to _get_ an element beyond the current index
range, nothing happens, you just get back a zero.

Rubber Array low-level data access
..................................

The data structure 'Rubber Array' ist stored as an array of two
elements. It is safe to add more data to that array, starting with
index 2. 'Maps', for example, which are also Rubber Arrays, add a
worldspace reference, and more (*note Map::).

var RADAT = 0
var RAMIN = RADAT+1
var RASIZE = RAMIN+1

If calling 'ar.rubber.Get' is too expensive, access element 'idx' in
'rubAr' as follows
@ar.rubber.Get(rubAr, idx) ---> rubAr[RADAT][idx - rubAr[RAMIN]]
where the index is bound by
rubAr[RAMIN] <= idx <= rubAr[RAMIN] + ArSize(rubAr[RADAT]) - 1

4.8.1 ar.rubber.Make
--------------------

-- Function: RubberArray Make ( INI, int MININDEX)
-- Function: RubberArray Make (array INITARRAY, int MININDEX)
Creates a rubber array, initialises, and _returns_ it.

* If INI is non-zero, it will become the element at MININDEX.

* However, if INI is the number zero, then the returned rubber
array has empty data (id est 'ArCreate(0)'), but the MININDEX
is set.

* If INITARRAY is an *empty array*, it will become the actual
rubber array, with the given MININDEX. So, in that case,
changes to INITARRAY will change the rubber array! Might come
in handy at times.

* However, if INITARRAY is not empty, we copy its contents to a
new array, leaving out leading and trailing zeroes.

Note: If, after working with a rubber array for a while, you feel
it were, for whatever reasons, time to get rid of zeroes at the
fringes, you may simply say
var RADAT = 0
var RAMIN = RADAT+1

rubArray = @ar.rubber.Make(rubArray[RADAT], rubArray[RAMIN])

4.8.2 ar.rubber.Get
-------------------

-- Function: Get (rubberArray G, int N)
* _Return value:_ The element of G at index N.

* _Side effects:_ None.

4.8.3 ar.rubber.Set
-------------------

-- Function: multi Set (RubberArray G, int N, multi V)
-- Function: multi Inc (RubberArray G, int N, numeric ADDEND)

* 'Set': Set G at index N to value V.

* 'Inc': Add ADDEND to the existing value.
Set G at index N to value V. _Return value:_ The new value..
_Side effects:_ Apart from actually changing the value: If
neccessary, expand G to include index N while filling possible gaps
with zeros.

4.8.4 ar.rubber.GetContent
--------------------------

-- Function: array GetContent (RubberArray R)
* R, a RubberArray

* _Return value:_ An array of the non-zero elements in R.

* _Side effects:_ None.

4.8.5 ar.rubber.GetExtent
-------------------------

-- Function: array GetExtent (RubberArray RUBAR)
Get overall index range of non-zero elements.
* _Return value:_ If RUBAR is empty, return zero (check against
'TypeInfo_Array'. Otherwise return a pair of the first / last
indices of non-zero elements in RUBAR. Beyond those is either
nothing but zeroes, or nothing at all.

* _Side effects:_ None.

4.8.6 ar.rubber.Copy
--------------------

-- Function: RubberArray Copy (RubberArray RUBAR)
* _Return value:_ A new rubber array, leaving out possible zeros
at the fringes.

* _Side effects:_ None.

4.8.7 ar.rubber.Merge
---------------------

-- Function: RubberArray Merge (RubberArray P, RubberArray Q)
Adds non-zero elements of Q to P, retaining their indices. _Does
not overwrite_ unless P is zero at that position.

Special cases:
* If Q is empty (no elements at all, or only zeros), do nothing
to P, and return it.
* If P is empty (no elements at all, or only zeros), then make P
a copy of Q (we copy the array elementwise).
_Return value and side effects:_ P.

4.8.8 ar.rubber.Remove
----------------------

-- Function: ref Remove (rubberArray A, int N)
Here, 'removing' means just setting to zero, unless N happens to be
the minimal or maximal index. In that case we shrink the array and
adjust the minimal index if neccessary.

* A, a rubber array

* I, index of the element to be removed. The new value will be
zero.

* _Return value:_ The removed element (which may be the number
zero), or the number zero, if there was none.

* _Side effects:_ A.

4.8.9 ar.rubber.Invalid
-----------------------

-- Function: boole Invalid (RubbberArray A)
Mainly for debugging.
* A, a Rubber Array candidate.

* _Return value:_ 'False' if A *could* be a Rubber Array.
'True' if there is *no way* that A is a Rubber Array.

* _Side effects:_ None.

4.9 Sorting
===========

Sorting of arrays of built-in or compound data types.
Sorting arrays of built-in data types can be done by simply calling
'Sort'. It will delegate the job to the proper function residing in
'ar/sort'.

For sorting with a self-written comparison function, please have a
look at 'ar.sort.Generic'. That's a shellsort, and compact enough to be
conveniently inlined, should need arise. The code has already been
prepared for that purpose (prefixed).

There is also a sorting functions for mixed-type arrays,
'ar.sort.Multi'. This is the one being used when pretty-printing
association lists.

4.9.1 ar.Sort
-------------

-- Function: array Sort (array A)
-- Function: array Shellsort (array A)
-- Function: array Strings (array A)
-- Function: array Radix (array A)
-- Function: array Refs (array A)

'Sort' assumes that A is a homogenous array.

Supported types are:
* Strings: 'StringCompare', case insensitive.

* References: Only references < '0x7fffffff'. (Less than 128
mods loaded.)

* Numbers

_Return value:_ A sorted array.

_Side effects:_ It is unspecified whether A is being altered or not
(depends if the used method is "in-place" or not). The correct use
is as in
A = @ar.Sort(A)
Or, in case you need A intact
B = @ar.Sort(@ar.Copy(A))

Choosing a sorting method
.........................

Most of the time, one would just call 'ar.Sort' and be done.

'ar.Sort' delegates the actual sorting job to one of the other
functions.

If you want to use them explicitely, here you go:
* 'Strings' uses Shellsort(1) employing 'StringCompare'.

_Return value, Side effect:_ Returns A (sorted).

* 'Shellsort' is for numbers.

_Return value, Side effect:_ Returns A (sorted).

* 'Radix' uses Radix sort(2) Here, A is an array of 31-bit
unsigned integers (that is, '<= 0x7F-FF-FF-FF'). So, in case
of sorting references, only '0x7F' mods loaded into CSE at a
time are supported.

_Returns_ a fresh, sorted, array. _Side effects:_ Unlike some
other sorting functions, this one does _not_ alter A.

* 'Refs' uses ShellSort for small arrays (<= 600) where test
runs show that it is still faster, and switches to RadixSort
for larger ones.

---------- Footnotes ----------

(1) Ciura's gap sequence, source Wikipedia, Creative Commons
Attribution-ShareAlike License

(2) Thanks to opendatastructures.org,


4.9.2 ar.sort.Multi
-------------------

-- Function: array Multi (array A)

Sorts an array of mixed-type elements. Grouped in the following
order:
* Reference
* Number
* String
* Array

Of course, especially with arrays, this can get terribly slow.

* _Side effects:_ None.

4.9.3 ar.sort.Generic
---------------------

-- Function: array Generic (array A, function CMP)
Uses Shellsort, with a user defined comparison function.

Roughly twenty lines of code long, it is also a good candidate for
inlining (the code has been prepared for that purpose).

5 Library Functions
*******************

5.1 Background Scripts
======================

A collection of Background scripts.

* 'FloorArray.coda' calls a certain coda file that gives an array of
forms back. "Floor" those forms (as if pressing the "F" key on an
object in the Render window).

* 'PlotPferdchenXPM.coda' can be used as a blueprint for adding a
Pixmap to the game world(*note Pixmaps::.

* The purpose of 'Sputnik.coda' is to demonstrate a technique of
writing a background script. It just prints lines to the Console."

[TODO: do it]

5.1.1 lib/Backgrounders/FloorArray
----------------------------------

-- Background: void FloorArray()

Use this to "floor" every Form in an array.

Executes the Coda script '...\Coda\Data\FloorArray.coda', which is
expected to return an array of FormIDs; calls 'FloorRef' on them,
repeatedly, until their '"z"' coordinate stops changing (that is,
they hit the floor proper).

This must be done in the background, because the object to be
floored has to be displayed in the Render window in order for
'FlooRef' to work.

The individual forms may be located all over the map.

The timers waiting for a fresh cell to load, or for the next object
in an already loaded cell, can be easily adjusted at the top of the
source code. By default, the script waits for four seconds for a
cell to load, 0.1 seconds for loading a ref, and another 0.1
seconds for flooring.

5.2 Comparisons
===============

In the makings of 'StringCompare'. But we return '<0 =0 >0', not '=-1
=0 =1'.
5.2.1 lib.comp.Compare
----------------------

-- Function: int Compare ( X, Y)
General purpose comparison function for number, reference, and
string types of arguments (strings: case ignored). It is an error
to pass an argument of any other than one of the mentioned types.
It is an error to pass arguments of different types.
* Strings. Return values are the same as returned by the
built-in 'StringCompare' (lexical order).
* Numbers. Return values are:
* 'x < y => < 0'
* 'x = y => 0'
* 'x > y => > 0'
* Reference. Same as for numbers.

5.2.2 lib.comp.EidCompare
-------------------------

-- Function: int EidCompare (reference R, string S)
Syntactic sugar.

Calls the built-in 'StringCompare' on R's EditorID and S, case
ignored.

5.3 Predicates
==============

Functions that return either '#0' ("False") or not ("True"). Functions
like *note ar.Filter:: or *note lib.graph.Make:: take them as arguments.
5.3.1 lib.pred.CellEmpty
------------------------

-- Function: boole CellEmpty (ref CELL)
-- Function: boole NotCellEmpty (ref CELL)
These check whether 'ArSize(GetCellObjects(cell) == 0)'.

* _Return value:_ Returns "True" (non-zero) if, and only if,
there is no (or at least one, respectively) object in cell.

* _Side effects:_ None.

5.3.2 lib.pred.BFEidSubStr
--------------------------

-- Function: boolean BFEidSubStr (ref R, string S)
-- Function: boolean EidSubStr (ref R, string S)
-- Function: boolean BFEIDSubStrAt (ref R, PARAM)
-- Function: boolean EIDSubStrAt (ref R, PARAM)
These functions check whether a given string is a substring in the
EditorID of the (base) form of the given reference.

Additionally, the '---At' versions require this string to start at
a certain index.

* R, a reference.

* S, the substring to find.

* PARAM is an '@ar.2(s, startIndex)'.
For performance reasons, we don't perform sanity checks on the
arguments to these functions.
* _Return value:_ Zero iff S is not found where required.

* _Side effects:_ None
Example: Filter all objects in a cell whose EID _start_ with
"MorningGlory".
@ar.Filter1(GetCellObjects(cell),\
"lib\\pred\\BFEidSubStrAt" \
@ar.2("MorningGlory", 0))
How about rocks:
@ar.Filter1(GetCellObjects(cell),\
"lib\\pred\\BFEidSubStr" \
"rock")

5.3.3 lib.pred.Equal
--------------------

-- Function: boolean Equal ( X, Y)
-- Function: boolean NotEqual ( X, Y)
Type-based equality check. _Returns_ '#0' ("False") iff X is
equal/not-equal to Y.

* Strings are *not* case sensitive (because EditorIDs aren't
either).
* Numbers and references are checked by "'=='".
* Arrays: if their structures differ, they are not equal; if
they differ in any of their elements, they are not equal.
* Returns "False" for any 'TypeInfo_Invalid' argument.
Note: For arrays, this function calls itself recursively, meaning
that it will break for insanely deep arrays (Coda's recursion
limit).

5.3.4 lib.pred.hasBF
--------------------

-- Function: boolean hasBF (ref X, ref BF)
_Return value:_ "False" iff BF is not the base form for X.

_Side effects:_ None.

5.3.5 lib.pred.LandscapeTextureSubstringAt
------------------------------------------

-- Function: boolean LandscapeTextureSubstringAt (ref CELL, array ARGS)
Check whether there is a landscape texture in CELL that has an
EditorID containing a certain string of characters.

* CELL is a reference to an exterior cell.

* ARGS is an array of two parameters, '@ar.2(token,
startIndex)'.

* 'string' TOKEN: The string to test for.

* 'int' STARTINDEX:
* '-1' means that TOKEN may appear anywhere in
EditorID

* '-2' means that TOKEN must be an exact match.

* For other (non-negative) values of STARTINDEX, TOKEN
must start at that index.
_Return_ "False" iff none of the ground textures in CELL have a
matching EditorID.

_Side effects: None._

5.3.6 lib.pred.MaxDistance
--------------------------

-- Function: boolean maxDistance (X, Y, D)
_Return value:_ "False" iff distance between two objects is greater
than D.

_Side effects:_ None.

5.3.7 lib.pred.Negative
-----------------------

-- Function: boolean Negative (numerical X)
-- Function: boolean NonNegative (numerical X)
-- Function: boolean Zero (numerical X)
-- Function: boolean NonZero (numerical X)
_Returns_ "False" iff X is negative/zero - or not, respectively.

5.3.8 lib.pred.parentWorldspace
-------------------------------

-- Function: boole parentWorldspace (ref CELL, ref WORLDSPACE)
_Returns_ "False" iff WORLDSPACE is not the parent worldspace for
CELL.

5.3.9 lib.pred.PersistentWorldspaceCell
---------------------------------------

-- Function: boole PersistentWorldspaceCell (ref CELL)
-- Function: boole NotPersistentWorldspaceCell (ref CELL)
* 'PersistentWorldspaceCell' returns "True" (that is, non-zero)
if, and only if,
* CELL has got the coordinates [0,0]
* _and_ there is at least one object in CELL
* _and_ every object in CELL is persistent.

* 'NotPersistentWorldspaceCell' returns "True" (that is,
non-zero) if, and only if,
* CELL has got coordinates other than [0,0]
* _or_ CELL is empty
* _or_ there is at least one object in CELL that is not
persistent.
Additionally, as a "True" value, 'NotPersistentWorldspaceCell'
returns a number code indicating where the cell fells short of
being a PWC (see the source code for more information).

_Note: _The code has been prepared for inlining.

5.3.10 lib.pred.sameBF
----------------------

-- Function: boolean sameBF (ref X, ref Y)
_Return value:_ '0x0' iff BaseForms differ.

5.3.11 lib.pred.sameEID
-----------------------

-- Function: boolean sameEID (ref X, ref Y)
_Return value:_ '0x0' iff Editor IDs differ.

5.3.12 lib.pred.sameWorldspace
------------------------------

-- Function: boolean sameWorldspace (ref X, ref Y)
* X, a cell

* Y, a cell

* _Return value:_ "False" iff X and Y don't share a worldspace

* _Side effects:_ None.

5.4 Graphs
==========

For our purpose, a "Graph" is an undirected, non-weighted graph (see (or
don't) ) represented by an
array of nodes (for us it will be references or FormIDs ergo hexadecimal
numbers) in conjunction with a "predicate" - a function, that takes two
nodes as arguments, and decides whether any given pair of nodes shares a
link or not.

Data structure
..............

Consider the following graph:
nodes = (a b c d)
graph | a--b |
| | / |
| |/ d |
| c |

*TL;DR ->* All the information we have about the graph is contained
in the output of
@lib.graph.Make("fictitious\Predicate", nodes)
;==> ((a b c 0) (b c 0) (c 0) (d))
*<- TL;DR*

Derived from the adjacency matrix for a graph, the ouput of the
function 'lib.graph.Make' contains all the information we have about the
graph (*note lib.graph.Make::)

Follows a little excursion developing the data structure we use, best
shown with the help of the above graph as an example.

The adjacency matrix 'M' looks like this:
......a.b.c.d.....
.
/ \ .
| 0 1 1 0 | . a
M = | 1 0 1 0 | . b
| 1 1 0 0 | . c
| 0 0 0 0 | . d
\ / .
.

Since 'M' is symmetrical, all we need to know is the upper-right
corner:
M' = ((0 1 1 0) (0 1 0) (0 0) (0))

For us it doesn't matter whether we store binary or hex numbers. So,
if we read 'M' in rows, from left to right, and replace each 1 with the
end point of the link it stands for, 'M'' turns into
A' = ((0 b c 0)) (0 c 0) (0 0) (0))

Now, since we're smart, we put into the diagonal element of each row
the node to wich it corresponds, and we get
A'' = ((a b c 0)) (b c 0) (c 0) (d)

Now all the information we have about the graph is present in 'A''',
and that's what 'lib.graph.Make' returns.
graph = @lib.graph.Make("fictitious\\Predicate", nodes)
;==> graph = ((a b c 0) (b c 0) (c 0) (d))

5.4.1 lib.graph.Make
--------------------

-- Function: array Make (function PREDICATE, array NODES)
-- Function: boolean SharesLink (ANODE, BNODE)
-- Function: array Make1 (function PREDICATE, array NODES, ARG)
-- Function: boolean SharesLink1 (ANODE, BNODE, ARG)
* 'Make' and 'Make1' apply PREDICATE to NODES while building the
graph representation as detailed in *note Graphs::.

* PREDICATE is a function that takes two arguments (an
additional one in case of 'Make1') as demonstrated by
"SharesLink". (To be clear: "SharesLink" is not an actual
library function.)

PREDICATE returns zero if, and only if, those nodes do not
have a link in common.

graphOfStuff = @lib.graph.Make("\\lib\\pred\\SameEID", cellObjects)
graphOfVines = @lib.graph.Make1("\\lib\\pred\\MaxDistance", vineParts, 165.0)

5.4.2 lib.graph.SubGraphs
-------------------------

-- Function: array SubGraphs (array A)

A is the representation of a graph as produced by 'lib.graph.Make'
(*note lib.graph.Make::).

_Return_ an array of arrays of nodes in disconnected sugraphs.

Note: The nodes in subgraphs retain the order of the parameter
NODES given to 'lib.graph.Make' - ordered NODES in, ordered
subgraphs out.

Example I.
..........

With the example of the introduction (*note Graphs::):
nodes = (a b c d)
graph | a--b |
| | / |
| |/ d |
| c |

A = @lib.graph.Make(, nodes)
;==> A = ((a b c 0) (b c 0) (c 0) (d))
subgraphs = lib.graph.SubGraphs(A)
;==> subgraphs = ((a b c) (d))

Example II.
...........

nodes = (a b c d)
graph | d |
| / |
| a |
| b--c |

Adjacency matrix:
/ \
| 0 0 0 1 |
| 0 0 1 0 |
| 0 1 0 0 |
| 1 0 0 0 |
\ /

;==> A = ((a 0 0 d) (b c 0) (c 0) (d))
subgraphs = lib.graph.SubGraphs(A)
;==> subgraphs = ((a d) (b c))

Real-life example: After this, all the cell objects are now nicely
grouped with respect to their Base Form, and are, as a treat,
within their group also sorted with respect to their FormID.
@lib.graph.SubGraphs(@lib.graph.Make(\
@ar.Sort(GetCellObjects(cell)), \
"\\lib\\pred\\sameBF"))

Another one: After this, all blue morning glory parts are grouped
within "vines". Every vine part in a subgraph has at least one
other vine part wich is less than "MaxDistance" away (one-element
subgraphs excluded).
blues = @ar.Filter1(GetCellObjects(cell),
"lib\pred\BFEidSubStrAt"
@ar.2("MorningGloryBlue", 0))
vines = @lib.graph.SubGraphs(@lib.graph.Make1(\
blues, "\\lib\\pred\\MaxDistance", 165.0))

5.5 Map
=======

A 'Map' structures references of cells in a worldspace in a
2-dimensional array. It doesn't neccessarily contain all cells - any
subset would do. Maps can be "joined" and "merged" in order to form a
larger map.

A map is organised as a rubber-array of rows of cells (rows, again,
being rubber-arrays (*note Rubber Array::).

Motivation
..........

The motivation for making Maps (and therefore rubber arrays) came from
the need to work on adjacent cells. 'GetHood' is the result (think
"neighbourhood", not "clothing").

With the help of functions like 'LandscapeTextureSubstringAt', one
might for example build maps of cells of a certain climate (*note
lib.pred.LandscapeTextureSubstringAt::).

Low-level Data Access
.....................

As a rubber array, a map shares its first fields with one.
var RADAT = 0
var RAMIN = RADAT+1
var RASIZE = RAMIN+1
Personally, I don't dare accessing 'map[RADAT]' directly. I use the
Get/Set-functions.

But a map also carries some easily accessible data, its _worldspace_,
and the _persistent worldspace cell_.
var MAPWS = RASIZE
var MAPPWC = MAPWS+1
var MAPSIZE = MAPPWC+1

It is save to add more data to your map starting after 'MAPSIZE'.

The Persistent Worldspace Cell
..............................

Coda provides no immediate way to distinguish the "Persistent Worldspace
Cell" (PWC) from regular ones. In Coda, the PWC has the coordinates
[0,0], but it must not conflict with the actual cell at [0,0].

However, there is a heuristical approach that never has failed us so
far (*note lib.pred.PersistentWorldspaceCell::). Cross your fingers.

One can always lookup the PWC with the help of "Tes4Edit."

It is also possible to identify the PWC via the "Cell View" window.
For example, the worldspace 'TestWorld', contains two cells with
coordinates [0,0], 0x23233 and 0x23763 (filter them with a Coda script).
The latter does not appear in the "Cell View" window, and that is the
PWC(1).

Note that there is a "Save" function that writes a Coda script to the
Console buffer allowing to quickly re-make the map in later sessions.

---------- Footnotes ----------

(1) Meaning, that CSE knows very well how to distinguish a PWC from
regular cells; but it doesn't pass this knowledge down to us. ^_^

5.5.1 lib.map.Make
------------------

-- Function: map Make ( array CELLS, PWC, RECT)

Arranges cells in a "map."

* CELLS: a (possibly empty) array of references to cells.

* PWC indicates how to tackle the problem of the persistent
world cell:
* The number '0': Don't check CELLS for being the PWC. Use
this if you are sure that CELLS doesn't contain it or
RECT excludes [0,0].

* The number '1': Check CELLS for the PWC by means of the
function 'PersistentWorldspaceCell' (*note
lib.pred.PersistentWorldspaceCell::).

* Otherwise: the reference to the actual PWC. If found in
CELLS, skip it.

RECT can be used to consider only cells inside a rectangular
part of the worldspace.

RECT may assume the following forms:
* RECT is the number '0', then use all CELLS.

* RECT is an array of four integers, e.g. '@ar.4(x1, y1,
x2, y2)'.

* RECT is an array of two corner points, each given as a
cell reference, or as an array of two integers, mixing
allowed, e.g. '@ar.2(south-west-cell-ref, @ar.2(40,
64))'.

If a cell does not exist at '(x,y)', the map holds the number
zero at that point.

We ignore interior cells (no need to filter them beforehand).

When encountering different cells that claim to occupy the
same space, we raise an error.

* _Return value:_ The freshly constructed map.

* _Side effects:_ None.

Something to toy with:
cellsTriplet = @lib.util.GetAllCellsInWorldspace(\
GetFormByEditorID("Testworld"), -1)
---> [empty-cells, non-empty-cells, (ref)0x23763]

testworld = @lib.map.Make(\
cellsTriplet[1], cellsTriplet[2])

testworldNE = @lib.map.Make(\
@lib.util.GetCellsInWorldspace(GetFormByEditorID("Testworld"), \
0, @ar.4(1, 2, 9, 9))

5.5.2 lib.map.Get
-----------------

-- Function: Get (MAP, int X, int Y)
-- Function: void Set (MAP, int X, int Y, V)
Sets or gets the value at the given coordinates.

Setting with a zero value V does never expand the map.

5.5.3 lib.map.Copy
------------------

-- Function: map Copy (map M)
A copy passes the Turing test.

* _Return value:_ A functional copy of M. Empty cells are set
to zero. Empty cells and rows at the fringes are removed.

* _Side effects:_ None.

Example: A slimmer M can be obtained like this:
M = @lib.map.Copy(M)

5.5.4 lib.map.SubMap
--------------------

-- Function: map SubMap (map M, ref|pair P, ref|pair Q, boole REMOVE)
Provided a map, and two corner points of a rectangle, 'SubMap'
returns a new map: the part of the original map that happens to be
inside that rectangle. P and/or Q may be cell references, or
arrays (size >= 2) of integers (coordinates).

If REMOVE is "True", a cell that enters the submap is removed from
M, thus making the map and the submap complementary.

_Return value:_ a map, or zero

_Side effects:_ M only if REMOVE is "True".

5.5.5 lib.map.GetCells
----------------------

-- Function: array GetCells (map MAP)
* _Return value:_ An array of all cell references in MAP,
starting from the top-left, row by row, ending at the
bottom-right.

* _Side effects:_ None.

Again, the returned cells are sorted by their coordinates, not by
their FormID. Namely, the returned array is not a 'RefArray'. Just
saying.

5.5.6 lib.map.GetExtent
-----------------------

-- Function: array GetExtent (map M)
Index range.
* _Return value:_ The bounding rectangle around the non-zero
cells of map M, as an array of four integers, '(xMin, yMin,
xMax, yMax)'. If M is empty, return '#0' (just check against
'TypeInfo_Array').

* _Side effects:_ None.

5.5.7 lib.map.GetHood
---------------------

-- Function: array GetHood (map MAP, array XY, array DIMENSIONS)
-- Function: array GetHood (map MAP, array XY, number RADIUS)
-- Function: array GetHood (map MAP, ref CELL, array DIMENSIONS)
-- Function: array GetHood (map MAP, ref CELL, number RADIUS)

Given an exterior cell, return an array of cells surrounding that
cell in a certain way(1).

* MAP.

* XY, an array of two integers, as the coordinates of a cell in
MAP.

* CELL, a valid cell reference in MAP.

* DIMENSIONS is an array of two numbers: Return cells in a
rectangle of the given edge lengths, with our cell in the
center. _Note:_ We support *odd* numbers only - the effect
for even numbers is unspecified (due to rounding errors).

* RADIUS is passed as a number: Return cells in a circle around
our cell.

_Return value_: Array of cells. _Note:_ We include only members of
the map that are not empty.

_Side effects: None._

Examples.
.........

Just to give you a feeling about how thing's work, here a few
popular examples:
* DIMENSIONS = (3, 3) => square: 3x3
@lib.map.GetHood(map, C, @ar.2(3, 3))
-----
-###-
-#C#-
-###-
-----

* DIMENSIONS = (3, 1) => 3 cells in a row
@lib.map.GetHood(map, C, @ar.2(3, 1))
-----
-#C#-
-----

* RADIUS = 0.5 => "circle":
@lib.map.GetHood(map, C, 0.5)
- # -
# C #
- # -

* RADIUS = 1 would (again) produce a 3x3 square:
@lib.map.GetHood(map, C, 1)
-----
-###-
-#C#-
-###-
-----

* RADIUS = 1.5 => "circle": shaped like a rhombus.
@lib.map.GetHood(map, C, 1.5)
- - # - -
- # # # -
# # C # #
- # # # -
- - # - -

* Getting ever more circular for larger radii
aerinsCamp = @lib.map.Make(\
@lib.map.GetHood(tamrielMap, \
GetFormByEditorID("AerinsCamp"), 3), \
#0, #0)
@lib.map.Print(aerinsCamp, "table")

Map
Worldspace: 0x0000003C EID='Tamriel' FT=53 'WorldSpace'
Extent: 29, 30, 35, 36
Persistent Worldspace Cell: None.
x-> . | . . . . +
36 - - # # # - -
35 - # # # # # -
34 # # # # # # #
33 # # # # # # #
32 # # # # # # #
31 - # # # # # -
30 - - # # # - -
x-> . | . . . . +

Background.
...........

We map the discrete coordinate system of cells onto the plane of
real numbers R² by identifying points in R², that happen to have
whole numbers as coordinates, with the cell of those coordinates.
We then can say that a cell (V,W) is "inside" a circle around cell
(X,Y) with radius r, when the point (v,w) is inside the circle
around (x,y) with radius (r+0.5) in R².

+cell +cell +
-+-----+-----+ +0.5
| | | | |
-+ 0,0 + 1,0 + y=0
| | | | |
-+-----+-----+ -0.5
0 1
x->

---------- Footnotes ----------

(1) By the way, sorry for the name. "AdjacentCells", "Hood" ~
Neighbourhood, ... Nothing catchy came to mind. Then "Hood" just
stuck.

5.5.8 lib.map.Join
------------------

-- Function: map Join (map MAP, array CELLS)
* MAP is the map to join. It may expand in the process.

* CELLS is an array of cell references. Interior cells are
silently ignored, as well as the MAP PWC, should it come by.

However, should there be a cell of a different worldspace, we
raise an error.

* _Return value:_ MAP.

* _Side effects:_ MAP.

wholeMap = @lib.map.Join(eastMap, @lib.map.GetCells(westMap))
;; Ooops! wholeMap == eastMap

5.5.9 lib.map.Remove
--------------------

-- Function: ref Remove (map M, array XY)
-- Function: ref Remove (map M, ref CELL)
* M, a map

* XY, coordinates of the cell to be removed

* CELL, the cell to be removed.

* _Return value:_ The removed cell, or the number zero, if there
was no cell at the given coordinates.

* _Side effects:_ M.

5.5.10 lib.map.Merge
--------------------

-- Function: map Merge (map A, map B)
Adds non-zero content of B to A, expanding A if neccessary.
Doesn't overwrite. _Return value:_ A _Side effects:_ A

5.5.11 lib.map.Print
--------------------

-- Function: void Print (map MAP), string MODE)
The string MODE controls the kind of the output:
* '"extent"' - The "bounding rectangle" as four indices: No cell
outside of this rectangle has an object in it.

* '"cells"' - Full (and perhaps long!) list of cells and their
coordinates.

* '"table"' - Suitable for small and compact maps, this function
prints a rectangular map: a '-' for an empty cell, a '#' for a
set one.

_Return value: None._

_Side effects: None._

5.5.12 lib.map.Save
-------------------

-- Function: void Save (map M, string SCRIPTNAME)
Motivation: Assembling a list of cells to put into a map can turn
out to be quite time consuming. (E.g. loading a cell into the
Render Window, waiting a three seconds, checking stuff by script,
adding to the list or not - for a worldspace, that may take well
till tomorrow).

"Save" prints a Coda script to the console which reproduces M
(minus empty cells at the fringes) when run under the _indentical
mod configuration_ under which it had been produced.

The script creates, and returns, a new map from the cells
constituating M (see *note lib.map.Make:: and *note
lib.map.GetCells::).

Those scripts can get rather big (typically 29 bytes per cell, plus
a few more lines of code).

Small ones, fitting into the console buffer, can be copy-pasted
from "Console->Contexts...->Coda Script". Others may be recovered
by opening the whole log in an external editor ("Console->Open
log"). 'sed' is your friend.

_Return value:_ None.

5.5.13 lib.map.Equal
--------------------

-- Function: boole Equal (map A, map B)
It's the Turing Test for maps.

* _Return value:_ We return the number zero, if, and only if, A
and B are not functionally the same, meaning that:

* Worldspace needs to be the same.

* PWC needs to be the same.

* And every non-zero entry (an actual cell, that is) needs
to be present, and the same, in both maps.

* _Side effects:_ None.

5.5.14 lib.map.Invalid
----------------------

-- Function: boole Invalid (map M)
This function judges the un-Map-ness of M.
* M, a potential Map.

* _Return value:_ The number zero ('False') if M could be a Map.
Otherwise we return a positive integer indicating where M
fails short of being a Map. Please consult the source code
for more information.

* _Side effects:_ None.

5.6 Pixmaps
===========

Turns an ASCII art pattern into a pattern of objects.

*TL;DR*: You'll find examples under 'examples\pixmap'. To convert a
'.xpm'-file to a Coda Pixmap is pretty straightforward. Then
"lib.pixmap.Draw" it, then "floor" it in a Background script.

About X_Pixmaps and our pixmaps
...............................

We stole this idea from X_PixMaps as introduced by X Windows for UNIX
(see Wikipedia for "X_PixMap"). There, each character of the ASCII art
picture corresponds to a pixel of a certain colour. We, of course,
substitute colours with objects. *Heads up!* Let's state right up
front that we don't support X_PixMaps where more than one character
define a colour (used for _many_ colours). I find it hard to believe
that such a feature would be of use for our purpose. Just reduce the
number of colours, eg in GIMP. Or drop a line in the comment section on
Nexusmods, if you feel you need a more versatile pixmap.

Pixmaps are first placed on a plane in plein air, well above the
ground, before being "floored," using the very same method you would use
on an object in the Render window when pressing the "F" key. This
"flooring" has to be done in a Background process.

Converting a XPixMap to a Coda Pixmap.
......................................

Example, A XPM file (this one being called 'pferdchen.xpm') as returned
by GIMP looks like this:
/* XPM */
static char * C:\Users\ich\Desktop\pferdchen_xpm[] = {
"40 44 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
" ..... ",
" ............ ",
" ............... ",
" .......+.+.......... ",
" ......++++++++......... ",
" .....++++++++++++........ ",
" ......++++++++++++++........ ",
" .......++++++++++++++......... ",
" .........+...+++++++++.......... ",
" ...............++++++++........... ",
" .............++++++++++............ ",
" ............++++++++++++.......+.... ",
" .............++++++++++++......++.... ",
" ....+++....+++++++++++++.....++++.... ",
" ...++++++.++++++++++++++....++++++... ",
" ..++..+++++++++++++++++.....++++++....",
" .+.....+++++++++++++.+.....++++++.....",
" .+++.+++.+++++++++++++.......+++++.+...",
" .++..++++++++++++++++++......+++++.+...",
"..++.++..++++++++++++++++......++++.+...",
"..++.+....+++++++++++++++++.....++++++..",
"..+.++.....++++++++++++++++++++...++++..",
"...+++........++++++++++++++++++..++++..",
"....+..........++++++++++++++++++.+++...",
"....++..........++++++++++++++++++......",
"....++............++++++++++++++++......",
" ...+...............++++++++++++++......",
" .......................+++++++++.......",
" .......................+++++++++.......",
" ......................++++++++....... ",
" .......................++++++........ ",
" ......................+++++++...... ",
" ......................+++++++..... ",
" ......................+++.++++... ",
" ..................+++++..+++.. ",
" ................+++....++.... ",
" .............++++....++.... ",
" ..........+++.....++..... ",
" .........++....++++.... ",
" .......+++....++..... ",
" ............+++... ",
" ...........++... ",
" ............ ",
" ... .. "};

Apart from the actual picture, we are interested in the size (40
columns by 44 rows), and the colours: there are three of them, "." for
the colour with the colour code '#00000' (black), "+" for colour
'#FFFFFF' (white), and the empty space for "None" (meaning
transparency). Furthermore, the "1" in the line '"40 44 3 1"' tells us
that a colour is defined by just one character - meaning that this XPM
file is ready for use.

We choose the baseform "LadysSmock", a white plant, for "+", and
zero, or transparency, for both the empty space and the period. This
may translate into a Coda pixmap as:
coda(GetPferdchenXpm)
var colors = ArCreate(0)
var aa = ArCreate(0)
begin
@ar.assoc.Push(colors, " ", #0)
@ar.assoc.Push(colors, ".", #0)
@ar.assoc.Push(colors, "+", "LadysSmock")

@ar.assoc.Push(aa, " ..... ")
@ar.assoc.Push(aa, " ............ ")
@ar.assoc.Push(aa, " ............... ")
@ar.assoc.Push(aa, " .......+.+.......... ")
@ar.assoc.Push(aa, " ......++++++++......... ")
@ar.assoc.Push(aa, " .....++++++++++++........ ")
@ar.assoc.Push(aa, " ......++++++++++++++........ ")
@ar.assoc.Push(aa, " .......++++++++++++++......... ")
@ar.assoc.Push(aa, " .........+...+++++++++.......... ")
@ar.assoc.Push(aa, " ...............++++++++........... ")
;; ... etc ...
@ar.assoc.Push(aa, " .......+++....++..... ")
@ar.assoc.Push(aa, " ............+++... ")
@ar.assoc.Push(aa, " ...........++... ")
@ar.assoc.Push(aa, " ............ ")
@ar.assoc.Push(aa, " ... .. ")
return(@lib.pixmap.Make(colors, aa))
end

Of course, we may produce simple ASCII Art images by hand. How about
just a single line of eight flowers:
aRow = @lib.pixmap.Make(@ar.assoc.Init(@ar.4("a", "Alkanet", \
"b", "Bergamot01")),
@ar.1("abababab"))

Drawing a Pixmap in the Game World
..................................

In order to place a pixmap in the game world, we need the position, and
the orientation.

Currently this library provides just one method, by means of
diagonally opposed corner points(1)

So, choose the upper left corner as well as the bottom right one, and
call the 'Draw' funtion.

Dropping the objects to the floor
.................................

We suggest you having a look at 'examples\pixmap\PlotPferdchenXpm.coda',
well suited as a blueprint.

Adjust the colours, the ASCII art, and the location - all in the
'StageInit' part -, and off you go. But, please, also check the
'StageEnd' part: There's a call to 'lib.util.Wiggle' there, which
prevents living flowers from standing there like tin soldiers.

What 'examples\pixmap\PlotPferdchenXpm.coda' does, can be summarised
like this:
* Initialise the pixmap.

* Load the cell into the Render window. (Takes a few seconds.)

* Load every object in the pixmap in turn into the Render window, and
"FloorRef" it until it really touches the ground (as opposed to eg
another plant).

* "Wiggle" all objects a bit in order to make the whole thing look
less sterile (*note lib.util.Wiggle::).

---------- Footnotes ----------

(1) If there's interest, we could add more. Ideas:
* Place a square, and therefore an inscribed circle, by its centre
point, and the midpoint of the "top" side.

* Give an vector defining the "top side", and a minimal distance for
objects to be placed.

* Project a rectangular strip onto a circle.

* ... (Insert your suggestions here. Or on the Nexusmods mod page.)
...

5.6.1 lib.pixmap.Make
---------------------

-- Function: alist Make (alist COLORS, array ASCIIART)

Turns an ASCII art pattern into a pattern of objects(1).

* COLORS, an association list, where the keys are single
character strings that appear in ASCIIART, and values are
EditorIDs of base forms.

* ASCIIART, an array of strings of uniform length, where the
individual characters correspond to the characters in COLORS.

We perform some basic error checks: Width of rows in ASCIIART must
be uniform; every character in ASCIIART must be declared in COLORS.

* _Return value:_ A "Pixmap", an alist with these key/value
pairs:
* "colors" -> COLORS
* "asciiArt" -> ASCIIART
* "width" -> length of ASCIIART strings
* "height" -> array size of ASCIIART
* "forms" -> #0, 'lib.pixmap.Draw' will set this to a
2-dimensional array.

* _Side effects:_ None here, but on return, the arrays passed as
parameters are part of the pixmap. Don't touch them.

Example: A single row of eight flowers, with empty space at either
end:
aRow = @lib.pixmap.Make(\
@ar.assoc.Init(\
@ar.6(\
".", #0, \
"a", "Alkanet", \
"b", "Bergamot01")), \
@ar.1(".abababab.")

---------- Footnotes ----------

(1) We stole the idea from pixmaps as introduced by X Windows for
UNIX (see Wikipedia for "X_PixMap"). There, each character of the ASCII
art picture corresponds to a pixel of a certain colour. Here, of
course, we substitute colours with objects.

5.6.2 lib.pixmap.Draw
---------------------

deindex lib.pixmap.Draw deindex Pixmap Draw deindex Draw Pixmap
-- Function: pixmap Draw (pixmap PIXMAP, array|ref UPPERLEFT array|ref
LOWERRIGHT, float Z, ref CELL, boolean EXECUTE)

Place PIXMAP in the game world. Alternatively, perform the
required calculations without changing the game world.

* PIXMAP. May have been "drawn" already, or be fresh out of the
mill (*note lib.pixmap.Make::).

* UPPERLEFTL and LOWERRIGTH denote the corner points of the
pixmap in the game world (that's us talking about the
_orientation_ of the pixmap, not the compass). Either of them
may be given as a pair of floats (the '(x, y)' coordinates),
or as a reference(1). For example, place a mushroom there,
and either copy'n'paste the 'x' and 'y' coordinates, or the
FormID.

* The Z coordinate should always be above the highest point of
where the pixmap is supposed to appear (in order to be able to
properly "floor" objects later). This includes trees and
rocks, mind!

* CELL, the cell reference being passed to 'CreateRef'.

* EXECUTE: If "False", the function does not actually do
anything in the game world; it only performs the necessary
calculations. Useful when you want to check up front whether
the pixmap would fit like intended. If "True", the function
checks whether it had been called for PIXMAP before, and if
so, draws it without recalculating its parameters.

PIXMAP gains the following key->value pairs:
* Keys "a", "b", "c", "d" -> '(x,y)' coordinates of the corner
points of this rectangle:
a-----b
| |
c-----d
where 'a' equals UL and 'd' equals LR.

* Keys "ColUnit", "RowUnit" -> vectors of the length of the
distances between columns, or rows, respectively, pointing in
the direction of 'a->b', or 'a->c' respectively.
a + (width * colUnit) = b
a + (height * rowUnit) = c
etc.

* On return, key "Forms" holds a 2-dimenional array of the
placed forms, with zeroes filling up the transparent
background. In order to get all forms, and only forms, in a
single array, you may do something like
forms = @ar.Filter(@ar.Flatten(@ar.assoc.Get(xpm, "forms")), "lib\\pred\\NonZero")

* _Return value:_ PIXMAP.

* _Side effects:_ PIXMAP.

---------- Footnotes ----------

(1) Actually, it's safe to pass an array of any size(like, three), as
we consider only the the first two elements, and ignore the rest.

5.6.3 lib.pixmap.GetColorAt
---------------------------

-- Function: multi GetColorAt (Pixmap PIXMAP, int COL, int ROW)

* _Return value:_ The ASCIIArt component at that point.

* _Side effects:_ None.

5.6.4 lib.pixmap.Transpose
--------------------------

-- Function: pixmap Transpose (pixmap XPM)
-- Function: pixmap MirrorH (pixmap XPM)
-- Function: pixmap MirrorV (pixmap XPM)

Manipulates XPM's 'AsciiArt'. Actually, this function doesn't
change XPM, but rather creates a new pixmap instead.

Provided that XPM looks like this,
a--b
xpm = | | (four columns, three rows)
c--d
then these functions would yield
a-c
transpose(xpm) = | |, (three columns, four rows)
| |
b-d

c--d
mirrorH(xpm) = | |,
a--b

b--a
mirrorV(xpm) = | |
d--c
Rotation about 90° to the right:
rot90 = mirrorV(transpose(xpm))
Rotation about 180°:
rot180 = mirrorV(mirrorH(xpm))
Rotation about 90° to the left:
rot270 = transpose(mirrorV(xpm))
_Reminder:_ 'lib.pixmap.Draw' called on transposed rectangles (not
squares) changes the orientation of the pixmap in the gameworld
when called with the same diagonal coordinates as the un-transposed
rectangle.

* _Return value:_ A freshly allocated pixmap.

* _Side effects:_ None.

5.7 Formatting Output
=====================

lib.Fmt strives to be the one-stop shop for formatting various data
types. Makes debugging, and reporting, so much easier.
There are also a couple of formatting functions for special uses,
e.g. 'lib.fmt.RubberArray', or 'lib.fmt.Time'.
5.7.1 lib.Fmt
-------------

-- Function: string Fmt (any THING)

'Fmt' tries to find a "canonical" representation of THING, ready to
'PrintToConsole'.

For arrays, we chose not do write out the contents (too messy),
just the size and overall number of elements. Call
'@lib.fmt.Array' (*note lib.fmt.Array::) if you need to.

5.7.2 lib.fmt.Array
-------------------

-- Function: string Array (array A)
This dives into A, collecting "canonical" representations of
elements of A, calling itself recursively for arrays (thus breaking
for arrays deeper than Coda's (adjustable) recursion limit).

a = @ar.4("tree", #1, "disabled", #0)
printc("a "//[email protected](a))
b = @ar.assoc.Init(a)
printc("b "//[email protected](b))
printc("b "//[email protected](b))
--->
[CSE] a
[CSE] b
[CSE] b (([0:0:]<"tree">([0:1:0:]<1.000000>))([1:0:]<"disabled">([1:1:0:]<0.000000>)))

I use this thing only when nested arrays got me really confused.

5.7.3 lib.fmt.RubberArray
-------------------------

-- Function: string RubberArray (R)
* _Return value:_ Descriptive string.

* _Side effects:_ None.

5.7.4 lib.fmt.Cell
------------------

-- Function: string Cell (ref C)
Returns a string describing the cell C.
@lib.fmt.Cell(cell) ==> "Cell 0x12345678 'EditorID' "

5.7.5 lib.fmt.Concat
--------------------

-- Function: string Concat (array A, string SEP)

Concatenates the "canonical" string representations of the elements
of A, seperated by SEP. A hexadecimal number indicates a
reference.

Like so:
printc(@lib.fmt.Concat(\
@ar.5(@ar.4(1, 2, 3, 4), (ref)0x467, 1, pi, "huhu"),\
"<-->"))
===> array[4]<-->0x00000467<-->1<-->3.14159<-->"huhu"

5.7.6 lib.fmt.Ints
------------------

-- Function: string Ints (array A)
This function was meant for ("small") signed integers, but actually
works for other numbers, too.

_Return value:_ A string where the elements of A are formatted as
signed integers, seperated by commas. Returns an empty string for
an empty A.

_Side effects:_ None.
@lib.fmt.Ints(@ar.4(-71, -44, 55, 60))
==> "-71, -44, 55, 60" ; Range of coordinates in Morrowind ^_^

5.7.7 lib.fmt.Ref
-----------------

-- Function: string Ref (ref X)
Returns a string describing the reference X.
@lib.fmt.Ref((ref)0x467) -->
0x00000467 EID='shadeMe' FT=35 'NPC'

@lib.fmt.Ref((ref)17D00E)
0x0017D00E EID='AerinCampBed' FT=49 'REFR' BF: 0x0001D5C0 EID='BedrollCrawlEntry'

5.7.8 lib.fmt.Time
------------------

-- Function: string Time (float SEC)

Convert SEC as seconds into a human readable time format.

I didn't implement weeks, months, or years....

Typical usage:
printc(GetSecondsPassed())
And here's how it looks like:
printc([email protected](60*60*48 + 60*60*11 + 60*15 + pi))
[CSE] 2 days, 11:15:3.13

5.8 Utilities
=============

Allow me to introduce you to 'lib.util.FTID'. One of my best, and
oldest, friends, although a bit slow to respond.
5.8.1 lib.util.CheckThis
------------------------

-- Function: boole checkThis ( THIS, string TITLE)

Prints "TITLE: True|False", depending on THIS. Saves a few lines
of code.

Sometimes you just want to be sure. (Like, after a long time away
from Coda, "what was that with the last parameter of StringCompare,
1 for case sensitive, or 1 for case INsensitive?"; saves you
getting the manual from the shelf.)

@lib.util.checkThis((StringCompare("huhu", "HUHU", 1) == 0), "str=?")
--> [Console] str=?: True
@lib.util.checkThis(GetRefDisabled((ref)0x6bd54), "IniD")
--> [Console] IniD: False

5.8.2 lib.util.CopyRef
----------------------

-- Function: ref CopyRef (ref SRC, ref BASEFORM)
-- Function: ref CopyRef (ref SRC, assoc ARGV)

Creates and returns a copy of a placeable object.

In the first form, the copy inherits all parameters save the base
form from SRC.

The second form additionally offers control over a bunch of
parameters. As it calls a dozen of library functions, it is also
comparibly slow.

* SRC, the object to copy.

* BASEFORM, the base form the copy will have. The copy will
inherit SRC's data. Just say "0" if you mean SRC's baseform.

* ARGV, an alist of named parameters:
* "BaseForm"
* => -1: inherit base form (default).
* => reference: copy will have this as baseform

* "VWD"
* => -1: inherit state (default)
* => 0
* => 1

* "Scale"
* => -1: inherit state (default)
* => number:

* "Disabled" (CSE flag "initially disabled")
* => -1: inherit state (default)
* => 0
* => 1

* "Modified" (Coda function 'MarkAsModified')
* => 0:
* => 1: (default)

* "Cell"
* => -1: inherit cell (default).
* => reference: copy will be placed in this cell

* "Position"
* => -1: inherit state (default).
* => 3-tuple of coordinates: absolute position

* "relative Position"
* => -1: inherit state (default).
* => 3-tuple of coordinates: add these to the original
position

* "Rotation"
* => -1: inherit state (default).
* => 3-tuple of radiants: absolute rotation

* "relative Rotation"
* => -1: inherit state (default).
* => 3-tuple of radiants: add these to the original
rotation

* "Disable source"
* => -1: don't touch (default)
* => 0
* => 1

* "Mark source as modified"
* => -1: When the above option ("disable source")
changed SRC's state, then mark it as modified;
otherwise don't. This seems sensible and therefore
is the default: (default)
* => 0:
* => 1:

In short:
* _Return value:_ Reference.

* _Side effects:_ May alter SRC.

5.8.3 lib.util.Div
------------------

-- Function: Function array Div (ref DIVIDEND, ref DIVISOR)
Division of natural numbers.

* 0 <= DIVIDEND

* 0 < DIVISOR

* _Return value:_ A 2-array [quotient, remainder].

* _Side effects:_ None.

Note, that we check for the divisor not to be zero, but nothing
else. Take care.

@lib.util.Div(10, 3)
--> [3, 1]
@lib.util.Div(0, 3)
--> [0, 10]

5.8.4 lib.util.FTID
-------------------

-- Function: int FTID (string FORMTYPE)
-- Function: string FTID (int FTID)
-- Function: string FTID (ref AREF)
-- Function: string FTID ("?")
Form Type ID as detailed in the Coda Command Documenation for
'GetDataHandlerFormList'.

* FORMTYPE is a string describing a form type (e.g. "NPC",
"Weather", ...). _Return_ the corresponding Form Type ID.

* 0 =< FTID <= 68. _Return_ the corresponding string ("NPC",
"Weather", ...)

* AREF is a reference. _Return_ its Form Type. If AREF is
zero, return zero (Form Type "None").

* If called with the string '"?"', however, we _return_ the
table given in the aforementioned doc. as a 2-dim array (a
service for those who are too lazy to get the manual from the
shelf).
Examples.
@lib.util.FTID("NPC")
---> 35
@lib.util.FTID(35)
---> "NPC"
@lib.util.FTID((ref)0x467) ; NPC shademe
---> "NPC"
This one prints the table of Form Type IDs, with floats, sadly:
@ar.PrintMatrix(@lib.util.FTID("?"), "", "", ": ")

5.8.5 lib.util.GetAllCellsInRect
--------------------------------

-- Function: array GetCellsInWorldspace (ref|string WS)
-- Function: array GetCellsInRect (ref|string WS, array RECT)
-- Function: array GetAllCellsInWorldspace (ref|string WS, int|ref PWC)
-- Function: array GetAllCellsInRect (ref|string WS, int|ref PWC, array
RECT)

'GetCellsInWorldspace' returns all non-empty cells in the
worldspace WS (interiors if WS is zero).

'GetCellsInRect' returns all non-empty cells in the given
rectangle, borders included.

The '..All..' versions return a triplet of '[empty-cells, non-empty
cells, PWC]' (PWC = persistent worldpsace cell, see *Note Map::).
They also allow for a more specific handling of the PWC problem.
In fact, the non-All versions are just syntactic sugar, returning
the 2nd element of that triplet.

Parameters:
...........

* WS, a worldspace, given as reference or EditorID. Can't be
zero for 'GetCellsInRect'.

* PWC: How to handle the "Persistent Worldspace Cell" for
exterior worldspaces:
* 0: don't check (because you know what you're doing)
* 1: do check
* cell reference: this is the PWC, exclude it.

* RECT, an array of coordinates defining a rectangle.
* RECT may be given as an array of four numbers (e.g.
'@ar.4(x1, y1, x2, y2)'.

* Alternatively, RECT may be an array of two cornerpoints,
where each point may be given as a cell reference or as a
pair of coordinates, mixing allowed.

Returned data:
..............

We group the cells in a triplet '[empty-cells, non-empty-cells,
PWC]' where
* 'empty-cells' is an array of cells that hold no objects

* 'non-empty-cells' is an array of cells that do

* and 'PWC' is either the persistent worldspace cell or the
number zero.

* _Side effects:_ None.

Examples:
;; PWC here, but I don't know its ref, so check.
var SEQuadrant = @lib.util.GetAllCellsInRect(\
worldspace, 1, @ar.2(@ar.2(0,0), cellFarSE))
;==> [[empty-cell, ..], [non-empty-cell, ..], refToPWC]

;; No PWC here, so don't bother.
var NWQuadrant = @lib.util.GetAllCellsInRect(\
worldspace, 0, @ar.4(-50,50,-1,0))
;==> [[empty-cell, ..], [non-empty-cell, ..], 0]

We apply some checking for valid parameters as a safeguard, but not
for the PWC: if you pass one, it must be valid.

5.8.6 lib.util.GetCoordinates
-----------------------------

-- Function: array GetCoordinatess (array|ref THING)

* If THING is a cell reference, _return_ with
'GetCellCoordinates', for any other reference, _return_ the
reference's x, y, z coordinates as a triple of floats; if an
array, _return_ that.

* _Side effects:_ None.
We're using this as shorthand in some function calling schemes.

5.8.7 lib.util.GetEditorID
--------------------------

-- Function: string GetEditorID (ref x)
Interfaces the built-in 'GetEditorID'.

Returns the EditorID of X.

5.8.8 lib.util.GetRefBaseForm
-----------------------------

-- Function: ref GetBaseForm (ref X)
Interfaces the built-in 'GetBaseForm'.

_Returns_ the base form of X.

5.8.9 lib.util.makeMapFromLandscapeTextures
-------------------------------------------

-- Function: map makeMapFromLandscapeTextures (ref|string WORLDSPACE,
string SUBSTR, int WHERE)

Forms a map (*note Map::) out of cells whose EditorIDs match a
string.

* WORLDSPACE, a reference to a worldspace, or the EditorID of
one such.
* SUBSTR and
* WHERE define the search string (*note
lib.pred.LandscapeTextureSubstringAt::)

* _Return value:_ *note Map::

* _Side effects:_ none
bitterCoast = makeMapFromLandscapeTextures("WrldMorrowind", "mwBC", 0)

5.8.10 lib.util.RefDistance
---------------------------

-- Function: float RefDistance (ref A, ref B)
* _Return value:_ Distance of the two arguments given. Hint:
'165.0' game units may be about a step or two.

* _Side effects:_ None

5.8.11 lib.util.RotateVector
----------------------------

-- Function: array RotateVector (array V, float RHO)

Planar rotation. Rotate V by RHO in the math. positive sense
(counter-clockwise).
* V, a 2-array of floats.

* RHO.

* _Return value:_ The rotated V, as a freshly allocated array.

* _Side effects:_ None.

5.8.12 lib.util.Wiggle
----------------------

-- Function: array Wiggle (array OBJECTS, array|boolean DELTA,
array|boolean ROTATION)
Randomly shift and rotate OBJECTS.
* OBJECTS, an array of references.

* DELTA, a triplet (dx,dy,dz), or the number zero. Move an
object about maximally that distance (e.g. +-dx, that is).

* ROTATION, a triplet (rx, ry, rz), or the number zero. Rotate
an object about maximally that amount; (rx, ry, rz) are
relative to the existing rotation. In radiant (2*pi ~ 360°).

* _Return value:_ OBJECTS.

* _Side effects:_ Hell, yeah. But not to the parameters
themselves.

5.9 Strings
===========

5.9.1 lib.string.Match
----------------------

-- Function: array Match (STRING, PATTERN, IGNORECASE)

A very simple pattern matching routine. PATTERN is an array of
strings and wildcards (given as numbers), where numbers greater
than zero mean that exact number of characters, and '-1' means any
number of characters.

Restrictions:
* 'Match' cannot look ahead and does not remember. It cannot
perform tasks like this one: '@lib.string.Match("foobarbaz",
@ar.3(-1, 3, "baz"), #1)' In other words, "store all until you
find a certain token ('baz'), and collect the three characters
that came before ('bar')." I'd rather have shadeMe including
regular expressions with CSE than me implementing stuff like
this in Coda.

Therefore,
* emtpy strings or the number zero are illegal as pattern
components, and

* using a wildcard immediately after a '-1' wildcard is an
error.

I think it is still worth including it in the library, as I hope
the example for Morroblivion below illustrates.

* _Return value:_ "False" if STRING does not match PATTERN.
Otherwise an array containing matches for PATTERN's wildcards.

* _Side effects:_ None.

Examples:
@lib.string.Match("0floraUbcUtreeU01", @ar.4("0floraU", 2, "UtreeU", -1), #1)
--> ["bc", "01"]
@lib.string.Match("0floraUbcUtreeU01", @ar.1(-1), #1)
--> ["0floraUbcUtreeU01"]
@lib.string.Match("0floraUbcUtreeU01", @ar.2(2, -1), #1)
--> ["0f", "loraUbcUtreeU01"]
@lib.string.Match("0floraUbcUtreeU01", @ar.3(-1, "bc", -1), #1)
--> ["0floraU", "UtreeU01"]

The example below groups all static trees (meaning NIF rather than
SpeedTrees) in Morroblivion with respect to their region (note the
use of the association list 'tree').
;; a static Bittercoast tree looks like "0floraUbcUtreeU01"
;; => search for "0floraU" 'bc' "UtreeU" '...'
pattern = @ar.4("0floraU", 2, "UtreeU", -1)

tree = ArCreate(0) ; an (empty) association list
foreach r <- GetDataHandlerFormList(@lib.util.FTID("Stat"))
m = @string.Match(GetEditorID(GetRefBaseForm(r)), pattern, #1)
if ((TypeId)m == TypeInfo_Array)
@ar.assoc.Push(trees, m[0], r)
endif
loop

BittercoastTrees = @ar.assoc.GetAll(trees, "bc")

5.9.2 lib.string.Replace
------------------------

-- Function: string Replace (string SOURCE, string INSERTION, int POS,
boolean EXPAND)

Replaces part of SOURCE with INSERTION, starting at POS. If EXPAND
is "False", the resulting string may not be larger than SOURCE.

* _Return value:_ A new string.

* _Side effects:_ None.
@lib.string.Replace("0123456789", "...", 4, #0)
---> "0123...789"
@lib.string.Replace("0123456789", "+++", 9, #1)
---> "012345678+++"
@lib.string.Replace("", "ABC", 0, #1)
---> "ABC"

5.9.3 lib.string.Reverse
------------------------

-- Function: string Reverse (string S)

_Returns_ a string that reads S backwards. _Side effects:_ None.

General Index
*************

* Menu:

* Alteration, Array Functions: Alteration. (line 472)
* Array Functions: Array Functions. (line 354)
* Array Functions, Alteration: Alteration. (line 472)
* Array Functions, Association List: Association List. (line 796)
* Array Functions, Creation: Creation. (line 360)
* Array Functions, Introspection: Introspection. (line 642)
* Array Functions, Mapping and Filtering: Mapping and Filtering.
(line 564)
* Array Functions, Output: Output. (line 722)
* Array Functions, Reference Array: Reference Array. (line 958)
* Array Functions, Rubber Array: Rubber Array. (line 1039)
* Array Functions, Sorting: Sorting. (line 1203)
* Association List, Array Functions: Association List. (line 796)
* Background Scripts, Library Functions: Background Scripts. (line 1313)
* bug, semicolon in strings: Words of Wisdom. (line 221)
* calling, conventions: Conventions. (line 334)
* Coda, Introduction: Introduction. (line 164)
* Coda, overview: About Coda. (line 174)
* coding, hints: Words of Wisdom. (line 221)
* Comparisons, Library Functions: Comparisons. (line 1354)
* compatibility, Little Coda Toolbox: Compatibility. (line 214)
* console output, monitoring: Words of Wisdom. (line 221)
* conventions: Conventions. (line 302)
* conventions, calling: Conventions. (line 334)
* conventions, naming: Conventions. (line 302)
* Copyright: Copying. (line 145)
* Creation, Array Functions: Creation. (line 360)
* Formatting Output, Library Functions: Formatting Output. (line 2478)
* Functions: Array Functions. (line 354)
* Functions <1>: Library Functions. (line 1310)
* Graphs, Library Functions: Graphs. (line 1559)
* hints, coding: Words of Wisdom. (line 221)
* hints, performance: Words of Wisdom. (line 221)
* Introduction: Introduction. (line 164)
* Introduction, Coda: Introduction. (line 164)
* Introduction, Little Coda Toolbox: Introduction. (line 164)
* Introspection, Array Functions: Introspection. (line 642)
* Library Functions: Library Functions. (line 1310)
* Library Functions, Background Scripts: Background Scripts. (line 1313)
* Library Functions, Comparisons: Comparisons. (line 1354)
* Library Functions, Formatting Output: Formatting Output. (line 2478)
* Library Functions, Graphs: Graphs. (line 1559)
* Library Functions, Map: Map. (line 1709)
* Library Functions, Pixmaps: Pixmaps. (line 2139)
* Library Functions, Predicates: Predicates. (line 1384)
* Library Functions, Strings: Strings. (line 2911)
* Library Functions, Utilities: Utilities. (line 2588)
* License: Copying. (line 145)
* Little Coda Toolbox, compatibility: Compatibility. (line 214)
* Little Coda Toolbox, Introduction: Introduction. (line 164)
* Little Coda Toolbox, overview: About The Little Coda Toolbox.
(line 187)
* Map, Library Functions: Map. (line 1709)
* Mapping and Filtering, Array Functions: Mapping and Filtering.
(line 564)
* MarkAsModified: Words of Wisdom. (line 221)
* monitor console output: Words of Wisdom. (line 221)
* naming, conventions: Conventions. (line 302)
* Output, Array Functions: Output. (line 722)
* overview, Coda: About Coda. (line 174)
* overview, Little Coda Toolbox: About The Little Coda Toolbox.
(line 187)
* performance, hints: Words of Wisdom. (line 221)
* Persistent Worldspace Cell, the: Map. (line 1749)
* Pixmaps, Library Functions: Pixmaps. (line 2139)
* Predicates, Library Functions: Predicates. (line 1384)
* PWC, Persistent Worldspace Cell: Map. (line 1749)
* Reference Array, Array Functions: Reference Array. (line 958)
* Rubber Array, Array Functions: Rubber Array. (line 1039)
* semicolon in strings, bug: Words of Wisdom. (line 221)
* Sorting, Array Functions: Sorting. (line 1203)
* Strings, Library Functions: Strings. (line 2911)
* Utilities, Library Functions: Utilities. (line 2588)
* wisdom, words of: Words of Wisdom. (line 221)

Functions
*********

Listing of all functions in the library.

* Menu:

* 0: ar.0. (line 363)
* 1: ar.0. (line 364)
* 10: ar.0. (line 373)
* 2: ar.0. (line 365)
* 3: ar.0. (line 366)
* 4: ar.0. (line 367)
* 5: ar.0. (line 368)
* 6: ar.0. (line 369)
* 7: ar.0. (line 370)
* 8: ar.0. (line 371)
* 9: ar.0. (line 372)
* Append: ar.Append. (line 475)
* Argv: ar.assoc.Argv. (line 818)
* Array: lib.fmt.Array. (line 2497)
* array: lib.util.Div. (line 2694)
* at0: ar.Nth. (line 646)
* at1: ar.Nth. (line 647)
* at2: ar.Nth. (line 648)
* BFEidSubStr: lib.pred.BFEidSubStr.
(line 1401)
* BFEIDSubStrAt: lib.pred.BFEidSubStr.
(line 1403)
* Cell: lib.fmt.Cell. (line 2525)
* CellEmpty: lib.pred.CellEmpty. (line 1389)
* Census: ar.Census. (line 688)
* checkThis: lib.util.CheckThis. (line 2593)
* Compare: lib.comp.Compare. (line 1359)
* Concat: lib.fmt.Concat. (line 2532)
* Copy: ar.Copy. (line 388)
* Copy <1>: ar.rubber.Copy. (line 1150)
* Copy <2>: lib.map.Copy. (line 1844)
* CopyRef: lib.util.CopyRef. (line 2611)
* CopyRef <1>: lib.util.CopyRef. (line 2612)
* Cut: ar.Cut. (line 491)
* Delete: ar.assoc.Lookup. (line 876)
* Draw: lib.pixmap.Draw. (line 2363)
* EidCompare: lib.comp.EidCompare.
(line 1375)
* EidSubStr: lib.pred.BFEidSubStr.
(line 1402)
* EIDSubStrAt: lib.pred.BFEidSubStr.
(line 1404)
* Enum: ar.Enum. (line 405)
* Equal: lib.pred.Equal. (line 1434)
* Equal <1>: lib.map.Equal. (line 2107)
* Erase: ar.ref.Erase. (line 983)
* Exists: ar.assoc.Exists. (line 863)
* Fill: ar.Fill. (line 423)
* Filter: ar.Filter. (line 582)
* Filter1: ar.Filter. (line 583)
* Flatten: ar.Flatten. (line 513)
* FloorArray(): lib/Backgrounders/FloorArray.
(line 1330)
* Fmt: lib.Fmt. (line 2485)
* FTID: lib.util.FTID. (line 2716)
* FTID <1>: lib.util.FTID. (line 2717)
* FTID <2>: lib.util.FTID. (line 2718)
* FTID <3>: lib.util.FTID. (line 2719)
* Generic: ar.sort.Generic. (line 1301)
* Get: ar.assoc.Pop. (line 900)
* Get <1>: ar.rubber.Get. (line 1106)
* Get <2>: lib.map.Get. (line 1835)
* GetAll: ar.assoc.Lookup. (line 875)
* GetAllCellsInRect: lib.util.GetAllCellsInRect.
(line 2752)
* GetAllCellsInWorldspace: lib.util.GetAllCellsInRect.
(line 2751)
* GetBaseForm: lib.util.GetRefBaseForm.
(line 2839)
* GetCells: lib.map.GetCells. (line 1874)
* GetCellsInRect: lib.util.GetAllCellsInRect.
(line 2750)
* GetCellsInWorldspace: lib.util.GetAllCellsInRect.
(line 2749)
* GetColorAt: lib.pixmap.GetColorAt.
(line 2427)
* GetContent: ar.rubber.GetContent.
(line 1128)
* GetCoordinatess: lib.util.GetCoordinates.
(line 2818)
* GetEditorID: lib.util.GetEditorID.
(line 2831)
* GetExtent: ar.rubber.GetExtent.
(line 1138)
* GetExtent <1>: lib.map.GetExtent. (line 1888)
* GetHood: lib.map.GetHood. (line 1900)
* GetHood <1>: lib.map.GetHood. (line 1901)
* GetHood <2>: lib.map.GetHood. (line 1902)
* GetHood <3>: lib.map.GetHood. (line 1903)
* GetIndex: ar.ref.GetIndex. (line 1000)
* GetIndex(array: ar.GetIndex. (line 661)
* GetIndexGen(array: ar.GetIndex. (line 662)
* GetIndexPP: ar.ref.GetIndex. (line 1001)
* GetIntersection: ar.ref.GetIntersection.
(line 1011)
* GetKeys: ar.assoc.GetKeys. (line 834)
* hasBF: lib.pred.hasBF. (line 1452)
* Inc: ar.assoc.Push. (line 918)
* Inc <1>: ar.rubber.Set. (line 1115)
* Init: ar.assoc.Init. (line 842)
* Ints: lib.fmt.Ints. (line 2547)
* Invalid: ar.rubber.Invalid. (line 1191)
* Invalid <1>: lib.map.Invalid. (line 2125)
* IsMember: ar.IsMember. (line 673)
* IsMemberGen: ar.IsMember. (line 674)
* Join: ar.Join. (line 528)
* Join <1>: ar.ref.Join. (line 1020)
* Join <2>: ar.ref.Join. (line 1021)
* Join <3>: lib.map.Join. (line 2018)
* JoinGen: ar.Join. (line 529)
* LandscapeTextureSubstringAt: lib.pred.LandscapeTextureSubstringAt.
(line 1460)
* Lookup: ar.assoc.Lookup. (line 874)
* Make: ar.Make. (line 438)
* Make <1>: ar.Make. (line 439)
* Make <2>: ar.ref.Make. (line 970)
* Make <3>: ar.rubber.Make. (line 1077)
* Make <4>: ar.rubber.Make. (line 1078)
* Make <5>: lib.graph.Make. (line 1621)
* Make <6>: lib.map.Make. (line 1775)
* Make <7>: lib.pixmap.Make. (line 2316)
* Make1: lib.graph.Make. (line 1623)
* makeMapFromLandscapeTextures: lib.util.makeMapFromLandscapeTextures.
(line 2847)
* Map: ar.Map. (line 567)
* Match: lib.string.Match. (line 2914)
* maxDistance: lib.pred.MaxDistance.
(line 1487)
* Merge: ar.rubber.Merge. (line 1159)
* Merge <1>: lib.map.Merge. (line 2053)
* MirrorH: lib.pixmap.Transpose.
(line 2437)
* MirrorV: lib.pixmap.Transpose.
(line 2438)
* Multi: ar.sort.Multi. (line 1285)
* Negative: lib.pred.Negative. (line 1496)
* NonNegative: lib.pred.Negative. (line 1497)
* NonZero: lib.pred.Negative. (line 1499)
* NotCellEmpty: lib.pred.CellEmpty. (line 1390)
* NotEqual: lib.pred.Equal. (line 1435)
* NotPersistentWorldspaceCell: lib.pred.PersistentWorldspaceCell.
(line 1513)
* Nth: ar.Nth. (line 645)
* parentWorldspace: lib.pred.parentWorldspace.
(line 1505)
* PersistentWorldspaceCell: lib.pred.PersistentWorldspaceCell.
(line 1512)
* Pop: ar.assoc.Pop. (line 899)
* Print: ar.Print. (line 725)
* Print <1>: ar.assoc.Print. (line 944)
* Print <2>: ar.ref.Print. (line 1032)
* Print <3>: lib.map.Print. (line 2060)
* PrintFmtNum: ar.PrintFmtNum. (line 753)
* Push: ar.Push. (line 547)
* Push <1>: ar.Push. (line 548)
* Push <2>: ar.assoc.Push. (line 916)
* Radix: ar.Sort. (line 1223)
* Random: ar.Random. (line 455)
* Ref: lib.fmt.Ref. (line 2562)
* RefDistance: lib.util.RefDistance.
(line 2867)
* Refs: ar.Sort. (line 1224)
* Remove: ar.rubber.Remove. (line 1173)
* Remove <1>: lib.map.Remove. (line 2037)
* Remove <2>: lib.map.Remove. (line 2038)
* Replace: lib.string.Replace. (line 2975)
* Reverse: lib.string.Reverse. (line 2994)
* RotateVector: lib.util.RotateVector.
(line 2876)
* RubberArray: lib.fmt.RubberArray.
(line 2517)
* sameBF: lib.pred.sameBF. (line 1535)
* sameEID: lib.pred.sameEID. (line 1541)
* sameWorldspace: lib.pred.sameWorldspace.
(line 1547)
* Save: lib.map.Save. (line 2079)
* Set: ar.assoc.Push. (line 917)
* Set <1>: ar.rubber.Set. (line 1114)
* Set <2>: lib.map.Get. (line 1836)
* SharesLink: lib.graph.Make. (line 1622)
* SharesLink1: lib.graph.Make. (line 1624)
* Shellsort: ar.Sort. (line 1221)
* ShowStructure: ar.ShowStructure. (line 696)
* Slice: ar.Cut. (line 492)
* Sort: ar.Sort. (line 1220)
* Split: ar.Split. (line 618)
* Split1: ar.Split. (line 619)
* Strings: ar.Sort. (line 1222)
* SubGraphs: lib.graph.SubGraphs.
(line 1642)
* SubMap: lib.map.SubMap. (line 1858)
* Time: lib.fmt.Time. (line 2573)
* Transpose: lib.pixmap.Transpose.
(line 2436)
* void: ar.Save. (line 768)
* Wiggle: lib.util.Wiggle. (line 2891)
* Zero: lib.pred.Negative. (line 1498)

Destructive Functions
*********************

I _do hope_ I've got all those in here

* Menu:

* Append: ar.Append. (line 475)
* ar.assoc.Delete: ar.assoc.Lookup. (line 874)
* ar.assoc.Inc: ar.assoc.Push. (line 916)
* ar.assoc.Pop: ar.assoc.Pop. (line 899)
* ar.assoc.Push: ar.assoc.Push. (line 916)
* ar.assoc.Set: ar.assoc.Push. (line 916)
* ar.Join: ar.Join. (line 528)
* ar.Join1: ar.Join. (line 528)
* ar.Pop, Stack: ar.Push. (line 547)
* ar.Push, Stack: ar.Push. (line 547)
* ar.ref.Erase, Erase: ar.ref.Erase. (line 983)
* ar.ref.Join: ar.ref.Join. (line 1020)
* ar.rubber.Remove: ar.rubber.Remove. (line 1173)
* ar.Sort: ar.Sort. (line 1220)
* ar.sort.Generic: ar.sort.Generic. (line 1301)
* ar.sort.Refs: ar.Sort. (line 1220)
* ar.sort.Shellsort: ar.Sort. (line 1220)
* ar.sort.Strings: ar.Sort. (line 1220)
* Ar.Split: ar.Split. (line 618)
* Ar.Split1: ar.Split. (line 618)
* assoc Inc: ar.assoc.Push. (line 916)
* assoc Push: ar.assoc.Push. (line 916)
* assoc Set: ar.assoc.Push. (line 916)
* CopyRef, lib.util.CopyRef: lib.util.CopyRef. (line 2611)
* Delete: ar.assoc.Lookup. (line 874)
* Erase, ar.ref.Erase: ar.ref.Erase. (line 983)
* Inc assoc: ar.assoc.Push. (line 916)
* Join, lib.map.Join: lib.map.Join. (line 2018)
* lib.map.Join, Join: lib.map.Join. (line 2018)
* lib.map.Remove: lib.map.Remove. (line 2037)
* lib.map.Set: lib.map.Get. (line 1835)
* lib.util.CopyRef, CopyRef: lib.util.CopyRef. (line 2611)
* Pop: ar.assoc.Pop. (line 899)
* Push assoc: ar.assoc.Push. (line 916)
* Remove, ar.rubber.Remove: ar.rubber.Remove. (line 1173)
* Remove, lib.map.Remove: lib.map.Remove. (line 2037)
* Set assoc: ar.assoc.Push. (line 916)
* Set, lib.map.Set: lib.map.Get. (line 1835)
* Stack, ar.Pop: ar.Push. (line 547)
* Stack, ar.Push: ar.Push. (line 547)