Table of Contents

Manual

Determine Whether a String Contains Digits

The wasStringContainsDigits returns 1 in case the supplied string contains digits or 0 otherwise.

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringContainsDigits: procedure /* True if string contains digits. */
    Parse ARG String
    Do i=0 To 9
        If Pos(i, String) ~= 0 Then 
            Return 1
    End
Return 0

An example call is the following:

If wasStringContainsDigits("V2.3") ~= 1 Then
    Do
        Say "The string does not contain digits..."
    End

Generate UUIDv4

The wasGenerateUUIDv4 function takes as input a seed and generates an UUIDv4:

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasGenerateUUIDv4: procedure /* Generates a version 4 UUID. */
    Parse ARG Seed
    UUID = ''
    Do i = 0 To 35
        Select
            When i = 8 | i = 13 | i = 18 | i = 23 Then
                UUID = UUID || '-'
            When i = 14 Then
                UUID = UUID || '4'
            When i = 19 Then
                UUID = UUID || SubStr('89AB', Random(1, 4, Seed))
            Otherwise
                UUID = UUID || D2X(Random(0, 15, Seed))
        End
    End
Return UUID

An example call is the following:

Result = wasGenerateUUIDv4(Time(SECONDS))
Say 'Generated UUID: ' Result

Map Preserving Quicksort

Given two sets of words, where the first set consists of numbers and the second set consists of strings, the wasDualQuicksort function will sort by the numbers while preserving the relative indices between the first set and the second set.

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasDualQuicksort: procedure /* Sorts by the first argument associatively */
    Parse ARG Score,Names
 
    Count = Words(Score)
    If Count <= 1 Then Return Space(Score' 'Names, 1)
 
    Mid = Trunc(Count/2)
 
    PivotS = SubWord(Score, Mid, 1)
    PivotN = SubWord(Names, Mid, 1)
 
    Score = Space(DelWord(Score, Mid, 1), 1)
    Names = Space(DelWord(Names, Mid, 1), 1)
 
    LessS = ''
    LessN = ''
    MoreS = ''
    MoreN = ''
 
    Count = Words(Score)
    Do i = 1 To Count
        Select
            When SubWord(Score, i, 1) > PivotS Then
                Do
                    LessS = SubWord(Score, i, 1)' 'LessS
                    LessN = SubWord(Names, i, 1)' 'LessN
                End
            Otherwise
                Do
                    MoreS = SubWord(Score, i, 1)' 'MoreS
                    MoreN = SubWord(Names, i, 1)' 'MoreN
                End
        End
    End
Return Strip(wasDualQuicksort(LessS, LessN)' 'PivotS' 'PivotN' 'wasDualQuicksort(MoreS, MoreN))

For example, a call such as:

Score = '1.5 8 3 17'
Names = 'Gogo Moco Coco Jun'
 
Say wasDualQuicksort(Score, Names)

will produce the output:

17 Jun 8 Moco 3 Coco 1.5 Gogo

Replace Sub-String

When you have a several character to replace in a string, the TRANSLATE built-in function works fine:

Say Translate('hellos/world', ' ', '/') 

will output:

hellos world

However, when you want to replace a sequence of characters, then the wasReplaceSubString does this for you:

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasReplaceSubString: procedure /* Replace a sub-string if found. */
    Parse ARG String,Search,Insert
    If Length(String) = 0 Then Return ''
    i = Pos(Search,String);
    If i = 0 Then Return String
    Less = Left(String, i - 1)
    More = SubStr(String, i + Length(Search))
Return Less || Insert || wasReplaceSubString(More,Search,Insert)

Token-Delimited Words

A series of utility functions used by Wizardry and Steamworks in various functions that emulate arrays based on a token sequence that delimits each element.

Compare Two Strings

wasStringCompare compares two strings by the ASCII values of each character:

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringCompare: procedure /* Compares two strings by ASCII values.     */
    Parse ARG One,Two
 
    Select
        When One = '' & Two ~= '' Then Return 1
        When One ~= '' & Two = '' Then Return -1
        When One = '' & Two = '' Then Return 0
        Otherwise Nop
    End
 
    a = Upper(Left(One, 1));
    b = Upper(Left(Two, 1));
 
    If C2D(a) < C2D(b) Then Return 1
    If C2D(a) > C2D(b) Then Return -1
 
    a = Right(One, Length(One)-1)
    b = Right(Two, Length(Two)-1)
 
