diff --git a/packages/README.md b/packages/README.md
index 6c682b7..e6818ba 100644
--- a/packages/README.md
+++ b/packages/README.md
@@ -49,7 +49,7 @@ run;
SHA256 digest for DFA: BB8768E977D62429368CFF2E5338A6553C35C998AEC09AF24088BA713BB54DDA
-- **macroArray**\[0.4\], implementation of an array concept in a macrolanguage, e.g.
+- **macroArray**\[0.5\], implementation of an array concept in a macrolanguage, e.g.
```
%array(ABC[17] (111:127), macarray=Y);
@@ -68,7 +68,7 @@ SHA256 digest for DFA: BB8768E977D62429368CFF2E5338A6553C35C998AEC09AF24088BA713
which = 1:H:2
);
```
-SHA256 digest for macroArray: 5C9208ADD091E354794C24FA830F527D17CFC758C24CB77BF2154949059F7E6F
+SHA256 digest for macroArray: 69F6CF496F921D0E21F3524FC7FD130B4B8290C1E0B9BB3ABEA212B734EBE8A1
- **BasePlus**\[0.62\] adds a bunch of functionalities I am missing in BASE SAS, such as:
diff --git a/packages/SHA256_for_packages.txt b/packages/SHA256_for_packages.txt
index d32174d..cf1646e 100644
--- a/packages/SHA256_for_packages.txt
+++ b/packages/SHA256_for_packages.txt
@@ -1,5 +1,7 @@
/* 20200914 */
SQLinDS: DD5E319EB5AA29C7054EC428072F987E77C29D36874DED1AE5C62E4B300845EB
+macroArray: 69F6CF496F921D0E21F3524FC7FD130B4B8290C1E0B9BB3ABEA212B734EBE8A1
+
/* 20200911 */
sqlindsdemo: CCA3CB51587E30D1A4338EAF732EF03E0922918AAA21C3ECF85CABE93CD2FB15
macroArray: 5C9208ADD091E354794C24FA830F527D17CFC758C24CB77BF2154949059F7E6F
diff --git a/packages/macroarray.md b/packages/macroarray.md
new file mode 100644
index 0000000..d95f55c
--- /dev/null
+++ b/packages/macroarray.md
@@ -0,0 +1,1121 @@
+# The macroArray package [ver. 0.5] ###############################################
+
+The **macroArray** package implements a macro array facility:
+- `%array()`,
+- `%do_over()`,
+- `%make_do_over()`,
+- `%deletemacarray()`,
+- `%concatarrays()`,
+- `%appendcell()`.
+- etc.
+
+The set of macros, which emulates classic
+data-step-array functionality on the macro
+programming level, is provided.
+
+*Note:*
+If you are working with BIG macroarrays do not
+forget to verify your session setting for macro
+memory limits. Run:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ proc options group = macro;
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+to verify the following options:
+
+| option | description |
+|-------------:|:-----------------------------------------------------------------------------------------------|
+|`MEXECSIZE=` | specifies the maximum macro size that can be executed in memory. |
+|`MSYMTABMAX=` | specifies the maximum amount of memory available to the macro variable symbol table or tables. |
+|`MVARSIZE=` | specifies the maximum size for a macro variable that is stored in memory. |
+
+
+Package contains:
+1. macro appendarray,
+2. macro appendcell,
+3. macro array,
+4. macro concatarrays,
+5. macro deletemacarray,
+6. macro do_over,
+7. macro do_over2,
+8. macro do_over3,
+9. macro make_do_over,
+
+Required SAS Components:
+ *Base SAS Software*
+
+*SAS package generated by generatePackage, version 20200911*
+
+The SHA256 hash digest for package macroArray
+`69F6CF496F921D0E21F3524FC7FD130B4B8290C1E0B9BB3ABEA212B734EBE8A1`
+
+### Content description #######################################################
+
+## >>> `%appendArray()` macro: <<< ##############################################
+
+The `%appendArray()` macro is a macrowrapper
+which allows to concatenate two macroarrays
+created by `%array()` macro.
+
+By default values of the second macroarray are *not* removed.
+
+Dimensions of the first macroarray are extended.
+
+The `%appendArray()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%appendArray(
+ first
+ ,second
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. first - *Required*, a name of a macroarray created by the `%array()` macro.
+
+2. second - *Required*, a name of a macroarray created by the `%array()` macro.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Append macroarrays LL and MM.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ll[2:4] $ 12,
+ function = quote(put(today() + 10*_I_, yymmdd10.)),
+ macarray=Y
+ )
+ %array(mm[10:13] $ 1200,
+ function = quote(repeat("A",12*_I_)),
+ macarray=Y
+ )
+ %put *%ll(2)*%ll(3)*%ll(4)*;
+
+ %appendArray(ll, mm);
+ %put *%ll(2)*%ll(3)*%ll(4)*%ll(5)*%ll(6)**%ll(7)*%ll(8)*;
+
+ %put *%mm(10)**%mm(11)*%mm(12)*%mm(13)*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Error handling.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %appendArray(ll, )
+ %appendArray(, mm)
+
+ %appendArray(noExistA, noExistB)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%appendCell()` macro: <<< ###############################################
+
+The `%appendCell()` macro allows to append
+a macrovariable to a macroarray created by the `%array()` macro.
+
+Dimensions of the macroarray are extended.
+
+The `%appendCell()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%appendCell(
+ first
+ ,second
+ ,hilo
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. first - *required*, a name of a macroarray created by the `%array()` macro.
+
+2. second - *required*, a name of a macrovariable to be append to the macroarray.
+
+3. hilo - *required*, if `H` macrovariable is appended at the end
+ if `L` macrovariable is appended at the beginning
+);
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Create two macro wrappers.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %* Macro wrapper to append a macrovariable to the end of a macroarray;
+ %macro appendHC(array,cell);
+ %appendCell(&array.,&cell.,H)
+ %mend appendHC;
+
+ %* macro wrapper to append a macrovariable to the beginning of a macroarray;
+ %macro appendLC(array,cell);
+ %appendCell(&array.,&cell.,L)
+ %mend appendLC;
+
+
+ %* create macroarrays X and variables W,Y,Z;
+
+ %array(X[2:4] $ ("AAA", "BBB", "CCC"), macarray=Y)
+ %let W=1;
+ %let Y=2;
+ %let Z=3;
+ %put *%do_over(X)*&=W*&=Y*&=Z*;
+
+ %put BEFORE *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*;
+ %appendCell(X,Y,H)
+ %put AFTER1 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*;
+
+ %appendLC(X,W)
+ %put AFTER2 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*;
+
+ %appendHC(X,Z)
+ %put AFTER3 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Error handling
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %appendCell(X,Y,blahblah)
+
+ %appendCell(X,,H)
+ %appendCell(,Y,H)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**EXAMPLE 3.** Adding variable below lower bound.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(zero[0:2] $ ("AAA", "BBB", "CCC"), macarray=Y)
+ %let belowzero=zzz;
+
+ %put BEFORE *%do_over(zero)**&=zeroLBOUND*&=zeroHBOUND*&=zeroN*;
+ %appendCell(zero,belowzero,L)
+ %put AFTER *%do_over(zero)**&=zeroLBOUND*&=zeroHBOUND*&=zeroN*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%array()` macro: <<< ####################################################
+
+The code of a macro was inspired by
+*Ted Clay's* and *David Katz's* macro `%array()`.
+
+The `%array()` macro version provided in the package
+is designed to facilitate
+the idea of macro array concept, i.e. *a list of
+macrovariables with common prefix and numerical suffixes*.
+Usually such construction is then resolved by
+double ampersand syntax, e.g. `&&perfix&i` or similar one.
+
+What is new/extension to the `%array()` macro concept are:
+
+0. The syntax is closer to the data step one.
+1. It is a pure macro code (it can be executed in any place
+ of 4GL code), this includes generating macro arrays out
+ of datasets.
+2. When a macroarrray is created it allows also to generate
+ a new macro (named the same as the array name) and replace
+ the double ampersand syntax with more array looking one,
+ i.e. for array ABC user can have `%ABC(1)`, `%ABC(2)`, or `%ABC(&i)`
+ constructions.
+3. The array macro allows to use data step functions to generate
+ array's entries.
+
+The `%array()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%array(
+ array
+ <,function=>
+ <,before=>
+ <,after=>
+ <,vnames=N>
+ <,macarray=N>
+ <,ds=>
+ <,vars=>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. `array` - *Mandatory*, an array name and a declaration/definition of an array,
+ e.g. `myArr[*] x1-x3 (4:6)`
+ or `myBrr[*] $ y1-y3 ("a" "b" "c")`
+ or `myCrr[3] $ ("d d d" "e,e,e" "f;f;f")`
+ or `myDrr p q r s`.
+ Macrovariables created by the macro are *global*.
+ If an array name is `_` (single underscore) then attached variables
+ list names are used, a call of the form:
+ `%array(_[*] p1 q2 r3 s4 (-1 -2 -3 -4))`
+ will create macrovariables: `p1`, `q2`, `r3`, and `s4` with respective
+ values: `-1`, `-2`, `-3`, and `-4`.
+ Three additional *global* macrovariables:
+ `LBOUND`, `HBOUND`, and `N`
+ are generated with the macroarray. See examples for more use-cases.
+
+* `function=` - *Optional*, a function or an expression to be applied to all array cells,
+ `_I_` is as array iterator, e.g. `_I_ + rand("uniform")`.
+
+* `before=` - *Optional*, a function or an expression to be added before looping through
+ array, e.g. `call streaminit(123)`.
+
+* `after=` - *Optional*, a function or an expression to be added after looping through
+ array, e.g. `call sortn(ABC)`.
+
+* `vnames=N` - *Optional*, Default value `N`, if set to `Y`/`YES` then macroarray is built based
+ on variables names instead values, e.g.
+ `%array(myArr[*] x1-x3 (4:6), vnames=Y)`
+ will use `x1`, `x2`, and `x3` as values instead `4`, `5`, and `6`.
+
+* `macarray=N` - *Optional*, Default value `N`, if set to `Y`/`YES` then macro named with array
+ name is compiled to create convenient envelope for multiple ampersands, e.g.
+ `%array(myArr[*] x1-x3 (4:6), macarray=Y)`
+ will create `%myArr(J)` macro which will allow to extract "data"
+ from macroarray like:
+ `%let x = %myArr(1);`
+ or when used with second parameter equal `I` (insert) allow to overwrite macroarrays
+ value:
+ `%let %myArr(17,i) = 42;`
+
+* `ds=` - *Optional*, Use a dataset as a basis for a macroarray data,
+ if used by default overwrites use of the `array` parameter, honors `macarray=`
+ argument, dataset options are allowed, e.g. `sashelp.class(obs=5)`
+
+* `vars=` - *Optional*, a list of variables used to create macroarrays from a dataset,
+ the list format can be as follows (`<...>` means optional):
+ `variable1 <... variableN>`
+ delimiters are hash(`#`) and pipe(`|`), currently only space
+ is supported as separator, the meaning of `#` and `|` delimiters
+ will be explained in the following example:
+ if the `vars = height#h weight weight|w age|` value is provided
+ then the following macroarrays will be created:
+ 1) macroarray "H" with ALL(`#`) values of variable "height"
+ 2) macroarray "WEIGHT" with ALL(no separator is equivalent to #)
+ values of variable "weight"
+ 3) macroarray "W" with UNIQUE(|) values of variable "weight" and
+ 4) macroarray "AGE" with UNIQUE(|) values of variable "age",
+
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+
+**EXAMPLE 1.** Basic use-case.
+ Creating macroarray like in the array statement;
+ values are used by default;
+ different types of brackets are allowed;
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(a[*] x1-x5 (1:5))
+
+ %array(b{5} (5*17))
+
+ %* Mind the $ since it is a character array!
+ %array(c(3) $ 10 ("a A" "b,B" "c;C"))
+
+ %array(d x1-x5 (5 4 3 2 1))
+ %put _user_;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Index ranges.
+ If range starts < 0 then it is shifted to 0.
+ In case when range is from `1` to `M`
+ then macrovariable `N` is set to `M`
+ In case when range is different
+ the `N` returns number of
+ elements in the array `(Hbound - Lbound + 1)`.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(d[-2:2] $ ("a" "b" "c" "d" "e"))
+ %put &=dLBOUND. &=dHBOUND. &=dN.;
+ %put &=d0. &=d1. &=d2. &=d3. &=d4.;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Functions.
+ It is possible to assign value of a function
+ or an expression to a cell of the array,
+ e.g. `array[_I_] = function(...)`.
+ You can use an iterator in a function.
+ As in case of usual arrays it is `_I_`.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(e[-3:3] $, function = "A" )
+ %put &=eLBOUND. &=eHBOUND. &=eN.;
+ %put &=e0. &=e1. &=e2. &=e3. &=e4. &=e5. &=e6.;
+
+ %array(f[-3:3], function = (2**_I_) )
+ %put &=fLBOUND. &=fHBOUND. &=fN.;
+ %put &=f0. &=f1. &=f2. &=f3. &=f4. &=f5. &=f6.;
+
+ %array(g[0:2], function = ranuni(123) )
+ %put &=gLBOUND. &=gHBOUND. &=gN.;
+ %put &=g0. &=g1. &=g2.;
+
+ %* Or something more complex;
+ %array(gg[0:11] $ 11, function = put(intnx("MONTH", '1jun2018'd, _I_, "E"), yymmn.))
+ %put &=ggLBOUND. &=ggHBOUND. &=ggN.;
+ %put &=gg0 &=gg1 &=gg2 ... &=gg11;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 4.** Functions cont.
+ If there is need for set-up something *before* or *after*:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(h[10:12]
+ ,function = rand('Uniform')
+ ,before = call streaminit(123)
+ ,after = call sortn(of h[*])
+ )
+ %put &=h10. &=h11. &=h12.;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 5.** Fibonacci series.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(i[1:10] (10*0)
+ ,function = ifn(_I_ < 2, 1, sum(i[max(_I_-2,1)], i[max(_I_-1,2)]) ) )
+ %put &=i1 &=i2 &=i3 &=i4 &=i5 &=i6 &=i7 &=i8 &=i9 &=i10;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 6a.** "Uppercas Letters"
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(UL[26] $, function = byte(rank("A")+_I_-1) )
+ %put &=UL1 &=UL2 ... &=UL25 &=UL26;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 6b.** "Lowercase Letters"
+ Extended by `macarray=Y` option and
+ the input mode support (with `I`).
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ll[26] $, function = byte(rank("a")+_I_-1), macarray=Y)
+ %put *%ll(&llLBOUND.)*%ll(3)*%ll(4)*%ll(5)*...*%ll(25)*%ll(&llHBOUND.)*;
+
+ %* The range handling, warning;
+ %put *%ll(265)*;
+
+ %* The input mode;
+ %put *before:*%ll(2)*;
+ %let %ll(2,I) = bbbbb;
+ %put *after: *%ll(2)*;
+
+ %* The range handling, error;
+ %let %ll(265,I) = bbb;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 7.** The use of `vnames=Y`
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(R R1978-R1982)
+ %put &=R1 &=R2 &=R3 &=R4 &=R5;
+
+ %array(R R1978-R1982 (78:82))
+ %put &=R1 &=R2 &=R3 &=R4 &=R5;
+
+ %array(R R1978-R1982 (78:82), vnames=Y)
+ %put &=R1 &=R2 &=R3 &=R4 &=R5;
+
+ %array(R R1978-R1982, vnames=Y)
+ %put &=R1 &=R2 &=R3 &=R4 &=R5;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 8.** A "no name" array i.e. the `_[*]` array
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(_[*] x1-x5 (1:5))
+ %put _user_;
+
+ %array(_[*] p q r s (4*42))
+ %put _user_;
+
+ %* If no variables named than use _1 _2 ... _N;
+ %array(_[4] (-1 -2 -3 -4))
+ %put &=_1 &=_2 &=_3 &=_4;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 9.** Pure macro code can be used in a data step.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ data test1;
+ set sashelp.class;
+ %array(ds[*] d1-d4 (4*17))
+ a1 = &ds1.;
+ a2 = &ds2.;
+ a3 = &ds3.;
+ a4 = &ds4.;
+ run;
+
+ data test2;
+ set sashelp.class;
+ %array(_[*] j k l m (4*17))
+ a1 = &j.;
+ a2 = &k.;
+ a3 = &l.;
+ a4 = &m.;
+ run;
+
+ data test3;
+ set sashelp.class;
+ %array(alpha[*] j k l m (101 102 103 104), macarray=Y)
+ a1 = %alpha(1);
+ a2 = %alpha(2);
+ a3 = %alpha(3);
+ a4 = %alpha(4);
+ a5 = %alpha(555);
+ run;
+
+ data test4;
+ set sashelp.class;
+ %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y)
+ a1 = "%beta(1)";
+ a2 = "%beta(2)";
+ a3 = "%beta(3)";
+ a4 = "%beta(4)";
+ a5 = "%beta(555)";
+ run;
+
+ data test5;
+ set sashelp.class;
+ %array(gamma[4] $ 12 ("101" "102" "103" "104"), macarray=Y)
+ a1 = "%gamma(1)";
+ a2 = "%gamma(2)";
+ a3 = "%gamma(3)";
+ a4 = "%gamma(4)";
+ a5 = "%gamma(555)";
+ run;
+
+ data test6;
+ set sashelp.class;
+ %array(ds = sashelp.cars, vars = Cylinders|, macarray=Y)
+ a0 = %Cylinders(0);
+ a1 = %Cylinders(1);
+ a2 = %Cylinders(2);
+ a3 = %Cylinders(3);
+ a4 = %Cylinders(4);
+ a5 = %Cylinders(555);
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 10.** Creating an array from a dataset, basic case.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ds = sashelp.class, vars = height weight age)
+ %put _user_;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 11. Creating an array from a dataset, advanced.
+ If: `vars = height#h weight weight|w age|`
+ then create:
+ 1. macroarray "h" with ALL(#) values of variable "height"
+ 2. macroarray "weight" with ALL(no separator is equivalent to #) values of variable "weight"
+ 3. macroarray "w" with UNIQUE(|) values of variable "weight"
+ 4. macroarray "age" with UNIQUE(|) values of variable "age"
+ Currently the only separator in VARS is a space.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ds = sashelp.class, vars = height#h weight weight|w age|)
+ %put _user_;
+
+ %array(ds = sashelp.class, vars = height#hght weight weight|wght age|, macarray=Y)
+ %put *%hght(&hghtLBOUND.)**%weight(2)**%wght(&wghtHBOUND.)**%age(3)*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 12.** Creating an array from a dataset with dataset options
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ds = sashelp.cars(obs=100 where=(Cylinders=6)), vars = Make| Type| Model, macarray=Y)
+ %put *%make(&makeLBOUND.)*%Model(2)*%Model(3)*%Model(4)*%type(&typeHBOUND.)*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%concatArrays()` macro: <<< #############################################
+
+The `%concatArrays()` macro allows to concatenate
+two macroarrays created by the `%array()` macro.
+
+By default values of the second macroarray are removed.
+
+Dimensions of the first macroarray are extended.
+
+The `%concatArrays()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%concatArrays(
+ first
+ ,second
+ <,removeSecond=Y>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. first - *Required*, a name of a macroarray created by the `%array()` macro.
+
+2. second - *Required*, a name of a macroarray created by the `%array()` macro.
+
+* removeSecond=Y - *Optional*, Default value `Y`, if set to `Y` then
+ the second array is removed.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Concatenate macroarrays LL and MM.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(ll[2:4] $ 12,
+ function = quote(put(today() + 10*_I_, yymmdd10.)),
+ macarray=Y
+ )
+ %array(mm[10:13] $ 12000,
+ function = quote(repeat("A",123*_I_)),
+ macarray=Y
+ )
+ %put *%ll(2)*%ll(3)*%ll(4)*;
+
+ %concatArrays(ll, mm);
+ %put *%ll(2)*%ll(3)*%ll(4)*%ll(5)*%ll(6)**%ll(7)*%ll(8)*;
+
+ %put *%mm(10)**%mm(11)*%mm(12)*%mm(13)*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Error handling.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %concatArrays(ll, )
+ %concatArrays(, mm)
+
+ %concatArrays(noExistA, noExistB)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%deleteMacArray()` macro: <<< ###########################################
+
+The `%deleteMacArray()` macro allows to delete
+macroarrays created by the `%array()` macro.
+
+The `%deleteMacArray()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%deleteMacArray(
+ arrs
+ <,macarray=N>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. arrs - *Required*, a space separated list of manes
+ of macroarray created by the `%array()` macro.
+
+* macarray=N - *Optional*, indicator should a macro
+ associated with macroarray to be deleted?
+ If `Y` or `YES` then the associated macro is deleted.
+
+
+
+## >>> `%do_over()` macro: <<< ################################################
+
+The code of the macro was inspired by
+*Ted Clay's* and *David Katz's* macro `%do_over()`.
+
+The `%DO_OVER()` macro allows to iterate over macroarray created with the `macarray=Y`
+parameter of the `%ARRAY()` macro.
+
+The `%do_over()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%do_over(
+ array
+ <,phrase=%nrstr(%&array(&_I_.))>
+ <,between=%str( )>
+ <,which = >
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. array - Required, indicates a macroarray which metadata (Lbound, Hbouns)
+ are to be used to loop in the `%do_over()`
+
+* phrase= - *Optional*, Default value `%nrstr(%&array(&_I_.))`,
+ a statement to be called in each iteration
+ of the internal do_over's loop. Loop iterator is `_I_`,
+ if you want to use `_I_` or array name
+ [e.g. `%myArr(&_I_.)`] *enclose it* in the `%NRSTR()`
+ macro quoting function.
+
+* between= - *Optional*, Default value `%str( )` (space),
+ a statement to be called in between each
+ iteration of the internal do_over loop.
+ If macroquoted (e.g. `%str( + )`) then the `%unquote()`
+ function is automatically applied.
+
+* which = - *Optional*, a _SPACE_ separated list of indexes which
+ should be used to iterate over selected macroarray.
+ Possible special characters are `H` and `L` which means
+ *high* and *low* bound of an array, list could be set with
+ colons(`:`) in form of `start:end:by` (*no spaces between!*),
+ if `by` is omitted the default is `1`. If possible use
+ `1:5` rather `1 2 3 4 5` since the firs works faster.
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Simple looping.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y)
+
+ %put #%do_over(beta)#;
+
+ %put #%do_over(beta, phrase=%nrstr("%beta(&_I_.)"), between=%str(,))#;
+
+ data test1;
+ %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y)
+ %do_over(beta, phrase=%nrstr(a&_I_. = "%beta(&_I_.)";))
+ put _all_;
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Multiple arrays looping.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(alpha[*] j k l m n, vnames=Y, macarray=Y)
+ %array( beta[5] $ , function = "a", macarray=Y)
+ %array(gamma[4] (101 102 103 104), macarray=Y)
+
+ data test2;
+ call streaminit(123);
+ %do_over(beta
+ , phrase = %nrstr(%beta(&_I_.) = %gamma(&_I_.) * rand('Uniform'); output;)
+ , between = put _all_;
+ );
+ put _all_;
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Multiple arrays looping, cont.
+ Create multiple datasets.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %do_over(beta
+ , phrase = %nrstr(
+ data %alpha(&_I_.)2;
+ call streaminit(123);
+ %beta(&_I_.)x = %gamma(&_I_.) * rand('Uniform');
+ output;
+ run;
+ )
+ )
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 4.** Multiple arrays looping, cont.
+ Create multiple datasets using a macro.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %macro doit(ds, var=a, val=1);
+ data &ds.;
+ call streaminit(123);
+ &var. = &val. * rand('Uniform');
+ output;
+ run;
+ %mend doit;
+
+ %do_over(beta
+ , phrase = %nrstr(
+ %DOIT(%alpha(&_I_.)1, var = %beta(&_I_.), val = %gamma(&_I_.))
+ )
+ )
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 5.** `%do_over()` inside `%array()`
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(test[*] x1-x12 (1:12), macarray=Y)
+
+ %put **%test(1)**%test(12)**;
+
+ %put #%do_over(test)#;
+
+ %array(abc[*] x1-x12 (%do_over(test,phrase=%nrstr(%eval(100-%test(&_I_.))))), macarray=Y)
+
+ %put **%abc(1)**%abc(12)**;
+
+ %put #%do_over(abc)#;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 6.** Looping over array with *macroquoted* separator.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(alpha[11] (5:15), macarray=Y)
+
+ %let x = %do_over(alpha
+ , phrase = %NRSTR(%alpha(&_I_.))
+ , between= %str( + )
+ );
+ %put &=x.;
+ %put %sysevalf(&x.);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 7.** Working with the `WHICH=` optional parameter
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(test[*] x01-x12, vnames= Y, macarray=Y)
+
+ %put #%do_over(test)#;
+
+ %put #%do_over(test, which= 1 3 5)#;
+
+ %put #%do_over(test, which= 1:5)#;
+
+ %put #%do_over(test, which= 1:5:2 7 8)#;
+
+ %put #%do_over(test, which= L:H l:h)#;
+
+ %put #%do_over(test, which= L:3 10:h)#;
+
+ %put #%do_over(test, which= L:H h:l:-1 13 14)#;
+
+ %put #%do_over(test, which= %eval(1+1):%eval(5+1))#;
+
+ %put #%do_over(test, which= L:H h:l:-1 13 14, between=%str(,))#;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%do_over2()` macro: <<< ################################################
+
+The code of the macro was inspired by
+*Ted Clay's* and *David Katz's* macro `%do_over()`.
+
+The `%DO_OVER2()` macro allows to iterate over *two* macroarray created with the `macarray=Y`
+parameter of the `%ARRAY()` macro.
+
+The `%do_over2()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%do_over2(
+ arrayI
+ ,arrayJ
+ <,phrase=%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.))>
+ <,between=%str( )>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. arrayI - Required, indicates the first macroarray which metadata (Lbound, Hbouns)
+ are to be used in the outer loop in the `%do_over2()`
+
+2. arrayJ - Required, indicates the second macroarray which metadata (Lbound, Hbouns)
+ are to be used in the inner loop in the `%do_over2()`
+
+* phrase= - *Optional*, Default value `%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.))`,
+ a statement to be called in each iteration
+ of the *inner* loop. The outer loop iterator is `_I_`,
+ the inner loop iterator is `_J_`,
+ if you want to use `_I_`, `_J_`, or arrays names
+ [e.g. `%myArr(&_I_.)`] *enclose them* in the `%NRSTR()`
+ macro quoting function.
+
+* between= - *Optional*, Default value `%str( )` (space),
+ a statement to be called in between each
+ iteration of the internal do_over2 loop.
+ If macroquoted (e.g. `%str( + )`) then the `%unquote()`
+ function is automatically applied.
+
+*/
+/*
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Looping over two arrays.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(alpha[*] j k l m n, vnames=Y, macarray=Y)
+ %array( beta[4] (101 102 103 104), macarray=Y)
+
+ %put *%do_over2(alpha, beta
+ , phrase = %NRSTR((%alpha(&_I_.), %beta(&_J_)))
+ )*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 2.** Looping over two arrays with a separator.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(alpha[11] (5:15), macarray=Y)
+ %array( beta[ 4] (101 102 103 104), macarray=Y)
+
+ %let x = %do_over2(alpha, beta
+ , phrase = %NRSTR((%alpha(&_I_.) * %beta(&_J_)))
+ , between= +
+ );
+ %put &=x.;
+ %put %sysevalf(&x.);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Looping over two arrays with *macroquoted* separator.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(alpha[11] (5:15), macarray=Y)
+ %array( beta[ 4] (101 102 103 104), macarray=Y)
+
+ %let x = %do_over2(alpha, beta
+ , phrase = %NRSTR((%alpha(&_I_.) * %beta(&_J_)))
+ , between= %str( + )
+ );
+ %put &=x.;
+ %put %sysevalf(&x.);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## >>> `%do_over3()` macro: <<< ################################################
+
+The code of the macro was inspired by
+*Ted Clay's* and *David Katz's* macro `%do_over()`.
+
+The `%DO_OVER3()` macro allows to iterate over *three* macroarray created with the `macarray=Y`
+parameter of the `%ARRAY()` macro.
+
+The `%do_over3()` macro executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%do_over2(
+ arrayI
+ ,arrayJ
+ ,arrayK
+ <,phrase=%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.) %&arrayK(&_K_.))>
+ <,between=%str( )>
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+1. arrayI - Required, indicates the first macroarray which metadata (Lbound, Hbouns)
+ are to be used in the outer loop in the `%do_over3()`
+
+2. arrayJ - Required, indicates the second macroarray which metadata (Lbound, Hbouns)
+ are to be used in the inner loop in the `%do_over3()`
+
+3. arrayK - Required, indicates the third macroarray which metadata (Lbound, Hbouns)
+ are to be used in the inner loop in the `%do_over3()`
+
+* phrase= - *Optional*, Default value `%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.) %&arrayK(&_K_.))`,
+ a statement to be called in each iteration
+ of the *inner* loop. The *outer* loop iterator is `_I_`,
+ the *middle* loop iterator is `_J_`, the *inner* loop iterator is `_K_`,
+ if you want to use `_I_`, `_J_`, `_K_`, or arrays names
+ [e.g. `%myArr(&_I_.)`] *enclose them* in the `%NRSTR()`
+ macro quoting function.
+
+* between= - *Optional*, Default value `%str( )` (space),
+ a statement to be called in between each
+ iteration of the internal do_over2 loop.
+ If macroquoted (e.g. `%str( + )`) then the `%unquote()`
+ function is automatically applied.
+
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Looping over 3 macroarrays.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(a1_[2] (0 1), macarray=Y)
+ %array(a2_[2] (2 3), macarray=Y)
+ %array(a3_[2] (4 5), macarray=Y)
+
+ %do_over3(a1_, a2_, a3_
+ , phrase = %NRSTR(%put (%a1_(&_I_.), %a2_(&_J_), %a3_(&_K_));)
+ )
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**EXAMPLE 2.** Looping 3 times over a macroarray.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(a[0:2] (0 1 2), macarray=Y)
+
+ %do_over3(a, a, a
+ , phrase = %NRSTR(%put (%a(&_I_.), %a(&_J_), %a(&_K_));)
+ )
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+/*
+## >>> `%make_do_over()` macro: <<< ###########################################
+
+The code of the macro was inspired by
+*Ted Clay's* and *David Katz's* macro `%do_over()`.
+
+The `%make_do_over()` macro allows to generate
+the `%DO_OVER()` macros. It works *only* for *n>3*!
+
+The `%make_do_over()` macro does *not* executes like a pure macro code.
+
+### SYNTAX: ###################################################################
+
+The basic syntax is the following, the `<...>` means optional parameters:
+~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+%make_do_over(
+ size
+)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+**Arguments description**:
+
+size - Required, indicates the number of dimensions
+ (i.e. inner loops) of the `%DO_OVER()` macro.
+
+
+
+### EXAMPLES AND USECASES: ####################################################
+
+**EXAMPLE 1.** Code of created "4-loop" %DO_OVER4() macro
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %macro do_over4(
+ arrayI1,
+ arrayI2,
+ arrayI3,
+ arrayI4,
+ phrase=%nrstr(
+ %&arrayI1(&_I1_.)
+ %&arrayI2(&_I2_.)
+ %&arrayI3(&_I3_.)
+ %&arrayI3(&_I4_.)
+ ),
+ between=%str( )
+ );
+ %local _I1_ _I2_ _I3_ _I4_;
+ %do _I1_ = &&&arrayI1.LBOUND %to &&&arrayI1.HBOUND;
+ %do _I2_ = &&&arrayI2.LBOUND %to &&&arrayI2.HBOUND;
+ %do _I3_ = &&&arrayI3.LBOUND %to &&&arrayI3.HBOUND;
+ %do _I4_ = &&&arrayI4.LBOUND %to &&&arrayI4.HBOUND;
+ %if not (
+ &_I1_. = &&&arrayI1.LBOUND
+ AND &_I2_. = &&&arrayI2.LBOUND
+ AND &_I3_. = &&&arrayI3.LBOUND
+ AND &_I4_. = &&&arrayI4.LBOUND
+ )
+ %then %do;%unquote(&between.)%end;%unquote(%unquote(&phrase.))
+ %end;
+ %end;
+ %end;
+ %end;
+ %mend do_over4;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 3.** Create a 4-loop `%DO_OVER4()` macro
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %make_do_over(4);
+
+ %array(a1_[2] (0 1), macarray=Y)
+
+ %do_over4(a1_, a1_, a1_, a1_
+ , phrase = %NRSTR(%put (%a1_(&_I1_.), %a1_(&_I2_), %a1_(&_I3_), %a1_(&_I4_));)
+ )
+
+ %put *%do_over4(a1_, a1_, a1_, a1_
+ , between = *
+ )*;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**EXAMPLE 3.** Create a 5-loop `%DO_OVER5()` macro
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %make_do_over(5);
+
+ %array(a1_[2] (0 1), macarray=Y)
+
+ %do_over5(a1_, a1_, a1_, a1_, a1_
+ , phrase = %NRSTR(%put (%a1_(&_I1_.), %a1_(&_I2_), %a1_(&_I3_), %a1_(&_I4_), %a1_(&_I5_));)
+ )
+
+ %put *%do_over5(a1_, a1_, a1_, a1_, a1_
+ , between = *
+ )*
+ ;
+
+ options nomprint;
+ data test2;
+ %do_over5(a1_, a1_, a1_, a1_, a1_
+ , phrase = %NRSTR(x1 = %a1_(&_I1_.); x2 = %a1_(&_I2_); x3 = %a1_(&_I3_); x4 = %a1_(&_I4_); x5 = %a1_(&_I5_);)
+ , between = output;
+ )
+ output;
+ run;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+**EXAMPLE 4.** Create all from 6 to 10 "do_overs"
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.sas}
+ %array(loop[6:10] (6:10), macarray=Y)
+ %do_over(loop
+ , phrase = %nrstr(
+ %make_do_over(%loop(&_I_.))
+ )
+ );
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+## License ####################################################################
+Copyright (c) Bartosz Jablonski, since January 2019
+
+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/macroarray.zip b/packages/macroarray.zip
index 0325ac7..50d13c5 100644
Binary files a/packages/macroarray.zip and b/packages/macroarray.zip differ