# Chapter 9 - Arrays and Matrices

An array is a data structure that allows you to group several numeric or string variables under a single name. It may be one-dimensional list or vector or a two-dimensional table or matrix, or it may have several dimensions. An array may contain either string or numeric values, but a given array may not contain both types of values.

Arrays are an extremely powerful tool for organizing the data used by your program. This chapter introduces the basics of arrays and matrices, as well as several advanced topics, to help you use these powerful data structures efficiently and effectively.

Array Basics
Often, your programs will use a large number of logically related values. Consider, for example, a program to work with a teacher’s grade book for a class of 15 students over the course of a semester.

Such a program would need to manipulate several values, including the names of the students and their grades. Using simple variables, sometimes called scalars, the program would need 15 variables to store the names. If there are ten grades per semester, then the program would need another 150 simple variables to store the grades. As you can imag- ine, building such a program using simple variables would be a real headache.

Fortunately, True BASIC offers arrays for the temporary storage and manipulation of related values. An array is a named collection of numeric or string values. You can think of an array as a group of variables with the same name. For instance, the grade-book program might use two arrays — one named names\$ containing the student names and another named grades containing their grades.
————————————————————————–––———————————————
[ ! ] Note: Like any variable, values stored in an array remain there only during the program run. You
must supply the values to your program as some form of input — from the keyboard, from DATA state-
ments, or from a file. If you wish to preserve array data for future use, you should have the program write the array contents to a file. Using arrays along with data files gives you a powerful tool for manip- ulating large amounts of data. This chapter gets you started with arrays; see Chapter 13 “Files for Data Input and Output” for more information on using data files.
————————————————————————–––——————————————— The individual variables contained in an array are called its elements. All of the elements of a given array must be of the same type — either numbers or strings. Thus, arrays are often referred to as numeric arrays or string arrays, depending upon the values they can contain. You may use any valid numeric variable name for a numeric array, as in grades, or any valid string variable name for a string array, as in names\$. However, you may not use the same name for both a simple variable and an array.

Before you can use an array, you must define it in a DIM (dimension) statement. A DIM statement tells True BASIC that you will be using the specified name as an array variable. The form of the name tells True BASIC whether you will be using the array to store string or numeric values, and what follows specifies the number of dimensions and the size of each dimension. For example:
defines the string array names\$ as a list of 15 elements. Each element in names\$ may be treated as a separate string value. It also defines the numeric matrix grades as a table with 15 rows (in the first dimension) and 10

columns (in the second dimension). The array dimensions specified in a DIM statement must be numeric con- stants, not variables or expressions.
Each element in an array is assigned a number called a subscript. Unless you specify otherwise, these numbers begin at 1. For instance, the fifteen items in the names\$ array would be numbered 1 through 15. Likewise, the fif- teen rows in the grades matrix would be numbered 1 through 15, and the 10 columns would be numbered 1 through 10.
You use subscripts to refer to specific elements within an array. You specify the subscript in parentheses after the array name. For instance, names\$(5) represents the fifth item in the names\$ array. With multi-dimensional arrays, you must specify a subscript for each dimension. For instance, grades(5,3) represents the item in the third column of the fifth row of the grades matrix. You may use any valid numeric expression as a subscript; if the value of the subscript is not an integer, True BASIC will round it to the nearest integer.
When you use a subscript to refer to a specific element of an array, you may use that array element as you would a simple variable of the same type:
LET score(i) = i * x
LET cost(n) = october(n,3) * d(3) PRINT name\$(7), age(k)
To better understand the use of arrays, let’s consider an example. The following program sets up a multiplication table and uses it:
DIM product(10,10)

FOR i = 1 to 10 ! For each row
FOR j = 1 to 10 ! For each column in current row
LET product(i,j) = i*j
NEXT j
NEXT i
DO
INPUT PROMPT “Enter two integers to multiply; use 0,0 to end: “: a, b
IF a = 0 and b = 0 then EXIT DO
PRINT a; “*”; b; “=”; product(a,b)
LOOP
END
The two FOR loops assign values to each element of the two-dimensional array product. The value for each ele- ment equals its row subscript multiplied by its column subscript. The INPUT PROMPT statement gets two numeric values from the user. The program then uses those values as subscripts and prints the value of the cor- responding element in product — which is the product of the two subscripts.

If one of the input numbers (rounded if necessary) is less than 1 or greater than 10, then a “Subscript out of bounds” error results and the program stops. You can usually adapt your programs to avoid such errors. For example, you could modify this program by placing the PRINT statement in an IF structure:
DIM product(10,10) FOR i = 1 to 10
FOR j = 1 to 10
LET product (i,j) = i*j
NEXT j
NEXT i
DO
INPUT PROMPT “Enter two integers to multiply; use 0,0 to end: “: a, b
IF a = 0 and b = 0 then
EXIT DO
ELSEIF a < 1 or a > 10 or b < 1 or b > 10 then
PRINT “Please re-enter, using two numbers from 1 to 10”
ELSE
PRINT a; “*”; b; “=”; product(a,b)
END IF
LOOP END
There are other ways of preventing errors from stopping your program. For more information see Chapter 16 “Error Handling.”
The lowest valued subscript in each dimension is that dimension’s lower bound. The highest valued subscript in each dimension is that dimension’s upper bound. If a DIM statement specifies only one numeric constant per dimension, True BASIC uses that number as the dimension’s upper bound and assumes 1 as the lower bound. How- ever, you may also specify a dimension’s lower bound in a DIM statement. For example:
DIM profit(1980 to 1995), count(-10 to 10, 3)
defines the numeric array profit as having a lower bound of 1980 and an upper bound of 1995. Thus, profit has
16 elements, which you may refer to using the year as the subscript. In a similar fashion, this statement defines the numeric matrix count as having a lower bound of -10 and an upper bound of 10 in the first dimension and a lower bound of 1 and an upper bound of 3 in the second dimension. Thus, the matrix count has 63 elements divided into
21 rows of 3 columns each. Note that you may use a colon (:) in place of the word TO in array dimensions.

Once an array has been defined to have a certain number of dimensions, the number of dimensions cannot be changed. However, you can change the size of each dimension. Several ways of altering the size of a dimension are discussed in the sections that follow.

It is important to realize that the DIM statement reserves memory (RAM) for each array that it defines. Thus, you should avoid dimensioning arrays you don’t need or making arrays much larger than required. Even if you never use them, every element in every array contains a value (0 or the null string if you haven’t assigned a specific value) and thus takes up space in memory. The size of your arrays is limited only by the amount of memory avail- able; however, if you are not careful, you may find yourself running out of memory.

