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