TweetFollow Us on Twitter

Prograph Primitive
Volume Number:7
Issue Number:5
Column Tag:Programmers' Workshop

Prograph Primitives

By Terry Kilshaw, British Columbia, Canada

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

[Terry Kilshaw is a free-lance consultant, writer and programmer now living in the Okanagan valley of British Columbia. He has been designing software and programming all sorts of computers for 12 years, the last 3 1/2 of which he spent as Manager of Software Development for TGS Systems. He can be reached at (604) 762 4451.]

Adding C Primitives to Prograph

Prograph is a very high-level, object-oriented, pictorial, data-flow programming language and integrated development environment. For an introduction to Prograph see Prograph Raises OOP to New Height MacTutor, July 1989 by Mark Szpakowski. It is fairly straightforward to add your own primitives to Prograph. These can be written in such a way that they can be called from both interpreted and compiled code.

In this article I’ll describe a couple of useful primitives, written using THINK C. One is for creating a sorted list of unique strings from an unsorted list whose strings are not necessarily unique, and the other is for performing a binary search on a sorted list. On the way we will take a look at some practical details of Prograph primitive construction and finish with a detailed example of a simple Prograph program which shows a useful string permutation generator and provides a test bed for the two new primitives.

Readers who don’t use or intend to use Prograph will still be able to adapt the C language binary search for their own purposes.

The Structure of Prograph Data Items

All Prograph data items are referred to by Macintosh Handles and begin with the three data fields, type, save and use. For historical reasons the size of the save and use fields, and indeed of all index fields in interpreted code, is an unsigned two byte integer value. In compiled code, however, they are four byte unsigned integers. Index fields include the maximum length of a string and the maximum number of elements in a list. Because Handles are used for strings and lists they can be grown and shrunk dynamically, a fact that we will make use of later. The reason for the difference in data type definitions lies in the evolution of Prograph. In subsequent releases we can expect these differences to disappear.

After the type, save and use, in fixed length data items, comes the actual data: an integer, floating point or boolean value. Lists, strings and instances of classes can vary in length and therefore also have a length field.

The Primitives

The two primitives which will be created are called binlist-sort and binlist-find. A “binlist” is just a name for a sorted list of strings with no duplicates. These primitives would appear in Prograph as follows:

binlist-sort expects a list of strings on its input. It discards any duplicate entries and creates the sorted list as its output.

binlist-find expects a sorted list of strings, with no duplicates, on its first input and the string to search for as its second input. binlist-find returns a boolean, on its first output, indicating TRUE if the string was found in the list and, on its second output, the list index where the string was found. If the string was not found, the value of the first output will be FALSE and the second output will be the list index where the string could be inserted into the binlist. So binlist-find can be used both for finding an entry in a binlist and for maintaining new entries to the list.

Making Primitives for the Interpreter

The _INTERPRETER flag in X_includes.h (see below) should be set to 1. Each group of primitives is compiled into a code resource whose resource type is defined as TGSC. For each primitive in the code resource an STR# resource consisting of 3 strings is created, giving the mnemonic names of input and output arguments, the types of input and output arguments, and a description of what the primitive does. The resource name is used as the primitive name. The TGSC and STR# resources are placed together in a file whose creator type is TGSL and whose file type is TGSC. On startup Prograph automatically searches its own folder for files of type TGSC, the code resources and the primitive names are loaded into the interpreter, and the primitives are available for use.

The STR# resources are shown, in RMaker format, in the listing of binlist_prims_I.r at the end of the article. Obviously you can use ResEdit or whatever resource creation tools are available to you. After running through RMaker an output file called binlist_prims.p.rsrc is produced. If this file is placed in the same folder as your THINK C project then the STR# resources will automatically be copied into the code resource file when the Build Code Resource item of the Project menu is chosen. The name of the file containing the finished primitives is binlist_prims.

The name of the STR# resource is used as the primitive’s name in Prograph. The STR# resource’s number maps on to the first argument of the AddPrimitive call in the main function as described below. The (32) which follows the STR# number indicates that the resource is to be purgeable. The three strings which follow that are for display by the Primitives item of Prograph’s Info menu.

Making Primitives for the Compiler

