/**
   @file ms_createmicohorts.sas
   @brief This macro creates exposure and control (unexposed, comparator) cohorts, process according to MICOHORTFILE including additional inclusion/exclusion criteria, and evaluate outcomes.

   @details
 	- Check if MIDAYSDIFF is specified, if so and MILTABLE exists,then merge in ADATE.				
 	- Assign libname for &RUNID._concepi_mstr_preg_tri dataset used.				
 	- Loop through milgrp.				
	 	- MICOHORTFILE parameters.			
		 	- Infant sex values applicable to demographics.		
		 	- When linked infant parameters are missing, assign default values.		
	 	- COHORTFILE for enrollment parameters.			
		 	- Age stratification.		
		 	- Enrollment gap.		
		 	- Pre-pregnancy period.		
	 	- Assess correct HDPS window for this specific milgrp.			
	 	- For data-driven query period.
			- Determine query period.			
		 	- Confirm outcome anchors are set correctly.		
		 	- Add MILGRP end date to monitoring file.		
		 	- Recreate look dataset.		
		 	- Output METADATA file.		
	 	- Custom warning if control cohort is unexposed and index date is INDEXDT_EXP.			
	 	- Custom warning if control cohort is unexposed and outcome anchor is INDEXDT_EXP.			
	 	- Reset cumulative.			
	 	- If OUTCOMEPOP = I or OUTCOMEPOP = MI, use gracedays to determine if infant enrollment start should be reassessed as the delivery date or remain as its original value.			
	 	- If OUTCOMEPOP = MI, create consolidated mother-infant enrollment spans by bridging enrollment spans using the ENROLGAP parameter.			
		 	- Find all infant enrollment dates from _pregcohort (with gracedays applied) and also find those from worktemp.dem_infant_enr_&ENRNUM. that are not in _pregcohort.		
		 	- Get all unique patid and linkedid so we can retrive all enrollment spans for this combination.		
		 	- Join all enrollment from mothers and infants.		
		 	- Consolidate mother-infant enrollment spans by bridging enrollment spans using the ENROLGAP parameter.		
		 	- Change episode if maximum allowable gap is reached.		
		 	- Reconciliation of eligible episodes.		
			 	- worktemp.outenr_&ENRNUM. is the consolidated enrollment dataset formed by bridging the enrollment episodes of mothers and infants.	
			 	- Add in consolidated enrollment dates (outcome_enr_start and outcome_end_end) to concepi_mstr_preg_tri dataset.	
	 	- Create new cohorts.			
	 	- Level 1: Initial Episode Count - Pregnancy episodes meeting initial cohort eligibility requirements (lowcase(group)="&GROUPNAME").		
		 	- Exposure cohort:		
			 	- Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date.	
			 	- Only keep if infant is the desired sex.	
	 	- Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (moiname = "&EXPMP." & exposuretri=1).			
	 	- Level 3: Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date.			
	 	- Level 4: Exclusion - Linked infant must satisfy the sex requirement (csexmatch = "Y").			
	 	- Control cohort:			
		 	- Level 1: Initial Episode Count Pregnancy episodes meeting initial cohort eligibility requirements (lowcase(group)="&GROUPNAME.").		
			 	- Only keep linked infant exceeded maximum allowable days required between birth date and delivery date.	
			 	- Only keep if infant is the desired sex.	
			 	- Because dplocal.&RUNID._concepi_mstr_preg_tri contains 1 row per MOI, roll up to 1 row per delivery to ensure delivery unexposed during exposure period.	
			 	- Only 1 delivery per unexposed cohort.	
				 	- Recode moiname to expMP to correctly count other period exposures.
		 	- Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (exposuretri = 1).		
		 	- Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (only 1 delivery per unexposed cohort). 		
		 	- Level 3: Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date.		
		 	- Level 4: Exclusion - Linked infant must satisfy the sex requirement (csexmatch = "Y").		
	 	- Determine maximum treewashper.			
		 	- PSMATCH, PSIPTW and PSSTRATIFICATION creation of analysisgrp.		
	 	- Merge exposure and comparator cohorts.			
		 	- Define exosurefrom/exposureto date.		
		 	- Reassign index date if begins prior to exposure from period.		
		 	- Reassign exposure index date if begins prior to exposure from period. If exposure period starts after the end of the exposure episode then set to missing.		
		 	- Assign ID to delivery in order to link Mothers-Infants during Incl/Excl processing.		
		 	- Compute difference between date of enrollment and date of  birth for linked infants.		
		 	- Compute length of enrollment for matched infants.		
		 	- For time to event analysis outcome assessment should only begin from the day of index exposure and later for cohorts with MOI.
				- Any outcome occurring before index exposure will be ignored. 
				- For unexposed control cohort, exposure start window or start date should Not be considered.		
		 	- Re-evaluate enrollment for baseline covariates using new index date.		
		 	- Re-evaluate enrollment for baseline covariates anchored on index exposure for exposure cohort and exposed reference cohort.		
		 	- Enrollment must be satisfied during the exposure and pre-pregnancy outcome assessment period.
				- When outcomepop=M, evaluate Treewash enrollment from outcome start date.		
		 	- Compute followuptime and ttc for treescan.		
		 	- Check if enrollment is met on the first day of the outcome window.		
		 	- Reassess zip_uncertain.		
		 	- Recompute age, year, month and quarter using the indexdt defined by INDEXDATE.		
		 	- Check if the indexdt defined by INDEXDATE is recorded in the query period.		
		 	- Check if required post-pregnancy enrollment criterion is met.		
	 	- Tree extraction, delivery date must meet look periods.			
	 	- Level 5: Exclusion - Live birth delivery must be during the look period.			
	 	- Reassess Age Group criteria.			
	 	- Level 6: Exclusion - Pregnancy episode index date must overlap the query period and satisfy the age range condition within the query period.			
	 	- List mother and infant IDs.			
	 	- If OUTCOMEPOP=MI we need to consider mother and infant bridged enrollment spans calculated above.			
	 	- Select all enrollment spans for exposure group.			
	 	- Select all enrollment spans for reference group.			
	 	- Process additional codes: Inclusion/Exclusion and outcomes.			
		 	- Evaluate separately for EXP and COMP cohorts.		
			 	- Create &GROUPIND. subset.	
			 	- Break out CaresettingPrincipal into Caresetting and Principal.	
			 	- Extract milgrp-specific records.	
			 	- Restricting to mothers and linked infants.	
			 	- Make sure all claims overlap enrollment span and only keep eligible exposures (overlaping Enr_Start Enr_End).	
			 	- Store maximum number of combination of stockpiling parameters.
			 	- Only process dataset if there are subsetted observations.	
			 	- Stack all datasets together.	
			 	- ExpireDt does not exist in _ITDrugsComb.	
			 	- Need to reshave because Stockpiling may have pushed claims outside enrollment period.	
			 	- Create _InclExcl _FUPEvent, and _FUTforTrunk datasets.	
			 	- Evaluate inclusion/exclusion criteria.	
				 	- Rename variables related to episodes nomenclature to make them consistent with cida cohort episodes and easily usable in the ms_createpov3 macro.
				 	- Reassign original variable names.
				 	- Identify all conditions on inclusioncodes file.
				 	- If one exposure is not excluded then all exposures related to that pregnancy are not excluded.
	 	- Level 7: Exclusion - Pregnancy episodes must satisfy the pre-pregnancy enrollment requirements within the query period.			
	 	- Level 8: Exclusion - Pregnancy episodes must satisfy the post-pregnancy enrollment requirements within the query period.			
	 	- Level 9: Exclusion - Pregnancy episodes must satisfy the inclusion and exclusion criteria.			
	 	- Check if FUT codes truncation needs to be applied. 			
	 	- Evaluate outcomes.			
		 	- Extract cohort specific codes (mother and infant).		
		 	- Select 1st event date in outcome window and merge with master episode list.		
	 	- Restrict to 1st episode per group and compute followuptime for time to event analysis.			
		 	- Timetocensor should start from User-defined Indexdate.		
	 	- Level 10: Exclusion - Must have at least 1 day of followup in the risk window. 			
		 	- For L2 analyses, this exclusion will be performed in ms_covariateadjustment after PS model.		
	 	- Level 11: Exclusion - Restrict to first valid pregnancy episode.			
 	- If inclusion/exclusion dataset exists then add attrition_cond dataset to _attrition table.				
 		- Output attrition table to msoc folder.			
 	- Combine _eoi and _ref cohorts.				
	 	- Compute MOI metrics for final cohorts for baseline/table 1.				
		 	- Summarize.		
		 	- Save to dplocal.			
  
   @par Program inputs
 	- &LIB_CONCEPI_MSTR.&RUNID._concepi_mstr_preg_tri (Dataset containaing trimester statistics for each pregnancy outcome and medical product of interest.)
 	- dplocal.&RUNID._concepi_mstr_preg_tri (Dataset for Type 4 analysis for the pregnant cohort containing trimester statistics for each pregnancy outcome and medical product of interest.) 	
 	- infolder.&&PSFILE_CURRENT. (Dataset for PS matching (&PSMATCHFILE.), IPTW (&IPTWFILE.) and/or PS stratification (&STRATIFICATIONFILE.).)
 	- infolder.&COHORTFILE. (Dataset used to define enrollment and demographic requirements, type of cohort identification strategy.)
 	- infolder.&INCLUSIONCODES. (Dataset with codes used to define additional cohort inclusion/exclusion criteria.)
 	- infolder.&IPTWFILE. (Dataset specifying the parameters for an IPTW analysis.)
 	- infolder.&MICOHORTFILE. (Dataset for Type 4 analysis with pregnancy cohorts among pregnancies matched to an infant specification.)
 	- infolder.&PSESTIMATIONFILE. (Dataset with the parameters for estimating a PS model and is required for PS-based analyses.)
 	- infolder.&STRATIFICATIONFILE. (Dataset with parameters for a PS stratification analysis and required for PS-based analyses.)
 	- infolder.&TREEFILE. (Dataset with parameters required to execute multiple SI analyses from a basic QRP execution.)
 	- infolder.&TYPE4FILE. (Dataset required for a pregnancy episodes identification strategy.)
 	- &COHORTCODES. (Dataset that includes the codes for outcomes.)
 	- &MONITORINGFILE. (Dataset that defines the relevant query periods for execution.)
 	- work._attrition (Temporary dataset with number of mother-infant pairs/pregnancy episodes excluded and remaining for each cohort.)
 	- work._attrition_cond (Dataset containing defined group pregnancy episodes.)
 	- work.cohortfile_for_enr (Dataset where same basecohort are grouped and assess order to process them correctly.)
 	- work.hdps (Dataset containing combination items that behave like DX (&DPLOCALLIB.&outname._diag and worktemp._diagextract).)
 	- work.stockpile_noncovar (Dataset containing variables to stockpile.)
 	- worktemp.dem_infant_enr_&ENRNUM. (Dataset containing infant enrollment spans.)
 	- worktemp.drugs (Dataset containing RX to extract.)
 	- worktemp.drugscomb (Dataset containing combination items that behave like RX.)
 	- worktemp.enr_&ENRNUM. (Dataset containing enrollment spans.)
 	- worktemp.labextract (Dataset containing labs to extract.)
 	- worktemp.meds (Dataset containing combination of _diags and _procs.)
	- worktemp.milextract (Dataset with mother-infant linkage data.)

   @par Program outputs 
 	- dplocal.&RUNID._mstr_mi (Dataset for Type 4 analyses produced for exposed and comparator/unexposed pregnant cohorts.)
 	- msoc.&RUNID._metadata_for_time_period_&PERIODIDSTART. (Dataset used when doing sequential SI analysis.)
 	- msoc.&RUNID._mil_attrition (Dataset containing the number of mother-infant pairs/pregnancy episodes excluded and remaining for each cohort for Type 4 analysis.)
 	- &COHORTCODES._mi (Dataset that includes the codes for mother-infant outcomes.)
 	- &MONITORINGFILE.&A. (Dataset that defines the relevant query periods for execution.)
 	- work._inclexcl (Dataset with a list of control patients and demo data.)
 	- work._metadata (Dataset containing metadata for types 2 and 3.)
 	- work._noexcl (Dataset containing unique values of pregnant start date per patient.)
 	- work.looks (Dataset containing non overlapping looks dates.)

* **Usage**

	  %ms_createmicohorts();

   @par Parameters
	None.

<h4> SAS Macros Dependencies </h4>
   @li ms_agestrat.sas
   @li ms_attrition_compute.sas
   @li ms_caresettingprincipal.sas
   @li ms_createpov3.sas
   @li ms_extractdrugs.sas
   @li ms_extractlabs.sas
   @li ms_loopmeds.sas
   @li ms_periodsoverlap.sas
   @li ms_processwildcards.sas
   @li ms_shaveoutside.sas
   @li ms_stockpiling.sas
   @li ms_time.sas

   @note When creating the cohorts, the MICOHORTFILE.INDEXDATE parameter is used to assign one of the following date to the INDEXDT variable: PREGSTARTDT, INDEXDT_EXP, PREGENDDT.
		 This is used for purposes of:
		 - calculating maternal age
		 - computing signal identification TTC
		 - computing zip_uncertain
		 - performing inferential/tree analyses.
 
   @author Sentinel Coordinating Center (info@sentinelsystem.org)
**/

