/**
   @file combo.sas
   @brief This macro creates combination items and virtual claims.

   @details 
	- 1. INPUTS PHASE:			
		- Execute %combo_processinputfiles if run as stand alone	
	- 2. MSCDM EXTRACTION:			
		- Copy MSCDM headers/formats into an empty dataset such that there is no need for multiple "existence" management thereafter.		
			- Cleaning lookup file in case the code is used both the combo and the cida settings.	
			- Run envelope for diagnoses.	
		- Copy MSCDM headers/formats into an empty dataset such that there is no need for multiple "existence" management thereafter.		
			- Create two groups of claims (potentially non mutually exclusive): 
				- Raw records for needed by the combo tool, and those needed by the MP.	
		- Copy formats/lengths of the DP.	         	
			- At least one combo with enrollment criterion.	
		- Copy formats/lengths of the DP.	         	
			- At least one combo with encounter criterion.	
				- Take the smallest LOS possible across CombGroups to extract all possible eligible patients.
				- EncType and Caresetting extraction.
				- DRG Extraction (if requested).
				- Encounter observations may have missing DDates.
					- Use a max function to assign ADate to DDate when this is the case (as it is done in MPs).
		- Copy formats/lengths of the DP.		
			- At least one combo with demographic criterion:	
				- Sex extraction.
				- Races extraction.
				- Hispanic extraction.
	- Utility Macros for 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION:			
		- Prepare diagnosis records:	
			- Restrict to diagnosis inputs.	
				- Select required raw records and add combo input file information to this-combo raw records.	
			- Apply caresetting criteria.	
			- Apply extra criterion for PDX only in the presence of IP.	
			- Create claim end date.	
		- Prepare procedure records:		
			- Restrict to procedure inputs.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Apply caresetting criteria.	
			- Create claim end date.	
		- Prepare raw dispensing records:		
			- Restrict to drug inputs.				
			- Set defaults and merge in information from original stockpiling file.	
			- De-duplicate rows to correctly assign stocknumber based on group.	
			- Join stocknumber back onto original dataset.	
			- Store maximum number of combination of stockpiling parameters.	
			- Only process dataset if there are subsetted observations.	
			- Stack all datasets together.	
		- Creating Edate.		
			- Parameter RawDurat commented on purpose.	
		- Prepare enrollment records:		
			- Restrict to eligibility inputs.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Combine the three types, duplicates records only at the RawOrder level.	
		- Prepare encounter records:		
			- Restrict to encounter based on EncType inputs.	
				- Select required raw records and add combo input file information to combo raw records.
			- Restrict to encounter based on DRG inputs (if requested).
				- Select required raw records and add combo input file information to combo raw records.	
			- Combine the two types, duplicates records only at the RawOrder level after nodupkey.	
		- Prepare demographic records		
			- Restrict to demogs based on sex.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Restrict to demogs based on race.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Restrict to demogs based on hispanic.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Combine the two types, duplicates records only at the RawOrder level after nodupkey.	
		- Prepare laboratory records:		
			- Lab extraction based on lookup - LAB01.	
				- Obtain the ms_test_name, ms_test_sub_category, specimen_source, ms_result_unit result_type, fast_ind and pt_loc based on RawCode.	
				- Code in infolder.&LABCODESMAP. should be unique.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Lab extraction based on LOINC - LAB02.	
				- Select required raw records and add combo input file information to combo raw records.	
			- Lab extraction based on PX - LABXX (XX not in (01 02)).	
				- Select required raw records and add combo input file information to combo raw records.	
			- Combine the three types, duplicates records only at the RawOrder level after nodupkey.	
			- Removing the true duplicates as a record could have been extracted three time but using three different criteria.	
		- Episode creation required (RawEpiType variable > 0).		
		- Create episodes considering allowed gaps.		
		- Restrict to diagnosis-like combo virtual claims:		
			- Select required raw virtual records and add combo input file information to this-combo raw records.	
			- Apply caresetting criteria.	
			- Apply extra criterion for PDX only in the presence of IP.	
			- Create claim end date.	
			- Set macro variable to dynamically.	
		- Restrict to procedure-like combo virtual claims:		
			- Select required raw records and add combo input file information to combo raw records.	
			- Apply caresetting criteria.	
			- Create claim end date.	
			- Set macro variable to dynamically.	
		- Restrict to Drug-like combo virtual claims:		
			- Select required raw records and add combo input file information to combo raw records.	
			- Apply caresetting criteria.	
			- Create claim end date.	
			- Set macro variable to dynamically.	
	- START 3. COMBGROUP LOOP:			
		- Extract the number of combo groups for iteration loop.		
		- Loop through ComboGroups.		
			- Get current combo group.	
			- Input parameters for the raw records fileres and to identify the combos.	
			- Input parameters to create the virtual record.
		- 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION:			
			- Initialize two_by_two.		
			- Create episode if necessary.		
			- No combos to create because there is only one RawOrder value, proceed to formatting the _One dataset as if final.		
				- Create Virtual claim Adate.	
				- Create Virtual claim Edate.	
		- 3.2. TWO-BY-TWO LOOP:			
			- Loop through items to combine.		
				- Check to see if this step is to apply an exclusion rule.	
				- Select records to combine to _One dataset - leave the rest in _ForCombine for the next iteration of the TwoByTwo method.	
				- Create Episodes (if necessary).	
				- Create Overlap looking start date.	
				- Create Overlap looking stop date.	
				- Identify combinations.	
				- In the case where exclusion=1, we will only have kept the overlaping records to exclude. 
					- Remerge by NumLine and only keep those lines that are not in _TwoByTwo.	
				- Apply final criteria to overlapping records.	
				- Create Virtual claim ADate.	
				- Create Virtual claim EDate.	
				- Because of the the flexibility of RawDaysFromStartDt and RawDaysFromEndDt combined with the selection of dates for the virtual claim, check that the event did not end before starting.	
	- 4. OUTPUT:		             	
		- Get specific data partners formats and lengths.		
		- Keep the virtual records in a permanent file.	
  
   @par Program inputs
	- indata.&DEMTABLE. (Dataset with demographic data.)
	- indata.&DIATABLE. (Dataset with diagnosis data.)
	- indata.&DISTABLE. (Dataset with dispensing data.)
	- indata.&ENCTABLE. (Dataset with encounter data.)
	- indata.&ENRTABLE. (Dataset with enrollment data.)
	- indata.&LABTABLE. (Dataset with lab result data.)
	- indata.&PROCTABLE. (Dataset with procedure data.)
	- infolder.&CODESFILE. (Dataset defining the combo codes input file.)
	- infolder.&COMBFILE. (Dataset defining the combo input file.)
	- infolder.&LABCODESMAP. (Dataset for lab result extraction.)
	- infolder.&STOCKPILINGFILE. (Dataset with valid dispensings selection used by the stockpiling algorithm to create exposure episodes.)
	- work._diag (Lookup dataset containing diagnosis codes to extract (if running as part of QRP).
	- work._lab (Lookup dataset containing raw lab result codes to extract (if running as part of QRP).
	- work._ndc (Lookup dataset containing dispensing codes to extract (if running as part of QRP).
	- work._proc (Lookup dataset containing procedure codes to extract (if running as part of QRP).
	- work._clab (Lookup dataset containing lab result codes to extract (if running as part of QRP).
	- work._cenr (Lookup dataset containing enrollment records to extract (if running as part of QRP).
	- work._cenc (Lookup dataset containing encounter records to extract (if running as part of QRP).
	- work._cdem (Lookup dataset containing demographic records to extract (if running as part of QRP).

   @par Program outputs
	- &SAVEDATALIBREF.&OUTNAME._diag (Dataset containing combination items that behave like DX.)
	- &SAVEDATALIBREF.&OUTNAME._drug (Dataset containing combination items that behave like RX.)
	- &SAVEDATALIBREF.&OUTNAME._proc (Dataset containing combination items that behave like PX.)
	- work._clab (Lookup dataset for raw labe result record extraction.)
	- work._codesfile (Dataset containing the DX combo codes.)
	- work._combfile (Dataset from the combo input file.)
	- work._combgrouplist (Dataset containing unique values of CombOrder CombGroup.)
	- worktemp._diagextract (Dataset containing combination items that behave like DX.)
	- worktemp._drugs (Dataset containing RX to extract.)
	- worktemp._procextract (Dataset containing combination items that behave like PX.)
	- worktemp.drugscomb (Dataset containing combination items that behave like RX.)
	- worktemp.labextract (Dataset containing labs to extract.)

* **Usage**

	  %combo(COMBFILE=&COMBOFILE.,
	         CODESFILE=&COMBOFILE.Codes,
			 RUNSTANDALONE=N,
	         DXENVEL=&DXENVEL.,
	         PXENVEL=&PXENVEL.,
	         OUTNAME=&RUNID._Combo,
	         STOCKPILING=1,
	         STOCKPILINGFILE=&COMBOFILE.Stock,
	         ENROLGAP=0,
	         LABCODESMAP=&LABCODESMAP.,
	         SAVEDATALIBREF=%if &QRP_DEBUG.=Y %then %do; DPLOCAL %end;,
	         PTSTOEXCLUDE=&PTSTOEXCLUDE.,
			 ENCIDTOEXCLUDE=&ENCIDTOEXCLUDE.);

   @param [in] COMBFILE Name of the SAS dataset defining the Combo Input File.
   @param [in] CODESFILE Name of the SAS dataset defining the Combo Codes Input File. 
   @param [in] RUNSTANDALONE Y/N indicator whether Combo is executing within QRP (N) or as a standalone macro (Y)
   @param [in] DXENVEL Indicator to determine if the envelope should be run on diagnosis claims.                                              
   @param [in] PXENVEL Indicator to determine if the envelope should be run on procedure claims.                                                              
   @param [out] OUTNAME Name of the prefix that will be used to produce the output files in DPLocal if the SAVETODPLOCAL parameter is set to Y.            
   @param [in] STOCKPILING Indicator to determine if stockpiling should be executed on outpatient pharmacy dispensings.                            
   @param [in] STOCKPILINGFILE Name of the SAS dataset defining the stockpiling settings for each RawGroup found in the Combo Codes Input File.                                                                    
   @param [in] ENROLGAP The number of days that will be bridged between two consecutive enrollment periods to create a "continuously enrolled" period.                                             
   @param [in] LABCODESMAP Name of the SAS dataset defining the Lab Code Map File.                         
   @param [in] SAVEDATALIBREF Libname where output files should be saved. If blank and RUNSTANDALONE = Y, the default will be work. If RUNSTANDALONE = N, the default will be to not save files.                    
   @param [in] PTSTOEXCLUDE Name of the SAS dataset defining the patients to exclude from MSDD data extraction.
   @param [in] ENCIDTOEXCLUDE Name of the SAS dataset defining the encounterids to exclude from MSDD data extraction.
   @param [in] LEFTCENSORDATE Extract all claims occuring on or after this date. A lookback period will be added to this date using RawDaysFromStartDt and RawDaysFromEndDt.
   @param [in] DXCOMBOLIST List of DX combo codes that are utilized within QRP.
   @param [in] PXCOMBOLIST List of PX combo codes that are utilized within QRP.

<h4> SAS Macros Dependencies </h4>
   @li combo_processinputfiles.sas
   @li ms_delencounterids.sas
   @li ms_delpatients.sas
   @li ms_envelope.sas
   @li ms_episoderec.sas
   @li ms_extractdrugs.sas
   @li ms_extractlabs.sas
   @li ms_extractmeds.sas
   @li ms_stockpiling.sas
   @li ms_time.sas

   @author Sentinel Coordinating Center (info@sentinelsystem.org)
**/