The _INTERPRETER flag in X_includes.h (see below) should be set to 0. In this case no STR# resources are needed, since on-line help is only appropriate in the interpreter. The primitives are then compiled and built into a THINK C library. That library is then added to your Prograph project and linked in with the Prograph code and libraries to form the finished stand-alone program.

Naming Conventions

Primitives can be named any way you like, but there is an important convention for the names of the C language functions which those primitives call which must be used if you want the compiler to recognize them. A primitive can be thought of as a sort of Universal Method. In compiled code, primitives and Universal Methods are actually indistinguishable. Consequently the naming conventions, for a primitive, start from the name that it is called by in the interpreter and prefix it with the characters U_. For example the primitive test would become U_test. The naming conventions of the C language, however, allow only alpha-numeric characters and the underscore is legal function names. In order to cope with names which include special characters such as the hyphen, the plus sign and so on, each special character is replaced with its two digit hexadecimal ASCII value and preceded and followed by underscores. For example binlist-sort becomes U_binlist_2D_sort, because 2D is the ASCII equivalent for the hyphen ( or minus sign, if you will ) in hex. form.

Now to the Code

The source for the C language examples can be found in the file binlist_prims.c

MacTypes.h is a standard header provided by THINK C. X_includes.h is supplied as part of the Prograph product and provides type declarations for code called by both the interpreter and the compiler. This file also defines the value of the preprocessor symbol _INTERPRETER, with the possible values of 0 for FALSE and 1 for TRUE. The value of _INTERPRETER is then used throughout the primitive’s code, for preprocessor directives to allow the single source to be used for primitives which will work in interpreted and compiled Prograph programs.

X_includes.h also provides typedefs for all of the usual C types, which map as follows:

THINK C Type Prograph Typedef

char Int1

unsigned char Nat1

int Int2, Bool

unsigned int Nat2

long Int4

unsigned long Nat4

Bool can have only two values, 0 & 1, defined as FALSE and TRUE respectively.

The data item typdefs all have a similar form. Here, for example is the one for Prograph’s integer data item as used by the interpreter:

/* 1 */

 typedef struct
 Int2 type;
 Nat2 save;
 Nat2 use;
 Int4 value;
 } *C_integer;

Now when an integer is declared in the code its declaration is of the form:

/* 2 */

 C_integer *myC_integer;

Experienced C programmers may wonder, when the data item is going to be referenced via a handle anyway, why the extra level of indirection is not included in the typedef. The reason for this is to try and make it easier for programmers to utilize THINK C’s object oriented syntax if it becomes necessary to use code originally written as a primitive directly as a class or universal method in compiled Prograph programs. See the Writing XCode section of Appendix II in the Prograph Reference Manual for more details.

An Index type is defined for separate use in interpreted and compiled Prograph programs, as explained above. LT, EQ and GT are equates for the return value of compare_strings.

Prototypes for all of the functions defined in the file (except for main) allow for forward references in the code and for the Check Pointer Types and Require Prototypes options to be enforced.

ct, which stands for common table, is a pointer to an array of ProcPtrs. main, which is called only once, when Prograph is launched, sets up this pointer to the common table. This allows primitives to have access to a group of functions which simplify the creation of data items, the manipulation of lists and instances, the maintenance of reference counts and so on. A number of these functions will be used in the construction of our two primitives.

The AddPrimitive functions, add the two primitives to an internal Prograph table. From that point on they can be used just like any of the primitives which are supplied with Prograph.

In a THINK C code-resource, global and static variables are referenced off of register A4. When each code resource is loaded and each call to AddPrimitive is made, the value of A4 is saved in a table. Whenever a call to that primitive is made later, Prograph automatically sets up the A4 register using the value which was saved. The consequence is that the writer of primitives can always assume that it is ok to use global variables in his or her code resources.

This whole section is bracketed by #if _INTERPRETER directives. It is only needed to add your primitives to the interpreter’s tables and to allow you access to the Supplied Functions. In compiled Prograph code the Supplied Functions are accessed through the Prograph library and the primitives are called just like any other function.

U_binlist_2D_sort( )