%macro ms_createmicohorts();

    %put =====> MACRO CALLED: ms_createmicohorts; 	

    %isdata(dataset=infolder.&MICOHORTFILE.);
    %if %eval(&nobs.>0) %then %do;
        %let n= &nobs.;

		data _diag
             _others;
            set &cohortcodes.;
            where ((indexcriteria in('INC','EXC','FUT')) | fupcriteria = 'DEF');
            if codecat ='DX' then output _diag;
            else output _others;
        run;

		%isdata(dataset=_diag);
		%if %eval(&nobs.>=1) %then %do;
		    %MS_ProcessWildcards(InFile=_diag, 
		                         CodeVar=code, 
		                         OutFile=_diag);
		%end;

		data &cohortcodes._MI;
		set _diag
			_others;
		run;

		/*check if MIDAYSDIFF is specified, if so and MILTABLE exists,then merge in ADATE*/
		%let MAX_MIDAYSDIFF = ;
		proc sql noprint;
		  select max(MIDAYSDIFF) into: MAX_MIDAYSDIFF 
          from infolder.&MICOHORTFILE.;
		quit;

		%ISDATA(dataset=worktemp.milextract);
        
		%if %EVAL(&NOBS.>0) and &MAX_MIDAYSDIFF. >= 0 %then %do;
		  proc sql noprint undo_policy=none;
		    create table &runid._concepi_mstr_preg_tri as
			select a.*, b.adate 
			from dplocal.&runid._concepi_mstr_preg_tri as a 
			left join worktemp.milextract(keep=adate patid linkedid where=(missing(patid) = 0 and missing(linkedid) = 0)) as b
			on a.patid=b.patid and a.linkedid = b.linkedid;
		  quit;
		  %let lib_concepi_mstr = ;
		%end;
		%else %do;
		  /* assign libname for &runid._concepi_mstr_preg_tri dataset used */
		  %let lib_concepi_mstr = dplocal.;
		%end;

        /*loop through milgrp*/
        %do a=1 %to &n.;

            %ms_starttimer(timestart=micohortruntime);

            *MICOHORTFILE parameters;
            data _null_;
                set infolder.&MICOHORTFILE. (firstobs=&a obs=&a);
                    call symputx('milgrp', lowcase(milgrp));
                    call symputx('groupname', lowcase(groupname));
                    call symputx('expmp', upcase(expmp));
                    if missing(controlmp) = 0 then do;
                        call symputx('controlmp', upcase(controlmp));
                    end;
                    else do;
                        call symputx('controlmp', '');
                    end;

                    *Infant sex values applicable to demographics;
                    if missing(csex) then do;
                        call symputx("Restrictcsex", "N");
                        call symputx("CDemogSex",'"F" "M" "O"');
                    end;
                    else do;			  
						if countc(upcase(csex), 'MFO') = 3 then call symputx("Restrictcsex", "N"); else call symputx("Restrictcsex", "Y");						  
                        call symputx('CDemogSex',cat('"',tranwrd(upcase(strip(csex))," ",'" "'),'"'));
                    end;
					
                    call symputx('exposureunit', upcase(exposureunit));
                    call symputx('exposurefrom', exposurefrom);
                    call symputx('exposureto', exposureto);
                    call symputx('outcomepop', upcase(outcomepop));
                    call symputx('indexdate', upcase(indexdate));
                    call symputx('outcomefromanchor', upcase(outcomefromanchor));
                    call symputx('outcometoanchor', upcase(outcometoanchor));
                    call symputx('outcomefrom', outcomefrom);
                    call symputx('outcometo', outcometo);
                    call symputx('createbaseline', upcase(createbaseline));

					if strip(cohortstocreate) =  "" then cohortstocreate="B";
					call symputx('cohortstocreate', upcase(upcase(cohortstocreate)));

					if reqdaysaftpreg = . then reqdaysaftpreg = 0;
					call symputx('reqdaysaftpreg', reqdaysaftpreg);

					*when linked infant parameters missing, assign default values;
                    if gracedays = . then gracedays = 0;
                    if MIDAYSDIFF = . then MIDAYSDIFF = 99999;
					call symputx('GRACEDAYS', GRACEDAYS);
                    call symputx('MIDAYSDIFF', MIDAYSDIFF);
            run;

			*COHORTFILE for enrollment parameters;
            data _null_;
                set cohortfile_for_enr;
                  where lowcase(cohortgrp) = "&groupname";  
                    call symputx('ENRNUM',enrollmentnum);     
					call symputx("enrdays",put(t4pregenrdays,best.));
					*Age stratification;
                    if missing(agestrat) then do;
                        call symputx("agestrat","00-01 02-04 05-09 10-14 15-18 19-21 22-44 45-64 65-74 75+");
                    end;
                    else do;
                        call symputx("agestrat",agestrat);
                    end;
					*Enrollment gap;
					if enrolgap ne . then call symputx("ENROLGAP",enrolgap);
					else call symputx("ENROLGAP","0"); 
             run;

			 data _null_;
                set infolder.&type4file.;
                where lowcase(group) = "&groupname";         
                *Pre pregnancy period;
                if missing(prepregdays) then call symputx('prepregdays', 0);
                else call symputx('prepregdays', abs(prepregdays));
            run;

			* Assess correct HDPS window for this specific milgrp;
			%let MinHDPSWinFromIndexdt=.;
			%let MinHDPSWinFromIndexdt_exp=.;
			%let MinHDPSWinFromEpisodeEnddt=0;

			%ISDATA(dataset=HDPS);  
			%IF %EVAL(&NOBS.>0) %THEN %DO;
				proc sql noprint;            
					select min(HDPSWinFrom) into :MinHDPSWinFromIndexdt trimmed                     
			        from HDPS 
			        where group in ("&milgrp._ref" "&milgrp._eoi") and HDPSWinFrom < 0 and lowcase(HDPSWinFromAnchor) in ("indexdt", "");

					select min(HDPSWinFrom) into :MinHDPSWinFromIndexdt_exp trimmed                      
					from HDPS
					where group in ("&milgrp._ref" "&milgrp._eoi") and HDPSWinFrom < 0 and lowcase(HDPSWinFromAnchor) = "indexdt_exp";

					select min(HDPSWinFrom) into :MinHDPSWinFromEpisodeEnddt trimmed                      
					from HDPS
					where group in ("&milgrp._ref" "&milgrp._eoi") and HDPSWinFrom < 0 and lowcase(HDPSWinFromAnchor) = "episodeenddt";
			      quit;
			%END;

            /* For data-driven query period, determine query period */
            %if "&datadrivenqueryperiod" = "Y" %then %do;

                *Confirm outcome anchors set correctly;
                %if %str("&outcomefromanchor") ne "INDEXDT_DELIV" | %str("&outcometoanchor") ne "INDEXDT_DELIV" %then %do;
                    %put ERROR: (Sentinel) Outcome anchors must be set to Delivery Date for data driven query periods;
                    %abort;
                %end; 

                data &monitoringfile.&a.;
                    set &monitoringfile.(drop=enddate);

                    *Add this MILGRP end date to monitoring file;
                    enddate = INTNX('Month', censordate-max(&outcometo.,0, &reqdaysaftpreg.),0, 'begin')-1;
                    
                    format enddate date9.;
                    call symputx('enddate', enddate);
                run;

                *Recreate look dataset;
                proc sort data=&Monitoringfile.&a. out=looks;
                    by PERIODID;
                run;
    
                data looks;
                    set looks;
                    format lenddate LookStartdt LookEnddt date9.;
                    length lenddate LookStartdt LookEnddt 4;
                    LookStartdt=startdate;
                    LookEnddt=enddate;
                    if periodid>1 then LookStartdt=lenddate+1;
                    lenddate=enddate;
                    retain lenddate;
                    rename PeriodId = Look;
                    keep PeriodId LookStartdt LookEnddt;
                run; 

                *Output METADATA file;
                data _metadata;
                    retain reqid runid group timeperiod startdate expperiodstartdt expperiodenddt survenddate;
                    merge &monitoringfile.&a.(keep=startdate censordate periodid rename=censordate =survenddate rename=periodid=timeperiod where=(timeperiod=&periodidstart.))
                        looks(keep=look lookstartdt lookenddt where=(timeperiod=&periodidstart.) 
                        rename=look=timeperiod rename=lookstartdt=expperiodstartdt rename=lookenddt = expperiodenddt);
                    by timeperiod;

                    format reqid $40. runid $3. group $40. startdate expperiodstartdt expperiodenddt survenddate date9.;
                    length reqid $40 runid $3 group $40;

                    reqid = "&reqid.";
                    runid = "&runid.";
                    group = "&milgrp.";
                run;

                %if %eval(&a.=1) %then %do;
                    data msoc.&runid._metadata_for_time_period_&PERIODIDSTART.;
                        set _metadata;
                    run;
                %end;
                %else %do;
                    proc append base=msoc.&runid._metadata_for_time_period_&PERIODIDSTART.
                        data=_metadata force;
                    run;
                %end;

            %end;

            *Custom warning if control cohort is unexposed and index date is INDEXDT_EXP;
            %if %str(&controlmp) = %str() and "&indexdate" = "INDEXDT_EXP" and (&cohortstocreate. eq B or &cohortstocreate. eq R) %then %do;
                %put ERROR: (Sentinel) User defined index date is set to exposure date, however control cohort is unexposed;
				%abort;
            %end;

            *Custom warning if control cohort is unexposed and outcome anchor is INDEXDT_EXP;
            %if %str(&controlmp) = %str() and ("&outcomefromanchor" = "INDEXDT_EXP" | "&outcometoanchor" = "INDEXDT_EXP") and (&cohortstocreate. eq B or &cohortstocreate. eq R) %then %do;
                %put ERROR: (Sentinel) User defined outcome anchor date is set to exposure date, however control cohort is unexposed;
				%abort;
            %end;            

			*Reset cumulative;
            proc datasets library=work nowarn nolist;
                delete _attrition;
            quit;

			*if OUTCOMEPOP = I or OUTCOMEPOP = MI, use gracedays to determine if infant enrollment start should be reassessed 
			   as the delivery date or remain as its original value;
			%if %index(&outcomepop,I) > 0 %then %do;
				data _pregcohort;
 			 		set &lib_concepi_mstr.&runid._concepi_mstr_preg_tri(where=(lowcase(group)="&groupname"));
						length outcome_enr_start outcome_enr_end 4.;
						format outcome_enr_start outcome_enr_end mmddyy10.;
				  		if cenr_start > episodeenddt and sum(cenr_start, -&gracedays.) <= episodeenddt 
							then outcome_enr_start = episodeenddt;
							else outcome_enr_start = cenr_start;
						 outcome_enr_end = cenr_end;
 				run;	
			%end;

		  	/*If OUTCOMEPOP = MI, create consolidated mother-infant enrollment spans by bridging enrollment spans using the ENROLGAP parameter*/
		  	%if %index(&outcomepop,MI) > 0 %then %do;
	
	  		 *find all infant enrollment dates from _pregcohort (with gracedays applied) and also find those from worktemp.dem_infant_enr_&enrnum.
	  		  that are not in _pregcohort;
			  proc sql noprint;                
				create table _infcohort (where=(outcome_enr_end >= outcome_enr_start)) as
				select patid, linkedid, outcome_enr_start length=4 format=mmddyy10., outcome_enr_end length=4 format=mmddyy10. 
				from 
				
	            (    (select a.patid, 
							a.linkedid,
						    a.outcome_enr_start , 
						    a.outcome_enr_end
	                from _pregcohort as a
	             	)
				    union
					(select b.patid,
							b.linkedid,
						    a.Enr_Start as outcome_enr_start ,
				   			a.Enr_End as outcome_enr_end 
	                from worktemp.dem_infant_enr_&enrnum.(keep=patid enr_start enr_end) as a 
	                join dplocal.&runid._mstr_preg(keep = patid linkedid group cenr_Start cenr_End where=(lowcase(group)="&groupname")) as b
					on a.patid=b.linkedid and a.enr_start ne b.cenr_Start and a.enr_end ne b.cenr_End)
					
				 )  
				
				order by patid, linkedid, outcome_enr_start, outcome_enr_end
				 ;
	            quit;

				*Get all unique patid and linkedid so we can retrive all enrollment spans for this combination*;
				proc sort data = dplocal.&runid._mstr_preg (where=(lowcase(group)="&groupname")) 
						   out = Ptslist_mstr_preg (keep = patid linkedid) nodupkey;
				 	by patid linkedid;
				run;
			 	 
				*join all enrollment from mothers and infants;	
				proc sql noprint;                
					create table _mienr (where=(outcome_enr_end >= outcome_enr_start)) as
					select patid, linkedid, outcome_enr_start length=4 format=mmddyy10., outcome_enr_end length=4 format=mmddyy10. 
					from 
					(
		                (select b.patid, 
								b.linkedid,
							    a.Enr_Start as outcome_enr_start,
							    a.Enr_End as outcome_enr_end
		                from worktemp.enr_&enrnum.(keep=patid Enr_Start Enr_End) as a
		                join Ptslist_mstr_preg as b 
						on a.patid=b.patid)
						union
						(select b.patid,
								b.linkedid,
							    a.outcome_enr_start,
							    a.outcome_enr_end
		                from _infcohort(keep=linkedid outcome_enr_start outcome_enr_end) as a 
		                join Ptslist_mstr_preg as b
						on a.linkedid=b.linkedid)
					)
					order by patid, linkedid, outcome_enr_start, outcome_enr_end
					;
	            quit;
			 	 
				*consolidate mother-infant enrollment spans by bridging enrollment spans using the ENROLGAP parameter;
				data _mienr2;
				   set  _mienr;
				   by patid linkedid;
				   retain enrepisode penr_end;
				   		if first.linkedid then do;
								enrepisode=1;
								penr_end  = outcome_enr_end;
						end;

				   *Change episode if maximum allowable gap is reached;
					if outcome_enr_start-(penr_end)-1 > &ENROLGAP. then 
							enrepisode=enrepisode+1; 
							penr_end = max(penr_end, outcome_enr_end);
					drop penr_end;		
				run;
				
			    /*Reconciliation of eligible episodes*/
				/*worktemp.outenr_&enrnum. is the consolidated enrollment dataset formed by bridging the enrollment episodes of mothers and infants*/
			    proc means data=_mienr2 nway missing noprint;
			       var outcome_enr_start outcome_enr_end;
			       class PatId linkedid enrepisode;
			       output out=worktemp.outenr_&enrnum.(drop=_:) min(outcome_enr_start)= max(outcome_enr_end)= / keeplen;
			    run;
				
				/*add in consolidated enrollment dates (outcome_enr_start and outcome_end_end) to concepi_mstr_preg_tri dataset*/ 
				proc sql noprint;
					 create table _pregcohort as
					 select a.*, b.outcome_enr_start, b.outcome_enr_end
					 from &lib_concepi_mstr.&runid._concepi_mstr_preg_tri(where=(lowcase(group)="&groupname")) a
					 join worktemp.outenr_&enrnum.  b
					 on a.patid = b.patid
					 and a.linkedid = b.linkedid
					 where outcome_enr_start <= EpisodeEndDt 
						and outcome_enr_end >= EpisodeEndDt;
				quit;

				proc datasets nowarn noprint lib=work;
					delete _mienr: _infcohort: Ptslist_mstr_preg;
				quit;

			%end; /*OUTCOMEPOP=MI*/

			%let level=1;
 			*Create new cohorts;
			data %if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; _preg_eoi %end;
				 %if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;_preg_ref; %end;;
				set
					 %if %index(&outcomepop,I) > 0 %then %do;
				 		_pregcohort;
					 %end;
					 %else %do;
					 	&lib_concepi_mstr.&runid._concepi_mstr_preg_tri(where=(lowcase(group)="&groupname"));
						   length outcome_enr_start outcome_enr_end 4.;
							format outcome_enr_start outcome_enr_end mmddyy10.;
							outcome_enr_start = enr_start;
							outcome_enr_end = enr_end;
					%end;
			run;	

			%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
				***** Level 1: Initial Episode Count - Pregnancy episodes meeting initial cohort eligibility requirements (lowcase(group)="&groupname") *****;  			
				%ms_attrition_compute(file=_preg_eoi,ToExcl=delnum=0,analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=IndexDt);					
				
                *Exposure cohort;			
                data _preg_eoi;
                    set _preg_eoi ;
					length exposuretri 3 csexmatch _MIDAYSDIFF $1;
 
			        _MIDAYSDIFF = "Y";
		

					%if "&lib_concepi_mstr" = "" %then %do;
				      *Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date;
				      if abs(cbirth_date - adate) > &MIDAYSDIFF. then _MIDAYSDIFF = "N"; 
					  drop adate;
					%end;

					*Only keep if infant is the desired sex;
					csexmatch = "Y";
					%if "&Restrictcsex" = "Y" %then %do;
					  if csex not in (&CDemogSex.) then csexmatch = "N";
					%end;

				 	if moiname = "&expmp." then do;
						if "&exposureunit" = "T" then do;
							do i=&exposurefrom. to &exposureto. until (exposuretri=1);
								if i = -1 then do;
									if usepre = 1 then exposuretri = 1;
								end;
								else if i = 1 then do;
									if anyt1 = 1 then exposuretri = 1;
								end;
								else if i = 2 then do;
									if anyt2 = 1 then exposuretri = 1;
								end;
								else if i = 3 then do;
									if anyt3 = 1 then exposuretri = 1;
								end;
							end;
							drop i;
						end;
						else if "&exposureunit" = "W" then do;
							if gestwkend >=&exposurefrom. and gestwk <=&exposureto. then exposuretri=1;
						end;
					end;
                run;

				***** Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (moiname = "&expmp." & exposuretri=1) *****;
				%ms_attrition_compute(file=_preg_eoi,ToExcl=exposuretri ^= 1,analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=IndexDt);

                ***** Level 3: Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date;				
				%ms_attrition_compute(file=_preg_eoi,ToExcl=_MIDAYSDIFF ^= "Y",analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=IndexDt); 
 		
				***** Level 4: Exclusion - Linked infant must satisfy the sex requirement (csexmatch = "Y") *****;				
				%ms_attrition_compute(file=_preg_eoi,ToExcl=csexmatch ^= "Y",analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=IndexDt); 
			%end; /* &cohortstocreate = B/E */
			%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
                *Control cohort;		
				***** Level 1: Initial Episode Count Pregnancy episodes meeting initial cohort eligibility requirements (lowcase(group)="&groupname") *****;
				%let level=1;
				%ms_attrition_compute(file=_preg_ref,ToExcl=delnum=0,analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=IndexDt); 

                data _preg_ref;
                    set _preg_ref;
					length exposuretri 3 csexmatch  _MIDAYSDIFF  $1;
                    exposuretri = 0;

			        _MIDAYSDIFF = "Y";
					

					%if "&lib_concepi_mstr" = "" %then %do;
				      *Only keep Linked infant exceeded maximum allowable days required between birth date and delivery date;
				      if abs(cbirth_date - adate) > &MIDAYSDIFF. then _MIDAYSDIFF = "N"; 
                      drop adate;
					%end;
				
                    *Only keep if infant is the desired sex;
                    csexmatch = "Y";
                    %if "&Restrictcsex" = "Y" %then %do;
                        if csex not in (&CDemogSex.) then csexmatch = "N";
                    %end;

                    %if %str(&controlmp.) ne %str() %then %do;
                        *comparator group;
                        if moiname = "&controlmp." then do;
                            if "&exposureunit" = "T" then do;
                                do i=&exposurefrom. to &exposureto. until (exposuretri=1);
                                    if i = -1 then do;
                                        if usepre = 1 then exposuretri = 1;
                                    end;
                                    else if i = 1 then do;
                                        if anyt1 = 1 then exposuretri = 1;
                                    end;
                                    else if i = 2 then do;
                                        if anyt2 = 1 then exposuretri = 1;
                                    end;
                                    else if i = 3 then do;
                                        if anyt3 = 1 then exposuretri = 1;
                                    end;
                                end;
								drop i;
                            end;
                            else if "&exposureunit" = "W" then do;
                                if gestwkend >=&exposurefrom. and gestwk <=&exposureto. then exposuretri=1;
                            end;
                        end;
                    %end;
                    *Unexposed group;
                    %if %str(&controlmp.) = %str() %then %do;
                        if moiname = "&expmp." then do;
                            if "&exposureunit" = "T" then do;
                                do i=&exposurefrom. to &exposureto. until (exposuretri=1);
                                    if i = -1 then do;
                                        if usepre = 1 then exposuretri = 1;
                                    end;
                                    else if i = 1 then do;
                                        if anyt1 = 1 then exposuretri = 1;
                                    end;
                                    else if i = 2 then do;
                                        if anyt2 = 1 then exposuretri = 1;
                                    end;
                                    else if i = 3 then do;
                                        if anyt3 = 1 then exposuretri = 1;
                                    end;
                                end;
								drop i;
                            end;
                            else if "&exposureunit" = "W" then do;
                                if gestwkend >=&exposurefrom. and gestwk <=&exposureto. then exposuretri=1;
                            end;
                        end;
                        else do;
                            exposuretri = 0;
                        end;
                    %end;
                run;	
                
                %if %str(&controlmp.) = %str() %then %do;

                *because dplocal.&runid._concepi_mstr_preg_tri contains 1 row per MOI, 
                     need to roll up to 1 row per delivery to ensure delivery unexposed during exposure period;
                    proc means data=_preg_ref noprint nway;
                        var exposuretri;
                        class patid indexdt;
                        output out=_preg_ref_1perdeliv(drop=_:) max=exposuredeliv;
                    run;

                    proc sql noprint;
                        create table _preg_ref1 as
                        select ref.*
                             , d.exposuredeliv
                        from _preg_ref as ref 
                        inner join _preg_ref_1perdeliv as d
                        on ref.patid = d.patid and ref.indexdt = d.indexdt
                        order by ref.patid, d.exposuredeliv, ref.indexdt;
                    quit;

                    *only 1 delivery per unexposed cohort;
                    data _preg_ref(drop=exposuredeliv); 
                        set _preg_ref1;
                        by patid exposuredeliv indexdt;
						length firstindex $1;
                        if first.indexdt and exposuredeliv ne 1 then firstindex="Y";
						else firstindex="N";

                        *recode moiname to expMP to correctly count other period exposures;
                        if moiname ne "&expmp." then do;
                            indexdt2 = .;
                            episodeenddt2 = .;
                            gestwkend = .;
                        end;
                        moiname = "&expmp.";

                    run;

                %end;

				%if %str(&controlmp.) ne %str() %then %do;
					***** Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (exposuretri = 1) *****;
					%ms_attrition_compute(file=_preg_ref,ToExcl=exposuretri ^=1 ,analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=IndexDt); 
				%end;
				%else %do;
					***** Level 2: Exclusion - Pregnancy episodes must have evidence of the MOI (only 1 delivery per unexposed cohort) *****;
					%ms_attrition_compute(file=_preg_ref,ToExcl=firstindex="N",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=IndexDt); 
				%end;

                ***** Level 3: Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date;				
				%ms_attrition_compute(file=_preg_ref,ToExcl=_MIDAYSDIFF ^= "Y",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=IndexDt); 

				***** Level 4: Exclusion - Linked infant must satisfy the sex requirement (csexmatch = "Y") *****;				
				%ms_attrition_compute(file=_preg_ref,ToExcl=csexmatch ^= "Y",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=IndexDt); 
 			%end; /* &cohortstocreate = B/R */
 
				*Determine maximum treewashper;
                %if "&analysis" = "tree" %then %do;                        													
					%let maxtreewashper=0;		
					%let ps_file =;  
			        /* PSMATCH, PSIPTW and PSSTRATIFICATION creation of analysisgrp*/
			        %if "&psmatch." = "Y" %then %let ps_file = &PSMATCHFILE.;
				    %if "&psstratification." = "Y" %then %let ps_file = &ps_file. &STRATIFICATIONFILE.;
					%if "&psiptw." = "Y" %then %let ps_file = &ps_file. &IPTWFILE.;
				 
					%if "&ps_file" ne ""  %then %do;
						%let psfile_count = %sysfunc(countw(&ps_file.," "));
                       
                        proc sql noprint;
                         	select max(treewashper) into: maxtreewashper
                            from infolder.&treefile.
                            where 
                             %do psfiles_run =1 %to &psfile_count.;
                               %let psfile_current=%scan(&ps_file., &psfiles_run.);
                               %if  &psfiles_run = 1 %then %do;
                                 lowcase(group) in 
                                   (select distinct analysisgrp 
                                   from infolder.&PSESTIMATIONFILE. a,
                                        infolder.&&psfile_current. b
                                   where (lowcase(eoi)= lowcase("&milgrp._eoi") | lowcase(ref)= lowcase("&milgrp._ref"))
                                   and lowcase(a.PSESTIMATEGRP) = lowcase(b.PSESTIMATEGRP))
                               %end;
                               %else %do;
                                 or lowcase(group) in 
                                    (select distinct analysisgrp 
                                    from infolder.&PSESTIMATIONFILE. a,
                                         infolder.&&psfile_current. b
                                    where (lowcase(eoi)= lowcase("&milgrp._eoi") | lowcase(ref)= lowcase("&milgrp._ref"))
                                    and lowcase(a.PSESTIMATEGRP) = lowcase(b.PSESTIMATEGRP))
                               %end;
                             %end;
                        ;quit; 

					%end; 
				%end; /*tree*/

                *Merge exposure and comparator cohorts;
                data _micohorts (rename=(_age=age));                                      
                    set %if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; _preg_eoi(in=a drop=group rename=indexdt=pregstartdt rename=episodeenddt = pregenddt) %end;
                        %if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do; _preg_ref(in=b drop=group rename=indexdt=pregstartdt rename=episodeenddt = pregenddt) %end;;
											
                    format group milgrp $40. indexdate outcometoanchor outcomefromanchor $20. indexdt indexdt_exp date9. outcomepop $2. MetMinEnroll $1. ;
                    length indexdt indexdt_exp exposurefromdt 4 MinEnrolMet MinEnrolMet2 3;

                    milgrp = "&milgrp";
                    %if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; if a then group = cats("&milgrp","_eoi"); %end;
                    %if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do; if b then group = cats("&milgrp","_ref"); %end;
					indexdt_exp = indexdt2;

					*define exosurefrom/exposureto date;
                    if "&exposureunit" = "T" then do;
                        if &exposurefrom. = -1 then do;
                            exposurefromdt = pregstartdt - &prepregdays.;
                        end;
                        else if &exposurefrom. = 1 then do;
                            exposurefromdt = pregstartdt;
                        end;
                        else if &exposurefrom. = 2 then do;
                            exposurefromdt = pregstartdt+98;
                        end;
                        else if &exposurefrom. = 3 then do;
                            exposurefromdt = pregstartdt+196;
                        end;

						if &exposureto. = -1 then do;
                            exposuretodt = pregstartdt - &prepregdays.;
                        end;
                        else if &exposureto. = 1 then do;
                            exposuretodt = pregstartdt+97;
                        end;
                        else if &exposureto. = 2 then do;
                            exposuretodt = pregstartdt+195;
                        end;
                        else if &exposureto. = 3 then do;
                            exposuretodt = pregenddt;
                        end;
                    end;
                    if "&exposureunit" = "W" then do;
                        if &exposurefrom. <0 then exposurefromdt = pregstartdt + ((int(((&exposurefrom.+1)*7)-1))-6);
                        else if &exposurefrom. >=0 then exposurefromdt = pregstartdt + int((&exposurefrom.)*7);

						exposuretodt = exposurefromdt + (((&exposureto. - &exposurefrom.)*7)+6);
                    end;
                   
                    *set indexdt;
                    if "&indexdate" = "INDEXDT" then do;
                        indexdt = pregstartdt;
                        indexdate = "INDEXDT";
                    end;
                    if "&indexdate" = "INDEXDT_EXP" then do;
                        indexdt = indexdt2;                        
                        indexdate = "INDEXDT_EXP";

                        *Reassign index date if begins prior to exposure from period;                        
						if indexdt < exposurefromdt then indexdt = exposurefromdt;                           
                    end;
                    if "&indexdate" = "INDEXDT_DELIV" then do;
                        indexdt = pregenddt;
                        indexdate = "INDEXDT_DELIV";
                    end;

					*Reassign exposure index date if begins prior to exposure from period. 
					 If exposure period starts after the end of the exposure episode then set to missing; 
					if indexdt_exp < exposurefromdt <= episodeenddt2 then indexdt_exp = exposurefromdt;
					else if episodeenddt2 < exposurefromdt then do;
						indexdt_exp=.;
						indexdt2=.;
						episodeenddt2=.;
					end;

                    *Assign ID to delivery in order to link Mothers-Infants during Incl/Excl processing;
                    milID = _n_;

                    /*Compute difference between date of enrollment and date of  birth for linked infants*/
                    enroll_diff = cenr_start - cbirth_date;

                    /*Compute length of enrollment for matched infants*/
                    birth_enroll = cenr_end - cenr_start;

                    /*Dates for Outcome evaluation*/
                    format outcomestart outcomeend date9.;
                    length outcomestart outcomeend 4;
                    if "&outcomefromanchor" = "INDEXDT" then do;
                        outcomestart = pregstartdt + &outcomefrom.;
                    end;
                    if "&outcomefromanchor" = "INDEXDT_EXP" then do;
                        outcomestart = indexdt2 + &outcomefrom.;
                    end;
                    if "&outcomefromanchor" = "INDEXDT_DELIV" then do;
                        outcomestart = pregenddt + &outcomefrom.; 
                    end;
                    if "&outcometoanchor" = "INDEXDT" then do;
                        outcomeend = pregstartdt + &outcometo.;
                    end;
                    if "&outcometoanchor" = "INDEXDT_EXP" then do;
                        outcomeend = indexdt2 + &outcometo.;
                    end;
                    if "&outcometoanchor" = "INDEXDT_DELIV" then do;
                        outcomeend = pregenddt + &outcometo.; 
                    end;

					/* For time to event analysis outcome assessment should only begin from the day of index exposure and later for cohorts with MOI. 
					   Any outcome occurring before index exposure will be ignored. 
					  However, for unexposed control cohort, exposure start window or start date should Not be considered*/
					%if &T4HOIMETHOD. eq timetoevent %then %do;
					if group = "&milgrp._eoi" |  (group = "&milgrp._ref"  and "&controlmp." ne " " ) then do;
						outcomestart = max(exposurefromdt, indexdt2, outcomestart);
					end;
					%end;
                
                    *Re-evaluate enrollment for baseline covariates using new index date;

                    *Re-evaluate enrollment for baseline covariates using new index date;
                    if Enr_Start <= pregstartdt -max(0,abs(&MinNegCovFromIndexdt.), abs(&MinNegHDPSWinFromIndexdt.), abs(&MinNegMFUFromIndexdt.)) and					   
					   Enr_Start <= pregenddt -max(0,abs(&MinNegCovFromEpisodeEnddt.), abs(&MinNegHDPSWinFromEpisodeEnddt.), abs(&MinNegMFUFromEpisodeEnddt.)) then MinEnrolMet=1;

					 *Re-evaluate enrollment for baseline covariates anchored on index exposure for exposure cohort and exposed reference cohort;
					 if group = "&milgrp._eoi" |  (group = "&milgrp._ref"  and "&controlmp." ne " " )  then do;
						if not (Enr_Start <= coalesce(indexdt_exp,99999) -max(0,abs(&MinNegCovFromIndexdt_exp.), abs(&MinNegHDPSWinFromIndexdt_exp.))) then 
						MinEnrolMet = .;
					 end;


					*Enrollment must be satisfied during the exposure and pre-pregnancy outcome assessment period;
					*When outcomepop=M, evaluate Treewash enrollment from outcome start date;
					if Enr_Start <= exposurefromdt and enr_end >= min(exposuretodt,pregenddt) 
					   %if &outcomepop. eq M %then %do;
					   and Enr_Start <= outcomestart - max(0, &maxtreewashper.) 
					   %end;
					   	then MinEnrolMet2 = 1;
										
                    *analysis specific parameter values;
                    exposureunit = "&exposureunit";
                    exposurefrom = &exposurefrom.;
                    exposureto = &exposureto.;
                    outcomepop = "&outcomepop";
                    outcomefromanchor = "&outcomefromanchor";
                    outcometoanchor = "&outcometoanchor";
                    outcomefrom = &outcomefrom.;
                    outcometo = &outcometo.;					

                    if MinEnrolMet and MinEnrolMet2 then MetMinEnroll="Y";
					else MetMinEnroll="N";

                    *For ADS;
                    LastLookFollowed = .;
                    
                    *compute followuptime and ttc for treescan;
					%if "&analysis" = "tree" %then %do;						
                        followuptime = min(outcomeend, outcome_enr_end) - pregenddt + 1;
                        if outcome_enr_end <= outcomeend then do;
                        	if outcomepop = "M" then ttc = outcome_enr_end - indexdt;
                        	else ttc = outcome_enr_end - pregenddt;
                    	end;
                    %end;                    
					
					*check if enrollment is met on the first day of the outcome window;
					if outcome_enr_start <= outcomestart <= outcome_enr_end then followupmet="Y";
					else followupmet="N";


                    *Reassess  zip_uncertain;						
					%if "&geog." = "Y" %then %do;
						if missing(zip_date) or indexdt < zip_date then Zip_uncertain = 'Y';
						else zip_uncertain = 'N';
					%end;
					%else %do;
						zip_uncertain = '';
					%end;

					*recompute age, year, month and quarter using the indexdt defined by INDEXDATE;
					_age = (indexdt - birth_date)/365.25;
					year = year(indexdt);
					month = month(indexdt);
					quarter=qtr(indexdt);

					*Check if the indexdt defined by INDEXDATE is recorded in the query period; 
					if indexdt >= &startdate. then inQueryPeriod="Y";

					*Check if required post-pregnancy enrollment criterion is met;
					MinPostPregEnrolMet="N";
					if &reqdaysaftpreg. eq 0 then MinPostPregEnrolMet="Y";
					else if outcome_enr_start <= pregenddt and min(outcome_enr_end, &censordate.) - pregenddt >= max(0,&reqdaysaftpreg.) then MinPostPregEnrolMet="Y";					
				
					drop age: csexmatch exposuretri %if "&analysis" = "tree" %then %do; IndexLookStartDt IndexLookEndDt IndexLook
					   %end;
					     exposuretodt MinEnrolMet MinEnrolMet2 %if %str(&controlmp.) = %str() and (&cohortstocreate. eq B or &cohortstocreate. eq R) %then %do; firstindex %end;;
				run;

            *Tree extraction - delivery date must meet look periods;
            %if "&analysis" = "tree" %then %do;
                proc sql noprint undo_policy=none;
                    create table _micohorts as
                    select dat.*,
					       (case when dat.milid = sub.milid then 'Y'
						         else 'N'
							end) as lookpreg,
                           sub.IndexLookStartDt,
                           sub.IndexLookEndDt,
                           sub.IndexLook							
					from _micohorts as dat left join
						(select mi.milid,
                           look.lookstartdt as IndexLookStartDt,
                           look.LookEnddt as IndexLookEndDt,
                           Look.Look as IndexLook
						from _micohorts as mi,
							 looks as look
						where look.LookStartdt<=mi.pregenddt<=look.LookEnddt) sub
					on dat.milid = sub.milid;
                quit;
								
				***** Level 5: Exclusion - Live birth delivery must be during the look period *****;
				%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
				%ms_attrition_compute(file=_micohorts,ToExcl=(lookpreg='N' and group="&milgrp._eoi") | group="&milgrp._ref",analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=pregstartdt); 
				%end;
				%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
				%let level = 5;
				%ms_attrition_compute(file=_micohorts,ToExcl=(lookpreg='N' and group="&milgrp._ref") | group="&milgrp._eoi",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=pregstartdt); 
				%end;
            %end;
			%else %do;				
				***** Level 5: Exclusion - Live birth delivery must be during the look period *****;
				%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
				%ms_attrition_compute(file=_micohorts,ToExcl=(delnum=0 and group="&milgrp._eoi") | group="&milgrp._ref",analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=pregstartdt); 
				%end;
				%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
				%let level = 5;
				%ms_attrition_compute(file=_micohorts,ToExcl=(delnum=0 and group="&milgrp._ref") | group="&milgrp._eoi",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=pregstartdt); 		
				%end;
			%end;

            *Reassess Age Group criteria;
            %ms_agestrat(infile=_micohorts %if "&analysis" = "tree" %then %do;(drop=lookpreg)%end;,
                         outfile=_micohorts,
                         startdt=birth_date,
                         enddt=indexdt,
                         timestrat=&agestrat.,
						 dropvars=minagedt maxagedt);

			***** Level 6: Exclusion - Pregnancy episode index date must overlap the query period and satisfy the age range condition within the query period *****;
			%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
			%ms_attrition_compute(file=_micohorts,ToExcl=((strip(AgeGroup) = "" or inQueryPeriod ne "Y") and group="&milgrp._eoi") | group="&milgrp._ref",analysisgrp=&milgrp._eoi,milattr=&milinkage.,pregdate=pregstartdt); 
			%end;
			%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
			%let level = 6;
			%ms_attrition_compute(file=_micohorts,ToExcl=((strip(AgeGroup) = "" or inQueryPeriod ne "Y") and group="&milgrp._ref") | group="&milgrp._eoi",analysisgrp=&milgrp._ref,milattr=&milinkage.,pregdate=pregstartdt); 
			%end;

            *list of mother and infant IDs;
            data %if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; _mi_ids_enr_eoi %end;
                 %if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do; _mi_ids_enr_ref %end;; 
				length enr_start enr_end 4.;
                set _micohorts(keep=patid enr_start enr_end group in=a)
                    _micohorts(keep=linkedid cenr_start cenr_end group rename=linkedid=patid rename=cenr_start=enr_start rename=cenr_end=enr_end
                                where=(missing(patid)=0));
                if a then mother="Y";
				else mother="N";
				%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; 
                if group = "&milgrp._eoi" then output _mi_ids_enr_eoi;
				%end;
				%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
                if group = "&milgrp._ref" then output _mi_ids_enr_ref;
				%end;
            run;
                                
            %macro create_mi_enrollment_spans(groupind=);
				proc sort data=_mi_ids_enr&groupind.(keep=patid mother) nodupkey out=PtsList&groupind.;
	                by patid;
	            run;

				/*If OUTCOMEPOP=MI we need to consider mother and infant bridged enrollment spans calculated above*/
				%if &outcomepop.=MI %then %do;
					proc sql noprint;                
						create table _mi_enr&groupind. as
		                (select a.patid length=&patid_len as patid,							
							    a.Enr_Start,
							    a.Enr_End
		                from worktemp.Outenr_&enrnum.(keep=patid outcome_enr_start outcome_enr_end rename=(outcome_enr_start=enr_start outcome_enr_end=enr_end)) as a
		                join PtsList&groupind.(where=(mother="Y")) as b 
						on a.patid=b.patid)
						union
						(select a.patid length=&patid_len as patid,							
							    a.Enr_Start,
							    a.Enr_End
		                from worktemp.Outenr_&enrnum.(keep=linkedid outcome_enr_start outcome_enr_end rename=(linkedid=patid outcome_enr_start=enr_start outcome_enr_end=enr_end)) as a
		                join PtsList&groupind.(where=(mother="N")) as b 
						on a.patid=b.patid)
						;
		            quit;
				%end;
				%else %do;
		            proc sql noprint;                
						create table _mi_enr&groupind. as
		                (select a.patid length=&patid_len as patid,							
							    a.Enr_Start,
							    a.Enr_End
		                from worktemp.enr_&enrnum.(keep=patid Enr_Start Enr_End) as a
		                join PtsList&groupind.(where=(mother="Y")) as b 
						on a.patid=b.patid)
						union
						(select a.patid length=&patid_len as patid,							
							    a.birth_date as Enr_start format mmddyy10.,
							    a.Enr_End
		                from worktemp.dem_infant_enr_&enrnum.(keep=patid Enr_End birth_date) as a
		                join PtsList&groupind.(where=(mother="N")) as b 
						on a.patid=b.patid)
						;
		            quit;
				%end;				
			%mend create_mi_enrollment_spans;

			/*Select all enrollment spans for exposure group*/
			%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
			%create_mi_enrollment_spans(groupind=_eoi);
           	%end;

            /*Select all enrollment spans for reference group*/
			%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
			%create_mi_enrollment_spans(groupind=_ref);
			%end;

            /**************************************************************/
            /* Process additional codes: Inclusion/Exclusion and outcomes */
            /**************************************************************/

            *Evaluate separately for EXP and COMP cohorts;

            %macro mi_pov3_5(groupind=);

                proc datasets nowarn noprint lib=work;
                    delete _ITGroupParamNDC _ITGroupParamMeds _ITGroupParamLabs _IT&inclusioncodes.;
                run;

				* Create &groupind subset;
				data _micohorts_pov3_5;
                set _micohorts(where=(group="&milgrp.&groupind."));
				run;

                *Are there any incl/excl conds to verify for this milgrp?;
                %let excl_incl = N; 
                %isdata(dataset=&inclusioncodes.);
                    %if %eval(&nobs.>0) %then %do;
                    data _IT&inclusioncodes.(drop=group);
                        set &cohortcodes. (keep=group indexcriteria stockgroup &InclExclVars. minrxdays codepop condinclusion subcondinclusion);
                        where lowcase(group)="&milgrp.&groupind." and indexcriteria in('INC','EXC');
                    run;
					
                    %isdata(dataset=_IT&inclusioncodes.);
                    %if %eval(&nobs.>0) %then %do;
						%let Excl_Incl = Y;						                    

						*Check elig for inclusion/exclusion subconditions;
						proc sql noprint;
							select condfrom, exclcondfromanchor, strip(codepop) into 
							:condfrom separated by " " ,
							:exclcondfromanchor separated by " ",
							:codepop separated by " "	
							from (select min(condfrom) as CondFrom, 
									case when strip(upcase(condfromanchor))="EPISODEENDDT" then "pregenddt"
									else condfromanchor
									end as exclcondfromanchor,
									codepop,
									cond,
									subcond
									from _IT&inclusioncodes.
									group by condfromanchor, codepop, cond, subcond);							
	                    quit;

						%put &=CondFrom;
						%put &=exclcondfromanchor;
						%put &=codepop;
                        					
						data _micohorts_pov3_5;
                        set _micohorts_pov3_5;
						%do condsubcond=1 %to %sysfunc(countw(&exclcondfromanchor.));	
							 * Only for mother claims;
							%if %index(%scan(&codepop., &condsubcond.), M) > 0 %then %do;		 	                            
	                            if not(Enr_Start <= COALESCE(%scan(&exclcondfromanchor., &condsubcond.),99999) + COALESCE(%scan(&condfrom., &condsubcond., %str(" ")),0)) then do;*Open ended means -infty/infty;
	                                MetMinEnroll="N";  
	                            end;
							%end;
						%end;	 
                        run;							
                       
					%end;
                %end;

                data _ITGroupParamNDC(keep= codecat codetype code dateonly codesupply stockgroup
										    indexcriteria fupcriteria
										    %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;)
                     _ITGroupParamMeds(keep= codecat codetype code dateonly codesupply 
											 CareSettingPrincipal indexcriteria fupcriteria
											 %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;)
                     _ITGroupParamLabs(keep= codecat codetype code dateonly codesupply LabResult LabDateType
                                             CareSettingprincipal indexcriteria fupcriteria
                                             %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;);
                    set &cohortcodes._MI;
					where lowcase(group) in ("&milgrp.&groupind.");

                        if Condinclusion=. then Condinclusion=1;
                        if missing(dateonly) then dateonly = 'N';
                        Code=compress(Code,'. ');
                        CodeCat=upcase(CodeCat);

                        if CodeCat="RX"          then output _ITGroupParamNDC;
                        else if CodeCat in("DX","PX") then output _ITGroupParamMeds;
                        else if CodeCat in("LB")      then output _ITGroupParamLabs;
                    run;

                *Break out CaresettingPrincipal into Caresetting and Principal;
                %ms_caresettingprincipal(InFile=_ITGroupParamMeds, 
                                         Var=CareSettingPrincipal, 
                                         OutFile=_ITGroupParamMeds);

                *Extract milgrp-specific records;
                %ms_loopmeds(datafile=worktemp.meds,
                             lookfile=_ITGroupParamMeds, 
                             multiplecodecat=Y,
                             outfile=_temp_PreMeds);

                %ms_extractdrugs(datafile=worktemp.drugs,
                                 lookfile=_ITGroupParamNDC, 
                                 lookvar=code , 
                                 mindate=,
                                 outfile=_temp_PreDrugs);

                %if &CHKCOMBREC = 1 %then %do;
                %ms_extractdrugs(datafile=worktemp.drugscomb,
                                 lookfile=_ITGroupParamNDC, 
                                 lookvar=code , 
                                 mindate=,
                                 outfile=_temp_PreDrugsComb);
                %end;

                %ms_extractlabs(datafile=worktemp.labextract,
                                lookfile=_ITGroupParamLabs, 
                                mindate=,
                                outfile=_temp_prelabs);  

                *restricting to mothers and linked infants;
                data _ITMeds(keep= DummyDate indexcriteria fupcriteria patid RxSup RxAmt codecat codesupply dateonly
                	                %if &excl_incl = Y %then %do; &InclExclVars. codepop %end; rename=DummyDate=ADate);
                    if 0 then set PtsList&groupind.;
                    declare hash pt (hashexp:16, dataset:"PtsList&groupind.");
                    pt.definekey('PatId');
                    pt.definedone();

                    do until(eof1);
                        set _temp_PreMeds end=eof1;
                        if pt.find()=0 then do;
                            format DummyDate mmddyy10.;
                            length DummyDate 4.;
                            DummyDate=ADate;
                            RxSup=1;
                            RxAmt=1;
                            drop ADate;
                            output;
                        end;
                    end;
                    stop;
                run;

                data _ITDrugs(keep=StockGroup indexcriteria fupcriteria patid rxdate RxSupEndDt RxSup RxAmt codecat 
                                    %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;);
                    if 0 then set PtsList&groupind.;
                    declare hash pt1 (hashexp:16, dataset:"PtsList&groupind.");
                    pt1.definekey('PatId');
                    pt1.definedone();

                    do until(eof1);
                      set _temp_PreDrugs end=eof1;
                      if pt1.find()=0 then do;
                        if not missing(codesupply) then rxsup = codesupply;
                        RxSupEndDt=Rxdate+RxSup-1;  
                        output;
                    end;
                    end;
                    stop;
                run;

                %if &CHKCOMBREC = 1 %then %do;
                data _ITDrugsComb(keep=StockGroup indexcriteria fupcriteria patid rxdate RxSupEndDt RxSup RxAmt codecat 
                                        %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;);
                    if 0 then set PtsList&groupind.;
                    declare hash pt1 (hashexp:16, dataset:"PtsList&groupind.");
                    pt1.definekey('PatId');
                    pt1.definedone();

                    do until(eof1);
                      set _temp_PreDrugsComb end=eof1;
                      if pt1.find()=0 then do;
                        if not missing(codesupply) then rxsup = codesupply;
                        RxSupEndDt=Rxdate+RxSup-1;  
                        output;
                    end;
                    end;
                    stop;
                run;
                %end;

                data _ITlabs(rename=DummyDate=ADate);
                    if 0 then set PtsList&groupind.;
                    declare hash pt (hashexp:16, dataset:"PtsList&groupind.");
                    pt.definekey('PatId');
                    pt.definedone();

                    do until(eof1);
                        set _temp_prelabs end=eof1;
                        if pt.find()=0 then do;
                            format DummyDate mmddyy10.;
                            length DummyDate 4.;
                            DummyDate=ADate;
                            RxSup=1;
                            RxAmt=1;
                            drop ADate;
                            output;
                        end;
                    end;
                    stop;
                run;

                *Make sure all claims overlap enrollment span and only 
                 keep eligible exposures (overlaping Enr_Start Enr_End);
                %ms_shaveoutside(reffile=_mi_enr&groupind.,
                                 refstart=Enr_Start,
                                 refend=Enr_End,
                                 KeepPartBf=N,
                                 ToShaveFile=_ITDrugs,
                                 ToShaveStart=RxDate,
                                 ToShaveEnd=RxSupEndDt,
                                 shaverx=Y,
                                 outfile=_ITDrugs);

                %ISDATA(dataset=_ITDrugsComb);
                %if %eval(&NOBS.>=1) %then %do;
                %ms_shaveoutside(reffile=_mi_enr&groupind.,
                                 refstart=Enr_Start,
                                 refend=Enr_End,
                                 KeepPartBf=N,
                                 ToShaveFile=_ITDrugsComb,
                                 ToShaveStart=RxDate,
                                 ToShaveEnd=RxSupEndDt,                                 
                                 shaverx=Y,
                                 outfile=_ITDrugsComb(drop=Enr_Start Enr_end));
                %end;
                
                %ms_shaveoutside(reffile=_mi_enr&groupind.,
                                 refstart=Enr_Start,
                                 refend=Enr_End,
                                 KeepPartBf=N,
                                 ToShaveFile=_ITMeds,
                                 ToShaveStart=ADate,
                                 ToShaveEnd=ADate,
                                 shaverx=N,
                                 outfile=_ITMeds);
            
                %isdata(dataset=_ITLabs);
                %if %eval(&NOBS.>=1) %then %do;                
                %ms_shaveoutside(reffile=_mi_enr&groupind.,
                               refstart=Enr_Start,
                               refend=Enr_End,
                               KeepPartBf=N,
                               ToShaveFile=_ITLabs,
                               ToShaveStart=ADate,
                               ToShaveEnd=ADate,
                               shaverx=N,
                               outfile=_ITLabs (drop=LabResult LabDateType));
                %end; 

               %let unique_stock_params=0;
               %ISDATA(dataset=STOCKPILE_NONCOVAR); 
                %IF %EVAL(&NOBS.>0) %THEN %DO;
                /* store maximum number of combination of stockpiling parameters */
                proc sql noprint;
                    select max(stocknumber)
                    into :unique_stock_params
                    from STOCKPILE_NONCOVAR
                    where group="&milgrp.&groupind.";
                quit;
                %END;

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

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

                %else %do;
                data _null_;
                    set STOCKPILE_NONCOVAR;
                    where group="&milgrp.&groupind.";
                    call symputx("SAMEDAY",strip(SameDay));
                    call symputx("SUPRANGE",strip(SupRange));
                    call symputx("AMTRANGE",strip(AmtRange));
                    call symputx("PERCENTDAYS",put(PercentDays,best.));
                run;
                %end; /* =1 */

                %MS_STOCKPILING(INFILE=_ITDrugs,            
                                CLMDATE=RxDate,                                 
                                CLMSUP=RxSup,                                   
                                CLMAMT=RxAmt,                                   
                                PROCFLAG=,                                      
                                PERCENTDAYS=&PERCENTDAYS.,                                  
                                GROUPING= StockGroup indexcriteria fupcriteria %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;,                           
                                SAMEDAY=&SAMEDAY.,                                 
                                SUPRANGE=&SUPRANGE.,                                    
                                AMTRANGE=&AMTRANGE.,
                                ID=CODECAT,                                             
                                OUTFILE=_ITDrugs,       
                                OUTFILEEXCL=_ITDrugsExcl);
                %end; /* <= 1 */

                %if &unique_stock_params > 1 %then %do; 
                    %do z = 1 %to &unique_stock_params;

                    data STOCKPILE_NONCOVAR_&z;
                    set STOCKPILE_NONCOVAR;
                    where group="&milgrp.&groupind." and stocknumber=&z;
                    call symputx("SAMEDAY",strip(SameDay));
                    call symputx("SUPRANGE",strip(SupRange));
                    call symputx("AMTRANGE",strip(AmtRange));
                    call symputx("PERCENTDAYS",put(PercentDays,best.));
                    run;

                    /* Only process dataset if there are subsetted observations */
                    %ISDATA(dataset=STOCKPILE_NONCOVAR_&z);
                    %if %eval(&NOBS>0) %then %do;
                    %let stock_list=;
                    proc sql noprint;
                      select distinct quote(strip(stockgroup))
                      into :stock_list separated by ' ' 
                      from STOCKPILE_NONCOVAR_&z;
                    quit;       

                %MS_STOCKPILING(INFILE=_ITDrugs(where=(stockgroup in (&stock_list))),            
                                CLMDATE=RxDate,                                 
                                CLMSUP=RxSup,                                   
                                CLMAMT=RxAmt,                                   
                                PROCFLAG=,                                      
                                PERCENTDAYS=&PERCENTDAYS.,                                  
                                GROUPING= StockGroup indexcriteria fupcriteria %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;,                           
                                SAMEDAY=&SAMEDAY.,                                  
                                SUPRANGE=&SUPRANGE.,                                    
                                AMTRANGE=&AMTRANGE.,
                                ID=CODECAT,                                             
                                OUTFILE=_ITDrugs_&z,       
                                OUTFILEEXCL=_ITDrugsExcl_&z);

                    %end; /* %isdata(STOCKPILE_NONCOVAR_&z) */

                    %end; /* z */

                    /* Stack all datasets together */
                    data _ITDrugs;
                      set _ITDrugs_:;
                    run;

                    data _ITDrugsExcl;
                      set _ITDrugsExcl_:;
                    run;

                    /* Delete temporary datasets */
                    proc datasets nowarn noprint lib=work;
                    delete _ITDrugs_: _ITDrugsExcl_: stockpile_noncovar_:;
                    quit;

                %end; /* unique_stock_params > 1 */

                %if &CHKCOMBREC = 1 %then %do;
                data _ITDrugs;
                set  _ITDrugs
                     _ITDrugsComb(in=a);
                *ExpireDt does not exist in _ITDrugsComb;
                if a then ExpireDt=Rxdate+RxSup-1;  
                run;
                %end;

                *Need to reshave because Stockpiling may have pushed claims outside enrollment period;
                %ISDATA(dataset=_ITDrugs);
                %IF %EVAL(&NOBS.>=1) %THEN %DO;
                    %ms_shaveoutside(reffile=_mi_enr&groupind.,
                                   refstart=Enr_Start,
                                   refend=Enr_End,
                                   KeepPartBf=N,
                                   ToShaveFile=_ITDrugs,
                                   ToShaveStart=RxDate,
                                   ToShaveEnd=ExpireDt,
                                   shaverx=Y,
                                   outfile=_ITDrugs);
                %END;   

                proc datasets library=work nowarn nolist;
                    delete _temp_:;
                quit;

                *Create _InclExcl _FUPEvent, and _FUTforTrunk datasets;
                data _InclExcl(keep=Patid Adate Expiredt dateonly %if &excl_incl = Y %then %do; &InclExclVars. codepop %end;)
                     _FUPEvent(keep=PatId Adate ExpireDt)
					 _FUTforTrunk(keep=PatId Adate ExpireDt);
                    set _ITDrugs(in=a rename=RxDate=ADate)
                        _ITMeds(in=b)
                        _ITLabs(in=c);

                    length expiredt 4;

                    if b or c then do;
                        if not missing(codesupply) then RxSup = CodeSupply;
                        else RxSup = 1;
                        Expiredt=Adate+RxSup-1;
                        RXAmt=1;
                        NumDispensing = 1;
                    end;

                    if indexcriteria in('INC','EXC') then output _InclExcl;
					else if indexcriteria='FUT' then output _FUTforTrunk;
                    if fupcriteria = 'DEF' then output _FUPEvent;
                run;

                /*****************************************/
                /* Evaluate inclusion/exclusion criteria */
                /*****************************************/
                %if &excl_incl = Y %then %do;

					* Rename variables related to episodes nomenclature to make them consistent 
					  with cida cohort episodes and easily usable in the ms_createpov3 macro;
					proc datasets library=work nolist;  
						modify _micohorts_pov3_5;
						rename indexdt=adate;
						rename pregstartdt=indexdt;
						rename pregenddt=episodeenddt;      
					quit;

					%ms_createpov3(cohortType=mil, inDataset=_micohorts_pov3_5, dateVar=IndexDt, IDvar=milID,
								   keepvars=linkedid indexdt_exp episodeenddt milID);

					* Reassign original variable names;
					proc datasets library=work nolist;  
						modify _micohorts_pov3_5;
						rename indexdt=pregstartdt;
						rename adate=indexdt;	
						rename episodeenddt=pregenddt;      
					quit;

                        data _micohorts&groupind.;
                        merge _micohorts_pov3_5 (in=a)
                              _pov3(in=b keep=milID)
							  _POV3incl_excl(in=c drop=indexdt);
                        by milID;
						if a;
						if b then hasexcl = 0;
						else hasexcl = 1;
                        run;										
						
					  /* Identify all conditions on inclusioncodes file */
						data INCLUSIONCODES;
						 set infolder.&INCLUSIONCODES. (where=(lowcase(group) = "&milgrp.&groupind."));
						 condlevel = upcase(condlevel);
						run;

						proc sort data = INCLUSIONCODES;
						by condlevel;
						run;
						
						proc sql noprint;	
							select count (distinct condlevel) 
									into :MaxInclExclCond trimmed
									from INCLUSIONCODES;

							select distinct condlevel
										  , condinclusion
								 into:condlvl1 - :condlvl&MaxInclExclCond.
								   ,:criteria1 - :criteria&MaxInclExclCond.
							from INCLUSIONCODES;
						quit;
	
					proc datasets nowarn noprint lib=work;
						delete INCLUSIONCODES;
					quit;
	   
					**If one exposure is not excluded then all exposures related to that pregnancy are not excluded;					
					proc sort data=_micohorts&groupind.(keep=patid pregstartdt hasexcl where=(hasexcl=0)) out=_noexcl(keep=patid pregstartdt) nodupkey;
						by patid pregstartdt;
					run;
					
					data _micohorts&groupind.;
						if 0 then set _noexcl;
						declare hash m(dataset:"_noexcl");
						m.definekey('patid', 'pregstartdt');
						m.definedone();
						do until (eof);
							set _micohorts&groupind. end=eof;
							if m.find() eq 0 then do; 
								hasexcl=0;
								lackincl_hasexcl=.;
								%do cond = 1 %to &MaxInclExclCond.; /*loop through each condition*/
									lackincl_hasexcl_&cond=.;
								%end;
							end;
							output _micohorts&groupind.;
						end;
					run;					
	   
                        %do cond = 1 %to &MaxInclExclCond.; /*loop through each condition*/
		                			
		                	proc sql noprint;
		                	select count(*) as counts format=12. into: numtoexcl trimmed
		                	from (select distinct PatId , pregstartdt
		                		  from _micohorts&groupind. where MetMinEnroll ne "N" and lackincl_hasexcl_&cond. = 1);
		                	quit;

							%isdata(dataset=_attrition_cond);
		                	data _attrition_cond;
		                	 format level $7. excluded comma10. analysisgrp $40. descr $500.;	
								%if %eval(&nobs.>=1) %then %do;
								  set _attrition_cond end=eof;
								  output;
								  if eof then do;
								%end;				
								level = "9.&cond."; 
		                	    levelsort1= 9;
		                	    levelsort2= &cond.;
		                	    remaining = .;		  									   
		                	    excluded  = &numtoexcl.;  
							    analysisgrp = "&milgrp.&groupind.";
									%if %eval(&&criteria&cond.. = 1) %then %do;
									  descr = "Information: Episodes excluded for lacking &&condlvl&cond..";
									%end;
									%else %do;
									  descr = "Information: Episodes excluded for &&condlvl&cond..";
									%end;
		                	    output;
								%if %eval(&nobs.>=1) %then %do;
								   end;
								%end;
		                	run;
                        %end;*end of condition;												
				%end;  /*INCLUSIONCODES*/
				
                %if &excl_incl = N %then %do;
				  proc sort data = _micohorts_pov3_5
                        out= _micohorts&groupind.;
				by patid indexdt indexdt2;
				run;
                %end;

                %if &excl_incl = Y %then %do;
                  proc sort data =_micohorts&groupind.;
				    by patid indexdt indexdt2;
				  run;
                %end;
				
				***** Level 7: Exclusion - Pregnancy episodes must satisfy the pre-pregnancy enrollment requirements within the query period *****;
				%let level=7;	
				%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=MetMinEnroll="N",analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt);

				***** Level 8: Exclusion - Pregnancy episodes must satisfy the post-pregnancy enrollment requirements within the query period *****;				
				%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=MinPostPregEnrolMet="N",analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt);
			
				***** Level 9: Exclusion - Pregnancy episodes must satisfy the inclusion and exclusion criteria *****;
				%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=%if &excl_incl = Y %then %do; hasexcl=1 %end; %else %do; delnum=0 %end;,analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt); 			
				                

				/****************************************************************************/
                /* Check if FUT codes truncation need to be applied                         */
                /****************************************************************************/				
                %if &T4HOIMETHOD. eq timetoevent %then %do;					
					proc sql noprint undo_policy=none;
					create table _episodeswithtrunk as
					select distinct a.milid,
						   min(b.adate) as trunkdt format=date9. length=4						    
					from _micohorts&groupind. as a
					join _futfortrunk as b
					on a.patid=b.patid
					where %MS_PeriodsOverlap(period1=%if &groupind. = _ref and %str(&controlmp.) eq %str() %then %do;
										      a.exposurefromdt %end; %else %do; a.indexdt2 %end; a.outcomeend,                                              
                                             period2=b.ADate)
					group by milid;
					quit;

					proc sql noprint undo_policy=none;
					create table _micohorts&groupind.(drop=outcomeend rename=outcomeend_new=outcomeend) as
					select a.*,
						   b.trunkdt,
						   min(trunkdt,  %if &censor_qryend. = 1 | &censor_dpend. = 1 %then %do; &censordate., %end; enr_end, %if &groupind. eq _eoi or (&groupind eq _ref and %str(&controlmp) ne %str()) %then %do; episodeenddt2, %end; outcomeend) as outcomeend_new format date9. length=4
					from _micohorts&groupind. as a
					left join _episodeswithtrunk as b
					on a.milid=b.milid;
					quit;
				%end;


                /*********************/
                /* Evaluate outcomes */
                /*********************/

                *Are there any FUP codes for this milgrp?;
                data _ITFUPoutcomes;
                    set &cohortcodes.;
                    where lowcase(group)="&milgrp.&groupind." and fupcriteria in('DEF');
                run;

                %isdata(dataset=_ITFUPoutcomes);
                %if %eval(&nobs.>=1) %then %do;

                    *Extract cohort specific codes - mother and infant;
                    %if %index(&outcomepop,M) > 0 %then %do;
                        proc sql noprint;
                            create table _episodeswitheventsM(where=(InGap=1)) as
                            select distinct index.Patid,
                                            index.linkedid,
                                            index.IndexDt,
                                            index.milID,
                                            inc.Adate as EventDt length=4,
                                            %MS_PeriodsOverlap(period1 = index.outcomestart index.outcomeend,                                             
                                                                period2=Inc.ADate Inc.ExpireDt) as InGap
                            from _micohorts&groupind. as index, 
                                 _FUPEvent as Inc
                            where index.Patid = Inc.Patid;
                        quit;
                    %end;
                    %if %index(&outcomepop,I) > 0 %then %do;
                        proc sql noprint;
                            create table _episodeswitheventsI(where=(InGap=1)) as
                            select distinct index.Patid,
                                            index.linkedid,
                                            index.IndexDt,
                                            index.milID,
                                            inc.Adate as EventDt length=4,
                                            %MS_PeriodsOverlap(period1=index.outcomestart index.outcomeend,                                             
                                                               period2=Inc.ADate Inc.ExpireDt) as InGap
                            from _micohorts&groupind. as index, 
                                 _FUPEvent as Inc
                            where index.linkedid = Inc.Patid;
                        quit;
                    %end;

                    *Select 1st event date in outcome window and merge with master episode list;
                    data _episodeswithevents;
                        set 
                        %if %index(&outcomepop,M) > 0 %then %do;
                            _episodeswitheventsM
                        %end;
                        %if %index(&outcomepop,I) > 0 %then %do;
                            _episodeswitheventsI
                        %end;   
                        ;
                    run;

                    proc means data=_episodeswithevents nway noprint;
                        var InGap EventDt;
                        class milID IndexDt;
                        output out=_episodeswithevents(drop=_:) sum(InGap)=NumEvents min(EventDt)=FEventDt/KEEPLEN;
                    run;

					proc sort data =_micohorts&groupind.;
				    by milID IndexDt;
				  	run;
             
                    data _micohorts&groupind.;
                        merge _micohorts&groupind.(in=a drop= exposurefromdt)
                              _EPisodesWithEvents(in=b);
                        by milID IndexDt;
                        if a;
                    run;					
                %end;
                %else %do;
                    data _micohorts&groupind.;
                        set _micohorts&groupind. (drop= exposurefromdt);
                            length feventdt 4.;
                            NumEvents = .;
                            FEventDt = .;
                    run;
                %end;

				/* Could have been sorted by milid when assessing outcomes */
				proc sort data =_micohorts&groupind.;
			    by patid indexdt indexdt2;
			  	run;

				/***************************************************************************************/
                /*Restrict to 1st episode per group and compute followuptime for time to event analysis*/
                /***************************************************************************************/
				%if &T4HOIMETHOD. eq timetoevent %then %do;	
				data _micohorts&groupind. (drop = ttcindexdate);	
					set _micohorts&groupind.(drop=trunkdt);
					by patid indexdt indexdt2;
					followuptime = min(outcomeend, feventdt) - outcomestart + 1;

					*timetocensor should start from User-defined Indexdate;
					%if %upcase(&indexdate.) = INDEXDT %then ttcindexdate = indexdt;
					%else %if %upcase(&indexdate.) = INDEXDT_DELIV %then ttcindexdate = pregenddt;
					%else %if %upcase(&indexdate.) = INDEXDT_EXP %then ttcindexdate = indexdt2;
					;
					length ttcindexdate 4.;
					timetocensor = max(0,min(%if &censor_qryend. = 1 | &censor_dpend. = 1 %then %do; &censordate., %end; enr_end) - ttcindexdate + 1);

					if followuptime <= 0 then do;
						followupmet="N";
						followuptime=0;
					end;	
					run;
				%end;
                
				***** Level 10: Exclusion - Must have at least 1 day of followup in the risk window *****;
				/* For L2 analyses, this exclusion will be performed in ms_covariateadjustment after PS model */
				%if "&run_ps_cov_analysis." ne "Y" %then %do;					
					%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=followupmet="N",analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt); 
				%end;
				%else %do;
					%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=delnum=0,analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt); 
				%end;	

				data _micohorts&groupind.;
                    set _micohorts&groupind. (drop=MetMinEnroll MinPostPregEnrolMet %if &excl_incl = Y %then %do; hasexcl lackincl_hasexcl: %end; );
                    by patid indexdt;
					length firstpreg 3;
					format feventdt mmddyy10.;
                    if first.patid then firstpreg=1;
					else firstpreg=0;					
                run;	

				***** Level 11: Exclusion - Restrict to first valid pregnancy episode *****;
				%ms_attrition_compute(file=_micohorts&groupind.,ToExcl=firstpreg=0,analysisgrp=&milgrp.&groupind.,milattr=&milinkage.,pregdate=pregstartdt); 				
        %mend mi_pov3_5;

        %if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do;
        %mi_pov3_5(groupind=_ref);
		%end;
		%if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do;
        %mi_pov3_5(groupind=_eoi);
		%end;

		proc sort data = _Attrition sortseq=linguistic(numeric_collation=ON);
		by analysisgrp level;
		run;
 
        data _Attrition;
            format descr $500. Excluded comma10.;
            retain level descr num Excluded;
            set _Attrition;
			by analysisgrp level;
			
            LastRemain=lag(num);
			Excluded=LastRemain-num;
			
			if first.analysisgrp then Excluded = .;

            if level="1" then descr="Initial Episode Count - Pregnancy episodes meeting initial cohort eligibility requirements";
            if level="2" then descr="Exclusion - Pregnancy episodes must have evidence of the MOI";					
            if level="3" then descr="Exclusion - Linked infant exceeded maximum allowable days required between birth date and delivery date";
            if level="4" then descr="Exclusion - Linked infant must satisfy the sex requirement";                 
			%if "&analysis" = "tree" %then %do;
				if level="5" then descr="Exclusion - Pregnancy outcome must be during the look period";
			%end;
			%else %do;
				if level="5" then descr="";
			%end;
            if level="6" then descr="Exclusion - Pregnancy episode index date must overlap the query period and satisfy the age range condition within the query period";
			if level="7" then descr="Exclusion - Pregnancy episodes must satisfy the pre-pregnancy outcome enrollment requirement";
			if level="8" then descr="Exclusion - Pregnancy episodes must satisfy the post-pregnancy outcome enrollment requirement";
            if level="9" then descr="Exclusion - Pregnancy episodes must satisfy the inclusion and exclusion criteria";            
			%if "&run_ps_cov_analysis." ne "Y" %then %do;
				if level="10" then descr="Exclusion - Pregnancy episodes must have at least 1 day of followup in the risk window";
			%end;
			%else %do;
				if level="10" then descr="";
			%end;
			if level="11" then descr="Exclusion - Restrict to first valid pregnancy episode";

            rename num=remaining;
            drop LastRemain;
        run;
		
		/* If inclusion/exclusion dataset exists then add attrition_cond dataset to _attrition table */		
		%ISDATA(dataset=_attrition_cond);
		%IF %EVAL(&NOBS.>=1) %THEN %DO;
			data _Attrition;
			 set _Attrition					  
				_attrition_cond (keep = analysisgrp level descr excluded);
			run;

			proc sort data = _Attrition sortseq=linguistic(numeric_collation=ON);
			by analysisgrp level;
			run;
	
			proc datasets nowarn noprint lib=work;
				delete _attrition_cond;
			quit;
		%END;	
				
		*Save to msoc;
        %if %eval(&a.=1) %then %do;
            /*Output attrition table to MSOC folder*/
            data MSOC.&runid._mil_Attrition;
                retain analysisgrp level descr remaining Excluded;
                set _Attrition;
            run;
        %end;
        %else %do;
            proc append base=MSOC.&runid._mil_Attrition 
                        data=_Attrition force;
            run;
        %end; 
				  
		proc sort data = MSOC.&runid._mil_Attrition sortseq=linguistic(numeric_collation=ON);
		by analysisgrp level;
		run;	

        *Combine _eoi and _ref cohorts;
        data _micohorts;
            set %if &cohortstocreate. eq B or &cohortstocreate. eq E %then %do; _micohorts_eoi(drop=firstpreg) %end;
				%if &cohortstocreate. eq B or &cohortstocreate. eq R %then %do; _micohorts_ref(drop=firstpreg) %end;;
            *drop moi metrics - need to compute per patient and not per MOI episode for baseline/table 1;
            drop usepre anyt: onlyt: sum: gestwk;
        run;

        /****************************************************************/
        /* Compute MOI metrics for final cohorts - for baseline/table 1 */
        /****************************************************************/

        proc sql noprint;
            create table _alldisp as
            select pts.patid,
                   pts.pregstartdt,
                   moi.moiname,
                   moi.gestwk,
                   moi.sumadjcntpre,
                   moi.sumadjcntanyt1,
                   moi.sumadjcntanyt2,
                   moi.sumadjcntanyt3,
                   moi.anyt1,
                   moi.anyt2,
                   moi.anyt3,
                   moi.usepre
            from _micohorts as pts
            left join dplocal.&runid._concepi_mstr_preg_tri(where=(lowcase(group)="&groupname")) as moi
            on pts.patid = moi.patid and pts.pregstartdt = moi.indexdt and pts.moiname = moi.moiname;
        quit;

        *summarize;
        proc means data=_alldisp nway noprint missing;
            var gestwk UsePre AnyT1 AnyT2 AnyT3 SumAdjCntPre SumAdjCntAnyT1 SumAdjCntAnyT2 SumAdjCntAnyT3;
            class PatId moiname pregstartdt ;   
            output out=_moi_by_del (drop=_:) min(gestwk) = ga_first
                                             max(usepre)=exp_pre
                                             max(anyt1)=exp_t1
                                             max(anyt2)=exp_t2
                                             max(anyt3)=exp_t3
                                             sum(SumAdjCntPre)=adjusteddisp_pre
                                             sum(SumAdjCntAnyT1)=adjusteddisp_t1
                                             sum(SumAdjCntAnyT2)=adjusteddisp_t2
                                             sum(SumAdjCntAnyT3)=adjusteddisp_t3;           
        run;

        proc sql noprint;
            create table MICohortMstList as
            select pts.*,
            	%if &T4HOIMETHOD ne timetoevent %then %do;
            	  %if &analysis ne tree %then %do;
            		. as followuptime,
            	  %end;
            	. as timetocensor,
            	%end;
                case when moi.ga_first > . then moi.ga_first
                else 0
                end as ga_first,
                case when moi.exp_pre > . then moi.exp_pre
                else 0
                end as exp_pre,
                case when moi.exp_t1 > . then moi.exp_t1
                else 0
                end as exp_t1,
                case when moi.exp_t2 > . then moi.exp_t2
                else 0
                end as exp_t2,
                case when moi.exp_t3 > . then moi.exp_t3
                else 0
                end as exp_t3,
                case when moi.adjusteddisp_pre > . then moi.adjusteddisp_pre
                else 0
                end as adjusteddisp_pre,
                case when moi.adjusteddisp_t1 > . then moi.adjusteddisp_t1
                else 0
                end as adjusteddisp_t1,
                case when moi.adjusteddisp_t2 > . then moi.adjusteddisp_t2
                else 0
                end as adjusteddisp_t2,
                case when moi.adjusteddisp_t3 > . then moi.adjusteddisp_t3
                else 0
                end as adjusteddisp_t3
            from _micohorts as pts
            left join _moi_by_del as moi
            on pts.patid = moi.patid and pts.pregstartdt = moi.pregstartdt and pts.moiname = moi.moiname;
        quit;

        *Save to DPlocal;
        %if %eval(&a.=1) %then %do;
            data DPLocal.&RUNID._mstr_mi;
                set MICohortMstList;
            run;
        %end;
        %else %do;
            proc append base=DPLocal.&RUNID._mstr_mi
                        data=MICohortMstList force;
            run;
        %end;  
     

        /*Output run time*/
        %ms_stoptimer(timestart=micohortruntime);
        %ms_outputruntimes(timevar=micohortruntime, step=%str(Create MI cohorts), group=&milgrp., monitoringperiod=);

        %end;

    %end;
	
    proc datasets nowarn noprint lib=work;
        delete _diag _others _episodeswithevents: _fupevent _alldisp _micohorts: _mi_enr: _mi_ids: _moi_by_del _attrition _tmp _FUT: _episodeswith:
			   _pregcohort _inclexcl _pov3: _it: _preg_: Micohortmstlist Ptslist_eoi Ptslist_ref &runid._concepi_mstr_preg_tri;
    quit;

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

%mend ms_createmicohorts;