Memory considerations are especially important with multi-dimensional arrays. True BASIC lets you create arrays of many, many dimensions, but you should avoid them unless you have good reason to use them. Multi- dimensional arrays can use up memory very quickly. The number of elements in a multi-dimensional array is the product of the sizes of its dimensions. For example:
DIM big(20,20,20,20)
defines a four-dimensional numeric array big. Doing the math, we get 20 x 20 x 20 x 20 = 160,000 elements! Since each numeric value in True BASIC takes up eight bytes, the number of bytes of memory required for big equals (roughly) the number of elements times 8, or well over a megabyte of memory. Although it might look relatively innocent, the array big could easily cause your program to run out of memory.

As a more extreme example, consider this: a 20-dimensional numeric array, even if each subscript could take on only two values, would require eight megabytes. The point is, quite simply, don’t add extra dimensions to an array unless they are absolutely necessary.

Array Input and Output
As the above examples demonstrate, you can treat an individual element of an array as you would treat a simple variable. If you wanted to do something to all the elements in an array, you could use a loop to repeat the same operation for each element:
DIM names\$(15) FOR i = 1 to 15
INPUT name\$(i) NEXT i
However, you will often find it easier to perform operations on an entire array at once. True BASIC provides sev- eral specialized MAT statements that allow you to do exactly that. The simplest examples of MAT statements are the MAT READ, MAT INPUT, and MAT PRINT statements. The first of these reads all the elements of the array from DATA statements, the second gets the values for the elements from the user, and the last displays all of the values in the array.

The MAT READ statement allows your program to read the values for one or more arrays from the current data pool of items in DATA statements. It works very much like the READ statement except that it reads values into an entire array. For multi-dimensional arrays it uses odometer order, that is, the last subscript runs through its range, then the next subscript (to the left of the last) is increased by one, etc. For a two-dimensional array this means that it reads the first row, then the second row, etc. For example:
DIM day\$(7), pay(3,2) MAT READ day\$, pay
DATA Monday, Tuesday, Wednesday, Thursday
DATA Friday, Saturday, Sunday
DATA 5.25, 7, 3.75, 12.10, 4.15, 5.35
The array day\$ will contain the days of the week. The matrix pay is 3 rows by 2 columns, hence:
pay(1,1)=5.25 pay(1,2)=7 pay(2,1)=3.75 pay(2,2)=12.10 pay(3,1)=4.15 pay(3,2)=5.35
The MAT INPUT statement works as does an INPUT statement with multiple input items. The MAT INPUT statement expects to receive all the elements of the array in the same order as they would be provided for the MAT READ statement. The user must enter the correct number of items, in the correct order, separated by commas. If the user enters too few or too many items, they will be asked to re-enter the data. The user can end a line with a comma if more space is needed to enter all the input.

Like the INPUT statement, the MAT INPUT statement displays a question mark as its default prompt. You can overrule the question mark by using the MAT INPUT PROMPT statement. For example,
DIM list(7)
MAT INPUT PROMPT “Type 7 numbers: “: list
The LINE INPUT statement also has an equivalent MAT statement:
DIM text\$(15)
MAT LINE INPUT text\$
This code segment will prompt the user with question marks for 15 lines of input. It will read each line as one ele- ment of the string array text\$.
The MAT PRINT statement displays the contents of an entire array. It prints elements in odometer order, begin- ning a new line after each row. It leaves a blank line after each array, or after two-dimensional sections of higher dimensional arrays. This makes it much easier to recognize the shape of the array. The normal convention is to print items in print zones, as if a comma had been used as the print separator between elements. As with the PRINT statement, the zone width is normally 16 characters, but you may reset it with a SET ZONEWIDTH state- ment. For example, consider two MAT PRINT statements added to the earlier sequence:
DIM day\$(7), pay(3,2) MAT READ day\$, pay
DATA Monday, Tuesday, Wednesday, Thursday
DATA Friday, Saturday, Sunday
DATA 5.25, 7, 3.75, 12.10, 4.15, 5.35
MAT PRINT pay
MAT PRINT day\$
END
This program produces the following output:
5.25 7
3.75 12.1
4.15 5.35

Monday Tuesday Wednesday Thursday Friday
Saturday Sunday
If you use a semicolon after the array name in a MAT PRINT statement, True BASIC will print the elements in a row close together, as if you had used a semicolon as the print separator between elements. For example:

MAT PRINT pay; day\$

will print the elements of the numeric array pay close together, and the elements of the string array day\$ in zones. Having no punctuation after the last array name has the same effect as having a comma after it. (Remem- ber that True BASIC prints a space before and after each positive numeric value.)
5.25 7
3.75 12.1
4.15 5.35

Monday Tuesday Wednesday Thursday Friday
Saturday Sunday
To dictate the format of the output, use a MAT PRINT USING statement. The MAT PRINT USING statement prints the individual elements of the array, formatted by the format string, just as if they were printed one by one with PRINT USING statements. If there are more elements than fields in the format string, the string is reused, starting from the beginning. For example:
LET format\$ = “##.### “
MAT PRINT USING format\$: pay
will print numbers rounded to three decimal places and leave three spaces after each number. In this example, two values are printed per line representing the two columns of each row in the table:
5.250 7.000
3.750 12.100
4.150 5.350
Keep in mind that you may sometimes want to use a loop rather than a MAT statement to input or print all the elements of an array. Consider the following simple version of a grade-book program that finds the average grade for each student:
FOR s = 1 to 6 ! For each student
NEXT g
NEXT s
MAT PRINT names\$, averages ! Print results
DATA J. Andersen, S. Bree, D. Cordoza, G. Davison, A. Ellis, M. Feinstein
DATA 90, 92, 96, 90, 88, 85, 78, 84, 83
DATA 77, 79, 81, 85, 89, 84, 85, 94, 86

GET KEY k ! Wait for keystroke
END
The MAT PRINT statement first prints all values of names\$ and then all values of averages, giving the fol- lowing output:
J. Andersen S. Bree D. Cordoza G. Davison A. Ellis
M. Feinstein

92.666667 87.666667 81.666667 79 86
88.333333
However, you might prefer the output you would get if you replace the statement:
MAT PRINT names\$, averages
with the loop:
FOR n = 1 to 6
PRINT names\$(n), averages(n) NEXT n

