*! 1.0.0 Ariel Linden 05Jul2025 

program markovfutureprob_ind, rclass
		version 11.0
		
		syntax anything , CURRent(string) [ PERiod(real 2) FORmat(string) TITle(string) ]

			// only one matrix may be specified
			local matcount : word count `anything'
			if (`matcount' > 1) {
				di as err "only one matrix may be specified"				
				exit = 103
			}
			// only one current state may be specified
			local currcount : word count `current'
			if (`currcount' > 1) {
				di as err "only one current state may be specified"
				exit = 103	
			}	
			
			local nrows = rowsof(`anything')
			local ncols = colsof(`anything')
                
			if `nrows' < 2 {
				di as err "the matrix must have at least 2 rows"
				exit 198
			}

			// check that matrix is symmetrical
			if `nrows' != `ncols' {
				di as err "the matrix must be symmetrical (equal number of rows and columns)"
				exit 198        
			}

			// loop through each row to check if the sum equals 1
			forval i = 1/`nrows' {
			* Calculate the row sum
				scalar row_sum = 0
				forval j = 1/`nrows' {
					scalar row_sum = row_sum + `anything'[`i', `j']
				}
				* Check if the row sum is not equal to 1
				if abs(row_sum - 1) > 1e-6 {
					di as err "row `i' of matrix `anything' does not sum to 1. Sum = " row_sum
					exit 198
				}
			}
			local rownames : rownames `anything'
			local colnames : colnames `anything'
			

			// determine if current state is a number or letter or combo //
			// Check for integer
			if regexm("`current'", "^[0-9]+$") {
				local nrows = rowsof(`anything')
				if `current' > `nrows' {
					di as err "`current' is not a valid row number"
					exit 198
				}
				else {
					local holdout = `current'
				}	
			}	
			// Check for alpha
			else if regexm("`current'", "^[a-zA-Z]+$") {
				if !`: list destination in rownames'  {
					di as err "{bf:`current'} is not in the list of matrix rownames: " "{bf:`rownames'}"
					exit 198
				}
				else {
					local i : list posof "`current'" in rownames
					local holdout = `i'
				}
			}	
			// else it's mixed alpha and numeric
			else if regexm("`current'", "^(?=.*[a-zA-Z])(?=.*[0-9]).+$") {
				if !`: list destination in rownames'  {
					di as err "{bf:`current'} is not in the list of matrix rownames: " "{bf:`rownames'}"
					exit 198
				}
				else {
					local i : list posof "`current'" in rownames
					local holdout = `i'
				}
			}				
			else {
				di as err "`current' is not in the list of matrix rownames or a row number"
				exit 198
			}
			
			mata: futureprob(`holdout', `period', "`anything'")
			
			if "`format'" != "" { 
				confirm numeric format `format' 
			}
			else local format %6.3f 
						
			local title title(Probabilities for `period' periods into the future)	
						
			// assign original rownames and colname that corresponds to the current state  
			matrix colnames futureprobs_full = `colnames'
			local rowfind : word `holdout' of `rownames'
			matrix rownames futureprobs_full = `rowfind'			
			
			matlist futureprobs_full,  border(top bottom) lines(oneline) tindent(1) aligncolnames(ralign) twidth(8) format(`format') `title'

			// save matrix
			return matrix futureprobs = futureprobs_full

end


version 11.0
mata:
mata clear
void function futureprob(real holdout, real period, string scalar stata_matrixname)

	{

		real matrix A
        
		// Convert Stata matrix to Mata		
		A = st_matrix(stata_matrixname)
		nrows = rows(A)
		// create matrix to identify current state
		curr = J(1, nrows, 0)		
		curr[holdout] = 1
		// Initialize matrix A as identity matrix
		ident = I(rows(A))
		
		// Multiply A by period (X times)
		for (i = 1; i <= period; i++) {
			ident = ident * A
		}		

		futureprobs_full = curr * ident
		
		// save as Stata matrix
		st_matrix("futureprobs_full", futureprobs_full)		
	}		
end