The first thing done in any primitive is to check the arity of the call, i.e. the number of inputs and outputs it has. Since binlist-sort is a fixed arity primitive (i.e. it does not have a variable number of inputs or outputs) the arity check is very simple. If the primitive is not being called with precisely one input and one output then an arity error is returned immediately. In THINK C the macro GETARITY extracts its values from register A0.

The supplied function IsType evaluates to TRUE or FALSE. It is passed the data item to be checked and the type identifier to check it against. If this input is not a list a type error is returned with the number of the input, where the offending type was found, added to the error value.

When called from a compiled Prograph program no primitive should ever fail. All error conditions should be discovered and programmed for using the interpreted environment. Consequently all error checking code can be dispensed with in the compiler version. Hence it is bracketed here by the #if _INTERPRETER ... #endif directives.

Prograph employs a use or reference counting mechanism as the basis of its automatic garbage collection. This scheme requires that a field in a data item is used to keep a count of the number of references to it. Whenever a new reference is made to an item, its use count is incremented by one. When that reference ceases, the use count is decremented by one. When an item’s use count reaches zero the item is garbaged. In Prograph a data item can be referenced from only a limited number of places. They are as follows:

1) from a persistent

2) rom a class attribute

3) from an instance attribute

4) from a list element

5) from a constant operation

6) from a match operation

7) from the root of an operation after it has been executed and before its case has finished execution.

To properly maintain use counts in a Prograph primitive you need to ask the question, “After the primitive has finished execution, what references to data items will remain?” Categories 3, 4 and 7 are the only ones that can be accessed from inside a primitive. You need also to realize that because of the data-flow nature of Prograph, with one exception, data items which are input to primitives are never altered. Once created they are basically constant values.

The exception referred to above is that of instances of classes. Such instances do have their data values changed, or rather exchanged. For example the first attribute of an instance may be an integer with the value 4. A new integer, with the value of 33 may be assigned to that attribute thereby changing the instance. But the value of the first integer itself will not be changed. Primitive data values, and these include the list type, are only created or destroyed, never changed.

Here then, it is necessary to create a new list, which is done using the supplied function MakeC_list. The new list is automatically created with a use count of 1, and we specify a length of 0. Unless any input values of the wrong type are encountered, this new list will be assigned to the output root of the primitive. This will be the only continuing reference to that list after the primitive has finished executing. A use count of 1 means “one thing is pointing to me”, the root of the primitive.

Next the type of each data item in the input list is checked. If it is not a string then the newly created list is garbaged by passing it to DecUse and a type error is returned, specifying input 1 as the origin of the error. When passed a list, the supplied function DecUse first decrements the use count of the list. If that use count is 0 then the use count of the data item in each list slot is also decremented. If the use count of any item falls to 0 its handle is garbaged i.e. its storage is released back to the heap. As before, this section of code is redundant in the compiler version of the primitive.

If the data item is a string we check to see if it is already in the new list by making a call to the function binlist_find. The binlist_find routine does a binary search on the contents of the new list, to see if a duplicate string is already in that list and to return the index for insertion if it is not.

If not found, the string is inserted in the list at the given index using the supplied function ListInsertSlot. The third argument to ListInsertSlot indicates that the use count of the inserted string should be incremented by 1. We know that this string is already being pointed to from InList and it will now also be referenced from sortList. Both of these references will continue after the primitive finishes executing.

This is why, if the primitive is aborted as described above, it is necessary to pass sortList to DecUse, so that the incremented use counts in each of the strings can be properly decremented before sortlist is garbaged.

Lastly the new list is assigned to the output and PCF_TRUE is returned as the value of the primitive’s control state. PCF stands for Prograph Control Flag. A primitive which encounters no errors during execution may return a value of TRUE or FALSE. FALSE is valid only for boolean primitives, such as integer?, which have no outputs and whose value can be TRUE or FALSE.

There is one obvious inefficiency in the binlist-sort routine. For every string inserted it is necessary to expand the list handle by 4 bytes, which may cause the whole handle contents to be copied, if it needs to be moved for the grow to occur. And, if the insertion is not at the very end of the list, all list entries from the index to the end of the list will need to be moved to make space for the new list element. On the other hand it is only the 4 byte references to the strings which are moved and not the strings themselves. In practice performance has been quite acceptable as you will see in the Prograph test example.

