summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Theodore <tonyt@logyst.com>2014-02-08 11:43:13 (GMT)
committerTony Theodore <tonyt@logyst.com>2014-02-08 11:43:13 (GMT)
commit5fd5e9bbecb20f7f8d06b557111630325bcf85e0 (patch)
tree9cb4e54defb3c7ce121642fb89583a00ac03490d
parent082d50a3bded4ea2d32eadf475b6f708bcb003b3 (diff)
downloadmxe-5fd5e9bbecb20f7f8d06b557111630325bcf85e0.zip
mxe-5fd5e9bbecb20f7f8d06b557111630325bcf85e0.tar.gz
mxe-5fd5e9bbecb20f7f8d06b557111630325bcf85e0.tar.bz2
Makefile and docs: add GNU Make Standard Library
-rw-r--r--Makefile9
-rw-r--r--doc/gmsl.html704
-rw-r--r--index.html5
-rw-r--r--tools/__gmsl919
-rw-r--r--tools/gmsl89
-rw-r--r--tools/gmsl-tests730
6 files changed, 2454 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 5642194..5e8b5ad 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,13 @@
# This file is part of MXE.
# See index.html for further information.
+MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+TOP_DIR := $(patsubst %/,%,$(dir $(MAKEFILE)))
+
+# GNU Make Standard Library (http://gmsl.sourceforge.net/)
+# See doc/gmsl.html for further information
+include $(TOP_DIR)/tools/gmsl
+
MXE_TARGET_LIST := i686-pc-mingw32 x86_64-w64-mingw32 i686-w64-mingw32
MXE_TARGETS := i686-pc-mingw32
DEFAULT_MAX_JOBS := 6
@@ -34,8 +41,6 @@ LOG_DIR := $(PWD)/log
TIMESTAMP := $(shell date +%Y%m%d_%H%M%S)
PKG_DIR := $(PWD)/pkg
TMP_DIR = $(PWD)/tmp-$(1)
-MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
-TOP_DIR := $(patsubst %/,%,$(dir $(MAKEFILE)))
PKGS := $(shell $(SED) -n 's/^.* class="package">\([^<]*\)<.*$$/\1/p' '$(TOP_DIR)/index.html')
BUILD := $(shell '$(TOP_DIR)/tools/config.guess')
BUILD_PKGS := $(shell grep -l 'BUILD_$$(BUILD)' '$(TOP_DIR)/src/'*.mk | $(SED) -n 's,.*src/\(.*\)\.mk,\1,p')
diff --git a/doc/gmsl.html b/doc/gmsl.html
new file mode 100644
index 0000000..accdd16
--- /dev/null
+++ b/doc/gmsl.html
@@ -0,0 +1,704 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>GNU Make Standard Library</title></head>
+
+<body>
+<h1>GNU Make Standard Library</h1>
+The GNU Make Standard Library (GMSL) is a collection of functions
+implemented using native GNU Make functionality that provide list and
+string manipulation, integer arithmetic, associative arrays, stacks,
+and debugging facilities.&nbsp; The GMSL is released under the BSD License.<br>
+<br>
+<a href="http://sourceforge.net/projects/gmsl/">[Project Page]</a> <a href="http://sourceforge.net/project/showfiles.php?group_id=129887">[Download]</a>
+<a href="http://sourceforge.net/forum/forum.php?forum_id=443916">[Discussion
+Forum]</a><br>
+<h2>Using GMSL</h2>
+The two files needed are <span style="font-family: monospace;">gmsl</span>
+and <span style="font-family: monospace;">__gmsl</span>.&nbsp; To
+include the GMSL in your Makefile do<br>
+<pre style="margin-left: 40px;">include gmsl</pre>
+<span style="font-family: monospace;">gmsl</span> automatically includes<span style="font-family: monospace;"> __gmsl</span>.&nbsp; To check that
+you have the right version of <span style="font-family: monospace;">gmsl</span>
+use the <span style="font-family: monospace;">gmsl_compatible</span>
+function (see
+below). The current version is <span style="font-family: monospace;">1 1 3</span>.<br>
+<br>
+The GMSL package also includes a test suite for GMSL.&nbsp; Just run <span style="font-family: monospace;">make -f gmsl-tests</span>.<br>
+<h2>Logical Operators</h2>GMSL has boolean $(true) (a non-empty string)
+and $(false) (an empty string).&nbsp; The following operators can be
+used with those variables.<br>
+<br>
+<hr style="width: 100%; height: 2px;"><span style="font-weight: bold;">not</span><br>
+
+<br>
+
+<span style="font-family: monospace;">Arguments: A boolean value</span><br style="font-family: monospace;">
+
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns $(true) if the boolean is $(false) and vice versa</span><br style="font-family: monospace;">
+
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;"></span><span style="font-weight: bold;">and</span><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns $(true) if both of the booleans are true</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;">or</span><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns $(true) if either of the booleans is true</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;">xor</span><br style="font-weight: bold;">
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns $(true) if exactly one of the booleans is true</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;">nand</span><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns value of 'not and'</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;">nor</span><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns value of 'not or'</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;"><span style="font-weight: bold;">xnor</span><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two boolean values</span><br style="font-family: monospace;">
+<span style="font-family: monospace;">Returns:&nbsp;&nbsp; Returns value of 'not xor'</span><br style="font-family: monospace;">
+<hr style="width: 100%; height: 2px; font-family: monospace;">
+<h2>List Manipulation Functions</h2>
+&nbsp;A list is a string of characters; the list separator is a space.<br>
+
+<br>
+<hr style="width: 100%; height: 2px;"><b>first</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the first element of a list<br>
+</span>
+<hr><b>last</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the last element of a list<br>
+</span>
+<hr><b>rest</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the list with the first element
+removed<br>
+</span>
+<hr><b>chop</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the list with the last element removed<br>
+</span>
+<hr><b>map</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of function to
+$(call) for each element of list<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: List to
+iterate over calling the function in 1<br>
+Returns:&nbsp;&nbsp;&nbsp;The list after calling the function on each
+element<br>
+</span>
+<hr><b>pairmap</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of function to
+$(call) for each pair of elements<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: List to
+iterate over calling the function in 1<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3: Second
+list to iterate over calling the function in 1<br>
+Returns:&nbsp;&nbsp;&nbsp;The list after calling the function on each
+pair of elements<br>
+</span>
+<hr><b>leq</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list to compare
+against...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: ...this
+list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the two lists are identical<br>
+</span>
+<hr><b>lne</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list to compare
+against...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: ...this
+list<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the two lists are different<br>
+</span>
+<hr><b>reverse</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list to reverse<br>
+Returns:&nbsp;&nbsp;&nbsp;The list with its elements in reverse order<br>
+</span>
+<hr><b>uniq</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list to deduplicate<br>
+Returns:&nbsp;&nbsp;&nbsp;The list with elements in order without duplicates<br>
+</span>
+<hr><b>length</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list<br>
+Returns:&nbsp;&nbsp;&nbsp;The number of elements in the list<br>
+</span>
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>String Manipulation Functions</h2>
+A string is any sequence of characters.<br>
+<br>
+<hr style="width: 100%; height: 2px;"><b>seq</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A string to compare
+against...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: ...this
+string<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the two strings are
+identical<br>
+</span>
+<hr><b>sne</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A string to compare
+against...<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: ...this
+string<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the two strings are not
+the same<br>
+</span>
+<hr><b>strlen</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A string<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the length of the string<br>
+</span>
+<hr><b>substr</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A string<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Start offset (first character is 1)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3: Ending offset (inclusive)<br>Returns:&nbsp;&nbsp;&nbsp;Returns a substring<br>
+</span>
+<hr><b>split</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: The character to
+split on<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: A
+string to split<br>
+Returns:&nbsp;&nbsp;&nbsp;Splits a string into a list separated by
+spaces at the split<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; character
+in the first argument<br>
+</span>
+<hr><b>merge</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: The character to
+put between fields<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: A list
+to merge into a string<br>
+Returns:&nbsp;&nbsp;&nbsp;Merges a list into a single string, list
+elements are separated<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; by the
+character in the first argument<br>
+</span>
+<hr><b>tr</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: The list of
+characters to translate from <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: The
+list of characters to translate to<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3: The
+text to translate<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the text after translating characters<br>
+</span>
+<hr><b>uc</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Text to upper case<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the text in upper case<br>
+</span>
+<hr><b>lc</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Text to lower case<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the text in lower case<br>
+</span>
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Set Manipulation Functions</h2>
+Sets are represented by sorted, deduplicated lists. To create a set
+from a list use <span style="font-family:
+monospace;">set_create</span>, or start with the <span
+style="font-family: monospace;">empty_set</span> and <span
+style="font-family: monospace;">set_insert</span> individual elements.
+The empty set is defined as <span style="font-family:
+monospace;">empty_set</span>.<p>
+
+<hr><b>set_create</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A list of set elements<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the newly created set<br>
+</span>
+
+<hr><b>set_insert</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A single element to add to a set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: A set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the set with the element added<br>
+</span>
+
+<hr><b>set_remove</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A single element to remove from a set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: A set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the set with the element removed<br>
+</span>
+
+<hr><b>set_is_member</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A single element<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: A set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the element is in the set<br>
+</span>
+
+<hr><b>set_union</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: Another set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the union of the two sets<br>
+</span>
+
+<hr><b>set_intersection</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: Another set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the intersection of the two sets<br>
+</span>
+
+<hr><b>set_is_subset</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: Another set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the first set is a subset of the second<br>
+</span>
+
+<hr><b>set_equal</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A set<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2: Another set<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns $(true) if the two sets are identical<br>
+</span>
+
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Integer Arithmetic Functions</h2>
+Integers are represented by lists with the equivalent number of
+x's.&nbsp; For example the number 4 is x x x x.&nbsp; The maximum
+integer that the library can handle as <span style="font-style: italic;">input</span> (i.e. as the argument to a
+call to <span style="font-family: monospace;">int_encode</span>) is
+65536. There is no limit on integer size for internal computations or
+output.<br>
+<br>
+The arithmetic library functions come in two forms: one form of each
+function takes integers as arguments and the other form takes the
+encoded form (x's created by a call to <span style="font-family: monospace;">int_encode</span>).&nbsp; For example,
+there are two plus functions: <span style="font-family: monospace;">plus</span>
+(called with integer arguments and returns an integer) and <span style="font-family: monospace;">int_plus</span> (called with encoded
+arguments and returns an encoded result).<br>
+<br>
+<span style="font-family: monospace;">plus</span> will be slower than <span style="font-family: monospace;">int_plus</span> because its arguments
+and result have to be translated between the x's format and
+integers.&nbsp; If doing a complex calculation use the <span style="font-family: monospace;">int_*</span> forms with a single
+encoding of inputs and single decoding of the output.&nbsp; For simple
+calculations the direct forms can be used.<br>
+<br>
+<hr style="width: 100%; height: 2px;"><b>int_decode</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number of x's
+representation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the integer for human consumption
+that is represented<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; by the
+string of x's<br>
+</span>
+<hr><b>int_encode</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in
+human-readable integer form<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the integer encoded as a string of x's<br>
+</span>
+<hr><b>int_plus</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+number in x's represntation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the sum of the two numbers in x's
+representation<br>
+</span>
+<hr><b>plus (wrapped version of int_plus)</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+integer<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the sum of the two integers<br>
+</span>
+<hr><b>int_subtract</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+number in x's represntation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the difference of the two numbers in
+x's representation,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or outputs
+an error on a numeric underflow<br>
+</span>
+<hr><b>subtract (wrapped version of int_subtract)</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+integer<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the difference of the two integers,<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or outputs
+an error on a numeric underflow<br>
+</span>
+<hr><b>int_multiply</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+number in x's represntation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the product of the two numbers in x's
+representation<br>
+</span>
+<hr><b>multiply (wrapped version of int_multiply)</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+integer<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the product of the two integers<br>
+</span>
+<hr><b>int_divide</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+number in x's represntation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the result of integer division of
+argument 1 divided<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; by
+argument 2 in x's representation<br>
+</span>
+<hr><b>divide (wrapped version of int_divide)</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+integer<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the integer division of the first
+argument by the second<br>
+</span>
+<hr><b>int_max, int_min</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+number in x's represntation<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the maximum or minimum of its
+arguments in x's<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+representation<br>
+</span>
+<hr><b>max, min</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Another
+integer<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns the maximum or minimum of its integer
+arguments<br>
+</span>
+<hr><b>int_gt, int_gte, int_lt, int_lte, int_eq, int_ne</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two x's representation
+numbers to be compared<br>
+Returns:&nbsp;&nbsp;&nbsp;$(true) or $(false)<br>
+<br>
+int_gt First argument greater than second argument<br>
+int_gte First argument greater than or equal to second argument<br>
+int_lt First argument less than second argument <br>
+int_lte First argument less than or equal to second argument<br>
+int_eq First argument is numerically equal to the second argument<br>
+int_ne First argument is not numerically equal to the second argument<br>
+</span>
+<hr><b>gt, gte, lt, lte, eq, ne</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: Two integers to be
+compared<br>
+Returns:&nbsp;&nbsp;&nbsp;$(true) or $(false)<br>
+<br>
+gt First argument greater than second argument<br>
+gte First argument greater than or equal to second argument<br>
+lt First argument less than second argument <br>
+lte First argument less than or equal to second argument<br>
+eq First argument is numerically equal to the second argument<br>
+ne First argument is not numerically equal to the second argument<br>
+</span>
+increment adds 1 to its argument, decrement subtracts 1. Note that<br>
+decrement does not range check and hence will not underflow, but<br>
+will incorrectly say that 0 - 1 = 0<br>
+<hr><b>int_inc</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+Returns:&nbsp;&nbsp;&nbsp;The number incremented by 1 in x's
+representation<br>
+</span>
+<hr><b>inc</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+Returns:&nbsp;&nbsp;&nbsp;The argument incremented by 1<br>
+</span>
+<hr><b>int_dec</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+Returns:&nbsp;&nbsp;&nbsp;The number decremented by 1 in x's
+representation<br>
+</span>
+<hr><b>dec</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+Returns:&nbsp;&nbsp;&nbsp;The argument decremented by 1<br>
+</span>
+<hr><b>int_double</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+Returns:&nbsp;&nbsp;&nbsp;The number doubled (i.e. * 2) and returned in
+x's representation<br>
+</span>
+<hr><b>double</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+Returns:&nbsp;&nbsp;&nbsp;The integer times 2<br>
+</span>
+<hr><b>int_halve</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A number in x's
+representation<br>
+Returns:&nbsp;&nbsp;&nbsp;The number halved (i.e. / 2) and returned in
+x's representation<br>
+</span>
+<hr><b>halve</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+Returns:&nbsp;&nbsp;&nbsp;The integer divided by 2<br>
+</span>
+<hr><b>sequence</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: An integer<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: An integer<br>
+Returns:&nbsp;&nbsp;&nbsp;The sequence [arg1 arg2] if arg1 >= arg2 or [arg2 arg1] if arg2 > arg1<br>
+</span>
+
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Associative Arrays</h2>
+An associate array maps a key value (a string with no spaces in it) to
+a single value (any string).&nbsp;&nbsp;&nbsp; <br>
+<b><br>
+</b>
+<hr style="width: 100%; height: 2px;"><b>set</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of associative
+array<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: The key
+value to associate<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3: The
+value associated with the key<br>
+Returns:&nbsp;&nbsp;&nbsp;Nothing<br>
+</span>
+<hr><b>get</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of associative
+array<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: The key
+to retrieve<br>
+Returns:&nbsp;&nbsp;&nbsp;The value stored in the array for that key<br>
+</span>
+<hr><b>keys</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of associative
+array<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns a list of all defined keys in the
+array<br>
+</span>
+<hr><b>defined</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of associative
+array<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: The key
+to test<br>
+Returns:&nbsp;&nbsp;&nbsp;Returns true if the key is defined (i.e. not
+empty)<br>
+</span>
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Named Stacks</h2>
+A stack is an ordered list of strings (with no spaces in them).<br>
+<br>
+<hr style="width: 100%; height: 2px;"><b>push</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of stack<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: Value
+to push onto the top of the stack (must not contain<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a space)<br>
+Returns:&nbsp;&nbsp;&nbsp;None<br>
+</span>
+<hr><b>pop</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of stack<br>
+Returns:&nbsp;&nbsp;&nbsp;Top element from the stack after removing it<br>
+</span>
+<hr><b>peek</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of stack<br>
+Returns:&nbsp;&nbsp;&nbsp;Top element from the stack without removing it<br>
+</span>
+<hr><b>depth</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of stack<br>
+Returns:&nbsp;&nbsp;&nbsp;Number of items on the stack<br>
+</span>
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Function memoization</h2>
+To reduce the number of calls to slow functions (such as $(shell) a single memoization function is provided.<br>
+<br>
+<hr style="width: 100%; height: 2px;"><b>memoize</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of function to memoize<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: String argument for the function<br>
+Returns:&nbsp;&nbsp;&nbsp;Result of $1 applied to $2 but only calls $1 once for each unique $2<br>
+</span>
+
+<hr style="width: 100%; height: 2px;"><span style="font-family: monospace;"></span>
+<h2>Miscellaneous and Debugging Facilities</h2>
+GMSL defines the following constants; all are accessed as normal GNU
+Make variables by wrapping them in <span style="font-family: monospace;">$()</span> or <span style="font-family: monospace;">${}</span>.<br>
+<br>
+<table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td><span style="font-style: italic;">Constant</span><br>
+ </td>
+ <td><span style="font-style: italic;">Value</span><br>
+ </td>
+ <td><span style="font-style: italic;">Purpose</span><br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">true</span><br>
+ </td>
+ <td><span style="font-family: monospace;">T</span><br>
+ </td>
+ <td>Boolean for <span style="font-family: monospace;">$(if)</span>
+and return from&nbsp; GMSL functions<br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">false</span><br>
+ </td>
+ <td><br>
+ </td>
+ <td>Boolean for <span style="font-family: monospace;">$(if)</span>
+and return from GMSL functions<br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">gmsl_version</span><br>
+ </td>
+ <td><span style="font-family: monospace;">1 0 0</span><br>
+ </td>
+ <td>GMSL version number as list: major minor revision<br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<span style="font-weight: bold;"><br>
+gmsl_compatible</span><span style="font-family: monospace;"><br>
+<br>
+Arguments: List containing the desired library version number (maj min
+rev)<br>
+</span><span style="font-family: monospace;">Returns:&nbsp;&nbsp;
+$(true) if this version of the library is compatible<br>
+</span><span style="font-family: monospace;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+with the requested version number, otherwise $(false)</span>
+<hr><b>gmsl-print-% (target not a function)</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: The % should be
+replaced by the name of a variable that you<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wish to
+print out.<br>
+Action:&nbsp;&nbsp;&nbsp; Echos the name of the variable that matches
+the % and its value.<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; For
+example, 'make gmsl-print-SHELL' will output the value of<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the SHELL
+variable<br>
+</span>
+<hr><b>assert</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: A boolean that must
+be true or the assertion will fail<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2: The
+message to print with the assertion<br>
+Returns:&nbsp;&nbsp;&nbsp;None<br>
+</span>
+<hr><b>assert_exists</b><br>
+<br>
+<span style="font-family: monospace;">Arguments: 1: Name of file that
+must exist, if it is missing an assertion<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; will be
+generated<br>
+Returns:&nbsp;&nbsp;&nbsp;None<br>
+</span>
+<hr style="width: 100%; height: 2px;"><br>
+GMSL has a number of environment variables (or command-line overrides)
+that control various bits of functionality:<br>
+<br>
+<table style="text-align: left;" border="1" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td><span style="font-style: italic;">Variable</span><br>
+ </td>
+ <td><span style="font-style: italic;">Purpose</span><br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">GMSL_NO_WARNINGS</span><br>
+ </td>
+ <td>If set prevents GMSL from outputting warning messages:
+artithmetic functions generate underflow warnings.<br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">GMSL_NO_ERRORS</span><br>
+ </td>
+ <td>If set prevents GMSL from generating fatal errors: division
+by zero or failed assertions are fatal.<br>
+ </td>
+ </tr>
+ <tr>
+ <td><span style="font-family: monospace;">GMSL_TRACE</span><br>
+ </td>
+ <td>Enables function tracing.&nbsp; Calls to GMSL functions will
+result in name and arguments being traced.<br>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<span style="font-family: monospace;"></span><br>
+<hr>
+Copyright (c) 2005-2012 <a href="http://www.jgc.org/">John Graham-Cumming</a>.<br>
+<hr style="width: 100%; height: 2px;">
+<table style="width: 100%; text-align: left;" border="0" cellpadding="2" cellspacing="2">
+ <tbody>
+ <tr>
+ <td style="width: 50%;">John Graham-Cumming's work on this
+project was sponsored by <a href="http://www.electric-cloud.com/">Electric
+Cloud, Inc</a>.<br>
+ <a href="http://www.electric-cloud.com/"><img alt="" src="http://gmsl.sf.net/ec_logo.gif" style="border: 0px solid ; width: 223px; height: 47px;"></a><br>
+ </td>
+ <td align="right">
+ <p><a href="http://sourceforge.net/"><img src="http://sourceforge.net/sflogo.php?group_id=129887&amp;type=1" alt="SourceForge.net Logo" border="0" height="31" width="88"></a></p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+</body></html>
diff --git a/index.html b/index.html
index aa019e7..7127074 100644
--- a/index.html
+++ b/index.html
@@ -2491,6 +2491,11 @@ local-pkg-list: $(LOCAL_PKG_LIST)</pre>
<a href="https://github.com/mxe/mxe/blob/master/src/xmlwrapp.mk">xmlwrapp.mk</a>.
And so on.
</p>
+
+ <p>
+ The <a href="doc/gmsl.html">GNU Make Standard Library</a> is also
+ available (though it should be unnecessary for most packages).
+ </p>
</li>
<li>
diff --git a/tools/__gmsl b/tools/__gmsl
new file mode 100644
index 0000000..90a9d69
--- /dev/null
+++ b/tools/__gmsl
@@ -0,0 +1,919 @@
+# ----------------------------------------------------------------------------
+#
+# GNU Make Standard Library (GMSL)
+#
+# A library of functions to be used with GNU Make's $(call) that
+# provides functionality not available in standard GNU Make.
+#
+# Copyright (c) 2005-2013 John Graham-Cumming
+#
+# This file is part of GMSL
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of the John Graham-Cumming nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ----------------------------------------------------------------------------
+
+# This is the GNU Make Standard Library version number as a list with
+# three items: major, minor, revision
+
+gmsl_version := 1 1 3
+
+# Used to output warnings and error from the library, it's possible to
+# disable any warnings or errors by overriding these definitions
+# manually or by setting GMSL_NO_WARNINGS or GMSL_NO_ERRORS
+
+__gmsl_name := GNU Make Standard Library
+__gmsl_warning = $(warning $(__gmsl_name): $1)
+__gmsl_error = $(error $(__gmsl_name): $1)
+
+ifdef GMSL_NO_WARNINGS
+__gmsl_warning :=
+endif
+ifdef GMSL_NO_ERRORS
+__gmsl_error :=
+endif
+
+# If GMSL_TRACE is enabled then calls to the library functions are
+# traced to stdout using warning messages with their arguments
+
+ifdef GMSL_TRACE
+__gmsl_tr1 = $(warning $0('$1'))
+__gmsl_tr2 = $(warning $0('$1','$2'))
+__gmsl_tr3 = $(warning $0('$1','$2','$3'))
+else
+__gmsl_tr1 :=
+__gmsl_tr2 :=
+__gmsl_tr3 :=
+endif
+
+# See if spaces are valid in variable names (this was the case until
+# GNU Make 3.82)
+ifeq ($(MAKE_VERSION),3.82)
+__gmsl_spaced_vars := $(false)
+else
+__gmsl_spaced_vars := $(true)
+endif
+
+# Figure out whether we have $(eval) or not (GNU Make 3.80 and above)
+# if we do not then output a warning message, if we do then some
+# functions will be enabled.
+
+__gmsl_have_eval := $(false)
+__gmsl_ignore := $(eval __gmsl_have_eval := $(true))
+
+# If this is being run with Electric Cloud's emake then warn that
+# their $(eval) support is incomplete in 1.x, 2.x, 3.x, 4.x and 5.0,
+# 5.1, 5.2 and 5.3
+
+ifdef ECLOUD_BUILD_ID
+__gmsl_emake_major := $(word 1,$(subst ., ,$(EMAKE_VERSION)))
+__gmsl_emake_minor := $(word 2,$(subst ., ,$(EMAKE_VERSION)))
+ifneq ("$(findstring $(__gmsl_emake_major),1 2 3 4)$(findstring $(__gmsl_emake_major)$(__gmsl_emake_minor),50 51 52 53)","")
+$(warning You are using a version of Electric Cloud's emake which has incomplete $$(eval) support)
+__gmsl_have_eval := $(false)
+endif
+endif
+
+# See if we have $(lastword) (GNU Make 3.81 and above)
+
+__gmsl_have_lastword := $(lastword $(false) $(true))
+
+# See if we have native or and and (GNU Make 3.81 and above)
+
+__gmsl_have_or := $(if $(filter-out undefined, \
+ $(origin or)),$(call or,$(true),$(false)))
+__gmsl_have_and := $(if $(filter-out undefined, \
+ $(origin and)),$(call and,$(true),$(true)))
+
+ifneq ($(__gmsl_have_eval),$(true))
+$(call __gmsl_warning,Your make version $(MAKE_VERSION) does not support $$$$(eval): some functions disabled)
+endif
+
+__gmsl_dollar := $$
+__gmsl_hash := \#
+
+# ----------------------------------------------------------------------------
+# Function: gmsl_compatible
+# Arguments: List containing the desired library version number (maj min rev)
+# Returns: $(true) if this version of the library is compatible
+# with the requested version number, otherwise $(false)
+# ----------------------------------------------------------------------------
+gmsl_compatible = $(strip \
+ $(if $(call gt,$(word 1,$1),$(word 1,$(gmsl_version))), \
+ $(false), \
+ $(if $(call lt,$(word 1,$1),$(word 1,$(gmsl_version))), \
+ $(true), \
+ $(if $(call gt,$(word 2,$1),$(word 2,$(gmsl_version))), \
+ $(false), \
+ $(if $(call lt,$(word 2,$1),$(word 2,$(gmsl_version))), \
+ $(true), \
+ $(call lte,$(word 3,$1),$(word 3,$(gmsl_version))))))))
+
+# ###########################################################################
+# LOGICAL OPERATORS
+# ###########################################################################
+
+# not is defined in gmsl
+
+# ----------------------------------------------------------------------------
+# Function: and
+# Arguments: Two boolean values
+# Returns: Returns $(true) if both of the booleans are true
+# ----------------------------------------------------------------------------
+ifneq ($(__gmsl_have_and),$(true))
+and = $(__gmsl_tr2)$(if $1,$(if $2,$(true),$(false)),$(false))
+endif
+
+# ----------------------------------------------------------------------------
+# Function: or
+# Arguments: Two boolean values
+# Returns: Returns $(true) if either of the booleans is true
+# ----------------------------------------------------------------------------
+ifneq ($(__gmsl_have_or),$(true))
+or = $(__gmsl_tr2)$(if $1$2,$(true),$(false))
+endif
+
+# ----------------------------------------------------------------------------
+# Function: xor
+# Arguments: Two boolean values
+# Returns: Returns $(true) if exactly one of the booleans is true
+# ----------------------------------------------------------------------------
+xor = $(__gmsl_tr2)$(if $1,$(if $2,$(false),$(true)),$(if $2,$(true),$(false)))
+
+# ----------------------------------------------------------------------------
+# Function: nand
+# Arguments: Two boolean values
+# Returns: Returns value of 'not and'
+# ----------------------------------------------------------------------------
+nand = $(__gmsl_tr2)$(if $1,$(if $2,$(false),$(true)),$(true))
+
+# ----------------------------------------------------------------------------
+# Function: nor
+# Arguments: Two boolean values
+# Returns: Returns value of 'not or'
+# ----------------------------------------------------------------------------
+nor = $(__gmsl_tr2)$(if $1$2,$(false),$(true))
+
+# ----------------------------------------------------------------------------
+# Function: xnor
+# Arguments: Two boolean values
+# Returns: Returns value of 'not xor'
+# ----------------------------------------------------------------------------
+xnor =$(__gmsl_tr2)$(if $1,$(if $2,$(true),$(false)),$(if $2,$(false),$(true)))
+
+# ###########################################################################
+# LIST MANIPULATION FUNCTIONS
+# ###########################################################################
+
+# ----------------------------------------------------------------------------
+# Function: first (same as LISP's car, or head)
+# Arguments: 1: A list
+# Returns: Returns the first element of a list
+# ----------------------------------------------------------------------------
+first = $(__gmsl_tr1)$(firstword $1)
+
+# ----------------------------------------------------------------------------
+# Function: last
+# Arguments: 1: A list
+# Returns: Returns the last element of a list
+# ----------------------------------------------------------------------------
+ifeq ($(__gmsl_have_lastword),$(true))
+last = $(__gmsl_tr1)$(lastword $1)
+else
+last = $(__gmsl_tr1)$(if $1,$(word $(words $1),$1))
+endif
+
+# ----------------------------------------------------------------------------
+# Function: rest (same as LISP's cdr, or tail)
+# Arguments: 1: A list
+# Returns: Returns the list with the first element removed
+# ----------------------------------------------------------------------------
+rest = $(__gmsl_tr1)$(wordlist 2,$(words $1),$1)
+
+# ----------------------------------------------------------------------------
+# Function: chop
+# Arguments: 1: A list
+# Returns: Returns the list with the last element removed
+# ----------------------------------------------------------------------------
+chop = $(__gmsl_tr1)$(wordlist 2,$(words $1),x $1)
+
+# ----------------------------------------------------------------------------
+# Function: map
+# Arguments: 1: Name of function to $(call) for each element of list
+# 2: List to iterate over calling the function in 1
+# Returns: The list after calling the function on each element
+# ----------------------------------------------------------------------------
+map = $(__gmsl_tr2)$(strip $(foreach a,$2,$(call $1,$a)))
+
+# ----------------------------------------------------------------------------
+# Function: pairmap
+# Arguments: 1: Name of function to $(call) for each pair of elements
+# 2: List to iterate over calling the function in 1
+# 3: Second list to iterate over calling the function in 1
+# Returns: The list after calling the function on each pair of elements
+# ----------------------------------------------------------------------------
+pairmap = $(strip $(__gmsl_tr3)\
+ $(if $2$3,$(call $1,$(call first,$2),$(call first,$3)) \
+ $(call pairmap,$1,$(call rest,$2),$(call rest,$3))))
+
+# ----------------------------------------------------------------------------
+# Function: leq
+# Arguments: 1: A list to compare against...
+# 2: ...this list
+# Returns: Returns $(true) if the two lists are identical
+# ----------------------------------------------------------------------------
+leq = $(__gmsl_tr2)$(strip $(if $(call seq,$(words $1),$(words $2)), \
+ $(call __gmsl_list_equal,$1,$2),$(false)))
+
+__gmsl_list_equal = $(if $(strip $1), \
+ $(if $(call seq,$(call first,$1),$(call first,$2)), \
+ $(call __gmsl_list_equal, \
+ $(call rest,$1), \
+ $(call rest,$2)), \
+ $(false)), \
+ $(true))
+
+# ----------------------------------------------------------------------------
+# Function: lne
+# Arguments: 1: A list to compare against...
+# 2: ...this list
+# Returns: Returns $(true) if the two lists are different
+# ----------------------------------------------------------------------------
+lne = $(__gmsl_tr2)$(call not,$(call leq,$1,$2))
+
+# ----------------------------------------------------------------------------
+# Function: reverse
+# Arguments: 1: A list to reverse
+# Returns: The list with its elements in reverse order
+# ----------------------------------------------------------------------------
+reverse =$(__gmsl_tr1)$(strip $(if $1,$(call reverse,$(call rest,$1)) \
+ $(call first,$1)))
+
+# ----------------------------------------------------------------------------
+# Function: uniq
+# Arguments: 1: A list from which to remove repeated elements
+# Returns: The list with duplicate elements removed without reordering
+# ----------------------------------------------------------------------------
+uniq = $(strip $(__gmsl_tr1) $(if $1,$(firstword $1) \
+ $(call uniq,$(filter-out $(firstword $1),$1))))
+
+# ----------------------------------------------------------------------------
+# Function: length
+# Arguments: 1: A list
+# Returns: The number of elements in the list
+# ----------------------------------------------------------------------------
+length = $(__gmsl_tr1)$(words $1)
+
+# ###########################################################################
+# STRING MANIPULATION FUNCTIONS
+# ###########################################################################
+
+# Helper function that translates any GNU Make 'true' value (i.e. a
+# non-empty string) to our $(true)
+
+__gmsl_make_bool = $(if $(strip $1),$(true),$(false))
+
+# ----------------------------------------------------------------------------
+# Function: seq
+# Arguments: 1: A string to compare against...
+# 2: ...this string
+# Returns: Returns $(true) if the two strings are identical
+# ----------------------------------------------------------------------------
+seq = $(__gmsl_tr2)$(if $(subst x$1,,x$2)$(subst x$2,,x$1),$(false),$(true))
+
+# ----------------------------------------------------------------------------
+# Function: sne
+# Arguments: 1: A string to compare against...
+# 2: ...this string
+# Returns: Returns $(true) if the two strings are not the same
+# ----------------------------------------------------------------------------
+sne = $(__gmsl_tr2)$(call not,$(call seq,$1,$2))
+
+# ----------------------------------------------------------------------------
+# Function: split
+# Arguments: 1: The character to split on
+# 2: A string to split
+# Returns: Splits a string into a list separated by spaces at the split
+# character in the first argument
+# ----------------------------------------------------------------------------
+split = $(__gmsl_tr2)$(strip $(subst $1, ,$2))
+
+# ----------------------------------------------------------------------------
+# Function: merge
+# Arguments: 1: The character to put between fields
+# 2: A list to merge into a string
+# Returns: Merges a list into a single string, list elements are separated
+# by the character in the first argument
+# ----------------------------------------------------------------------------
+merge = $(__gmsl_tr2)$(strip $(if $2, \
+ $(if $(call seq,1,$(words $2)), \
+ $2,$(call first,$2)$1$(call merge,$1,$(call rest,$2)))))
+
+ifdef __gmsl_have_eval
+# ----------------------------------------------------------------------------
+# Function: tr
+# Arguments: 1: The list of characters to translate from
+# 2: The list of characters to translate to
+# 3: The text to translate
+# Returns: Returns the text after translating characters
+# ----------------------------------------------------------------------------
+tr = $(strip $(__gmsl_tr3)$(call assert_no_dollar,$0,$1$2$3) \
+ $(eval __gmsl_t := $3) \
+ $(foreach c, \
+ $(join $(addsuffix :,$1),$2), \
+ $(eval __gmsl_t := \
+ $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)), \
+ $(__gmsl_t))))$(__gmsl_t))
+
+# Common character classes for use with the tr function. Each of
+# these is actually a variable declaration and must be wrapped with
+# $() or ${} to be used.
+
+[A-Z] := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z #
+[a-z] := a b c d e f g h i j k l m n o p q r s t u v w x y z #
+[0-9] := 0 1 2 3 4 5 6 7 8 9 #
+[A-F] := A B C D E F #
+
+# ----------------------------------------------------------------------------
+# Function: uc
+# Arguments: 1: Text to upper case
+# Returns: Returns the text in upper case
+# ----------------------------------------------------------------------------
+uc = $(__gmsl_tr1)$(call assert_no_dollar,$0,$1)$(call tr,$([a-z]),$([A-Z]),$1)
+
+# ----------------------------------------------------------------------------
+# Function: lc
+# Arguments: 1: Text to lower case
+# Returns: Returns the text in lower case
+# ----------------------------------------------------------------------------
+lc = $(__gmsl_tr1)$(call assert_no_dollar,$0,$1)$(call tr,$([A-Z]),$([a-z]),$1)
+
+# ----------------------------------------------------------------------------
+# Function: strlen
+# Arguments: 1: A string
+# Returns: Returns the length of the string
+# ----------------------------------------------------------------------------
+__gmsl_characters := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+__gmsl_characters += a b c d e f g h i j k l m n o p q r s t u v w x y z
+__gmsl_characters += 0 1 2 3 4 5 6 7 8 9
+__gmsl_characters += ` ~ ! @ \# $$ % ^ & * ( ) - _ = +
+__gmsl_characters += { } [ ] \ : ; ' " < > , . / ? |
+
+# This results in __gmsl_space containing just a space
+
+__gmsl_space :=
+__gmsl_space +=
+
+strlen = $(__gmsl_tr1)$(call assert_no_dollar,$0,$1)$(strip $(eval __temp := $(subst $(__gmsl_space),x,$1))$(foreach a,$(__gmsl_characters),$(eval __temp := $$(subst $$a,x,$(__temp))))$(eval __temp := $(subst x,x ,$(__temp)))$(words $(__temp)))
+
+# This results in __gmsl_newline containing just a newline
+
+define __gmsl_newline
+
+
+endef
+
+# This results in __gmsl_tab containing a tab
+
+__gmsl_tab := #
+
+# ----------------------------------------------------------------------------
+# Function: substr
+# Arguments: 1: A string
+# 2: Start position (first character is 1)
+# 3: End position (inclusive)
+# Returns: A substring.
+# Note: The string in $1 must not contain a §
+# ----------------------------------------------------------------------------
+
+substr = $(__gmsl_tr3)$(call assert_no_dollar,$0,$1$2$3)$(strip $(eval __temp := $$(subst $$(__gmsl_space),§ ,$$1))$(foreach a,$(__gmsl_characters),$(eval __temp := $$(subst $$a,$$a$$(__gmsl_space),$(__temp))))$(eval __temp := $(wordlist $2,$3,$(__temp))))$(subst §,$(__gmsl_space),$(subst $(__gmsl_space),,$(__temp)))
+
+endif # __gmsl_have_eval
+
+# ###########################################################################
+# SET MANIPULATION FUNCTIONS
+# ###########################################################################
+
+# Sets are represented by sorted, deduplicated lists. To create a set
+# from a list use set_create, or start with the empty_set and
+# set_insert individual elements
+
+# This is the empty set
+empty_set :=
+
+# ----------------------------------------------------------------------------
+# Function: set_create
+# Arguments: 1: A list of set elements
+# Returns: Returns the newly created set
+# ----------------------------------------------------------------------------
+set_create = $(__gmsl_tr1)$(sort $1)
+
+# ----------------------------------------------------------------------------
+# Function: set_insert
+# Arguments: 1: A single element to add to a set
+# 2: A set
+# Returns: Returns the set with the element added
+# ----------------------------------------------------------------------------
+set_insert = $(__gmsl_tr2)$(sort $1 $2)
+
+# ----------------------------------------------------------------------------
+# Function: set_remove
+# Arguments: 1: A single element to remove from a set
+# 2: A set
+# Returns: Returns the set with the element removed
+# ----------------------------------------------------------------------------
+set_remove = $(__gmsl_tr2)$(filter-out $1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: set_is_member
+# Arguments: 1: A single element
+# 2: A set
+# Returns: Returns $(true) if the element is in the set
+# ----------------------------------------------------------------------------
+set_is_member = $(__gmsl_tr2)$(if $(filter $1,$2),$(true),$(false))
+
+# ----------------------------------------------------------------------------
+# Function: set_union
+# Arguments: 1: A set
+# 2: Another set
+# Returns: Returns the union of the two sets
+# ----------------------------------------------------------------------------
+set_union = $(__gmsl_tr2)$(sort $1 $2)
+
+# ----------------------------------------------------------------------------
+# Function: set_intersection
+# Arguments: 1: A set
+# 2: Another set
+# Returns: Returns the intersection of the two sets
+# ----------------------------------------------------------------------------
+set_intersection = $(__gmsl_tr2)$(filter $1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: set_is_subset
+# Arguments: 1: A set
+# 2: Another set
+# Returns: Returns $(true) if the first set is a subset of the second
+# ----------------------------------------------------------------------------
+set_is_subset = $(__gmsl_tr2)$(call set_equal,$(call set_intersection,$1,$2),$1)
+
+# ----------------------------------------------------------------------------
+# Function: set_equal
+# Arguments: 1: A set
+# 2: Another set
+# Returns: Returns $(true) if the two sets are identical
+# ----------------------------------------------------------------------------
+set_equal = $(__gmsl_tr2)$(call seq,$1,$2)
+
+# ###########################################################################
+# ARITHMETIC LIBRARY
+# ###########################################################################
+
+# Integers a represented by lists with the equivalent number of x's.
+# For example the number 4 is x x x x. The maximum integer that the
+# library can handle as _input_ is __gmsl_input_int which is defined
+# here as 65536
+
+__gmsl_sixteen := x x x x x x x x x x x x x x x x
+__gmsl_input_int := $(foreach a,$(__gmsl_sixteen), \
+ $(foreach b,$(__gmsl_sixteen), \
+ $(foreach c,$(__gmsl_sixteen), \
+ $(__gmsl_sixteen)))))
+
+# ----------------------------------------------------------------------------
+# Function: int_decode
+# Arguments: 1: A number of x's representation
+# Returns: Returns the integer for human consumption that is represented
+# by the string of x's
+# ----------------------------------------------------------------------------
+int_decode = $(__gmsl_tr1)$(words $1)
+
+# ----------------------------------------------------------------------------
+# Function: int_encode
+# Arguments: 1: A number in human-readable integer form
+# Returns: Returns the integer encoded as a string of x's
+# ----------------------------------------------------------------------------
+int_encode = $(__gmsl_tr1)$(wordlist 1,$1,$(__gmsl_input_int))
+
+# The arithmetic library functions come in two forms: one form of each
+# function takes integers as arguments and the other form takes the
+# encoded form (x's created by a call to int_encode). For example,
+# there are two plus functions:
+#
+# plus Called with integer arguments and returns an integer
+# int_plus Called with encoded arguments and returns an encoded result
+#
+# plus will be slower than int_plus because its arguments and result
+# have to be translated between the x's format and integers. If doing
+# a complex calculation use the int_* forms with a single encoding of
+# inputs and single decoding of the output. For simple calculations
+# the direct forms can be used.
+
+# Helper function used to wrap an int_* function into a function that
+# takes a pair of integers, perhaps a function and returns an integer
+# result
+__gmsl_int_wrap = $(call int_decode,$(call $1,$(call int_encode,$2),$(call int_encode,$3)))
+__gmsl_int_wrap1 = $(call int_decode,$(call $1,$(call int_encode,$2)))
+__gmsl_int_wrap2 = $(call $1,$(call int_encode,$2),$(call int_encode,$3))
+
+# ----------------------------------------------------------------------------
+# Function: int_plus
+# Arguments: 1: A number in x's representation
+# 2: Another number in x's represntation
+# Returns: Returns the sum of the two numbers in x's representation
+# ----------------------------------------------------------------------------
+int_plus = $(strip $(__gmsl_tr2)$1 $2)
+
+# ----------------------------------------------------------------------------
+# Function: plus (wrapped version of int_plus)
+# Arguments: 1: An integer
+# 2: Another integer
+# Returns: Returns the sum of the two integers
+# ----------------------------------------------------------------------------
+plus = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_plus,$1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: int_subtract
+# Arguments: 1: A number in x's representation
+# 2: Another number in x's represntation
+# Returns: Returns the difference of the two numbers in x's representation,
+# or outputs an error on a numeric underflow
+# ----------------------------------------------------------------------------
+int_subtract = $(strip $(__gmsl_tr2)$(if $(call int_gte,$1,$2), \
+ $(filter-out xx,$(join $1,$2)), \
+ $(call __gmsl_warning,Subtraction underflow)))
+
+# ----------------------------------------------------------------------------
+# Function: subtract (wrapped version of int_subtract)
+# Arguments: 1: An integer
+# 2: Another integer
+# Returns: Returns the difference of the two integers,
+# or outputs an error on a numeric underflow
+# ----------------------------------------------------------------------------
+subtract = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_subtract,$1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: int_multiply
+# Arguments: 1: A number in x's representation
+# 2: Another number in x's represntation
+# Returns: Returns the product of the two numbers in x's representation
+# ----------------------------------------------------------------------------
+int_multiply = $(strip $(__gmsl_tr2)$(foreach a,$1,$2))
+
+# ----------------------------------------------------------------------------
+# Function: multiply (wrapped version of int_multiply)
+# Arguments: 1: An integer
+# 2: Another integer
+# Returns: Returns the product of the two integers
+# ----------------------------------------------------------------------------
+multiply = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_multiply,$1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: int_divide
+# Arguments: 1: A number in x's representation
+# 2: Another number in x's represntation
+# Returns: Returns the result of integer division of argument 1 divided
+# by argument 2 in x's representation
+# ----------------------------------------------------------------------------
+int_divide = $(__gmsl_tr2)$(strip $(if $2, \
+ $(if $(call int_gte,$1,$2), \
+ x $(call int_divide,$(call int_subtract,$1,$2),$2),), \
+ $(call __gmsl_error,Division by zero)))
+
+# ----------------------------------------------------------------------------
+# Function: divide (wrapped version of int_divide)
+# Arguments: 1: An integer
+# 2: Another integer
+# Returns: Returns the integer division of the first argument by the second
+# ----------------------------------------------------------------------------
+divide = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_divide,$1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: int_max, int_min
+# Arguments: 1: A number in x's representation
+# 2: Another number in x's represntation
+# Returns: Returns the maximum or minimum of its arguments in x's
+# representation
+# ----------------------------------------------------------------------------
+int_max = $(__gmsl_tr2)$(subst xx,x,$(join $1,$2))
+int_min = $(__gmsl_tr2)$(subst xx,x,$(filter xx,$(join $1,$2)))
+
+# ----------------------------------------------------------------------------
+# Function: max, min
+# Arguments: 1: An integer
+# 2: Another integer
+# Returns: Returns the maximum or minimum of its integer arguments
+# ----------------------------------------------------------------------------
+max = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_max,$1,$2)
+min = $(__gmsl_tr2)$(call __gmsl_int_wrap,int_min,$1,$2)
+
+# ----------------------------------------------------------------------------
+# Function: int_gt, int_gte, int_lt, int_lte, int_eq, int_ne
+# Arguments: Two x's representation numbers to be compared
+# Returns: $(true) or $(false)
+#
+# int_gt First argument greater than second argument
+# int_gte First argument greater than or equal to second argument
+# int_lt First argument less than second argument
+# int_lte First argument less than or equal to second argument
+# int_eq First argument is numerically equal to the second argument
+# int_ne First argument is not numerically equal to the second argument
+# ----------------------------------------------------------------------------
+int_gt = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(filter-out $(words $2), \
+ $(words $(call int_max,$1,$2))))
+int_gte = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(call int_gt,$1,$2)$(call int_eq,$1,$2))
+int_lt = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(filter-out $(words $1), \
+ $(words $(call int_max,$1,$2))))
+int_lte = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(call int_lt,$1,$2)$(call int_eq,$1,$2))
+int_eq = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(filter $(words $1),$(words $2)))
+int_ne = $(__gmsl_tr2)$(call __gmsl_make_bool, \
+ $(filter-out $(words $1),$(words $2)))
+
+# ----------------------------------------------------------------------------
+# Function: gt, gte, lt, lte, eq, ne
+# Arguments: Two integers to be compared
+# Returns: $(true) or $(false)
+#
+# gt First argument greater than second argument
+# gte First argument greater than or equal to second argument
+# lt First argument less than second argument
+# lte First argument less than or equal to second argument
+# eq First argument is numerically equal to the second argument
+# ne First argument is not numerically equal to the second argument
+# ----------------------------------------------------------------------------
+gt = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_gt,$1,$2)
+gte = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_gte,$1,$2)
+lt = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_lt,$1,$2)
+lte = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_lte,$1,$2)
+eq = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_eq,$1,$2)
+ne = $(__gmsl_tr2)$(call __gmsl_int_wrap2,int_ne,$1,$2)
+
+# increment adds 1 to its argument, decrement subtracts 1. Note that
+# decrement does not range check and hence will not underflow, but
+# will incorrectly say that 0 - 1 = 0
+
+# ----------------------------------------------------------------------------
+# Function: int_inc
+# Arguments: 1: A number in x's representation
+# Returns: The number incremented by 1 in x's representation
+# ----------------------------------------------------------------------------
+int_inc = $(strip $(__gmsl_tr1)$1 x)
+
+# ----------------------------------------------------------------------------
+# Function: inc
+# Arguments: 1: An integer
+# Returns: The argument incremented by 1
+# ----------------------------------------------------------------------------
+inc = $(__gmsl_tr1)$(call __gmsl_int_wrap1,int_inc,$1)
+
+# ----------------------------------------------------------------------------
+# Function: int_dec
+# Arguments: 1: A number in x's representation
+# Returns: The number decremented by 1 in x's representation
+# ----------------------------------------------------------------------------
+int_dec = $(__gmsl_tr1)$(strip $(if $(call sne,0,$(words $1)), \
+ $(wordlist 2,$(words $1),$1), \
+ $(call __gmsl_warning,Decrement underflow)))
+
+# ----------------------------------------------------------------------------
+# Function: dec
+# Arguments: 1: An integer
+# Returns: The argument decremented by 1
+# ----------------------------------------------------------------------------
+dec = $(__gmsl_tr1)$(call __gmsl_int_wrap1,int_dec,$1)
+
+# double doubles its argument, and halve halves it
+
+# ----------------------------------------------------------------------------
+# Function: int_double
+# Arguments: 1: A number in x's representation
+# Returns: The number doubled (i.e. * 2) and returned in x's representation
+# ----------------------------------------------------------------------------
+int_double = $(strip $(__gmsl_tr1)$1 $1)
+
+# ----------------------------------------------------------------------------
+# Function: double
+# Arguments: 1: An integer
+# Returns: The integer times 2
+# ----------------------------------------------------------------------------
+double = $(__gmsl_tr1)$(call __gmsl_int_wrap1,int_double,$1)
+
+# ----------------------------------------------------------------------------
+# Function: int_halve
+# Arguments: 1: A number in x's representation
+# Returns: The number halved (i.e. / 2) and returned in x's representation
+# ----------------------------------------------------------------------------
+int_halve = $(__gmsl_tr1)$(strip $(subst xx,x,$(filter-out xy x y, \
+ $(join $1,$(foreach a,$1,y x)))))
+
+# ----------------------------------------------------------------------------
+# Function: halve
+# Arguments: 1: An integer
+# Returns: The integer divided by 2
+# ----------------------------------------------------------------------------
+halve = $(__gmsl_tr1)$(call __gmsl_int_wrap1,int_halve,$1)
+
+# ----------------------------------------------------------------------------
+# Function: sequence
+# Arguments: 1: An integer
+# 2: An integer
+# Returns: The sequence [arg1, arg2] of integers if arg1 < arg2 or
+# [arg2, arg1] if arg2 > arg1. If arg1 == arg1 return [arg1]
+# ----------------------------------------------------------------------------
+sequence = $(__gmsl_tr2)$(strip $(if $(call lte,$1,$2), \
+ $(call __gmsl_sequence_up,$1,$2), \
+ $(call __gmsl_sequence_dn,$2,$1)))
+
+__gmsl_sequence_up = $(if $(call seq,$1,$2),$1,$1 $(call __gmsl_sequence_up,$(call inc,$1),$2))
+__gmsl_sequence_dn = $(if $(call seq,$1,$2),$1,$2 $(call __gmsl_sequence_dn,$1,$(call dec,$2)))
+
+ifdef __gmsl_have_eval
+# ###########################################################################
+# ASSOCIATIVE ARRAYS
+# ###########################################################################
+
+# Magic string that is very unlikely to appear in a key or value
+
+__gmsl_aa_magic := faf192c8efbc25c27992c5bc5add390393d583c6
+
+# ----------------------------------------------------------------------------
+# Function: set
+# Arguments: 1: Name of associative array
+# 2: The key value to associate
+# 3: The value associated with the key
+# Returns: Nothing
+# ----------------------------------------------------------------------------
+set = $(__gmsl_tr3)$(call assert_no_space,$0,$1$2)$(call assert_no_dollar,$0,$1$2$3)$(eval __gmsl_aa_$1_$(__gmsl_aa_magic)_$2_gmsl_aa_$1 := $3)
+
+# Only used internally by memoize function
+
+__gmsl_set = $(call set,$1,$2,$3)$3
+
+# ----------------------------------------------------------------------------
+# Function: get
+# Arguments: 1: Name of associative array
+# 2: The key to retrieve
+# Returns: The value stored in the array for that key
+# ----------------------------------------------------------------------------
+get = $(strip $(__gmsl_tr2)$(call assert_no_space,$0,$1$2)$(call assert_no_dollar,$0,$1$2)$(__gmsl_aa_$1_$(__gmsl_aa_magic)_$2_gmsl_aa_$1))
+
+# ----------------------------------------------------------------------------
+# Function: keys
+# Arguments: 1: Name of associative array
+# Returns: Returns a list of all defined keys in the array
+# ----------------------------------------------------------------------------
+keys = $(__gmsl_tr1)$(call assert_no_space,$0,$1)$(call assert_no_dollar,$0,$1)$(sort $(patsubst __gmsl_aa_$1_$(__gmsl_aa_magic)_%_gmsl_aa_$1,%, \
+ $(filter __gmsl_aa_$1_$(__gmsl_aa_magic)_%_gmsl_aa_$1,$(.VARIABLES))))
+
+# ----------------------------------------------------------------------------
+# Function: defined
+# Arguments: 1: Name of associative array
+# 2: The key to test
+# Returns: Returns true if the key is defined (i.e. not empty)
+# ----------------------------------------------------------------------------
+defined = $(__gmsl_tr2)$(call assert_no_space,$0,$1$2)$(call assert_no_dollar,$0,$1$2)$(call sne,$(call get,$1,$2),)
+
+endif # __gmsl_have_eval
+
+ifdef __gmsl_have_eval
+# ###########################################################################
+# NAMED STACKS
+# ###########################################################################
+
+# ----------------------------------------------------------------------------
+# Function: push
+# Arguments: 1: Name of stack
+# 2: Value to push onto the top of the stack (must not contain
+# a space)
+# Returns: None
+# ----------------------------------------------------------------------------
+push = $(__gmsl_tr2)$(call assert_no_space,$0,$1$2)$(call assert_no_dollar,$0,$1$2)$(eval __gmsl_stack_$1 := $2 $(if $(filter-out undefined,\
+ $(origin __gmsl_stack_$1)),$(__gmsl_stack_$1)))
+
+# ----------------------------------------------------------------------------
+# Function: pop
+# Arguments: 1: Name of stack
+# Returns: Top element from the stack after removing it
+# ----------------------------------------------------------------------------
+pop = $(__gmsl_tr1)$(call assert_no_space,$0,$1)$(call assert_no_dollar,$0,$1)$(strip $(if $(filter-out undefined,$(origin __gmsl_stack_$1)), \
+ $(call first,$(__gmsl_stack_$1)) \
+ $(eval __gmsl_stack_$1 := $(call rest,$(__gmsl_stack_$1)))))
+
+# ----------------------------------------------------------------------------
+# Function: peek
+# Arguments: 1: Name of stack
+# Returns: Top element from the stack without removing it
+# ----------------------------------------------------------------------------
+peek = $(__gmsl_tr1)$(call assert_no_space,$0,$1)$(call assert_no_dollar,$0,$1)$(call first,$(__gmsl_stack_$1))
+
+# ----------------------------------------------------------------------------
+# Function: depth
+# Arguments: 1: Name of stack
+# Returns: Number of items on the stack
+# ----------------------------------------------------------------------------
+depth = $(__gmsl_tr1)$(call assert_no_space,$0,$1)$(call assert_no_dollar,$0,$1)$(words $(__gmsl_stack_$1))
+
+endif # __gmsl_have_eval
+
+ifdef __gmsl_have_eval
+# ###########################################################################
+# STRING CACHE
+# ###########################################################################
+
+# ----------------------------------------------------------------------------
+# Function: memoize
+# Arguments: 1. Name of the function to be called if the string
+# has not been previously seen
+# 2. A string
+# Returns: Returns the result of a memo function (which the user must
+# define) on the passed in string and remembers the result.
+#
+# Example: Set memo = $(shell echo "$1" | md5sum) to make a cache
+# of MD5 hashes of strings. $(call memoize,memo,foo bar baz)
+# ----------------------------------------------------------------------------
+__gmsl_memoize = $(subst $(__gmsl_space),§,$1)cc2af1bb7c4482f2ba75e338b963d3e7$(subst $(__gmsl_space),§,$2)
+memoize = $(__gmsl_tr2)$(strip $(if $(call defined,__gmsl_m,$(__gmsl_memoize)),\
+ $(call get,__gmsl_m,$(__gmsl_memoize)), \
+ $(call __gmsl_set,__gmsl_m,$(__gmsl_memoize),$(call $1,$2))))
+
+endif # __gmsl_have_eval
+
+# ###########################################################################
+# DEBUGGING FACILITIES
+# ###########################################################################
+
+# ----------------------------------------------------------------------------
+# Target: gmsl-print-%
+# Arguments: The % should be replaced by the name of a variable that you
+# wish to print out.
+# Action: Echos the name of the variable that matches the % and its value.
+# For example, 'make gmsl-print-SHELL' will output the value of
+# the SHELL variable
+# ----------------------------------------------------------------------------
+gmsl-print-%: ; @echo $* = $($*)
+
+# ----------------------------------------------------------------------------
+# Function: assert
+# Arguments: 1: A boolean that must be true or the assertion will fail
+# 2: The message to print with the assertion
+# Returns: None
+# ----------------------------------------------------------------------------
+assert = $(if $1,,$(call __gmsl_error,Assertion failure: $2))
+
+# ----------------------------------------------------------------------------
+# Function: assert_exists
+# Arguments: 1: Name of file that must exist, if it is missing an assertion
+# will be generated
+# Returns: None
+# ----------------------------------------------------------------------------
+assert_exists = $(call assert,$(wildcard $1),file '$1' missing)
+
+# ----------------------------------------------------------------------------
+# Function: assert_no_dollar
+# Arguments: 1: Name of a function being executd
+# 2: Arguments to check
+# Returns: None
+# ----------------------------------------------------------------------------
+assert_no_dollar = $(call __gmsl_tr2)$(call assert,$(call not,$(findstring $(__gmsl_dollar),$2)),$1 called with a dollar sign in argument)
+
+# ----------------------------------------------------------------------------
+# Function: assert_no_space
+# Arguments: 1: Name of a function being executd
+# 2: Arguments to check
+# Returns: None
+# ----------------------------------------------------------------------------
+ifeq ($(__gmsl_spaced_vars),$(false))
+assert_no_space = $(call assert,$(call not,$(findstring $(__gmsl_aa_magic),$(subst $(__gmsl_space),$(__gmsl_aa_magic),$2))),$1 called with a space in argument)
+else
+assert_no_space =
+endif
diff --git a/tools/gmsl b/tools/gmsl
new file mode 100644
index 0000000..b4e907b
--- /dev/null
+++ b/tools/gmsl
@@ -0,0 +1,89 @@
+# ----------------------------------------------------------------------------
+#
+# GNU Make Standard Library (GMSL)
+#
+# A library of functions to be used with GNU Make's $(call) that
+# provides functionality not available in standard GNU Make.
+#
+# Copyright (c) 2005-2013 John Graham-Cumming
+#
+# This file is part of GMSL
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of the John Graham-Cumming nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ----------------------------------------------------------------------------
+
+# Determine if the library has already been included and if so don't
+# bother including it again
+
+ifndef __gmsl_included
+
+# Standard definitions for true and false. true is any non-empty
+# string, false is an empty string. These are intended for use with
+# $(if).
+
+true := T
+false :=
+
+# ----------------------------------------------------------------------------
+# Function: not
+# Arguments: 1: A boolean value
+# Returns: Returns the opposite of the arg. (true -> false, false -> true)
+# ----------------------------------------------------------------------------
+not = $(if $1,$(false),$(true))
+
+# Prevent reinclusion of the library
+
+__gmsl_included := $(true)
+
+# Try to determine where this file is located. If the caller did
+# include /foo/gmsl then extract the /foo/ so that __gmsl gets
+# included transparently
+
+ifneq ($(MAKEFILE_LIST),)
+__gmsl_root := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+
+# If there are any spaces in the path in __gmsl_root then give up
+
+ifeq (1,$(words $(__gmsl_root)))
+__gmsl_root := $(patsubst %gmsl,%,$(__gmsl_root))
+else
+__gmsl_root :=
+endif
+
+include $(__gmsl_root)__gmsl
+
+else
+
+include __gmsl
+
+endif
+
+endif # __gmsl_included
+
diff --git a/tools/gmsl-tests b/tools/gmsl-tests
new file mode 100644
index 0000000..1f96481
--- /dev/null
+++ b/tools/gmsl-tests
@@ -0,0 +1,730 @@
+# ----------------------------------------------------------------------------
+#
+# GNU Make Standard Library (GMSL) Test Suite
+#
+# Test suite for the GMSL
+#
+# Copyright (c) 2005-2013 John Graham-Cumming
+#
+# This file is part of GMSL
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of the John Graham-Cumming nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ----------------------------------------------------------------------------
+
+.PHONY: all
+all:
+ @echo
+ @echo Test Summary
+ @echo ------------
+ @echo "$(call int_decode,$(passed)) tests passed; $(call int_decode,$(failed)) tests failed"
+
+include gmsl
+
+passed :=
+failed :=
+
+ECHO := /bin/echo
+
+start_test = $(shell $(ECHO) -n "Testing '$1': " >&2)$(eval current_test := OK)
+stop_test = $(shell $(ECHO) " $(current_test)" >&2)
+test_pass = .$(eval passed := $(call int_inc,$(passed)))
+test_fail = X$(eval failed := $(call int_inc,$(failed)))$(eval current_test := ERROR '$1' != '$2')
+test_assert = $(if $(filter undefined,$(origin 2)),$(eval 2 :=))$(shell $(ECHO) -n $(if $(call seq,$1,$2),$(call test_pass,$1,$2),$(call test_fail,$1,$2)) >&2)
+
+$(call start_test,not)
+$(call test_assert,$(call not,$(true)),$(false))
+$(call test_assert,$(call not,$(false)),$(true))
+$(call stop_test)
+
+$(call start_test,or)
+$(call test_assert,$(call or,$(true),$(true)),$(true))
+$(call test_assert,$(call or,$(true),$(false)),$(true))
+$(call test_assert,$(call or,$(false),$(true)),$(true))
+$(call test_assert,$(call or,$(false),$(false)),$(false))
+$(call stop_test)
+
+$(call start_test,and)
+$(call test_assert,$(call and,$(true),$(true)),$(true))
+$(call test_assert,$(call and,$(true),$(false)),$(false))
+$(call test_assert,$(call and,$(false),$(true)),$(false))
+$(call test_assert,$(call and,$(false),$(false)),$(false))
+$(call stop_test)
+
+$(call start_test,xor)
+$(call test_assert,$(call xor,$(true),$(true)),$(false))
+$(call test_assert,$(call xor,$(true),$(false)),$(true))
+$(call test_assert,$(call xor,$(false),$(true)),$(true))
+$(call test_assert,$(call xor,$(false),$(false)),$(false))
+$(call stop_test)
+
+$(call start_test,nand)
+$(call test_assert,$(call nand,$(true),$(true)),$(false))
+$(call test_assert,$(call nand,$(true),$(false)),$(true))
+$(call test_assert,$(call nand,$(false),$(true)),$(true))
+$(call test_assert,$(call nand,$(false),$(false)),$(true))
+$(call stop_test)
+
+$(call start_test,nor)
+$(call test_assert,$(call nor,$(true),$(true)),$(false))
+$(call test_assert,$(call nor,$(true),$(false)),$(false))
+$(call test_assert,$(call nor,$(false),$(true)),$(false))
+$(call test_assert,$(call nor,$(false),$(false)),$(true))
+$(call stop_test)
+
+$(call start_test,xnor)
+$(call test_assert,$(call xnor,$(true),$(true)),$(true))
+$(call test_assert,$(call xnor,$(true),$(false)),$(false))
+$(call test_assert,$(call xnor,$(false),$(true)),$(false))
+$(call test_assert,$(call xnor,$(false),$(false)),$(true))
+$(call stop_test)
+
+$(call start_test,first)
+$(call test_assert,$(call first,1 2 3),1)
+$(call test_assert,$(call first,1),1)
+$(call test_assert,$(call first,),)
+$(call stop_test)
+
+$(call start_test,last)
+$(call test_assert,$(call last,1 2 3),3)
+$(call test_assert,$(call last,1),1)
+$(call test_assert,$(call last,),)
+$(call stop_test)
+
+$(call start_test,rest)
+$(call test_assert,$(call rest,1 2 3),2 3)
+$(call test_assert,$(call rest,1),)
+$(call test_assert,$(call rest,),)
+$(call stop_test)
+
+$(call start_test,chop)
+$(call test_assert,$(call chop,1 2 3),1 2)
+$(call test_assert,$(call chop,1 2 3 4),1 2 3)
+$(call test_assert,$(call chop,1),)
+$(call test_assert,$(call chop,),)
+$(call stop_test)
+
+$(call start_test,length)
+$(call test_assert,$(call length,1 2 3),3)
+$(call test_assert,$(call length,1 2 3 4),4)
+$(call test_assert,$(call length,1),1)
+$(call test_assert,$(call length,),0)
+$(call stop_test)
+
+$(call start_test,map)
+$(call test_assert,$(call map,origin,__undefined map MAKE),undefined file default)
+$(call test_assert,$(call map,origin,),)
+$(call stop_test)
+
+joinem = $1$2
+$(call start_test,pairmap)
+$(call test_assert,$(call pairmap,addsuffix,2 1 3,a b c),a2 b1 c3)
+$(call test_assert,$(call pairmap,addprefix,2 1 3,a b c d),2a 1b 3c d)
+$(call test_assert,$(call pairmap,addprefix,2 1 3 4,a b c),2a 1b 3c)
+$(call test_assert,$(call pairmap,joinem,2 1 3 4,a b c),2a 1b 3c 4)
+$(call stop_test)
+
+$(call start_test,seq)
+$(call test_assert,$(call seq,abc,abc),T)
+$(call test_assert,$(call seq,x,),)
+$(call test_assert,$(call seq,,x),)
+$(call test_assert,$(call seq,x,x),T)
+$(call test_assert,$(call seq,a%c,abc),)
+$(call test_assert,$(call seq,abc,a%c),)
+$(call test_assert,$(call seq,abc,ABC),)
+$(call test_assert,$(call seq,abc,),)
+$(call test_assert,$(call seq,,),T)
+$(call test_assert,$(call seq,a b c,a b c),T)
+$(call test_assert,$(call seq,aa% bb% cc,aa% bb% cc),T)
+$(call test_assert,$(call seq,aa% bb% cc,aa% bb cc),)
+$(call test_assert,$(call seq,aa% bb% cc,xx yy zz),)
+$(call test_assert,$(call seq,x x,),)
+$(call test_assert,$(call seq, xx x,x xx),)
+$(call test_assert,$(call seq, , ),T)
+$(call test_assert,$(call seq,, ),)
+$(call test_assert,$(call seq, ,),)
+$(call test_assert,$(call seq,y,xy),)
+$(call stop_test)
+
+$(call start_test,sne)
+$(call test_assert,$(call sne,abc,abc),)
+$(call test_assert,$(call sne,x,),T)
+$(call test_assert,$(call sne,,x),T)
+$(call test_assert,$(call sne,x,x),)
+$(call test_assert,$(call sne,abc,ABC),T)
+$(call test_assert,$(call sne,abc,),T)
+$(call test_assert,$(call sne,,),)
+$(call test_assert,$(call sne,a b c,a b c),)
+$(call test_assert,$(call sne,aa% bb% cc,aa% bb% cc),)
+$(call test_assert,$(call sne,aa% bb% cc,aa% bb cc),T)
+$(call stop_test)
+
+$(call start_test,strlen)
+$(call test_assert,$(call strlen,),0)
+$(call test_assert,$(call strlen,a),1)
+$(call test_assert,$(call strlen,a b),3)
+$(call test_assert,$(call strlen,a ),2)
+$(call test_assert,$(call strlen, a),2)
+$(call test_assert,$(call strlen, ),2)
+$(call test_assert,$(call strlen, ),3)
+$(call test_assert,$(call strlen, ),4)
+$(call stop_test)
+
+$(call start_test,substr)
+$(call test_assert,$(call substr,xyz,1,1),x)
+$(call test_assert,$(call substr,xyz,1,2),xy)
+$(call test_assert,$(call substr,xyz,2,3),yz)
+$(call test_assert,$(call substr,some string,1,1),s)
+$(call test_assert,$(call substr,some string,1,2),so)
+$(call test_assert,$(call substr,some string,1,3),som)
+$(call test_assert,$(call substr,some string,1,4),some)
+$(call test_assert,$(call substr,some string,1,5),some )
+$(call test_assert,$(call substr,some string,1,6),some s)
+$(call test_assert,$(call substr,some string,5,5), )
+$(call test_assert,$(call substr,some string,5,6), s)
+$(call test_assert,$(call substr,some string,5,7), st)
+$(call test_assert,$(call substr,some string,5,8), str)
+$(call test_assert,$(call substr,some string,1,100),some string)
+$(call stop_test)
+
+$(call start_test,lc)
+$(call test_assert,$(call lc,The Quick Brown Fox),the quick brown fox)
+$(call test_assert,$(call lc,the1 quick2 brown3 fox4),the1 quick2 brown3 fox4)
+$(call test_assert,$(call lc,The_),the_)
+$(call test_assert,$(call lc,),)
+$(call stop_test)
+
+$(call start_test,uc)
+$(call test_assert,$(call uc,The Quick Brown Fox),THE QUICK BROWN FOX)
+$(call test_assert,$(call uc,the1 quick2 brown3 fox4),THE1 QUICK2 BROWN3 FOX4)
+$(call test_assert,$(call uc,The_),THE_)
+$(call test_assert,$(call uc,),)
+$(call stop_test)
+
+$(call start_test,tr)
+$(call test_assert,$(call tr,A B C,1 2 3,CAPITAL),31PIT1L)
+$(call test_assert,$(call tr,a b c,1 2 3,CAPITAL),CAPITAL)
+$(call test_assert,$(call tr,E L I,3 1 1,I AM ELITE),1 AM 311T3)
+$(call stop_test)
+
+$(call start_test,leq)
+$(call test_assert,$(call leq,1 2 3,1 2 3),T)
+$(call test_assert,$(call leq,1 2 3,1 2 3 4),)
+$(call test_assert,$(call leq,1 2 3 4,1 2 3),)
+$(call test_assert,$(call leq,1,1),T)
+$(call test_assert,$(call leq,,),T)
+$(call stop_test)
+
+$(call start_test,lne)
+$(call test_assert,$(call lne,1 2 3,1 2 3),)
+$(call test_assert,$(call lne,1 2 3,1 2 3 4),T)
+$(call test_assert,$(call lne,1 2 3 4,1 2 3),T)
+$(call test_assert,$(call lne,1,1),)
+$(call test_assert,$(call lne,,),)
+$(call stop_test)
+
+$(call start_test,empty_set)
+$(call test_assert,$(empty_set),)
+$(call test_assert,$(empty_set),$(call set_create,))
+$(call stop_test)
+
+$(call start_test,set_create)
+$(call test_assert,$(call set_create,),)
+$(call test_assert,$(call set_create,1 2 2 3),1 2 3)
+$(call test_assert,$(call set_create,2 1 1 2 2 3),1 2 3)
+$(call test_assert,$(call set_create,1),1)
+$(call stop_test)
+
+$(call start_test,set_insert)
+$(call test_assert,$(call set_insert,1,$(empty_set)),1)
+$(call test_assert,$(call set_insert,1,$(call set_create,1)),1)
+$(call test_assert,$(call set_insert,1,$(call set_create,1 2)),1 2)
+$(call test_assert,$(call set_insert,0,$(call set_create,1 2)),0 1 2)
+$(call stop_test)
+
+$(call start_test,set_remove)
+$(call test_assert,$(call set_remove,1,$(empty_set)),$(empty_set))
+$(call test_assert,$(call set_remove,1,$(call set_create,1 2)),2)
+$(call test_assert,$(call set_remove,1,$(call set_create,1 11 2)),11 2)
+$(call test_assert,$(call set_remove,0,$(call set_create,1 2)),1 2)
+$(call stop_test)
+
+$(call start_test,set_is_member)
+$(call test_assert,$(call set_is_member,1,$(empty_set)),)
+$(call test_assert,$(call set_is_member,1,$(call set_create,2 3)),)
+$(call test_assert,$(call set_is_member,1,$(call set_create,1 2 3)),T)
+$(call test_assert,$(call set_is_member,1,$(call set_create,1)),T)
+$(call stop_test)
+
+$(call start_test,set_union)
+$(call test_assert,$(call set_union,,),)
+$(call test_assert,$(call set_union,1 2,),1 2)
+$(call test_assert,$(call set_union,,3 4),3 4)
+$(call test_assert,$(call set_union,1 2,3 4),1 2 3 4)
+$(call test_assert,$(call set_union,1 2 3,3 4 5),1 2 3 4 5)
+$(call stop_test)
+
+$(call start_test,set_intersection)
+$(call test_assert,$(call set_intersection,,),)
+$(call test_assert,$(call set_intersection,1 2,),)
+$(call test_assert,$(call set_intersection,,3 4),)
+$(call test_assert,$(call set_intersection,1 2,3 4),)
+$(call test_assert,$(call set_intersection,1 2 3 4,3 4 5),3 4)
+$(call stop_test)
+
+$(call start_test,set_is_subset)
+$(call test_assert,$(call set_is_subset,,),T)
+$(call test_assert,$(call set_is_subset,1 2,),)
+$(call test_assert,$(call set_is_subset,,3 4),T)
+$(call test_assert,$(call set_is_subset,1 2,3 4),)
+$(call test_assert,$(call set_is_subset,1 2,1 2 3 4 5),T)
+$(call test_assert,$(call set_is_subset,1 2,1 2),T)
+$(call test_assert,$(call set_is_subset,1 2,1 3 4 5),)
+$(call stop_test)
+
+$(call start_test,set_equal)
+$(call test_assert,$(call set_equal,,),T)
+$(call test_assert,$(call set_equal,1,),)
+$(call test_assert,$(call set_equal,,1),)
+$(call test_assert,$(call set_equal,1,1),T)
+$(call test_assert,$(call set_equal,1 2,),)
+$(call test_assert,$(call set_equal,,1 2),)
+$(call test_assert,$(call set_equal,1 2,1 2 3),)
+$(call stop_test)
+
+$(call start_test,int_encode)
+$(call test_assert,$(call int_encode,0),)
+$(call test_assert,$(call int_encode,1),x)
+$(call test_assert,$(call int_encode,2),x x)
+$(call test_assert,$(call int_encode,10),x x x x x x x x x x)
+$(call stop_test)
+
+$(call start_test,int_decode)
+$(call test_assert,$(call int_decode,),0)
+$(call test_assert,$(call int_decode,x),1)
+$(call test_assert,$(call int_decode,x x),2)
+$(call test_assert,$(call int_decode,x x x x x x x x x x),10)
+$(call stop_test)
+
+$(call start_test,int_plus)
+$(call test_assert,$(call int_plus,$(call int_encode,3),$(call int_encode,4)),$(call int_encode,7))
+$(call test_assert,$(call int_plus,$(call int_encode,0),$(call int_encode,4)),$(call int_encode,4))
+$(call test_assert,$(call int_plus,$(call int_encode,3),$(call int_encode,0)),$(call int_encode,3))
+$(call test_assert,$(call int_plus,$(call int_encode,0),$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_plus,$(call int_encode,1),$(call int_encode,0)),$(call int_encode,1))
+$(call stop_test)
+
+$(call start_test,plus)
+$(call test_assert,$(call plus,3,4),7)
+$(call test_assert,$(call plus,4,3),7)
+$(call test_assert,$(call plus,0,4),4)
+$(call test_assert,$(call plus,3,0),3)
+$(call test_assert,$(call plus,0,0),0)
+$(call stop_test)
+
+__gmsl_warning = $1
+$(call start_test,int_subtract)
+$(call test_assert,$(call int_subtract,$(call int_encode,3),$(call int_encode,4)),Subtraction underflow)
+$(call test_assert,$(call int_subtract,$(call int_encode,4),$(call int_encode,3)),$(call int_encode,1))
+$(call test_assert,$(call int_subtract,$(call int_encode,3),$(call int_encode,0)),$(call int_encode,3))
+$(call test_assert,$(call int_subtract,$(call int_encode,0),$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_subtract,$(call int_encode,1),$(call int_encode,0)),$(call int_encode,1))
+$(call stop_test)
+
+__gmsl_warning = x x x x x x x x x x
+$(call start_test,subtract)
+$(call test_assert,$(call subtract,3,4),10)
+$(call test_assert,$(call subtract,4,3),1)
+$(call test_assert,$(call subtract,3,0),3)
+$(call test_assert,$(call subtract,0,0),0)
+$(call stop_test)
+
+$(call start_test,int_multiply)
+$(call test_assert,$(call int_multiply,$(call int_encode,3),$(call int_encode,4)),$(call int_encode,12))
+$(call test_assert,$(call int_multiply,$(call int_encode,4),$(call int_encode,3)),$(call int_encode,12))
+$(call test_assert,$(call int_multiply,$(call int_encode,3),$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_multiply,$(call int_encode,0),$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_multiply,$(call int_encode,1),$(call int_encode,0)),$(call int_encode,0))
+$(call stop_test)
+
+$(call start_test,multiply)
+$(call test_assert,$(call multiply,3,4),12)
+$(call test_assert,$(call multiply,4,3),12)
+$(call test_assert,$(call multiply,3,0),0)
+$(call test_assert,$(call multiply,0,3),0)
+$(call test_assert,$(call multiply,0,0),0)
+$(call stop_test)
+
+__gmsl_error = $1
+$(call start_test,int_divide)
+$(call test_assert,$(call int_divide,$(call int_encode,3),$(call int_encode,4)),$(call int_encode,0))
+$(call test_assert,$(call int_divide,$(call int_encode,4),$(call int_encode,3)),$(call int_encode,1))
+$(call test_assert,$(call int_divide,$(call int_encode,31),$(call int_encode,3)),$(call int_encode,10))
+$(call test_assert,$(call int_divide,$(call int_encode,30),$(call int_encode,3)),$(call int_encode,10))
+$(call test_assert,$(call int_divide,$(call int_encode,29),$(call int_encode,3)),$(call int_encode,9))
+$(call test_assert,$(call int_divide,$(call int_encode,0),$(call int_encode,1)),$(call int_encode,0))
+$(call test_assert,$(call int_divide,$(call int_encode,1),$(call int_encode,0)),Division by zero)
+$(call stop_test)
+
+__gmsl_error = x x x x x x x x x x
+$(call start_test,divide)
+$(call test_assert,$(call divide,3,4),0)
+$(call test_assert,$(call divide,4,3),1)
+$(call test_assert,$(call divide,21,2),10)
+$(call test_assert,$(call divide,20,2),10)
+$(call test_assert,$(call divide,19,2),9)
+$(call test_assert,$(call divide,1,0),10)
+$(call stop_test)
+
+$(call start_test,associative array)
+$(call test_assert,$(call get,myarray,key1),)
+$(call test_assert,$(call set,myarray,key1,value1),)
+$(call test_assert,$(call get,myarray,key1),value1)
+$(call test_assert,$(call get,myarray,key2),)
+$(call test_assert,$(call get,myarray1,key1),)
+$(call test_assert,$(call defined,myarray,key1),T)
+$(call test_assert,$(call defined,myarray,key2),)
+$(call test_assert,$(call defined,myarray1,key1),)
+$(call test_assert,$(call set,myarray,key2,value2),)
+$(call test_assert,$(call keys,myarray),key1 key2)
+$(call test_assert,$(call keys,myarray1),)
+$(call test_assert,$(call set,foo,bar_baz,bob),)
+$(call test_assert,$(call set,foo_bar,baz,alice),)
+$(call test_assert,$(call get,foo,bar_baz),bob)
+$(call test_assert,$(call get,foo_bar,baz),alice)
+$(call test_assert,$(call set,foo,bar,baz/baz),)
+$(call test_assert,$(call get,foo,bar),baz/baz)
+$(call test-assert,$(call set,foo,bar-baz,baz),)
+$(call test_assert,$(call get,foo,bar-baz),baz)
+$(call set,foo,bar-baz,baz)
+$(call set,foo,bar,baz/baz)
+$(call stop_test)
+
+$(call start_test,named stack)
+$(call test_assert,$(call pop,mystack),)
+$(call test_assert,$(call push,mystack,e2))
+$(call push,mystack,e1)
+$(call test_assert,$(call pop,mystack),e1)
+$(call test_assert,$(call pop,mystack),e2)
+$(call push,mystack,f3)
+$(call push,mystack,f1)
+$(call test_assert,$(call pop,mystack),f1)
+$(call push,mystack,f2)
+$(call test_assert,$(call peek,mystack),f2)
+$(call test_assert,$(call depth,mystack),2)
+$(call test_assert,$(call pop,mystack),f2)
+$(call test_assert,$(call depth,mystack),1)
+$(call test_assert,$(call pop,myotherstack),)
+$(call stop_test)
+
+$(call start_test,reverse)
+$(call test_assert,$(call reverse,),)
+$(call test_assert,$(call reverse,1),1)
+$(call test_assert,$(call reverse,1 2),2 1)
+$(call test_assert,$(call reverse,1 2 3),3 2 1)
+$(call stop_test)
+
+$(call start_test,uniq)
+$(call test_assert,$(call uniq,),)
+$(call test_assert,$(call uniq,a),a)
+$(call test_assert,$(call uniq,a a),a)
+$(call test_assert,$(call uniq,a aa),a aa)
+$(call test_assert,$(call uniq,a aa a),a aa)
+$(call test_assert,$(call uniq,a b ba ab b a a ba a),a b ba ab)
+$(call stop_test)
+
+c:=,
+$(call start_test,split)
+$(call test_assert,$(call split,$c,comma$cseparated$cstring),comma separated string)
+$(call test_assert,$(call split,*,star*field*record),star field record)
+$(call test_assert,$(call split,*,star*),star)
+$(call test_assert,$(call split,*,star*field),star field)
+$(call test_assert,$(call split,*,star****field),star field)
+$(call test_assert,$(call split,*,),)
+$(call stop_test)
+
+$(call start_test,merge)
+$(call test_assert,$(call merge,$c,list of things),list$cof$cthings)
+$(call test_assert,$(call merge,*,list of things),list*of*things)
+$(call test_assert,$(call merge,*,list),list)
+$(call test_assert,$(call merge,*,),)
+$(call stop_test)
+
+$(call start_test,int_max)
+$(call test_assert,$(call int_max,$(call int_encode,2),$(call int_encode,1)),$(call int_encode,2))
+$(call test_assert,$(call int_max,$(call int_encode,1),$(call int_encode,2)),$(call int_encode,2))
+$(call test_assert,$(call int_max,$(call int_encode,2),$(call int_encode,0)),$(call int_encode,2))
+$(call test_assert,$(call int_max,$(call int_encode,0),$(call int_encode,2)),$(call int_encode,2))
+$(call test_assert,$(call int_max,$(call int_encode,2),$(call int_encode,2)),$(call int_encode,2))
+$(call test_assert,$(call int_max,$(call int_encode,0),$(call int_encode,0)),$(call int_encode,0))
+$(call stop_test)
+
+$(call start_test,max)
+$(call test_assert,$(call max,2,1),2)
+$(call test_assert,$(call max,1,2),2)
+$(call test_assert,$(call max,2,0),2)
+$(call test_assert,$(call max,0,2),2)
+$(call test_assert,$(call max,2,2),2)
+$(call test_assert,$(call max,0,0),0)
+$(call stop_test)
+
+$(call start_test,int_min)
+$(call test_assert,$(call int_min,$(call int_encode,2),$(call int_encode,1)),$(call int_encode,1))
+$(call test_assert,$(call int_min,$(call int_encode,1),$(call int_encode,2)),$(call int_encode,1))
+$(call test_assert,$(call int_min,$(call int_encode,2),$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_min,$(call int_encode,0),$(call int_encode,2)),$(call int_encode,0))
+$(call test_assert,$(call int_min,$(call int_encode,2),$(call int_encode,2)),$(call int_encode,2))
+$(call test_assert,$(call int_min,$(call int_encode,0),$(call int_encode,0)),$(call int_encode,0))
+$(call stop_test)
+
+$(call start_test,min)
+$(call test_assert,$(call min,2,1),1)
+$(call test_assert,$(call min,1,2),1)
+$(call test_assert,$(call min,2,0),0)
+$(call test_assert,$(call min,0,2),0)
+$(call test_assert,$(call min,2,2),2)
+$(call test_assert,$(call min,0,0),0)
+$(call stop_test)
+
+__gmsl_error = $1
+$(call start_test,assert functions)
+$(call test_assert,$(call assert,$(true),ignore),)
+$(call test_assert,$(call assert,$(false),failed),Assertion failure: failed)
+$(call test_assert,$(call assert_exists,gmsl-tests),)
+$(call test_assert,$(call assert_exists,MISSING-gmsl-tests),Assertion failure: file 'MISSING-gmsl-tests' missing)
+$(call stop_test)
+
+$(call start_test,int_inc)
+$(call test_assert,$(call int_inc,$(call int_encode,0)),$(call int_encode,1))
+$(call test_assert,$(call int_inc,$(call int_encode,1)),$(call int_encode,2))
+$(call test_assert,$(call int_inc,$(call int_encode,4)),$(call int_encode,5))
+$(call test_assert,$(call int_inc,$(call int_encode,10)),$(call int_encode,11))
+$(call stop_test)
+
+$(call start_test,inc)
+$(call test_assert,$(call inc,0),1)
+$(call test_assert,$(call inc,1),2)
+$(call test_assert,$(call inc,4),5)
+$(call test_assert,$(call inc,10),11)
+$(call stop_test)
+
+__gmsl_warning = $1
+$(call start_test,int_dec)
+$(call test_assert,$(call int_dec,$(call int_encode,0)),Decrement underflow)
+$(call test_assert,$(call int_dec,$(call int_encode,1)),$(call int_encode,0))
+$(call test_assert,$(call int_dec,$(call int_encode,4)),$(call int_encode,3))
+$(call test_assert,$(call int_dec,$(call int_encode,10)),$(call int_encode,9))
+$(call stop_test)
+
+__gmsl_warning = x x x x x x x x x x
+$(call start_test,dec)
+$(call test_assert,$(call dec,0),10)
+$(call test_assert,$(call dec,1),0)
+$(call test_assert,$(call dec,4),3)
+$(call test_assert,$(call dec,10),9)
+$(call stop_test)
+
+$(call start_test,int_double)
+$(call test_assert,$(call int_double,$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_double,$(call int_encode,1)),$(call int_encode,2))
+$(call test_assert,$(call int_double,$(call int_encode,4)),$(call int_encode,8))
+$(call stop_test)
+
+$(call start_test,double)
+$(call test_assert,$(call double,0),0)
+$(call test_assert,$(call double,1),2)
+$(call test_assert,$(call double,4),8)
+$(call stop_test)
+
+$(call start_test,int_halve)
+$(call test_assert,$(call int_halve,$(call int_encode,0)),$(call int_encode,0))
+$(call test_assert,$(call int_halve,$(call int_encode,2)),$(call int_encode,1))
+$(call test_assert,$(call int_halve,$(call int_encode,8)),$(call int_encode,4))
+$(call test_assert,$(call int_halve,$(call int_encode,7)),$(call int_encode,3))
+$(call stop_test)
+
+$(call start_test,halve)
+$(call test_assert,$(call halve,0),0)
+$(call test_assert,$(call halve,2),1)
+$(call test_assert,$(call halve,8),4)
+$(call test_assert,$(call halve,7),3)
+$(call stop_test)
+
+$(call start_test,gt)
+$(call test_assert,$(call gt,2,3),)
+$(call test_assert,$(call gt,3,2),$(true))
+$(call test_assert,$(call gt,2,2),)
+$(call stop_test)
+
+$(call start_test,gte)
+$(call test_assert,$(call gte,2,3),)
+$(call test_assert,$(call gte,3,2),$(true))
+$(call test_assert,$(call gte,2,2),$(true))
+$(call stop_test)
+
+$(call start_test,lt)
+$(call test_assert,$(call lt,2,3),$(true))
+$(call test_assert,$(call lt,3,2),)
+$(call test_assert,$(call lt,2,2),)
+$(call stop_test)
+
+$(call start_test,lte)
+$(call test_assert,$(call lte,2,3),$(true))
+$(call test_assert,$(call lte,3,2),)
+$(call test_assert,$(call lte,2,2),$(true))
+$(call stop_test)
+
+$(call start_test,eq)
+$(call test_assert,$(call eq,2,3),)
+$(call test_assert,$(call eq,3,2),)
+$(call test_assert,$(call eq,2,2),$(true))
+$(call stop_test)
+
+$(call start_test,ne)
+$(call test_assert,$(call ne,2,3),$(true))
+$(call test_assert,$(call ne,3,2),$(true))
+$(call test_assert,$(call ne,2,2),)
+$(call stop_test)
+
+$(call start_test,int_gt)
+$(call test_assert,$(call int_gt,$(call int_encode,2),$(call int_encode,3)),)
+$(call test_assert,$(call int_gt,$(call int_encode,3),$(call int_encode,2)),$(true))
+$(call test_assert,$(call int_gt,$(call int_encode,2),$(call int_encode,2)),)
+$(call stop_test)
+
+$(call start_test,int_gte)
+$(call test_assert,$(call int_gte,$(call int_encode,2),$(call int_encode,3)),)
+$(call test_assert,$(call int_gte,$(call int_encode,3),$(call int_encode,2)),$(true))
+$(call test_assert,$(call int_gte,$(call int_encode,2),$(call int_encode,2)),$(true))
+$(call stop_test)
+
+$(call start_test,int_lt)
+$(call test_assert,$(call int_lt,$(call int_encode,2),$(call int_encode,3)),$(true))
+$(call test_assert,$(call int_lt,$(call int_encode,3),$(call int_encode,2)),)
+$(call test_assert,$(call int_lt,$(call int_encode,2),$(call int_encode,2)),)
+$(call stop_test)
+
+$(call start_test,int_lte)
+$(call test_assert,$(call int_lte,$(call int_encode,2),$(call int_encode,3)),$(true))
+$(call test_assert,$(call int_lte,$(call int_encode,3),$(call int_encode,2)),)
+$(call test_assert,$(call int_lte,$(call int_encode,2),$(call int_encode,2)),$(true))
+$(call stop_test)
+
+$(call start_test,int_eq)
+$(call test_assert,$(call int_eq,$(call int_encode,2),$(call int_encode,3)),)
+$(call test_assert,$(call int_eq,$(call int_encode,3),$(call int_encode,2)),)
+$(call test_assert,$(call int_eq,$(call int_encode,2),$(call int_encode,2)),$(true))
+$(call stop_test)
+
+$(call start_test,int_ne)
+$(call test_assert,$(call int_ne,$(call int_encode,2),$(call int_encode,3)),$(true))
+$(call test_assert,$(call int_ne,$(call int_encode,3),$(call int_encode,2)),$(true))
+$(call test_assert,$(call int_ne,$(call int_encode,2),$(call int_encode,2)),)
+$(call stop_test)
+
+$(call start_test,sequence)
+$(call test_assert,$(call sequence,0,0),0)
+$(call test_assert,$(call sequence,1,1),1)
+$(call test_assert,$(call sequence,10,10),10)
+$(call test_assert,$(call sequence,0,1),0 1)
+$(call test_assert,$(call sequence,0,2),0 1 2)
+$(call test_assert,$(call sequence,1,2),1 2)
+$(call test_assert,$(call sequence,1,4),1 2 3 4)
+$(call test_assert,$(call sequence,10,20),10 11 12 13 14 15 16 17 18 19 20)
+$(call test_assert,$(call sequence,1,0),1 0)
+$(call test_assert,$(call sequence,2,1),2 1)
+$(call test_assert,$(call sequence,3,1),3 2 1)
+$(call test_assert,$(call sequence,20,10),20 19 18 17 16 15 14 13 12 11 10)
+$(call stop_test)
+
+$(call start_test,memoize)
+memo_counter = $(call int_encode,0)
+memo = $(eval memo_counter := $(call int_inc,$(memo_counter)))$(firstword $1)
+$(call test_assert,$(call int_decode,$(memo_counter)),0)
+$(call test_assert,$(call memoize,memo,hello john),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),1)
+$(call test_assert,$(call memoize,memo,hello john),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),1)
+$(call test_assert,$(call memoize,memo,hello john how are you),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),2)
+$(call test_assert,$(call memoize,memo,john),john)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+$(call test_assert,$(call memoize,memo,hello john),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+$(call test_assert,$(call memoize,memo,hello john how are you),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+$(call test_assert,$(call memoize,memo,john),john)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+md5_counter = $(call int_encode,0)
+ifneq ("$(shell echo -n hello | md5sum 2> /dev/null)","")
+md5_program := md5sum
+endif
+ifneq ("$(shell md5 -s hello 2> /dev/null)","")
+md5_program := md5
+endif
+ifeq ("$(md5_program)","")
+$(error Can't find suitable MD5 program. Tried md5sum and md5)
+endif
+md5 = $(eval md5_counter = $(call int_inc,$(md5_counter)))$(firstword $(shell echo "$1" | $(md5_program)))
+$(call test_assert,$(call memoize,md5,hello john),2d62190b10246ee2f2e233f9df840445)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+$(call test_assert,$(call int_decode,$(md5_counter)),1)
+$(call test_assert,$(call memoize,memo,hello john),hello)
+$(call test_assert,$(call int_decode,$(memo_counter)),3)
+$(call test_assert,$(call int_decode,$(md5_counter)),1)
+$(call test_assert,$(call memoize,md5,hello john),2d62190b10246ee2f2e233f9df840445)
+$(call test_assert,$(call int_decode,$(md5_counter)),1)
+$(call test_assert,$(call memoize,md5,hello john how are you),fd9b9651aa9f92d3d6d15a60bf5ccf15)
+$(call test_assert,$(call int_decode,$(md5_counter)),2)
+$(call test_assert,$(call memoize,md5,hello john),2d62190b10246ee2f2e233f9df840445)
+$(call test_assert,$(call int_decode,$(md5_counter)),2)
+$(call test_assert,$(call memoize,md5,hello john how are you),fd9b9651aa9f92d3d6d15a60bf5ccf15)
+$(call test_assert,$(call int_decode,$(md5_counter)),2)
+$(call stop_test)
+
+$(call start_test,gmsl_compatible)
+$(call test_assert,$(call gmsl_compatible,$(gmsl_version)),$(true))
+$(call test_assert,$(call gmsl_compatible,0 9 0),$(true))
+$(call test_assert,$(call gmsl_compatible,0 0 1),$(true))
+$(call test_assert,$(call gmsl_compatible,0 0 0),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 8),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 8),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 10),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 11),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 12),$(true))
+$(call test_assert,$(call gmsl_compatible,1 0 13),$(true))
+$(call test_assert,$(call gmsl_compatible,1 1 0),$(true))
+$(call test_assert,$(call gmsl_compatible,1 1 1),$(true))
+$(call test_assert,$(call gmsl_compatible,1 1 2),$(true))
+$(call test_assert,$(call gmsl_compatible,1 1 3),$(true))
+$(call test_assert,$(call gmsl_compatible,1 1 4),)
+$(call test_assert,$(call gmsl_compatible,1 2 0),)
+$(call test_assert,$(call gmsl_compatible,2 0 0),)
+$(call stop_test)
+ \ No newline at end of file