%MACRO combo(COMBFILE=,
			 CODESFILE=,
			 RUNSTANDALONE=,
			 DXENVEL=,
			 PXENVEL=,
			 OUTNAME=,
			 STOCKPILING=,
             STOCKPILINGFILE=,
			 ENROLGAP=,
			 LABCODESMAP=,
			 SAVEDATALIBREF=,
			 PTSTOEXCLUDE=,
			 ENCIDTOEXCLUDE=,
			 LEFTCENSORDATE=,
			 DXCOMBOLIST=,
			 PXCOMBOLIST=);

	%PUT =====> MACRO CALLED: combo;   

	/*Set performance time variables*/

    data _NULL_;
       temp=DATETIME();
       call symputx("STARTCOMBO",put(temp,best.));       
    run;

	/*Utility Macros*/

	/*Macros to capture looping macros runtimes*/ 	
	%macro StartLoopRuntime();
		data _NULL_;
	       temp=DATETIME();
	       call symputx("STARTLOOP",put(temp,best.));	      
	    run;
	%mend StartLoopRuntime;

	%macro StopLoopRuntime(macroname=);
		data _NULL_;
	       temp=DATETIME();
	       seconds=temp-&STARTLOOP.;
	       hours=int(seconds/3600);
	       minutes=int((seconds-hours*3600)/60);
	       seconds2=int((seconds-hours*3600-minutes*60));	       
	       call symput('hoursLoop',put(hours,4.0));
	       call symput('minutesLoop',put(minutes,2.0));
	       call symput('secondsLoop',put(seconds2,2.0));
	    run;
		
		%put TOTAL RUN TIME FOR &macroname. was &hoursLoop. h &minutesLoop. m &secondsLoop. s; 
	%mend StopLoopRuntime;

	/*Macro to determine whether a dataset is empty or not*/
	%MACRO ISDATA(dataset=);
	    %GLOBAL NOBS;
	    %let NOBS=0;
	    %if %sysfunc(exist(&dataset.))=1 and %LENGTH(&dataset.) ne 0 %then %do;
	        data _null_;
	        dsid=open("&dataset.");
	        call symputx("NOBS",attrn(dsid,"NLOBS"));
	        run;
	    %end;   
	%PUT &NOBS.;
	%MEND ISDATA;

    /*Macro to check whether an optional variable exists in an input file*/
    %MACRO VAREXIST(DS,VAR);
    %GLOBAL VAREXIST;
    %LET VAREXIST=0;
    %LET DSID = %SYSFUNC(OPEN(&DS));
    %IF (&DSID) %THEN %DO;
        %IF %SYSFUNC(VARNUM(&DSID,&VAR)) %THEN %LET VAREXIST=1;
        %LET RC = %SYSFUNC(CLOSE(&DSID));
    %END;
    %MEND VAREXIST;

    %MACRO IMPORTFILES(var=);

        %IF %INDEX(%UPCASE("&VAR."),CPORT) %THEN %DO;
            proc cimport infile="&infolder.&VAR." library=infolder memtype=data;
            run;
        %END;
    
    %MEND IMPORTFILES;
        
/**************************************************************************************************/
/* 1. INPUTS PHASE: if runing as part of QRP, code lists have already been imported and processed */
/**************************************************************************************************/

	%if &RUNSTANDALONE eq N %then %do;
		%ms_starttimer(timestart=comboruntime);
	%end;

    %LET COMBFILE=%LOWCASE(&COMBFILE.);
    %LET CODESFILE=%LOWCASE(&CODESFILE.);

	%if &runstandalone = Y %then %do;
		%let DXCOMBOLIST=;
		%let PXCOMBOLIST=;
		%combo_processinputfiles(COMBFILE=&COMBOFILE.,
                   				 CODESFILE=&COMBOFILE.Codes);
	%end;

	*check if we need to keep RawStockGroup after stockpilling;
	%let keepRawStockgroup=0;
	proc sql noprint;
	select count(*) into :keepRawStockgroup trimmed from _COMBFILE where RawEpiType = 2;
	quit;

	*If LEFTCENSORDATE is specified, compute lookback period;
	%let minextractdate=;
	%if %str("&LEFTCENSORDATE") ne %str("") %then %do;
		/* Compute lookback period. The smallest sum of min(negative RawDaysFromStartDt, negative RawDaysFromEndDt) across Combo groups will ensure we extract everything needed */
		data _leftdays;
		set _COMBFILE(keep=RawDaysFromStartDt RawDaysFromEndDt CombGroup);
		format leftdaysFromStart leftdaysFromEnd leftdays best8.;

		if strip(RawDaysFromStartDt)="" then leftdaysFromStart=0;
		else if index(RawDaysFromStartDt,",")>0 then leftdaysFromStart=input(substr(RawDaysFromStartDt, 1,index(RawDaysFromStartDt,",")-1),best8.);
		else leftdaysFromStart=input(RawDaysFromStartDt, best8.);
		
		if strip(RawDaysFromEndDt)="" then leftdaysFromEnd=0;
		else if index(RawDaysFromEndDt,",")>0 then leftdaysFromEnd=input(substr(RawDaysFromEndDt, 1,index(RawDaysFromEndDt,",")-1),best8.);
		else leftdaysFromEnd=input(RawDaysFromEndDt, best8.);

		/* We do not want to consider positive numbers in the sum as they are related to a look forward window so the maximum leftdays value is 0 */
		if leftdaysFromStart > 0 then leftdaysFromStart = 0;
		if leftdaysFromEnd > 0 then leftdaysFromEnd = 0;

		leftdays=min(leftdaysFromStart,leftdaysFromEnd);
		run;		

		proc means data=_leftdays nway noprint;
		class CombGroup;
		var leftdays;
		output out=_leftdays(drop=_:) sum=;
		run;

		proc sql noprint;
		select min(leftdays) into :leftdays from _leftdays;
		quit;

		%put &=leftdays;

		%let minextractdate=&LEFTCENSORDATE. + &leftdays.;		
	%end;