U_binlist_2D_find( )

We begin, as usual, by checking the arity and the types of inputs, i.e. 2 inputs ( a list and a string ) and 2 outputs. No attempt is made here to verify that the input list contains only strings and no other types of data item or NULL slots. Nor is there any check to ensure that the list is in sorted order. It is up to the user to maintain his or her binlists properly. As before, the arity and type checks are redundant in the compiler version and are bracketed by preprocessor directives.

The guts of the find mechanism has been factored out into the function binlist_find, so that it can be called from U_binlist_2D_sort as well as from U_binlist_2D_find. binlist_find is sent the list and the string to search for. It returns TRUE or FALSE in found and the index at which it was found, or at which the string could be inserted into the list, in index.

The values of found and index are then packaged up as a boolean and an integer which are created and assigned to outputs 1 and 2, respectively, using the supplied functions MakeC_boolean and MakeC_integer. New data items are created with a use count of 1. On completion of the primitive, only the output roots that the boolean and integer were assigned to, will refer to those data items. Therefore a use count of 1 is correct.

binlist_find( )

To keep the search as fast as possible, pointers, register variables and bit-shifting by 1 instead of integer division by two have all been used here. The variable table is used as a pointer into the list. The variable lo is set to 0 and hi is set to the length of the list minus 1. Thus if the length of the list is 0, hi will be -1 and therefore less than lo. In that case the while loop is not executed, FALSE is returned for found and 0 is returned as the insert position of the string. Remember that, internally, lists use a 0 based index but when called from Prograph a 1 based index is used.

Next the hit point is computed. This is (lo + hi) divided by 2. The general principle of binary search is the process of divide and conquer. Because the list is in sorted order we first divide it into two parts, determine which of the two parts the string being sought belongs to, concentrate on that part then repeat the process until the string is matched or lo becomes greater than hi.

Hence if the value of compString is less than the value of the string in table[hit], the value of lo stays the same and the value of hi is set to hit minus 1. Otherwise if the value of compString is greater than the value of the string in table[hit], the value of hi stays the same and the value of lo is set to hit plus 1.

The process will then continue to loop while lo is less than or equal to hi, at which point lo will be left pointing to the place in the table where compString should be inserted.

The only other possibility is that the strings are equal, in which case found is set to TRUE and index is set to hit.

Note that in this implementation it is necessary that hi can assume negative values. This restricts us to lists of 231 items in length.


This is a simple string comparison routine. Although it works on two handles it is not necessary to lock them because nothing in this routine causes memory to be moved in the heap.

The Example Prograph

In order to run some simple timing tests on the two new primitives we shall construct a Prograph which generates a list of strings from the permutations of the characters of a starting string, creates a sorted list and then searches the sorted list for an entry. See the file binlist_test.pgs.

If, for each subsequent execution of the test program, you use as your starting strings “a”, “ab”, “abc” etc then the permutation algorithm will generate N factorial strings ( where N is the number of characters in the string ), each of which will be unique, ( because all of the characters of the starting string are unique ). If the starting string is used as the string for which to search then we are guaranteed to find it in the list and it is guaranteed to take the longest possible search time because it will always sort to the very first place in the list. You can verify that by a consideration of the code for binlist_find above.

One point to note is that 8! = 40320. 40320 strings will require more than 1 megabyte of RAM and even on an FX will tie your computer up for a while.

The Local Method permute first calls the primitive ask, which puts up a dialog box and asks you to input a string. If you do so then that string will be permuted into the required list of strings, which is done in the time permute Local Method. Both the starting string and the list will be saved in the persistents string and perms. The time taken to accomplish the permutations is then sent to the Method do-show which displays the length of the created list and the number of ticks ( i.e. 60ths of a second ) that it took.

You have the option here of not inputting a string and just pressing the return key or clicking the OK button of the ask dialog. In that case NULL is returned and Case 2 of permute is executed.

Case 2 of permute just outputs the list of permutations which were computed during a previous execution and saves having to recreate the list every time.

For the sake of comparison the permutation process is timed as well as the sort and search. The timing mechanism is very simple and, as we shall see later, a little too rough-grained for all our purposes. It consists of getting the value of the Mac global Ticks before and after the call you want to time. Then one is subtracted from the other to get the elapsed ticks.