In this case, the corresponding elements from each array are printed together, as follows:
J. Andersen 92.666667
S. Bree 87.666667
D. Cordoza 81.666667
G. Davison 79
A. Ellis 86
M. Feinstein 88.333333
What if you find it easier to enter the data keeping student names and grades together, as follows?
DATA J. Andersen, 90, 92, 96
DATA S. Bree, 90, 88, 85
DATA D. Cordoza, 78, 84, 83
...
To do that you will need to replace
with a more complex, nested loop so that you read an entire row of grades for each element in names\$:
FOR n = 1 to 6 ! For each student
NEXT n
When you are deciding whether to use a MAT statement or an equivalent loop with multiple arrays, be sure you consider how the data will be organized as well as how you wish to write the program code.

Any of the MAT input and output statements may be used with a channel number to get input from a file or to print results to a printer or file. For example:
DIM names\$(15)
OPEN #1: name “StuNams” OPEN #10: printer
MAT LINE INPUT #1: names\$ ! Get names
MAT PRINT #10: names\$ ! Print to printer
....
Arrays used with data files are very helpful in managing and manipulating large sets of related data. Keep in mind, however, that data in a file must be in the correct order and format for the input statement you’ll use. If you print to a file and intend to input that data again later, you must print the data in an acceptable format. This issue is discussed in Chapter 12 “Files for Data Input and Output.”

Redimensioning Arrays
Unlike many other programming languages, True BASIC allows your program to change the number of elements in each dimension of an array while it is running. This process is known as redimensioning, although the word is somewhat misleading since the number of dimensions cannot be changed. The lower and upper bounds of each dimension, however, can be changed, and so the array may become larger or smaller. You can redimension arrays with the MAT REDIM statement or by specifying the new dimensions in certain MAT statements.

Here’s an example of the MAT REDIM statement:
DIM table(0 to 2, 5)
MAT REDIM table(4, -1 to 6)
The first statement creates the numeric array table with three rows numbered 0 to 2 and five columns num- bered 1 to 5. The MAT REDIM statement changes table so that it has four rows numbered 1 to 4 and eight columns from -1 to 6.

Redimensioning has many uses. A typical use is to have the program ask the user for a list and its size, as in the example below. This technique lets you use the same program for arrays with different number of elements, and it helps conserve memory by keeping arrays as small as possible.

DIM name\$(1)
INPUT PROMPT “How many names: “: n
MAT REDIM name\$(n)
MAT INPUT PROMPT “Enter them: “: name\$
...
You must still use the DIM statement to declare that name\$ is a one-dimensional string array, but the number of ele- ments you specify is irrelevant, since you will redimension it when you supply n in the MAT REDIM statement.

This combination of statements is so common that True BASIC lets you redimension the array directly in a MAT INPUT, MAT LINE INPUT, or MAT READ statement. You can combine the MAT REDIM and MAT INPUT statements above to form a single MAT INPUT statement that includes a variable for the dimension of name\$:
DIM name\$(1)
INPUT PROMPT “How many names: “: n
MAT INPUT PROMPT “Enter them: “: name\$(n)
...
Here is a similar example that reads an array from DATA statements:
DIM table(10,10)
READ m, n ! Actual size
DATA 3, 4
DATA 1,2,3,4,5,6,7,8,9,10,11,12
...
This example sets up the array, then reads the first two data items which are used to define the actual size of the table. With this trick, the program can work for tables of any size without your having to rewrite the program. You need to change only the data lists in the DATA statements.

The MAT assignment statement, described in the “Array Assignment” section below, can also redimension an array when you assign values to its elements.

Finally, there is a version of the MAT INPUT statement that allows you to input a one-dimensional array of an unspecified size:
MAT INPUT list(?)
The question mark in the statement instructs the program to accept a list of any length. True BASIC adjusts the upper bound of the subscript to make the size exactly right for the number of elements the user enters. The user must separate items with commas and end a line with no comma to indicate the end of the input items. Note that you can use the question mark (?) only with the MAT INPUT statement and only for one-dimensional arrays.

Redimensioning with the MAT READ or MAT INPUT statement changes both the shape and the contents of an array. MAT REDIM will preserve all or part of the contents of an array. While this may be useful, you should be careful in using it, as shown below.

Suppose that the 2-by-2 array sample contains the following values:
æ 1 2 ö è 3 4 ø
and you then use the statement:

MAT REDIM sample(3,2)
The array is now:
æ 1 2 ö ç 3 4 ÷ è 0 0 ø
as you would expect. But if you use the statement:
MAT REDIM sample(2,3)

the result would be:
æ 1 2 3 ö è 4 0 0 ø

which may not be what you want.

Two precautions will prevent this problem. If you wish to use the MAT REDIM statement and retain the previ- ous contents of the array, then
• do not change the lower bound(s), and
• redimension only one-dimensional arrays or only the first dimension of two-dimensional arrays.
The MAT REDIM statement can be extremely useful when you need to make the most of the memory available to your program. A dimension of 0 effectively removes the array from memory, thus freeing the space it occupied. Therefore, when you need as much memory as you can muster, redimension any unnecessary arrays to zero ele- ments in each dimension. Beware, however, that in doing so you will lose the contents of that array; use this tech- nique only when you no longer need the contents of an array.

Functions That Find Array Sizes
Along with letting you redimension arrays, True BASIC provides functions that let you find out the current sizes of arrays. Three functions may be used to discover the ranges of subscripts:

Subscript Range Functions
——————————————————————————————————————
Function Result
LBOUND(array,d) Lower bound of subscript in dimension d of array UBOUND(array,d) Upper bound of subscript in dimension d of array SIZE(array,d) Total number of elements in dimension d of array
——————————————————————————————————————

If the array has only one dimension, you may omit the second argument d in the argument list of the LBOUND and UBOUND functions. If you omit the second argument d in the argument list of the SIZE function, the func- tion returns the total number of elements in the entire array. For example, the following program inputs a list and prints it in reverse order:
DIM list(10)
PRINT “Enter a list of numbers: “
MAT INPUT list(?)
! Input any number of items & redimension
LET n = Size(list)
! How many numbers
FOR i = n to 1 step -1
! Reverse order
PRINT list(i); NEXT i
END
These array subscript functions are also useful for writing array-handling subroutines. The following code seg- ment searches the array name\$ for a particular name n\$:

