diff --git a/README.md b/README.md
index 0134743..e11f80f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# SAS_PACKAGES - a SAS Packages Framework and Repository
+---
+
## Intro:
A **SAS package** is an automatically generated, single, stand alone *zip* file containing organised and ordered code structures, created by the developer and extended with additional automatically generated "driving" files (i.e. description, metadata, load, unload, and help files).
@@ -18,6 +20,8 @@ Short description of the SAS Packages Framework macros can be found [here](https
- [SAS Global Forum 2020 V.E.](https://www.youtube.com/watch?v=qCkb-bx0Dv8&t=0s "SGF2020") (April 2020)
- [Sasensei International Dojo](https://www.youtube.com/watch?v=BFhdUBQgjYQ&t=0s "SID no. 1") (April 2020)
- [SAS dla Administratorów i Praktyków 2020](https://www.youtube.com/watch?v=mXuep2k48Z8&feature=youtu.be&t=0s "SASAiP2020") (October 2020, in Polish)
+ - [Boston Area SAS Users Group webinar](https://us02web.zoom.us/rec/share/p6ZpCsvc5YZDQGpLOOLOB4zyNGA4vjfjJcNhwaGQ7jKKR00Z_bmeCcBkcwkut6Pr.Q6UoueYAOcv6dPQf "BASUG") (November 2020)
+---
### The User:
To use a package:
@@ -51,20 +55,32 @@ filename SPFinit url "https://raw.githubusercontent.com/yabwon/SAS_PACKAGES/main
```
[**Workshop video for the User**](https://youtu.be/qX_-HJ76g8Y)\[May 6th, 2020\] [a bit outdated but gives the idea how it works]
-
+
+---
+
### The Developer:
To create your own package:
- Read the [**`SAS(r) packages - the way to share (a how to)- Paper 4725-2020 - extended.pdf`**](https://github.com/yabwon/SAS_PACKAGES/blob/main/SPF/Documentation/SAS(r)%20packages%20-%20the%20way%20to%20share%20(a%20how%20to)-%20Paper%204725-2020%20-%20extended.pdf "SAS packages - the way to share") to learn more details.
- Download and use the `SPFinit.sas` file (the SAS Packages Framework), the part of the framework required for *testing* is there too.
+---
+
#### If you have any questions, suggestions, or ideas do not hesitate to contact me!
-**Update**\[June 3rd, 2020\]**:** `%installPackage()` **macro is available**. The `%installPackage()` macro is embedded in the `loadpackage.sas` part of the framework.
-
-**Update**\[June 10th, 2020\]**:** To see help info about framework macros and their parameters just run: `%generatePackage()`, `%installPackage()`, `%helpPackage()`, `%loadPackage()`, and `%unloadPackage()` with empty parameter list.
-
+---
+
+**Update**\[October 15th, 2020\]**:** `%previewPackage()` **macro is available**.
+
+**Update**\[September 11th, 2020\]**:** ` %loadPackageS()` and `%verifyPackage()` **macros are available**.
+
**Update**\[July 30th, 2020\]**:** All components of SAS Packages Framework are now in one file `SPFinit.sas` (located in the `./SPF` directory). Documentation moved to `./SPF/Documentation` directory. Packages zip files moved to `./packages` directory.
+**Update**\[June 10th, 2020\]**:** To see help info about framework macros and their parameters just run: `%generatePackage()`, `%installPackage()`, `%helpPackage()`, `%loadPackage()`, and `%unloadPackage()` with empty parameter list.
+
+**Update**\[June 3rd, 2020\]**:** `%installPackage()` **macro is available**. The `%installPackage()` macro is embedded in the `loadpackage.sas` part of the framework.
+
+---
+
## Where the SAS Packages Framework is used:
This is a list of locations where the SAS Packages Framework is used. If you want to share that you are using SPF let me know and I'll update the list.
@@ -73,6 +89,8 @@ The List:
- Poland
- Warsaw
+---
+
## Available packages:
Currently the following packages are available (see the `./packages` directory):
@@ -92,10 +110,11 @@ SHA256 digest for MacroCore: A23C29529F3CE7D0C8BEE9545C5D22D5B5594907547374A5135
[Documentation for MacroCore](https://core.sasjs.io "Documentation for MacroCore")
-- **DFA** (Dynamic Function Arrays)\[0.2\], contains set of macros and FCMP functions which implement: a dynamically allocated array, a stack, a fifo queue, an ordered stack, and a priority queue, run `%helpPackage(DFA,createDFArray)` to find examples.
+- **DFA** (Dynamic Function Arrays)\[0.3\], contains set of macros and FCMP functions which implement: a dynamically allocated array, a stack, a fifo queue, an ordered stack, and a priority queue, run `%helpPackage(DFA,createDFArray)` to find examples.
-SHA256 digest for DFA: C795736F55B3C6EFBEF2E82362694EB017D37C54E6AEC3EB0F6F813F69F54B5F
+SHA256 digest for DFA: 1FC8D030D576C33F1B5DEB27E17534946209BC148D57A1357CA025ED1E69AEB8
+[Documentation for DFA](https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/dfa.md "Documentation for DFA")
- **macroArray**\[0.7\], implementation of an array concept in a macrolanguage, e.g.
```
@@ -121,7 +140,7 @@ SHA256 digest for macroArray: 0F1B985E2FC34C91D2A3BD237DC294502A76913B71266D7670
[Documentation for macroArray](https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/macroarray.md "Documentation for macroArray")
-- **BasePlus**\[0.9\] adds a bunch of functionalities I am missing in BASE SAS, such as:
+- **BasePlus**\[0.99\] adds a bunch of functionalities I am missing in BASE SAS, such as:
```
call arrMissToRight(myArray);
call arrFillMiss(17, myArray);
@@ -135,7 +154,7 @@ format x bool.;
%put %getVars(sashelp.class, pattern = ght$, sep = +, varRange = _numeric_);
```
-SHA256 digest for BasePlus: B25A3992B6FCD13528BEE462B3ADD0F5A6D15E607A6DABAA984CA66B0AD69415
+SHA256 digest for BasePlus: 7933E6BCFDCA7C04EAAC537773574799759007A5D2AED639E86CF4EA631F1351
[Documentation for BasePlus](https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md "Documentation for BasePlus")
diff --git a/packages/README.md b/packages/README.md
index 4c3860f..e2d3a10 100644
--- a/packages/README.md
+++ b/packages/README.md
@@ -32,7 +32,7 @@ SHA256 digest for MacroCore: A23C29529F3CE7D0C8BEE9545C5D22D5B5594907547374A5135
---
-- **DFA** (Dynamic Function Arrays)\[0.2\], contains set of macros and FCMP functions which implement: a dynamically allocated array, a stack, a fifo queue, an ordered stack, and a priority queue, run `%helpPackage(DFA,createDFArray)` to find examples.
+- **DFA** (Dynamic Function Arrays)\[0.3\], contains set of macros and FCMP functions which implement: a dynamically allocated array, a stack, a fifo queue, an ordered stack, and a priority queue, run `%helpPackage(DFA,createDFArray)` to find examples.
```
%createDFArray(ArrDynamic, resizefactor=17);
@@ -59,7 +59,9 @@ data _null_;
end;
run;
```
-SHA256 digest for DFA: C795736F55B3C6EFBEF2E82362694EB017D37C54E6AEC3EB0F6F813F69F54B5F
+SHA256 digest for DFA: 1FC8D030D576C33F1B5DEB27E17534946209BC148D57A1357CA025ED1E69AEB8
+
+[Documentation for DFA](https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/dfa.md "Documentation for DFA")
---
@@ -88,7 +90,7 @@ SHA256 digest for macroArray: 0F1B985E2FC34C91D2A3BD237DC294502A76913B71266D7670
---
-- **BasePlus**\[0.9\] adds a bunch of functionalities I am missing in BASE SAS, such as:
+- **BasePlus**\[0.99\] adds a bunch of functionalities I am missing in BASE SAS, such as:
```
call arrMissToRight(myArray);
call arrFillMiss(17, myArray);
@@ -102,7 +104,7 @@ format x bool.;
%put %getVars(sashelp.class, pattern = ght$, sep = +, varRange = _numeric_);
```
-SHA256 digest for BasePlus: B25A3992B6FCD13528BEE462B3ADD0F5A6D15E607A6DABAA984CA66B0AD69415
+SHA256 digest for BasePlus: 7933E6BCFDCA7C04EAAC537773574799759007A5D2AED639E86CF4EA631F1351
[Documentation for BasePlus](https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md "Documentation for BasePlus")
diff --git a/packages/SHA256_for_packages.txt b/packages/SHA256_for_packages.txt
index a77bb0e..1a8208a 100644
--- a/packages/SHA256_for_packages.txt
+++ b/packages/SHA256_for_packages.txt
@@ -1,3 +1,9 @@
+/* 20201202 */
+BasePlus: 7933E6BCFDCA7C04EAAC537773574799759007A5D2AED639E86CF4EA631F1351
+
+/* 20201130 */
+DFA: 1FC8D030D576C33F1B5DEB27E17534946209BC148D57A1357CA025ED1E69AEB8
+
/* 20201115 */
BasePlus: B25A3992B6FCD13528BEE462B3ADD0F5A6D15E607A6DABAA984CA66B0AD69415
DFA: C795736F55B3C6EFBEF2E82362694EB017D37C54E6AEC3EB0F6F813F69F54B5F
diff --git a/packages/baseplus.md b/packages/baseplus.md
index 44a75cb..c4989e8 100644
--- a/packages/baseplus.md
+++ b/packages/baseplus.md
@@ -33,12 +33,20 @@
* [`%dedupListP()` macro](#deduplistp-macro)
* [`%dedupListX()` macro](#deduplistx-macro)
* [`%QdedupListX()` macro](#qdeduplistx-macro)
-
+ * [`brackets.` format](#brackets-format)
+ * [`semicolon.` format](#semicolon-format)
+ * [`bracketsC()` function](#bracketsc-function)
+ * [`bracketsN()` function](#bracketsn-function)
+ * [`semicolonC()` function](#semicolonc-function)
+ * [`semicolonN()` function](#semicolonn-function)
+ * [`%zipEvalf()` macro](#zipevalf-macro)
+ * [`%QzipEvalf()` macro](#qzipevalf-macro)
+
* [License](#license)
---
-# The BasePlus package [ver. 0.9] ###############################################
+# The BasePlus package [ver. 0.99] ###############################################
The **BasePlus** package implements useful
functions and functionalities I miss in the BASE SAS.
@@ -162,47 +170,61 @@ Kudos to all who inspired me to generate this package:
%put *%dedupListS(&list.)*;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+**Example 8**: Zip elements of two space separated list.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let x = %zipEvalf(1 2 3 4 5 6, 2018 2019 2020, argMd=5, function=MDY, format=date11.);
+%put &=x;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
---
Package contains:
- 1. macro deduplistc
- 2. macro deduplistp
- 3. macro deduplists
- 4. macro deduplistx
- 5. macro getvars
- 6. macro qdeduplistx
- 7. macro qgetvars
- 8. macro symdelglobal
- 9. format bool
- 10. format boolz
- 11. format ceil
- 12. format floor
- 13. format int
- 14. functions arrfill
- 15. functions arrfillc
- 16. functions arrmissfill
- 17. functions arrmissfillc
- 18. functions arrmisstoleft
- 19. functions arrmisstoleftc
- 20. functions arrmisstoright
- 21. functions arrmisstorightc
- 22. functions catxfc
- 23. functions catxfi
- 24. functions catxfj
- 25. functions catxfn
- 26. functions deldataset
- 27. proto qsortincbyprocproto
- 28. functions frommissingtonumberbs
- 29. functions fromnumbertomissing
- 30. functions quicksort4notmiss
- 31. functions quicksorthash
- 32. functions quicksorthashsddv
- 33. functions quicksortlight
+1. macro deduplistc
+2. macro deduplistp
+3. macro deduplists
+4. macro deduplistx
+5. macro getvars
+6. macro qdeduplistx
+7. macro qgetvars
+8. macro qzipevalf
+9. macro symdelglobal
+10. macro zipevalf
+11. format bool
+12. format boolz
+13. format ceil
+14. format floor
+15. format int
+16. functions arrfill
+17. functions arrfillc
+18. functions arrmissfill
+19. functions arrmissfillc
+20. functions arrmisstoleft
+21. functions arrmisstoleftc
+22. functions arrmisstoright
+23. functions arrmisstorightc
+24. functions bracketsc
+25. functions bracketsn
+26. functions catxfc
+27. functions catxfi
+28. functions catxfj
+29. functions catxfn
+30. functions deldataset
+31. functions semicolonc
+32. functions semicolonn
+33. format brackets
+34. format semicolon
+35. proto qsortincbyprocproto
+36. functions frommissingtonumberbs
+37. functions fromnumbertomissing
+38. functions quicksort4notmiss
+39. functions quicksorthash
+40. functions quicksorthashsddv
+41. functions quicksortlight
*SAS package generated by generatePackage, version 20201115*
The SHA256 hash digest for package BasePlus:
-`B25A3992B6FCD13528BEE462B3ADD0F5A6D15E607A6DABAA984CA66B0AD69415`
+`7933E6BCFDCA7C04EAAC537773574799759007A5D2AED639E86CF4EA631F1351`
---
# Content description ############################################################################################
@@ -2491,6 +2513,425 @@ XlistXofXxXseparatedXvalues
---
+## >>> `brackets.` format: <<< #######################
+
+The **brackets** format adds brackets around a text or a number.
+Leading and trailing spaces are dropped before adding brackets.
+
+### EXAMPLES AND USECASES: ####################################################
+
+**Example 1.**
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+data _null_;
+ input x;
+ if x < 0 then put x= brackets.;
+ else put x= best32.;
+cards;
+2
+1
+0
+-1
+-2
+;
+run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+
+## >>> `semicolon.` format: <<< #######################
+
+The **semicolon** format adds semicolon after text or number.
+Leading and trailing spaces are dropped before adding semicolon.
+
+### EXAMPLES AND USECASES: ####################################################
+
+**Example 1.**
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+data _null_;
+ x = 1;
+ y = "A";
+ put x= semicolon. y= $semicolon.;
+run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+
+## >>> `bracketsC()` function: <<< #######################
+
+The **bracketsC()** function is internal function used by the *brackets* format.
+Returns character value of length 32767.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+bracketsC(X)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `X` - Character value.
+
+---
+
+## >>> `bracketsN()` function: <<< #######################
+
+The **bracketsN()** function is internal function used by the *brackets* format.
+Returns character value of length 34.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+bracketsN(X)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `X` - Numeric value.
+
+---
+
+## >>> `semicolonC()` function: <<< #######################
+
+The **semicolonC()** function is internal function used by the *semicolon* format.
+Returns character value of length 32767.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+semicolonC(X)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `X` - Character value.
+
+---
+
+## >>> `semicolonN()` function: <<< #######################
+
+The **semicolonN()** function is internal function used by the *semicolon* format.
+Returns character value of length 33.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+semicolonN(X)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `X` - Numeric value.
+
+---
+
+## >>> `%QzipEvalf()` macro: <<< #######################
+
+The zipEvalf() and QzipEvalf() macro functions
+allow to use a function on elements of pair of
+space separated lists.
+
+For two space separated lists of text strings the corresponding
+elements are taken and the macro applies a function, provided by user,
+to calculate result of the function on taken elements.
+
+When one of the lists is shorter then elements are "reused" starting
+from the beginning.
+
+The zipEvalf() returns unquoted value [by %unquote()].
+The QzipEvalf() returns quoted value [by %superq()].
+
+See examples below for the details.
+
+The `%QzipEvalf()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+%QzipEvalf(
+ first
+ ,second
+ <,function=>
+ <,operator=>
+ <,argBf=>
+ <,argMd=>
+ <,argAf=>
+ <,format=>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `first` - *Required*, a space separated list of texts.
+
+2. `second` - *Required*, a space separated list of texts.
+
+* `function = cat` - *Optional*, default value is `cat`,
+ a function which will be applied
+ to corresponding pairs of elements of
+ the first and the second list.
+
+* `operator =` - *Optional*, default value is empty,
+ arithmetic infix operator used with elements
+ the first and the second list. The first
+ list is used on the left side of the operator
+ the second list is used on the right side
+ of the operator.
+
+* `argBf =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *before* elements the first list.
+ If multiple should be comma separated.
+
+* `argMd =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *between* elements the first list and
+ the second list.
+ If multiple should be comma separated.
+
+* `argAf =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *after* elements the second list.
+ If multiple should be comma separated.
+
+* `format=` - *Optional*, default value is empty,
+ indicates a format which should be used
+ to format the result, does not work when
+ the `operator=` is used.
+
+### EXAMPLES AND USECASES: ####################################################
+
+See examples in `%zipEvalf()` help for the details.
+
+---
+
+## >>> `%zipEvalf()` macro: <<< #######################
+
+The zipEvalf() and QzipEvalf() macro functions
+allow to use a function on elements of pair of
+space separated lists.
+
+For two space separated lists of text strings the corresponding
+elements are taken and the macro applies a function, provided by user,
+to calculate result of the function on taken elements.
+
+When one of the lists is shorter then elements are "reused" starting
+from the beginning.
+
+The zipEvalf() returns unquoted value [by %unquote()].
+The QzipEvalf() returns quoted value [by %superq()].
+
+See examples below for the details.
+
+The `%zipEvalf()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~sas
+%zipEvalf(
+ first
+ ,second
+ <,function=>
+ <,operator=>
+ <,argBf=>
+ <,argMd=>
+ <,argAf=>
+ <,format=>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `first` - *Required*, a space separated list of texts.
+
+2. `second` - *Required*, a space separated list of texts.
+
+* `function = cat` - *Optional*, default value is `cat`,
+ a function which will be applied
+ to corresponding pairs of elements of
+ the first and the second list.
+
+* `operator =` - *Optional*, default value is empty,
+ arithmetic infix operator used with elements
+ the first and the second list. The first
+ list is used on the left side of the operator
+ the second list is used on the right side
+ of the operator.
+
+* `argBf =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *before* elements the first list.
+ If multiple should be comma separated.
+
+* `argMd =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *between* elements the first list and
+ the second list.
+ If multiple should be comma separated.
+
+* `argAf =` - *Optional*, default value is empty,
+ arguments of the function inserted
+ *after* elements the second list.
+ If multiple should be comma separated.
+
+* `format=` - *Optional*, default value is empty,
+ indicates a format which should be used
+ to format the result, does not work when
+ the `operator=` is used.
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Simple concatenation of elements:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y);
+%put &=x;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Shorter list is "reused":
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let x = %zipEvalf(1 2 3 4 5 6, a b c);
+%put &=x;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Use of the `operator=`, shorter list is "reused":
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let y = %zipEvalf(1 2 3 4 5 6, 100 200, operator = +);
+%put &=y;
+
+%let z = %zipEvalf(1 2 3 4 5 6 8 9 10, 1 2 3 4 5 6 8 9 10, operator = **);
+%put &=z;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 4.** Format result:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y, format=$upcase.);
+%put &=x;
+
+%put *
+%zipEvalf(
+ ą ż ś ź ę ć ń ó ł
+,Ą Ż Ś Ź Ę Ć Ń Ó Ł
+,format = $brackets.
+)
+*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 5.** Use with macrovariables:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let abc = 10 100 1000;
+%put *
+%zipEvalf(
+%str(1 2 3 4 5 6 7 8 9)
+,&abc.
+,function = sum
+)
+*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 6.** If one of elements is empty:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%put *
+%zipEvalf(
+ abc efg
+,
+)
+*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 7.** Use of the `function=`, shorter list is "reused":
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%put *
+%zipEvalf(
+ a b c
+,efg
+,function = catx
+,argBf = %str(,)
+,format = $brackets.
+)
+*;
+
+%put *
+%zipEvalf(
+ a b c
+,efg
+,function = catx
+,argBf = %str( )
+,format = $upcase.
+)
+*;
+
+%put *
+%zipEvalf(
+ %str(! @ # $ [ ] % ^ & * )
+,1 2 3 4 5 6 7 8 9
+,function = catx
+,argBf = %str( )
+,format = $quote.
+)
+*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 8.** Use inside resolve:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+data _null_;
+z = resolve('
+%zipEvalf(
+ %nrstr(! @ # $ [ ] % ^ & *)
+,1 2 3 4 5 6 7 8 9
+,function = catx
+,argBf = %str(.)
+,format = $quote.
+)');
+put z=;
+run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 9.** Use in data step:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+data test;
+ %zipEvalf(
+ a b c d e f g
+ ,1 2 3 4 5 6 7
+ ,function = catx
+ ,argBf = =
+ ,format = $semicolon.
+ )
+run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 10.** With 9.4M6 hashing() function:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%put %zipEvalf(MD5 SHA1 SHA256 SHA384 SHA512 CRC32, abcd, function = HASHING);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 11.** Use middle argument:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%let x = %zipEvalf(1 2 3 4 5 6, 2020, argMd=5, function=MDY, format=date11.);
+%put &=x;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+
+
+
+
## License ####################################################################
Copyright (c) 2020 Bartosz Jablonski
diff --git a/packages/baseplus.zip b/packages/baseplus.zip
index a67760b..94c186a 100644
Binary files a/packages/baseplus.zip and b/packages/baseplus.zip differ
diff --git a/packages/dfa.md b/packages/dfa.md
new file mode 100644
index 0000000..b01f9dd
--- /dev/null
+++ b/packages/dfa.md
@@ -0,0 +1,1150 @@
+- [The DFA package](#dfa-package)
+- [Content description](#content-description)
+ * [`%createDFArray()` macro](#createdfarray-macro)
+ * [`%createDHArray()` macro](#createdharray-macro)
+ * [`%createDHFifo()` macro](#createdhfifo-macro)
+ * [`%createDHOrdStack()` macro](#createdhordstack-macro)
+ * [`%createDHPrtQueue()` macro](#createdhprtqueue-macro)
+ * [`%createDHStack()` macro](#createdhstack-macro)
+ * [`generateArrays` exec](#createdhprtqueue-exec)
+ * [`generateArrays` clean](#createdhprtqueue-clean)
+
+ * [License](#license)
+
+---
+
+# The DFA package [ver. 0.3] ###############################################
+
+The **DFA** (a.k.a. *Dynamic Function Array*) package implements:
+ - dynamic numeric and character arrays,
+ - dynamic stacks (filo),
+ - dynamic queues (fifo),
+ - dynamic ordered stacks,
+ - priority queues.
+
+The package provides a set of *macros*,
+which allows to *generate* `call routines`
+simulating data structures mentioned above.
+
+Few exemplary functions are also generated.
+See particular macro help for further details.
+
+---
+
+Package contains:
+ 1. macro createdfarray
+ 2. macro createdharray
+ 3. macro createdhfifo
+ 4. macro createdhordstack
+ 5. macro createdhprtqueue
+ 6. macro createdhstack
+ 7. exec generatearrays
+ 8. clean generatearrays
+
+*SAS package generated by generatePackage, version 20201115*
+
+The SHA256 hash digest for package BasePlus:
+`1FC8D030D576C33F1B5DEB27E17534946209BC148D57A1357CA025ED1E69AEB8`
+
+---
+# Content description ############################################################################################
+
+## >>> `%createDFArray()` macro: <<< #######################
+
+The `%createDFArray()` macro allows to generate
+a `dynamic function array` which is a FCMP based
+approach to create *dynamically* allocated **numeric**
+array with possible values searching and `WHICHN()`
+function emulation.
+
+*Note:* Arrays provided by the macro are *one dimensional* arrays.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDFArray(
+ arrayName
+ <,debug=0>
+ <,simple=0>
+ <,resizeFactor=0>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `arrayName` - *Required*, creates a FCMP call subroutine which is also
+ an array name. In the data step it is used in form of
+ a call subroutine, e.g. `call arrayName("Allocate", -3, 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `simple=` - *Optional*, the default value is `0`. A *simple* dynamic
+ function array is one which is not searchable and does not
+ allows to use `which()` functionality.
+ If set to `1` then it disables `SEARCH` and `WHICH` functionality.
+ See examples below for details.
+
+* `resizeFactor=` - *Optional*, the default value is `0`. If set to `0` then
+ the dynamic array size is not changeable(mutable) after initial
+ size allocation.
+ If set not to `0` then arrays dimensions are mutable after allocation,
+ i.e. even if an array is allocated for ten elements (like `A[1:10]`)
+ you can do `A[17] = 42` and it will resize itself dynamically.
+ *Hint!* Set to, e.g. 4999, for faster allocation process.
+ See examples below for details.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &arrayName.(IO, position, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `R`, `Return` - to get the data from an array,
+ - `I`, `Input` - to insert the data into an array,
+ - `+`, `Add` - to increment given position by a value,
+ - `C`, `Clear` - to reduce an array to a single empty cell,
+ - `A`, `Allocate` - to reserve space for array width and set starting values,
+ - `D`, `Dimension` - to return minimal and maximal index of an array,
+ - `F`, `Find`, `Exist` - to find out if a given value exist in an array,
+ - `W`, `Which` - to search the first position of data in array, `WHICHN()` function emulator,
+ - `Sum` - to return the sum of non-missing elements of an array,
+ - `Nonmiss` - to return the number of non-missing elements of an array,
+ - `Avg`, `Mean`, `Average` - to return the average of non-missing elements of an array,
+ - `Min`, `Minimum` - to return the minimum of non-missing elements of an array,
+ - `Max`, `Maximum` - to return the maximum of non-missing elements of an array.
+
+2. `position` - is a *numeric* argument and depends on the `IO` value.
+ Behaves in the following way:
+ - for `O`, `Output`, `R`, `Return`/ `I`, `Input`/ `+`, `Add` it takes
+ an arrays index from (into) which data is get (put),
+ - for `C`, `Clear` is ignored,
+ - for `A`, `Allocate` sets the value of the minposition, i.e. the minimal position of the array index,
+ - for `D`, `Dimension` it returns value of the minposition,
+ - for `Sum`, `Nonmiss`, `Avg`, `Mean`, `Average`, `Min`, `Minimum`, `Max`, and `Maximum` is ignored,
+ - for `F`, `Find`, `Exist` it returns number of occurrences of a given value in an array,
+ - for `W`, `Which` it returns position the first occurrence of a given value in an array.
+
+.3 `value` - is a *numeric* argument and depends on the `IO` value.
+ Behaves in the following way:
+ - for `O`, `Output`, `R`, `Return` it holds value retrieved from an array on a given position,
+ - for `I`, `Input` it holds the value inserted into an array on a given position,
+ - for `+`, `Add` it holds the value that is used to increment an array value at a given position,
+ - for `C`, `Clear` is ignored,
+ - for `A`, `Allocate` it sets the value of the maxposition, i.e. maximal position of the array index,
+ - for `D`, `Dimension` it returns value of the maxposition
+ - for `Sum`, `Nonmiss`, `Avg`, `Mean`, `Average`, `Min`, `Minimum`, `Max`, and `Maximum` it returns
+ the calculated summary value,
+ - for `F`, `Find`, `Exist`, `W`, `Which` is the value to be searched in an array.
+
+The `position` and the `value` arguments are **outargs**, i.e. can be changed by the function.
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Searchable, and Immutable array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDFArray(ArrDSI);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example1;
+ call ArrDSI("Allocate", 1, 10);
+ L = 0; H = 0;
+ call ArrDSI("Dim", L, H);
+ put L= H=;
+
+ * populate array with data ;
+ do i = L to H;
+ call ArrDSI("Input", i, i**2);
+ end;
+
+ * searchability allows to find number of occurrences of value in the array ;
+ F = .;
+ call ArrDSI("Find", F, 16);
+ put "Value 16 occurs " F "times";
+ call ArrDSI("Find", F, 17);
+ put "Value 17 occurs " F "times";
+
+ * increase value of cell 4 by 1, and verify at WHICH position is 17 (by searchability);
+ call ArrDSI("+", 4, 1);
+ call ArrDSI("Which", F, 17);
+ put "Value 17 occurred for the first time at position " F;
+
+ * get values from the array ;
+ Value = .;
+ do i = L to H;
+ call ArrDSI("Output", i, Value);
+ put i= Value=;
+ end;
+
+ * some basic statistics ;
+ call ArrDSI("Sum", ., STAT); put "sum = " STAT;
+ call ArrDSI("Avg", ., STAT); put "avg = " STAT;
+ call ArrDSI("Min", ., STAT); put "min = " STAT;
+ call ArrDSI("Max", ., STAT); put "max = " STAT;
+ call ArrDSI("Cnt", ., STAT); put "cnt = " STAT;
+
+ * immutability does _not_ allow to increase dimensions automatically;
+ * this line returns an error ;
+ call ArrDSI("Input", 42, -1);
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Searchable, and Mutable array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDFArray(ArrDSM, resizefactor=17);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+ call ArrDSM("Allocate", -2, 2);
+
+ do i = -2 to 2;
+ call ArrDSM("Input", i, 2**i);
+ end;
+
+ L = .; H = .;
+ call ArrDSM("Dim", L, H);
+ put L= H=;
+
+ * mutability allows to increase dimensions automatically
+ * create index 3 and -3;
+ call ArrDSM("+", 3, 8);
+ call ArrDSM("+",-3, 0.125);
+ call ArrDSM("Dim", L, H);
+ put L= H=;
+
+ Value = .;
+ do i = L to H;
+ call ArrDSM("O", i, Value);
+ put i= Value=;
+ end;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Dynamic, non-searchable (a.k.a. SiMPle), and Immutable array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDFArray(ArrDSMPLI, simple=1);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example3;
+ call ArrDSMPLI("Allocate", -2, 2);
+
+ do i = -2 to 2;
+ call ArrDSMPLI("Input", i, 2**i);
+ end;
+
+ * non-searchable array (a.k.a. simple) does not allow ;
+ * to find number of occurrences of value in the array ;
+ * and verify what is the first position of a value ;
+ * this lines return a warning ;
+ call ArrDSMPLI("Exist", i, 1);
+ call ArrDSMPLI("Which", i, 1);
+ run;
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 4.** Dynamic, non-searchable (a.k.a. SiMPle), and Mutable array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDFArray(ArrDSMPLM, simple=1, resizefactor=42);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example4;
+ call ArrDSMPLM("Allocate", 1, 1);
+
+ * mutability allows to increase dimensions automatically ;
+ do i = -12 to 12;
+ call ArrDSMPLM("Input", i, i*2);
+ end;
+
+ * non-searchable array (a.k.a. simple) does not allow ;
+ * to find number of occurrences of value in the array ;
+ * and verify what is the first position of a value ;
+ * this lines return a warning ;
+ i = .;
+ call ArrDSMPLM("Exist", i, -24);
+ put "Exist " i=;
+ call ArrDSMPLM("Which", i, 24);
+ put "Which " i=;
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `%createDHArray()` macro: <<< #######################
+
+The `%createDHArray()` macro allows to generate
+a `dynamic hash array` which is a FCMP based approach
+to create *dynamically* allocated **numeric**
+or **character** array
+
+*Note:* Arrays provided by the macro are *one dimensional* arrays.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDHArray(
+ arrayName
+ <,debug=0>
+ <,type=8>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `arrayName` - *Required*, creates a FCMP call subroutine which is also
+ an array name. In the data step it is used in form of
+ a call subroutine, e.g. `call arrayName("Allocate", -3, 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `type=` - *Optional*, the default value is `8`.
+ Indicates what *type* (numeric/character) and *length*
+ are data portion of generated array. Should be in line
+ with the LENGTH statement, e.g. `8`, `$ 30`, etc.
+ Determines if the `value` argument is numeric or character.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &arrayName.(IO, position, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `R`, `Return` - to get the data from an array,
+ - `I`, `Input` - to insert the data into an array,
+ - `C`, `Clear` - to reduce an array to a single empty cell,
+ - `L`, `Low`, `Lower`, `Lbound` - to return minimal position of index,
+ - `H`, `High`, `Higher`, `Hbound` - to return maximal position of index.
+
+2. `position` - is a *numeric* argument and depends on the `IO` value.
+ Behaves in the following way:
+ - for `O`, `Output`, `R`, `Return`/ `I`, and `Input` it is an array
+ index from (into) which data is get (put),
+ - for `C` it is ignored,
+ - for `L`, `Low`, `Lower`, and `Lbound` it returns the first position of an index,
+ - for `H`, `High`, `Higher`, and `Hbound` it returns the last position of an index,
+ - otherwise is not modified.
+
+3. `value` - is a *numeric* or *character* argument (determined by the `type=`)
+ and depends on the `IO` value. Behaves in the following way:
+ - for `O`, `Output`, `R`, and `Return` it holds value retrieved from an array from a given position,
+ - for `I`, `Input` it holds the value inserted into an array into a given position,
+ - for `C` is ignored,
+ - for `L`, `Low`, `Lower`, and `Lbound` returns first value of index,
+ - for `H`, `High`, `Higher`, and `Hbound` returns last value of index,
+ - otherwise is not modified.
+
+The `position` and the `value` arguments are **outargs**, i.e. can be changed by the function.
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Hash-based, and Character array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHArray(ArrDHC, type = $ 12);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ %let zeros = 6; *[to test bigger sizes];
+ data Example1;
+
+ t = time();
+ do _I_ = -1e&zeros. to 1e&zeros.;
+ _X_ = put(_I_*10, z12.);
+ call ArrDHC("Input", _I_, _X_);
+ end;
+ t = time() - t;
+ put t= / _X_= /;
+
+ * get the size info ;
+ LB = 0; HB = 0;
+ drop LB HB;
+ call ArrDHC('Lbound', LB, _X_);
+ call ArrDHC('Hbound', HB, _X_);
+ put LB= HB= /;
+
+ t = time();
+ do _I_ = HB + 1 to LB - 1 by -1;
+ call ArrDHC('Output', _I_, _X_);
+ output;
+ end;
+ t = time() - t;
+ put t= / _X_= /;
+
+ * clear for further reuse ;
+ call ArrDHC('C', ., '');
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Hash-based, and Numeric array:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHArray(ArrDHN);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+
+ do i = -2 to 2;
+ call ArrDHN("Input", i, 2**i);
+ end;
+
+ do i = -2 to 2;
+ call ArrDHN("+", i, -10);
+ end;
+
+ v = .;
+ do i = -2 to 2;
+ call ArrDHN("Output", i, v);
+ put i= v=;
+ end;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `%createDHFifo()` macro: <<< #######################
+
+The `%createDHFifo()` macro allows to generate
+a `dynamic hash fifo` which is a FCMP based approach
+to create dynamically allocated numeric or character
+"first in first out" [queue](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))
+
+Interesting reading about implementing a fifo via hash table
+can be found in *chapter 10.4* of the:
+*"Data Management Solutions Using SAS Hash Table Operations:
+ A Business Intelligence Case Study"* book
+by Paul Dorfman and Don Henderson.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDHFifo(
+ fifoName
+ <,debug=0>
+ <,type=8>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `fifoName` - *Required*, creates a FCMP call subroutine which is also
+ a fifo name. In the data step it is used in form of
+ a call subroutine, e.g. `call fifoName("Enqueue", 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `type=` - *Optional*, the default value is `8`.
+ Indicates what *type* (numeric/character) and *length*
+ are data portion of generated array. Should be in line
+ with the LENGTH statement, e.g. `8`, `$ 30`, etc.
+ Determines if the `value` argument is numeric or character.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &fifoName.(IO, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `D`, `Dequeue`, `R`, `Return` - to get the data from a fifo (and remove it from the fifo)
+ - `I`, `Input`, `E`, `Enqueue`, and `Insert` - to insert the data into a fifo
+ - `C`, `Clear` - to reduce a fifo to an empty one
+ - `P`, `Peek`, `T`, and `Tail` - to peek the data from a fifo (and NOT remove it from the fifo)
+ - `H`, `Head` - to peek the data from a fifo head (and NOT remove it from the fifo)
+ - `Sum` - returns sum of nonmissing numeric elements of a stack
+ - `Avg`, `Mean`, `Average` - returns average of nonmissing numeric elements of a stack
+ - `Nonmiss`, `Cnt` - returns number of nonmissing elements of a stack
+ - `Height` - returns height a stack
+
+2. `value` - is a *numeric* or *character* argument (determined by the `type=`)
+ and depends on the `IO` value. Behaves in the following way:
+ - for `O`, `Output`, `D`, `Dequeue`, `R`, `Return` it holds the value popped from the fifo,
+ - for `I`, `Input`, `E`, `Enqueue`, `Insert` it holds the value to be pushed into the fifo,
+ - for `C`, `Clear` it is ignored,
+ - for `P`, `Peek` holds the value peeked from the fifo,
+ - for `Sum`, `Nonmiss`, `Cnt`, `Avg`, `Mean`, `Average`, and `Height` it returns calculated summary value.
+
+The `value` argument is **outarg**, i.e. can be changed by the function.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Hash-based, and Character fifo:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHFifo(FifoDHC, type = $ 12);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+
+ %let zeros = 6; *[to test bigger sizes];
+ data Example1;
+
+ t = time(); drop t;
+ do _I_ = 1 to 1e&zeros.;
+ _X_ = put(_I_*10, z12.);
+ call FifoDHC("Enqueue", _X_);
+ end;
+ t = time() - t;
+
+ call FifoDHC("Height", _X_);
+ put t= / _X_=;
+
+ t = time();
+ do _I_ = 1 to 1e&zeros. + 3;
+ call FifoDHC('Dequeue', _X_);
+ output;
+ end;
+ t = time() - t;
+
+ call FifoDHC("Height", _X_);
+ put t= / _X_=;
+
+ %* clear for further reuse *;
+ call FifoDHC('Clear', '');
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Hash-based, and Numeric fifo:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHFifo(FifoDHN);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+
+ do _I_ = 1,.,2,.,3,.,4,.,5,.,6;
+ call FifoDHN("E", _I_);
+ end;
+
+ call FifoDHN("Sum", _I_);
+ put "Sum " _I_=;
+
+ call FifoDHN("Avg", _I_);
+ put "Avg " _I_=;
+
+ call FifoDHN("Cnt", _I_);
+ put "Cnt " _I_=;
+
+ call FifoDHN("Height", _I_);
+ put "Height " _I_=;
+
+ call FifoDHN("Tail", _I_);
+ put "Tail of fifo is " _I_=;
+
+ call FifoDHN("Height", _I_);
+ put "Height after Tail " _I_=;
+
+ call FifoDHN("Head", _I_);
+ put "Head of fifo is " _I_=;
+
+ call FifoDHN("Height", _I_);
+ put "Height after Head" _I_=;
+
+ _X_ = 0;
+ do _I_ = 1 to _I_;
+ call FifoDHN('D', _X_);
+ output;
+ end;
+
+ call FifoDHN("Height", _I_);
+ put "Height " _I_=;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `%createDHOrdStack()` macro: <<< #######################
+
+The `%createDHOrdStack()` macro allows to generate
+a `dynamic ORDERED hash stack` which is a FCMP based approach
+to create dynamically allocated numeric or character
+*ordered* [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+
+Interesting reading about implementing a stack via hash table
+can be found in *chapter 10.4* of the:
+*"Data Management Solutions Using SAS Hash Table Operations:
+ A Business Intelligence Case Study"* book
+by Paul Dorfman and Don Henderson.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDHOrdStack(
+ fifoName
+ <,debug=0>
+ <,type=8>
+ <,order=A>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `stackName` - *Required*, creates a FCMP call subroutine which is also
+ a stack name. In the data step it is used in form of
+ a call subroutine, e.g. `call stackName("Push", 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `type=` - *Optional*, the default value is `8`.
+ Indicates what *type* (numeric/character) and *length*
+ are data portion of generated array. Should be in line
+ with the LENGTH statement, e.g. `8`, `$ 30`, etc.
+ Determines if the `value` argument is numeric or character.
+
+* `order=` - *Optional*, the default value is `A`.
+ Indicates a method of ordering of the stack,
+ allowed values are: `A` for ascending and `D` for descending.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &stackName.(IO, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `Pop`, `G`, `Get`, `R`, `Return` - to get the data from a stack (and remove it from the top),
+ - `I`, `Input`, `Push`, `Put`, `Insert` - to insert the data into a stack,
+ - `C`, `Clear` - to reduce a stack to an empty one,
+ - `P`, `Peek` - to peek the data from a stack (and NOT remove it from the top),
+ - `Sum` - returns sum of non-missing numeric elements of a stack,
+ - `Avg`, `Mean`, `Average` - returns average of non-missing numeric elements of a stack,
+ - `Nonmiss`, `Cnt`, `Nnm` - returns number of non-missing elements of a stack,
+ - `Height` - returns height a stack,
+ - `Min`, `Minimum` - returns minimum of non-missing elements of a stack,
+ - `Max`, `Maximum` - returns maximum of non-missing elements of a stack.
+
+2. `value` - is a *numeric* or *character* argument (determined by the `type=`)
+ and depends on the `IO` value. Behaves in the following way:
+ - for `O`, `Output`, `Pop`, `G`, `Get`, `R`, `Return` it holds a value popped from a stack,
+ - for `I`, `Input`, `Push`, `Put`, `Insert` it holds a value to be pushed into a stack,
+ - for `C`, `Clear` it is ignored,
+ - for `P`, `Peek` it holds a value peeked from a stack,
+ - for `Sum`, `Nonmiss`, `Cnt`, `Avg`, `Mean`, `Average`, `Height`, `Min`, `Minimum`, `Max`, and `Maximum`
+ it returns calculated summary value,
+
+The `value` argument is **outarg**, i.e. can be changed by the function.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Hash-based, and Character Descending Ordered stack:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHOrdStack(DescStackC, type = $ 12, order=D);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example1;
+
+ do _X_ = "A","B"," ","C","A"," ","B","C";
+ call DescStackC("Push", _X_);
+ end;
+
+ length S $ 12;
+ call DescStackC('Height', S);
+ put 'Height ' S;
+
+ do until(strip(S) = "0");
+ call DescStackC('Get', _X_);
+ call DescStackC('Height', S);
+ put S= _X_=;
+ output;
+ end;
+
+ %* clear for further reuse *;
+ call DescStackC('Clear','');
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Hash-based, and Numeric Ascending Ordered stack:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHOrdStack(DescStackN, order=A);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+
+ call missing(Sum, Avg, Min, Max, Cnt, Hgt, Peek);
+ do _X_ = 1,6,2,.,5,3,4;
+ call DescStackN("Put", _X_);
+ call DescStackN('Sum', Sum);
+ call DescStackN('Avg', Avg);
+ call DescStackN('Min', Min);
+ call DescStackN('Max', Max);
+ call DescStackN('Cnt', Cnt);
+ call DescStackN('Height', Hgt);
+ put (_ALL_) (=);
+ end;
+
+ call DescStackN('Peek', Peek);
+ put Peek=;
+
+ do _I_ = 1 to Hgt;
+ call DescStackN('Output', _X_);
+ keep _X_;
+ if _X_ > .z then output;
+ end;
+
+ call DescStackN('Peek', Peek);
+ put Peek=;
+
+ %* clear for further reuse *;
+ call DescStackN('Clear',.);
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `%createDHPrtQueue()` macro: <<< #######################
+
+
+The `%createDHPrtQueue()` macro allows to generate
+a `dynamic PRIORITY hash queue` which is a FCMP based approach
+to create dynamically allocated numeric or character
+[priority queue](https://en.wikipedia.org/wiki/Priority_queue)
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDHPrtQueue(
+ fifoName
+ <,debug=0>
+ <,type=8>
+ <,newOnTop=+>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `queueName` - *Required*, creates a FCMP call subroutine which is also
+ a queue name. In the data step it is used in form of
+ a call subroutine, e.g. `call queueName("Bottom", 1, 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `type=` - *Optional*, the default value is `8`.
+ Indicates what *type* (numeric/character) and *length*
+ are data portion of generated array. Should be in line
+ with the LENGTH statement, e.g. `8`, `$ 30`, etc.
+ Determines if the `value` argument is numeric or character.
+
+* `newOnTop=` - *Optional*, the default value is `+`.
+ Indicates how to keep order in the same priority group,
+ allowed values are `+` or `-`. Plus(`+`) sets new elements
+ at the top of the group, minus(`-`) at the bottom.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &queueName.(IO, position, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `D`, `Dequeue`, `R`, `Return` - it pops/gets/outputs the data from the queue head (high priority),
+ - `B`, `Bottom` - it pops/gets/outputs the data from the queue tail (low priority),
+ - `I`, `Input`, `E`, `Enqueue`, `Insert` - it push/puts/inserts the data into the queue,
+ - `C`, `Clear` - it reduces a queue to an empty one,
+ - `H`, `Head` - it peeks the data from the queue head and NOT removes it,
+ - `T`, `Tail` - it peeks the data from the queue tail and NOT removes it,
+ - `Sum` - it returns sum of non-missing *numeric* elements of the queue,
+ - `Avg`, `Mean`, `Average` - it returns average of non-missing *numeric* elements of the queue,
+ - `Nonmiss`, `Cnt` - it returns number of non-missing elements of the queue,
+ - `Height` - it returns height of the queue.
+
+2. `position` - is a *numeric* argument and depends on the `IO` value.
+ Behaves in the following way:
+ - for `O`, `Output`, `D`, `Dequeue`, `R`, `Return` and `B`, `Bottom`, or `H`, `Head`, `T`, `Tail`
+ it holds a priority level of value popped from the queue,
+ - for `I`, `Input`, `E`, `Enqueue`, `Insert` it holds a priority level of value to be pushed into the queue,
+ - for `C` ignored,
+ - for *numeric* queue and `Sum`, `Nonmiss`, `Cnt`, `Avg`, `Mean`, `Average`, `Height` returns calculated summary value.
+
+3. `value` - is a *numeric* or *character* argument (determined by the `type=`)
+ and depends on the `IO` value. Behaves in the following way:
+ - for `O`, `Output`, `D`, `Dequeue`, `R`, `Return` and `B`, `Bottom` or `H`, `Head`, `T`, `Tail`
+ it holds a value popped from the queue,
+ - for `I`, `Input`, `E`, `Enqueue`, `Insert` it holds a value to be pushed into the queue,
+ - for `C` ignored,
+ - for *numeric* queue and `Sum`, `Nonmiss`, `Cnt`, `Avg`, `Mean`, `Average`, `Height` returns calculated summary value,
+ - otherwise does not modify value.
+
+The `position` and the `value` arguments are **outargs**, i.e. can be changed by the function.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Hash-based, and Character Priority queue:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHPrtQueue(PriorityQueuePC, type = $ 12, newOnTop=+);
+ %createDHPrtQueue(PriorityQueueNC, type = $ 12, newOnTop=-);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example1;
+
+ _I_ = .;
+ length _X_ _Y_ $ 3;
+ do _X_ = "AAA","BBB","CCC","AA","BB","CC","A","B","C";
+ _I_ + 1;
+ call PriorityQueuePC("I", mod(_I_, 3), _X_);
+ call PriorityQueueNC("I", mod(_I_, 3), _X_);
+ end;
+
+ Height = .;
+ call PriorityQueuePC('Height', Height, '');
+ put Height=;
+
+ do until(Height = 0);
+ call PriorityQueuePC('Dequeue', _I_, _X_);
+ call PriorityQueueNC("Dequeue", _I_, _Y_);
+ call PriorityQueueNC('Height', Height, '');
+ put (_ALL_) (=);
+ output;
+ end;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Hash-based, and Numeric Priority queue:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHPrtQueue(PriorityQueueN);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+
+ do _X_ = -5 to 5;
+ call PriorityQueueN("Enqueue", abs(_X_), _X_);
+ end;
+
+ call missing(Sum, Avg, Cnt, Hgt);
+ call PriorityQueueN('Sum', ., Sum);
+ call PriorityQueueN('Avg', ., Avg);
+ call PriorityQueueN('Cnt', ., Cnt);
+ call PriorityQueueN('Height', ., Hgt);
+ put (_ALL_) (=);
+
+ do _N_ = 1 to Hgt;
+ call PriorityQueueN("Dequeue", _X_, _Y_);
+ put _X_= _Y_=;
+ end;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `%createDHStack()` macro: <<< #######################
+
+The `%createDHStack()` macro allows to generate
+a `dynamic hash stack` which is a FCMP based approach
+to create dynamically allocated numeric or character
+[stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))
+
+Interesting reading about implementing a stack via hash table
+can be found in *chapter 10.4* of the:
+*"Data Management Solutions Using SAS Hash Table Operations:
+ A Business Intelligence Case Study"* book
+by Paul Dorfman and Don Henderson.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+%createDHStack(
+ fifoName
+ <,debug=0>
+ <,type=8>
+ <,outlib=work.DFAfcmp.package>
+ <,hashexp=13>
+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `stackName` - *Required*, creates a FCMP call subroutine which is also
+ a stack name. In the data step it is used in form of
+ a call subroutine, e.g. `call stackName("Push", 3)`.
+ Has to satisfy FCMP function naming requirements, but with
+ maximum of 24 characters.
+
+* `debug=` - *Optional*, the default value is `0`.
+ If set to `1` then it turns on a debugging mode.
+
+* `type=` - *Optional*, the default value is `8`.
+ Indicates what *type* (numeric/character) and *length*
+ are data portion of generated array. Should be in line
+ with the LENGTH statement, e.g. `8`, `$ 30`, etc.
+ Determines if the `value` argument is numeric or character.
+
+* `outlib=` - *Optional*, the default value is `work.DFAfcmp.package`.
+ It points the default location for new generated dynamic
+ function arrays compiled by FCMP.
+ *Hint!* Keep it as it is.
+
+* `hashexp=` - *Optional*, the default value is `13`. It is the default `hashexp=`
+ value for internal hash table used by the function.
+
+
+**Created function arguments description**:
+
+A function generated by the macro is:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+call &stackName.(IO, value)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+and accepts the following list of arguments and values:
+
+1. `IO` - is a *character* steering argument, possible
+ values and behaviour they call are the following:
+ - `O`, `Output`, `Pop`, `G`, `Get`, `R`, `Return` - to get the data from a stack (and remove it from the top),
+ - `I`, `Input`, `Push`, `Put`, `Insert` - to insert the data into a stack,
+ - `C`, `Clear` - to reduce a stack to an empty one,
+ - `P`, `Peek` - to peek the data from a stack (and NOT remove it from the top),
+ - `Sum` - returns sum of non-missing numeric elements of a stack,
+ - `Avg`, `Mean`, `Average` - returns average of non-missing numeric elements of a stack,
+ - `Nonmiss`, `Cnt`, `Nnm` - returns number of non-missing elements of a stack,
+ - `Height` - returns height a stack,
+
+2. `value` - is a *numeric* or *character* argument (determined by the `type=`)
+ and depends on the `IO` value. Behaves in the following way:
+ - for `O`, `Output`, `Pop`, `G`, `Get`, `R`, `Return` it holds a value popped from a stack,
+ - for `I`, `Input`, `Push`, `Put`, `Insert` it holds a value to be pushed into a stack,
+ - for `C`, `Clear` it is ignored,
+ - for `P`, `Peek` it holds a value peeked from a stack,
+ - for `Sum`, `Nonmiss`, `Cnt`, `Avg`, `Mean`, `Average`, `Height` it returns calculated summary value,
+
+The `value` argument is **outarg**, i.e. can be changed by the function.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Dynamic, Hash-based, and Character stack:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHStack(StackDHC, type = $ 12);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+
+ %let zeros = 6; *[to test bigger sizes];
+ data Example1;
+
+ t = time(); drop t;
+ do _I_ = 1 to 1e&zeros.;
+ _X_ = put(_I_*10, z12.);
+ call StackDHC("Put", _X_);
+ end;
+ t = time() - t;
+
+ call StackDHC("Height", _X_);
+ put t= / _X_=;
+
+ t = time();
+ do _I_ = 1 to 1e&zeros. + 3;
+ call StackDHC('Pop', _X_);
+ output;
+ end;
+ t = time() - t;
+
+ call StackDHC("Height", _X_);
+ put t= / _X_=;
+
+ %* clear for further reuse *;
+ call StackDHC('Clear', '');
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Dynamic, Hash-based, and Numeric stack:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
+ %createDHStack(StackDHN);
+ options APPEND=(cmplib = WORK.DFAfcmp) ;
+
+ data Example2;
+
+ do _I_ = 1,.,2,.,3,.,4,.,5,.,6;
+ call StackDHN("Put", _I_);
+ end;
+
+ call StackDHN("Sum", _I_);
+ put "Sum " _I_=;
+
+ call StackDHN("Avg", _I_);
+ put "Avg " _I_=;
+
+ call StackDHN("Cnt", _I_);
+ put "Cnt " _I_=;
+
+ call StackDHN("Height", _I_);
+ put "Height " _I_=;
+
+ _X_ = 0;
+ do _I_ = 1 to _I_;
+ call StackDHN('Pop', _X_);
+ output;
+ end;
+
+ call StackDHN("Height", _I_);
+ put "Height " _I_=;
+
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+---
+## >>> `generateArrays` exec: <<< #######################
+
+The generateArrays exec file provides a **list of automatically generated examples** of functions
+emulating data structures.
+
+The list of provided examples is the following:
+- `SmpArray` - Simple Immutable Dynamic Function Array
+- `SmpMtbArray` - Simple Mutable Dynamic Function Array
+- `SrchArray` - Searchable Immutable Dynamic Function Array
+- `SrchMtbArray` - Searchable Mutable Dynamic Function Array
+- `DynArrayC` - Dynamic Hash-based Character Function Array (length 256 bytes)
+- `StackC` - Dynamic Hash-based Character Stack (length 256 bytes)
+- `StackN` - Dynamic Hash-based Numeric Stack
+- `FifoC` - Dynamic Hash-based Character Fifo (length 256 bytes)
+- `FifoN` - Dynamic Hash-based Character Fifo
+- `DescStackC` - Dynamic Hash-based Character Descending Ordered Stack (length 256 bytes)
+- `AscStackC` - Dynamic Hash-based Character Ascending Ordered Stack (length 256 bytes)
+- `DescStackN` - Dynamic Hash-based Numeric Descending Ordered Stack
+- `AscStackN` - Dynamic Hash-based Numeric Ascending Ordered Stack
+- `PrtQueueNTC` - Dynamic Hash-based Character Priority Queue with *New on Top* (length 256 bytes)
+- `PrtQueueNBC` - Dynamic Hash-based Character Priority Queue with *New on Bottom* (length 256 bytes)
+- `PrtQueueNTN` - Dynamic Hash-based Numeric Priority Queue with *New on Top*
+- `PrtQueueNBN` - Dynamic Hash-based Numeric Priority Queue with *New on Bottom*
+
+The `outlib=` option is set to `work.DFAfcmp.package`. The `cmplib=` option is updated automatically.
+
+---
+## >>> `generateArrays` clean: <<< #######################
+
+The generateArrays clean file clears the list of automatically generated examples of functions
+emulating data structures provided in the `generatearrays.sas` exec file.
+
+The `cmplib=` option is updated automatically.
+
+---
+
+## License ####################################################################
+
+Copyright (c) 2019 Bartosz Jablonski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
diff --git a/packages/dfa.zip b/packages/dfa.zip
index 5de6acf..82ec660 100644
Binary files a/packages/dfa.zip and b/packages/dfa.zip differ