The actual permutation process occurs in the Method do-perm.

Case 1 of do-perm first checks to see if the input is the empty string. If the string is not empty it is split into two parts; the first character and the rest of the string. The rest of the string is passed to a recursive call to do-perm and the recursion continues until the length of the input string is 0. At that point Case 2 of do-perm is executed and the recursion terminates, outputing a list which contains the empty string, to the root of the most recent do-perm call.

The string(s) in this list are then fed one at a time into the right input of the Local Method make string list. The character which was removed by the prefix operator in do-perm is also fed to make string list on its left input.

1 is added to the length of the string on the right input and a list that length of the form ( 0 1 2 3 etc ) is made. The input character and the input string are then fed repeatedly to the Local Method make string together with an integer from the list of numbers. The strings generated by make string are accumulated on its list output.

If, for example, the inputs are “a”, “bc” and 0 then the output string will be “abc”. If input 3 is 1 instead of 0 then the output string will be “bac”, and so on.

At the end of all recursions and iterations a list of lists of strings will be present on the output of make string list in the Method do-perm. But what is needed is a list of strings. It is the function of the (join) operation to accomplish that. The empty list is fed into its left input and is looped back around as each list of strings in the list of lists is joined to the empty list to give the final output of do-perm. This eventually returns through the Local Method time perm to the Local Method permute where the results can be displayed by a call to do-show.

The do-show Method puts up a dialog with a line of text in it of the form:

permute ( or sort or find): N strings: M ticks.

The unsorted list is next fed into the Local Method sort.

Which feeds it to time sort.

It is here that one of the new primitives that we created is first used. The unsorted list is fed to the input of binlist_sort and the new sorted list comes from its output. This eventually feeds back up to sort and then into the Local Method find.

Here the original string is retrieved from the persistent where it was saved and fed to the Local Method time find.

The second primitive, binlist-find, is called and the result of that timing is fed back for display to find.

The Results

The tests were executed on a Mac Plus with 4 Megabytes of RAM running under Finder. Here is a tabulation of the results. All times are in ticks, i.e. 1/60ths of a second.

Memory management, i.e. handle allocation, is the major cost both in generating the string permutations and for the sort operation. Nevertheless, for the maintenance of short lists, 2 seconds to sort 720 strings should be quite acceptable for many applications.

The find operation is faster than can be properly timed using the Mac’s tick count. It appears here to be absolutely steady. Other tests that have been carried out indicate an average search time of about 0.0002 seconds for a 10 element list, 0.0003 seconds for a 100 element list and 0.0005 seconds for a 1000 element list. In fact if N is the number of entries in the list the total number of times through the search loop is only about log2N, and the only memory management overhead involved is generating the handles for the primitive’s boolean and integer return values.

It would be simple enough to extend the binary search mechanism to accommodate duplicate entries. All that is necessary when a hit matches the input string, is to search forward and back through contiguous table entries to find all identical strings. Then the indices of the first and last matching entries can be returned. It would also be quite straightforward to develop versions of binlist-sort to sort lists of integers or reals, or of instances based upon the contents of any given instance attribute. As usual, these extensions are left as an exercise for the reader.

LISTING of binlist_prims_I.r



binlist-sort, 1 (32)
Inputs: InList\0D++
Outputs: OutList
Inputs: list\0D++
Outputs: list
Accept a list of strings, output a new list with the strings ++
sorted in ascending alphabetic order. Duplicate strings are ++
not included in OutList.

binlist-find, 2 (32)
Inputs: TheList; TheSearchString\0D++
Outputs: Found; TheIndex
Inputs: list; string\0D++
Outputs: boolean; integer 
Search TheList for a string equal to TheSearchString. ++
If found Found is TRUE and TheIndex is where it was found. ++
Otherwise Found is FALSE and TheIndex  is where ++
TheSearchString should go in TheList, if you wish to add it.
LISTING of binlist_prims.c

#include “MacTypes.h”
#include “X_includes.h”

/* size of a string and a list differ in interp. & compiled code */

 typedef Nat2 Index;
 typedef Nat4 Index;