LET u = Ubound(name\$)
FOR i = 1 to u
! i will be position in list
IF name\$(i) = n\$ then EXIT FOR NEXT i
IF i > u then LET i = 0

Array Assignment
As you have seen, loop structures provide a concise and convenient mechanism for processing the individual elements of an array in series. The following program segment that copies the contents of one array into another:
DIM source(0), target(0)
PRINT “Enter a list of numbers: “
MAT INPUT source(?)
! Input any number of items & redimension
LET n = Size(source)
! How many numbers entered MAT REDIM target(n)
! Redimension target array FOR i = 1 to n
! Copy source to target
LET target(i) = source(i) NEXT i
Just as the MAT READ, MAT INPUT, and MAT PRINT statements let you input or print an entire array instead of using a loop to operate on each element, the MAT statement lets you copy the entire contents of one array to another. The following equivalent of the previous example shows how it simplifies array operations:
DIM source(0), target(0)
PRINT “Enter a list of numbers: “
MAT INPUT source(?)
! Input any number of items & redimension
MAT target = source
! Copy source to target
The MAT statement assigns values to each element in an array. You can think of it as a specialized version of the
LET statement that operates exclusively on arrays.

When you assign the values in one array to another by using an array variable on the right side of the MAT state- ment, the two arrays must be of the same type (numeric or string) and must have the same number of dimensions. If necessary, True BASIC automatically adjusts the upper bounds of the array being assigned to. For example:
DIM growth(2,1991 to 1993), temp(1,1) MAT READ growth
DATA 25.6, 13.92, 15.2, 29.89, 12.64, 28.01
MAT temp = growth
Here the array temp takes on the values of the array growth, and its upper bounds are adjusted to the proper size. Note that the lower bounds are not changed. Thus, the new dimensions for temp are (2,3) after the assign- ment. To avoid unexpected results, you may wish to specifically dimension arrays used in assignments to have the same lower bounds.

While the MAT statement is commonly used to assign the contents of one array to another, you can also use it to assign the same value to each element of an array. If the value to the right of the equal sign is a constant, expres- sion, or variable representing a single value, that single value is assigned to every element of the array specified to the left of the equal sign. For example:
DIM name\$(10), grades(10,6), factor(10,6), init(6) INPUT f
MAT name\$ = “unregistered” MAT grades = 100
MAT factor = f
MAT init = ((f*100) / 5)
These statements assign the string “unregistered” to all 10 elements in the array name\$, the value 100 to all elements in grades, and the value input for the variable f to all elements in factor. Each element in init takes on the value of the expression (f*100)/5. Note that you must always use parentheses around an expression rep- resenting a single value in a MAT statement, as in the last line of the example above.

Built-in Array Constants
True BASIC provides several built-in array constants that you may use with the MAT statement. Array con- stants are special functions that return a particular array; they may be used only in a MAT statement on the right side of the equal sign. For example:
MAT word\$ = Nul\$
! all elements null strings
MAT table = Zer(3,4)
! 3-by-4 table, all elements 0
MAT table = Con
! all elements 1, or “constant” MAT table = 7*Con(2,6)
! 2-by-6 table, all elements 7
If you give no dimensions with the array constant, the array being assigned to keeps its current dimensions. If you do provide dimensions, the array being assigned to will be redimensioned to the size specified. Note, however, that

the lower bound will not be changed, and the upper bound will be adjusted so that the resulting array has the proper number of elements. Consider, for example:
DIM test(10)
! A 10-element array MAT test = Zer(3:9)
! A 7-element array PRINT Lbound(test), Ubound(test)
END
The DIM statement in this example defines the array test as having a lower bound of 1 and an upper bound of
10. Given the dimensions supplied with the ZER array constant used in the MAT statement, you might expect the new lower bound to be 3 and the new upper bound to be 9. However, if you run this program you will see that the lower bound of test remains 1 and the upper bound becomes 7. True BASIC retains the original lower bound of
1 and adjusts the upper bound so that test contains the same number of elements as in the constant Zer(3:9).

True BASIC’s array constants are as follows:
Examples of Array Constants
——————————————————————————————————————
Constant Result
CON An array with every element set to 1
IDN An identity matrix. It must be a square two-dimensional array, and it will have elements set to 1 running along the diagonal from top left to the bottom right. All other elements are set to 0. Since the array must be square (both dimensions the same size), you may redimension the array being assigned to by specifying the bounds of one or both dimensions.
For example Idn(4) is equivalent to Idn(4,4) and contains the elements:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
NUL\$
An array with each element set to the empty or null string “” ZER
An array with each element set to 0
——————————————————————————————————————

Array Arithmetic
True BASIC supports all of the fundamental mathematical operations for arrays and matrices. Many of these operations use the standard arithmetical operators with arrays as their operands. Other operations require spe- cial built-in functions. This section introduces operator-based array arithmetic and the following section discusses the function-based options. Both sections assume that you are familiar with the general concepts of matrix arith- metic, and make no attempt to teach these concepts.

True BASIC’s array arithmetic expressions follow the normal mathematical rules for matrix arithmetic. The array to which the result is assigned must always have the correct number of dimensions, but True BASIC auto- matically redimensions it to the correct shape by changing the upper bounds. Note that as with array assignment described above, True BASIC redimensions an array by changing the upper bounds only; it does not change the lower bounds. We recommend that all arrays involved in matrix arithmetic have the same lower bounds. Addi- tional restrictions for each operation are described below.

For array expressions, True BASIC allows only a single operator per MAT statement. The array operations are addition, subtraction, multiplication, and scalar multiplication.

In array addition and subtraction the specified operation is applied to all pairs of corresponding elements. Thus, the arrays must be of exactly the same size and shape (same number of dimenstions and same number of elements in each dimension). Although the lower and upper bounds for the dimensions are not important, the number of ele- ments is. In the statement:
MAT c = a + b ! Sum of corresponding elements of two arrays
the first element in b will be added to the first element in a, then the second elements in each will be added, then the third, and so forth until all pairs of corresponding elements have been added. The resulting array of sums will be assigned to c, and c will be redimensioned (if necessary) so that it has the same size and shape as a and b.

For a more complete example of array addition and subtraction, consider the following program:
DIM a(5), b(5), sum(1), diff(1) MAT READ a, b
DATA 1, 2, 3, 4, 5
DATA 5, 4, 3, 2, 1
MAT sum = a + b MAT diff = a - b MAT PRINT sum
MAT PRINT diff
END
which produces these results:
6 6 6 6 6