/***********************/
/* 2. MSCDM EXTRACTION */
/***********************/

    %MACRO EXTRACTCOMBMEDS();
        *Copy MSCDM headers/formats into an empty dataset such that 
         there is no need for multiple "existence" management thereafter;
        data _ComboProc   /*Required by Combo Tool*/
             worktemp._procextract /*Required by MPs*/; 
        set indata.&proctable.(obs=0);
        keep PatID ADate EncType PX PX_CodeType; 
        run;
       
		%ISDATA(dataset=_proc);
        %IF %eval(&NOBS.>0) %THEN %DO; 

           %ms_extractmeds(datafile=indata.&proctable.,
                           datacode=PX ,
                           datacodetype=PX_codetype ,
                           lookfile=_proc, 
                           mindate=&minextractdate.,
                           outfile=worktemp._procextract(keep=PatID ADate EncType PX PX_CodeType qrpcombocode %if %str(&ENCIDTOEXCLUDE.) ne %str() %then %do; encounterid %end;));

           *delete data for patients/encounterids in DPs &PTSTOEXCLUDE./&ENCIDTOEXCLUDE. files; 
           %ms_delpatients(datafile=worktemp._procextract,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=worktemp._procextract);
		   	
		   %ms_delencounterids(datafile=worktemp._procextract,
			                   EncIdsfile=&ENCIDTOEXCLUDE.,
			                   Outfile=worktemp._procextract); 			

            %IF &PXENVEL.=1 %THEN %DO; *&PXENVEL.= 0 can only happen standalone use of combo;
                %isdata(dataset=worktemp._procextract);
                %IF %eval(&NOBS.>=1) %THEN %DO;
                %MS_ENVELOPE(INFILE=worktemp._procextract,
                             ENCOUNTERINFILE=indata.&enctable.,
							 MINDATE=&minextractdate.,
                             OUTFILE=worktemp._procextract);
                %END;
            %END;

            data _ComboProc
                 worktemp._procextract /*Required by MPs*/;
            set worktemp._procextract;
            PX=compress(PX,'.');
            *Records specific to the creation of Combos;
            if qrpcombocode in ('B', 'C') then output _ComboProc;
            *Records that are specific to the Modular Program;
            if qrpcombocode in ('B', 'Q') then output worktemp._procextract;
            keep PatID ADate EncType PX PX_CodeType; 
            run;

            Proc sort nodupkey data=_ComboProc;
            by _ALL_;
            run;

        %END;

        data _ComboDiag   /*Required by Combo Tool*/
             worktemp._diagextract /*Required by MPs*/;
        set indata.&Diatable.(obs=0);
        keep PatID ADate EncType DX DX_CodeType PDX; 
        run;

        %ISDATA(dataset=_diag);
        %IF %eval(&NOBS.>0) %THEN %DO; 

           %ms_extractmeds(datafile=indata.&diatable.,
                           datacode=DX ,
                           datacodetype=DX_codetype ,
                           lookfile=_diag, 
                           mindate=&minextractdate.,
                           outfile=worktemp._diagextract(keep = PatID ADate EncType DX DX_CodeType PDX qrpcombocode ));

           *delete data for patients in DPs &PTSTOEXCLUDE. files;
           %ms_delpatients(datafile=worktemp._diagextract,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=worktemp._diagextract);			

    		*Run envelope for diagnoses (macro parameter);
            %macro wrapper;
            %IF %eval(&DXENVEL.=1) %THEN %DO;  *&DXENVEL.= 0 can only happen standalone use of combo;
                %isdata(dataset=worktemp._diagextract);
                %IF %eval(&NOBS.>=1) %THEN %DO;
                %MS_ENVELOPE(INFILE=worktemp._diagextract,
                             ENCOUNTERINFILE=indata.&enctable.,
							 MINDATE=&minextractdate.,
                             OUTFILE=worktemp._diagextract);
                %END;
            %END;
            %mend;
            %wrapper;

            data _ComboDiag
                 worktemp._diagextract /*Required by MPs*/;
            set worktemp._diagextract;
            DX=compress(DX,'.');
            *Records specific to the creation of Combos;            
            if qrpcombocode in ('B', 'C') then output _ComboDiag;
            *Records that are specific to the Modular Program;
            if qrpcombocode in ('B', 'Q') then output worktemp._diagextract;
            keep PatID ADate EncType DX DX_CodeType PDX; 
            run;

            Proc sort nodupkey data=_ComboDiag;
            by _ALL_;
            run;
        
        %END;
    %MEND EXTRACTCOMBMEDS;
    %ExtractCombMeds;

    %MACRO EXTRACTCOMBDRUGS();

        *Copy MSCDM headers/formats into an empty dataset such that 
         there is no need for multiple "existence" management thereafter;
	    data worktemp._drugs;
	        set indata.&distable.(obs=0)  _ndc(obs=0);
	        rename RxDate=ADate;
	        qrpcombocode='';
	        RawCode=Rx;
	        drop Rx;       
        run;

        %ms_extractdrugs(datafile=indata.&distable.,
                      lookfile=_ndc, 
                      lookvar=code , 
                      mindate=&minextractdate.,
                      outfile=_ComboDrug);

       *delete data for patients in DPs &PTSTOEXCLUDE. files; 
       %ms_delpatients(datafile=_ComboDrug,
                       ptsfile=&PTSTOEXCLUDE.,
                       Outfile=_ComboDrug);

        *Create two groups of claims (potentially non mutually exclusive): 
         Raw records for needed by the combo tool, and those needed by the MP;
        data _ComboDrug(rename=RxDate=Adate)
             worktemp.Drugs;  /*For MP linking Only*/
        	set _ComboDrug(drop=code);
			if qrpcombocode in ('B', 'C') then output _ComboDrug;
            if qrpcombocode in ('B', 'Q') then output worktemp.Drugs;
			drop qrpcombocode;
        run;

        proc sort nodupkey data = _ComboDrug;
           by _ALL_;
        run;
    %MEND EXTRACTCOMBDRUGS;
    %EXTRACTCOMBDRUGS();

    %MACRO EXTRACTENR();

        %GLOBAL MedCov;
        %GLOBAL DrugCov;
        %GLOBAL MedDrugCov;

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboEnr1
             _ComboEnr2
             _ComboEnr3;
        set indata.&enrtable.(obs=0 keep=patid);		
        EStart=.;
        EEnd=.;        
        run;

        *At least one combo with enrollment criterion;
        %ISDATA(dataset=_cenr);        
            %IF %EVAL(&NOBS.>0) %THEN %DO;

                %LET MedCov=No;
                %LET DrugCov=No;
                %LET MedDrugCov=No;

                data _null_;
                   set _cenr;
                   if substr(RawCodeType,3,2) in('01') then call symput("MedCov","Yes");
                   if substr(RawCodeType,3,2) in('02') then call symput("DrugCov","Yes");
                   if substr(RawCodeType,3,2) in('03') then call symput("MedDrugCov","Yes");
                run;

                %PUT &MedCov.;
                %PUT &DrugCov.;
                %PUT &MedDrugCov.;

                %IF &MedCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.(keep=Patid Enr_Start Enr_End MedCov DrugCov Chart),
                                   OUTFILE=_ComboEnr1,
                                   MEDCOV=Yes,  
                                   DRUGCOV=No,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr1,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr1);
                %END;
                %IF &DrugCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.(keep=Patid Enr_Start Enr_End MedCov DrugCov Chart),
                                   OUTFILE=_ComboEnr2,
                                   MEDCOV=No,   
                                   DRUGCOV=Yes,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr2,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr2);
                %END;
                %IF &MedDrugCov.=Yes %THEN %DO;
                    %MS_EPISODEREC(INFILE=indata.&enrtable.(keep=Patid Enr_Start Enr_End MedCov DrugCov Chart),
                                   OUTFILE=_ComboEnr3,
                                   MEDCOV=Yes,  
                                   DRUGCOV=Yes,        
                                   ENRSTART=Enr_Start,
                                   ENREND=Enr_End,
                                   ENROLGAP=&ENROLGAP.,
                                   REMOVEDUP=Yes);
                    *delete data for patients in DPs &PTSTOEXCLUDE. files; 
                    %ms_delpatients(datafile=_ComboEnr3,
                                   ptsfile=&PTSTOEXCLUDE.,
                                   Outfile=_ComboEnr3);
                %END;
            %END;     
		
		data _ComboEnr;	 
		set _ComboEnr1(keep= patid EStart EEnd)
            _ComboEnr2(keep= patid EStart EEnd)
            _ComboEnr3(keep= patid EStart EEnd);
		length EnrollmentLength 4;
		EnrollmentLength = EEnd - EStart + 1;
		run;

		proc datasets library=WORK nowarn nolist;
        delete _ComboEnr1 _ComboEnr2 _ComboEnr3;
        quit;
    %MEND EXTRACTENR;
    %EXTRACTENR;

    %MACRO EXTRACTENC();

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboEnc;
        set indata.&enctable.(obs=0);
        keep PatID Adate Ddate EncType DRG; 
        run;

		%global numenctype drgs;
        %LET enctypes='AV' 'IP' 'OA' 'IS' 'ED';
        %LET los=.;
        %LET drgs='-';
		%let numenctype=0;

        *at least one combo with Encouter criterion;
        %ISDATA(dataset=_cenc);
        %IF %EVAL(&NOBS.>0) %THEN %DO;

            *Take the smallest LOS possible across CombGroups to extract all possible eligible patients;                
            data _los;
            set _cenc;
            where index(CodeType,'01');
            LOS=input(RawCode,best.); 
            keep LOS;   
            run;

            proc sort data=_los;
            by LOS;                             
            run;

            data _null_;
            set _los;               
            if _N_=1 then call symput("los",put(LOS,best.));
            run;                
            %put &los.;

            *EncType and Caresetting extraction;
            proc sort nodupkey data=_cenc out=_enctype(keep=RawCaresetting);
            by RawCaresetting;
            where index(CodeType,'01');
            run;

            %ISDATA(dataset=_enctype);
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                data _enctype;
                set _enctype;
                format EncType $30.;
                EncType=trim(left(RawCaresetting));                 
                drop RawCaresetting;
                run;

                proc sql noprint;
                select distinct cat('"',tranwrd(strip(EncType)," ",'" "'),'"')
                   into :EncTypes separated by ' '
                   from _EncType;
                quit;
                %put &EncTypes.;
            %END;

			%let numenctype = %sysfunc(countw(&EncTypes.));

            *DRG Extraction;
            proc sort nodupkey data=_cenc out=_drg(keep=RawCode);
            by RawCode;
            where index(RawCodeType,'02');
            run;

            %ISDATA(dataset=_drg);
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                data _drg;
                set _drg;
                format drg $6.;
                drg="'"||trim(left(RawCode))||"'";
                drop RawCode;
                run;

                proc sql noprint;
                select drg
                   into :drgs separated by ','
                   from _drg;
                quit;
                %put &drgs.;
            %END;

            data _ComboEnc %if %eval(&numenctype.)=1 & &drgs. = '-' %then %do;(drop=enctype) %end;;
				set indata.&enctable.(keep= PatID Adate Ddate EncType %if &drgs. ne '-' %then %do; DRG %end; %if %str(&ENCIDTOEXCLUDE.) ne %str() %then %do; encounterid %end;);
	            *Encounter observations may have missing DDates. Therefore we use a max function to
	             assign ADate to DDate when this is the case (as it is done in MPs);
	            where ((EncType in(&enctypes.) and (max(Ddate,ADate) - Adate + 1) >= &LOS.)
				%if &drgs. ne '-' %then %do; or DRG in:(&drgs.) %end;)
				%if %str(&minextractdate.) ne %str() %then %do; and adate >= &minextractdate. %end;;

	            if missing(DDate) then DDate=ADate;
            run;

           *delete data for patients/encounterids in DPs &PTSTOEXCLUDE./&ENCIDTOEXCLUDE. files;
           %ms_delpatients(datafile=_ComboEnc,
                           ptsfile=&PTSTOEXCLUDE.,
                           Outfile=_ComboEnc);
		  
		   %ms_delencounterids(datafile=_ComboEnc,
			                   EncIdsfile=&ENCIDTOEXCLUDE.,
			                   Outfile=_ComboEnc); 			

			proc datasets noprint nowarn lib=work;
				delete _enctype _drg;
			quit;
    	%END;
    %MEND EXTRACTENC;
    %EXTRACTENC;

    %MACRO EXTRACTDEM();

        *This is to copy formats/lengths of the DP for the proc append;
        data _ComboDem;
        set indata.&demtable.(obs=0);
        keep PatID Sex Hispanic Race; 
        run;        

        *at least one combo with Demographic criterion;
        %ISDATA(dataset=_cdem);
        
        %MACRO WRAPPER;
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                
                *Sex Extraction;
                proc sort nodupkey data=_cdem out=_sex(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'01');
                run;

                %LET sexes='F' 'M' 'U' 'A';

                %ISDATA(dataset=_sex);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select cat('"',tranwrd(strip(RawCode)," ",'" "'),'"')
                       into :sexes separated by ' '
                       from _sex;
                    quit;
                    %put &sexes.;
                %END;

                *Races Extraction;
                proc sort nodupkey data=_cdem out=_race(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'02');
                run;
    
                %LET races='0' '1' '2' '3' '4' '5' 'M';

                %ISDATA(dataset=_race);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select cat('"',tranwrd(strip(RawCode)," ",'" "'),'"')
                       into :races separated by ' '
                       from _race;
                    quit;
                    %put &races.;
                %END;

                *Hispanic Extraction;
                proc sort nodupkey data=_cdem out=_hispanic(keep=RawCode);
                by RawCode;
                where index(RawCodeType,'03');
                run;

                %LET hispanics='Y' 'N' 'U';

                %ISDATA(dataset=_hispanic);
                %IF %EVAL(&NOBS.>0) %THEN %DO;                  
                    proc sql noprint;
                    select cat('"',tranwrd(strip(RawCode)," ",'" "'),'"')
                       into :hispanics separated by ' '
                       from _hispanic;
                    quit;
                    %put &hispanics.;
                %END;

                data _ComboDem;
                set indata.&demtable.;              
                where upcase(sex) in (&sexes.) and upcase(hispanic) in (&hispanics.) and upcase(race) in (&races.);             
                keep PatID Sex Hispanic Race; 
                run;
               *delete data for patients in DPs &PTSTOEXCLUDE. files; 
               %ms_delpatients(datafile=_ComboDem,
                               ptsfile=&PTSTOEXCLUDE.,
                               Outfile=_ComboDem);

			proc datasets nowarn noprint lib=work;
				delete _sex _race _hispanic;
			quit;

            %END;
        %MEND WRAPPER;
        %WRAPPER;
    %MEND EXTRACTDEM;
    %EXTRACTDEM;

    %MACRO EXTRACTCOMBOLAB();

        proc means data=_lab nway noprint missing;
        var Comb cida ;
        class Code Codetype RawLabResult RawLabDateType RawCaresetting RawGroup;
        output out=_lab_(keep=code Codetype RawLabResult RawLabDateType RawCaresetting RawGroup comb cida ) max= / keeplen;
        run;

		data _lab_(drop=cida comb);
			set _lab_;
			if comb = 1 and cida ne 1 then qrpcombocode='C'; /*used in combo, not in QRP*/
			else if comb ne 1 and cida = 1 then qrpcombocode='Q'; /*used in QRP, not in combo*/
			else if comb = 1 and cida = 1 then qrpcombocode='B'; /*used in combo and QRP*/
		run;

        %ms_extractlabs(datafile=indata.&labtable.,
                        lookfile=_lab_, 
                        mindate=&minextractdate.,
                        outfile=worktemp.labextract,
                        Lab_var = Raw);

       *delete data for patients in DPs &PTSTOEXCLUDE. files; 
        %ms_delpatients(datafile=worktemp.labextract,
                       ptsfile=&PTSTOEXCLUDE.,
                       Outfile=worktemp.labextract);

        %isdata(dataset=worktemp.labextract);

        data _ComboLab(keep=patid Adate ms_result_c ms_result_n ms_test_name  ms_test_sub_category specimen_source 
                            ms_result_unit result_type fast_ind pt_loc loinc px px_codetype ResultTyp RawGroup)
             worktemp.labextract /*Required by MPs*/;
        set worktemp.labextract;
        PX=compress(PX,'.');
        %if %eval(&nobs.>0) %then %do;
            *Records specific to the creation of Combos;
			if qrpcombocode in ('B', 'C') then output _ComboLab;
            *Records that are specific to the Modular Program;
            if qrpcombocode in ('B', 'Q') then output worktemp.labextract;
            drop qrpcombocode;
        %end;
        %else %do;
            ResultTyp = '';
        %end;
        run;
		
        Proc sort nodupkey data=_ComboLab;
		* Same as by _ALL_ but specifying key variables first for the set by below;
        by ms_test_name ms_test_sub_category specimen_source ms_result_unit result_type fast_ind pt_loc RawGroup
		   patid Adate ms_result_c ms_result_n loinc px px_codetype ResultTyp ;
		run;

		*When extracting labs in the LoopLab macro below during the looping algorithm it will be more efficient to 
		 use a unique key for extraction using labcodesmap instead of a 8 variables defining key;
		data _ComboLab;		
		set _ComboLab;
		by ms_test_name ms_test_sub_category specimen_source ms_result_unit result_type fast_ind pt_loc RawGroup;
		format NewRawGroup $&rawgroup_len..;
		length labkey 4;
		NewRawGroup=RawGroup;
		if _N_=1 then labkey=0;
		if first.Rawgroup then labkey=labkey+1;
		retain labkey;
		drop RawGroup;
		rename NewRawGroup=RawGroup;
		run;

		proc sort nodupkey data=_ComboLab(keep=ms_test_name ms_test_sub_category specimen_source ms_result_unit result_type fast_ind pt_loc RawGroup labkey) out=_labkey;
		by labkey;
		run;

		*Code in infolder.&LABCODESMAP. should be unique. Sorting for usage in LoopLab;
        proc sort nodupkey data=infolder.&LABCODESMAP.(rename=Code=RawCode) out=_labcodesMap;
        by RawCode;
        run;

        Proc sort nodupkey data=_ComboLab;
        by _ALL_;
        run;

    proc datasets library=WORK nowarn nolist;
    delete _lab_;
    quit;
    %MEND EXTRACTCOMBOLAB;

    %macro RunLab;
    %ISDATA(dataset=_lab);
    %IF %EVAL(&NOBS.>0) %THEN %DO;
        %EXTRACTCOMBOLAB;
    %end;
    %mend RunLab;
    %RunLab;