Return wasStringCompare(a, b)

Quicksort

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringQuicksort: procedure /* Sorts strings separated by token.       */
    Parse ARG String,Lambda
 
    Count = Words(String)
 
    If Count <= 1 Then Return String
 
    Middle = Trunc(Count/2)
 
    Pivot = SubWord(String,Middle,1)
    String = DelWord(String,Middle,1)
    Count = Count - 1
 
    Less = ''
    More = ''
 
    Do i = 1 To Count
        Word = SubWord(String,i,1)
        Compare = wasStringCompare(Word, Pivot)
        Select
            When Compare = 1 Then
                Select
                    When Less ~= '' Then 
                        Less = Less||' '||Word
                    Otherwise Less = Word
                End
            Otherwise
                Select
                    When More ~= '' Then 
                        More = More||' '||Word
                    Otherwise More = Word
                End
        End
 
    End
 
    Interpret Lambda
 
    Less = wasStringQuicksort(Less,Lambda)
    More = wasStringQuicksort(More,Lambda)
 
    If Less = '' & More ~= '' Then Return Pivot||' '||More
    If Less ~= '' & More = '' Then Return Less||' '||Pivot
 
Return Less||' ' ||Pivot||' '||More

Binary Search

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasBinarySearch: procedure /* Finds a string within a string by token.   */
    Parse ARG String,Search,Lambda
 
    Count = Words(String)
 
    If Count <= 1 Then Return String
 
    Pivot = SubWord(String,Trunc(Count/2),1)
    Index = Find(String,Pivot)
 
    Compare = wasStringCompare(Pivot, Search)
    Select
        When Compare < 0 Then
            Do
                Less = ''
                Do i = 1 To Index - 1
                    Word = SubWord(String,i,1)
                    Select
                        When Less ~= '' Then Less = Less||' ' ||Word
                        Otherwise Less = Word
                    End
                End
                String = Less
            End
        When Compare > 0 Then
            Do
                More = ''
                Do i = Index + 1 To Count
                    Word = SubWord(String,i,1)
                    Select
                        When More ~= '' Then More = More||' ' ||Word
                        Otherwise More = Word
                    End
                End
                String = More
            End
        Otherwise Return Pivot
    End
 
    Interpret Lambda
 
Return wasBinarySearch(String,Search,Lambda)

Intersection of Two Sorted Sets

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasSortedSetsIntersect: procedure /* Intersects two sorted sets.         */
    Parse ARG A,B,Lambda
 
    If A = '' | B = '' Then Return ''
 
    Parse VAR A AHead ' ' ATail
    Parse VAR B BHead ' ' BTail
 
    Interpret Lambda
 
    If AHead ~= BHead Then
        Do
            Compare = wasStringCompare(AHead, BHead)
            If Compare < 0 Then
                Return wasSortedSetsIntersect(A, BTail, Lambda)
            If Compare > 0 Then
                Return wasSortedSetsIntersect(ATail, B, Lambda)
        End
 
    Look = wasSortedSetsIntersect(ATail, BTail, Lambda)
    If Look ~= '' Then Return AHead||' '||Look
Return AHead

Subtract Two Sorted Sets

Given two sets of space-delimited words:

SetA = '2345 Aweet Bram Clipboards Disk.info ENV FMS Gram Ham Jam kketc sdiierr SSFEerr ssqqq T Zam'
SetB = 'Disk.info ENV kketc'

The wasSortedSetsSubtract function subtracts SetB from SetA, resulting in:

2345 Aweet Bram Clipboards FMS Gram Ham Jam sdiierr SSFEerr ssqqq T Zam
/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasSortedSetsSubtract: procedure /* Subtract two sorted sets: A \ B      */
    Parse ARG A,B,Lambda
 
    If A = '' Then Return ''
 
    Parse VAR A AHead ' ' ATail
    Parse VAR B BHead ' ' BTail
 
    Interpret Lambda
 
    Select
        When AHead ~= BHead Then Nop
        Otherwise Return wasSortedSetsSubtract(ATail, BTail, Lambda)
    End
 
Return AHead' 'wasSortedSetsSubtract(ATail, B, Lambda)

Replace a Word