-4 -2 0 2 4
Notice that the target arrays sum and diff must be defined in the DIM statement. Although True BASIC will resize them as necessary, it will not create them automatically.

As long as they are of the same size and shape, you can add and subtract two arrays of any number of dimensions.

Array Multiplication
Array multiplication may be applied only to one- and two-dimensional arrays. If both arrays are one-dimensional, they must be the same size and the product will be a one-dimensional array containing one element. The array specified as the first (or left hand) operand of the multiplication operator will be treated as a row vector, and the array specified as the second (or right hand) operand of the multiplication operator will be treated as a column vec- tor. The result will be the “dot product” of the two operand arrays. For example:
DIM a(3), b(3), product(1) MAT a = 2
MAT b = 4
MAT product = a * b
MAT PRINT a
MAT PRINT b
MAT PRINT product
END
gives the results:
2 2 2

4 4 4

24
If the arrays to be multiplied are two-dimensional, the number of columns in the first array must equal the number of rows in the second. The result will have the number of rows of the first array and the number of columns of the sec- ond. In other words, if a is an l by m array, then b must be m by n, and the result will be l by n.

Array Multiplication
——————————————————————————————————————
a(2,3) b(3,4) c(2,4)
——————————————————————————————————————
As an example of matrix multiplication, consider the following program:
DIM a(2,3), b(3,4), product(1,1) MAT a = 2
MAT b = 4
MAT product = a * b
MAT PRINT a
MAT PRINT b
MAT PRINT product
END
which gives the following results:
2 2 2
2 2 2

4 4 4 4
4 4 4 4
4 4 4 4

24 24 24 24
24 24 24 24
If one array operand of the multiplication operator is one-dimensional and the other is two-dimensional, the prod- uct will be one-dimensional. If the first array operand is one-dimensional, it is treated as a row vector (single row with multiple columns) and must match the first dimension of the second array. If the second array operand is one- dimensional, it is treated as a column vector (many rows in one column) and must match the second dimension of the first array.

In other words an array with m elements may be multiplied by an m by n array, or an l by m array may be multi- plied by an array with m elements. In the first case the product will be a one-dimensional array with n elements, while in the second case the product will be a one-dimensional array with l element. For example:
DIM a(2), b(2,4), product(1) MAT a = 2
MAT b = 4
MAT product = a * b
MAT PRINT a
MAT PRINT b
MAT PRINT product
END
produces:
2 2

4 4 4 4
4 4 4 4

16 16 16 16

Scalar Multiplication
You can also multiply an array by a single, non-array value. Such non-array values are often called scalar val- ues. The scalar value used in the multiplication may be a positive numeric constant, a numeric variable, or any numeric expression enclosed in parentheses. The scalar value must appear as the first operand and may not be preceded by a sign; thus, to use a negative scalar value you must use a variable or enter the value as an expression in parentheses, such as (-4). The array operand, which must appear as the second operand, may be of any number of dimensions. Each element of the array operand is multiplied by the scalar value producing an array of the same size and shape as the array operand. For example:

DIM apples(2,3), income(1,1) LET cost = .59
DATA 27, 14, 52, 22, 29, 7
MAT income = cost * apples ! Scalar multiplication
MAT PRINT USING “\$##.## “: income
GET KEY k ! Wait for keystroke
END
produces:
\$15.93 \$ 8.26 \$30.68
\$12.98 \$17.11 \$ 4.13
Multiplication is the only array operation allowed for a scalar. If you wish to add or subtract the same value from all elements of an array, create an array with all elements containing that value and use array addition or sub- traction as described previously. Note as well that the only operators defined for array arithmetic are addition, subtraction, and multiplication.

Built-in Functions for Array Operations
True BASIC also supplies some built-in functions that take one- or two-dimensional arrays as their arguments. Some return a single numeric value and others return array values. These functions allow you to perform calcu- lations on arrays beyond those supported by the array operators described in the previous section.

Functions for Array Operations
——————————————————————————————————————
Function Result
DET The determinant of the last square two-dimensional array inverted by the
INV function; returns 0 if no array has been inverted
DET(x) The determinant of the square two-dimensional array x; returns 0 if x
is singular
INV(x) The inverse of the square two-dimensional array x; if x is singular or nearly singular True BASIC gives an error message
DOT(x,y) The dot product of the two one-dimensional arrays x and y, which must have the same number of elements
TRN(x) The transposition of the two-dimensional array x
——————————————————————————————————————
The DET function returns a numeric scalar value representing the determinant of the square matrix specified as its argument. If the square matrix is singular, the DET function returns a value of 0. If no array argument is spec- ified, the DET function returns the determinant of the last matrix inverted with the INV function. If no matrix has been inverted, a value of 0 is returned.

The INV function returns the inverse of the square matrix specified as its argument. The result will be a square matrix of the same dimensions as the argument. If the argument is singular, or nearly singular, then True BASIC generates an error. Since matrix inversions are notoriously susceptible to round-off errors, it is wise to check the value returned by the DET function after each inversion to determine if the matrix just inverted was almost sin- gular. If the DET function returns a value that is nearly zero, the inverted results are probably meaningless.

The following program demonstrates the use of the INV and DET functions:
DIM scores(2,2), inverse(1,1) MAT READ scores
DATA 1,2,3,4
MAT inverse = Inv(scores) MAT PRINT scores
MAT PRINT inverse
PRINT “Determinant: “; Det
END
This program produces the following output:
1 2
3 4

-2. 1.
1.5 -.5

Determinant: -2
The DOT function returns a numeric scalar value representing the dot product, or inner product, of the two one- dimensional arrays specified as its arguments. The two arrays must be one-dimensional and must have the same number of elements. Here is a typical use of the DOT function:
DIM price(12), amount(12)
MAT INPUT PROMPT “Prices: “: price
MAT INPUT PROMPT “Amounts: “: amount
LET total = Dot(amount, price) ! Total purchase price
...
Each element of the amount array is multiplied by the corresponding element of the price array and the value of total is set equal to the sum of these products.

The TRN function returns the transposition of the two-dimensional array specified as its argument. The result will be a two-dimensional array with the rows and columns of the argument array interchanged. In other words, the transposition of an m by n matrix named x gives an n by m matrix which we will call r such that r(i,j) equals x(j,i). For example, the program:
DIM Scores (2,4), Transpose(1,1) MAT READ Scores
DATA 27, 14, 34, 52, 22,12, 29, 7
MAT Transpose = Trn(Scores) MAT PRINT Scores
MAT PRINT Transpose
END
produces the results:
27 14 34 52
22 12 29 7

