"A jewel, a fully responsive package that meets the needs of the Test & Evaluation world." |

Windows can be used and assigned directly in SPL routines.

For example:

wtest() { w1 := gnorm(100, .01); w2 := integ(w1); } |

Sets W1 to a random series and W2 to integrate the contents of W1. This syntax is much simpler than using SETWFORM.

Note that:

W1 := gnorm(100, .01) |

assigns the formula and data to W1 whereas:

W1 = gnorm(100, .01) |

only assigns the data. The original Window formula is preserved.

Global hot variables can be assigned with the := operator.

htest(hvar) { a := integ(w1); if (argc > 0) { eval(sprintf("%s := deriv(a)")); } } htest("dadt") |

creates two hot variables, A and DADT. This syntax is a bit simpler than SETHOTVAR.

SPL supports C/C++ ternary conditionals. For example:

a = (b > 0) ? b : b*b; |

is equivalent to:

if (b > 0) { a = b; } else { a = b*b; } |

Optional user defined SPL error handlers are supported in the form:

func_error(errnum, errmes) |

where func is the original SPL function name, errnum is the supplied error number and errmes is the original error message. For example:

myfun(x) { return(x*x); } |

Running:

b = myfun(); |

terminates execution and displays an error message. Because execution was discontinued, variable b is not assigned. However, by specifying an error handler:

myfun_error(errnum, errmes) { message(sprintf("myfun error %d, %s",errnum,errmes)); return(-1); } |

Running:

b = myfun(); |

pops up the message:

myfun error -102, Line 3 x: Uninitialized Variable |

but variable b is now assigned the value -1, the result of the error handler. Of course, the error handler is not required to display or process the error number or message.

An error handler can also terminate execution simply by calling the ERROR function. For example:

myfun_error(errnum, errmes) { error(sprintf("myfun error %d, %s",errnum,errmes),1); } |

is identical to the default behavior except in this case the error message is displayed in a pop-up box.

The errnum and errmes input arguments are optional for an error handler.

SPL functions now accept a variable number of optional arguments. For example:

/* maximum of one or more inputs */ vmax(argv) { local i, s; /* 0 or 1 arg case */ if (argc < 2) { if (argc < 1) { s = max(); } else { s = max(getargv(1)); } } else { /* initialize */ s = max(getargv(1), getargv(2)); /* compare input args */ for (i = 3; i <= argc; i++) { s = max(s, getargv(i)); } } return(s); } vmax(1, 3, 2, -1, 0) |

returns 3.

The argv specifier indicates the SPL function accepts variable arguments and GETARGV(i) returns the ith argument.

An empty array can be created with the {} assignment.

x = {} |

defines a variable x that is an empty series.

The == operator performs case sensitive string comparison

For example:

a = "foo" b = "foo" c = "Foo" a == b returns 1 a == c returns 0 |

Strings variables can be addressed with the [] syntax. For example:

a = "Larry"; a[1] = 'H'; |

Variable a contains the string Harry.

SPL routines can now designate variables as global with the extern keyword. For example:

myfun(x) { extern var; var = x * x; } |

sets the global variable var to the square of the input. This is equivalent to:

myfun(x) { setvar("var", x * x); } |

The extern keyword can also be specified as: global, e.g.

myfun(x) { global var; var = x * x; } |

Any SPL function can automatically iterate through each column of an input array, one by one, by specifying the ITERATE modifier.

Consider two similar SPL routines:

scale1(x) { y = x / min(x); return(y); } ITERATE scale2(x) { y = x / min(x); return(y); } A = {{1, 2, 3}, {2, 4, 6}, {3, 6, 9}} y1 = scale1(A); y2 = scale2(A); y1 == {{1, 2, 3}, {2, 4, 6}, {3, 6, 9}} y2 == {{1, 1, 1}, {2, 2, 2}, {3, 3, 3}} |

scale1 is defined as a standard SPL function and receives the entire array A as input. Consequently, the minimum is calculated as the minimum of the entire array, in this case 1.0.

Because scale2 is declared as an ITERATE function, it automatically iterates through each column of the input array one at a time. Since each iteration only receives a single column of the original input, the minimum is calculated as the minimum of the current column. Thus, scale1 behaves as:

x / min(x) |

but scale2 behaves as:

x / colmin(x) |

The ITERATE keyword makes it very easy to write a routine to handle each column of an array in the same manner without having to get bogged down in the details of multi-column operations. Simply write the function as if it will only receive a single column input and DADiSP will automatically apply the function to each column of the input array and accumulate the results.

The iteration behavior of an SPL function can be temporarily set or cleared at run time with the ITERATE function. For example:

iterate("scale1", 1) |

or

iterate scale1 |

converts scale1 to an iterator function such that it operates identically to scale2. This behavior persists only for the duration of the current session. Use the ITERATE modifier in the function definition to make the behavior permanent.

The SERIES modifier forces an SPL routine to return a series. Consider two similar SPL routines:

smax1(s) { return(max(s)); } SERIES smax2(s) { return(max(s)); } a = {{1, 2, 3}, {2, 4, 6}, {3, 6, 9}} m1 = smax1(a); m2 = smax2(a); m1 == 9 m2 == {9} |

Since the input is an array, smax1 returns the maximum of the entire array as a scalar, but smax2 returns the maximum as a series. This is equivalent to:

{max(s)} |

The SERIES modifier forces an SPL routine to always return a series. This behavior optimizes evaluation since the return type is already known before the calculation takes place. The optimizations are similar to what occurs with internal functions that always return a series such as FFT or INTEG.

The series mode of an SPL function can be temporarily set or cleared at run time with the SERIES function. For example:

series("smax1", 1) |

or

series smax1 |

converts smax1 to a function that always returns a series. This behavior persists only for the duration of the current session. Use the SERIES modifier in the function definition to make the behavior permanent.

If A is an array, A[end] returns the last element. For example:

a = 1..100 b = a[end] b == 100 |

The statement:

a = b @@ c |

is more compact and equivalent to:

a = concat(b, c) |

The statement:

a @= b |

is equivalent to:

append(a, b) |

The @= operator appends the series B to the end of series A in place. a @= b is much faster than a = a @@ b for large series because @= operates on the existing series whereas @@ creates and assigns a new series.

The for and loop functions have been optimized for more efficient loop variable access, resulting in approximately 30% faster execution.

The ++, --, +=, -=, *=, /= operators have been optimized by using inplace series and variable assignments. These operators now execute directly on the target argument, avoiding duplicate series and variable creation.

The SPL include statement

#include mymac.mac |

automatically processes mymac.mac as a macro file. This is useful if you wish to specifically include legacy .MAC files into your SPL code.

The startindex() function sets the start index of an array for the current SPL function. For example:

func1(a) { local i, b; startindex(0); b = 0; for (i = 0; i < length(a); i++) { b += a[i] * i; } return(b); } |

The startindex is automatically reset to the configuration parameter SPL_START_INDEX (default 1) when the function exits. The startindex function only effects the current SPL routine, routines called by the current function are not effected. Startindex makes it easier to develop routines where a starting index other than 1 is more natural for the algorithm.