#define LT-1
#define EQ0
#define GT1

/* prototypes for functions in this file */

Nat2 U_binlist_2D_sort( C_list *, C_list ** );
Nat2 U_binlist_2D_find( C_list *, C_string *, C_boolean **, C_integer 
** );
void binlist_find( C_list *, C_string *, Bool *, Index * );
Int2 compare_strings( C_string *, C_string * );


/* common table for supplied functions */

ProcPtr * ct;

/* main loads primitives into prograph interpreter’s primitive table 

void main( table )
ProcPtr * table; /* common table */
 move.l a4, -(sp)  /* move A4 on to the stack */
 move.l a0, a4   /* move address of code res. to A4 */
 ct = table;/* set up common table for shared functions */
 AddPrimitive( 1, 0x0101, PF_USER, 0, &U_binlist_2D_sort );
 AddPrimitive( 2, 0x0202, PF_USER, 0, &U_binlist_2D_find );

 asm { move.l (sp)+, a4 }/* move saved value back into A4 */


/* input list of strings, output sorted list of unique strings */

Nat2 U_binlist_2D_sort( inList, outList )
C_list *inList;  /* unsorted list */
C_list **outList;/* new sorted list */
Nat2 inarity;    /* number of inputs */
Nat2 outarity;   /* number of outputs */
register Index size; /* size of unsorted list */
register Index index;/* index in unsorted list */
Index sortIndex; /* index in sorted list */
C_string *curString; /* current string of inList */
C_list *sortList;/* the sorted list */
Bool found; /* TRUE if string found */

 GETARITY( inarity, outarity )

 if ( inarity != 1 || outarity != 1 )

 if ( !IsType( (C_object *)inList, C_LIST )  )
 return PRIMERR_TYPE + 1;

 sortList = MakeC_list( 0 );
 for ( index = 0, size = (**inList).length; index < size; index++ )
 curString = (C_string *)((**inList).data[index]);
 if ( !IsType( (C_object *) curString, C_STRING ) )
 DecUse( sortList );
 return PRIMERR_TYPE + 1;

 binlist_find( sortList, curString, &found, &sortIndex );
 if ( found == FALSE )
 ListInsertSlot( sortList, sortIndex, TRUE, (C_object *)curString );
 *outList = sortList;
 return PCF_TRUE;

/* primitive to find string in alpha sorted list by matching string */
Nat2 U_binlist_2D_find( input1, input2, output1, output2 )
C_list *input1;  /* sorted list of strings */
C_string *input2;/* string for key comparison */
C_boolean **output1; /* boolean is TRUE if found */
C_integer **output2; /* index found or new position */
Nat2 inarity;    /* number of inputs */
Nat2 outarity;   /* number of outputs */
Bool found; /* TRUE if found */
Index index;/* index found or new position */

 GETARITY( inarity, outarity );
 if ( inarity != 2 || outarity != 2 )

 if ( !IsType( (C_object *) input1, C_LIST )  )
 return PRIMERR_TYPE + 1;

 if ( !IsType( (C_object *) input2, C_STRING )  )
 return PRIMERR_TYPE + 2;
 binlist_find( input1, input2, &found, &index );
 *output1 = MakeC_boolean( found );
 *output2 = MakeC_integer( (Nat4)index + 1 );
 return PCF_TRUE;

/* find string in alpha sorted table by matching string */

void binlist_find( list, compString, found, index )
C_list *list;    /* sorted list */
C_string *compString;/* string for key comparison */
Bool *found;/* TRUE if found */
Index *index;    /* index found or index to put at */
register Int4 hit; /* table hit point */
register Int4 lo;/* table low bound */
register Int4 hi;/* table high bound */
register C_string **table;/* pointer to start of table */
register Bool compare;    /* result of string comparison */

 table = (C_string **)((**list).data);
 hi = (Int4)((**list).length) - 1;
 lo = 0;
 while ( lo <= hi )
 hit = ( lo + hi) >> 1;
 compare = compare_strings( compString, table[hit] );
 if ( compare == LT )
 hi = hit - 1;
 else if ( compare == GT )
 lo = hit + 1;
 *index = hit;
 *found = TRUE;
 *index = lo;
 *found = FALSE;