27 22
14 12
34 29
52 7
Remember that you may use only one array or matrix operation per MAT statement. You may build more complex matrix expressions, however, using a series of MAT statements. The following program illustrates the computa- tion of such a complex expression:
! Compute the inverse of i - (1/2)*a*Trn(a) DIM a(3,2), i(3,3), x(3,3), c(3,3)

DATA .1, .2, .3, .4, .5, .6
MAT x = Trn(a) MAT x = a * x
MAT x = (1/2) * x ! (1/2)*a*Trn(a) MAT i = Idn(3)
MAT x = i - x
MAT c = Inv(x) ! Inverse
MAT PRINT c
PRINT Det(x) ! Check determinant
END
In this example, only the shape of the array a is relevant; the other arrays are redimensioned by MAT statements. Note that the “working matrix” x occurs on both the left and right side of the equal sign in several statements. True BASIC always evaluates the right side first, then redimensions the matrix on the left (if necessary) and assigns the answer to it. This is why the program above works correctly.

Sorting and Searching the Contents of Arrays
As you work with arrays, you may want to sort the elements within them or search for a particular element. True BASIC provides several subroutines that can sort or search your arrays. A subroutine is a block of statements that carry out a specific task. As with functions, you may need to specify one or more arguments when you invoke a sub- routine. Subroutines differ from functions in that you invoke them with a CALL statement and they return values via the arguments you specify — thus a subroutine may return more than one value or array.

The sorting and searching subroutines are not built into True BASIC, they are contained in the SortLib.TRC “library file” that is placed in the TBLIBS directory when you install True BASIC. This section introduces these subroutines and the SortLib.TRC library. For complete information on subroutines and library files, see Chapters 10 “User- defined Functions and Subroutines” and Chapter 11 “Libraries and Modules.” Briefly, a LIBRARY statement must name the file or files containing any non-built-in subroutines your program will use. You then invoke the subroutine with a CALL statement that includes arguments for the subroutine, as follows:
! Print an alphabetized list of names

LIBRARY “c:\TBSilver\TBLIBS\SortLib.TRC” ! Use appropriate pathname

DIM names\$(1)
PRINT “Input names (last name first), typing a comma” PRINT “after each name except the last.”
MAT INPUT names\$(?)
CALL SortS (names\$()) ! Sort string array names\$

FOR i = 1 to Ubound(names\$())
PRINT names\$(i) ! Print sorted array, one name per line
NEXT i

END
At the CALL statement, True BASIC carries out the task defined by the specified subroutine. That task may assign new values to the argument in the CALL statement as it does for the names\$ array above. (The SortS subroutine is described more fully below.)

Most of the sorting and searching subroutines are in the library file SortLib.TRC (saved in TBLIBS). The LIBRARY statement must use the appropriate “pathname” format to indicate the location of the library file for the computer you will use to run your program.

SortLib.TRC includes subroutines for simply and quickly sorting, reversing, or searching the elements of both numeric and string arrays. These simple sorts are very fast and require little memory as the sort is done “in place”
— the original array is replaced by the values of the sorted array.

SortLib.TRC also includes subroutines for “pointer sorts” and customized “comparison sorts.” A pointer sort does not change the original array, but instead creates an index array containing subscript pointers to the sorted val- ues of the original array. Pointer sorts are helpful if it is important to keep the original order for ties or if you wish to use the sorted order of one array to print values from other “parallel” arrays.

A comparison sort lets you customize the way values will be sorted or sort values based on one or more specific
“key fields.” You may customize both simple and pointer sorts.

Simple Sorts and Searches
The simple sorting and searching subroutines in the SortLib.TRC library are as follows:

Simple Sorting and Searching Subroutines
——————————————————————————————————————
Subroutine Library Result
SortN(a()) SortLib Sorts numeric array a into ascending order SortS(a\$()) SortLib Sorts string array a\$ into ascending order ReverseN(a()) SortLib Reverses order of elements in numeric array a ReverseS(a\$()) SortLib Reverses order of elements in string array a\$ SearchN(a(),n,i,f) SortLib Searches the sorted numeric array a for the value n;
if found, reports the subscript value as n and f as a non-zero value
SearchS(a\$(),s\$,i,f) SortLib Searches the sorted string array a\$ for the value s\$; if found, reports the subscript value as n and f as a non-zero value
——————————————————————————————————————

The SortN and SortS subroutines sort numeric and string arrays into ascending order. They arrange values according to the standard meaning of True BASIC’s <= operator. Numeric values are sorted in ascending order by value and strings are sorted according to code values for the standard character set; thus, uppercase characters come before lowercase characters (see Appendix A for code values of the standard character set).

To use these subroutines, you must name SortLib.TRC in a LIBRARY statement (using the pathname format appropriate for your computer) and invoke the subroutine with a CALL statement. Both of these subroutines require one argument — a numeric array for the SortN subroutine or a string array for the SortS subroutine. When the CALL statement is executed, the subroutine sorts the elements in the array passed as an argument. The subroutine changes the contents of the array, returning an array with all of its elements sorted into ascend- ing order. For example, the following program:
! Sort a list of scores

LIBRARY “SortLib.TRC” ! Use appropriate pathname

DIM scores(10)
DO WHILE MORE DATA LET i = i + 1
DATA 99.5, 87.5, 89, 93.25, 89, 75, 80
CALL SortN (scores()) ! Sort in ascending order
MAT PRINT scores ! Print the result
END
prints the following:
0 0 0 75 80
87.5 89 89 93.25 99.5
Notice that the SortN subroutine sorts the entire array. It keeps all tie values and includes zeroes for unassigned values.

The SortS subroutine works similarly for string arrays, merging in null strings for any unassigned values. Remember that characters are sorted by their character-code value. Thus the program:
! Sort a list of phrases

LIBRARY “SortLib.TRC” ! Use appropriate pathname

DIM words\$(10)
DO WHILE MORE DATA LET i = i + 1
MAT REDIM words\$(i) ! Eliminate unassigned elements
DATA zebra, orangutan, Tiger
DATA apples, apple pie, tiger
DATA “widget, small”, “widget, large”
CALL SortS (words\$()) ! Sort in ascending order
MAT PRINT words\$ ! Print the result
END
produces the following output:
Tiger apple tarts apples orangutan tiger widget, large widget, small zebra
Here, “Tiger” and “tiger” are not equivalent because uppercase and lowercase letters are different charac- ters; and “Tiger” precedes all the lowercase words because the uppercase alphabet precedes lowercase letters in the standard character code. Similarly “apple tarts” comes before “apples” because the space character comes before letters in the standard character set. (If you wish to sort uppercase and lowercase letters as equiva- lents you could create a second array with all uppercase or lowercase elements and sort that or use a comparison sort as explained below.)