/*****************************************************************/
/* Utility Macros for 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION */
/*****************************************************************/

    *Prepare diagnosis records (Duplic inevitable);
    %MACRO LoopDiag();    
		%StartLoopRuntime();
 
        *Select required raw DX records and add combo input file information to this-combo raw records;
        data _LoopDiag;
		if 0 then set _LoopRawDX;
		declare hash h(dataset:'_LoopRawDX',HASHEXP:8, multidata:'Y');
		h.definekey('DX_Codetype','DX');
		h.definedata(all:'yes');
		h.definedone();

		do until (eof);
			set _ComboDiag end=eof;			
			*create claim end date;
	        format Edate mmddyy10.; 
			length EDate 4; 
	        if not rawdurat then edate = adate;
	        else edate = sum(adate,rawdurat,-1);
	        keep Patid Adate Edate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;

			if h.num_items>0 then do;
				rc = h.find();
				do while (rc = 0);
				   	if (/*Apply caresetting criteria*/
					    ((RawCaresetting ne "" and indexw(upcase(RawCaresetting),upcase(EncType))) or RawCaresetting eq "") and
					    /*Apply extra criterion for PDX only in the presence of IP (defensive coding)*/
					    ((RawPDX ne "" and upcase(EncType) in('IP') and upcase(PDX) eq upcase(RAWPDX)) or (RawPDX eq "" or upcase(EncType) not in ('IP')))
					   ) then output;
				 	rc = h.find_next();
				end;
				drop rc;
			end;
		end;
		run;
			
		%StopLoopRuntime(macroname=LoopDiag);
    %MEND LoopDiag;

    *Prepare Procedure records (Duplic inevitable);
    %MACRO LoopProc();  
		%StartLoopRuntime();
 
		*Select required raw PX records and add combo input file information to combo raw records;
		data _LoopProc;
		if 0 then set _LoopRawPX;
		declare hash h(dataset:'_LoopRawPX',HASHEXP:8, multidata:'Y');
		h.definekey('PX_Codetype','PX');
		h.definedata(all:'yes');
		h.definedone();

		do until (eof);
			set _ComboProc end=eof;			
			*create claim end date;
	        format Edate mmddyy10.; 
			length EDate 4; 
	        if not rawdurat then edate = adate;
	        else edate = sum(adate,rawdurat,-1);
	        keep Patid Adate Edate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;

			if h.num_items>0 then do;
				rc = h.find();
				do while (rc = 0);
				   	if (/*Apply caresetting criteria*/
					    ((RawCaresetting ne "" and indexw(upcase(RawCaresetting),upcase(EncType))) or RawCaresetting eq "")
					   ) then output;
				 	rc = h.find_next();
				end;
				drop rc;
			end;			
		end;
		run;

		%StopLoopRuntime(macroname=LoopProc);
    %MEND LoopProc;

    *Prepare Raw Dispensing Records;
    %MACRO LoopDrug();  
		%StartLoopRuntime();

		/* Rename ADate to make the extraction possible using the ms_extractdrugs macro */  
		proc datasets library=work nolist;  
			modify _ComboDrug;
			rename ADate=RXDate;		     
		quit;

		 %ms_extractdrugs(datafile=_ComboDrug,
			              lookfile=_LoopRawRX, 
			              lookvar=code, 
			              mindate=,
			              outfile=_LoopDrug);

		/* Rename RxDate for remaining claims processing */
		proc datasets library=work nolist;  
			modify _ComboDrug;
			rename RXDate=ADate;	
			modify _LoopDrug;
			rename RXDate=ADate;	 
		quit;

        %put &STOCKPILING;
       
        %IF %eval(&STOCKPILING.=1) %THEN %DO;

            proc sql noprint;
                create table _RawGroupList as 
                select distinct RawGroup
                from _LoopDrug;

                select count (distinct RawGroup) into :NBRAWGROUPS
                from _RawGroupList;
            quit;

            %put &NBRAWGROUPS.;

            %isdata(dataset=infolder.&STOCKPILINGFILE);
            %if %eval(&NOBS>0) %then %do;
            proc sql noprint;
            /* Set defaults and merge in information from original stockpiling file */
            create table STOCKPILINGCOMBO as
            select distinct upper(A.rawgroup) as rawgroup, A.rawstockgroup, 
                case when(B.sameday2 is missing) then 'aa' else B.sameday2 end as sameday, 
                case when (B.suprange2 is missing) then '0<-HIGH' else B.suprange2 end as suprange, 
                case when (B.amtrange2 is missing) then '0<-HIGH' else B.amtrange2 end as amtrange, 
                B.percentdays
                from infolder.&CODESFILE(where=(rawcodetype=:'RX')) as A
                LEFT JOIN 
                      (select upper(b.rawgroup) as rawgroup, b.rawstockgroup, b.sameday as sameday2, 
                              b.suprange as suprange2, 
                              b.amtrange as amtrange2, 
                              b.percentdays
                      from infolder.&STOCKPILINGFILE b) AS B
                    on a.rawgroup=b.rawgroup and a.rawstockgroup = b.rawstockgroup;
            quit;

           /* De-duplicate rows to correctly assign stocknumber based on group */
           proc sort data = STOCKPILINGCOMBO nodupkey out=stock_num;
            by rawgroup sameday suprange amtrange percentdays;
           run;

           data stock_num;
            set stock_num;
            by rawgroup sameday suprange amtrange percentdays;
            retain stocknumber;
            if first.rawgroup then stocknumber=1;
            else stocknumber+1;
           run;

            /* Join stocknumber back onto original dataset */
            proc sql noprint undo_policy=none;
              create table STOCKPILINGCOMBO as
                select a.*, b.stocknumber 
                from STOCKPILINGCOMBO a, stock_num b
                where a.sameday = b.sameday and a.suprange = b.suprange
                      and a.amtrange = b.amtrange and a.percentdays = b.percentdays;
            quit;

            proc datasets nowarn noprint lib=work;
              delete stock_num;
            quit;
            %end;

            %do j = 1 %to &NBRAWGROUPS.;

                data _null_;
				set _RawGroupList(firstobs=&j obs=&j keep=RawGroup);
				call symput('ITRAWGROUP',strip(RawGroup));
				run;     

                %let combo_stock_params = 0;
                %ISDATA(dataset=infolder.&STOCKPILINGFILE.);        
                %IF %EVAL(&NOBS.>0) %THEN %DO;
                /* store maximum number of combination of stockpiling parameters */
                proc sql noprint;
                  select max(stocknumber)
                    into :combo_stock_params
                    from STOCKPILINGCOMBO
                    where rawgroup="&ITRAWGROUP.";
                quit;
                %END;

                %if &combo_stock_params <= 1 %then %do;

                %if &combo_stock_params <= 0 %then %do;
                %let SAMEDAY=aa;
                %let SUPRANGE=0<-HIGH;
                %let AMTRANGE=0<-HIGH;
                %let PERCENTDAYS=;
                %end;

                %else %do;
                data _null_;
                set STOCKPILINGCOMBO;
                where rawgroup="&ITRAWGROUP";
                call symputx("SAMEDAY",strip(SameDay));
                call symputx("SUPRANGE",strip(SupRange));
                call symputx("AMTRANGE",strip(AmtRange));
                call symputx("PERCENTDAYS",put(PercentDays,best.));
                run;
                %end;

                data _LoopDrugsIter;
                set _LoopDrug(keep=Patid RawGroupNum RawGroup RawStockGroup ADate RxSup RxAmt);
                where RawGroup = "&ITRAWGROUP.";
                run;

                %MS_STOCKPILING(INFILE=_LoopDrugsIter,          
                                CLMDATE=ADate,                                  
                                CLMSUP=RxSup,                                   
                                CLMAMT=RxAmt,                                   
                                PROCFLAG=,                                      
                                PERCENTDAYS=&PERCENTDAYS.,                                  
                                GROUPING=RawGroupNum RawStockGroup,                           
                                SAMEDAY=&SAMEDAY.,                                  
                                SUPRANGE=&SUPRANGE.,                                    
                                AMTRANGE=&AMTRANGE.,                                    
                                ID=,                                            
                                OUTFILE=_LoopDrugsIter,         
                                OUTFILEEXCL=_LoopDrugsIterEx    
                );
                %end; /* <= 1 */

                %if &combo_stock_params > 1 %then %do;

                %do z = 1 %to &combo_stock_params;

                    data STOCKPILINGCOMBO_&z;
                    set STOCKPILINGCOMBO;
                    where RawGroup = "&ITRAWGROUP." and stocknumber=&z;
                      call symput('SAMEDAY',strip(SameDay));
                      call symput('SUPRANGE',strip(SupRange)); 
                      call symput('AMTRANGE',strip(AmtRange)); 
                      call symput('PERCENTDAYS',put(PercentDays,best.));
                    run;
                
                %put &ITRAWGROUP. &SAMEDAY. &SUPRANGE. &AMTRANGE. &PERCENTDAYS.;

                /* Only process dataset if there are subsetted observations */
                %ISDATA(dataset=STOCKPILINGCOMBO_&z);
                %if %eval(&NOBS>0) %then %do;
                %let stock_list=;
                proc sql noprint;
                  select distinct quote(strip(rawstockgroup))
                  into :stock_list separated by ' ' 
                  from STOCKPILINGCOMBO_&z;
                quit;
        
                data _LoopDrugsIter_&z;
                set _LoopDrug(keep=Patid RawGroupNum RawGroup RawStockGroup ADate RxSup RxAmt);
                where RawGroup = "&ITRAWGROUP." and RawStockGroup in (&stock_list);
                run;

                %MS_STOCKPILING(INFILE=_LoopDrugsIter_&z,          
                                CLMDATE=ADate,                                  
                                CLMSUP=RxSup,                                   
                                CLMAMT=RxAmt,                                   
                                PROCFLAG=,                                      
                                PERCENTDAYS=&PERCENTDAYS.,                                  
                                GROUPING=RawGroupNum RawStockGroup,                           
                                SAMEDAY=&SAMEDAY.,                                  
                                SUPRANGE=&SUPRANGE.,                                    
                                AMTRANGE=&AMTRANGE.,                                    
                                ID=,                                            
                                OUTFILE=_LoopDrugsIter_&z,         
                                OUTFILEEXCL=_LoopDrugsIterEx_&z    
                );

                %end; /* STOCKPILINGCOMBO_&z */

                %end; /* z */

                /* Stack all datasets together */
                data _LoopDrugsIter;
                  set _LoopDrugsIter_:;
                run;

                data _LoopDrugsIterEx;
                  set _LoopDrugsIterEx_:;
                run;

                /* Delete temporary datasets */
                proc datasets nowarn noprint lib=work;
                delete _LoopDrugsIter_: _LoopDrugsIterEx_: stockpilingcombo_:;
                quit;

                %end; /* > 1 */

                %let SAMEDAY=aa;
                %let SUPRANGE=0<-HIGH;
                %let AMTRANGE=0<-HIGH;
                %let PERCENTDAYS=;

                %if &j.=1 %then %do;
                    data _LoopDrugs;
                       set _LoopDrugsIter(drop=NumDispensing expiredt rxamt %if &keepRawStockgroup. eq 0 %then %do; rawstockgroup %end;);
                    run;                                
                %end;
                %else %do;
                    proc append base = _LoopDrugs
                                data = _LoopDrugsIter(drop=NumDispensing expiredt rxamt %if &keepRawStockgroup. eq 0 %then %do; rawstockgroup %end;) force;
                    run;                                
                %end;
            %end;       

            %ISDATA(dataset=_loopdrugs);        
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                proc datasets library=work;
                delete _loopdrug;
                change _loopdrugs = _loopdrug;
                quit;   
            %END;                
        %END;

		proc datasets nowarn noprint lib=work;
			delete _RAWGROUPLIST _loopdrugsiter:; 
		quit;

        *Creating Edate;
        data _LoopDrug;
        set _LoopDrug;
        length Adate EDate 4.;
        format Edate mmddyy10.;
        /*Parameter RawDurat commented on purpose. Usage deferred to a future version. Need to remove RawDurat from drop statement above when needed*/
        Edate=sum(ADate,RxSup/*,RawDurat*/)-1; *already Stockpiled - for overlap only;
		keep PatId Adate EDate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;
        run;

		%StopLoopRuntime(macroname=LoopDrug);
    %MEND LoopDrug;

    *Prepare Enrollment records;
    %MACRO LoopEnr();
		%StartLoopRuntime();

        *Select required raw eligibility records and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopEnr as
        Select claim.EStart as ADate length=4 format=mmddyy10.,
			   claim.EEnd as Edate length=4 format=mmddyy10.,
               claim.Patid,
			   %if &keepRawStockgroup. > 0 %then %do; input.rawstockgroup, %end;
			   input.RawGroupNum
           from _LoopRawEL(keep=RawGroupNum EnrollmentLength %if &keepRawStockgroup. >0 %then %do; rawstockgroup %end;) as input,
                _ComboEnr(keep=Patid EStart EEnd EnrollmentLength) as claim
           Where claim.EnrollmentLength >= input.EnrollmentLength; 
        quit;

		%StopLoopRuntime(macroname=LoopEnr);
    %MEND LoopEnr;

    *Prepare Encounter records;
    %MACRO LoopEnc();     
		%StartLoopRuntime();
 
        *select required raw records based on LOS and add combo input file information to combo raw records;
        proc sql noprint;
        Create table _LoopEnc as
        Select claim.ADate as ADate length=4 format=mmddyy10.,
			   claim.DDate as Edate length=4 format=mmddyy10.,
               claim.Patid,
			   %if &keepRawStockgroup. > 0 %then %do; input.rawstockgroup, %end;
			   input.RawGroupNum
           from _LoopRawEN01 as input,
                _ComboEnc(keep=Patid ADate DDate %if %eval(&numenctype.)>1 | &drgs. ne '-' %then %do; EncType %end;) as claim
            /*If LOS =. then always true for that part of the criteria*/
            where (DDate-ADate+1) >= LOS %if %eval(&numenctype.)>1 | &drgs. ne '-' %then %do; and RawCaresetting = EncType %end;; 
        quit;
        
        *Select required raw records based on DRG and add combo input file information to combo raw records;
		%isdata(dataset=_LoopRawEN02);
		%if %eval(&nobs.>0) %then %do;
	        proc sql noprint;
	        Create table _LoopEnc2 as
	        Select claim.ADate as ADate length=4 format=mmddyy10.,
				   claim.DDate as Edate length=4 format=mmddyy10.,
	               claim.Patid,
				   %if &keepRawStockgroup. > 0 %then %do; input.rawstockgroup, %end;
				   input.RawGroupNum
	           from _LoopRawEN02 as input,
	                _ComboEnc(keep=Patid ADate DDate Drg EncType) as claim
	            where strip(drg)=strip(RawCode) and RawCaresetting = EncType; 
	        quit;

	        proc datasets library=work nolist nowarn;
	        append base=_LoopEnc data=_LoopEnc2 force;
	        delete _LoopEnc2;
	        quit;
		%end;

		%StopLoopRuntime(macroname=LoopEnc);
    %MEND LoopEnc;

    *Prepare Demographic records;
    %MACRO LoopDem();
		%StartLoopRuntime();

		*select required raw records based on demographic and add combo input file information to combo raw records; 
		data _LoopDem;
		if 0 then set _LoopRawDM01;
		declare hash hsex(dataset:'_LoopRawDM01',HASHEXP:8, multidata:'Y');
		hsex.definekey('Sex');
		hsex.definedata(all:'yes');
		hsex.definedone();

		if 0 then set _LoopRawDM02;
		declare hash hrace(dataset:'_LoopRawDM02',HASHEXP:8, multidata:'Y');
		hrace.definekey('Race');
		hrace.definedata(all:'yes');
		hrace.definedone();

		if 0 then set _LoopRawDM03;
		declare hash hhispanic(dataset:'_LoopRawDM03',HASHEXP:8, multidata:'Y');
		hhispanic.definekey('Hispanic');
		hhispanic.definedata(all:'yes');
		hhispanic.definedone();

		do until (eof);
			set _ComboDem end=eof;						
			length Adate Edate 4.;
	        format Adate Edate mmddyy10.;
	        /*Parameter RawDurat commented on purpose. Usage deferred to a future version
	        Edate=sum(Ddate,RawDurat); */
	        ADate=-99999;
	        Edate=99999;
			keep Patid ADate EDate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;; 

			if hsex.num_items>0 then do;
				rc = hsex.find();
				do while (rc = 0);
				   	output;
				 	rc = hsex.find_next();
				end;
				drop rc;
			end;	

			if hrace.num_items>0 then do;
				rc = hrace.find();
				do while (rc = 0);
				   	output;
				 	rc = hrace.find_next();
				end;
				drop rc;
			end;

			if hhispanic.num_items>0 then do;
				rc = hhispanic.find();
				do while (rc = 0);
				   	output;
				 	rc = hhispanic.find_next();
				end;
				drop rc;
			end;			
		end;
        run;

		%StopLoopRuntime(macroname=LoopDem);
    %MEND LoopDem;

    *Prepare Laboratory records;
    %MACRO LoopLab();
		%StartLoopRuntime();

        *Lab extraction based on lookup - LAB01;
		*Obtain the ms_test_name, ms_test_sub_category, specimen_source, ms_result_unit
         result_type, fast_ind and pt_loc based on RawCode;
		proc sql noprint undo_policy=none;
		create table _LoopRawLB01 as
		select a.RawGroupNum,
			   a.RawLabResult,			   
			   a.RawGroup, 
			   %if &keepRawStockgroup. > 0 %then %do; a.rawstockgroup, %end;
			   b.*
		from _LoopRawLB01 as a
		join _labcodesMap as b
		on a.RawCode =  b.RawCode;
		quit;
       
		* Get the lab key (only one variable instead of 8) for extraction below;  
		proc sql noprint undo_policy=none;
		create table _LoopRawLB01 as
		select a.RawGroupNum,
			   a.RawLabResult,
			   %if &keepRawStockgroup. > 0 %then %do; a.rawstockgroup, %end;
			   b.labkey
		from _LoopRawLB01 as a
		join _labkey as b
		on   a.ms_test_name         = b.ms_test_name and 
			 a.ms_test_sub_category = b.ms_test_sub_category  and 
			 a.specimen_source      = b.specimen_source and 
			 a.ms_result_unit       = b.ms_result_unit and
			 a.result_type          = b.result_type and
			 a.fast_ind             = b.fast_ind and
			 a.pt_loc               = b.pt_loc and
			 a.RawGroup             = b.RawGroup;
		quit;

		data _Looplab;
			if 0 then set _LoopRawLB01;
			declare hash h(dataset:'_LoopRawLB01',HASHEXP:8, multidata:'Y');
			h.definekey('labkey');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _ComboLab(keep=Patid Adate ms_result_c ResultTyp labkey) end=eof;				
		        length Adate EDate 4;
		        format ADate Edate mmddyy10.;
		        Edate=Adate;        		        
		        keep Patid ADate EDate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;     
 
				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	if (/*Apply RawLabResult criteria*/
				  	        ((ResultTyp eq "C" and ms_result_c eq RawLabResult) or ResultTyp ne "C")
			               ) then output;
						rc = h.find_next();
					end;
					drop rc;
				end;						
			end;
		run; 

        *Lab extraction based on LOINC - LAB02;
		data _Looplab2;
			if 0 then set _LoopRawLB02;			
			declare hash h(dataset:'_LoopRawLB02',HASHEXP:8, multidata:'Y');
			h.definekey('Loinc', 'RawGroup');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _ComboLab(keep=Patid Adate ms_result_c ResultTyp Loinc Rawgroup) end=eof;				
		        length Adate EDate 4;
		        format ADate Edate mmddyy10.;
		        Edate=Adate;        		        
		        keep Patid ADate EDate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;      
 
				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	if (/*Apply RawLabResult criteria*/
				  	        ((ResultTyp eq "C" and ms_result_c eq RawLabResult) or ResultTyp ne "C")
			               ) then output;
						rc = h.find_next();
					end;
					drop rc;
				end;				
			end;
		run;

        *Lab extraction based on PX - LABXX (XX not in (01 02));
		data _Looplab3;
			if 0 then set _LoopRawLBPX;
			declare hash h(dataset:'_LoopRawLBPX',HASHEXP:8, multidata:'Y');
			h.definekey('PX_codetype', 'PX', 'RawGroup');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _ComboLab(keep=Patid Adate ms_result_c ResultTyp PX_codetype PX Rawgroup) end=eof;				
		        length Adate EDate 4;
		        format ADate Edate mmddyy10.;
		        Edate=Adate;        		        
		        keep Patid ADate EDate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;              
				
				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	if (/*Apply RawLabResult criteria*/
				  	        ((ResultTyp eq "C" and ms_result_c eq RawLabResult) or ResultTyp ne "C")
			               ) then output;
						rc = h.find_next();
					end;
					drop rc;
				end;				
			end;
		run;

		proc datasets library=work nolist nowarn;
	        append data=_LoopLab2 base=_LoopLab force;
			append data=_LoopLab3 base=_LoopLab force;
        	delete _LoopLab2 _LoopLab3;
        quit;
 
        *Removing the true duplicates as a record could have been extracted three time
         but using three different criteria;
        proc sort nodupkey data=_LoopLab;
        	by _ALL_;  
        run;

		%StopLoopRuntime(macroname=LoopLab);
    %MEND LoopLab;

    %MACRO CreateEpi(INFILE=,OUTFILE=,STARTDT=,ENDDT=);        
        %IF &RawEpiType. > 0 %THEN %DO;
			%if &RawEpiType.=1 %then %do;
				%let by=PatId;
				%let by2=PatId;
				%let by3=PatId;
			%end;
			%else %if &RawEpiType.=2 %then %do;
				%let by=PatId RawStockGroup;
				%let by2=%quote(PatId, RawStockGroup);
				%let by3=RawStockGroup;
			%end;

            *Episode creation required (RawEpiType variable > 0);
            proc sql noprint;
            create table _episodes_ as
            Select *
            from &INFILE.
            order by &by2., &STARTDT., &ENDDT.;
            quit;

            *Create episodes considering allowed gaps;
            data _episodes_(drop=LRunOutDt gap);
            set  _episodes_;
            by &by.;

            if first.&by3. then do;
              LRunOutDt=&ENDDT.;
              episode=1;
              gap=.;
            end;
            else do; 
              gap = &STARTDT. - LRunOutDt-1;         
              if gap > &RawEpiGap. then do;
                  episode=episode+1; 
                  LRunOutDt = &ENDDT.;
              end;
              else LRunOutDt = max(&ENDDT.,LRunOutDt);  * in case of encapsulated records;
            end;

            retain episode LRunOutDt;
            run;            

            Proc SQL Noprint;
            Create Table &OUTFILE.(drop=episode) as
            Select  file.*,
                    min(&STARTDT.) as EStart format=mmddyy10. length=4,
                    max(&ENDDT.) as EEnd format=mmddyy10. length=4
            From _episodes_ as file  
            group by &by2., episode;
            quit;

            proc sort data=&OUTFILE. out=&OUTFILE.(drop=&STARTDT. &ENDDT. rename=EStart=&STARTDT. rename=EEnd=&ENDDT.) NODUPKEY;
            by &by. EStart EEnd;
            run;

			proc datasets nowarn noprint lib=work;
				delete _episodes_;
			quit;
        %END;
    %MEND CreateEpi;

    %MACRO LoopCB();
		%StartLoopRuntime();

	    *initialize/reset;
	    %global CBDIAG;
	    %global CBPROC;
	    %global CBDRUG;
	    %let CBDIAG=;
	    %let CBPROC=;
	    %let CBDRUG=;

    	*Restrict to diagnosis-like combo virtual claims;        
        %ISDATA(dataset=_LoopRawCBDX);     

        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw virtual records and add combo input file information to this-combo raw records;
	        data _LoopDiagCB;
			if 0 then set _LoopRawCBDX;
			declare hash h(dataset:'_LoopRawCBDX',HASHEXP:8, multidata:'Y');
			/* No need to join on codetype since always CB */ 
			h.definekey('DX');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _VCDiag end=eof;				
				*create claim end date;
		        format Edate mmddyy10.;
				length Edate 4;
	            if not rawdurat then edate = adate;
	            else edate = sum(adate,rawdurat,-1);
	            keep Patid Adate Edate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;
				
				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	if (/*Apply caresetting criteria*/
					   	    ((RawCaresetting ne "" and indexw(upcase(RawCaresetting),upcase(EncType))) or RawCaresetting eq "") and
					        /*Apply extra criterion for PDX only in the presence of IP (defensive coding)*/
					        ((RawPDX ne "" and upcase(EncType) in('IP') and upcase(PDX) eq upcase(RAWPDX)) or (RawPDX eq "" or upcase(EncType) not in ('IP')))
					       ) then output;
						rc = h.find_next();
					end;
					drop rc;
				end;				
			end;
			run;
                       
            *set macro variable to dynamically be included in later datastep;
            %let CBDIAG=_LoopDiagCB;
        %END;

    	*Restrict to procedure-like combo virtual claims;        
        %ISDATA(dataset=_LoopRawCBPX);     
        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw records and add combo input file information to combo raw records;
			data _LoopProcCB;
			if 0 then set _LoopRawCBPX;
			declare hash h(dataset:'_LoopRawCBPX',HASHEXP:8, multidata:'Y');
			/* No need to join on codetype since always CB */ 
			h.definekey('PX');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _VCProc end=eof;				         	            
				*create claim end date;
		        format Edate mmddyy10.;
				length Edate 4;
	            if not rawdurat then edate = adate;
	            else edate = sum(adate,rawdurat,-1);
	            keep Patid Adate Edate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;

				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	if (/*Apply caresetting criteria*/
					   	    ((RawCaresetting ne "" and indexw(upcase(RawCaresetting),upcase(EncType))) or RawCaresetting eq "")
					  	   ) then output;
						rc = h.find_next();
					end;
					drop rc;
				end;						
			end;
			run;

            *set macro variable to dynamically be included in later datastep;
            %let CBPROC=_LoopProcCB;
        %END;

    	*Restrict to Drug-like combo virtual claims;
        %ISDATA(dataset=_LoopRawCBRX);     
        %IF %EVAL(&NOBS.>0) %THEN %DO; 
            *Select required raw records and add combo input file information to combo raw records;
			data _LoopDrugCB;
			if 0 then set _LoopRawCBRX;
			declare hash h(dataset:'_LoopRawCBRX',HASHEXP:8, multidata:'Y');			
			h.definekey('RX');
			h.definedata(all:'yes');
			h.definedone();

			do until (eof);
				set _VCDrug(rename=RxDate=aDate) end=eof;				            	            
				*create claim end date;
		        format Edate mmddyy10.;
				length Edate 4;	           
				if not rawdurat then edate = sum(adate,rxsup-1);
            	else edate = sum(adate,rxsup-1,rawdurat,-1); 
	            keep Patid Adate Edate RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;;

				if h.num_items>0 then do;
					rc = h.find();
					do while (rc = 0);
					   	output;
						rc = h.find_next();
					end;
					drop rc;
				end;	
			end;
			run;           

            *set macro variable to dynamically be included in later datastep;
            %let CBDRUG=_LoopDrugCB;
        %END;

		%StopLoopRuntime(macroname=LoopCB);
    %MEND LoopCB;