/* compare_strings - compare two strings. return LT -1, EQ 0, GT 1  */
Int2 compare_strings( string1, string2 )
C_string *string1; /* first string */
C_string *string2; /* second string */
register Index ind1; /* length of 1st string */
register Index ind2; /* length of 2nd string */
register Nat1 *ptr1; /* pointer into 1st string */
register Nat1 *ptr2; /* pointer into 2nd string */

 for( ind1 = (**string1).length, 
  ind2 = (**string2).length,
  ptr1 = (**string1).text, 
  ptr2 = (**string2).text ;
  ind1 && ind2 ; 
  ind1--, ind2--, ptr1++, ptr2++ )
 if ( *ptr1 < *ptr2 )
 return LT;
 else if ( *ptr1 > *ptr2 )
 return GT;
 if ( ind1 )
 return GT;
 else if ( ind2 )
 return LT;
 return EQ;


Community Search:
MacTech Search:

Software Updates via MacUpdate

Art Text 3.1 - $49.99
Art Text is graphic design software to create stunning illustrations, such as badges, flyers, logos, social headers and icons, text mockups, website graphics and buttons, picture captions, word art,... Read more
AirRadar 3.1.9 - $9.95
With AirRadar, scanning for wireless networks is now easier and more personalized! It allows you to scan for open networks and tag them as favourites or filter them out. View detailed network... Read more
Alarm Clock Pro 10.2.5 - $19.95
Alarm Clock Pro isn't just an ordinary alarm clock. Use it to wake you up in the morning, send and compose e-mails, remind you of appointments, randomize the iTunes selection, control an internet... Read more
MacCleanse 5.1.6 - $29.95
MacCleanse is the product of thousands of hours of intense research and development. It meticulously scans all of the nooks and crannies of a computer for unnecessary junk that can take up huge... Read more
Apple macOS Sierra 10.12.1 - The latest...
With Apple macOS Sierra, Siri makes its debut on Mac, with new features designed just for the desktop. Your Mac works with iCloud and your Apple devices in smart new ways, and intelligent... Read more
Backblaze - Online backup serv...
Backblaze is an online backup service designed from the ground-up for the Mac. With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Apple Safari 10.0.1 - Apple's Web b...
Note: The direct download link is currently unavailable. It is available in the OS X 10.11.6 release, as well as in the Apple Security Updates. Apple Safari is Apple's web browser that comes with OS... Read more
Postbox 5.0.5 - Powerful and flexible em...
Postbox is a new email application that helps you organize your work life and get stuff done. It has all the elegance and simplicity of Apple Mail, but with more power and flexibility to manage even... Read more
Opera 40.0.2308.90 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Hazel 4.0.7 - Create rules for organizin...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a familiar... Read more

Latest Forum Discussions

See All

WitchSpring2 (Games)
WitchSpring2 1.27 Device: iOS Universal Category: Games Price: $3.99, Version: 1.27 (iTunes) Description: This is the story of Luna, the Moonlight Witch as she sets out into the world. This is a sequel to Witch Spring. Witch Spring 2... | Read more »
4 popular apps getting a Halloween makeo...
'Tis the season for all things spooky. So much, so, in fact, that even apps are getting into the spirt of things, dressing up in costume and spreading jack o' lanterns all about the place. These updates bring frightening new character skins, scary... | Read more »
Pokémon GO celebrates Halloween with can...
The folks behind Pokémon GO have some exciting things planned for their Halloween celebration, the first in-game event since it launched back in July. Starting October 26 and ending on November 1, trainers will be running into large numbers of... | Read more »
Best Fiends Forever Guide: How to collec...
The fiendship in Seriously's hit Best Fiends has been upgraded this time around in Best Fiends Forever. It’s a fast-paced clicker with lots of color and style--kind of reminiscent of a ‘90s animal mascot game like Crash Bandicoot. The game... | Read more »
5 apps for the budding mixologist
Creating your own cocktails is something of an art form, requiring a knack for unique tastes and devising interesting combinations. It's easy to get started right in your own kitchen, though, even if you're a complete beginner. Try using one of... | Read more »
5 mobile strategy games to try when you...
Strategy enthusiasts everywhere are celebrating the release of Civilization VI this week, and so far everyone seems pretty satisfied with the first full release in the series since 2010. The series has always been about ultra-addictive gameplay... | Read more »
Popclaire talk to us about why The Virus...
Humanity has succumbed to a virus that’s spread throughout the world. Now the dead have risen with a hunger for human flesh, and all that remain are a few survivors. One of those survivors has just called you for help. That’s the plot in POPCLAIRE’... | Read more »
Oceans & Empires preview build sets...
Hugely ambitious sea battler Oceans & Empires is available to play in preview form now on Google Play - but download it quickly, as it’s setting sail away in just a few days. [Read more] | Read more »
Rusty Lake: Roots (Games)
Rusty Lake: Roots 1.1.4 Device: iOS Universal Category: Games Price: $2.99, Version: 1.1.4 (iTunes) Description: James Vanderboom's life drastically changes when he plants a special seed in the garden of the house he has inherited.... | Read more »
Flippy Bottle Extreme! and 3 other physi...
Flippy Bottle Extreme! takes on the bottle flipping craze with a bunch of increasingly tricky physics platforming puzzles. It's difficult and highly frustrating, but also addictive. When you begin to master the game, the sense of achievement is... | Read more »