Notice that the above program eliminates the problem of null strings by redimensioning the array to eliminate unassigned elements before sorting.

The SortN and SortS subroutines are very fast and, because they work “in place” in the original array, they use very little memory beyond that already required by the array itself. They gain speed at the sake of complexity, however. The additional sorting subroutines described in the rest of this chapter give you greater control over how elements are sorted.

The SortN and SortS subroutines sort in ascending order only. You can easily sort in descending order, however, by reversing the elements after a simple ascending sort. The ReverseN and ReverseS subroutines (also contained in SortLib.TRC) reverse the order of the numeric or string array argument, respectively. For example, you could adapt the numeric sort example above to print scores in reverse order by adding a call to the ReverseN subroutine:
! Sort a list of scores

LIBRARY “SortLib.TRC” ! Use appropriate pathname

DIM scores(10)
DO while more data
LET i = i + 1
DATA 99.5, 87.5, 89, 93.25, 89, 75, 80
CALL SortN (scores()) ! Sort in ascending order
CALL ReverseN (scores()) ! Reverse the order MAT PRINT scores ! Print the result END

This program prints the following:
99.5 93.25 89 89 87.5
80 75 0 0 0
Similarly, you could use the ReverseS subroutine with a sorted string array.

The SearchN and SearchS subroutines search sorted numeric or string arrays for a specified value. These sub- routines take four arguments: the array to be searched, the value to search for, and two numeric variables that are used to return the results of the search. For example:
LIBRARY “SortLib.TRC” ! Use appropriate pathname

DIM array(20) FOR i = 1 to 20
LET array(i) = Int(100*Rnd) + 1 ! Create array of 20 random numbers
NEXT i
CALL SortN(array()) ! Sort random numbers in ascending order
DO
INPUT PROMPT “Enter a number from 1 to 100 (0 to quit): “: number
IF number <= 0 then EXIT DO
CALL SearchN (array(), number, index, found) IF found <> 0 then
PRINT number; “found at element”; index
ELSE
LOOP

END
The last argument, found in the example above, returns 0 if the search value (number above) is not found or a non-zero value if it is found. The third argument, index above, returns the subscript of the element if the value is found. If the value is not found, index equals the subscript where the value would have been stored in the sorted array if it existed.

The SearchS subroutine works the same way except that the array and search value must be strings. Note that the array passed to the SearchN or SearchS subroutine must be sorted into ascending order.

——————————————————————–––—————————————————
[ ! ] Note: The SearchN and SearchS subroutines must be passed arrays that are already sorted into
ascending order because these subroutines use a binary search, which is generally much faster than an
element-by-element, or “sequential,” search. In a binary search, the subroutine looks first at the ele- ment at the mid-point of the array. If that element does not equal the search value, the routine finds out if it is greater than or less than the search value. The binary search then “ignores” the half of the array that is too small or too large and looks at the mid-point of the remaining range of elements. This process continues until the value is found or two consecutive elements are found to bracket the search value. The values of the array must therefore be arranged in order from smallest to largest.
————————————————————–––———————————————————

Pointer Sorts
Though quick and efficient, the SortN and SortS subroutines lose the original order of elements in the array. This could be a problem if, for example, you have separate arrays for names, addresses, and phone numbers. If you use the SortS subroutine to change the order of elements in the array of names, you would lose the relationship between the arrays. For such cases, you should use a “pointer sort.” The two basic pointer sorts are implemented by the PSortN and PSortS subroutines for numeric and string arrays, respectively.

——————————————————————————————————————
Subroutine Library Result
PSortN(a(),i()) SortLib Performs a “pointer sort” on values in numeric array a and stores the sorted pointers, or indices, in the array i
PSortS(a\$(),i()) SortLib Performs a “pointer sort” on values in string array a\$ and stores the sorted pointers, or indices, in the array i
——————————————————————————————————————
In a pointer sort, the original array is not changed. Instead, the subroutine sorts the values and creates an index array whose elements are subscripts pointing to the sorted values of the first array. For example, here’s an adap- tation of the earlier phrase-sorting program:
! Sort a list of phrases

LIBRARY “SortLib.TRC” ! Use appropriate pathname

DIM words\$(10), index(10) DO WHILE MORE DATA
LET i = i + 1
MAT REDIM words\$(i), index(i) ! Eliminate unassigned elements
DATA zebra, bananas, orangutan
DATA apples, kiwis
CALL PSortS (words\$(), index()) ! Sort in ascending order
PRINT “The words\$ array contains the values:”
MAT PRINT words\$ ! Print the original array
PRINT “The index array contains the values:”
MAT PRINT index ! Print the index array
PRINT “Thus, the sorted values of words\$ are:” FOR n = 1 to i
PRINT words\$(index(n)), ! Print words\$ elements in sorted order
NEXT n
PRINT

END
This gives the results:
The words\$ array contains the values:
zebra bananas orangutan apples kiwis

The index array contains the values:
4 2 5 3 1

Thus, the sorted values of words\$ are:
apples bananas kiwis orangutan zebra

Note that the order of elements in words\$ has not changed, but index gives the order in which the elements of words\$ should be read to produce a sorted list. The first word in the sorted list is element 4 or “apples”, the second word is element 2 or “bananas”, and so on. As with simple sorts, pointer sorts sort the entire array. Thus, if an array contains unassigned elements, the resulting index array will contain pointers for all the zeros or null strings in the array.

Pointer sorts are especially useful if your program uses “parallel” arrays. If you have one array containing student names and another containing their grades, you can do a pointer sort on grades and then print both arrays in sorted order using the index array. The following example does just that, and uses the ReverseN subroutine to sort the index array in descending order:

LIBRARY “SortLib.TRC” ! Use appropriate pathname

DATA Adams, Bell, Cosi, Du, Eisen
DATA 77, 94, 88, 80, 95
CALL PSortN (grades(), index()) ! Sort in ascending order
CALL ReverseN (index()) ! Reverse indices
FOR n = 1 to 5
PRINT