Since ARexx does not allow replacing a word in a sequence of words, the following trick should work fine:

Search = 'Clipboards ENV FMS T Disk.info Test Bram Gram Ham Jam Zam Aweet 2345 Pooww SSFEerr sdiierr ssqqq kketc'
 
Index = Find(Search, 'FMS')
Search = SubWord(Search, 1, Index - 1)||' '||'NewWord'||' '||SubWord(Search, Index + 1)
 
Say Search

The snippet searches the string of words Search for the word FMS and grabs the word index. After that, the Search string of words is rebuilt as a composition of all words before the word FMS, the new word to be inserted NewWord and the rest of the remaining words.

Insertion Sort

Given a set of words, the wasStringInsertSort function will sort the words and return the resulting set.

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringInsertSort: procedure /* Sorts strings of words.                */
    Parse ARG String
    Count = Words(String)
    Do i = 2 To Count
        Select = SubWord(String, i, 1)
        j = i
        Do While j > 1
            Previous = SubWord(String, j - 1, 1)
            If wasStringCompare(Previous, Select) ~= -1 Then Leave
            String = SubWord(String, 1, j - 1)||' '||Previous||' '||SubWord(String, j + 1)
            j = j - 1
        End
        If SubWord(String, 1, j) ~= Select Then
            Do
                String = SubWord(String, 1, j - 1)||' '||Select||' '||SubWord(String, j + 1)
            End
    End
Return String

Generate Random Hexadecimal String

The wasRandomHexString function returns a random hexadecimal string of a specified size:

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasRandomHexString: procedure /* Generates a random hexadecimal string.  */
    Parse ARG Seed,Size
    If Size = 0 Then Return ''
    Random = Random(0, 15, Size + Seed)
Return D2X(Random)||wasRandomHexString(Random, Size - 1)

For example:

Say wasRandomHexString(TIME(SECONDS), 5)

will output a hexadecimal string of length 5.

Merge Sort

Based on the string comparer we can write a merge-sort algorithm for ARexx:

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringMergeSortMerge: procedure /* Compares and merges partitions.    */
    Parse ARG L,R,Lambda
 
    If Words(L) = 0 | Words(R) = 0 Then Return L||' '||R
 
    Parse VAR L LHead ' ' LTail
    Parse VAR R RHead ' ' RTail
 
    Interpret Lambda
 
    If wasStringCompare(LHead, RHead) < 1 Then
        Return RHead||' '||wasStringMergeSortMerge(L, RTail, Lambda)
 
Return LHead||' '||wasStringMergeSortMerge(LTail, R, Lambda)
 
/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasStringMergeSort: procedure /* Merge sort a set of words.              */
    Parse ARG String,Lambda
 
    Count = Words(String)
 
    If Count <= 1 Then Return String
 
    Middle = Trunc(Count/2)
 
    Interpret Lambda
 
    L = wasStringMergeSort(SubWord(String, 1, Middle), Lambda)
    R = wasStringMergeSort(SubWord(String, Middle+1), Lambda)
 
Return wasStringMergeSortMerge(L, R, Lambda)

Given some input words, we can call wasStringMergeSort on the set of input words:

Search = 'Clipboards ENV FMS T Disk.info Test Bram Gram Ham Jam Zam Aweet 2345 Pooww SSFEerr sdiierr ssqqq kketc'
Say wasStringMergeSort(Search)

which produces the result:

2345 Aweet Bram  Clipboards  Disk.info  ENV  FMS  Gram  Ham Jam  kketc Pooww  sdiierr  SSFEerr  ssqqq T  Test  Zam

Encode and Decode To and From Base64

Following conversations with Stefan Haubenthal, here are some functions that encode to Base64 strings and decode Base64 strings. Note that these functions do not account for the CR LF that must be added at the 76th character in order to conform to MIME however once you have the encoded strings it is easy to just split them on the 76th character and insert the CR LF yourself.

Here is an example call of both functions:

String = 'Good day!'
 
Encoded = wasBase64Encode(String)
Say 'Encoded: 'Encoded
Decoded = wasBase64Decode(Encoded)
Say 'Decoded: 'Decoded