Price Scanner via

11-inch MacBook Airs on sale for up to $120 o...
Newegg has 11″ MacBook Airs on sale for up to $120 off MSRP. Shipping is free: - 11″ 1.6GHz/128GB MacBook Air: $799.99 $100 off MSRP - 11″ 1.6GHz/256GB MacBook Air: $979 $120 off MSRP Read more
Up to $300 off Macs, $20 off iPads with Apple...
Purchase a new Mac or iPad using Apple’s Education Store and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free, and... Read more
Apple’s Thursday “Hello Again” Event A Largel...
KGI Securities analyst Ming-Chi Kuo, who has a strong record of Apple hardware prediction accuracy, forecasts in a new note to investors released late last week that a long-overdue redo of the... Read more
12-inch Retina MacBooks on sale for $100 off...
Amazon has 2016 12″ Apple Retina MacBooks on sale for $100 off MSRP. Shipping is free: - 12″ 1.1GHz Silver Retina MacBook: $1199.99 $100 off MSRP - 12″ 1.1GHz Gold Retina MacBook: $1199.99 $100 off... Read more
Save up to $600 with Apple refurbished Mac Pr...
Apple has Certified Refurbished Mac Pros available for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The following... Read more
PixelStyle Inexpensive Photo Editor For Mac W...
PixelStyle is an all-in-one Mac Photo Editor with a huge range of high-end filters including lighting, blurs, distortions, tilt-shift, shadows, glows and so forth. PixelStyle Photo Editor for Mac... Read more
13-inch MacBook Airs on sale for $100-$140 of...
B&H has 13″ MacBook Airs on sale for $100-$140 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 13″ 1.6GHz/128GB MacBook Air (sku MMGF2LL/A): $899 $100 off... Read more
2.8GHz Mac mini available for $988, includes...
Adorama has the 2.8GHz Mac mini available for $988, $11 off MSRP, including a free copy of Apple’s 3-Year AppleCare Protection Plan. Shipping is free, and Adorama charges sales tax in NY & NJ... Read more
21-inch 3.1GHz 4K on sale for $1379, $120 off...
Adorama has the 21″ 3.1GHz 4K iMac on sale $1379.99. Shipping is free, and Adorama charges NY & NJ sales tax only. Their price is $120 off MSRP. To purchase an iMac at this price, you must first... Read more
Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more

Jobs Board

*Apple* Retail - Multiple Positions- Towson,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Software Engineering Intern: Integration / QA...
Job Summary Apple is currently seeking enthusiastic interns who can work full-time for a minimum of 12-weeks between Fall 2015 and Summer 2016. Our software Read more
Software Engineering Intern: Frameworks at *...
Job Summary Apple is currently seeking enthusiastic interns who can work full-time for a minimum of 12-weeks between Fall 2015 and Summer 2016. Our software Read more
*Apple* Retail - Multiple Positions- Nashua,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions- Napervi...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.