GET KEY k ! Wait for keystroke
END
This produces the following output:
Eisen 95
Bell 94
Cosi 88
Du 80
Pointer sorts are also helpful if you need a stable sort, which keeps the original order in case of ties. This is not important for simple sorts where the whole string is compared. But if you sort by just part of the data, as you can do with comparison sorts, this may be important.

Customized Sorts and Searches
The sorting routines you’ve seen so far all use the usual True BASIC <= operator to sort values into ascending
order, and they consider the entire value in making the comparison. Customized sorting and searching routines let you sort or search on one or more key parts of the data or define exactly how to compare two values. Thus, you could choose a sort that ignores the difference between uppercase and lowercase letters, or devise one that sorts roman numerals in the correct order.

SortLib.TRC contains custom-sorting versions of all the sorting and searching subroutines introduced so far
(except the reversing routines, for which custom comparisons are not needed):

Customized Comparison Sorting Subroutines
———————————————————————————————–––———————
Subroutine Result
CSortN(a()) Sorts numeric array a in ascending order using customized comparison routine called CompareN
CSortS(a\$()) Sorts string array a\$ in ascending order using one or more special options
CSearchN(a(),n,i,f) Searches the sorted numeric array a for the value n using a customized comparison routine called CompareN; if found, reports the subscript value as i and f as a non-zero value
CSearchS(a\$(),s\$,i,f) Searches the sorted string array a\$ for the value s\$ using one or more special options; if found, reports the subscript value as iand f as a non-zero value

CPSortN(a(),i()) Performs a “pointer sort” on values in numeric array a using a customized comparison routine called CompareN and stores the sorted pointers, or indices, in the array i
CPSortS(a\$(),i()) Performs a “pointer sort” on values in string array a\$
using one or more special options to customize the sort, and stores the sorted pointers, or indices, in the array i
———————————————————–––———————–––———————————— First, let’s see how we can sort a string array ignoring case. Before calling the subroutine CSortS, we call the subroutine Sort_IgnoreCase.
! Sort a list of phrases ignoring case

LIBRARY “SortLib.TRC”, “CompCase.TRC” ! Use appropriate path names
DIM words\$(10)
DO while more data
LET i = i + 1
MAT REDIM words\$(i)
DATA zebra, ELEPHANTS, Tiger DATA apples, tiger, Llama DATA Widget, Oranges
CALL Sort_IgnoreCase ! Treat upper- and lowercase alike CALL CSortS (words\$()) ! Sort in order defined in CompareS MAT PRINT words\$ ! Print the result
END
This program prints the following:
apples ELEPHANTS Llama Oranges Tiger tiger Widget zebra
Besides ignoring case, there are several other options.

Special Customized Sorting Options
——————————————————————————————————————
Subroutine Result
CALL Sort_Off Remove all special string sorting options
CALL Sort_ObserveCase Do not ignore case (default)
CALL Sort_IgnoreCase Ignore distinction between upper- and lowercase CALL Sort_NiceNumbers_on Use "intuitive" ordering for numbers within strings CALL Sort_NiceNumbers_off Ignore numbers in strings (default)
CALL Sort_NoKey No key fields (default)
CALL Sort_OneKey (f1, t1) One key field
CALL Sort_TwoKeys (f1, t1, f2, t2) Two key fields
——————————————————————————————————————
As an example of a sort based on a key field, here’s a program that sorts strings based on area code and then last names within area codes:
LIBRARY “SortLib.TRC” ! Use appropriate pathname

DATA “Smith Rosario 802-543-1234” DATA “Li Steven 617-123-1200” DATA “Arndt J. K. 802-331-3333” DATA “de Forbe Francis 205-256-2424”
CALL Sort_TwoKeys (20, 22, 1, 9) CALL CSortS (phonelist\$())

FOR i = 1 to 4
PRINT phonelist\$(i)
NEXT i

END
This produces the output:
de Forbe Francis 205-256-2424
Li Steven 617-123-1200
Arndt J. K. 802-331-3333
Smith Rosario 802-543-1234
As an example of sorting numbers "intuitively," imagine you have strings containing numbers, such as A1, A2, A3, A10, B1, B2, B12, and so on. The SortS or PSortS subroutine would arrange these as:
A1 A10 A2 A3 B1 B12 B2
Calling the subroutine Sort_NiceNumbers_on before calling CSortS subroutine would sort them as follows, putting the numeric text in proper numeric sequence:
A1 A2 A3 A10 B1 B2 B12
You may use customized comparisons with searches as well. Since the CSearchN and CSearchS subroutines both use the binary search method, the data must first be sorted. For example, if you wish to search the phone list in the example above by last name, you should first sort by the last-name field, as follows.
LIBRARY “SortLib.TRC” ! Use appropriate pathname

DATA “Smith Rosario 802-543-1234” DATA “Li Steven 617-123-1200” DATA “Arndt J. K. 802-331-3333” DATA “de Forbe Francis 205-256-2424”
CALL Sort_IgnoreCase
CALL Sort_OneKey (1, 9)
CALL CSortS (phonelist\$()) ! Sort using chars 1 through 9 only
INPUT PROMPT “Enter last name: “: find\$

LET find\$ = (find\$ & " ")[1:9] ! Make 9 characters long

CALL CSearchS (phonelist\$, find\$, index, found) ! Search with option
IF found <> 0 then
PRINT phonelist\$(index)
ELSE
END IF

END
Observe that you must use the same customized options for the search phase as for the previous sort phase.

You can write your own customized comparison routines. Note that the sort and search subroutines whose names begin with the letter "C" invoke a special comparison subroutine CompareN or CompareS, for numeric arrays and string arrays, respectively. The CompareN and CompareS subroutines use the same three-parameter format:

CompareN (a,b,r)
or
CompareS (a\$,b\$,r)

The first two parameters pass the two values to be compared — numeric or string — from the sorting routine to the comparison routine. The third parameter must be a numeric variable that returns a value to indicate the sort order, as follows:

r must return -1 if a should come before b, i.e., a < b r must return 0 if a and b tie, i.e., a = b
r must return 1 if a should come after b, i.e., a > b

If you have special sorting requirements, you should examine the source code for the sorting library, found in SortLib.tru. You can precede each line in CompareN or CompareS with an exclamation point "!", which is the comment character for True BASIC. Then simply write your own routine without exclamation points! Use the existing routines as patterns. The routines CSortS, etc., are already set up to call a subroutine by the name CompareS, etc.