list of numbers
From Wiki
Numbers are a means to add some programmatic functionality to Sword of Moonlight via calculator like mathematical expressions. Numbers are typically setup inside of an Ex.ini file. This list contains built in and so-called system numbers only.
Preface |
---|
This page is not and should not be a definitive document describing the programming language and environment represented here. Yet still as with any programming methodology familiar words take on new and peculiar meanings. So here, in what would otherwise be a big empty space to the side of the Table of Contents, is a hopeful attempt to develop a formal nomenclature that over time will hopefully be of use to students and wiki editors alike.
But first, background. This technology was originally and still is developed as a method to input mathematical expressions or formulas into configuration files that would otherwise only be able to receive fixed numerical values. Its audience is the lay public, non-programmers, or more specifically anyone who ever came to rely upon a calculator to pass their high school math exams. It never was a programming language per se. In fact it was designed very consciously to restrict the realm of possibilities available to its would be programmers. Still basic calculator style mathematical notation shares enough in common with basic full featured programming languages to present with it most if not all of programmings core challenges and complexities. As not really being a proper programming language, there never actually was a proper name, much less a short and catchy one. Retrospectively the most historical name has to be Excalculator. It is after all encapsulated within the EX::Calculator C++ class, as part of the Ex framework, engineered to add programming features to the Ex.ini configuration files, to be featured most prominently by the Exselector graphical configurator—until that is its spelling changed to Exselector and the bottom fell out of "Excalculator". Currently Exor is considered the best computer language name and Exreckoner is thought to be a good name for a calculator tool and in-game counterpart to the Exselector and Excalibur if needed. Speaking of nomenclature. There are two fundamental building blocks to keep in mind. They can be easy to remember, because they appear very similar side by side. One we call "Sums" and the the others are "Sets". Each are described methodically in detail below: A Sum (singular; sounds like SOM) is a slot, or what is called an item within the language machinery's source code, within a Number, capital N, known as a table by the source code, that is indexed by a Subscript, and represents both sides of an equation, where the side to the left of the equal sign (we don't solve algebraic equations) is indicated by its Number and Subscript, and the side to the right of the equal sign is a mathematical expression, which can include input parameters, what programmers would call arguments, in order to express a mathematical function. A Set is a more advanced feature (in fact it is yet to be adopted or implemented) that is analogous to a Sum. In fact sets share the namespace with sums, so that a set is assigned to a sum. A Set is a set of Sums and or other Sets and or nothing. Technically a Set is a multiset of links, or what programmers call references, to sums and by extension other sets. Because sets can contain within them even more sets, sets can be arranged into tree like structures, and have been designed to facilitate many common patterns familiar to programming languages of all stripes. Still even with sets "Ex More specifically its job is to spit out numbers, little n this time. We also say Values to refer to intermediate numbers. Where Numbers refer to names, or what programmers would call identifiers, or an algebra text book might refer to as "variables". Just for the record we don't say variable to refer to named numbers. We take the programming convention of referring to figures as being either variable or constant. A constant figure does not change for any reason ever. Whereas a variable may change, could change, for all we know will change, and probably just changed. In other words, a variable is an unknown. This can have implications. Not limited to optimizations, wherein many constants values are collapsed into a single value. If an expression contains a variable, then that part of the expression will prevent a complete collapse. You'll have to settle at the very least for a variable part, and a constant part, or just the variable part if it is a pure variable or the constant part happens to collapse to a 0 additive term. |
Sword of Moonlight
The numbers in this section are peculiar to Sword of Moonlight.
Variables
Variable numbers can change. Normally it is assumed that they are always in flux. The introduction of a variable short-circuits all operations that depend upon the variable such that normal operations involving variables always reconsider the value of the variables involved.
c
c yields the first of a game's counters. In a classical game of Sword of Moonlight there are 1024 counters. The word counter is translated from the Katakana-English loan word used by Sword of Moonlight itself.
A label can be applied to each counter. These labels are part of the game's Sys.dat file. If the counter's label is not blank it is applied to c.
pc
pc is not always available in all contexts. By default it describes the player character. In the context of combat care should be taken to the meaning of pc, as its role can shift. In the context of an attack, pc is the attacker. In the context of defense, pc is the defender. The attacker or defender may be the player character, or a non player character.
Since 1.2.1.12 the first subscript of pc is a character identifier. SomEx translates this two-part character class and SOM_PRM item number by extension of character_identifier.
+pc[12] is added by SomEx 1.2.0.10. 4 through 11 were supposed to be previously available, however a programming error had limited the selection to 0 through 2. 3 (Speed) is reserved.
+pc[16] is moved by SomEx 1.2.1.12 from pc[0] to pc[16] so that a character identifier can use pc[0] because it can be written optionally without the subscript.
sum | equals | value |
---|---|---|
pc+ | = | character ID |
pc[1] | = | 1st stat. aka. Strength |
pc[2] | = | 2nd stat. aka. Magic |
pc[3] | = | 3rd stat. aka. Speed |
pc[4] | = | weapon ID |
pc[5] | = | headgear ID |
sum | equals | value |
---|---|---|
pc[6] | = | body armor ID |
pc[7] | = | gloves ID |
pc[8] | = | leggings ID |
pc[9] | = | shield ID |
pc[10] | = | accessory ID |
pc[11] | = | magic ID |
sum | equals | value+ + |
---|---|---|
pc[12] | = | dash phase (0~1) |
pc[13] | = | X coordinate |
pc[14] | = | Y coordinate |
pc[15] | = | elevation (Z) |
pc[16] | = | scale of character |
npc
npc reflects the character opposite of #pc. So in a context where pc is an attacker, npc is the character being attacked, and so on. It is identical to pc in every way, except that it refers to a different character in most circumstances. (It is possible for a character to injure themself, if not "attack" themself.)
Until 1.2.1.12 this number was #nan although its name/future purpose were described by this document.
Constants
Constant numbers do not change. When a constant operates on a constant the result is another constant. Normally the original constants are discarded so that it is not necessary to perform constant-constant operations more than one time.
Legend | |
---|---|
SOM_SYS | settings part of SOM_SYS (SOM_SYS.exe) usually kept in the Sys.dat file |
m/s | the constants units are meters per second |
c/s | the constants units are radians per second Note that constants based on figures originally in degrees are converted to radians. |
- _WALK (SOM_SYS; m/s)
- the player character walking speed.
- _DASH (SOM_SYS; m/s)
- the player character dashing speed.
- _TURN (SOM_SYS; c/s)
- the player character turning speed.
- _MODE (SomEx 1.2.1.12)
- This integer is increased when built-in numbers are added or changed.
Modes Accounting 0 1.2.1.12 changes #pc and enables #npc; 10 trig functions; Adds #_MODE. #nan in earlier versions
Serialization
It is possible to write a series of values. Care must be taken to not do so accidentally. The following table demonstrates the rules that are to be observed. However the basics are really simple: If a mathematical operator has a space on one side, then it must have a space on its other side, and vice versa. In other words, 1 + 2 or 1+2. If a positive (+) or negative (-) sign appears in front of a number it must be flush with the number, or -1, not - 1.
input | produces | output |
---|---|---|
1 2 | => | 1, 2 |
2 +3 | => | 2, 3 |
3 + 4 | => | 3+4 |
4 + -5 | => | 4-5 |
input | produces | output |
---|---|---|
5 +-6 | => | 5, -6 |
-6+7 | => | -6+7 |
-7--8 | => | -7+8 |
-8 --9 | => | -8, 9 |
If you experience different results then that is a software bug that should not be relied upon in order to ensure you expressions continue to work correctly in the future.
Tip: where there is no empty space an expression cannot be interpreted as a series. 2(X) is 2*X, (X)(Y) is X*Y, and Y(2)X is X[Y(2)] and 2XY is XY[2]. This all works because plain numbers when written as a function behave as if multiplied. The rest is explained by #subscripts.
Space and separators
Spacing characters include space ( ), tab, carriage-return, new-line, and any other character that produces empty space wherever it appears. Note that tab and other kinds of characters sometimes appear as zero width spaces. This can be a nuisance as they will nonetheless be interpreted as space.
End of the line characters (carriage-return and new-line) are ignored along with any spacing characters that follow. When working with multiple lines care must be taken to observe spacing rules precisely. When a series is spread over multiple lines an explicit separator (#,;) should be placed between the last element on the line and the first element on the next line. A semicolon (;) at the end of the line is recommended for this.
Like with HTML (except for following the end of a line where empty space is ignored) one space, or two spaces, or any number of spacing characters (and or separators) is collapsed into a single space for purposes of serialization.
,;
The colon (,) and semicolon (;) are reserved separators. They are strictly equivalent to a space ( ) character, except you can see them as written. In other words 1 2 or 1,2 or 1;2 it doesn't matter. If necessary more separators can be added or an extension can be provided to allow for the definition of custom separators.
Note that separators are simply filtered out. So if you write 1,+,2 this is equivalent to 1+2. Care should still be taken to observe the rules of serialization and cautions about working with multiple lines in the previous section.
%
A percent sign (%) can appear as the first symbol in an assignment or after a separator in or order to suppress default parameter resolution. Graphically the slash means no as in "no smoking" and the two circles represent the original parameter and its default parameter. So altogether % can be read: "no two parameters". Of course, everything defaults to #nan. Refer to #_.
Tip: It's a good practice to highlight all default parameters in this way. Even when parameters are not present.
Subscripts and labels
A sum can be written with a subscript. When not so, the subscript is 0. Subscripts must be positive whole numbers no more than 65534. Mathematical symbols (eg. +) cannot be written with subscripts. Technically this is because of #placeholders. But in general it would be bad form to do so.
Without introducing extended Unicode characters a subscript can be written the following ways:
notation | interpretation |
---|---|
A[0] | 0th subscript of A |
B | 0th subscript of B |
C[1] | 1st subscript of C |
1D | 1st subscript of D |
E[1][1] | 2nd subscript of E |
F[1+1] | 2nd subscript of F |
notation | interpretation |
---|---|
1G[2] | 3rd subscript of G |
f(X)H | "f of Xth" subscript of H |
f(X)I[+1] | "f of X plus 1th" subscript of I |
f(X)1J | incompatible (#nan) |
K[-1] | not allowed (nan) |
L[2][-1] | still not allowed |
Subscripts cannot be less than zero (0) or negative. Fractional numbers are rounded down to the nearest integer. Sets of brackets can be chained together—in addition to a subscript written without brackets to the front of its number—so that each successive subscript is added to the sum of previous subscripts in the chain, so that X[1][+2] becomes X[3].
[]
Square brackets ([]) surround labels and are another way to write subscripts. Square brackets always appear directly adjacent to the right side of a named number. Labels can only be written inside of square brackets flush to the brackets on both ends. Labels are not required to be unique. When combining subscripts, label resolution (the search for the label) begins at the previously indicated subscript, or 0 if a label appears as the first subscript.
A label must be able to be converted into a constant subscript. This is by design. Labels in other words are merely a more literate way to write a literal subscript. A subscript is "constant", or invariable, if under no circumstances does it ever change. A function's local input parameters are always assumed to be inconstant, or variable, but this only holds true within the function itself. From outside the function, the parameters have definite values, and therefore the result, or yield, of the function is constant insofar as its internal parameters are also constant. In other words, even if it just so happens that a parameter of a function is only ever one value, that value is still held to be inconstant. Variables include #r and #Variables.
Note: many labels can be applied to the same subscript. It is not possible to remove a label once applied. Many subscripts with the same label can coexist by way of offset based label resolution described above. However brackets will be interpreted as a label whether the label appears before or after the indicated offset. Whether this behavior should be considered a defect or feature has not been decided; regardless this behavior is extremely unlikely to ever change.
Digitization
These numbers pack multiple smaller positive integer values into a single larger positive integer value.
Base encoding
The underlying representation of the so-called real and imaginary components of a complex number value is at least a 32bit floating point encoded real number with a 24bit mantissa. The facilities utilize only those 24 bits of a value's real component. A base 2 number requires 1 bit to represent 0 or 1. So 24 base 2 numbers can be represented by 24 bits. A base 4 number requires 2 bits to represent 0, 1, 2, or 3. So 12 base 4 numbers can be represented by 24 bits, and so on.
id
id is read ID or identify. It works with one of the following subscripts: 2, 4, 8, 16, 64, 256, and 4096. Each one indicates a different base numbering system that id is able to facilitate. So 2id or id[2] does base 2, or binary, encoding. For example, 2id(1,0) encodes the binary number 2. Where the input parameters are in the order of highest to lowest order digit, just as when writing the decimal number 10.
Consider the hexadecimal number 16id(9,15). Note that we do not write 16id(9,F) as hexadecimal is so often written. This is because id is not concerned with converting between numbering systems of different bases. It is only concerned with representing many numbers between 0 and the base minus 1. 15 in the case of hexadecimal. This is why only a select number of bases are supported. Namely bases that will divide evenly into 24 bits.
id[2] or 2id accepts 24 input parameters from 0 to 1. Anything outside this range or beyond 24 inputs yields #nan. 4id accepts 12 inputs from 0 to 3. 8id accepts 8 inputs from 0 to 7. 16id accepts 6 inputs from 0 to 15. 64id accepts 4 inputs from 0 to 63. 256id accepts 3 inputs from 0 to 255. And 4096id accepts 2 inputs from 0 to 4095.
Tip: if you absolutely need to it is possible to combine two or more different bases within the same number by way of addition: as long as you understand how many bits are required by each unit of each base, and have the unused overlapping units equal to 0. For example, 4id(2,0)+2id(1,0). 2 requires 1 bit, 4 requires 2 bits, 8 requires 3 bits, 16 requires 4 bits, 64 requires 6 bits, 256 requires 8 bits, and 4096 requires 12 bits.
SomEx 1.2.0.6 adds base 16777216 and will automatically deduce the base based on the number of inputs provided for 1, 2, 3, 4, 6, or 8 inputs, resulting in bases 16777216, 4096, 256, 64, 16, and 8, respectively.
Base decoding
Decoding is the reverse of encoding, or the extraction of a single digit from an encoded value.
di
di is read digit, or disassemble ID, or di as in however that is easiest to pronounce. Di can mean two, and DI is the mirror of ID. Two is particularly appropriate to di because it is peculiar in requiring two subscripts. One the base, and the other the position of the digit to be extracted from a number produced by #id. The subscripts are added together (as is the case with subscripts) however it is best to write them separately for sake of legibility and correctness. 2di[1](2id(1,0)) yields the second from lowest order digit or 1 in this example. Alternatively 2di[1] can be written 3di, di[3], di[2+1], di[2,1], di[2][1], just to name a few ways.
di supports only the bases supported by id. If a base is not supported, or the digit position is out of range, then di yields #nan. di can also accept a second input parameter specifying the position of the digit to extract. This is strictly speaking safer than using subscripts alone because an out of range position can overflow into a following base, or underflow into a previous base, and not be detected as a user error. When specifying the position this way the subscript must be one of the bases supported by #id or di yields nan.
Mathematics
With math it is possible to write a number as a mathematical expression with 0 or more input parameters. Refer to #_.
Calculations
A basic expression should appear familiar to everyone who has ever relied on a calculator to make it through a grade school math course. In fact it is never necessary to deviate from standard calculator notation. What follows is a refresher course along with an introduction to some quirkier ways to write expressions that can either be enlightening or treacherous, depending on how you look at it.
Grouping semantics allow expressions to contain subexpressions. Series within a subexpression are interpreted as input parameters. Refer to #() and #||.
An expression is made up of numerical terms held together by mathematical operators. A term can be a single number or a group of numbers. Operators resemble punctuation. For example: 1+2 includes two terms, 1 and 2, and one operator, +, or plus.
This distinction is stressed because each expression is a kind of computer program where each character in the program must be strictly interpreted. There is no tolerance for error, and in 9 out of 10 cases no red flags to be raised in case of error. Intolerance is a feature of all programming languages, and is nonnegotiable. Red flags on the other hand, can be supplied on demand (at the cost of alternative ways to write expressions.)
Numbers directly adjacent to (but not inside of) parentheses become a function of the inner terms of the parentheses, or the function's input parameters. Plain numbers, such as 1, 1.5, or (1.5) share a built in function: multiplication: however in keeping with arithmetic the priority, or precedence, assigned to this function is equivalent to regular multiplication (in other words it is as if an invisible #* is situated between the numbers.) Other numbers can only be written with the parentheses on their right. Refer to #() (when written on the left the result is a #subscript; not a function at all.)
While operators technically are functions, they are not usually written as such. However it is possible to do so. For example, 1+(2,3) adds together 1 2 and 3.
Wherever a term should logically appear but is omitted a 0 is provided. For instance. Operators always have a left term and a right term. And groups are not allowed to be empty. Consider -1+2. Which becomes (0-1)+2. Note that it does not become 0-1+2. The distinction can sometimes make a difference. Though less common, the same rule applies on the right: 1- becomes 1-0. Here parentheses are not needed. And last but not least: () becomes 0 (so consider that f() becomes f(0) where f is some mathematical function.)
Likewise terms and operators must always appear together. If an operator is omitted then a + is provided. So (1,2) becomes 0+(1,2) adding together 0 1 and 2 (and 1 becomes 0+1. Rules are rules.)
Arithmetic
Basic arithmetic includes the + - * and / operators used for addition, subtraction, multiplication, and division respectively. The operations are carried out from left to right, except that * and / have a higher what-is-called precedence, and are therefore always carried out first (barring grouping. Refer to #().)
these | equal | this |
---|---|---|
1+2 | = | 3 |
2-3 | = | -1 |
3*4 | = | 12 |
4/5 | = | 0.8 |
these | equal | this |
---|---|---|
1+2*3 | = | 7 |
(1+2)*3 | = | 9 |
3/0 | = | #inf |
0/0 | = | #nan |
When written with input parameters these operators can operate over more than two values. For example, (1,2)+(3,4,5) equals the sum of 1+2+3+4+5. The same is true for * - and / (for example (1,2)*(3,4,5) equals 1*2*3*4*5.)
Tip: note that +(3,4,5) equals 0+3+4+5. This is inconsequential, however *(3,4,5) may produce unexpected results since 0*3*4*5 is 0. As is *3 (on the other hand. Consider *[+1][+2][+3]+<your actual expression with #placeholders here>. Here we have three internal parameters front loaded with nearly unmistakable intent.)
()
Parentheses (()) are used in two ways. The first way is to group terms and operations together. The groupings can be nested, for example: (2*(3+4)), wherein the innermost groups are assigned greatest precedence. So that in this example 3 and 4 are added up first, and then multiplied by 2, even though 2 comes first and * normally takes precedence over +. Refer to Order of operations.
When parentheses appear after a sum the series bracketed by the parentheses become its input parameters. If the sum can be described as a function, it will know what to do with the input parameters and yield either a constant value unique to the parameters, or a variable value unique to itself in relation to the input parameters. Refer to Function.
An error is generated if ( is not matched by a closing ) or a ) is not matched by an opening (.
||
Enclosing an expression within two vertical bars (|) yields its absolute value. For example, |-7| makes 7. But you must beware that nesting without parentheses (#()) is not supported. Something like |c[|A|]| also works where square brackets are able to be used in place of parentheses.
In other words: |(|A|-|B|)| does work. While ||A|-|B|| does not work. Alternatively consider #abs(|A|-|B|).
An error is generated if | does not appear in pairs confined to a single group. In other words an opening | must be followed by a closing |.
abs
Like #|| abs yields the absolute value of its input parameter. If there is more than one input abs yields the absolute values of all of the inputs added together.
Decision trees
Sometimes we have to make choices. One way to do that is branching.
The only kind of built in branching avaliable is binary. Yes or no. This way or that way. And there is only one way to do it, two ways to write it: #if and #?:.
However. It is possible to string binary branches together. And if that doesn't cut it, while technically unrelated, if you think about it you can use a number with #subscripts to emulate something like a menu. For instance if K[0] is 2 and K[1] is 3, you can do something like K[and(X)] to choose between 2 and 3.
K[X] works too. However doing so risks going outside of the range [0,1]. If #n(K) is 2 and X is not 0 and X is not 1 then K[X] is #nan.
Tip: sometimes it is useful to think of nan as "none of the above".
?:
This is a ternary "inline-if" syntax that SomEx does not yet have an implementation for. It is to be functionally equivalent to #if below.
if
Note: that the current SomEx implementation of if is naive, in that both branch solutions are calculated before considering the branch condition. This implementation is therefore unsuited for recursive functionality. This implementation is simply expedient. A recursive implementation will be included as part of a future release. Especially where there is demand.
if requires exactly three inputs. The first is a branch condition. If the first input is greater than 0 with respect to #_E then if yields the third input. Otherwise if yields the second input.
Note: that the value of #nan while not 0 is neither greater than 0 as it is "not-a-number", therefore if(nan,A,B) yields B or the second input parameter.
Logical operations
The following functions along with #_E implement logical gates.
_E
_E is a very small positive fraction used to determine if a number is close enough to 0 to be considered a so-called Boolean false value equal to 0. If not 0 or false then the number is 1 or true.
Built in functions which rely upon _E first convert a value to its absolute value and then ask if that new value is greater than _E. If so the value becomes 1. If not the value becomes 0.
Note: that a #nan value is not greater than anything, therefore it becomes 0 in the above scenario.
and
and yields 1 if all of its input parameters are not 0 with respect to #_E. Otherwise and yields 0.
nand
nand yields 0 if all of its input parameters are not 0 with respect to #_E. Otherwise nand yields 1. In other words nand is the inverse of #and.
or
or yields 0 if all of its input parameters are 0 with respect to #_E. Otherwise or yields 1.
nor
nor yields 1 if all of its input parameters are 0 with respect to #_E. Otherwise nor yields 0. In other words nor is the inverse of #or.
xor
xor requires 2 or more input parameters. If the inputs are not equal with respect to #_E then xor yields 1. Otherwise xor yields 0.
If there are more than two inputs xor(A,B,C) is equivalent to xor(xor(A,B),C) and so on.
xnor
xnor requires 2 or more input parameters. If the inputs are not equal with respect to #_E then xnor yields 0. Otherwise xnor yields 1. In other words xnor is the inverse of #xor.
If there are more than two inputs xnor(A,B,C) is equivalent to xnor(xnor(A,B),C) and so on.
Complex numbers
Complex numbers have a so-called "real" part and a so-called "imaginary" part.
Tip: all values have the potential to be complex numbers. Consider the imaginary unit notation i: #pow(-1,0.5).
x
x yields the "real" part of its first input parameter.
y
y yields the "imaginary" part of its first input parameter as a "real" value. Compare to #iy.
iy
iy yields the "imaginary" part of its first input parameter as a purely "imaginary" value. Compare to #y.
Identity coalescence and inequality
These functions all work the same way. #nan and #inf are unique in yielding generally useful values that cannot be clearly expressed otherwise.
nan
nan yields the value NaN. This means not a not-a-number, though it is a number, just a number of indeterminate value. Operations that hold true for all numbers, such as multiplication by zero, may hold true with a NaN value. Especially within the context of optimization.
nan(A) yields 1 if A is a NaN value, and 0 otherwise. nan(A,B,C,D,E,F) yields the first value A through F (and so on) that is not a NaN value. Or "F" if all values are NaN values. This is called coalescence and can be quite handy since NaN is the value of a sum that has never been assigned a value.
#_ without a subscript is always a NaN value. It's shorter than writing nan. Well in case you ever encounter that, that's what it is doing.
inf
inf yields the value positive infinity (∞). Many operations do not make sense within the context of infinity. Infinity can also be negative, ie. -inf.
inf(A) yields 1 if A is not finite, and 0 otherwise. inf(A,B,C,D,E,F) and so on coalesces around the first value that is not infinite. Refer to #nan for an explanation of coalescence.
not
not yields the value 0 (just write 0 instead.)
not(A) yields 1 if A if the absolute value of A is less than #_E, and 0 otherwise. _E is expected to be very close to 0, but not quite. Electronics cannot be infinitely precise.
not(A,B,C,D,E,F) and so on coalesces around the first value that is not within _E of 0. Refer to #nan for an explanation of coalescence.
Tip: subtract (-) two numbers and use not to find out if they are equal or not.
neg
neg yields the value negative #_E. Or -_E.
neg(A) yields 1 if A not equal to or greater than 0, where 0 is defined with respect to _E. To check for exactly less than 0 you could do something like neg(A-neg) to remove the bias of _E.
neg(A,B,C,D,E,F) and so on coalesces around the first value that is equal to or greater than 0 with respect to _E. Refer to #nan for an explanation of coalescence.
Note: that comparing complex numbers for inequality on a positive and negative number line does not make sense. To neg a complex number is not negative. Refer to #x and #y (technically a complex number is NaN to neg. And neither can NaN be said to be negative. Refer to #nan.)
Tip: in theory it should be possible to do any kind of inequality calculus with neg and some strategically placed minus (-) signs.
Reflection and recursion
Numbers in this section are somewhat "meta". Only #_ is strictly necessary too basic calculation. It is used to evaluate a function's local input parameters. Note that _ cannot be directly linked to in this document. #underscore can be used instead.
_
Note: just to come right out and say it. If a number is to be a mathematical function, then it must interact with _.
_ is a variable that changes from assignment to assignment. The 1st subscript (1) is the first of the current input parameters into a function. The 2nd subscript (2) is the second parameter. And so on. The 0th subscript (0) would be the function in question. It is always equal to #nan (because a "stateless" function with equivalent input parameters will always be equal to itself. Although not recommended it (_) can reliably be used as an ever present shorthand for nan.) Refer to #_S.
Tip: #n(1_) yields the number of non-default input parameters.
_S
_S is a shorthand way to refer to numbers in a series within a serial assignment. So _S[0] is the first assignment of the series, _S[1] the second, and so on.
The #subscript component of _S must be a single plain number less than or equal to the number of #separators to the left of where _S appears within a serial assignment. So 1 _S[0] is fine, whereas _S[1] 0 is not.
#_N or #n(_S) yields undefined behavior. In other words. They are incompatible.
_$
_$ works just like _S except it refers to the previous assignment of the subscript while it is being assigned. So if X is 2 (or an unknown value) and you want to double X, it is possible to write X = 2*_$. Where X = 2*X would be X = 2*(2*(2*(2*(2*... or an infinite recursion resulting in undefined error behavior.
The [[#subscript] of _$ must be a single plain number can only appear within the same subscript that is being assigned to. So that X = 0, _$[1] is fine, whereas X = 0, _$[0] is not.
#_N or #n(_$) yields undefined behavior. In other words. They are incompatible.
Note: assignments are normally evaluated out of sequence. However _$ introduces a sequence point, essentially flushing all of the assignments required by _$. It isn't recommended but if you must lock a number in place reassigning it to _$ does the trick.
_N
_N(X) yields the subscript of X. In this case 0. _N(X[2]) yields 2. _N is provided if for no other reason so that you can subtract from the subscript of a label like so: X[_N(X[label])-1] whereas X[label][-1] will not work because subscripts cannot be less than 0. _N complements #n.
_N is incompatible with #_S and #_$.
Statistical analysis
These are bound to come in handy at some point. Mind #r. Technically it is a #Variable.
n
Despite appearances, n is a special function. Its single input parameter is a sum. Its yield is the count of sums with subscripts greater to and equal to its input parameter. Its count includes unassigned sums. So if #c holds 1024 sums, then n(c) yields 1024, and n(c[1]) yields 1023. Furthermore n(c[1024]) yields 0, and n(c[1025]) yields #nan.
n works with input parameters as well. If #_ is the input parameters of a function, then n(_[1]) yields the number of input parameters. Default parameters do not count. n complements #_N.
n is incompatible with #_S and #_$.
int
int(A) yields the whole number part of the mixed fraction A. For complex numbers int(A) is equivalent to int(x(A))+int(iy(A)).
Ceiling and floor variations are not provided. If there is demand they are to be enabled by extension and made available via int[ceiling] and int[floor] respectively.
Tip: a modulo function can be written A-B*int(A/B).
min
min yields the smallest possible positive value that is not zero. min(A,B,C) yields the lowest value among A, B, and C. Complex numbers are ineligible because they cannot be place on the positive and negative number line. Complex numbers are converted to #nan and therefore if all input parameters are complex min yields nan.
max
max yields the largest possible positive value that is not infinity. max(A,B,C) yields the largest value among A, B, and C. Complex numbers are ineligible because they cannot be place on the positive and negative number line. Complex numbers are converted to #nan and therefore if all input parameters are complex max yields nan.
r
Despite appearances, r is not a function at all, but a pseudo number containing random numbers. r[2] is a random number between 0 and 1. r[1] is 0. Subscripts cannot be larger than 65534. A larger subscript yields #nan. r[0] is an unconstrained positive whole number. The default random number generator for r without input parameters is the C programming language's rand function. This function never actually produces numbers larger than 32767. There's no such thing as a random number. Most functions demonstrate obvious biases. Input parameters are ignored by all current versions of r.
Note: r never contains the same number unless by coincidence. To make use of a random number that does not change consider using an in-game event to record a random number into one of Sword of Moonlight's counters. Refer to #c.
Transcendental functions
These functions are said to transcend algebra. In other words you can't do them with algebra.
^
A^B is functionally equivalent to #pow(A,B). However ^ is an operator. Refer to the following notes.
Note: that as an operator ^ has lower precedence than single operand operator prefixes, for example, -2^3 yields pow(-2,3) instead of -pow(2,3). Conventions can vary here. Compare with 2-3^4. Since - is no longer a prefix, it now has lower precedence than ^. So 2-2^2 yields 2-pow(2,2) instead of pow(2-2,2). To avoid confusion use pow or place prefixes within #(). Eg. (-2)^3. Otherwise ^ enjoys a higher precedence than the #Arithmetic operators.
cos
cos(X) yields the trigonometric cosine function of X.
exp
exp(X) yields e to the power of X. Or #pow(2.718281,x) where 2.718281 approximates the natural base e. Note that e is not a built in number.
log
log(X) yields the logarithm of X to the natural base e approximated to 2.718281. log(X,Y) yields the logarithm of X to the base Y. Note that e is not a built in number.
pow
pow(X,Y,Z) yields the exponentiation of X to the power of Y, then that to the power of Z, and so on. The first two input parameters are required.
Tip: use fractional exponents to find the Nth root. For example, the square root of 2 is pow(2,1/2), the cubed root is pow(2,1/3), and so on.
sin
sin(X) yields the trigonometric sine function of X.
tan
tan(X) yields the trigonometric tangent function of X.
Inverse trigonometric functions
Wikipedia has an entry on deriving the inverse functions (asin, acos, atan & atan2) with #log. These functions are available if #_MODE is 0. The complex forms are implemented in a way described as "naive."[1]
atan2
Atan2 is a specialized function. It receives two parameters and only operates over their real-components. As a result, the resulting value is a real number.
Hyperbolic functions
Hyperbolic functions (sinh, cosh, tanh, asinh, acosh, atanh) are available if #_MODE is 0. The complex forms are implemented in a way described as "naive."[2] The non-complex forms of the inverse functions are derived from this Wikipedia entry because older Microsoft math libraries don't have them.
Sets
Note: the language of this proposal has not been updated to reflect the #Preface.
Sets are a proposed feature. Implementation (and finalization) is to be delayed until thought necessary.
Proposal
A set here is not a set of number values. It is a multiset of named number and subscript pair references referred to as elements below. If a plain number is added to a set it is said to be anonymous. The individual numbers of a set are accessed by ordinal with #el.
Sets and #subscripts are incompatible. In other words, el(A,1)[2] does not work (where el(A[2],1) does—just to be 100% clear—but means something completely different beside the point. Bottom line, subscripts are for numbers. Also note that el(A,1)[2] does not produce an error as [2] is interpreted as a #placeholder.)
Where a set is operated upon like a number it is equivalent to its 0 ordinal element. And if that element is itself a set, the 0 ordinal element of that set. And so on.
Tip: a named set B equal to {A} becomes the functional equivalent of A. Although references to A remain distinct from references to B (consider #no(C,{A}) where C contains references to B but not A. Note that neither does this extend to subscripts. Therefore B[2] is not by extension functionally equivalent to A[2].)
Elements default to a single element set containing #nan. It is explained below that this can be written {nan}. Note that nan is not a value. But yields the value NaN when expressed without input parameters.
{:}
An empty set is written as {}. A two element set is written as {A,B} or {A B}. Here A is assigned ordinal 0, and B is assigned ordinal 1. Ordinals can be skipped by writing {A,2:B}. Here A is again assigned ordinal 0, and B is instead assigned ordinal 2. Ordinals must be positive whole numbers.
If A is itself a set then this must be acknowledged by writing {A} or {{A},B} or else A will be taken as the 0 ordinal element of set A. This does not introduce an intermediary set. Too add an anonymous set write {{A,1,2},B} or {{{A},1,2},B} depending on whether element A is to be a set or number reference.
el
el yields undefined behavior. el(A) yields A as a set, or {A}. el(A,1) yields the ordinal 1 element of the set A as a set. el(A,1,2) yields the ordinal 2 element of the ordinal 1 set of set A.
no
no yields undefined behavior. no(A) yields one more than the greatest ordinal of set A, or 0 if A is the empty set {}, or 1 if A is in fact not a set.
no(A,1) yields the number of elements equal to the number 1 with respect to #_E. no(A,1,2) yields the number of elements between the range of 1 and 2. no(A,1,2,0) yields the number of elements between 1 and 2 with a precision of 0, or 0 is used in place of _E.
no(A,{B}) yields the number of elements of set A that reference B. no(A,{B,C}) is equivalent to no(A,{B})+no(A,{C}).
Tip: no(A)-no(A,nan) yields the number of non-NaN elements in set A. But this looks for the value NaN. Since ordinals default to {nan} you may want to look for no(A,{nan}) instead.