/***************************/
/* START 3. COMBGROUP LOOP */
/***************************/

	*Extract the number of combo groups for iteration loop and compute RawGroupNum variable;
    data _null_;
       set _CombGroupList end=fin;
       if fin then call symput('NCOMBGROUPS',put(_n_,best.));
    run;
   	%PUT &NCOMBGROUPS.;

	proc sort data=_COMBFILE;
	by CombOrder RawGroup;
	run;

	data _COMBFILE;
	set _COMBFILE;
	by comborder RawGroup;
	length RawGroupNum 3;
	if _N_=1 then RawGroupNum=0;
	if first.RawGroup then RawGroupNum=RawGroupNum+1;
	retain RawGroupNum;
	run;

	proc sort data=_COMBFILE;
	by CombOrder RawOrder;
	run;

   	*Loop through ComboGroups;
   	%DO i = 1 %TO &NCOMBGROUPS.;

        *Get current combo group;
        data _null_;
           set _CombGroupList(firstobs=&i obs=&i);
           call symput('ITCOMBGROUP',strip(CombGroup));
        run;
        %PUT &ITCOMBGROUP.;      
    
        proc sql noprint;
        create table _LoopRaw as 
        select Comb.RawOrder,
			   Comb.RawGroupNum,
               Comb.RawEpiType,
               Comb.RawEpiGap,
               Comb.RawExclusion,
               Comb.RawDaysFromStartDt,
               Comb.RawDaysFromEndDt,
               Comb.RawDurat,
               Comb.RawADateType,
               Comb.RawEDateType,
               Raw.*
        from _COMBFILE(keep=CombGroup Raw:) as Comb,
             _CODESFILE as Raw
        where CombGroup in("&ITCOMBGROUP.") and Comb.RawGroup=Raw.RawGroup;
        quit;

        data _LoopRaw(keep=Raw:);  /*input parameters for the raw records fileres and to identify the combos*/
            set _LoopRaw;			
            * letters in RawType and numbers in RawCodeType;
            if RawCodeType in:("LAB") then RawType="LB";
            else RawType=substr(RawCodetype,1,2);

            if RawCodeType in:("DX")  then RawCodeType=compress(RawCodeType,"DX");
            if RawCodeType in:("RX")  then RawCodeType=compress(RawCodeType,"RX");
            if RawCodeType in:("PX")  then RawCodeType=compress(RawCodeType,"PX");   
            if RawCodeType in:("EN")  then RawCodeType=compress(RawCodeType,"EN"); 
            if RawCodeType in:("DM")  then RawCodeType=compress(RawCodeType,"DM");  
            if RawCodeType in:("EL")  then RawCodeType=compress(RawCodeType,"EL"); 
            *Must remove the "N" or "C" at the end of codetype; 
            if RawCodeType in:("LAB") then do;
                RawCodeType=compress(RawCodeType,"LAB");  
                RawCodeType=substr(RawCodeType,1,2);
            end;
        run;

		* Deduplicate at RawGroupNum level;
		proc sort nodupkey data=_LoopRaw out=_LoopRawExtract;
		by RawGroupNum Rawcode RawCodeType RawCaresetting RawPDX RawLabDateType RawLabResult;
		run;

        data _LoopComb(keep=Comb:); /*input parameters to create the virtual record*/           
            set _COMBFILE(where=(CombGroup in("&ITCOMBGROUP.")))  end=eof1;         
            if _N_=1 then output _LoopComb;

            call symput("Behave",upcase(strip(CombBehavior)));    

			if RawOrder = 1 then do;
				call symput("RawAdateType",upcase(strip(RawAdateType)));  
				call symput("RawEdateType",upcase(strip(RawEdateType)));
				call symput("RawEpiType",put(RawEpiType,best.));  
				call symput("RawEpiGap",put(RawEpiGap,best.)); 
				call symput("RawGroupNum",put(RawGroupNum,best.)); 
 			end; 

            *Max Num Orders for *this combo;
            if eof1 then call symput("NUMORDER",put(RawOrder,best.));
        run;

		proc sql noprint;
		select count(RawGroupNum) into :NumFirstRawGroup trimmed
		from _COMBFILE(where=(RawGroupNum=&RawGroupNum. and CombGroup in("&ITCOMBGROUP.")));
		quit;

        %put &NUMORDER.;  			*the number of times to apply the two-by-two method - 1;
        %put &=NumFirstRawGroup;	*the number of times the RawGroup associated with RawOrder=1 is used to create the virtual claims;

		/**********************************************/
		/* 3.1. COMBGROUP-SPECIFIC RECORDS EXTRACTION */
		/**********************************************/

		*Create lookup files for this loop extraction;
		data _LoopRawDX(keep=RawGroupNum Rawcode RawCodeType RawCaresetting RawPDX RawDurat %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;
						rename=(RawCodeType=DX_Codetype Rawcode=DX))
			_LoopRawPX(keep=RawGroupNum Rawcode RawCodeType RawCaresetting RawDurat %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;
					   rename=(RawCodeType=PX_Codetype Rawcode=PX)) 
			_LoopRawRX(keep=RawGroupNum RawCodeType Rawcode RawDurat RawGroup rawstockgroup rename=(RawCode=Code RawCodeType=CodeType))
			_LoopRawEL(keep=RawGroupNum EnrollmentLength RawType %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;)
			_LoopRawEN01(keep=RawGroupNum RawCaresetting LOS %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;)
			_LoopRawEN02(keep=RawGroupNum RawCaresetting RawCode %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;)
			_LoopRawDM01(keep=RawGroupNum RawCode %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=Sex)	
			_LoopRawDM02(keep=RawGroupNum RawCode %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=Race)	
			_LoopRawDM03(keep=RawGroupNum RawCode %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=Hispanic)	
			_LoopRawLB01(keep=rawcode RawGroupNum rawgroup RawLabResult %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;)
			_LoopRawLB02(keep=rawcode RawGroupNum rawgroup RawLabResult %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=Loinc)
			_LoopRawLBPX(keep= RawCodeType rawcode RawGroupNum rawgroup RawLabResult %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=(Rawcodetype=PX_codetype Rawcode=PX))	
			_LoopRawCBDX(keep=RawCode RawDurat RawCaresetting RawPDX RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=DX)
			_LoopRawCBPX(keep=RawCode RawDurat RawCaresetting RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=PX)
			_LoopRawCBRX(keep=RawCode RawDurat RawGroupNum %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end; rename=RawCode=RX)	
		;
		set _LoopRawExtract;
		if RawType=:"DX" and RawCodeType ne "CB" then output _LoopRawDX;
		else if RawType=:"PX" and RawCodeType ne "CB" then output _LoopRawPX;
		else if RawType=:"RX" and RawCodeType ne "CB" then do;			
		    /* Defensive coding - change RX09 and RX11 values all to ND if they exist */
		    if RawCodeType in ("09" "11") then RawCodeType = "ND";
			output _LoopRawRX;
		end;
		else if RawType=:"EL" then do;
			EnrollmentLength=input(RawCode,best.);
			output _LoopRawEL;
		end;
		else if RawType=:"EN" and RawCodeType="01" then do;
			*Need output Method for Enctype;
			LOS=input(RawCode,best.);
		    ET=RawCaresetting;
		    NumEncType=COUNTW(RawCaresetting);
		    do i=1 to NumEncType;
		        RawCaresetting=SCAN(ET,i); 
		        output _LoopRawEN01;
		    end;
		    drop i NumEncType ET;	
		end;
		else if RawType=:"EN" and RawCodeType="02" then do;
			*Need output Method for Enctype;
			ET=RawCaresetting;
		    NumEncType=COUNTW(RawCaresetting);
		    do i=1 to NumEncType;
		        RawCaresetting=SCAN(ET,i); 
		        output _LoopRawEN02;
		    end;        
			drop i NumEncType ET;
		end;
		else if RawType=:"DM" and RawCodeType="01" then do;
			Sex=compress(RawCode, "'");
		    NumSex=COUNTW(RawCode);
		    do i=1 to NumSex;
		        RawCode=SCAN(Sex,i); 
		        output _LoopRawDM01;
		    end;   
			drop Sex NumSex;     
		end;
		else if RawType=:"DM" and RawCodeType="02" then do;
			Race=compress(RawCode, "'");
		    NumRace=COUNTW(RawCode);
		    do i=1 to Numrace;
		        RawCode=SCAN(Race,i); 
		        output _LoopRawDM02;
		    end;  
			drop Race NumRace;     
		end;
		else if RawType=:"DM" and RawCodeType="03" then do;
			Hispanic=compress(RawCode, "'");
		    NumHispanic=COUNTW(RawCode);
		    do i=1 to NumHispanic;
		        RawCode=SCAN(Hispanic,i); 
		        output _LoopRawDM03;
		    end;  
			drop Hispanic NumHispanic;     
		end;
		else if RawType=:"LB" and RawCodeType="01" then output _LoopRawLB01;
		else if RawType=:"LB" and RawCodeType="02" then output _LoopRawLB02;
		else if RawType=:"LB" and RawCodeType not in("01","02") then output _LoopRawLBPX;
		else if RawType=:"DX" and RawCodeType="CB" then output _LoopRawCBDX;
		else if RawType=:"PX" and RawCodeType="CB" then output _LoopRawCBPX;
		else if RawType=:"RX" and RawCodeType="CB" then output _LoopRawCBRX;
		run;

        *Get MSCDM data for *this loop (current combo group);
        %LoopDiag;
        %LoopProc;
        %LoopDrug;
        %LoopEnr;
        %LoopEnc;
        %loopDem;
        %LoopCB;

        %macro RunLab;
            %ISDATA(dataset=_clab);
            %global lab;
            %IF %EVAL(&NOBS.>0) %THEN %DO;
                %LoopLab;
                %let lab=_LoopLab;
            %end;
            %else %do;
                %let lab=;
            %end;
        %mend RunLab;
        %RunLab;
        %put &lab.;

		*Delete lookup file used for this loop extraction;
		proc datasets library=work nolist nowarn;
        delete _LoopRawDX _LoopRawPX _LoopRawRX _LoopRawEL _LoopRawEN01 _LoopRawEN02 _LoopRawDM01 _LoopRawDM02
			   _LoopRawDM03 _LoopRawLB01 _LoopRawLB02 _LoopRawLBPX _LoopRawCBDX _LoopRawCBPX _LoopRawCBRX;
        quit;

        *Initialize two_by_two;
		data _ForCombine			
			_One(drop=RawGroupNum rename=Adate=AdateB rename=Edate=EdateB /*Suffix B is for Base*/);			
		set _LoopDiag
		    _LoopProc
		    _LoopDrug
		    _loopEnr
		    _loopEnc
		    _loopDem
		    &lab.
		    &CBPROC.
		    &CBDIAG.
		    &CBDRUG.;
			/* If the RawGroup associated with RawOrder=1 is used only once, no need to output in _ForCombine */;
			%if &NumFirstRawGroup. = 1 %then %do; 
			if RawGroupNum=&RawGroupNum. then output _One; 
			else output _ForCombine;
			%end;
			%else %do;
			if RawGroupNum=&RawGroupNum. then output _One;
			output _ForCombine; * Output everything if &NumFirstRawGroup. > 1;
			%end;
		run;

		proc datasets nowarn noprint lib=work;
			delete _LoopDiag _LoopProc _LoopDrug _loopEnr _loopEnc _loopDem &lab. &CBPROC. &CBDIAG. &CBDRUG.;
		quit;
            
        *Create Episode if necessary;
        %CreateEpi(INFILE=_One,
                 OUTFILE=_One,
                 STARTDT=ADateB,
                 ENDDT=EdateB);
   
        *No combos to create because there is only one RawOrder value - proceed to formatting the _One dataset as if final;
        %MACRO wrapper;
        %if &NUMORDER.=1 %then %do;  
            data _One(rename=Adate=AdateB rename=Edate=EdateB);
            set _One;

            format Adate Edate date9.;
			length Adate Edate 4;

            *Create Virtual claim Adate;
            %if &RawAdateType.=EDATEB %then %do;
				Adate=EdateB;    *The end of the Base interval;
			%end;
			%else %do;
				Adate=adateB;
			%end;
            *Create Virtual claim Edate;
            %if &RawEdateType.=ADATEB  %then %do; 
				Edate=adateB;    *The start of the Base interval;
			%end;
			%else %do;
				Edate=EdateB;
			%end;

            keep PatId Adate Edate;* NumDaysB2C low high; 
            run;
        %end;*if &order.=1;
        %mend wrapper;
        %wrapper;

		/************************/
		/* 3.2. TWO-BY-TWO LOOP */
		/************************/

        *Loop through items to combine;
        %DO order=2 %TO &NUMORDER.;
            
            *Check to see if this step is to apply an exclusion rule;
            %let RawExclusion=0;
            proc sql noprint;
                select max(RawExclusion), strip(RawGroup), upcase(strip(RawAdateType)), upcase(strip(RawEdateType)),
					   strip(RawDaysFromStartDt), strip(RawDaysFromEndDt), RawEpiType, RawEpiGap, RawGroupNum 
					   into :RawExclusion trimmed, :ITRAWGROUP trimmed, :RawAdateType trimmed, :RawEdateType trimmed,
							:RawDaysFromStartDt trimmed, :RawDaysFromEndDt trimmed, :RawEpiType trimmed, :RawEpiGap trimmed, :RawGroupNum trimmed 
                from _LoopRaw
                where RawOrder=&order.;
			quit;
			%if %length(&RawDaysFromStartDt) > 0 %then %let RawDaysFromStartDt=%sysfunc(tranwrd(%quote(&RawDaysFromStartDt.),%str(,),%str(@)));
            %if %length(&RawDaysFromEndDt) > 0 %then %let RawDaysFromEndDt=%sysfunc(tranwrd(%quote(&RawDaysFromEndDt.),%str(,),%str(@)));

            *Select records to combine to _One dataset - leave the rest in _ForCombine for 
             the next iteration of the TwoByTwo method;
            data _Two(keep=PatId ADateC EDateC %if &keepRawStockgroup. > 0 %then %do; rawstockgroup %end;);
			set _ForCombine(where=(RawGroupNum=&RawGroupNum.) rename=Adate=AdateC rename=Edate=EdateC /*Suffix C is for CombineTo*/);
            run;
         
            *Create Episodes if necessary;
            %CreateEpi(INFILE=_Two,
                       OUTFILE=_Two,
                       STARTDT=ADateC,
                       ENDDT=EdateC);

            %ISDATA(dataset=_One);
            %put NOBS in _One: &NOBS.;
            
            %MACRO WRAPPER;
                *Assign relationship to Base and compute the "reach" of base record to identify overlap with _two;
                Data _One;
                set _One;
                format OverlapStartDt OverlapStopDt mmddyy10.;
				length OverlapStartDt OverlapStopDt 4;
	
            %IF %eval(&NOBS.>0) %THEN %DO;
				format AdateLeft AdateRight EdateLeft EdateRight mmddyy10.;
				length AdateLeft AdateRight EdateLeft EdateRight 4;

				AdateRight=.;
                AdateLeft=.;
				EdateRight=.;
                EdateLeft=.;

                *Creating Overlap looking start date;
                %if %length(&RawDaysFromStartDt.) = 0 and %length(&RawDaysFromEndDt.) = 0 %then %do;
                AdateLeft=AdateB; *If empty, use actual date;
                EdateLeft=EdateB; *If empty, use actual date;				
                %end;

                %if %length(&RawDaysFromStartDt.) > 0 %then %do;             
                    AdateLeft=sum(AdateB,input(%SCAN(&RawDaysFromStartDt.,1,%str(@)),best.));
                    %if %upcase(%SCAN(&RawDaysFromStartDt.,2,%str(@))) ne X and %length(%SCAN(&RawDaysFromStartDt.,2,%str(@))) > 0 %then %do;
                        AdateRight=sum(AdateB,input(%SCAN(&RawDaysFromStartDt.,2,%str(@)),best.));  *missing if second argument is x;
                    %end;
                %end;

                *Creating Overlap looking Stop date;
                %if %length(&RawDaysFromEndDt.) > 0 %then %do;    
                    EdateLeft=sum(EdateB,input(%SCAN(&RawDaysFromEndDt.,1,%str(@)),best.));
                    %if %upcase(%SCAN(&RawDaysFromEndDt.,2,%str(@))) ne X and %length(%SCAN(&RawDaysFromEndDt.,2,%str(@))) > 0 %then %do;                   
                        EdateRight=sum(EdateB,input(%SCAN(&RawDaysFromEndDt.,2,%str(@)),best.));  *missing if second argument is x;
                    %end;
                %end;

                OverlapStartDt=min(AdateLeft,AdateRight,EdateLeft,EdateRight);
                OverlapStopDt =max(AdateLeft,AdateRight,EdateLeft,EdateRight);		
				drop AdateLeft AdateRight EdateLeft EdateRight;	
            %END;
            %ELSE %DO;                
                OverlapStartDt=.;
                OverlapStopDt=.;            
            %END;
			%IF &RawExclusion.=1 %THEN %DO;
                *Keep numline to identify lines to delete in the case of an exclusion - will be used to merge;
                NumLine=_N_;        				
                run;
			%END;

            %MEND WRAPPER;
            %WRAPPER;

            *Identify combinations;
            proc sql noprint;
            Create table _TwoByTwo as
                Select distinct
					   /* Numline only required when RawExclusion=1
					   	  ADateB and EdateB will be pulled from _one dataset below whereas
					   	  ADateC and EDateC are not required because
					   	  RawAdateType will be forced to ADATEB and RawEdateType to EDATEB
					   */
					   %if &RawExclusion.=1 %then %do;
                       one.NumLine 
					   %end;                       
					   %else %do;
					   one.ADateB, 
                       one.EdateB,
                       two.ADateC,
                       two.EDateC,
					   two.PatId 
					   %end;                        					   
                from _One(keep=PatId ADateB EDateB OverlapStartDt OverlapStopDt %if &RawExclusion.=1 %then %do; NumLine %end;) as one,
                     _Two(keep=Patid ADateC EDateC) as two
                Where one.Patid = two.PatId and 
                      max(one.OverlapStartDt,two.ADateC) <= min(one.OverlapStopDt,two.EDateC) /*MUST OVERLAP*/
                ; 
            quit;

			proc datasets nowarn noprint lib=work;
				delete _two;
			quit;

            *In the case where exclusion=1, we will only have kept the overlaping records to exclude.
             We now need to remerge by NumLine and only keep those lines that are not in _TwoByTwo;
			%let twobytwoset= _twobytwo;
            %ISDATA(dataset=_TwoByTwo);            
            %IF %eval(&NOBS.>0) & &RawExclusion.=1 %THEN %DO;
                proc sort nodupkey data=_TwoByTwo(keep=NumLine);
                by NumLine;
                run;

                data _TwoByTwo(drop=NumLine);
                merge _One(in=a) _TwoByTwo(in=b); 
                by NumLine;
                if a and not b;                
                run;
				
				%let RawAdateType=ADATEB; *Cannot be something else because of exclusion;  
				%let RawEdateType=EDATEB; *Cannot be something else because of exclusion;					
            %END;
            %ELSE %IF &RawExclusion.=1 %THEN %DO;                
                *_TwoByTwo is empty: nothing to exclude;
                %let twobytwoset=_one;
            %END;            
                
            %ISDATA(dataset=&twobytwoset.); 
            *Apply final criteria to overlapping records;
            data _One(rename=Adate=AdateB rename=Edate=EdateB); *rename to B because _One will become the new base in the next iteration;
            set &twobytwoset.(keep=PatId ADateB EDateB %if &RawExclusion. ne 1 %then %do; ADateC EDateC %end;);
			format Adate Edate date9.;
			length Adate Edate 4;

	        %IF %eval(&NOBS.>0)%THEN %DO;                           
				%IF &RawExclusion.=0 %THEN %DO; 
		            *Create Virtual claim Adate;
		            %if &RawADateType.=ADATEB %then %do; Adate=adateB; %end;                        	/* The start of the Base interval */
		            %else %if &RawADateType.=ADATEC  %then %do; Adate=adateC; %end;                    	/* The start of the Combined-to interval */
		            %else %if &RawADateType.=MINADATEBC %then %do; Adate=min(adateB,adateC); %end;     	/* The minimum between Base and Combined-to start date */
		            %else %if &RawADateType.=MAXADATEBC %then %do; Adate=max(adateB,adateC); %end;     	/* The maximum between Base and Combined-to start date */
		            %else %if &RawADateType.=EDATEB %then %do; Adate=EdateB; %end;                    	/* The end of the Base interval */
		            %else %if &RawADateType.=EDATEC %then %do; Adate=EdateC; %end;                    	/* The end of the Combined-to interval */
		            %else %if &RawADateType.=MINEDATEBC %then %do; Adate=min(EdateB,EdateC); %end;     	/* The minimum between Base and Combined-to end date */
		            %else %if &RawADateType.=MAXEDATEBC %then %do; Adate=max(EdateB,EdateC); %end;     	/* The maximum between Base and Combined-to end date */
		            %else %if &RawADateType.=MINOVER %then %do; Adate=max(adateB,adateC); %end;        	/* The start of the overlapping period between Base and Combined-to */
		            %else %if &RawADateType.=MAXOVER %then %do; Adate=min(EdateB,EdateC); %end;        	/* The end of the overlapping period between Base and Combined-to */

		            *Create Virtual claim Edate;
		            %if &RawEDateType.=ADATEB %then %do; Edate=adateB; %end;                           	/* The start of the Base interval */
		            %else %if &RawEDateType.=ADATEC %then %do; Edate=adateC; %end;                     	/* The start of the Combined-to interval */
		            %else %if &RawEDateType.=MINADATEBC %then %do; Edate=min(adateB,adateC); %end;     	/* The minimum between Base and Combined-to start date */
		            %else %if &RawEDateType.=MAXADATEBC %then %do; Edate=max(adateB,adateC); %end;      /* The maximum between Base and Combined-to start date */
		            %else %if &RawEDateType.=EDATEB %then %do; Edate=EdateB; %end;                     	/* The end of the Base interval */
		            %else %if &RawEDateType.=EDATEC %then %do; Edate=EdateC; %end;                     	/* The end of the Combined-to interval */
		            %else %if &RawEDateType.=MINEDATEBC %then %do; Edate=min(EdateB,EdateC); %end;     	/* The minimum between Base and Combined-to end date */
		            %else %if &RawEDateType.=MAXEDATEBC %then %do; Edate=max(EdateB,EdateC); %end;     	/* The maximum between Base and Combined-to end date */         
		            %else %if &RawEDateType.=MINOVER %then %do; Edate=max(adateB,adateC); %end;        	/* The start of the overlapping period between Base and Combined-to */
		            %else %if &RawEDateType.=MAXOVER %then %do; Edate=min(EdateB,EdateC); %end;        	/* The end of the overlapping period between Base and Combined-to */
				%END;
				%ELSE %DO;
					*Cannot be something else because of exclusion;  
					Adate=adateB;
					Edate=EdateB;
				%END;
	            * Because of the the flexibility of RawDaysFromStartDt and RawDaysFromEndDt combined with 
	              the selection of dates for the virtual claim, we need to check that the 
	              event did not end before starting;
	            if Adate <=Edate;
	        %END;
	        %ELSE %DO;
	            Adate=.;    
	            Edate=.;
	        %END;
            keep PatId Adate Edate;
            run;

            *The claim virtual date selection could have created duplicates;
            proc sort nodupkey data=_One;
            by _ALL_;
            run;

			proc datasets nowarn noprint lib=work;
				delete _twobytwo;
			quit;

        %END;* ORDER LOOP - this marks the end of the two-by-two step for this combgroup;

	/*************/
	/* 4. OUTPUT */
	/*************/

        *Add Comb* input variables to create final virtual claim;
        data _one;
	        set _one;
	        if _N_=1 then set _LoopComb(drop=comborder combbehavior);
		run;

        %put &Behave.;

        %if %eval(&scdmver1. = 7) %then %let CodeType=CodeType;
        %else %let CodeType=Rx_CodeType;
        
        %IF &Behave.=RX %THEN %DO;          
            data _VC;
            retain CombGroup;
            set indata.&DISTABLE.(obs=0) /*Empty row with specific data partners formats and lengths (defensive coding to avoid format errors and/or truncation)*/
                _one;
            format Rxdate mmddyy10. rxsup best.;
			length Rxdate 4;
            Rxdate=AdateB;
            Rx=CombCode;
            Rx_CodeType='CB';
            RxSup=EdateB-AdateB+1;
            RxAmt=1;
            keep Patid Rxdate Rx Rx_CodeType RxSup RxAmt CombGroup;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCDrug force;
            delete _VC;
            quit;
        %END;
        %IF &Behave.=DX %THEN %DO;          
            data _VC;
            retain CombGroup;
            set indata.&DIATABLE.(obs=0) /*Empty row with specific data partners formats and lengths (defensive coding to avoid format errors and/or truncation)*/
				_one;
            EncType=CombEncType;
            Dx=CombCode;
            Dx_CodeType='CB';
            PDX=CombPDX;
            do Adate=AdateB to EdateB;
                output;
            end;
            keep Patid Adate EncType Dx Dx_CodeType PDX CombGroup;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCDiag force;
            delete _VC;
            quit;
        %END;

        %IF &Behave.=PX %THEN %DO;          
            data _VC;
            retain CombGroup;
            set indata.&PROCTABLE.(obs=0) /*Empty row with specific data partners formats and lengths (defensive coding to avoid format errors and/or truncation)*/
				_one;
            EncType=CombEncType;
            Px=CombCode;
            Px_CodeType='CB';
            do Adate=AdateB to EdateB;
                output;
            end;
            keep Patid Adate EncType Px Px_CodeType CombGroup;
            run;

            proc datasets library=work nolist nowarn;
            append data=_VC base=_VCProc force;
            delete _VC;
            quit;
        %END;       

		proc datasets nowarn noprint lib=work;
			delete _LOOPCOMB _Loopraw: ;
		quit;

    %END;*NCOMBGROUPS LOOP;

    /**********************************************************************/
    /* Keep the virtual records in a permanent file. Only necessary when: */
	/*if SAVEDATALIBREF is specified, save files to that location         */
	/*if SAVEDATALIBREF is blank and RUNSTANDALONE = Y, save to work 	  */
	/*if SAVEDATALIBREF is blank and RUNSTANDALONE = N, do not save       */
    /**********************************************************************/
	%if &runstandalone. = Y | %str(&SAVEDATALIBREF.) ne %str() %then %do;
		/*append dot to davedatalibref*/
		%if %str(&SAVEDATALIBREF.) ne %str() %then %let SAVEDATALIBREF = &SAVEDATALIBREF..;
        %ISDATA(dataset=_VCDiag);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &SAVEDATALIBREF.&outname._diag;
            set _VCDiag;
            run;
        %END;
        %ELSE %DO;
            data &SAVEDATALIBREF.&outname._diag;
            retain CombGroup;
            set indata.&DIATABLE.(obs=0)
				_one;
            EncType=CombEncType;
            Dx=CombCode;
            Dx_CodeType='CB';
            PDX=CombPDX;
            Adate=AdateB;
            stop;
            keep Patid Adate EncType Dx Dx_CodeType PDX CombGroup;
            run;
        %END;
        %ISDATA(dataset=_VCDrug);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &SAVEDATALIBREF.&outname._drug;
            set _VCDrug;
            run;
        %END;
        %ELSE %DO;
            data &SAVEDATALIBREF.&outname._drug;
            retain CombGroup;
            set indata.&DISTABLE.(obs=0)
				_one;
            format Rxdate mmddyy10.;
			length Rxdate 4;
            Rxdate=AdateB;
            Rx=CombCode;
            Rx_CodeType='CB';           
            RxSup=EdateB-AdateB+1;
            RxAmt=1;
            stop;
            keep Patid Rxdate Rx RxSup Rx_CodeType RxAmt CombGroup;
            run;
        %END;
        %ISDATA(dataset=_VCProc);
        %IF %eval(&NOBS.>0) %THEN %DO;
            data &SAVEDATALIBREF.&outname._proc;
            set _VCProc;
            run;
        %END;
        %ELSE %DO;
            data &SAVEDATALIBREF.&outname._proc;
            retain CombGroup;
            set indata.&PROCTABLE.(obs=0)
				_one;
            EncType=CombEncType;
            Px=CombCode;
            Px_CodeType='CB';
            Adate=AdateB;
            stop;
            keep Patid Adate EncType Px Px_CodeType CombGroup;
            run;
        %END;
	%end;

	*link to MPs if tool ne used as standalone;
	%if &RUNSTANDALONE = N %then %do; *Combo was called within a MP;

	    %ISDATA(dataset=_VCDrug);
	    %IF %eval(&NOBS.>0) %THEN %DO;
	        *Adding parameters to raw drug claims for MPs usage;
	        proc sql noprint;
		        create table worktemp.drugsComb as
		        select Dispensing.Patid, 
		               Dispensing.Rx,
		               Dispensing.RxDate,
		               Dispensing.RxSup,
		               Dispensing.RxAmt,
					   case when missing(Dispensing.Rx_CodeType) then 'CB' 
					   else dispensing.Rx_CodeType
					   end as Rx_CodeType
		        from _ndc(drop= qrpcombocode ) as CodeList right join
		            _VCDrug (where=(RxSup > 0 and RxAmt>0)) as Dispensing
		        on Dispensing.Rx = CodeList.Code;
	        quit;    

			%isdata(dataset=worktemp.drugsComb);
			%if %eval(&nobs.>0) %then %do;
				%let CHKCOMBREC=1;
			%end;
	    %END;

	    %ISDATA(dataset=_VCDiag);
	    %IF %eval(&NOBS.>0) %THEN %DO;
	        proc append base= worktemp._diagextract data=_VCDiag(%if %length(&DXCOMBOLIST) > 0 %then %do; where=(dx in (&DXCOMBOLIST)) %end; drop=CombGroup) force;
	        run;
	    %END;
	    %ISDATA(dataset=_VCProc);
	    %IF %eval(&NOBS.>0) %THEN %DO;
	        proc append base= worktemp._procextract data=_VCProc(%if %length(&PXCOMBOLIST) > 0 %then %do; where=(px in (&PXCOMBOLIST)) %end; drop=CombGroup)  force;
	        run;
	    %END;

		proc datasets nowarn noprint lib=work;
			delete _one _forcombine;
		quit;

	%END;

	proc datasets library=work nolist nowarn;
    	delete _VC: _combodem _combodiag _combodrug _comboenc _comboenr: _combolab _comboproc _labkey _labcodesMap _leftdays;
    quit;

	%symdel numenctype drgs;

	*execution time;
	%if &RUNSTANDALONE = N %then %do;
		%ms_stoptimer(timestart=comboruntime);
		%ms_outputruntimes(timevar=comboruntime, step=%str(Combo), group=combo, monitoringperiod=);
	%end;
	%else %do;
	    data _NULL_;
	       temp=DATETIME();
	       seconds=temp-&STARTCOMBO.;
	       hours=int(seconds/3600);
	       minutes=int((seconds-hours*3600)/60);
	       seconds2=int((seconds-hours*3600-minutes*60));
	       call symput('STOP',put(temp,best.));
	       call symput('hours',put(hours,4.0));
	       call symput('minutes',put(minutes,2.0));
	       call symput('seconds',put(seconds2,2.0));
	    run;

	    %PUT TOTAL RUN TIME was &hours. h &minutes. m &seconds. s; 
	%end;

	%put NOTE: ********END OF MACRO: combo ********;

%MEND combo;