Encode

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasBase64Encode: procedure /* Encodes a string into Base64.              */
    Parse ARG String
 
    If Length(String) = 0 Then Return ''
 
    Pad = ""
    Do While Length(String) // 3 ~= 0
        String = String||X2C(0)
        Pad = Pad||'='
    End
 
    Count = Length(String)
    BinaryString = ''
    Do i = 1 To Count
        BinaryString = BinaryString||C2B(SubStr(String, i, 1))
    End
 
    b64 =,
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
 
    Count = Length(BinaryString)
    Result = ''
    Do i = 1 To Count By 6
        Result = Result||,
            SubStr(b64, C2D(B2C("00"SubStr(BinaryString, i, 6))) + 1, 1)
    End
 
Return SubStr(Result, 1, Length(Result) - Length(Pad))||Pad

Decode

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasBase64Decode: procedure /* Decodes a Base64 string.                   */
    Parse ARG String
 
    Count = Length(String)
 
    If Count = 0 Then Return ''
 
    Pad = ''
    Select
        When SubStr(String, Count-1, 2) = '==' Then Pad = 'AA'
        When SubStr(String, Count, 1) = '=' Then Pad = 'A'
        Otherwise Nop
    End
 
    String = SubStr(String, 1, Length(String) - Length(Pad))||Pad
 
    b64 =,
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
 
    Count = Length(String)
    Result = ''
    Do i = 1 To Count By 4
        b1 = Pos(SubStr(String, i, 1), b64) - 1
        b2 = Pos(SubStr(String, i+1, 1), b64) - 1
        b3 = Pos(SubStr(String, i+2, 1), b64) - 1
        b4 = Pos(SubStr(String, i+3, 1), b64) - 1
        r1 = b1 * 4 + b2 % 16
        r2 = 16 * (b2 // 16) + (b3 % 4)
        r3 = 64 * (b3 // 4) + b4
        Result = Result||D2C(r1)||D2C(r2)||D2C(r3)
    End
 
Return SubStr(Result, 1, Length(Result) - Length(Pad))

Generate Sequential Strings

The wasGenerateStringSequence function generates string sequences starting with a string and provided a number of increments to perform. The starting string can be alpha-numeric.

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasGenerateStringSequence: procedure /* Generate a string sequence.      */
	Parse ARG First,Count,X
 
	If Count = 0 Then Return ''
 
	If Length(X) ~= 1 Then 
	    Return First||" "||wasGenerateStringSequence(First, Count - 1, 1)
 
	Result = ''
	c = 1
	Length = Length(First)
	Do While Length ~= 0
	    x = SubStr(First, Length, 1)
	    Select
	        When DataType(x) = 'NUM' Then
	            Do
	                Sum = x + c
	                Select
            	        When Sum > 9 Then
            	            Do
            	                Result = 0||Result
            	                c = 1
            	            End
            	        Otherwise
            	            Do
            	                Result = Sum||Result
            	                c = 0
            	            End
            	    End
	            End
	        Otherwise
	            Do
	                Sum = D2C(C2D(x) + c)
	                Select
	                    When Sum > 'z' Then
	                        Do
	                            Result = 'a'||Result
	                            c = 1
	                        End
	                    Otherwise
	                        Do
	                            Result = Sum||Result
	                            c = 0
	                        End
	                End
	            End
	    End
	    Length = Length - 1
	End
 
	If c ~= 0 Then Result = c||Result
 
	If Count - 1 = 0 Then Return Result
 
Return Result||" "||wasGenerateStringSequence(Result, Count - 1, 1)

For example, a call such as:

Say wasGenerateStringSequence("0yz8", 3)

Will output the following three sequential words:

0yz8 0yz9 0za0

The generator follows the rules:

Here is another example call that demonstrates the extension of the string by one:

Say wasGenerateStringSequence("9z", 30)

that outputs the words:

9z 10a 10b 10c 10d 10e 10f 10g 10h 10i 10j 10k 10l 10m 10n 10o 10p 10q 10r 10s 10t 10u 10v 10w 10x 10y 10z 11a

Bit-Shift Operations

Shift Left

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasShiftLeft: procedure /* logical shift left of c by n bits             */
    Parse ARG c,n
Return Right(D2C(C2D(c) * 2 ** n), 1)

Shift Right

/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
wasShiftRight: procedure /* logical shift right of c by n bits           */
    Parse ARG c,n
Return D2C(C2D(c) % 2 ** n)